msgpack 1.4.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +57 -0
  3. data/ChangeLog +60 -0
  4. data/README.md +25 -1
  5. data/Rakefile +1 -2
  6. data/bench/bench.rb +78 -0
  7. data/bin/console +8 -0
  8. data/doclib/msgpack/factory.rb +47 -3
  9. data/doclib/msgpack/packer.rb +5 -4
  10. data/doclib/msgpack/unpacker.rb +2 -2
  11. data/ext/java/org/msgpack/jruby/Buffer.java +23 -16
  12. data/ext/java/org/msgpack/jruby/Decoder.java +29 -21
  13. data/ext/java/org/msgpack/jruby/Encoder.java +68 -30
  14. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +37 -49
  15. data/ext/java/org/msgpack/jruby/ExtensionValue.java +5 -8
  16. data/ext/java/org/msgpack/jruby/Factory.java +47 -7
  17. data/ext/java/org/msgpack/jruby/Packer.java +29 -17
  18. data/ext/java/org/msgpack/jruby/Unpacker.java +66 -42
  19. data/ext/msgpack/buffer.c +38 -57
  20. data/ext/msgpack/buffer.h +19 -10
  21. data/ext/msgpack/buffer_class.c +90 -52
  22. data/ext/msgpack/compat.h +0 -99
  23. data/ext/msgpack/extconf.rb +9 -22
  24. data/ext/msgpack/factory_class.c +133 -43
  25. data/ext/msgpack/packer.c +60 -36
  26. data/ext/msgpack/packer.h +27 -18
  27. data/ext/msgpack/packer_class.c +84 -77
  28. data/ext/msgpack/packer_class.h +11 -0
  29. data/ext/msgpack/packer_ext_registry.c +24 -32
  30. data/ext/msgpack/packer_ext_registry.h +40 -33
  31. data/ext/msgpack/sysdep.h +5 -2
  32. data/ext/msgpack/unpacker.c +128 -97
  33. data/ext/msgpack/unpacker.h +17 -10
  34. data/ext/msgpack/unpacker_class.c +75 -80
  35. data/ext/msgpack/unpacker_class.h +11 -0
  36. data/ext/msgpack/unpacker_ext_registry.c +42 -18
  37. data/ext/msgpack/unpacker_ext_registry.h +23 -16
  38. data/lib/msgpack/bigint.rb +69 -0
  39. data/lib/msgpack/factory.rb +103 -0
  40. data/lib/msgpack/symbol.rb +21 -4
  41. data/lib/msgpack/time.rb +1 -1
  42. data/lib/msgpack/version.rb +1 -1
  43. data/lib/msgpack.rb +5 -7
  44. data/msgpack.gemspec +2 -2
  45. data/spec/bigint_spec.rb +26 -0
  46. data/spec/cruby/buffer_spec.rb +17 -0
  47. data/spec/factory_spec.rb +351 -12
  48. data/spec/msgpack_spec.rb +1 -1
  49. data/spec/packer_spec.rb +18 -0
  50. data/spec/spec_helper.rb +20 -3
  51. data/spec/timestamp_spec.rb +38 -0
  52. data/spec/unpacker_spec.rb +54 -4
  53. metadata +25 -41
  54. data/.travis.yml +0 -39
  55. data/bench/pack.rb +0 -23
  56. data/bench/pack_log.rb +0 -33
  57. data/bench/pack_log_long.rb +0 -65
  58. data/bench/pack_symbols.rb +0 -28
  59. data/bench/run.sh +0 -14
  60. data/bench/run_long.sh +0 -35
  61. data/bench/run_symbols.sh +0 -26
  62. data/bench/unpack.rb +0 -21
  63. data/bench/unpack_log.rb +0 -34
  64. data/bench/unpack_log_long.rb +0 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 451ed743fe37bd3726f897f4daae9e7fd9b2cb065a34cf438ca786350060e0ec
