msgpack 1.4.5 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a51f64e1c017cafcef6fe080992135f5c610984ecd126f4a3a2127baa4e4a644
4
- data.tar.gz: bf45726e8f6cb658390199ecb51345e034c908207b090d53934f037555d7c37a
3
+ metadata.gz: c9e04f0f240684f2b81a5f00f9afd951feac68ee06cf4dd6e229fa8d5925c5b0
4
+ data.tar.gz: 8bf61d049bcf041bcff6e33631dbc3a821de096aca8b1c5069478934a49cd2e6
5
5
  SHA512:
6
- metadata.gz: 0ee24ab0cb11c648e9a022f23053c118c020b7c79cac14e00a1bb82faf90c59302727a2dc691c63a959225b06a5c40ec5e09fd8f35154c7474b1e652e27b9bc5
7
- data.tar.gz: 2fee550949875cc119f1865b9519c75794124ec17065b1da1ec0fd06af29ed7fbdd37d709bf8cdb217d3cf86e3004a18fab16a6ac032eec176735725439ec268
6
+ metadata.gz: 58acbb7f3c139dfa153b21445242d202335dea3b1dab6f3f8cd6eac9d427122ea6ec9d4629a5f82674dda53fd9f8b546051d704555c1e1db40c566d9cb617ff9
7
+ data.tar.gz: cb3ec4aba6ef01722ef39fc3188ed43f1f3305daae900dc7bab2a15ff1e074512f75b482331c9d962f8e9ce258bee93bf2d6dbfb29cd2a0c8abccd3fcff58fd8
data/ChangeLog CHANGED
@@ -1,3 +1,9 @@
1
+ 2022-04-06 version 1.5.0:
2
+
3
+ * Add recursive option on Factory#register_type to operate Packer/Unpacker manually
4
+ * Add oversized_integer_extension option on Factory#register_type to pack/unpack bigint using ext types
5
+ * Add Factory#pool method and Factory::Pool class to provide pooled Packer and Unpacker instances
6
+
1
7
  2022-02-15 version 1.4.5:
2
8
 
3
9
  * Fix to create UTF-8 Symbol keys when symbolize_keys: true
data/README.md CHANGED
@@ -187,6 +187,28 @@ MessagePack::DefaultFactory.register_type(0x03, MyClass3)
187
187
  MessagePack.unpack(data_with_ext_typeid_03) #=> MyClass3 instance
