msgpack 1.4.4 → 1.5.1

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: c57015d343570a2bccf9f0cb8877bf7e0a83cffa478fa4a99816dc1bb4e5f3d0
4
- data.tar.gz: 13e5f99e0b73c98071e8b0bb8bf841a52283ef9f1502ea578732a4baf1f4d9de
3
+ metadata.gz: 2c5ae461c691018c21ea088c4b629510c0626a31c16e640d7128d1d4be70276a
4
+ data.tar.gz: 32ac330fedeb8fdcf90e172df4480d05564185ef9b3e72f430c83de04eea90b1
5
5
  SHA512:
6
- metadata.gz: 2c048635dc9d9451ace0aff991a3ea92ccd7bbc397cfa4431c6428a07149fcdf9b31c1b1cd9392aca806669839236b26f90234cc99b8232061b3441707adc409
7
- data.tar.gz: 63af2e6f9f11f8a96115d81aa9d8754220b44cdda069c0094ac5c635c01df75e84ac1e76dd3c2e9e81c7b3d6f5a1ba66f9006c620ec30ef047bfc7c857ffec0d
6
+ metadata.gz: 2a5a1c96cbfdc2030c38cf559a61aaacc466a5b4fcaaa6af809b8d062f3042a44ee6df0f731fe77bbdf1ebba0a123da2b9db033e7117baabcc6e9664dd6f56ef
7
+ data.tar.gz: 07c313bd70f92996b0131a0c5ce9836f025dd4fa98fdbe365ad04fa860b2bd6ca81c62fbd90cc2296a75eddd2a9b648b3ca48b8838a40ffab72a5845dfd6e724
@@ -12,6 +12,7 @@ on:
12
12
  jobs:
13
13
  mri:
14
14
  strategy:
15
+ fail-fast: false
15
16
  matrix:
16
17
  os: [ubuntu, macos, windows]
17
18
  ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1']
@@ -26,11 +27,10 @@ jobs:
26
27
 
27
28
  jruby:
28
29
  strategy:
30
+ fail-fast: false
29
31
  matrix:
30
32
  os: [ubuntu]
31
- # TODO: update to 9.3.3.0 once supported
32
- # https://github.com/ruby/setup-ruby#supported-versions
33
- ruby: ['jruby-9.2.19.0', 'jruby-9.3.2.0']
33
+ ruby: ['jruby-9.2.19.0', 'jruby-9.3.3.0']
34
34
  runs-on: ${{ matrix.os }}-latest
35
35
  steps:
36
36
  - uses: actions/checkout@v2
@@ -43,6 +43,7 @@ jobs:
43
43
  head-versions:
44
44
  continue-on-error: true
45
45
  strategy:
46
+ fail-fast: false
46
47
  matrix:
47
48
  os: [ubuntu]
48
49
  ruby: ['ruby-head', 'jruby-head', 'truffleruby']
data/ChangeLog CHANGED
@@ -1,3 +1,21 @@
1
+ 2022-04-07 version 1.5.1:
2
+
3
+ * Fix bug about packing/unpacking ext type objects with the recursive option
4
+
5
+ 2022-04-06 version 1.5.0:
6
+
7
+ * Add recursive option on Factory#register_type to operate Packer/Unpacker manually
8
+ * Add oversized_integer_extension option on Factory#register_type to pack/unpack bigint using ext types
9
+ * Add Factory#pool method and Factory::Pool class to provide pooled Packer and Unpacker instances
10
+
11
+ 2022-02-15 version 1.4.5:
12
+
13
+ * Fix to create UTF-8 Symbol keys when symbolize_keys: true
14
+ * Fix to assume Symbols as US-ASCII or UTF-8
15
+ * Optimize Packer/Unpacker initialization
16
+ * Optimize extension class lookup
17
+ * Rename Packer#clear as Packer#reset (#clear is still available as an alias)
18
+
1
19
  2022-01-22 version 1.4.4:
2
20
 
3
21
  * Specify the build option --platform=8 for older Java platforms
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,13 +57,13 @@ 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
 
64
64
  #
65
65
  # Register a type and Class to be registered for packer and/or unpacker.
66
- # If options are not speicified, factory will use :to_msgpack_ext for packer, and
66
+ # If options are not specified, factory will use :to_msgpack_ext for packer, and
67
67
  # :from_msgpack_ext for unpacker.
68
68
  #
69
69
  # @param type [Fixnum] type id of registered Class (0-127)
@@ -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
@@ -14,7 +14,7 @@ module MessagePack
14
14
  # @overload initialize(io, options={})
15
15
  # @param io [IO]
16
16
  # @param options [Hash]
17
- # This packer writes serialzied objects into the IO when the internal buffer is filled.
17
+ # This packer writes serialized objects into the IO when the internal buffer is filled.
18
18
  # _io_ must respond to write(string) or append(string) method.
19
19
  #
20
20
  # Supported options:
@@ -33,12 +33,12 @@ module MessagePack
33
33
  #
34
34
  # @overload register_type(type, klass, &block)
35
35
  # @param type [Fixnum] type id (0-127) user defined type id for specified Class
36
- # @param klass [Class] Class to be serialized with speicifed type id
36
+ # @param klass [Class] Class to be serialized with specified type id
37
37
  # @yieldparam object [Object] object to be serialized
38
38
  #
39
39
  # @overload register_type(type, klass, method_name)
40
40
  # @param type [Fixnum] type id (0-127) user defined type id for specified Class
41
- # @param klass [Class] Class to be serialized with speicifed type id
41
+ # @param klass [Class] Class to be serialized with specified type id
42
42
  # @param method_name [Symbol] method which returns bytes of serialized representation
43
43
  #
44
44
  # @return nil
@@ -155,8 +155,9 @@ module MessagePack
155
155
  #
156
156
  # @return nil
157
157
  #
158
- def clear
158
+ def reset
159
159
  end
160
+ alias clear reset
160
161
 
161
162
  #
162
163
  # Returns size of the internal buffer. Same as buffer.size.
@@ -36,7 +36,7 @@ module MessagePack
36
36
  #
37
37
  # @overload register_type(type, klass, class_method_name)
38
38
  # @param type [Fixnum] type id (0-127) user defined type id for specified Class
39
- # @param klass [Class] Class to be serialized with speicifed type id
39
+ # @param klass [Class] Class to be serialized with specified type id
40
40
  # @param class_method_name [Symbol] class method which returns an instance object
41
41
  #
42
42
  # @return nil
@@ -149,7 +149,7 @@ module MessagePack
149
149
  #
150
150
  # It repeats until the io or internal buffer does not include any complete objects.
151
151
  #
152
- # If the an IO is set, it repeats to read data from the IO when the buffer
152
+ # If an IO is set, it repeats to read data from the IO when the buffer
153
153
  # becomes empty until the IO raises EOFError.
154
154
  #
155
155
  # This method could raise same errors with _read_ excepting EOFError.
@@ -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,31 @@ 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
+ 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
+ }
407
435
  return true;
408
436
  }
409
437
  }
@@ -59,64 +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
- *
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
  }
@@ -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
  }
@@ -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
  }