4
- data.tar.gz: 87335c4b1d35f9ac88af648963b1fe60caa72059ec7e6c1b6b507027da9118d8
3
+ metadata.gz: bb08f890b2d9a36312600e284933cd0de93a00a6b949507bfc3d7c5c7eab1638
4
+ data.tar.gz: 11e536f8bde329edbc966e5e1050134350024f89f1cb911878f86fd59ff1fc79
5
5
  SHA512:
6
- metadata.gz: eb519049e9d8e4b36682e4b2c2625f10172745ce74ad1cfc8e6d93468b219071ed579bcfd36b7f0e1210eeb177197b435da2a028f46d2c71c73a3bcf2b53dcf8
7
- data.tar.gz: e8c4765ac836226f5a2ce68a62194f112fc77bcdd9744d39040ff2db5a2c27d876348fb6002110c00b122bccdff2d539223d82ff73ea0bcb4cf1a3d9fb62defe
6
+ metadata.gz: 9491cdf494d1826cce7f5e420d73a65ce88c029e060338d9c28fb1e20e4d01dc71fdb46b91859d025bbcc8f5a0471100b171cb9b3bac5fed6f0f0715fd76abb2
7
+ data.tar.gz: 8d0b4db4f93ebf98063f275c0629648ab9316c7cc3cd81738f7f083c6ed4c47fc647c2fc5df1308089b96c36d7e13944028e75bae7e5897b5bb357701c0bcfd9
@@ -0,0 +1,57 @@
1
+ name: ci
2
+
3
+ on:
4
+ pull_request:
5
+ branches: '*'
6
+ push:
7
+ branches:
8
+ - master
9
+ - main
10
+ - 'release-*'
11
+
12
+ jobs:
13
+ mri:
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ os: [ubuntu, macos, windows]
18
+ ruby: ['2.4', '2.5', '2.6', '2.7', '3.0', '3.1']
19
+ runs-on: ${{ matrix.os }}-latest
20
+ steps:
21
+ - uses: actions/checkout@v2
22
+ - uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler-cache: true # 'bundle install' and cache
26
+ - run: bundle exec rake
27
+
28
+ other:
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ os: [ubuntu]
33
+ ruby: ['jruby-9.2.19.0', 'jruby-9.3.3.0', 'truffleruby']
34
+ runs-on: ${{ matrix.os }}-latest
35
+ steps:
36
+ - uses: actions/checkout@v2
37
+ - uses: ruby/setup-ruby@v1
38
+ with:
39
+ ruby-version: ${{ matrix.ruby }}
40
+ bundler-cache: true # 'bundle install' and cache
41
+ - run: bundle exec rake spec
42
+
43
+ head-versions:
44
+ continue-on-error: true
45
+ strategy:
46
+ fail-fast: false
47
+ matrix:
48
+ os: [ubuntu]
49
+ ruby: ['ruby-head', 'jruby-head']
50
+ runs-on: ${{ matrix.os }}-latest
51
+ steps:
52
+ - uses: actions/checkout@v2
53
+ - uses: ruby/setup-ruby@v1
54
+ with:
55
+ ruby-version: ${{ matrix.ruby }}
56
+ bundler-cache: true # 'bundle install' and cache
57
+ - run: bundle exec rake spec || echo "failed, but ignore it"
data/ChangeLog CHANGED
@@ -1,3 +1,63 @@
1
+ 2022-09-30 1.6.0:
2
+
3
+ * Fix a potential use-after-free bug in Buffer_free when accessing a packer or unpacker buffer.
4
+ * `old-style-definition` compilation warnings.
5
+ * Restore zero-copy buffer feed when provided a Ruby string. This was accidentally broken in 1.5.4.
6
+ * Provide implementations for `ObjectSpace.memsize`. Message pack objects now properly report their size to Ruby.
7
+ * Fix an endianess bug on Windows platform.
8
+
9
+ 2022-08-23 1.5.6:
10
+
11
+ * No actual code change, just re-release the `java` version properly.
12
+
13
+ 2022-08-22 1.5.5:
14
+
15
+ * Fix a segfault when GC triggers inside a recursive extension.
16
+
17
+ 2022-07-25 1.5.4:
18
+
19
+ * Fix a segfault when deserializing empty symbol (`:""`).
20
+ * Improve compilation flags to not strip debug symbols.
21
+
22
+ 2022-05-30 version 1.5.3:
23
+
24
+ * Fix deduplication of empty strings when using the `freeze: true` option.
25
+ * Use `rb_hash_new_capa` when available (Ruby 3.2) for improved performance when parsing large hashes.
26
+
27
+ 2022-05-27 version 1.5.2:
28
+
29
+ * Fix bug about unpacking ext type objects with the recursive option
30
+
31
+ 2022-04-07 version 1.5.1:
32
+
33
+ * Fix bug about packing/unpacking ext type objects with the recursive option
34
+
35
+ 2022-04-06 version 1.5.0:
36
+
37
+ * Add recursive option on Factory#register_type to operate Packer/Unpacker manually
38
+ * Add oversized_integer_extension option on Factory#register_type to pack/unpack bigint using ext types
39
+ * Add Factory#pool method and Factory::Pool class to provide pooled Packer and Unpacker instances
40
+
41
+ 2022-02-15 version 1.4.5:
42
+
43
+ * Fix to create UTF-8 Symbol keys when symbolize_keys: true
44
+ * Fix to assume Symbols as US-ASCII or UTF-8
45
+ * Optimize Packer/Unpacker initialization
46
+ * Optimize extension class lookup
47
+ * Rename Packer#clear as Packer#reset (#clear is still available as an alias)
48
+
49
+ 2022-01-22 version 1.4.4:
50
+
51
+ * Specify the build option --platform=8 for older Java platforms
52
+
53
+ 2022-01-20 version 1.4.3:
54
+
55
+ * Optimize serialization/deserialization of Symbols
56
+ * Support registering ext types for objects of subclasses of primitive types (like Hash)
57
+ * Add optimized_symbols_parsing option to Factory#register_type on MRI implementation
58
+ * Optimize to deduplicate Hash keys on JRuby
59
+ * Support JRuby 9.3 (and drop 9.1)
60
+
1
61
  2021-02-01 version 1.4.2:
