msgpack 1.4.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +57 -0
  3. data/ChangeLog +60 -0
  4. data/README.md +25 -1
  5. data/Rakefile +1 -2
  6. data/bench/bench.rb +78 -0
  7. data/bin/console +8 -0
  8. data/doclib/msgpack/factory.rb +47 -3
  9. data/doclib/msgpack/packer.rb +5 -4
  10. data/doclib/msgpack/unpacker.rb +2 -2
  11. data/ext/java/org/msgpack/jruby/Buffer.java +23 -16
  12. data/ext/java/org/msgpack/jruby/Decoder.java +29 -21
  13. data/ext/java/org/msgpack/jruby/Encoder.java +68 -30
  14. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +37 -49
  15. data/ext/java/org/msgpack/jruby/ExtensionValue.java +5 -8
  16. data/ext/java/org/msgpack/jruby/Factory.java +47 -7
  17. data/ext/java/org/msgpack/jruby/Packer.java +29 -17
  18. data/ext/java/org/msgpack/jruby/Unpacker.java +66 -42
  19. data/ext/msgpack/buffer.c +38 -57
  20. data/ext/msgpack/buffer.h +19 -10
  21. data/ext/msgpack/buffer_class.c +90 -52
  22. data/ext/msgpack/compat.h +0 -99
  23. data/ext/msgpack/extconf.rb +9 -22
  24. data/ext/msgpack/factory_class.c +133 -43
  25. data/ext/msgpack/packer.c +60 -36
  26. data/ext/msgpack/packer.h +27 -18
  27. data/ext/msgpack/packer_class.c +84 -77
  28. data/ext/msgpack/packer_class.h +11 -0
  29. data/ext/msgpack/packer_ext_registry.c +24 -32
  30. data/ext/msgpack/packer_ext_registry.h +40 -33
  31. data/ext/msgpack/sysdep.h +5 -2
  32. data/ext/msgpack/unpacker.c +128 -97
  33. data/ext/msgpack/unpacker.h +17 -10
  34. data/ext/msgpack/unpacker_class.c +75 -80
  35. data/ext/msgpack/unpacker_class.h +11 -0
  36. data/ext/msgpack/unpacker_ext_registry.c +42 -18
  37. data/ext/msgpack/unpacker_ext_registry.h +23 -16
  38. data/lib/msgpack/bigint.rb +69 -0
  39. data/lib/msgpack/factory.rb +103 -0
  40. data/lib/msgpack/symbol.rb +21 -4
  41. data/lib/msgpack/time.rb +1 -1
  42. data/lib/msgpack/version.rb +1 -1
  43. data/lib/msgpack.rb +5 -7
  44. data/msgpack.gemspec +2 -2
  45. data/spec/bigint_spec.rb +26 -0
  46. data/spec/cruby/buffer_spec.rb +17 -0
  47. data/spec/factory_spec.rb +351 -12
  48. data/spec/msgpack_spec.rb +1 -1
  49. data/spec/packer_spec.rb +18 -0
  50. data/spec/spec_helper.rb +20 -3
  51. data/spec/timestamp_spec.rb +38 -0
  52. data/spec/unpacker_spec.rb +54 -4
  53. metadata +25 -41
  54. data/.travis.yml +0 -39
  55. data/bench/pack.rb +0 -23
  56. data/bench/pack_log.rb +0 -33
  57. data/bench/pack_log_long.rb +0 -65
  58. data/bench/pack_symbols.rb +0 -28
  59. data/bench/run.sh +0 -14
  60. data/bench/run_long.sh +0 -35
  61. data/bench/run_symbols.sh +0 -26
  62. data/bench/unpack.rb +0 -21
  63. data/bench/unpack_log.rb +0 -34
  64. data/bench/unpack_log_long.rb +0 -67
