msgpack 1.4.5 → 1.5.2

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: a51f64e1c017cafcef6fe080992135f5c610984ecd126f4a3a2127baa4e4a644
4
- data.tar.gz: bf45726e8f6cb658390199ecb51345e034c908207b090d53934f037555d7c37a
3
+ metadata.gz: 7de0658b1f87162270c3cc2aac70ffff8d408d974faac6ff5ad009fa11dba4dd
4
+ data.tar.gz: 9da35f2cc47ced45efd96b10ad8a3b491f0658b5ba247ad7b4aa22b901c66110
5
5
  SHA512:
6
- metadata.gz: 0ee24ab0cb11c648e9a022f23053c118c020b7c79cac14e00a1bb82faf90c59302727a2dc691c63a959225b06a5c40ec5e09fd8f35154c7474b1e652e27b9bc5
7
- data.tar.gz: 2fee550949875cc119f1865b9519c75794124ec17065b1da1ec0fd06af29ed7fbdd37d709bf8cdb217d3cf86e3004a18fab16a6ac032eec176735725439ec268
6
+ metadata.gz: b23c683b4c7fbbedd8dedc5d6eb8f5d6778aa3d3f7afef839cfe3227fc2edff713d9f17d6c8ce3808a6103325d7923ca6abfaf8792dba11485ed9d067fe6aa42
7
+ data.tar.gz: ced82946481b00d99a69aea792efbeb93982d39836bc763c7c4a0f71844ca48044cff1f58c9695aa4ebb19d0be6a0b43f2e3b4af04ca06d7ad4bfc798cacd0b8
@@ -25,12 +25,12 @@ jobs:
25
25
  bundler-cache: true # 'bundle install' and cache
26
26
  - run: bundle exec rake
27
27
 
28
- jruby:
28
+ other:
29
29
  strategy:
30
30
  fail-fast: false
31
31
  matrix:
32
32
  os: [ubuntu]
33
- ruby: ['jruby-9.2.19.0', 'jruby-9.3.3.0']
33
+ ruby: ['jruby-9.2.19.0', 'jruby-9.3.3.0', 'truffleruby']
34
34
  runs-on: ${{ matrix.os }}-latest
35
35
  steps:
36
36
  - uses: actions/checkout@v2
@@ -38,7 +38,7 @@ jobs:
38
38
  with:
39
39
  ruby-version: ${{ matrix.ruby }}
40
40
  bundler-cache: true # 'bundle install' and cache
41
- - run: bundle exec rake
41
+ - run: bundle exec rake spec
42
42
 
43
43
  head-versions:
44
44
  continue-on-error: true
@@ -46,7 +46,7 @@ jobs:
46
46
  fail-fast: false
47
47
  matrix:
48
48
  os: [ubuntu]
49
- ruby: ['ruby-head', 'jruby-head', 'truffleruby']
49
+ ruby: ['ruby-head', 'jruby-head']
50
50
  runs-on: ${{ matrix.os }}-latest
51
51
  steps:
52
52
  - uses: actions/checkout@v2
@@ -54,4 +54,4 @@ jobs:
54
54
  with:
55
55
  ruby-version: ${{ matrix.ruby }}
56
56
  bundler-cache: true # 'bundle install' and cache
57
- - run: bundle exec rake || echo "failed, but ignore it"
57
+ - run: bundle exec rake spec || echo "failed, but ignore it"
data/ChangeLog CHANGED
@@ -1,3 +1,17 @@
1
+ 2022-05-27 version 1.5.2:
2
+
3
+ * Fix bug about unpacking ext type objects with the recursive option
4
+
5
+ 2022-04-07 version 1.5.1:
6
+
7
+ * Fix bug about packing/unpacking ext type objects with the recursive option
8
+
9
+ 2022-04-06 version 1.5.0:
10
+
11
+ * Add recursive option on Factory#register_type to operate Packer/Unpacker manually
12
+ * Add oversized_integer_extension option on Factory#register_type to pack/unpack bigint using ext types
13
+ * Add Factory#pool method and Factory::Pool class to provide pooled Packer and Unpacker instances
14
+
1
15
  2022-02-15 version 1.4.5:
2
16
 
3
17
  * 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,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,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
  }