2
62
 
3
63
  * Add the required Ruby version (>= 2.4) to avoid compilation errors on older Ruby runtimes
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 is highly welcomed.
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/Rakefile CHANGED
@@ -34,8 +34,7 @@ if RUBY_PLATFORM =~ /java/
34
34
  jars = ["#{jruby_home}/lib/jruby.jar"]
35
35
  ext.classpath = jars.map { |x| File.expand_path(x) }.join(':')
36
36
  ext.lib_dir = File.join(*['lib', 'msgpack', ENV['FAT_DIR']].compact)
37
- ext.source_version = '1.6'
38
- ext.target_version = '1.6'
37
+ ext.release = '8'
39
38
  end
40
39
  else
41
40
  require 'rake/extensiontask'
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/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "msgpack"
6
+
7
+ require "irb"
8
+ IRB.start(__FILE__)
@@ -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)
@@ -75,6 +75,8 @@ module MessagePack
75
75
  #
76
76
  # * *:packer* specify symbol or proc object for packer
77
77
  # * *:unpacker* specify symbol or proc object for unpacker
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.
78
80
  #
79
81
  def register_type(type, klass, options={})
80
82
  end
@@ -97,5 +99,47 @@ module MessagePack
97
99
  #
98
100
  def type_registered?(klass_or_type, selector=:both)
99
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
100
144
  end
101
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.
@@ -21,6 +21,7 @@ import org.jcodings.Encoding;
21
21
 
22
22
  @JRubyClass(name="MessagePack::Buffer")
