msgpack 1.4.4 → 1.5.1

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 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
  }