msgpack 0.6.0-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +26 -0
  4. data/ChangeLog +129 -0
  5. data/Dockerfile +62 -0
  6. data/LICENSE +177 -0
  7. data/README.rdoc +141 -0
  8. data/Rakefile +68 -0
  9. data/bench/pack.rb +23 -0
  10. data/bench/pack_log.rb +33 -0
  11. data/bench/pack_log_long.rb +65 -0
  12. data/bench/run.sh +14 -0
  13. data/bench/run_long.sh +35 -0
  14. data/bench/unpack.rb +21 -0
  15. data/bench/unpack_log.rb +34 -0
  16. data/bench/unpack_log_long.rb +67 -0
  17. data/doclib/msgpack.rb +77 -0
  18. data/doclib/msgpack/buffer.rb +193 -0
  19. data/doclib/msgpack/core_ext.rb +101 -0
  20. data/doclib/msgpack/error.rb +14 -0
  21. data/doclib/msgpack/packer.rb +134 -0
  22. data/doclib/msgpack/unpacker.rb +146 -0
  23. data/ext/java/org/msgpack/jruby/Buffer.java +221 -0
  24. data/ext/java/org/msgpack/jruby/Decoder.java +201 -0
  25. data/ext/java/org/msgpack/jruby/Encoder.java +308 -0
  26. data/ext/java/org/msgpack/jruby/ExtensionValue.java +136 -0
  27. data/ext/java/org/msgpack/jruby/MessagePackLibrary.java +107 -0
  28. data/ext/java/org/msgpack/jruby/Packer.java +78 -0
  29. data/ext/java/org/msgpack/jruby/Types.java +37 -0
  30. data/ext/java/org/msgpack/jruby/Unpacker.java +170 -0
  31. data/ext/msgpack/buffer.c +695 -0
  32. data/ext/msgpack/buffer.h +447 -0
  33. data/ext/msgpack/buffer_class.c +507 -0
  34. data/ext/msgpack/buffer_class.h +32 -0
  35. data/ext/msgpack/compat.h +114 -0
  36. data/ext/msgpack/core_ext.c +129 -0
  37. data/ext/msgpack/core_ext.h +26 -0
  38. data/ext/msgpack/extconf.rb +30 -0
  39. data/ext/msgpack/packer.c +168 -0
  40. data/ext/msgpack/packer.h +441 -0
  41. data/ext/msgpack/packer_class.c +302 -0
  42. data/ext/msgpack/packer_class.h +30 -0
  43. data/ext/msgpack/rbinit.c +33 -0
  44. data/ext/msgpack/rmem.c +94 -0
  45. data/ext/msgpack/rmem.h +109 -0
  46. data/ext/msgpack/sysdep.h +115 -0
  47. data/ext/msgpack/sysdep_endian.h +50 -0
  48. data/ext/msgpack/sysdep_types.h +46 -0
  49. data/ext/msgpack/unpacker.c +771 -0
  50. data/ext/msgpack/unpacker.h +122 -0
  51. data/ext/msgpack/unpacker_class.c +405 -0
  52. data/ext/msgpack/unpacker_class.h +32 -0
  53. data/lib/msgpack.rb +13 -0
  54. data/lib/msgpack/version.rb +3 -0
  55. data/msgpack.gemspec +31 -0
  56. data/msgpack.org.md +46 -0
  57. data/spec/cases.json +1 -0
  58. data/spec/cases.msg +0 -0
  59. data/spec/cases_compact.msg +0 -0
  60. data/spec/cases_spec.rb +39 -0
  61. data/spec/cruby/buffer_io_spec.rb +256 -0
  62. data/spec/cruby/buffer_packer.rb +29 -0
  63. data/spec/cruby/buffer_spec.rb +572 -0
  64. data/spec/cruby/buffer_unpacker.rb +19 -0
  65. data/spec/cruby/packer_spec.rb +120 -0
  66. data/spec/cruby/unpacker_spec.rb +305 -0
  67. data/spec/format_spec.rb +282 -0
  68. data/spec/jruby/benchmarks/shootout_bm.rb +73 -0
  69. data/spec/jruby/benchmarks/symbolize_keys_bm.rb +25 -0
  70. data/spec/jruby/msgpack/unpacker_spec.rb +290 -0
  71. data/spec/jruby/msgpack_spec.rb +142 -0
  72. data/spec/pack_spec.rb +67 -0
  73. data/spec/random_compat.rb +24 -0
  74. data/spec/spec_helper.rb +27 -0
  75. data/spec/unpack_spec.rb +60 -0
  76. metadata +208 -0