23
23
  public class Buffer extends RubyObject {
24
+ private static final long serialVersionUID = 8441244627425629412L;
24
25
  private IRubyObject io;
25
26
  private ByteBuffer buffer;
26
27
  private boolean writeMode;
@@ -49,7 +50,7 @@ public class Buffer extends RubyObject {
49
50
  }
50
51
  this.buffer = ByteBuffer.allocate(CACHE_LINE_SIZE - ARRAY_HEADER_SIZE);
51
52
  this.writeMode = true;
52
- this.binaryEncoding = ctx.getRuntime().getEncodingService().getAscii8bitEncoding();
53
+ this.binaryEncoding = ctx.runtime.getEncodingService().getAscii8bitEncoding();
53
54
  return this;
54
55
  }
55
56
 
@@ -87,17 +88,17 @@ public class Buffer extends RubyObject {
87
88
  writeMode = true;
88
89
  }
89
90
  buffer.clear();
90
- return ctx.getRuntime().getNil();
91
+ return ctx.runtime.getNil();
91
92
  }
92
93
 
93
94
  @JRubyMethod(name = "size")
94
95
  public IRubyObject size(ThreadContext ctx) {
95
- return ctx.getRuntime().newFixnum(rawSize());
96
+ return ctx.runtime.newFixnum(rawSize());
96
97
  }
97
98
 
98
99
  @JRubyMethod(name = "empty?")
99
100
  public IRubyObject isEmpty(ThreadContext ctx) {
100
- return rawSize() == 0 ? ctx.getRuntime().getTrue() : ctx.getRuntime().getFalse();
101
+ return rawSize() == 0 ? ctx.runtime.getTrue() : ctx.runtime.getFalse();
101
102
  }
102
103
 
103
104
  private IRubyObject bufferWrite(ThreadContext ctx, IRubyObject str) {
@@ -105,7 +106,7 @@ public class Buffer extends RubyObject {
105
106
  int length = bytes.length();
106
107
  ensureRemainingCapacity(length);
107
108
  buffer.put(bytes.unsafeBytes(), bytes.begin(), length);
108
- return ctx.getRuntime().newFixnum(length);
109
+ return ctx.runtime.newFixnum(length);
109
110
 
110
111
  }
111
112
 
@@ -131,19 +132,19 @@ public class Buffer extends RubyObject {
131
132
  length = (int) args[0].convertToInteger().getLongValue();
132
133
  }
133
134
  if (raiseOnUnderflow && rawSize() < length) {
134
- throw ctx.getRuntime().newEOFError();
135
+ throw ctx.runtime.newEOFError();
135
136
  }
136
137
  int readLength = Math.min(length, rawSize());
137
138
  if (readLength == 0 && length > 0) {
138
- return ctx.getRuntime().getNil();
139
+ return ctx.runtime.getNil();
139
140
  } else if (readLength == 0) {
140
- return ctx.getRuntime().newString();
141
+ return ctx.runtime.newString();
141
142
  } else {
142
143
  ensureReadMode();
143
144
  byte[] bytes = new byte[readLength];
144
145
  buffer.get(bytes);
145
146
  ByteList byteList = new ByteList(bytes, binaryEncoding);
146
- return ctx.getRuntime().newString(byteList);
147
+ return ctx.runtime.newString(byteList);
147
148
  }
148
149
  }
149
150
 
@@ -161,12 +162,12 @@ public class Buffer extends RubyObject {
161
162
  feed(ctx);
162
163
  int length = (int) _length.convertToInteger().getLongValue();
163
164
  if (raiseOnUnderflow && rawSize() < length) {
164
- throw ctx.getRuntime().newEOFError();
165
+ throw ctx.runtime.newEOFError();
165
166
  }
166
167
  ensureReadMode();
167
168
  int skipLength = Math.min(length, rawSize());
168
169
  buffer.position(buffer.position() + skipLength);
169
- return ctx.getRuntime().newFixnum(skipLength);
170
+ return ctx.runtime.newFixnum(skipLength);
170
171
  }
171
172
 
172
173
  @JRubyMethod(name = "skip")
@@ -188,23 +189,23 @@ public class Buffer extends RubyObject {
188
189
  ensureReadMode();
189
190
  int length = buffer.limit() - buffer.position();
190
191
  ByteList str = new ByteList(buffer.array(), buffer.position(), length, binaryEncoding, true);
191
- return ctx.getRuntime().newString(str);
192
+ return ctx.runtime.newString(str);
192
193
  }