@@ -38,12 +38,16 @@ public class Encoder {
38
38
  private final Encoding utf8Encoding;
39
39
  private final boolean compatibilityMode;
40
40
  private final ExtensionRegistry registry;
41
+ private final Packer packer;
41
42
 
42
43
  public boolean hasSymbolExtType;
44
+ private boolean hasBigintExtType;
45
+ private boolean recursiveExtension;
43
46
 
44
47
  private ByteBuffer buffer;
45
48
 
46
- public Encoder(Ruby runtime, boolean compatibilityMode, ExtensionRegistry registry, boolean hasSymbolExtType) {
49
+ public Encoder(Ruby runtime, Packer packer, boolean compatibilityMode, ExtensionRegistry registry, boolean hasSymbolExtType, boolean hasBigintExtType) {
50
+ this.packer = packer;
47
51
  this.runtime = runtime;
48
52
  this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
49
53
  this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
@@ -51,6 +55,7 @@ public class Encoder {
51
55
  this.compatibilityMode = compatibilityMode;
52
56
  this.registry = registry;
53
57
  this.hasSymbolExtType = hasSymbolExtType;
58
+ this.hasBigintExtType = hasBigintExtType;
54
59
  }
55
60
 
56
61
  public boolean isCompatibilityMode() {
@@ -66,9 +71,17 @@ public class Encoder {
66
71
  }
67
72
 
68
73
  private IRubyObject readRubyString() {
69
- IRubyObject str = runtime.newString(new ByteList(buffer.array(), 0, buffer.position(), binaryEncoding, false));
70
- buffer.clear();
71
- return str;
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
+ }
72
85
  }
73
86
 
74
87
  public IRubyObject encode(IRubyObject object) {
@@ -119,7 +132,9 @@ public class Encoder {
119
132
  } else if (object instanceof RubyFloat) {
120
133
  appendFloat((RubyFloat) object);
121
134
  } else if (object instanceof RubyString) {
122
- appendString((RubyString) object);
135
+ if (object.getType() == runtime.getString() || !tryAppendWithExtTypeLookup(object)) {
136
+ appendString((RubyString) object);
137
+ }
123
138
  } else if (object instanceof RubySymbol) {
124
139
  if (hasSymbolExtType) {
125
140
  appendOther(object, destination);
@@ -127,9 +142,13 @@ public class Encoder {
127
142
  appendString(((RubySymbol) object).asString());
128
143
  }
129
144
  } else if (object instanceof RubyArray) {
130
- appendArray((RubyArray) object);
145
+ if (object.getType() == runtime.getArray() || !tryAppendWithExtTypeLookup(object)) {
146
+ appendArray((RubyArray) object);
147
+ }
131
148
  } else if (object instanceof RubyHash) {
132
- appendHash((RubyHash) object);
149
+ if (object.getType() == runtime.getHash() || !tryAppendWithExtTypeLookup(object)) {
150
+ appendHash((RubyHash) object);
151
+ }
133
152
  } else if (object instanceof ExtensionValue) {
134
153
  appendExtensionValue((ExtensionValue) object);
135
154
  } else {
@@ -141,7 +160,10 @@ public class Encoder {
141
160
  BigInteger value = object.getBigIntegerValue();
142
161
  if (value.compareTo(RubyBignum.LONG_MIN) < 0 || value.compareTo(RubyBignum.LONG_MAX) > 0) {
143
162
  if (value.bitLength() > 64 || (value.bitLength() > 63 && value.signum() < 0)) {
144
- throw runtime.newArgumentError(String.format("Cannot pack big integer: %s", value));
163
+ if (hasBigintExtType && tryAppendWithExtTypeLookup(object)) {
164
+ return;
165
+ }
166
+ throw runtime.newRangeError(String.format("Cannot pack big integer: %s", value));
145
167
  }
146
168
  ensureRemainingCapacity(9);
147
169
  buffer.put(value.signum() < 0 ? INT64 : UINT64);
@@ -153,7 +175,7 @@ public class Encoder {
153
175
  }
154
176
 
155
177
  private void appendInteger(RubyInteger object) {
156
- long value = ((RubyInteger) object).getLongValue();
178
+ long value = object.getLongValue();
157
179
  if (value < 0) {
158
180
  if (value < Short.MIN_VALUE) {
159
181
  if (value < Integer.MIN_VALUE) {
@@ -241,7 +263,7 @@ public class Encoder {
241
263
  } else {
242
264
  ensureRemainingCapacity(5 + length);
243
265
  buffer.put(binary ? BIN32 : STR32);
244
- buffer.putInt((int) length);
266
+ buffer.putInt(length);
245
267
  }
246
268
  }
247
269
 
@@ -249,7 +271,7 @@ public class Encoder {
249
271
  Encoding encoding = object.getEncoding();
250
272
  boolean binary = !compatibilityMode && encoding == binaryEncoding;
251
273
  if (encoding != utf8Encoding && encoding != binaryEncoding) {
252
- object = (RubyString) ((RubyString) object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding));
274
+ object = (RubyString)(object).encode(runtime.getCurrentContext(), runtime.getEncodingService().getEncoding(utf8Encoding));
253
275
  }
254
276
  ByteList bytes = object.getByteList();
255
277
  int length = bytes.length();
@@ -257,12 +279,12 @@ public class Encoder {
257
279
  buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
258
280
  }
259
281
 
260
- private void appendArray(RubyArray object) {
282
+ private void appendArray(RubyArray<?> object) {
261
283
  appendArrayHeader(object);
262
284
  appendArrayElements(object);
263
285
  }
264
286
 
265
- private void appendArrayHeader(RubyArray object) {
287
+ private void appendArrayHeader(RubyArray<?> object) {
266
288
  appendArrayHeader(object.size());
267
289
  }
268
290
 
@@ -281,7 +303,7 @@ public class Encoder {
281
303
  }
282
304
  }
283
305
 
284
- private void appendArrayElements(RubyArray object) {
306
+ private void appendArrayElements(RubyArray<?> object) {
285
307
  int size = object.size();
286
308
  for (int i = 0; i < size; i++) {
287
309
  appendObject(object.eltOk(i));
@@ -383,25 +405,41 @@ public class Encoder {
383
405
  appendExt((int) type, payloadBytes);
384
406
  }
385
407
 
386
- private void appendOther(IRubyObject object, IRubyObject destination) {
408
+ private boolean tryAppendWithExtTypeLookup(IRubyObject object) {
387
409
  if (registry != null) {
388
- RubyModule lookupClass;
389
-
390
- if (object.getType() == runtime.getSymbol()) {
391
- lookupClass = object.getType();
392
- } else {
393
- lookupClass = object.getSingletonClass();
394
- }
395
-
396
- IRubyObject[] pair = registry.lookupPackerForObject(object);
397
- if (pair != null) {
398
- RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString();
399
- int type = (int) ((RubyFixnum) pair[1]).getLongValue();
400
- appendExt(type, bytes.getByteList());
401
- return;
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;
402
436
  }
403
437
  }
404
- appendCustom(object, destination);
438
+ return false;
439
+ }
440
+
441
+ private void appendOther(IRubyObject object, IRubyObject destination) {
442
+ if (!tryAppendWithExtTypeLookup(object)) { appendCustom(object, destination); }
405
443
  }
406
444
 
407
445
  private void appendCustom(IRubyObject object, IRubyObject destination) {
@@ -37,7 +37,7 @@ public class ExtensionRegistry {
37
37
  }
38
38
 
39
39
  public IRubyObject toInternalPackerRegistry(ThreadContext ctx) {
40
- RubyHash hash = RubyHash.newHash(ctx.getRuntime());
40
+ RubyHash hash = RubyHash.newHash(ctx.runtime);
41
41
  for (RubyModule extensionModule : extensionsByModule.keySet()) {
42
42
  ExtensionEntry entry = extensionsByModule.get(extensionModule);
43
43
  if (entry.hasPacker()) {
@@ -48,75 +48,57 @@ public class ExtensionRegistry {
48
48
  }
49
49
 
50
50
  public IRubyObject toInternalUnpackerRegistry(ThreadContext ctx) {
51
- RubyHash hash = RubyHash.newHash(ctx.getRuntime());
51
+ RubyHash hash = RubyHash.newHash(ctx.runtime);
52
52
  for (int typeIdIndex = 0 ; typeIdIndex < 256 ; typeIdIndex++) {
53
53
  ExtensionEntry entry = extensionsByTypeId[typeIdIndex];
54
54
  if (entry != null && entry.hasUnpacker()) {
55
- IRubyObject typeId = RubyFixnum.newFixnum(ctx.getRuntime(), typeIdIndex - 128);
55
+ IRubyObject typeId = RubyFixnum.newFixnum(ctx.runtime, typeIdIndex - 128);
56
56
  hash.put(typeId, entry.toUnpackerTuple(ctx));
57
57
  }
58
58
  }
59
59
  return hash;
60
60
  }
61
61
 
62
- public void put(RubyModule mod, int typeId, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) {
63
- ExtensionEntry entry = new ExtensionEntry(mod, typeId, packerProc, packerArg, unpackerProc, unpackerArg);
62
+ public void put(RubyModule mod, int typeId, boolean recursive, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) {
63
+ ExtensionEntry entry = new ExtensionEntry(mod, typeId, recursive, packerProc, packerArg, unpackerProc, unpackerArg);
64
64
  extensionsByModule.put(mod, entry);
65
65
  extensionsByTypeId[typeId + 128] = entry;
66
66
  extensionsByAncestor.clear();
67
67
  }
68
68
 
69
- public IRubyObject lookupUnpackerByTypeId(int typeId) {
69
+ public ExtensionEntry lookupExtensionByTypeId(int typeId) {
70
70
  ExtensionEntry e = extensionsByTypeId[typeId + 128];
71
71
  if (e != null && e.hasUnpacker()) {
72
- return e.getUnpackerProc();
73
- } else {
74
- return null;
72
+ return e;
75
73
  }
74
+ return null;
76
75
  }
77
76
 
78
- public IRubyObject[] lookupPackerForObject(IRubyObject object) {
77
+ public ExtensionEntry lookupExtensionForObject(IRubyObject object) {
79
78
  RubyModule lookupClass = null;
80
- IRubyObject[] pair;
79
+ ExtensionEntry entry = null;
81
80
  /*
82
81
  * Objects of type Integer (Fixnum, Bignum), Float, Symbol and frozen
83
82
  * String have no singleton class and raise a TypeError when trying to get
84
83
  * it.
85
- *
86
- * Since all but symbols are already filtered out when reaching this code
87
- * only symbols are checked here.
88
84
  */
89
- if (!(object instanceof RubySymbol)) {
90
- lookupClass = object.getSingletonClass();
91
- pair = fetchEntryByModule(lookupClass);
92
- if (pair != null) {
93
- return pair;
94
- }
85
+ lookupClass = object.getMetaClass();
86
+ entry = extensionsByModule.get(lookupClass);
87
+ if (entry != null && entry.hasPacker()) {
88
+ return entry;
95
89
  }
96
90
 
97
- pair = fetchEntryByModule(object.getType());
98
- if (pair != null) {
99
- return pair;
91
+ RubyModule realClass = object.getType();
92
+ if (realClass != lookupClass) {
93
+ entry = extensionsByModule.get(realClass);
94
+ if (entry != null && entry.hasPacker()) {
95
+ return entry;
96
+ }
100
97
  }
101
98
 
102
- if (lookupClass == null) {
103
- lookupClass = object.getType(); // only for Symbol
104
- }
105
- ExtensionEntry e = findEntryByModuleOrAncestor(lookupClass);
106
- if (e != null && e.hasPacker()) {
107
- extensionsByAncestor.put(e.getExtensionModule(), e);
108
- return e.toPackerProcTypeIdPair(lookupClass.getRuntime().getCurrentContext());
109
- }
110
- return null;
111
- }
112
-
113
- private IRubyObject[] fetchEntryByModule(final RubyModule mod) {
114
- ExtensionEntry e = extensionsByModule.get(mod);
115
- if (e == null) {
116
- e = extensionsByAncestor.get(mod);
117
- }
118
- if (e != null && e.hasPacker()) {
119
- return e.toPackerProcTypeIdPair(mod.getRuntime().getCurrentContext());
99
+ entry = findEntryByModuleOrAncestor(lookupClass);
100
+ if (entry != null && entry.hasPacker()) {
101
+ return entry;
120
102
  }
121
103
  return null;
122
104
  }
@@ -124,7 +106,7 @@ public class ExtensionRegistry {
124
106
  private ExtensionEntry findEntryByModuleOrAncestor(final RubyModule mod) {
125
107
  ThreadContext ctx = mod.getRuntime().getCurrentContext();
126
108
  for (RubyModule extensionModule : extensionsByModule.keySet()) {
127
- RubyArray ancestors = (RubyArray) mod.callMethod(ctx, "ancestors");
109
+ RubyArray<?> ancestors = (RubyArray)mod.callMethod(ctx, "ancestors");
128
110
  if (ancestors.callMethod(ctx, "include?", extensionModule).isTrue()) {
129
111
  return extensionsByModule.get(extensionModule);
130
112
  }
@@ -132,17 +114,19 @@ public class ExtensionRegistry {
132
114
  return null;
133
115
  }
134
116
 
135
- private static class ExtensionEntry {
117
+ public static class ExtensionEntry {
136
118
  private final RubyModule mod;
137
119
  private final int typeId;
120
+ private final boolean recursive;
138
121
  private final IRubyObject packerProc;
139
122
  private final IRubyObject packerArg;
140
123
  private final IRubyObject unpackerProc;
141
124
  private final IRubyObject unpackerArg;
142
125
 
143
- public ExtensionEntry(RubyModule mod, int typeId, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) {
126
+ public ExtensionEntry(RubyModule mod, int typeId, boolean recursive, IRubyObject packerProc, IRubyObject packerArg, IRubyObject unpackerProc, IRubyObject unpackerArg) {
144
127
  this.mod = mod;
145
128
  this.typeId = typeId;
129
+ this.recursive = recursive;
146
130
  this.packerProc = packerProc;
147
131
  this.packerArg = packerArg;
148
132
  this.unpackerProc = unpackerProc;
@@ -157,6 +141,10 @@ public class ExtensionRegistry {
157
141
  return typeId;
158
142
  }
159
143
 
144
+ public boolean isRecursive() {
145
+ return recursive;
146
+ }
147
+
160
148
  public boolean hasPacker() {
161
149
  return packerProc != null;
162
150
  }
@@ -173,16 +161,16 @@ public class ExtensionRegistry {
173
161
  return unpackerProc;
174
162
  }
175
163
 
176
- public RubyArray toPackerTuple(ThreadContext ctx) {
177
- return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {RubyFixnum.newFixnum(ctx.getRuntime(), typeId), packerProc, packerArg});
164
+ public RubyArray<?> toPackerTuple(ThreadContext ctx) {
165
+ return ctx.runtime.newArray(new IRubyObject[] {ctx.runtime.newFixnum(typeId), packerProc, packerArg});
178
166
  }
179
167
 
180
- public RubyArray toUnpackerTuple(ThreadContext ctx) {
181
- return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {mod, unpackerProc, unpackerArg});
168
+ public RubyArray<?> toUnpackerTuple(ThreadContext ctx) {
169
+ return ctx.runtime.newArray(new IRubyObject[] {mod, unpackerProc, unpackerArg});
182
170
  }
183
171
 
184
172
  public IRubyObject[] toPackerProcTypeIdPair(ThreadContext ctx) {
185
- return new IRubyObject[] {packerProc, RubyFixnum.newFixnum(ctx.getRuntime(), typeId)};
173
+ return new IRubyObject[] {packerProc, ctx.runtime.newFixnum(typeId)};
186
174
  }
187
175
  }
188
176
  }
@@ -25,6 +25,7 @@ import static org.msgpack.jruby.Types.*;
25
25
 
26
26
  @JRubyClass(name="MessagePack::ExtensionValue")
27
27
  public class ExtensionValue extends RubyObject {
28
+ private static final long serialVersionUID = 8451274621449322492L;
28
29
  private final Encoding binaryEncoding;
29
30
 
30
31
  private RubyFixnum type;
@@ -77,12 +78,10 @@ public class ExtensionValue extends RubyObject {
77
78
  }
78
79
  if (o instanceof ExtensionValue) {
79
80
  ExtensionValue other = (ExtensionValue) o;
80
- if (!this.type.eql_p(other.type).isTrue())
81
+ if (!this.type.eql_p(other.type).isTrue()) {
81
82
  return runtime.getFalse();
82
- if (runtime.is1_8()) {
83
- return this.payload.str_eql_p(ctx, other.payload);
84
83
  } else {
85
- return this.payload.str_eql_p19(ctx, other.payload);
84
+ return this.payload.str_eql_p(ctx, other.payload);
86
85
  }
87
86
  }
88
87
  return runtime.getFalse();
@@ -96,12 +95,10 @@ public class ExtensionValue extends RubyObject {
96
95
  }
97
96
  if (o instanceof ExtensionValue) {
98
97
  ExtensionValue other = (ExtensionValue) o;
99
- if (!this.type.op_equal(ctx, other.type).isTrue())
98
+ if (!this.type.op_equal(ctx, other.type).isTrue()) {
100
99
  return runtime.getFalse();
101
- if (runtime.is1_8()) {
102
- return this.payload.op_equal(ctx, other.payload);
103
100
  } else {
104
- return this.payload.op_equal19(ctx, other.payload);
101
+ return this.payload.op_equal(ctx, other.payload);
105
102
  }
106
103
  }
107
104
  return runtime.getFalse();
@@ -12,6 +12,8 @@ import org.jruby.RubyInteger;
12
12
  import org.jruby.RubyFixnum;
13
13
  import org.jruby.RubyString;
14
14
  import org.jruby.RubySymbol;
15
+ import org.jruby.RubyProc;
16
+ import org.jruby.RubyMethod;
15
17
  import org.jruby.runtime.builtin.IRubyObject;
16
18
  import org.jruby.anno.JRubyClass;
17
19
  import org.jruby.anno.JRubyMethod;
@@ -23,15 +25,18 @@ import static org.jruby.runtime.Visibility.PRIVATE;
23
25
 
24
26
  @JRubyClass(name="MessagePack::Factory")
25
27
  public class Factory extends RubyObject {
28
+ private static final long serialVersionUID = 8441284623445322492L;
26
29
  private final Ruby runtime;
27
- private final ExtensionRegistry extensionRegistry;
30
+ private ExtensionRegistry extensionRegistry;
28
31
  private boolean hasSymbolExtType;
32
+ private boolean hasBigIntExtType;
29
33
 
30
34
  public Factory(Ruby runtime, RubyClass type) {
31
35
  super(runtime, type);
32
36
  this.runtime = runtime;
33
37
  this.extensionRegistry = new ExtensionRegistry();
34
38
  this.hasSymbolExtType = false;
39
+ this.hasBigIntExtType = false;
35
40
  }
36
41
 
37
42
  static class FactoryAllocator implements ObjectAllocator {
@@ -49,9 +54,17 @@ public class Factory extends RubyObject {
49
54
  return this;
50
55
  }
51
56
 
52
- @JRubyMethod(name = "packer", optional = 1)
57
+ @JRubyMethod(name = "dup")
58
+ public IRubyObject dup() {
59
+ Factory clone = (Factory)super.dup();
60
+ clone.extensionRegistry = extensionRegistry();
61
+ clone.hasSymbolExtType = hasSymbolExtType;
62
+ return clone;
63
+ }
64
+
65
+ @JRubyMethod(name = "packer", optional = 2)
53
66
  public Packer packer(ThreadContext ctx, IRubyObject[] args) {
54
- return Packer.newPacker(ctx, extensionRegistry(), hasSymbolExtType, args);
67
+ return Packer.newPacker(ctx, extensionRegistry(), hasSymbolExtType, hasBigIntExtType, args);
55
68
  }
56
69
 
57
70
  @JRubyMethod(name = "unpacker", optional = 2)
@@ -61,7 +74,7 @@ public class Factory extends RubyObject {
61
74
 
62
75
  @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE)
63
76
  public IRubyObject registeredTypesInternal(ThreadContext ctx) {
64
- return RubyArray.newArray(ctx.getRuntime(), new IRubyObject[] {
77
+ return RubyArray.newArray(ctx.runtime, new IRubyObject[] {
65
78
  extensionRegistry.toInternalPackerRegistry(ctx),
66
79
  extensionRegistry.toInternalUnpackerRegistry(ctx)
67
80
  });
@@ -69,13 +82,15 @@ public class Factory extends RubyObject {
69
82
 
70
83
  @JRubyMethod(name = "register_type", required = 2, optional = 1)
71
84
  public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args) {
72
- Ruby runtime = ctx.getRuntime();
85
+ Ruby runtime = ctx.runtime;
73
86
  IRubyObject type = args[0];
74
87
  IRubyObject mod = args[1];
75
88
 
76
89
  IRubyObject packerArg;
77
90
  IRubyObject unpackerArg;
78
91
 
92
+ RubyHash options = null;
93
+
79
94
  if (isFrozen()) {
80
95
  throw runtime.newRuntimeError("can't modify frozen Factory");
81
96
  }
@@ -85,9 +100,13 @@ public class Factory extends RubyObject {
85
100
  unpackerArg = runtime.newSymbol("from_msgpack_ext");
86
101
  } else if (args.length == 3) {
87
102
  if (args[args.length - 1] instanceof RubyHash) {
88
- RubyHash options = (RubyHash) args[args.length - 1];
103
+ options = (RubyHash) args[args.length - 1];
89
104
  packerArg = options.fastARef(runtime.newSymbol("packer"));
90
105
  unpackerArg = options.fastARef(runtime.newSymbol("unpacker"));
106
+ IRubyObject optimizedSymbolsParsingArg = options.fastARef(runtime.newSymbol("optimized_symbols_parsing"));
107
+ if (optimizedSymbolsParsingArg != null && optimizedSymbolsParsingArg.isTrue()) {
108
+ throw runtime.newArgumentError("JRuby implementation does not support the optimized_symbols_parsing option");
109
+ }
91
110
  } else {
92
111
  throw runtime.newArgumentError(String.format("expected Hash but found %s.", args[args.length - 1].getType().getName()));
93
112
  }
@@ -113,17 +132,38 @@ public class Factory extends RubyObject {
113
132
  if (unpackerArg != null) {
114
133
  if (unpackerArg instanceof RubyString || unpackerArg instanceof RubySymbol) {
115
134
  unpackerProc = extModule.method(unpackerArg.callMethod(ctx, "to_sym"));
135
+ } else if (unpackerArg instanceof RubyProc || unpackerArg instanceof RubyMethod) {
136
+ unpackerProc = unpackerArg;
116
137
  } else {
117
138
  unpackerProc = unpackerArg.callMethod(ctx, "method", runtime.newSymbol("call"));
118
139
  }
119
140
  }
120
141
 
121
- extensionRegistry.put(extModule, (int) typeId, packerProc, packerArg, unpackerProc, unpackerArg);
142
+ boolean recursive = false;
143
+ if (options != null) {
144
+ IRubyObject recursiveExtensionArg = options.fastARef(runtime.newSymbol("recursive"));
145
+ if (recursiveExtensionArg != null && recursiveExtensionArg.isTrue()) {
146
+ recursive = true;
147
+ }
148
+ }
149
+
150
+ extensionRegistry.put(extModule, (int) typeId, recursive, packerProc, packerArg, unpackerProc, unpackerArg);
122
151
 
123
152
  if (extModule == runtime.getSymbol()) {
124
153
  hasSymbolExtType = true;
125
154
  }
126
155
 
156
+ if (options != null) {
157
+ IRubyObject oversizedIntegerExtensionArg = options.fastARef(runtime.newSymbol("oversized_integer_extension"));
158
+ if (oversizedIntegerExtensionArg != null && oversizedIntegerExtensionArg.isTrue()) {
159
+ if (extModule == runtime.getModule("Integer")) {
160
+ hasBigIntExtType = true;
161
+ } else {
162
+ throw runtime.newArgumentError("oversized_integer_extension: true is only for Integer class");
163
+ }
164
+ }
165
+ }
166
+
127
167
  return runtime.getNil();
128
168
  }
129
169
  }
@@ -27,53 +27,65 @@ import static org.jruby.runtime.Visibility.PRIVATE;
27
27
 
28
28
  @JRubyClass(name="MessagePack::Packer")
29
29
  public class Packer extends RubyObject {
30
+ private static final long serialVersionUID = 8451274621499362492L;
30
31
  public ExtensionRegistry registry;
31
32
  private Buffer buffer;
32
33
  private Encoder encoder;
33
34
  private boolean hasSymbolExtType;
35
+ private boolean hasBigintExtType;
34
36
  private Encoding binaryEncoding;
35
37
 
36
- public Packer(Ruby runtime, RubyClass type, ExtensionRegistry registry, boolean hasSymbolExtType) {
38
+ public Packer(Ruby runtime, RubyClass type, ExtensionRegistry registry, boolean hasSymbolExtType, boolean hasBigintExtType) {
37
39
  super(runtime, type);
38
40
  this.registry = registry;
39
41
  this.hasSymbolExtType = hasSymbolExtType;
42
+ this.hasBigintExtType = hasBigintExtType;
40
43
  }
41
44
 
42
45
  static class PackerAllocator implements ObjectAllocator {
43
46
  public IRubyObject allocate(Ruby runtime, RubyClass type) {
44
- return new Packer(runtime, type, null, false);
47
+ return new Packer(runtime, type, null, false, false);
45
48
  }
46
49
  }
47
50
 
48
51
  @JRubyMethod(name = "initialize", optional = 2)
49
52
  public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
50
53
  boolean compatibilityMode = false;
51
- if (args.length > 0 && args[args.length - 1] instanceof RubyHash) {
52
- RubyHash options = (RubyHash) args[args.length - 1];
53
- IRubyObject mode = options.fastARef(ctx.getRuntime().newSymbol("compatibility_mode"));
54
- compatibilityMode = (mode != null) && mode.isTrue();
54
+ Ruby runtime = ctx.runtime;
55
+ if (args.length > 0) {
56
+ RubyHash options = null;
57
+ if (args[args.length - 1] instanceof RubyHash) {
58
+ options = (RubyHash) args[args.length - 1];
59
+ } else if (args.length > 1 && args[args.length - 2] instanceof RubyHash) {
60
+ options = (RubyHash) args[args.length - 2];
61
+ }
62
+
63
+ if (options != null) {
64
+ IRubyObject mode = options.fastARef(runtime.newSymbol("compatibility_mode"));
65
+ compatibilityMode = (mode != null) && mode.isTrue();
66
+ }
55
67
  }
56
68
  if (registry == null) {
57
69
  // registry is null when allocate -> initialize
58
70
  // registry is already initialized (and somthing might be registered) when newPacker from Factory
59
71
  this.registry = new ExtensionRegistry();
60
72
  }
61
- this.encoder = new Encoder(ctx.getRuntime(), compatibilityMode, registry, hasSymbolExtType);
62
- this.buffer = new Buffer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Buffer"));
73
+ this.encoder = new Encoder(runtime, this, compatibilityMode, registry, hasSymbolExtType, hasBigintExtType);
74
+ this.buffer = new Buffer(runtime, runtime.getModule("MessagePack").getClass("Buffer"));
63
75
  this.buffer.initialize(ctx, args);
64
- this.binaryEncoding = ctx.getRuntime().getEncodingService().getAscii8bitEncoding();
76
+ this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
65
77
  return this;
66
78
  }
67
79
 
68
- public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, boolean hasSymbolExtType, IRubyObject[] args) {
69
- Packer packer = new Packer(ctx.getRuntime(), ctx.getRuntime().getModule("MessagePack").getClass("Packer"), extRegistry, hasSymbolExtType);
80
+ public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, boolean hasSymbolExtType, boolean hasBigintExtType, IRubyObject[] args) {
81
+ Packer packer = new Packer(ctx.runtime, ctx.runtime.getModule("MessagePack").getClass("Packer"), extRegistry, hasSymbolExtType, hasBigintExtType);
70
82
  packer.initialize(ctx, args);
71
83
  return packer;
72
84
  }
73
85
 
74
86
  @JRubyMethod(name = "compatibility_mode?")
75
87
  public IRubyObject isCompatibilityMode(ThreadContext ctx) {
76
- return encoder.isCompatibilityMode() ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse();
88
+ return encoder.isCompatibilityMode() ? ctx.runtime.getTrue() : ctx.runtime.getFalse();
77
89
  }
78
90
 
79
91
  @JRubyMethod(name = "registered_types_internal", visibility = PRIVATE)
@@ -83,7 +95,7 @@ public class Packer extends RubyObject {
83
95
 
84
96
  @JRubyMethod(name = "register_type", required = 2, optional = 1)
85
97
  public IRubyObject registerType(ThreadContext ctx, IRubyObject[] args, final Block block) {
86
- Ruby runtime = ctx.getRuntime();
98
+ Ruby runtime = ctx.runtime;
87
99
  IRubyObject type = args[0];
88
100
  IRubyObject mod = args[1];
89
101
 
@@ -112,7 +124,7 @@ public class Packer extends RubyObject {
112
124
  }
113
125
  RubyModule extModule = (RubyModule) mod;
114
126
 
115
- registry.put(extModule, (int) typeId, proc, arg, null, null);
127
+ registry.put(extModule, (int) typeId, false, proc, arg, null, null);
116
128
 
117
129
  if (extModule == runtime.getSymbol()) {
118
130
  encoder.hasSymbolExtType = true;
@@ -182,12 +194,12 @@ public class Packer extends RubyObject {
182
194
 
183
195
  @JRubyMethod(name = "write_true")
184
196
  public IRubyObject writeTrue(ThreadContext ctx) {
185
- return write(ctx, ctx.getRuntime().getTrue());
197
+ return write(ctx, ctx.runtime.getTrue());
186
198
  }
187
199
 
188
200
  @JRubyMethod(name = "write_false")
189
201
  public IRubyObject writeFalse(ThreadContext ctx) {
190
- return write(ctx, ctx.getRuntime().getFalse());
202
+ return write(ctx, ctx.runtime.getFalse());
191
203
  }
192
204
 
193
205
  @JRubyMethod(name = "write_nil")
@@ -255,7 +267,7 @@ public class Packer extends RubyObject {
255
267
  return buffer.size(ctx);
256
268
  }
257
269
 
258
- @JRubyMethod(name = "clear")
270
+ @JRubyMethod(name = "clear", alias = { "reset" })
259
271
  public IRubyObject clear(ThreadContext ctx) {
260
272
  return buffer.clear(ctx);
261
273
  }