@@ -0,0 +1,221 @@
1
+ package org.msgpack.jruby;
2
+
3
+
4
+ import java.nio.ByteBuffer;
5
+
6
+ import org.jruby.Ruby;
7
+ import org.jruby.RubyClass;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.RubyHash;
10
+ import org.jruby.RubyIO;
11
+ import org.jruby.RubyInteger;
12
+ import org.jruby.runtime.builtin.IRubyObject;
13
+ import org.jruby.anno.JRubyClass;
14
+ import org.jruby.anno.JRubyMethod;
15
+ import org.jruby.runtime.ThreadContext;
16
+ import org.jruby.runtime.ObjectAllocator;
17
+ import org.jruby.util.ByteList;
18
+
19
+ import org.jcodings.Encoding;
20
+
21
+
22
+ @JRubyClass(name="MessagePack::Buffer")
23
+ public class Buffer extends RubyObject {
24
+ private RubyHash options;
25
+ private IRubyObject io;
26
+ private ByteBuffer buffer;
27
+ private boolean writeMode;
28
+ private Encoding binaryEncoding;
29
+
30
+ private static final int CACHE_LINE_SIZE = 64;
31
+ private static final int ARRAY_HEADER_SIZE = 24;
32
+
33
+ public Buffer(Ruby runtime, RubyClass type) {
34
+ super(runtime, type);
35
+ }
36
+
37
+ static class BufferAllocator implements ObjectAllocator {
38
+ public IRubyObject allocate(Ruby runtime, RubyClass type) {
39
+ return new Buffer(runtime, type);
40
+ }
41
+ }
42
+
43
+ @JRubyMethod(name = "initialize", optional = 2)
44
+ public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
45
+ if (args.length > 0) {
46
+ if (args[0].respondsTo("read") && args[0].respondsTo("write")) {
47
+ this.io = args[0];
48
+ }
49
+ if (args[args.length - 1] instanceof RubyHash) {
50
+ this.options = (RubyHash) args[args.length - 1];
51
+ }
52
+ }
53
+ this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
54
+ this.writeMode = true;
55
+ this.binaryEncoding = ctx.getRuntime().getEncodingService().getAscii8bitEncoding();
56
+ return this;
57
+ }
58
+
59
+ private void ensureRemainingCapacity(int c) {
60
+ if (!writeMode) {
61
+ buffer.compact();
62
+ writeMode = true;
63
+ }
64
+ if (buffer.remaining() < c) {
65
+ int newLength = Math.max(buffer.capacity() + (buffer.capacity() >> 1), buffer.capacity() + c);
66
+ newLength += CACHE_LINE_SIZE - ((ARRAY_HEADER_SIZE + newLength) % CACHE_LINE_SIZE);
67
+ buffer = ByteBuffer.allocate(newLength).put(buffer.array(), 0, buffer.position());
68
+ }
69
+ }
70
+
71
+ private void ensureReadMode() {
72
+ if (writeMode) {
73
+ buffer.flip();
74
+ writeMode = false;
75
+ }
76
+ }
77
+
78
+ private int rawSize() {
79
+ if (writeMode) {
80
+ return buffer.position();
81
+ } else {
82
+ return buffer.limit() - buffer.position();
83
+ }
84
+ }
85
+
86
+ @JRubyMethod(name = "clear")
87
+ public IRubyObject clear(ThreadContext ctx) {
88
+ buffer.clear();
89
+ return ctx.getRuntime().getNil();
90
+ }
91
+
92
+ @JRubyMethod(name = "size")
93
+ public IRubyObject size(ThreadContext ctx) {
94
+ return ctx.getRuntime().newFixnum(rawSize());
95
+ }
96
+
97
+ @JRubyMethod(name = "empty?")
98
+ public IRubyObject isEmpty(ThreadContext ctx) {
99
+ return rawSize() == 0 ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse();
100
+ }
101
+
102
+ private IRubyObject bufferWrite(ThreadContext ctx, IRubyObject str) {
103
+ ByteList bytes = str.asString().getByteList();
104
+ int length = bytes.length();
105
+ ensureRemainingCapacity(length);
106
+ buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
107
+ return ctx.getRuntime().newFixnum(length);
108
+
109
+ }
110
+
111
+ @JRubyMethod(name = "write", alias = {"<<"})
112
+ public IRubyObject write(ThreadContext ctx, IRubyObject str) {
113
+ if (io == null) {
114
+ return bufferWrite(ctx, str);
115
+ } else {
116
+ return io.callMethod(ctx, "write", str);
117
+ }
118
+ }
119
+
120
+ private void feed(ThreadContext ctx) {
121
+ if (io != null) {
122
+ bufferWrite(ctx, io.callMethod(ctx, "read"));
123
+ }
124
+ }
125
+
126
+ private IRubyObject readCommon(ThreadContext ctx, IRubyObject[] args, boolean raiseOnUnderflow) {
127
+ feed(ctx);
128
+ int length = rawSize();
129
+ if (args != null && args.length == 1) {
130
+ length = (int) args[0].convertToInteger().getLongValue();
131
+ }
132
+ if (raiseOnUnderflow && rawSize() < length) {
133
+ throw ctx.getRuntime().newEOFError();
134
+ }
135
+ int readLength = Math.min(length, rawSize());
136
+ if (readLength == 0 && length > 0) {
137
+ return ctx.getRuntime().getNil();
138
+ } else if (readLength == 0) {
139
+ return ctx.getRuntime().newString();
140
+ } else {
141
+ ensureReadMode();
142
+ byte[] bytes = new byte[readLength];
143
+ buffer.get(bytes);
144
+ ByteList byteList = new ByteList(bytes, binaryEncoding);
145
+ return ctx.getRuntime().newString(byteList);
146
+ }
147
+ }
148
+
149
+ @JRubyMethod(name = "read", optional = 1)
150
+ public IRubyObject read(ThreadContext ctx, IRubyObject[] args) {
151
+ return readCommon(ctx, args, false);
152
+ }
153
+
154
+ @JRubyMethod(name = "read_all", optional = 1)
155
+ public IRubyObject readAll(ThreadContext ctx, IRubyObject[] args) {
156
+ return readCommon(ctx, args, true);
157
+ }
158
+
159
+ private IRubyObject skipCommon(ThreadContext ctx, IRubyObject _length, boolean raiseOnUnderflow) {
160
+ feed(ctx);
161
+ int length = (int) _length.convertToInteger().getLongValue();
162
+ if (raiseOnUnderflow && rawSize() < length) {
163
+ throw ctx.getRuntime().newEOFError();
164
+ }
165
+ ensureReadMode();
166
+ int skipLength = Math.min(length, rawSize());
167
+ buffer.position(buffer.position() + skipLength);
168
+ return ctx.getRuntime().newFixnum(skipLength);
169
+ }
170
+
171
+ @JRubyMethod(name = "skip")
172
+ public IRubyObject skip(ThreadContext ctx, IRubyObject length) {
173
+ return skipCommon(ctx, length, false);
174
+ }
175
+
176
+ @JRubyMethod(name = "skip_all")
177
+ public IRubyObject skipAll(ThreadContext ctx, IRubyObject length) {
178
+ return skipCommon(ctx, length, true);
179
+ }
180
+
181
+ @JRubyMethod(name = "to_s", alias = {"to_str"})
182
+ public IRubyObject toS(ThreadContext ctx) {
183
+ ensureReadMode();
184
+ int length = buffer.limit() - buffer.position();
185
+ ByteList str = new ByteList(buffer.array(), buffer.position(), length, binaryEncoding, true);
186
+ return ctx.getRuntime().newString(str);
187
+ }
188
+
189
+ @JRubyMethod(name = "to_a")
190
+ public IRubyObject toA(ThreadContext ctx) {
191
+ return ctx.getRuntime().newArray(toS(ctx));
192
+ }
193
+
194
+ @JRubyMethod(name = "io")
195
+ public IRubyObject getIo(ThreadContext ctx) {
196
+ return io == null ? ctx.getRuntime().getNil() : io;
197
+ }
198
+
199
+ @JRubyMethod(name = "flush")
200
+ public IRubyObject flush(ThreadContext ctx) {
201
+ if (io == null) {
202
+ return ctx.getRuntime().getNil();
203
+ } else {
204
+ return io.callMethod(ctx, "flush");
205
+ }
206
+ }
207
+
208
+ @JRubyMethod(name = "close")
209
+ public IRubyObject close(ThreadContext ctx) {
210
+ if (io == null) {
211
+ return ctx.getRuntime().getNil();
212
+ } else {
213
+ return io.callMethod(ctx, "close");
214
+ }
215
+ }
216
+
217
+ @JRubyMethod(name = "write_to")
218
+ public IRubyObject writeTo(ThreadContext ctx, IRubyObject io) {
219
+ return io.callMethod(ctx, "write", readCommon(ctx, null, false));
220
+ }
221
+ }
@@ -0,0 +1,201 @@
1
+ package org.msgpack.jruby;
2
+
3
+
4
+ import java.math.BigInteger;
5
+ import java.nio.ByteBuffer;
6
+ import java.nio.BufferUnderflowException;
7
+ import java.util.Iterator;
8
+ import java.util.Arrays;
9
+
10
+ import org.jruby.Ruby;
11
+ import org.jruby.RubyObject;
12
+ import org.jruby.RubyClass;
13
+ import org.jruby.RubyBignum;
14
+ import org.jruby.RubyString;
15
+ import org.jruby.RubyHash;
16
+ import org.jruby.exceptions.RaiseException;
17
+ import org.jruby.runtime.builtin.IRubyObject;
18
+ import org.jruby.util.ByteList;
19
+
20
+ import org.jcodings.Encoding;
21
+ import org.jcodings.specific.UTF8Encoding;
22
+
23
+ import static org.msgpack.jruby.Types.*;
24
+
25
+
26
+ public class Decoder implements Iterator<IRubyObject> {
27
+ private final Ruby runtime;
28
+ private final Encoding binaryEncoding;
29
+ private final Encoding utf8Encoding;
30
+ private final RubyClass unpackErrorClass;
31
+ private final RubyClass underflowErrorClass;
32
+
33
+ private ByteBuffer buffer;
34
+ private boolean symbolizeKeys;
35
+
36
+ public Decoder(Ruby runtime) {
37
+ this(runtime, new byte[] {}, 0, 0);
38
+ }
39
+
40
+ public Decoder(Ruby runtime, byte[] bytes) {
41
+ this(runtime, bytes, 0, bytes.length);
42
+ }
43
+
44
+ public Decoder(Ruby runtime, byte[] bytes, int offset, int length) {
45
+ this.runtime = runtime;
46
+ this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
47
+ this.utf8Encoding = UTF8Encoding.INSTANCE;
48
+ this.unpackErrorClass = runtime.getModule("MessagePack").getClass("UnpackError");
49
+ this.underflowErrorClass = runtime.getModule("MessagePack").getClass("UnderflowError");
50
+ feed(bytes, offset, length);
51
+ }
52
+
53
+ public void symbolizeKeys(boolean symbolize) {
54
+ this.symbolizeKeys = symbolize;
55
+ }
56
+
57
+ public void feed(byte[] bytes) {
58
+ feed(bytes, 0, bytes.length);
59
+ }
60
+
61
+ public void feed(byte[] bytes, int offset, int length) {
62
+ if (buffer == null) {
63
+ buffer = ByteBuffer.wrap(bytes, offset, length);
64
+ } else {
65
+ ByteBuffer newBuffer = ByteBuffer.allocate(buffer.remaining() + length);
66
+ newBuffer.put(buffer);
67
+ newBuffer.put(bytes, offset, length);
68
+ newBuffer.flip();
69
+ buffer = newBuffer;
70
+ }
71
+ }
72
+
73
+ public void reset() {
74
+ buffer.rewind();
75
+ }
76
+
77
+ public int offset() {
78
+ return buffer.position();
79
+ }
80
+
81
+ private IRubyObject consumeUnsignedLong() {
82
+ long value = buffer.getLong();
83
+ if (value < 0) {
84
+ return RubyBignum.newBignum(runtime, BigInteger.valueOf(value & ((1L<<63)-1)).setBit(63));
85
+ } else {
86
+ return runtime.newFixnum(value);
87
+ }
88
+ }
89
+
90
+ private IRubyObject consumeString(int size, Encoding encoding) {
91
+ byte[] bytes = readBytes(size);
92
+ ByteList byteList = new ByteList(bytes, encoding);
93
+ return runtime.newString(byteList);
94
+ }
95
+
96
+ private IRubyObject consumeArray(int size) {
97
+ IRubyObject[] elements = new IRubyObject[size];
98
+ for (int i = 0; i < size; i++) {
99
+ elements[i] = next();
100
+ }
101
+ return runtime.newArray(elements);
102
+ }
103
+
104
+ private IRubyObject consumeHash(int size) {
105
+ RubyHash hash = RubyHash.newHash(runtime);
106
+ for (int i = 0; i < size; i++) {
107
+ IRubyObject key = next();
108
+ if (this.symbolizeKeys && key instanceof RubyString) {
109
+ key = ((RubyString) key).intern();
110
+ }
111
+ hash.fastASet(key, next());
112
+ }
113
+ return hash;
114
+ }
115
+
116
+ private IRubyObject consumeExtension(int size) {
117
+ int type = buffer.get();
118
+ byte[] payload = readBytes(size);
119
+ return ExtensionValue.newExtensionValue(runtime, type, payload);
120
+ }
121
+
122
+ private byte[] readBytes(int size) {
123
+ byte[] payload = new byte[size];
124
+ buffer.get(payload);
125
+ return payload;
126
+ }
127
+
128
+ @Override
129
+ public void remove() {
130
+ throw new UnsupportedOperationException();
131
+ }
132
+
133
+ @Override
134
+ public boolean hasNext() {
135
+ return buffer.remaining() > 0;
136
+ }
137
+
138
+ @Override
139
+ public IRubyObject next() {
140
+ int position = buffer.position();
141
+ try {
142
+ byte b = buffer.get();
143
+ outer: switch ((b >> 4) & 0xf) {
144
+ case 0x8: return consumeHash(b & 0x0f);
145
+ case 0x9: return consumeArray(b & 0x0f);
146
+ case 0xa:
147
+ case 0xb: return consumeString(b & 0x1f, utf8Encoding);
148
+ case 0xc:
149
+ switch (b) {
150
+ case NIL: return runtime.getNil();
151
+ case FALSE: return runtime.getFalse();
152
+ case TRUE: return runtime.getTrue();
153
+ case BIN8: return consumeString(buffer.get() & 0xff, binaryEncoding);
154
+ case BIN16: return consumeString(buffer.getShort() & 0xffff, binaryEncoding);
155
+ case BIN32: return consumeString(buffer.getInt(), binaryEncoding);
156
+ case VAREXT8: return consumeExtension(buffer.get());
157
+ case VAREXT16: return consumeExtension(buffer.getShort());
158
+ case VAREXT32: return consumeExtension(buffer.getInt());
159
+ case FLOAT32: return runtime.newFloat(buffer.getFloat());
160
+ case FLOAT64: return runtime.newFloat(buffer.getDouble());
161
+ case UINT8: return runtime.newFixnum(buffer.get() & 0xffL);
162
+ case UINT16: return runtime.newFixnum(buffer.getShort() & 0xffffL);
163
+ case UINT32: return runtime.newFixnum(buffer.getInt() & 0xffffffffL);
164
+ case UINT64: return consumeUnsignedLong();
165
+ default: break outer;
166
+ }
167
+ case 0xd:
168
+ switch (b) {
169
+ case INT8: return runtime.newFixnum(buffer.get());
170
+ case INT16: return runtime.newFixnum(buffer.getShort());
171
+ case INT32: return runtime.newFixnum(buffer.getInt());
172
+ case INT64: return runtime.newFixnum(buffer.getLong());
173
+ case FIXEXT1: return consumeExtension(1);
174
+ case FIXEXT2: return consumeExtension(2);
175
+ case FIXEXT4: return consumeExtension(4);
176
+ case FIXEXT8: return consumeExtension(8);
177
+ case FIXEXT16: return consumeExtension(16);
178
+ case STR8: return consumeString(buffer.get() & 0xff, utf8Encoding);
179
+ case STR16: return consumeString(buffer.getShort() & 0xffff, utf8Encoding);
180
+ case STR32: return consumeString(buffer.getInt(), utf8Encoding);
181
+ case ARY16: return consumeArray(buffer.getShort() & 0xffff);
182
+ case ARY32: return consumeArray(buffer.getInt());
183
+ case MAP16: return consumeHash(buffer.getShort() & 0xffff);
184
+ case MAP32: return consumeHash(buffer.getInt());
185
+ default: break outer;
186
+ }
187
+ case 0xe:
188
+ case 0xf: return runtime.newFixnum((0x1f & b) - 0x20);
189
+ default: return runtime.newFixnum(b);
190
+ }
191
+ buffer.position(position);
192
+ throw runtime.newRaiseException(unpackErrorClass, "Illegal byte sequence");
193
+ } catch (RaiseException re) {
194
+ buffer.position(position);
195
+ throw re;
196
+ } catch (BufferUnderflowException bue) {
197
+ buffer.position(position);
198
+ throw runtime.newRaiseException(underflowErrorClass, "Not enough bytes available");
199
+ }
200
+ }
201
+ }
@@ -0,0 +1,308 @@
1
+ package org.msgpack.jruby;
2
+
3
+
4
+ import java.math.BigInteger;
5
+ import java.nio.ByteBuffer;
6
+
7
+ import org.jruby.Ruby;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.RubyNil;
10
+ import org.jruby.RubyBoolean;
11
+ import org.jruby.RubyBignum;
12
+ import org.jruby.RubyInteger;
13
+ import org.jruby.RubyFixnum;
14
+ import org.jruby.RubyFloat;
15
+ import org.jruby.RubyString;
16
+ import org.jruby.RubySymbol;
17
+ import org.jruby.RubyArray;
18
+ import org.jruby.RubyHash;
19
+ import org.jruby.RubyEncoding;
20
+ import org.jruby.runtime.builtin.IRubyObject;
21
+ import org.jruby.util.ByteList;
22
+
23
+ import org.jcodings.Encoding;
24
+ import org.jcodings.specific.UTF8Encoding;
25
+
26
+ import static org.msgpack.jruby.Types.*;
27
+
28
+
29
+ public class Encoder {
30
+
31
+ private static final int CACHE_LINE_SIZE = 64;
32
+ private static final int ARRAY_HEADER_SIZE = 24;
33
+
34
+ private final Ruby runtime;
35
+ private final Encoding binaryEncoding;
36
+ private final Encoding utf8Encoding;
37
+
38
+ private ByteBuffer buffer;
39
+
40
+ public Encoder(Ruby runtime) {
41
+ this.runtime = runtime;
42
+ this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
43
+ this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
44
+ this.utf8Encoding = UTF8Encoding.INSTANCE;
45
+ }
46
+
47
+ private void ensureRemainingCapacity(int c) {
48
+ if (buffer.remaining() < c) {
49
+ int newLength = Math.max(buffer.capacity() + (buffer.capacity() >> 1), buffer.capacity() + c);
50
+ newLength += CACHE_LINE_SIZE - ((ARRAY_HEADER_SIZE + newLength) % CACHE_LINE_SIZE);
51
+ buffer = ByteBuffer.allocate(newLength).put(buffer.array(), 0, buffer.position());
52
+ }
53
+ }
54
+
55
+ private IRubyObject readRubyString() {
56
+ IRubyObject str = runtime.newString(new ByteList(buffer.array(), 0, buffer.position(), binaryEncoding, false));
57
+ buffer.clear();
58
+ return str;
59
+ }
60
+
61
+ public IRubyObject encode(IRubyObject object) {
62
+ appendObject(object);
63
+ return readRubyString();
64
+ }
65
+
66
+ public IRubyObject encode(IRubyObject object, IRubyObject destination) {
67
+ appendObject(object, destination);
68
+ return readRubyString();
69
+ }
70
+
71
+ public IRubyObject encodeArrayHeader(int size) {
72
+ appendArrayHeader(size);
73
+ return readRubyString();
74
+ }
75
+
76
+ public IRubyObject encodeMapHeader(int size) {
77
+ appendHashHeader(size);
78
+ return readRubyString();
79
+ }
80
+
81
+ private void appendObject(IRubyObject object) {
82
+ appendObject(object, null);
83
+ }
84
+
85
+ private void appendObject(IRubyObject object, IRubyObject destination) {
86
+ if (object == null || object instanceof RubyNil) {
87
+ ensureRemainingCapacity(1);
88
+ buffer.put(NIL);
89
+ } else if (object instanceof RubyBoolean) {
90
+ ensureRemainingCapacity(1);
91
+ buffer.put(((RubyBoolean) object).isTrue() ? TRUE : FALSE);
92
+ } else if (object instanceof RubyBignum) {
93
+ appendBignum((RubyBignum) object);
94
+ } else if (object instanceof RubyInteger) {
95
+ appendInteger((RubyInteger) object);
96
+ } else if (object instanceof RubyFloat) {
97
+ appendFloat((RubyFloat) object);
98
+ } else if (object instanceof RubyString) {
99
+ appendString((RubyString) object);
100
+ } else if (object instanceof RubySymbol) {
101
+ appendString(((RubySymbol) object).asString());
102
+ } else if (object instanceof RubyArray) {
103
+ appendArray((RubyArray) object);
104
+ } else if (object instanceof RubyHash) {
105
+ appendHash((RubyHash) object);
106
+ } else if (object.respondsTo("to_msgpack")) {
107
+ appendCustom(object, destination);
108
+ } else {
109
+ throw runtime.newArgumentError(String.format("Cannot pack type: %s", object.getClass().getName()));
110
+ }
111
+ }
112
+
113
+ private void appendBignum(RubyBignum object) {
114
+ BigInteger value = ((RubyBignum) object).getBigIntegerValue();
115
+ if (value.bitLength() > 64 || (value.bitLength() > 63 && value.signum() < 0)) {
116
+ throw runtime.newArgumentError(String.format("Cannot pack big integer: %s", value));
117
+ }
118
+ ensureRemainingCapacity(9);
119
+ buffer.put(value.signum() < 0 ? INT64 : UINT64);
120
+ byte[] b = value.toByteArray();
121
+ buffer.put(b, b.length - 8, 8);
122
+ }
123
+
124
+ private void appendInteger(RubyInteger object) {
125
+ long value = ((RubyInteger) object).getLongValue();
126
+ if (value < 0) {
127
+ if (value < Short.MIN_VALUE) {
128
+ if (value < Integer.MIN_VALUE) {
129
+ ensureRemainingCapacity(9);
130
+ buffer.put(INT64);
131
+ buffer.putLong(value);
132
+ } else {
133
+ ensureRemainingCapacity(5);
134
+ buffer.put(INT32);
135
+ buffer.putInt((int) value);
136
+ }
137
+ } else if (value >= -0x20L) {
138
+ ensureRemainingCapacity(1);
139
+ byte b = (byte) (value | 0xe0);
140
+ buffer.put(b);
141
+ } else if (value < Byte.MIN_VALUE) {
142
+ ensureRemainingCapacity(3);
143
+ buffer.put(INT16);
144
+ buffer.putShort((short) value);
145
+ } else {
146
+ ensureRemainingCapacity(2);
147
+ buffer.put(INT8);
148
+ buffer.put((byte) value);
149
+ }
150
+ } else {
151
+ if (value < 0x10000L) {
152
+ if (value < 128L) {
153
+ ensureRemainingCapacity(1);
154
+ buffer.put((byte) value);
155
+ } else if (value < 0x100L) {
156
+ ensureRemainingCapacity(2);
157
+ buffer.put(UINT8);
158
+ buffer.put((byte) value);
159
+ } else {
160
+ ensureRemainingCapacity(3);
161
+ buffer.put(UINT16);
162
+ buffer.putShort((short) value);
163
+ }
164
+ } else if (value < 0x100000000L) {
165
+ ensureRemainingCapacity(5);
166
+ buffer.put(UINT32);
167
+ buffer.putInt((int) value);
168
+ } else {
169
+ ensureRemainingCapacity(9);
170
+ buffer.put(INT64);
171
+ buffer.putLong(value);
172
+ }
173
+ }
174
+ }
175
+
176
+ private void appendFloat(RubyFloat object) {
177
+ double value = object.getDoubleValue();
178
+ float f = (float) value;
179
+ //TODO: msgpack-ruby original does encode this value as Double, not float
180
+ // if (Double.compare(f, value) == 0) {
181
+ // ensureRemainingCapacity(5);
182
+ // buffer.put(FLOAT32);
183
+ // buffer.putFloat(f);
184
+ // } else {
185
+ ensureRemainingCapacity(9);
186
+ buffer.put(FLOAT64);
187
+ buffer.putDouble(value);
188
+ // }
189
+ }
190
+
191
+ private void appendString(RubyString object) {
192
+ Encoding encoding = object.getEncoding();
193
+ boolean binary = encoding == binaryEncoding;
194
+ if (encoding != utf8Encoding && encoding != binaryEncoding) {
195
+ object = (RubyString) ((RubyString) object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding));
196
+ }
197
+ ByteList bytes = object.getByteList();
198
+ int length = bytes.length();
199
+ if (length < 32 && !binary) {
200
+ ensureRemainingCapacity(1 + length);
201
+ buffer.put((byte) (length | FIXSTR));
202
+ } else if (length <= 0xff) {
203
+ ensureRemainingCapacity(2 + length);
204
+ buffer.put(binary ? BIN8 : STR8);
205
+ buffer.put((byte) length);
206
+ } else if (length <= 0xffff) {
207
+ ensureRemainingCapacity(3 + length);
208
+ buffer.put(binary ? BIN16 : STR16);
209
+ buffer.putShort((short) length);
210
+ } else {
211
+ ensureRemainingCapacity(5 + length);
212
+ buffer.put(binary ? BIN32 : STR32);
213
+ buffer.putInt((int) length);
214
+ }
215
+ buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
216
+ }
217
+
218
+ private void appendArray(RubyArray object) {
219
+ appendArrayHeader(object);
220
+ appendArrayElements(object);
221
+ }
222
+
223
+ private void appendArrayHeader(RubyArray object) {
224
+ appendArrayHeader(object.size());
225
+ }
226
+
227
+ private void appendArrayHeader(int size) {
228
+ if (size < 16) {
229
+ ensureRemainingCapacity(1);
230
+ buffer.put((byte) (size | 0x90));
231
+ } else if (size < 0x10000) {
232
+ ensureRemainingCapacity(3);
233
+ buffer.put(ARY16);
234
+ buffer.putShort((short) size);
235
+ } else {
236
+ ensureRemainingCapacity(5);
237
+ buffer.put(ARY32);
238
+ buffer.putInt(size);
239
+ }
240
+ }
241
+
242
+ private void appendArrayElements(RubyArray object) {
243
+ int size = object.size();
244
+ for (int i = 0; i < size; i++) {
245
+ appendObject(object.eltOk(i));
246
+ }
247
+ }
248
+
249
+ private void appendHash(RubyHash object) {
250
+ appendHashHeader(object);
251
+ appendHashElements(object);
252
+ }
253
+
254
+ private void appendHashHeader(RubyHash object) {
255
+ appendHashHeader(object.size());
256
+ }
257
+
258
+ private void appendHashHeader(int size) {
259
+ if (size < 16) {
260
+ ensureRemainingCapacity(1);
261
+ buffer.put((byte) (size | 0x80));
262
+ } else if (size < 0x10000) {
263
+ ensureRemainingCapacity(3);
264
+ buffer.put(MAP16);
265
+ buffer.putShort((short) size);
266
+ } else {
267
+ ensureRemainingCapacity(5);
268
+ buffer.put(MAP32);
269
+ buffer.putInt(size);
270
+ }
271
+ }
272
+
273
+ private void appendHashElements(RubyHash object) {
274
+ int size = object.size();
275
+ HashVisitor visitor = new HashVisitor(size);
276
+ object.visitAll(visitor);
277
+ if (visitor.remain != 0) {
278
+ object.getRuntime().newConcurrencyError("Hash size changed while packing");
279
+ }
280
+ }
281
+
282
+ private class HashVisitor extends RubyHash.Visitor {
283
+ public int remain;
284
+
285
+ public HashVisitor(int size) {
286
+ remain = size;
287
+ }
288
+
289
+ public void visit(IRubyObject key, IRubyObject value) {
290
+ if (remain-- > 0) {
291
+ appendObject(key);
292
+ appendObject(value);
293
+ }
294
+ }
295
+ }
296
+
297
+ private void appendCustom(IRubyObject object, IRubyObject destination) {
298
+ if (destination == null) {
299
+ IRubyObject result = object.callMethod(runtime.getCurrentContext(), "to_msgpack");
300
+ ByteList bytes = result.asString().getByteList();
301
+ int length = bytes.length();
302
+ ensureRemainingCapacity(length);
303
+ buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
304
+ } else {
305
+ object.callMethod(runtime.getCurrentContext(), "to_msgpack", destination);
306
+ }
307
+ }
308
+ }