193
194
 
194
195
  @JRubyMethod(name = "to_a")
195
196
  public IRubyObject toA(ThreadContext ctx) {
196
- return ctx.getRuntime().newArray(toS(ctx));
197
+ return ctx.runtime.newArray(toS(ctx));
197
198
  }
198
199
 
199
200
  @JRubyMethod(name = "io")
200
201
  public IRubyObject getIo(ThreadContext ctx) {
201
- return io == null ? ctx.getRuntime().getNil() : io;
202
+ return io == null ? ctx.runtime.getNil() : io;
202
203
  }
203
204
 
204
205
  @JRubyMethod(name = "flush")
205
206
  public IRubyObject flush(ThreadContext ctx) {
206
207
  if (io == null) {
207
- return ctx.getRuntime().getNil();
208
+ return ctx.runtime.getNil();
208
209
  } else {
209
210
  return io.callMethod(ctx, "flush");
210
211
  }
@@ -213,7 +214,7 @@ public class Buffer extends RubyObject {
213
214
  @JRubyMethod(name = "close")
214
215
  public IRubyObject close(ThreadContext ctx) {
215
216
  if (io == null) {
216
- return ctx.getRuntime().getNil();
217
+ return ctx.runtime.getNil();
217
218
  } else {
218
219
  return io.callMethod(ctx, "close");
219
220
  }
@@ -223,4 +224,10 @@ public class Buffer extends RubyObject {
223
224
  public IRubyObject writeTo(ThreadContext ctx, IRubyObject io) {
224
225
  return io.callMethod(ctx, "write", readCommon(ctx, null, false));
225
226
  }
227
+
228
+ public ByteList getBytes() {
229
+ byte[] bytes = new byte[rawSize()];
230
+ buffer.get(bytes);
231
+ return new ByteList(bytes, binaryEncoding);
232
+ }
226
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;
@@ -122,7 +123,6 @@ public class Decoder implements Iterator<IRubyObject> {
122
123
  ByteList byteList = new ByteList(bytes, encoding);
123
124
  RubyString string = runtime.newString(byteList);
124
125
  if (this.freeze) {
125
- string.setFrozen(true);
126
126
  string = runtime.freezeAndDedupString(string);
127
127
  }
128
128
  return string;
@@ -140,9 +140,14 @@ public class Decoder implements Iterator<IRubyObject> {
140
140
  RubyHash hash = RubyHash.newHash(runtime);
141
141
  for (int i = 0; i < size; i++) {
142
142
  IRubyObject key = next();
143
- if (this.symbolizeKeys && key instanceof RubyString) {
143
+ if (key instanceof RubyString) {
144
+ if (this.symbolizeKeys) {
144
145
  key = ((RubyString) key).intern();
146
+ } else {
147
+ key = runtime.freezeAndDedupString((RubyString) key);
148
+ }
145
149
  }
150
+
146
151
  hash.fastASet(key, next());
147
152
  }
148
153
  return hash;
@@ -150,18 +155,21 @@ public class Decoder implements Iterator<IRubyObject> {
150
155
 
151
156
  private IRubyObject consumeExtension(int size) {
152
157
  int type = buffer.get();
153
- byte[] payload = readBytes(size);
154
-
155
- if (registry != null) {
156
- IRubyObject proc = registry.lookupUnpackerByTypeId(type);
157
- if (proc != null) {
158
- ByteList byteList = new ByteList(payload, runtime.getEncodingService().getAscii8bitEncoding());
159
- 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
+ }
160
168
  }
161
169
  }
162
170
 
163
171
  if (this.allowUnknownExt) {
164
- return ExtensionValue.newExtensionValue(runtime, type, payload);
172
+ return ExtensionValue.newExtensionValue(runtime, type, readBytes(size));
165
173
  }
166
174
 
167
175
  throw runtime.newRaiseException(unknownExtTypeErrorClass, "unexpected extension type");