188
188
  ```
189
189
 
190
+ Alternatively, extension types can call the packer or unpacker recursively to generate the extension data:
191
+
192
+ ```ruby
193
+ Point = Struct.new(:x, :y)
194
+ factory = MessagePack::Factory.new
195
+ factory.register_type(
196
+ 0x01,
197
+ Point,
198
+ packer: ->(point, packer) {
199
+ packer.write(point.x)
200
+ packer.write(point.y)
201
+ },
202
+ unpacker: ->(unpacker) {
203
+ x = unpacker.read
204
+ y = unpacker.read
205
+ Point.new(x, y)
206
+ },
207
+ recursive: true,
208
+ )
209
+ factory.load(factory.dump(Point.new(12, 34))) # => #<struct Point x=12, y=34>
210
+ ```
211
+
190
212
  ## Buffer API
191
213
 
192
214
  MessagePack for Ruby provides a buffer API so that you can read or write data by hand, not via Packer or Unpacker API.
@@ -31,7 +31,7 @@ module MessagePack
31
31
  #
32
32
  # See Packer#initialize for supported options.
33
33
  #
34
- def dump(obj, options={})
34
+ def dump(obj, options=nil)
35
35
  end
36
36
  alias pack dump
37
37
 
@@ -57,7 +57,7 @@ module MessagePack
57
57
  #
58
58
  # See Unpacker#initialize for supported options.
59
59
  #
60
- def load(data, options={})
60
+ def load(data, options=nil)
61
61
  end
62
62
  alias unpack load
63
63
 
@@ -76,6 +76,7 @@ module MessagePack
76
76
  # * *:packer* specify symbol or proc object for packer
77
77
  # * *:unpacker* specify symbol or proc object for unpacker
78
78
  # * *:optimized_symbols_parsing* specify true to use the optimized symbols parsing (not supported on JRuby now)
79
+ # * *recursive* specify true to receive the packer or unpacker as argument to generate the extension body manually.
79
80
  #
80
81
  def register_type(type, klass, options={})
81
82
  end
@@ -98,5 +99,47 @@ module MessagePack
98
99
  #
99
100
  def type_registered?(klass_or_type, selector=:both)
100
101
  end
102
+
103
+ #
104
+ # Creates a MessagePack::PooledFactory instance of the given size.
105
+ #
106
+ # PooledFactory keeps Packer and Unpacker instance in a pool for improved performance.
107
+ # Note that the size defines how many instances are kept in cache, not the maximum of instances
108
+ # that can be created. If the pool limit is reached, a new instance is created anyway.
109
+ #
110
+ # @param size [Fixnum] specify how many Packer and Unpacker to keep in cache (default 1)
111
+ # @param options [Hash] Combined options for Packer and Unpacker. See Packer#initialize and Unpacker#initialize
112
+ # for supported options.
113
+ def pool(size=1, **options)
114
+ end
115
+
116
+ class Pool
117
+ #
118
+ # Deserializes an object from the string or io and returns it.
119
+ #
120
+ # If there're not enough data to deserialize one object, this method raises EOFError.
121
+ # If data format is invalid, this method raises MessagePack::MalformedFormatError.
122
+ # If the object nests too deeply, this method raises MessagePack::StackError.
123
+ #
124
+ # @param data [String]
125
+ # @return [Object] deserialized object
126
+ #
127
+ # See Unpacker#initialize for supported options.
128
+ #
129
+ def load(data)
130
+ end
131
+
132
+ #
133
+ # Serialize the passed value
134
+ #
135
+ # If it could not serialize the object, it raises
136
+ # NoMethodError: undefined method `to_msgpack' for #<the_object>.
137
+ #
138
+ # @param obj [Object] object to serialize
139
+ # @return [String] serialized object
140
+ #
141
+ def dump(object)
142
+ end
143
+ end
101
144
  end
102
145
  end
@@ -224,4 +224,10 @@ public class Buffer extends RubyObject {
224
224
  public IRubyObject writeTo(ThreadContext ctx, IRubyObject io) {
225
225
  return io.callMethod(ctx, "write", readCommon(ctx, null, false));
226
226
  }
227
+
228
+ public ByteList getBytes() {
229
+ byte[] bytes = new byte[rawSize()];
230
+ buffer.get(bytes);
231
+ return new ByteList(bytes, binaryEncoding);
232
+ }
227
233
  }
@@ -14,6 +14,7 @@ import org.jruby.RubyBignum;
14
14
  import org.jruby.RubyString;
15
15
  import org.jruby.RubyArray;
16
16
  import org.jruby.RubyHash;
17
+ import org.jruby.RubyInteger;
17
18
  import org.jruby.exceptions.RaiseException;
18
19
  import org.jruby.runtime.builtin.IRubyObject;
19
20
  import org.jruby.util.ByteList;
