msgpack 0.6.0-x86-mswin32-60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.travis.yml +26 -0
- data/ChangeLog +129 -0
- data/Dockerfile +62 -0
- data/LICENSE +177 -0
- data/README.rdoc +141 -0
- data/Rakefile +68 -0
- data/bench/pack.rb +23 -0
- data/bench/pack_log.rb +33 -0
- data/bench/pack_log_long.rb +65 -0
- data/bench/run.sh +14 -0
- data/bench/run_long.sh +35 -0
- data/bench/unpack.rb +21 -0
- data/bench/unpack_log.rb +34 -0
- data/bench/unpack_log_long.rb +67 -0
- data/doclib/msgpack.rb +77 -0
- data/doclib/msgpack/buffer.rb +193 -0
- data/doclib/msgpack/core_ext.rb +101 -0
- data/doclib/msgpack/error.rb +14 -0
- data/doclib/msgpack/packer.rb +134 -0
- data/doclib/msgpack/unpacker.rb +146 -0
- data/ext/java/org/msgpack/jruby/Buffer.java +221 -0
- data/ext/java/org/msgpack/jruby/Decoder.java +201 -0
- data/ext/java/org/msgpack/jruby/Encoder.java +308 -0
- data/ext/java/org/msgpack/jruby/ExtensionValue.java +136 -0
- data/ext/java/org/msgpack/jruby/MessagePackLibrary.java +107 -0
- data/ext/java/org/msgpack/jruby/Packer.java +78 -0
- data/ext/java/org/msgpack/jruby/Types.java +37 -0
- data/ext/java/org/msgpack/jruby/Unpacker.java +170 -0
- data/ext/msgpack/buffer.c +695 -0
- data/ext/msgpack/buffer.h +447 -0
- data/ext/msgpack/buffer_class.c +507 -0
- data/ext/msgpack/buffer_class.h +32 -0
- data/ext/msgpack/compat.h +114 -0
- data/ext/msgpack/core_ext.c +129 -0
- data/ext/msgpack/core_ext.h +26 -0
- data/ext/msgpack/extconf.rb +30 -0
- data/ext/msgpack/packer.c +168 -0
- data/ext/msgpack/packer.h +441 -0
- data/ext/msgpack/packer_class.c +302 -0
- data/ext/msgpack/packer_class.h +30 -0
- data/ext/msgpack/rbinit.c +33 -0
- data/ext/msgpack/rmem.c +94 -0
- data/ext/msgpack/rmem.h +109 -0
- data/ext/msgpack/sysdep.h +115 -0
- data/ext/msgpack/sysdep_endian.h +50 -0
- data/ext/msgpack/sysdep_types.h +46 -0
- data/ext/msgpack/unpacker.c +771 -0
- data/ext/msgpack/unpacker.h +122 -0
- data/ext/msgpack/unpacker_class.c +405 -0
- data/ext/msgpack/unpacker_class.h +32 -0
- data/lib/msgpack.rb +13 -0
- data/lib/msgpack/version.rb +3 -0
- data/msgpack.gemspec +31 -0
- data/msgpack.org.md +46 -0
- data/spec/cases.json +1 -0
- data/spec/cases.msg +0 -0
- data/spec/cases_compact.msg +0 -0
- data/spec/cases_spec.rb +39 -0
- data/spec/cruby/buffer_io_spec.rb +256 -0
- data/spec/cruby/buffer_packer.rb +29 -0
- data/spec/cruby/buffer_spec.rb +572 -0
- data/spec/cruby/buffer_unpacker.rb +19 -0
- data/spec/cruby/packer_spec.rb +120 -0
- data/spec/cruby/unpacker_spec.rb +305 -0
- data/spec/format_spec.rb +282 -0
- data/spec/jruby/benchmarks/shootout_bm.rb +73 -0
- data/spec/jruby/benchmarks/symbolize_keys_bm.rb +25 -0
- data/spec/jruby/msgpack/unpacker_spec.rb +290 -0
- data/spec/jruby/msgpack_spec.rb +142 -0
- data/spec/pack_spec.rb +67 -0
- data/spec/random_compat.rb +24 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/unpack_spec.rb +60 -0
- 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
|
+
}
|