msgpack 1.4.5 → 1.5.6
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 +4 -4
- data/.github/workflows/ci.yaml +5 -5
- data/ChangeLog +32 -0
- data/README.md +25 -1
- data/bench/bench.rb +78 -0
- data/doclib/msgpack/factory.rb +45 -2
- data/ext/java/org/msgpack/jruby/Buffer.java +6 -0
- data/ext/java/org/msgpack/jruby/Decoder.java +23 -19
- data/ext/java/org/msgpack/jruby/Encoder.java +46 -18
- data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +24 -31
- data/ext/java/org/msgpack/jruby/Factory.java +40 -5
- data/ext/java/org/msgpack/jruby/Packer.java +21 -11
- data/ext/java/org/msgpack/jruby/Unpacker.java +44 -22
- data/ext/msgpack/buffer.c +9 -36
- data/ext/msgpack/buffer.h +9 -1
- data/ext/msgpack/buffer_class.c +18 -9
- data/ext/msgpack/compat.h +0 -99
- data/ext/msgpack/extconf.rb +9 -11
- data/ext/msgpack/factory_class.c +62 -5
- data/ext/msgpack/packer.c +42 -29
- data/ext/msgpack/packer.h +25 -7
- data/ext/msgpack/packer_class.c +23 -20
- data/ext/msgpack/packer_ext_registry.c +13 -4
- data/ext/msgpack/packer_ext_registry.h +10 -5
- data/ext/msgpack/unpacker.c +99 -68
- data/ext/msgpack/unpacker.h +10 -6
- data/ext/msgpack/unpacker_class.c +16 -8
- data/ext/msgpack/unpacker_ext_registry.c +3 -2
- data/ext/msgpack/unpacker_ext_registry.h +5 -2
- data/lib/msgpack/bigint.rb +69 -0
- data/lib/msgpack/factory.rb +103 -0
- data/lib/msgpack/version.rb +1 -1
- data/lib/msgpack.rb +4 -5
- data/msgpack.gemspec +1 -0
- data/spec/bigint_spec.rb +26 -0
- data/spec/factory_spec.rb +248 -0
- data/spec/spec_helper.rb +9 -1
- data/spec/timestamp_spec.rb +2 -2
- data/spec/unpacker_spec.rb +12 -0
- metadata +20 -13
- data/bench/pack.rb +0 -23
- data/bench/pack_log.rb +0 -33
- data/bench/pack_log_long.rb +0 -65
- data/bench/pack_symbols.rb +0 -28
- data/bench/run.sh +0 -14
- data/bench/run_long.sh +0 -35
- data/bench/run_symbols.sh +0 -26
- data/bench/unpack.rb +0 -21
- data/bench/unpack_log.rb +0 -34
- data/bench/unpack_log_long.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c3bd9f87eaddd3212797209da93c4ec3a1c2384cb4a41e0ea9feb48a75523f2
|
4
|
+
data.tar.gz: 16d6223c1c98f50e629c029f077383cbbd7cd194af038255fd28d37aeecbb39a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d4bd9048f852ac4c6e1365b07e6a8df9869cd0d9b2ce2c8bff7c836fe3e513451cf8895b36a719d09beaafdb85101363b705da719e5e9e9c7361ee1de96f2cf
|
7
|
+
data.tar.gz: a15ac8e77af9dc0ccb06ed266efe1ff93a2bae78e2ab5b980ba87af486d08e34676f9203fbc6b646b3c7f14e2fbdf7d4e9e1892fd58d0ca214a39de0dd2ee345
|
data/.github/workflows/ci.yaml
CHANGED
@@ -25,12 +25,12 @@ jobs:
|
|
25
25
|
bundler-cache: true # 'bundle install' and cache
|
26
26
|
- run: bundle exec rake
|
27
27
|
|
28
|
-
|
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'
|
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,35 @@
|
|
1
|
+
2022-08-23 1.5.6:
|
2
|
+
|
3
|
+
* No actual code change, just re-release the `java` version properly.
|
4
|
+
|
5
|
+
2022-08-22 1.5.5:
|
6
|
+
|
7
|
+
* Fix a segfault when GC triggers inside a recursive extension.
|
8
|
+
|
9
|
+
2022-07-25 1.5.4:
|
10
|
+
|
11
|
+
* Fix a segfault when deserializing empty symbol (`:""`).
|
12
|
+
* Improve compilation flags to not strip debug symbols.
|
13
|
+
|
14
|
+
2022-05-30 version 1.5.3:
|
15
|
+
|
16
|
+
* Fix deduplication of empty strings when using the `freeze: true` option.
|
17
|
+
* Use `rb_hash_new_capa` when available (Ruby 3.2) for improved performance when parsing large hashes.
|
18
|
+
|
19
|
+
2022-05-27 version 1.5.2:
|
20
|
+
|
21
|
+
* Fix bug about unpacking ext type objects with the recursive option
|
22
|
+
|
23
|
+
2022-04-07 version 1.5.1:
|
24
|
+
|
25
|
+
* Fix bug about packing/unpacking ext type objects with the recursive option
|
26
|
+
|
27
|
+
2022-04-06 version 1.5.0:
|
28
|
+
|
29
|
+
* Add recursive option on Factory#register_type to operate Packer/Unpacker manually
|
30
|
+
* Add oversized_integer_extension option on Factory#register_type to pack/unpack bigint using ext types
|
31
|
+
* Add Factory#pool method and Factory::Pool class to provide pooled Packer and Unpacker instances
|
32
|
+
|
1
33
|
2022-02-15 version 1.4.5:
|
2
34
|
|
3
35
|
* Fix to create UTF-8 Symbol keys when symbolize_keys: true
|
data/README.md
CHANGED
@@ -40,7 +40,7 @@ or build msgpack-ruby and install:
|
|
40
40
|
MessagePack for Ruby should run on x86, ARM, PowerPC, SPARC and other CPU architectures.
|
41
41
|
|
42
42
|
And it works with MRI (CRuby) and Rubinius.
|
43
|
-
Patches to improve portability
|
43
|
+
Patches to improve portability are highly welcomed.
|
44
44
|
|
45
45
|
|
46
46
|
## Serializing objects
|
@@ -51,6 +51,7 @@ Use `MessagePack.pack` or `to_msgpack`:
|
|
51
51
|
require 'msgpack'
|
52
52
|
msg = MessagePack.pack(obj) # or
|
53
53
|
msg = obj.to_msgpack
|
54
|
+
File.binwrite('mydata.msgpack', msg)
|
54
55
|
```
|
55
56
|
|
56
57
|
### Streaming serialization
|
@@ -71,6 +72,7 @@ Use `MessagePack.unpack`:
|
|
71
72
|
|
72
73
|
```ruby
|
73
74
|
require 'msgpack'
|
75
|
+
msg = File.binread('mydata.msgpack')
|
74
76
|
obj = MessagePack.unpack(msg)
|
75
77
|
```
|
76
78
|
|
@@ -187,6 +189,28 @@ MessagePack::DefaultFactory.register_type(0x03, MyClass3)
|
|
187
189
|
MessagePack.unpack(data_with_ext_typeid_03) #=> MyClass3 instance
|
188
190
|
```
|
189
191
|
|
192
|
+
Alternatively, extension types can call the packer or unpacker recursively to generate the extension data:
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
Point = Struct.new(:x, :y)
|
196
|
+
factory = MessagePack::Factory.new
|
197
|
+
factory.register_type(
|
198
|
+
0x01,
|
199
|
+
Point,
|
200
|
+
packer: ->(point, packer) {
|
201
|
+
packer.write(point.x)
|
202
|
+
packer.write(point.y)
|
203
|
+
},
|
204
|
+
unpacker: ->(unpacker) {
|
205
|
+
x = unpacker.read
|
206
|
+
y = unpacker.read
|
207
|
+
Point.new(x, y)
|
208
|
+
},
|
209
|
+
recursive: true,
|
210
|
+
)
|
211
|
+
factory.load(factory.dump(Point.new(12, 34))) # => #<struct Point x=12, y=34>
|
212
|
+
```
|
213
|
+
|
190
214
|
## Buffer API
|
191
215
|
|
192
216
|
MessagePack for Ruby provides a buffer API so that you can read or write data by hand, not via Packer or Unpacker API.
|
data/bench/bench.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# % bundle install
|
2
|
+
# % bundle exec ruby bench/bench.rb
|
3
|
+
|
4
|
+
require 'msgpack'
|
5
|
+
|
6
|
+
require 'benchmark/ips'
|
7
|
+
|
8
|
+
object_plain = {
|
9
|
+
'message' => '127.0.0.1 - - [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"'
|
10
|
+
}
|
11
|
+
|
12
|
+
data_plain = MessagePack.pack(object_plain)
|
13
|
+
|
14
|
+
object_structured = {
|
15
|
+
'remote_host' => '127.0.0.1',
|
16
|
+
'remote_user' => '-',
|
17
|
+
'date' => '10/Oct/2000:13:55:36 -0700',
|
18
|
+
'request' => 'GET /apache_pb.gif HTTP/1.0',
|
19
|
+
'method' => 'GET',
|
20
|
+
'path' => '/apache_pb.gif',
|
21
|
+
'protocol' => 'HTTP/1.0',
|
22
|
+
'status' => 200,
|
23
|
+
'bytes' => 2326,
|
24
|
+
'referer' => 'http://www.example.com/start.html',
|
25
|
+
'agent' => 'Mozilla/4.08 [en] (Win98; I ;Nav)',
|
26
|
+
}
|
27
|
+
|
28
|
+
data_structured = MessagePack.pack(object_structured)
|
29
|
+
|
30
|
+
class Extended
|
31
|
+
def to_msgpack_ext
|
32
|
+
MessagePack.pack({})
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.from_msgpack_ext(data)
|
36
|
+
MessagePack.unpack(data)
|
37
|
+
Extended.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
object_extended = {
|
42
|
+
'extended' => Extended.new
|
43
|
+
}
|
44
|
+
|
45
|
+
extended_packer = MessagePack::Packer.new
|
46
|
+
extended_packer.register_type(0x00, Extended, :to_msgpack_ext)
|
47
|
+
data_extended = extended_packer.pack(object_extended).to_s
|
48
|
+
|
49
|
+
Benchmark.ips do |x|
|
50
|
+
x.report('pack-plain') do
|
51
|
+
MessagePack.pack(object_plain)
|
52
|
+
end
|
53
|
+
|
54
|
+
x.report('pack-structured') do
|
55
|
+
MessagePack.pack(object_structured)
|
56
|
+
end
|
57
|
+
|
58
|
+
x.report('pack-extended') do
|
59
|
+
packer = MessagePack::Packer.new
|
60
|
+
packer.register_type(0x00, Extended, :to_msgpack_ext)
|
61
|
+
packer.pack(object_extended).to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
x.report('unpack-plain') do
|
65
|
+
MessagePack.unpack(data_plain)
|
66
|
+
end
|
67
|
+
|
68
|
+
x.report('unpack-structured') do
|
69
|
+
MessagePack.unpack(data_structured)
|
70
|
+
end
|
71
|
+
|
72
|
+
x.report('unpack-extended') do
|
73
|
+
unpacker = MessagePack::Unpacker.new
|
74
|
+
unpacker.register_type(0x00, Extended, :from_msgpack_ext)
|
75
|
+
unpacker.feed data_extended
|
76
|
+
unpacker.read
|
77
|
+
end
|
78
|
+
end
|
data/doclib/msgpack/factory.rb
CHANGED
@@ -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
|
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,
|
49
|
-
this(runtime,
|
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,
|
57
|
-
this(runtime,
|
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,
|
61
|
-
this(runtime,
|
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,
|
65
|
-
this(runtime,
|
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,
|
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.
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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,
|
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
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
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
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
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
|
69
|
+
public ExtensionEntry lookupExtensionByTypeId(int typeId) {
|
70
70
|
ExtensionEntry e = extensionsByTypeId[typeId + 128];
|
71
71
|
if (e != null && e.hasUnpacker()) {
|
72
|
-
return e
|
73
|
-
} else {
|
74
|
-
return null;
|
72
|
+
return e;
|
75
73
|
}
|
74
|
+
return null;
|
76
75
|
}
|
77
76
|
|
78
|
-
public
|
77
|
+
public ExtensionEntry lookupExtensionForObject(IRubyObject object) {
|
79
78
|
RubyModule lookupClass = null;
|
80
|
-
|
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
|
-
|
88
|
-
if (
|
89
|
-
return
|
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
|
-
|
95
|
-
if (
|
96
|
-
return
|
93
|
+
entry = extensionsByModule.get(realClass);
|
94
|
+
if (entry != null && entry.hasPacker()) {
|
95
|
+
return entry;
|
97
96
|
}
|
98
97
|
}
|
99
98
|
|
100
|
-
|
101
|
-
if (
|
102
|
-
|
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
|
-
|
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
|
}
|