ed-precompiled_msgpack 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/ChangeLog +368 -0
- data/LICENSE +177 -0
- data/README.md +302 -0
- data/ext/java/org/msgpack/jruby/Buffer.java +233 -0
- data/ext/java/org/msgpack/jruby/Decoder.java +307 -0
- data/ext/java/org/msgpack/jruby/Encoder.java +456 -0
- data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +167 -0
- data/ext/java/org/msgpack/jruby/ExtensionValue.java +128 -0
- data/ext/java/org/msgpack/jruby/Factory.java +130 -0
- data/ext/java/org/msgpack/jruby/MessagePackLibrary.java +45 -0
- data/ext/java/org/msgpack/jruby/Packer.java +266 -0
- data/ext/java/org/msgpack/jruby/Types.java +37 -0
- data/ext/java/org/msgpack/jruby/Unpacker.java +336 -0
- data/ext/msgpack/buffer.c +669 -0
- data/ext/msgpack/buffer.h +604 -0
- data/ext/msgpack/buffer_class.c +616 -0
- data/ext/msgpack/buffer_class.h +33 -0
- data/ext/msgpack/compat.h +26 -0
- data/ext/msgpack/extconf.rb +53 -0
- data/ext/msgpack/extension_value_class.c +34 -0
- data/ext/msgpack/extension_value_class.h +31 -0
- data/ext/msgpack/factory_class.c +276 -0
- data/ext/msgpack/factory_class.h +33 -0
- data/ext/msgpack/packer.c +199 -0
- data/ext/msgpack/packer.h +513 -0
- data/ext/msgpack/packer_class.c +442 -0
- data/ext/msgpack/packer_class.h +43 -0
- data/ext/msgpack/packer_ext_registry.c +74 -0
- data/ext/msgpack/packer_ext_registry.h +140 -0
- data/ext/msgpack/rbinit.c +35 -0
- data/ext/msgpack/rmem.c +93 -0
- data/ext/msgpack/rmem.h +109 -0
- data/ext/msgpack/sysdep.h +118 -0
- data/ext/msgpack/sysdep_endian.h +50 -0
- data/ext/msgpack/sysdep_types.h +46 -0
- data/ext/msgpack/unpacker.c +986 -0
- data/ext/msgpack/unpacker.h +152 -0
- data/ext/msgpack/unpacker_class.c +447 -0
- data/ext/msgpack/unpacker_class.h +43 -0
- data/ext/msgpack/unpacker_ext_registry.c +74 -0
- data/ext/msgpack/unpacker_ext_registry.h +62 -0
- data/lib/msgpack/bigint.rb +69 -0
- data/lib/msgpack/buffer.rb +9 -0
- data/lib/msgpack/core_ext.rb +139 -0
- data/lib/msgpack/factory.rb +211 -0
- data/lib/msgpack/packer.rb +37 -0
- data/lib/msgpack/symbol.rb +26 -0
- data/lib/msgpack/time.rb +29 -0
- data/lib/msgpack/timestamp.rb +76 -0
- data/lib/msgpack/unpacker.rb +41 -0
- data/lib/msgpack/version.rb +6 -0
- data/lib/msgpack.rb +53 -0
- data/msgpack.gemspec +41 -0
- metadata +216 -0
@@ -0,0 +1,456 @@
|
|
1
|
+
package org.msgpack.jruby;
|
2
|
+
|
3
|
+
|
4
|
+
import java.math.BigInteger;
|
5
|
+
import java.nio.ByteBuffer;
|
6
|
+
import java.util.Arrays;
|
7
|
+
|
8
|
+
import org.jruby.Ruby;
|
9
|
+
import org.jruby.RubyObject;
|
10
|
+
import org.jruby.RubyModule;
|
11
|
+
import org.jruby.RubyNil;
|
12
|
+
import org.jruby.RubyBoolean;
|
13
|
+
import org.jruby.RubyNumeric;
|
14
|
+
import org.jruby.RubyBignum;
|
15
|
+
import org.jruby.RubyInteger;
|
16
|
+
import org.jruby.RubyFixnum;
|
17
|
+
import org.jruby.RubyFloat;
|
18
|
+
import org.jruby.RubyString;
|
19
|
+
import org.jruby.RubySymbol;
|
20
|
+
import org.jruby.RubyArray;
|
21
|
+
import org.jruby.RubyHash;
|
22
|
+
import org.jruby.RubyEncoding;
|
23
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
24
|
+
import org.jruby.util.ByteList;
|
25
|
+
|
26
|
+
import org.jcodings.Encoding;
|
27
|
+
import org.jcodings.specific.UTF8Encoding;
|
28
|
+
|
29
|
+
import static org.msgpack.jruby.Types.*;
|
30
|
+
|
31
|
+
public class Encoder {
|
32
|
+
|
33
|
+
private static final int CACHE_LINE_SIZE = 64;
|
34
|
+
private static final int ARRAY_HEADER_SIZE = 24;
|
35
|
+
|
36
|
+
private final Ruby runtime;
|
37
|
+
private final Encoding binaryEncoding;
|
38
|
+
private final Encoding utf8Encoding;
|
39
|
+
private final boolean compatibilityMode;
|
40
|
+
private final ExtensionRegistry registry;
|
41
|
+
private final Packer packer;
|
42
|
+
|
43
|
+
public boolean hasSymbolExtType;
|
44
|
+
private boolean hasBigintExtType;
|
45
|
+
private boolean recursiveExtension;
|
46
|
+
|
47
|
+
private ByteBuffer buffer;
|
48
|
+
|
49
|
+
public Encoder(Ruby runtime, Packer packer, boolean compatibilityMode, ExtensionRegistry registry, boolean hasSymbolExtType, boolean hasBigintExtType) {
|
50
|
+
this.packer = packer;
|
51
|
+
this.runtime = runtime;
|
52
|
+
this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
|
53
|
+
this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
|
54
|
+
this.utf8Encoding = UTF8Encoding.INSTANCE;
|
55
|
+
this.compatibilityMode = compatibilityMode;
|
56
|
+
this.registry = registry;
|
57
|
+
this.hasSymbolExtType = hasSymbolExtType;
|
58
|
+
this.hasBigintExtType = hasBigintExtType;
|
59
|
+
}
|
60
|
+
|
61
|
+
public boolean isCompatibilityMode() {
|
62
|
+
return compatibilityMode;
|
63
|
+
}
|
64
|
+
|
65
|
+
private void ensureRemainingCapacity(int c) {
|
66
|
+
if (buffer.remaining() < c) {
|
67
|
+
int newLength = Math.max(buffer.capacity() + (buffer.capacity() >> 1), buffer.capacity() + c);
|
68
|
+
newLength += CACHE_LINE_SIZE - ((ARRAY_HEADER_SIZE + newLength) % CACHE_LINE_SIZE);
|
69
|
+
buffer = ByteBuffer.allocate(newLength).put(buffer.array(), 0, buffer.position());
|
70
|
+
}
|
71
|
+
}
|
72
|
+
|
73
|
+
private IRubyObject readRubyString() {
|
74
|
+
if (recursiveExtension) {
|
75
|
+
// If recursiveExtension is true, it means we re-entered encode, so we MUST NOT flush the buffer.
|
76
|
+
// Instead we return an empty string to act as a null object for the caller. The buffer will actually
|
77
|
+
// be flushed once we're done serializing the recursive extension.
|
78
|
+
// All other method that consume the buffer should do so through readRubyString or implement the same logic.
|
79
|
+
return runtime.newString();
|
80
|
+
} else {
|
81
|
+
IRubyObject str = runtime.newString(new ByteList(buffer.array(), 0, buffer.position(), binaryEncoding, false));
|
82
|
+
buffer.clear();
|
83
|
+
return str;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
public IRubyObject encode(IRubyObject object) {
|
88
|
+
appendObject(object);
|
89
|
+
return readRubyString();
|
90
|
+
}
|
91
|
+
|
92
|
+
public IRubyObject encode(IRubyObject object, IRubyObject destination) {
|
93
|
+
appendObject(object, destination);
|
94
|
+
return readRubyString();
|
95
|
+
}
|
96
|
+
|
97
|
+
public IRubyObject encodeArrayHeader(int size) {
|
98
|
+
appendArrayHeader(size);
|
99
|
+
return readRubyString();
|
100
|
+
}
|
101
|
+
|
102
|
+
public IRubyObject encodeMapHeader(int size) {
|
103
|
+
appendHashHeader(size);
|
104
|
+
return readRubyString();
|
105
|
+
}
|
106
|
+
|
107
|
+
public IRubyObject encodeBinHeader(int size) {
|
108
|
+
appendStringHeader(size, true);
|
109
|
+
return readRubyString();
|
110
|
+
}
|
111
|
+
|
112
|
+
public IRubyObject encodeFloat32(RubyNumeric numeric) {
|
113
|
+
appendFloat32(numeric);
|
114
|
+
return readRubyString();
|
115
|
+
}
|
116
|
+
|
117
|
+
private void appendObject(IRubyObject object) {
|
118
|
+
appendObject(object, null);
|
119
|
+
}
|
120
|
+
|
121
|
+
private void appendObject(IRubyObject object, IRubyObject destination) {
|
122
|
+
if (object == null || object instanceof RubyNil) {
|
123
|
+
ensureRemainingCapacity(1);
|
124
|
+
buffer.put(NIL);
|
125
|
+
} else if (object instanceof RubyBoolean) {
|
126
|
+
ensureRemainingCapacity(1);
|
127
|
+
buffer.put(((RubyBoolean) object).isTrue() ? TRUE : FALSE);
|
128
|
+
} else if (object instanceof RubyBignum) {
|
129
|
+
appendBignum((RubyBignum) object);
|
130
|
+
} else if (object instanceof RubyInteger) {
|
131
|
+
appendInteger((RubyInteger) object);
|
132
|
+
} else if (object instanceof RubyFloat) {
|
133
|
+
appendFloat((RubyFloat) object);
|
134
|
+
} else if (object instanceof RubyString) {
|
135
|
+
if (object.getType() == runtime.getString() || !tryAppendWithExtTypeLookup(object)) {
|
136
|
+
appendString((RubyString) object);
|
137
|
+
}
|
138
|
+
} else if (object instanceof RubySymbol) {
|
139
|
+
if (hasSymbolExtType) {
|
140
|
+
appendOther(object, destination);
|
141
|
+
} else {
|
142
|
+
appendString(((RubySymbol) object).asString());
|
143
|
+
}
|
144
|
+
} else if (object instanceof RubyArray) {
|
145
|
+
if (object.getType() == runtime.getArray() || !tryAppendWithExtTypeLookup(object)) {
|
146
|
+
appendArray((RubyArray) object);
|
147
|
+
}
|
148
|
+
} else if (object instanceof RubyHash) {
|
149
|
+
if (object.getType() == runtime.getHash() || !tryAppendWithExtTypeLookup(object)) {
|
150
|
+
appendHash((RubyHash) object);
|
151
|
+
}
|
152
|
+
} else if (object instanceof ExtensionValue) {
|
153
|
+
appendExtensionValue((ExtensionValue) object);
|
154
|
+
} else {
|
155
|
+
appendOther(object, destination);
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
private void appendBignum(RubyBignum object) {
|
160
|
+
BigInteger value = object.getBigIntegerValue();
|
161
|
+
if (value.compareTo(RubyBignum.LONG_MIN) < 0 || value.compareTo(RubyBignum.LONG_MAX) > 0) {
|
162
|
+
if (value.bitLength() > 64 || (value.bitLength() > 63 && value.signum() < 0)) {
|
163
|
+
if (hasBigintExtType && tryAppendWithExtTypeLookup(object)) {
|
164
|
+
return;
|
165
|
+
}
|
166
|
+
throw runtime.newRangeError(String.format("Cannot pack big integer: %s", value));
|
167
|
+
}
|
168
|
+
ensureRemainingCapacity(9);
|
169
|
+
buffer.put(value.signum() < 0 ? INT64 : UINT64);
|
170
|
+
byte[] b = value.toByteArray();
|
171
|
+
buffer.put(b, b.length - 8, 8);
|
172
|
+
} else {
|
173
|
+
appendInteger(object);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
private void appendInteger(RubyInteger object) {
|
178
|
+
long value = object.getLongValue();
|
179
|
+
if (value < 0) {
|
180
|
+
if (value < Short.MIN_VALUE) {
|
181
|
+
if (value < Integer.MIN_VALUE) {
|
182
|
+
ensureRemainingCapacity(9);
|
183
|
+
buffer.put(INT64);
|
184
|
+
buffer.putLong(value);
|
185
|
+
} else {
|
186
|
+
ensureRemainingCapacity(5);
|
187
|
+
buffer.put(INT32);
|
188
|
+
buffer.putInt((int) value);
|
189
|
+
}
|
190
|
+
} else if (value >= -0x20L) {
|
191
|
+
ensureRemainingCapacity(1);
|
192
|
+
byte b = (byte) (value | 0xe0);
|
193
|
+
buffer.put(b);
|
194
|
+
} else if (value < Byte.MIN_VALUE) {
|
195
|
+
ensureRemainingCapacity(3);
|
196
|
+
buffer.put(INT16);
|
197
|
+
buffer.putShort((short) value);
|
198
|
+
} else {
|
199
|
+
ensureRemainingCapacity(2);
|
200
|
+
buffer.put(INT8);
|
201
|
+
buffer.put((byte) value);
|
202
|
+
}
|
203
|
+
} else {
|
204
|
+
if (value < 0x10000L) {
|
205
|
+
if (value < 128L) {
|
206
|
+
ensureRemainingCapacity(1);
|
207
|
+
buffer.put((byte) value);
|
208
|
+
} else if (value < 0x100L) {
|
209
|
+
ensureRemainingCapacity(2);
|
210
|
+
buffer.put(UINT8);
|
211
|
+
buffer.put((byte) value);
|
212
|
+
} else {
|
213
|
+
ensureRemainingCapacity(3);
|
214
|
+
buffer.put(UINT16);
|
215
|
+
buffer.putShort((short) value);
|
216
|
+
}
|
217
|
+
} else if (value < 0x100000000L) {
|
218
|
+
ensureRemainingCapacity(5);
|
219
|
+
buffer.put(UINT32);
|
220
|
+
buffer.putInt((int) value);
|
221
|
+
} else {
|
222
|
+
ensureRemainingCapacity(9);
|
223
|
+
buffer.put(INT64);
|
224
|
+
buffer.putLong(value);
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
private void appendFloat(RubyFloat object) {
|
230
|
+
double value = object.getDoubleValue();
|
231
|
+
//TODO: msgpack-ruby original does encode this value as Double, not float
|
232
|
+
// float f = (float) value;
|
233
|
+
// if (Double.compare(f, value) == 0) {
|
234
|
+
// ensureRemainingCapacity(5);
|
235
|
+
// buffer.put(FLOAT32);
|
236
|
+
// buffer.putFloat(f);
|
237
|
+
// } else {
|
238
|
+
ensureRemainingCapacity(9);
|
239
|
+
buffer.put(FLOAT64);
|
240
|
+
buffer.putDouble(value);
|
241
|
+
// }
|
242
|
+
}
|
243
|
+
|
244
|
+
private void appendFloat32(RubyNumeric object) {
|
245
|
+
float value = (float) object.getDoubleValue();
|
246
|
+
ensureRemainingCapacity(5);
|
247
|
+
buffer.put(FLOAT32);
|
248
|
+
buffer.putFloat(value);
|
249
|
+
}
|
250
|
+
|
251
|
+
private void appendStringHeader(int length, boolean binary) {
|
252
|
+
if (length < 32 && !binary) {
|
253
|
+
ensureRemainingCapacity(1 + length);
|
254
|
+
buffer.put((byte) (length | FIXSTR));
|
255
|
+
} else if (length <= 0xff && !compatibilityMode) {
|
256
|
+
ensureRemainingCapacity(2 + length);
|
257
|
+
buffer.put(binary ? BIN8 : STR8);
|
258
|
+
buffer.put((byte) length);
|
259
|
+
} else if (length <= 0xffff) {
|
260
|
+
ensureRemainingCapacity(3 + length);
|
261
|
+
buffer.put(binary ? BIN16 : STR16);
|
262
|
+
buffer.putShort((short) length);
|
263
|
+
} else {
|
264
|
+
ensureRemainingCapacity(5 + length);
|
265
|
+
buffer.put(binary ? BIN32 : STR32);
|
266
|
+
buffer.putInt(length);
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
private void appendString(RubyString object) {
|
271
|
+
Encoding encoding = object.getEncoding();
|
272
|
+
boolean binary = !compatibilityMode && encoding == binaryEncoding;
|
273
|
+
if (encoding != utf8Encoding && encoding != binaryEncoding) {
|
274
|
+
object = (RubyString)(object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding));
|
275
|
+
}
|
276
|
+
ByteList bytes = object.getByteList();
|
277
|
+
int length = bytes.length();
|
278
|
+
appendStringHeader(length, binary);
|
279
|
+
buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
|
280
|
+
}
|
281
|
+
|
282
|
+
private void appendArray(RubyArray<?> object) {
|
283
|
+
appendArrayHeader(object);
|
284
|
+
appendArrayElements(object);
|
285
|
+
}
|
286
|
+
|
287
|
+
private void appendArrayHeader(RubyArray<?> object) {
|
288
|
+
appendArrayHeader(object.size());
|
289
|
+
}
|
290
|
+
|
291
|
+
private void appendArrayHeader(int size) {
|
292
|
+
if (size < 16) {
|
293
|
+
ensureRemainingCapacity(1);
|
294
|
+
buffer.put((byte) (size | 0x90));
|
295
|
+
} else if (size < 0x10000) {
|
296
|
+
ensureRemainingCapacity(3);
|
297
|
+
buffer.put(ARY16);
|
298
|
+
buffer.putShort((short) size);
|
299
|
+
} else {
|
300
|
+
ensureRemainingCapacity(5);
|
301
|
+
buffer.put(ARY32);
|
302
|
+
buffer.putInt(size);
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
private void appendArrayElements(RubyArray<?> object) {
|
307
|
+
int size = object.size();
|
308
|
+
for (int i = 0; i < size; i++) {
|
309
|
+
appendObject(object.eltOk(i));
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
private void appendHash(RubyHash object) {
|
314
|
+
appendHashHeader(object);
|
315
|
+
appendHashElements(object);
|
316
|
+
}
|
317
|
+
|
318
|
+
private void appendHashHeader(RubyHash object) {
|
319
|
+
appendHashHeader(object.size());
|
320
|
+
}
|
321
|
+
|
322
|
+
private void appendHashHeader(int size) {
|
323
|
+
if (size < 16) {
|
324
|
+
ensureRemainingCapacity(1);
|
325
|
+
buffer.put((byte) (size | 0x80));
|
326
|
+
} else if (size < 0x10000) {
|
327
|
+
ensureRemainingCapacity(3);
|
328
|
+
buffer.put(MAP16);
|
329
|
+
buffer.putShort((short) size);
|
330
|
+
} else {
|
331
|
+
ensureRemainingCapacity(5);
|
332
|
+
buffer.put(MAP32);
|
333
|
+
buffer.putInt(size);
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
private void appendHashElements(RubyHash object) {
|
338
|
+
int size = object.size();
|
339
|
+
HashVisitor visitor = new HashVisitor(size);
|
340
|
+
object.visitAll(visitor);
|
341
|
+
if (visitor.remain != 0) {
|
342
|
+
object.getRuntime().newConcurrencyError("Hash size changed while packing");
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
private class HashVisitor extends RubyHash.Visitor {
|
347
|
+
public int remain;
|
348
|
+
|
349
|
+
public HashVisitor(int size) {
|
350
|
+
remain = size;
|
351
|
+
}
|
352
|
+
|
353
|
+
public void visit(IRubyObject key, IRubyObject value) {
|
354
|
+
if (remain-- > 0) {
|
355
|
+
appendObject(key);
|
356
|
+
appendObject(value);
|
357
|
+
}
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
private void appendExt(int type, ByteList payloadBytes) {
|
362
|
+
int payloadSize = payloadBytes.length();
|
363
|
+
int outputSize = 0;
|
364
|
+
boolean fixSize = payloadSize == 1 || payloadSize == 2 || payloadSize == 4 || payloadSize == 8 || payloadSize == 16;
|
365
|
+
if (fixSize) {
|
366
|
+
outputSize = 2 + payloadSize;
|
367
|
+
} else if (payloadSize < 0x100) {
|
368
|
+
outputSize = 3 + payloadSize;
|
369
|
+
} else if (payloadSize < 0x10000) {
|
370
|
+
outputSize = 4 + payloadSize;
|
371
|
+
} else {
|
372
|
+
outputSize = 6 + payloadSize;
|
373
|
+
}
|
374
|
+
ensureRemainingCapacity(outputSize);
|
375
|
+
if (payloadSize == 1) {
|
376
|
+
buffer.put(FIXEXT1);
|
377
|
+
} else if (payloadSize == 2) {
|
378
|
+
buffer.put(FIXEXT2);
|
379
|
+
} else if (payloadSize == 4) {
|
380
|
+
buffer.put(FIXEXT4);
|
381
|
+
} else if (payloadSize == 8) {
|
382
|
+
buffer.put(FIXEXT8);
|
383
|
+
} else if (payloadSize == 16) {
|
384
|
+
buffer.put(FIXEXT16);
|
385
|
+
} else if (payloadSize < 0x100) {
|
386
|
+
buffer.put(VAREXT8);
|
387
|
+
buffer.put((byte) payloadSize);
|
388
|
+
} else if (payloadSize < 0x10000) {
|
389
|
+
buffer.put(VAREXT16);
|
390
|
+
buffer.putShort((short) payloadSize);
|
391
|
+
} else {
|
392
|
+
buffer.put(VAREXT32);
|
393
|
+
buffer.putInt(payloadSize);
|
394
|
+
}
|
395
|
+
buffer.put((byte) type);
|
396
|
+
buffer.put(payloadBytes.unsafeBytes(), payloadBytes.begin(), payloadSize);
|
397
|
+
}
|
398
|
+
|
399
|
+
private void appendExtensionValue(ExtensionValue object) {
|
400
|
+
long type = ((RubyFixnum)object.get_type()).getLongValue();
|
401
|
+
if (type < -128 || type > 127) {
|
402
|
+
throw object.getRuntime().newRangeError(String.format("integer %d too big to convert to `signed char'", type));
|
403
|
+
}
|
404
|
+
ByteList payloadBytes = ((RubyString)object.payload()).getByteList();
|
405
|
+
appendExt((int) type, payloadBytes);
|
406
|
+
}
|
407
|
+
|
408
|
+
private boolean tryAppendWithExtTypeLookup(IRubyObject object) {
|
409
|
+
if (registry != null) {
|
410
|
+
ExtensionRegistry.ExtensionEntry entry = registry.lookupExtensionForObject(object);
|
411
|
+
if (entry != null) {
|
412
|
+
IRubyObject proc = entry.getPackerProc();
|
413
|
+
int type = entry.getTypeId();
|
414
|
+
|
415
|
+
if (entry.isRecursive()) {
|
416
|
+
ByteBuffer oldBuffer = buffer;
|
417
|
+
buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
|
418
|
+
boolean previousRecursiveExtension = recursiveExtension;
|
419
|
+
recursiveExtension = true;
|
420
|
+
|
421
|
+
ByteList payload;
|
422
|
+
try {
|
423
|
+
IRubyObject args[] = { object, packer };
|
424
|
+
proc.callMethod(runtime.getCurrentContext(), "call", args);
|
425
|
+
payload = new ByteList(buffer.array(), 0, buffer.position(), binaryEncoding, false);
|
426
|
+
} finally {
|
427
|
+
recursiveExtension = previousRecursiveExtension;
|
428
|
+
buffer = oldBuffer;
|
429
|
+
}
|
430
|
+
appendExt(type, payload);
|
431
|
+
} else {
|
432
|
+
RubyString bytes = proc.callMethod(runtime.getCurrentContext(), "call", object).asString();
|
433
|
+
appendExt(type, bytes.getByteList());
|
434
|
+
}
|
435
|
+
return true;
|
436
|
+
}
|
437
|
+
}
|
438
|
+
return false;
|
439
|
+
}
|
440
|
+
|
441
|
+
private void appendOther(IRubyObject object, IRubyObject destination) {
|
442
|
+
if (!tryAppendWithExtTypeLookup(object)) { appendCustom(object, destination); }
|
443
|
+
}
|
444
|
+
|
445
|
+
private void appendCustom(IRubyObject object, IRubyObject destination) {
|
446
|
+
if (destination == null) {
|
447
|
+
IRubyObject result = object.callMethod(runtime.getCurrentContext(), "to_msgpack");
|
448
|
+
ByteList bytes = result.asString().getByteList();
|
449
|
+
int length = bytes.length();
|
450
|
+
ensureRemainingCapacity(length);
|
451
|
+
buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
|
452
|
+
} else {
|
453
|
+
object.callMethod(runtime.getCurrentContext(), "to_msgpack", destination);
|
454
|
+
}
|
455
|
+
}
|
456
|
+
}
|
@@ -0,0 +1,167 @@
|
|
1
|
+
package org.msgpack.jruby;
|
2
|
+
|
3
|
+
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyHash;
|
5
|
+
import org.jruby.RubyArray;
|
6
|
+
import org.jruby.RubyModule;
|
7
|
+
import org.jruby.RubyFixnum;
|
8
|
+
import org.jruby.RubySymbol;
|
9
|
+
import org.jruby.runtime.ThreadContext;
|
10
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
11
|
+
|
12
|
+
import java.util.Map;
|
13
|
+
import java.util.HashMap;
|
14
|
+
|
15
|
+
public class ExtensionRegistry {
|
16
|
+
private final Map<RubyModule, ExtensionEntry> extensionsByModule;
|
17
|
+
private final Map<RubyModule, ExtensionEntry> extensionsByAncestor;
|
18
|
+
private final ExtensionEntry[] extensionsByTypeId;
|
19
|
+
|
20
|
+
public ExtensionRegistry() {
|
21
|
+
this(new HashMap<RubyModule, ExtensionEntry>(), new ExtensionEntry[256]);
|
22
|
+
}
|
23
|
+
|
24
|
+
private ExtensionRegistry(Map<RubyModule, ExtensionEntry> extensionsByModule, ExtensionEntry[] extensionsByTypeId) {
|
25
|
+
this.extensionsByModule = new HashMap<RubyModule, ExtensionEntry>(extensionsByModule);
|
26
|
+
this.extensionsByAncestor = new HashMap<RubyModule, ExtensionEntry>();
|
27
|
+
this.extensionsByTypeId = extensionsByTypeId.clone();
|
28
|
+
}
|
29
|
+
|
30
|
+
public ExtensionRegistry dup() {
|
31
|
+
return new ExtensionRegistry(extensionsByModule, extensionsByTypeId);
|
32
|
+
}
|
33
|
+
|
34
|
+
public IRubyObject toInternalPackerRegistry(ThreadContext ctx) {
|
35
|
+
RubyHash hash = RubyHash.newHash(ctx.runtime);
|
36
|
+
for (RubyModule extensionModule : extensionsByModule.keySet()) {
|
37
|
+
ExtensionEntry entry = extensionsByModule.get(extensionModule);
|
38
|
+
if (entry.hasPacker()) {
|
39
|
+
hash.put(extensionModule, entry.toPackerTuple(ctx));
|
40
|
+
}
|
41
|
+
}
|
42
|
+
return hash;
|
43
|
+
}
|
44
|
+
|
45
|
+
public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) {
|
46
|
+
RubyHash hash = RubyHash.newHash(ctx.runtime);
|
47
|
+
for (int typeIdIndex = 0 ; typeIdIndex < 256 ; typeIdIndex++) {
|
48
|
+
ExtensionEntry entry = extensionsByTypeId[typeIdIndex];
|
49
|
+
if (entry != null && entry.hasUnpacker()) {
|
50
|
+
IRubyObject typeId = RubyFixnum.newFixnum(ctx.runtime, typeIdIndex - 128);
|
51
|
+
hash.put(typeId, entry.toUnpackerTuple(ctx));
|
52
|
+
}
|
53
|
+
}
|
54
|
+
return hash;
|
55
|
+
}
|
56
|
+
|
57
|
+
public void put(RubyModule mod, int typeId, boolean recursive, IRubyObject packerProc, IRubyObject unpackerProc) {
|
58
|
+
ExtensionEntry entry = new ExtensionEntry(mod, typeId, recursive, packerProc, unpackerProc);
|
59
|
+
extensionsByModule.put(mod, entry);
|
60
|
+
extensionsByTypeId[typeId + 128] = entry;
|
61
|
+
extensionsByAncestor.clear();
|
62
|
+
}
|
63
|
+
|
64
|
+
public ExtensionEntry lookupExtensionByTypeId(int typeId) {
|
65
|
+
ExtensionEntry e = extensionsByTypeId[typeId + 128];
|
66
|
+
if (e != null && e.hasUnpacker()) {
|
67
|
+
return e;
|
68
|
+
}
|
69
|
+
return null;
|
70
|
+
}
|
71
|
+
|
72
|
+
public ExtensionEntry lookupExtensionForObject(IRubyObject object) {
|
73
|
+
RubyModule lookupClass = null;
|
74
|
+
ExtensionEntry entry = null;
|
75
|
+
/*
|
76
|
+
* Objects of type Integer (Fixnum, Bignum), Float, Symbol and frozen
|
77
|
+
* String have no singleton class and raise a TypeError when trying to get
|
78
|
+
* it.
|
79
|
+
*/
|
80
|
+
lookupClass = object.getMetaClass();
|
81
|
+
entry = extensionsByModule.get(lookupClass);
|
82
|
+
if (entry != null && entry.hasPacker()) {
|
83
|
+
return entry;
|
84
|
+
}
|
85
|
+
|
86
|
+
RubyModule realClass = object.getType();
|
87
|
+
if (realClass != lookupClass) {
|
88
|
+
entry = extensionsByModule.get(realClass);
|
89
|
+
if (entry != null && entry.hasPacker()) {
|
90
|
+
return entry;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
entry = findEntryByModuleOrAncestor(lookupClass);
|
95
|
+
if (entry != null && entry.hasPacker()) {
|
96
|
+
return entry;
|
97
|
+
}
|
98
|
+
return null;
|
99
|
+
}
|
100
|
+
|
101
|
+
private ExtensionEntry findEntryByModuleOrAncestor(final RubyModule mod) {
|
102
|
+
ThreadContext ctx = mod.getRuntime().getCurrentContext();
|
103
|
+
for (RubyModule extensionModule : extensionsByModule.keySet()) {
|
104
|
+
RubyArray<?> ancestors = (RubyArray)mod.callMethod(ctx, "ancestors");
|
105
|
+
if (ancestors.callMethod(ctx, "include?", extensionModule).isTrue()) {
|
106
|
+
return extensionsByModule.get(extensionModule);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
return null;
|
110
|
+
}
|
111
|
+
|
112
|
+
public static class ExtensionEntry {
|
113
|
+
private final RubyModule mod;
|
114
|
+
private final int typeId;
|
115
|
+
private final boolean recursive;
|
116
|
+
private final IRubyObject packerProc;
|
117
|
+
private final IRubyObject unpackerProc;
|
118
|
+
|
119
|
+
public ExtensionEntry(RubyModule mod, int typeId, boolean recursive, IRubyObject packerProc, IRubyObject unpackerProc) {
|
120
|
+
this.mod = mod;
|
121
|
+
this.typeId = typeId;
|
122
|
+
this.recursive = recursive;
|
123
|
+
this.packerProc = packerProc;
|
124
|
+
this.unpackerProc = unpackerProc;
|
125
|
+
}
|
126
|
+
|
127
|
+
public RubyModule getExtensionModule() {
|
128
|
+
return mod;
|
129
|
+
}
|
130
|
+
|
131
|
+
public int getTypeId() {
|
132
|
+
return typeId;
|
133
|
+
}
|
134
|
+
|
135
|
+
public boolean isRecursive() {
|
136
|
+
return recursive;
|
137
|
+
}
|
138
|
+
|
139
|
+
public boolean hasPacker() {
|
140
|
+
return packerProc != null && !packerProc.isNil();
|
141
|
+
}
|
142
|
+
|
143
|
+
public boolean hasUnpacker() {
|
144
|
+
return unpackerProc != null && !unpackerProc.isNil();
|
145
|
+
}
|
146
|
+
|
147
|
+
public IRubyObject getPackerProc() {
|
148
|
+
return packerProc;
|
149
|
+
}
|
150
|
+
|
151
|
+
public IRubyObject getUnpackerProc() {
|
152
|
+
return unpackerProc;
|
153
|
+
}
|
154
|
+
|
155
|
+
public RubyArray<?> toPackerTuple(ThreadContext ctx) {
|
156
|
+
return ctx.runtime.newArray(new IRubyObject[] {ctx.runtime.newFixnum(typeId), packerProc});
|
157
|
+
}
|
158
|
+
|
159
|
+
public RubyArray<?> toUnpackerTuple(ThreadContext ctx) {
|
160
|
+
return ctx.runtime.newArray(new IRubyObject[] {mod, unpackerProc});
|
161
|
+
}
|
162
|
+
|
163
|
+
public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) {
|
164
|
+
return new IRubyObject[] {packerProc, ctx.runtime.newFixnum(typeId)};
|
165
|
+
}
|
166
|
+
}
|
167
|
+
}
|