@@ -35,7 +36,7 @@ public class Decoder implements Iterator<IRubyObject> {
35
36
  private final RubyClass unexpectedTypeErrorClass;
36
37
  private final RubyClass unknownExtTypeErrorClass;
37
38
 
38
- private ExtensionRegistry registry;
39
+ private Unpacker unpacker;
39
40
  private ByteBuffer buffer;
40
41
  private boolean symbolizeKeys;
41
42
  private boolean freeze;
@@ -45,29 +46,29 @@ public class Decoder implements Iterator<IRubyObject> {
45
46
  this(runtime, null, new byte[] {}, 0, 0, false, false, false);
46
47
  }
47
48
 
48
- public Decoder(Ruby runtime, ExtensionRegistry registry) {
49
- this(runtime, registry, new byte[] {}, 0, 0, false, false, false);
49
+ public Decoder(Ruby runtime, Unpacker unpacker) {
50
+ this(runtime, unpacker, new byte[] {}, 0, 0, false, false, false);
50
51
  }
51
52
 
52
53
  public Decoder(Ruby runtime, byte[] bytes) {
53
54
  this(runtime, null, bytes, 0, bytes.length, false, false, false);
54
55
  }
55
56
 
56
- public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes) {
57
- this(runtime, registry, bytes, 0, bytes.length, false, false, false);
57
+ public Decoder(Ruby runtime, Unpacker unpacker, byte[] bytes) {
58
+ this(runtime, unpacker, bytes, 0, bytes.length, false, false, false);
58
59
  }
59
60
 
60
- public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, boolean symbolizeKeys, boolean freeze, boolean allowUnknownExt) {
61
- this(runtime, registry, bytes, 0, bytes.length, symbolizeKeys, freeze, allowUnknownExt);
61
+ public Decoder(Ruby runtime, Unpacker unpacker, byte[] bytes, boolean symbolizeKeys, boolean freeze, boolean allowUnknownExt) {
62
+ this(runtime, unpacker, bytes, 0, bytes.length, symbolizeKeys, freeze, allowUnknownExt);
62
63
  }
63
64
 
64
- public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length) {
65
- this(runtime, registry, bytes, offset, length, false, false, false);
65
+ public Decoder(Ruby runtime, Unpacker unpacker, byte[] bytes, int offset, int length) {
66
+ this(runtime, unpacker, bytes, offset, length, false, false, false);
66
67
  }
67
68
 
68
- public Decoder(Ruby runtime, ExtensionRegistry registry, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean freeze, boolean allowUnknownExt) {
69
+ public Decoder(Ruby runtime, Unpacker unpacker, byte[] bytes, int offset, int length, boolean symbolizeKeys, boolean freeze, boolean allowUnknownExt) {
69
70
  this.runtime = runtime;
70
- this.registry = registry;
71
+ this.unpacker = unpacker;
71
72
  this.symbolizeKeys = symbolizeKeys;
72
73
  this.freeze = freeze;
73
74
  this.allowUnknownExt = allowUnknownExt;
@@ -154,18 +155,21 @@ public class Decoder implements Iterator<IRubyObject> {
154
155
 
155
156
  private IRubyObject consumeExtension(int size) {
156
157
  int type = buffer.get();
157
- byte[] payload = readBytes(size);
158
-
159
- if (registry != null) {
160
- IRubyObject proc = registry.lookupUnpackerByTypeId(type);
161
- if (proc != null) {
162
- ByteList byteList = new ByteList(payload, runtime.getEncodingService().getAscii8bitEncoding());
163
- return proc.callMethod(runtime.getCurrentContext(), "call", runtime.newString(byteList));
158
+ if (unpacker != null) {
159
+ ExtensionRegistry.ExtensionEntry entry = unpacker.lookupExtensionByTypeId(type);
160
+ if (entry != null) {
161
+ IRubyObject proc = entry.getUnpackerProc();
162
+ if (entry.isRecursive()) {
163
+ return proc.callMethod(runtime.getCurrentContext(), "call", unpacker);
164
+ } else {
165
+ ByteList byteList = new ByteList(readBytes(size), runtime.getEncodingService().getAscii8bitEncoding());
166
+ return proc.callMethod(runtime.getCurrentContext(), "call", runtime.newString(byteList));
167
+ }
164
168
  }
165
169
  }
166
170
 
167
171
  if (this.allowUnknownExt) {
168
- return ExtensionValue.newExtensionValue(runtime, type, payload);
172
+ return ExtensionValue.newExtensionValue(runtime, type, readBytes(size));
169
173
  }
170
174
 
171
175
  throw runtime.newRaiseException(unknownExtTypeErrorClass, "unexpected extension type");
@@ -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) {
@@ -147,7 +160,10 @@ public class Encoder {
147
160
  BigInteger value = object.getBigIntegerValue();
148
161
  if (value.compareTo(RubyBignum.LONG_MIN) < 0 || value.compareTo(RubyBignum.LONG_MAX) > 0) {
149
162
  if (value.bitLength() > 64 || (value.bitLength() > 63 && value.signum() < 0)) {
150
- 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));
151
167
  }
152
168
  ensureRemainingCapacity(9);
153
169
  buffer.put(value.signum() < 0 ? INT64 : UINT64);
@@ -391,19 +407,30 @@ public class Encoder {
391
407
 
392
408
  private boolean tryAppendWithExtTypeLookup(IRubyObject object) {
393
409
  if (registry != null) {
394
- RubyModule lookupClass;
395
-
396
- if (object.getType() == runtime.getSymbol()) {
397
- lookupClass = object.getType();
398
- } else {
399
- lookupClass = object.getSingletonClass();
400
- }
401
-
402
- IRubyObject[] pair = registry.lookupPackerForObject(object);
403
- if (pair != null) {
404
- RubyString bytes = pair[0].callMethod(runtime.getCurrentContext(), "call", object).asString();
405
- int type = (int) ((RubyFixnum) pair[1]).getLongValue();
406
- appendExt(type, bytes.getByteList());
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
+ recursiveExtension = true;
419
+
420
+ ByteList payload;
421
+ try {
422
+ IRubyObject args[] = { object, packer };
423
+ proc.callMethod(runtime.getCurrentContext(), "call", args);
424
+ payload = new ByteList(buffer.array(), 0, buffer.position(), binaryEncoding, false);
425
+ } finally {
426
+ recursiveExtension = false;
427
+ buffer = oldBuffer;
428
+ }
429
+ appendExt(type, payload);
430
+ } else {
431
+ RubyString bytes = proc.callMethod(runtime.getCurrentContext(), "call", object).asString();
432
+ appendExt(type, bytes.getByteList());
433
+ }
407
434
  return true;
408
435
  }
409
436
  }
@@ -59,59 +59,46 @@ public class ExtensionRegistry {
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
84
  */
86
85
  lookupClass = object.getMetaClass();
87
- pair = fetchEntryByModule(lookupClass);
88
- if (pair != null) {
89
- return pair;
86
+ entry = extensionsByModule.get(lookupClass);
87
+ if (entry != null && entry.hasPacker()) {
88
+ return entry;
90
89
  }
91
90
 
92
91
  RubyModule realClass = object.getType();
93
92
  if (realClass != lookupClass) {
94
- pair = fetchEntryByModule(realClass);
95
- if (pair != null) {
96
- return pair;
93
+ entry = extensionsByModule.get(realClass);
94
+ if (entry != null && entry.hasPacker()) {
95
+ return entry;
97
96
  }
98
97
  }
99
98
 
100
- ExtensionEntry e = findEntryByModuleOrAncestor(lookupClass);
101
- if (e != null && e.hasPacker()) {
102
- extensionsByAncestor.put(e.getExtensionModule(), e);
103
- return e.toPackerProcTypeIdPair(lookupClass.getRuntime().getCurrentContext());
104
- }
105
- return null;
106
- }
107
-
108
- private IRubyObject[] fetchEntryByModule(final RubyModule mod) {
109
- ExtensionEntry e = extensionsByModule.get(mod);
110
- if (e == null) {
111
- e = extensionsByAncestor.get(mod);
112
- }
113
- if (e != null && e.hasPacker()) {
114
- return e.toPackerProcTypeIdPair(mod.getRuntime().getCurrentContext());
99
+ entry = findEntryByModuleOrAncestor(lookupClass);
100
+ if (entry != null && entry.hasPacker()) {
101
+ return entry;
115
102
  }
116
103
  return null;
117
104
  }
@@ -127,17 +114,19 @@ public class ExtensionRegistry {
127
114
  return null;
128
115
  }
129
116
 
130
- private static class ExtensionEntry {
117
+ public static class ExtensionEntry {
131
118
  private final RubyModule mod;
132
119
  private final int typeId;
120
+ private final boolean recursive;
133
121
  private final IRubyObject packerProc;
134
122
  private final IRubyObject packerArg;
135
123
  private final IRubyObject unpackerProc;
136
124
  private final IRubyObject unpackerArg;
137
125
 
138
- 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) {
139
127
  this.mod = mod;
140
128
  this.typeId = typeId;
129
+ this.recursive = recursive;
141
130
  this.packerProc = packerProc;
142
131
  this.packerArg = packerArg;
143
132
  this.unpackerProc = unpackerProc;
@@ -152,6 +141,10 @@ public class ExtensionRegistry {
152
141
  return typeId;
153
142
  }
154
143
 
144
+ public boolean isRecursive() {
145
+ return recursive;
146
+ }
147
+
155
148
  public boolean hasPacker() {
156
149
  return packerProc != null;
157
150
  }
@@ -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;
@@ -25,14 +27,16 @@ import static org.jruby.runtime.Visibility.PRIVATE;
25
27
  public class Factory extends RubyObject {
26
28
  private static final long serialVersionUID = 8441284623445322492L;
27
29
  private final Ruby runtime;
28
- private final ExtensionRegistry extensionRegistry;
30
+ private ExtensionRegistry extensionRegistry;
29
31
  private boolean hasSymbolExtType;
32
+ private boolean hasBigIntExtType;
30
33
 
31
34
  public Factory(Ruby runtime, RubyClass type) {
32
35
  super(runtime, type);
33
36
  this.runtime = runtime;
34
37
  this.extensionRegistry = new ExtensionRegistry();
35
38
  this.hasSymbolExtType = false;
39
+ this.hasBigIntExtType = false;
36
40
  }
37
41
 
38
42
  static class FactoryAllocator implements ObjectAllocator {
@@ -50,9 +54,17 @@ public class Factory extends RubyObject {
50
54
  return this;
51
55
  }
52
56
 
53
- @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)
54
66
  public Packer packer(ThreadContext ctx, IRubyObject[] args) {
55
- return Packer.newPacker(ctx, extensionRegistry(), hasSymbolExtType, args);
67
+ return Packer.newPacker(ctx, extensionRegistry(), hasSymbolExtType, hasBigIntExtType, args);
56
68
  }
57
69
 
58
70
  @JRubyMethod(name = "unpacker", optional = 2)
@@ -77,6 +89,8 @@ public class Factory extends RubyObject {
77
89
  IRubyObject packerArg;
78
90
  IRubyObject unpackerArg;
79
91
 
92
+ RubyHash options = null;
93
+
80
94
  if (isFrozen()) {
81
95
  throw runtime.newRuntimeError("can't modify frozen Factory");
82
96
  }
@@ -86,7 +100,7 @@ public class Factory extends RubyObject {
86
100
  unpackerArg = runtime.newSymbol("from_msgpack_ext");
87
101
  } else if (args.length == 3) {
88
102
  if (args[args.length - 1] instanceof RubyHash) {
89
- RubyHash options = (RubyHash) args[args.length - 1];
103
+ options = (RubyHash) args[args.length - 1];
90
104
  packerArg = options.fastARef(runtime.newSymbol("packer"));
91
105
  unpackerArg = options.fastARef(runtime.newSymbol("unpacker"));
92
106
  IRubyObject optimizedSymbolsParsingArg = options.fastARef(runtime.newSymbol("optimized_symbols_parsing"));
@@ -118,17 +132,38 @@ public class Factory extends RubyObject {
118
132
  if (unpackerArg != null) {
119
133
  if (unpackerArg instanceof RubyString || unpackerArg instanceof RubySymbol) {
120
134
  unpackerProc = extModule.method(unpackerArg.callMethod(ctx, "to_sym"));
135
+ } else if (unpackerArg instanceof RubyProc || unpackerArg instanceof RubyMethod) {
136
+ unpackerProc = unpackerArg;
121
137
  } else {
122
138
  unpackerProc = unpackerArg.callMethod(ctx, "method", runtime.newSymbol("call"));
123
139
  }
124
140
  }
125
141
 
126
- 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);
127
151
 
128
152
  if (extModule == runtime.getSymbol()) {
129
153
  hasSymbolExtType = true;
130
154
  }
131
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
+
132
167
  return runtime.getNil();
133
168
  }
134
169
  }
@@ -32,17 +32,19 @@ public class Packer extends RubyObject {
32
32
  private Buffer buffer;
33
33
  private Encoder encoder;
34
34
  private boolean hasSymbolExtType;
35
+ private boolean hasBigintExtType;
35
36
  private Encoding binaryEncoding;
36
37
 
37
- public Packer(Ruby runtime, RubyClass type, ExtensionRegistry registry, boolean hasSymbolExtType) {
38
+ public Packer(Ruby runtime, RubyClass type, ExtensionRegistry registry, boolean hasSymbolExtType, boolean hasBigintExtType) {
38
39
  super(runtime, type);
39
40
  this.registry = registry;
40
41
  this.hasSymbolExtType = hasSymbolExtType;
42
+ this.hasBigintExtType = hasBigintExtType;
41
43
  }
42
44
 
43
45
  static class PackerAllocator implements ObjectAllocator {
44
46
  public IRubyObject allocate(Ruby runtime, RubyClass type) {
45
- return new Packer(runtime, type, null, false);
47
+ return new Packer(runtime, type, null, false, false);
46
48
  }
47
49
  }
48
50
 
@@ -50,25 +52,33 @@ public class Packer extends RubyObject {
50
52
  public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
51
53
  boolean compatibilityMode = false;
52
54
  Ruby runtime = ctx.runtime;
53
- if (args.length > 0 && args[args.length - 1] instanceof RubyHash) {
54
- RubyHash options = (RubyHash) args[args.length - 1];
55
- IRubyObject mode = options.fastARef(runtime.newSymbol("compatibility_mode"));
56
- compatibilityMode = (mode != null) && mode.isTrue();
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
+ }
57
67
  }
58
68
  if (registry == null) {
59
69
  // registry is null when allocate -> initialize
60
70
  // registry is already initialized (and somthing might be registered) when newPacker from Factory
61
71
  this.registry = new ExtensionRegistry();
62
72
  }
63
- this.encoder = new Encoder(runtime, compatibilityMode, registry, hasSymbolExtType);
73
+ this.encoder = new Encoder(runtime, this, compatibilityMode, registry, hasSymbolExtType, hasBigintExtType);
64
74
  this.buffer = new Buffer(runtime, runtime.getModule("MessagePack").getClass("Buffer"));
65
75
  this.buffer.initialize(ctx, args);
66
76
  this.binaryEncoding = runtime.getEncodingService().getAscii8bitEncoding();
67
77
  return this;
68
78
  }
69
79
 
70
- public static Packer newPacker(ThreadContext ctx, ExtensionRegistry extRegistry, boolean hasSymbolExtType, IRubyObject[] args) {
71
- Packer packer = new Packer(ctx.runtime, ctx.runtime.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);
72
82
  packer.initialize(ctx, args);
73
83
  return packer;
74
84
  }
@@ -114,7 +124,7 @@ public class Packer extends RubyObject {
114
124
  }
115
125
  RubyModule extModule = (RubyModule) mod;
116
126
 
117
- registry.put(extModule, (int) typeId, proc, arg, null, null);
127
+ registry.put(extModule, (int) typeId, false, proc, arg, null, null);
118
128
 
119
129
  if (extModule == runtime.getSymbol()) {
120
130
  encoder.hasSymbolExtType = true;
@@ -257,7 +267,7 @@ public class Packer extends RubyObject {
257
267
  return buffer.size(ctx);
258
268
  }
259
269
 
260
- @JRubyMethod(name = "clear")
270
+ @JRubyMethod(name = "clear", alias = { "reset" })
261
271
  public IRubyObject clear(ThreadContext ctx) {
262
272
  return buffer.clear(ctx);
263
273
  }