msgpack 1.4.3 → 1.5.0

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +7 -6
  3. data/ChangeLog +18 -0
  4. data/README.md +22 -0
  5. data/Rakefile +1 -2
  6. data/doclib/msgpack/factory.rb +46 -3
  7. data/doclib/msgpack/packer.rb +5 -4
  8. data/doclib/msgpack/unpacker.rb +2 -2
  9. data/ext/java/org/msgpack/jruby/Buffer.java +6 -0
  10. data/ext/java/org/msgpack/jruby/Decoder.java +23 -19
  11. data/ext/java/org/msgpack/jruby/Encoder.java +45 -18
  12. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +28 -40
  13. data/ext/java/org/msgpack/jruby/Factory.java +40 -5
  14. data/ext/java/org/msgpack/jruby/Packer.java +21 -11
  15. data/ext/java/org/msgpack/jruby/Unpacker.java +44 -22
  16. data/ext/msgpack/buffer.h +2 -2
  17. data/ext/msgpack/buffer_class.c +23 -15
  18. data/ext/msgpack/factory_class.c +78 -16
  19. data/ext/msgpack/packer.c +42 -5
  20. data/ext/msgpack/packer.h +24 -0
  21. data/ext/msgpack/packer_class.c +29 -22
  22. data/ext/msgpack/packer_ext_registry.c +23 -9
  23. data/ext/msgpack/packer_ext_registry.h +38 -31
  24. data/ext/msgpack/unpacker.c +37 -19
  25. data/ext/msgpack/unpacker.h +2 -2
  26. data/ext/msgpack/unpacker_class.c +26 -45
  27. data/ext/msgpack/unpacker_ext_registry.c +40 -16
  28. data/ext/msgpack/unpacker_ext_registry.h +21 -14
  29. data/lib/msgpack/bigint.rb +69 -0
  30. data/lib/msgpack/factory.rb +103 -0
  31. data/lib/msgpack/symbol.rb +8 -1
  32. data/lib/msgpack/version.rb +1 -1
  33. data/lib/msgpack.rb +4 -5
  34. data/msgpack.gemspec +1 -2
  35. data/spec/bigint_spec.rb +26 -0
  36. data/spec/factory_spec.rb +263 -14
  37. data/spec/spec_helper.rb +3 -4
  38. data/spec/timestamp_spec.rb +0 -2
  39. data/spec/unpacker_spec.rb +22 -3
  40. metadata +7 -29
@@ -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
  }
@@ -56,30 +56,48 @@ public class Unpacker extends RubyObject {
56
56
 
57
57
  @JRubyMethod(name = "initialize", optional = 2, visibility = PRIVATE)
58
58
  public IRubyObject initialize(ThreadContext ctx, IRubyObject[] args) {
59
+ Ruby runtime = ctx.runtime;
60
+
59
61
  symbolizeKeys = false;
60
62
  allowUnknownExt = false;
61
63
  freeze = false;
62
- if (args.length > 0) {
63
- Ruby runtime = ctx.runtime;
64
- if (args[args.length - 1] instanceof RubyHash) {
65
- RubyHash options = (RubyHash) args[args.length - 1];
66
- IRubyObject sk = options.fastARef(runtime.newSymbol("symbolize_keys"));
67
- if (sk != null) {
68
- symbolizeKeys = sk.isTrue();
69
- }
70
- IRubyObject f = options.fastARef(runtime.newSymbol("freeze"));
71
- if (f != null) {
72
- freeze = f.isTrue();
73
- }
74
- IRubyObject au = options.fastARef(runtime.newSymbol("allow_unknown_ext"));
75
- if (au != null) {
76
- allowUnknownExt = au.isTrue();
77
- }
64
+
65
+ IRubyObject io = null;
66
+ RubyHash options = null;
67
+
68
+ if (args.length >= 1) {
69
+ io = args[0];
70
+ }
71
+
72
+ if (args.length >= 2 && args[1] != runtime.getNil()) {
73
+ options = (RubyHash)args[1];
74
+ }
75
+
76
+ if (options == null && io != null && io instanceof RubyHash) {
77
+ options = (RubyHash)io;
78
+ io = null;
79
+ }
80
+
81
+ if (options != null) {
82
+ IRubyObject sk = options.fastARef(runtime.newSymbol("symbolize_keys"));
83
+ if (sk != null) {
84
+ symbolizeKeys = sk.isTrue();
78
85
  }
79
- if (args[0] != runtime.getNil() && !(args[0] instanceof RubyHash)) {
80
- setStream(ctx, args[0]);
86
+ IRubyObject f = options.fastARef(runtime.newSymbol("freeze"));
87
+ if (f != null) {
88
+ freeze = f.isTrue();
81
89
  }
90
+ IRubyObject au = options.fastARef(runtime.newSymbol("allow_unknown_ext"));
91
+ if (au != null) {
92
+ allowUnknownExt = au.isTrue();
93
+ }
94
+
82
95
  }
96
+
97
+ if (io != null && io != runtime.getNil()) {
98
+ setStream(ctx, io);
99
+ }
100
+
83
101
  return this;
84
102
  }
85
103
 
@@ -139,7 +157,7 @@ public class Unpacker extends RubyObject {
139
157
  throw runtime.newRangeError(String.format("integer %d too big to convert to `signed char'", typeId));
140
158
  }
141
159
 
142
- registry.put(extModule, (int) typeId, null, null, proc, arg);
160
+ registry.put(extModule, (int) typeId, false, null, null, proc, arg);
143
161
  return runtime.getNil();
144
162
  }
145
163
 
@@ -157,7 +175,7 @@ public class Unpacker extends RubyObject {
157
175
  if (limit == -1) {
158
176
  limit = byteList.length() - offset;
159
177
  }
160
- Decoder decoder = new Decoder(ctx.runtime, registry, byteList.unsafeBytes(), byteList.begin() + offset, limit, symbolizeKeys, freeze, allowUnknownExt);
178
+ Decoder decoder = new Decoder(ctx.runtime, this, byteList.unsafeBytes(), byteList.begin() + offset, limit, symbolizeKeys, freeze, allowUnknownExt);
161
179
  try {
162
180
  data = null;
163
181
  data = decoder.next();
@@ -187,7 +205,7 @@ public class Unpacker extends RubyObject {
187
205
  public IRubyObject feed(ThreadContext ctx, IRubyObject data) {
188
206
  ByteList byteList = data.asString().getByteList();
189
207
  if (decoder == null) {
190
- decoder = new Decoder(ctx.runtime, registry, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, freeze, allowUnknownExt);
208
+ decoder = new Decoder(ctx.runtime, this, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, freeze, allowUnknownExt);
191
209
  } else {
192
210
  decoder.feed(byteList.unsafeBytes(), byteList.begin(), byteList.length());
193
211
  }
@@ -325,7 +343,11 @@ public class Unpacker extends RubyObject {
325
343
  ByteList byteList = str.getByteList();
326
344
  this.stream = stream;
327
345
  this.decoder = null;
328
- this.decoder = new Decoder(ctx.runtime, registry, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, freeze, allowUnknownExt);
346
+ this.decoder = new Decoder(ctx.runtime, this, byteList.unsafeBytes(), byteList.begin(), byteList.length(), symbolizeKeys, freeze, allowUnknownExt);
329
347
  return getStream(ctx);
330
348
  }
349
+
350
+ public ExtensionRegistry.ExtensionEntry lookupExtensionByTypeId(int typeId) {
351
+ return registry.lookupExtensionByTypeId(typeId);
352
+ }
331
353
  }
data/ext/msgpack/buffer.h CHANGED
@@ -494,9 +494,9 @@ static inline VALUE msgpack_buffer_read_top_as_string(msgpack_buffer_t* b, size_
494
494
  #endif // HAVE_RB_ENC_INTERNED_STR
495
495
  }
496
496
 
497
- static inline VALUE msgpack_buffer_read_top_as_symbol(msgpack_buffer_t* b, size_t length)
497
+ static inline VALUE msgpack_buffer_read_top_as_symbol(msgpack_buffer_t* b, size_t length, bool utf8)
498
498
  {
499
- return rb_str_intern(msgpack_buffer_read_top_as_string(b, length, true, false));
499
+ return rb_str_intern(msgpack_buffer_read_top_as_string(b, length, true, utf8));
500
500
  }
501
501
 
502
502
  #endif
@@ -29,6 +29,11 @@ static ID s_write;
29
29
  static ID s_append;
30
30
  static ID s_close;
31
31
 
32
+ static VALUE sym_read_reference_threshold;
33
+ static VALUE sym_write_reference_threshold;
34
+ static VALUE sym_io_buffer_size;
35
+
36
+
32
37
  #define BUFFER(from, name) \
33
38
  msgpack_buffer_t *name = NULL; \
34
39
  Data_Get_Struct(from, msgpack_buffer_t, name); \
@@ -62,24 +67,22 @@ static VALUE Buffer_alloc(VALUE klass)
62
67
 
63
68
  static ID get_partial_read_method(VALUE io)
64
69
  {
65
- if(rb_respond_to(io, s_readpartial)) {
70
+ if(io != Qnil && rb_respond_to(io, s_readpartial)) {
66
71
  return s_readpartial;
67
- } else if(rb_respond_to(io, s_read)) {
68
- return s_read;
69
- } else {
70
- return s_read;
71
72
  }
73
+ return s_read;
72
74
  }
73
75
 
74
76
  static ID get_write_all_method(VALUE io)
75
77
  {
76
- if(rb_respond_to(io, s_write)) {
77
- return s_write;
78
- } else if(rb_respond_to(io, s_append)) {
79
- return s_append;
80
- } else {
81
- return s_write;
78
+ if(io != Qnil) {
79
+ if(rb_respond_to(io, s_write)) {
80
+ return s_write;
81
+ } else if(rb_respond_to(io, s_append)) {
82
+ return s_append;
83
+ }
82
84
  }
85
+ return s_write;
83
86
  }
84
87
 
85
88
  void MessagePack_Buffer_set_options(msgpack_buffer_t* b, VALUE io, VALUE options)
@@ -91,17 +94,17 @@ void MessagePack_Buffer_set_options(msgpack_buffer_t* b, VALUE io, VALUE options
91
94
  if(options != Qnil) {
92
95
  VALUE v;
93
96
 
94
- v = rb_hash_aref(options, ID2SYM(rb_intern("read_reference_threshold")));
97
+ v = rb_hash_aref(options, sym_read_reference_threshold);
95
98
  if(v != Qnil) {
96
99
  msgpack_buffer_set_read_reference_threshold(b, NUM2ULONG(v));
97
100
  }
98
101
 
99
- v = rb_hash_aref(options, ID2SYM(rb_intern("write_reference_threshold")));
102
+ v = rb_hash_aref(options, sym_write_reference_threshold);
100
103
  if(v != Qnil) {
101
104
  msgpack_buffer_set_write_reference_threshold(b, NUM2ULONG(v));
102
105
  }
103
106
 
104
- v = rb_hash_aref(options, ID2SYM(rb_intern("io_buffer_size")));
107
+ v = rb_hash_aref(options, sym_io_buffer_size);
105
108
  if(v != Qnil) {
106
109
  msgpack_buffer_set_io_buffer_size(b, NUM2ULONG(v));
107
110
  }
@@ -245,10 +248,11 @@ static VALUE read_until_eof_rescue(VALUE args)
245
248
  return Qnil;
246
249
  }
247
250
 
248
- static VALUE read_until_eof_error(VALUE args)
251
+ static VALUE read_until_eof_error(VALUE args, VALUE error)
249
252
  {
250
253
  /* ignore EOFError */
251
254
  UNUSED(args);
255
+ UNUSED(error);
252
256
  return Qnil;
253
257
  }
254
258
 
@@ -480,6 +484,10 @@ void MessagePack_Buffer_module_init(VALUE mMessagePack)
480
484
  s_append = rb_intern("<<");
481
485
  s_close = rb_intern("close");
482
486
 
487
+ sym_read_reference_threshold = ID2SYM(rb_intern("read_reference_threshold"));
488
+ sym_write_reference_threshold = ID2SYM(rb_intern("write_reference_threshold"));
489
+ sym_io_buffer_size = ID2SYM(rb_intern("io_buffer_size"));
490
+
483
491
  msgpack_buffer_static_init();
484
492
 
485
493
  cMessagePack_Buffer = rb_define_class_under(mMessagePack, "Buffer", rb_cObject);
@@ -30,7 +30,8 @@ typedef struct msgpack_factory_t msgpack_factory_t;
30
30
 
31
31
  struct msgpack_factory_t {
32
32
  msgpack_packer_ext_registry_t pkrg;
33
- msgpack_unpacker_ext_registry_t ukrg;
33
+ msgpack_unpacker_ext_registry_t *ukrg;
34
+ bool has_bigint_ext_type;
34
35
  bool has_symbol_ext_type;
35
36
  bool optimized_symbol_ext_type;
36
37
  int symbol_ext_type;
@@ -49,14 +50,14 @@ static void Factory_free(msgpack_factory_t* fc)
49
50
  return;
50
51
  }
51
52
  msgpack_packer_ext_registry_destroy(&fc->pkrg);
52
- msgpack_unpacker_ext_registry_destroy(&fc->ukrg);
53
+ msgpack_unpacker_ext_registry_release(fc->ukrg);
53
54
  xfree(fc);
54
55
  }
55
56
 
56
57
  void Factory_mark(msgpack_factory_t* fc)
57
58
  {
58
59
  msgpack_packer_ext_registry_mark(&fc->pkrg);
59
- msgpack_unpacker_ext_registry_mark(&fc->ukrg);
60
+ msgpack_unpacker_ext_registry_mark(fc->ukrg);
60
61
  }
61
62
 
62
63
  static VALUE Factory_alloc(VALUE klass)
@@ -72,7 +73,7 @@ static VALUE Factory_initialize(int argc, VALUE* argv, VALUE self)
72
73
  FACTORY(self, fc);
73
74
 
74
75
  msgpack_packer_ext_registry_init(&fc->pkrg);
75
- msgpack_unpacker_ext_registry_init(&fc->ukrg);
76
+ // fc->ukrg is lazily initialized
76
77
 
77
78
  fc->has_symbol_ext_type = false;
78
79
 
@@ -87,6 +88,41 @@ static VALUE Factory_initialize(int argc, VALUE* argv, VALUE self)
87
88
  return Qnil;
88
89
  }
89
90
 
91
+ static VALUE Factory_dup(VALUE self)
92
+ {
93
+ VALUE clone = Factory_alloc(rb_obj_class(self));
94
+
95
+ FACTORY(self, fc);
96
+ FACTORY(clone, cloned_fc);
97
+
98
+ cloned_fc->has_symbol_ext_type = fc->has_symbol_ext_type;
99
+ cloned_fc->pkrg = fc->pkrg;
100
+ msgpack_unpacker_ext_registry_borrow(fc->ukrg, &cloned_fc->ukrg);
101
+ msgpack_packer_ext_registry_dup(&fc->pkrg, &cloned_fc->pkrg);
102
+
103
+ return clone;
104
+ }
105
+
106
+ static VALUE Factory_freeze(VALUE self) {
107
+ if(!rb_obj_frozen_p(self)) {
108
+ FACTORY(self, fc);
109
+
110
+ if (RTEST(fc->pkrg.hash)) {
111
+ rb_hash_freeze(fc->pkrg.hash);
112
+ if (!RTEST(fc->pkrg.cache)) {
113
+ // If the factory is frozen, we can safely share the packer cache between
114
+ // all packers. So we eagerly create it now so it's available when #packer
115
+ // is called.
116
+ fc->pkrg.cache = rb_hash_new();
117
+ }
118
+ }
119
+
120
+ rb_obj_freeze(self);
121
+ }
122
+
123
+ return self;
124
+ }
125
+
90
126
  VALUE MessagePack_Factory_packer(int argc, VALUE* argv, VALUE self)
91
127
  {
92
128
  FACTORY(self, fc);
@@ -99,6 +135,7 @@ VALUE MessagePack_Factory_packer(int argc, VALUE* argv, VALUE self)
99
135
 
100
136
  msgpack_packer_ext_registry_destroy(&pk->ext_registry);
101
137
  msgpack_packer_ext_registry_dup(&fc->pkrg, &pk->ext_registry);
138
+ pk->has_bigint_ext_type = fc->has_bigint_ext_type;
102
139
  pk->has_symbol_ext_type = fc->has_symbol_ext_type;
103
140
 
104
141
  return packer;
@@ -113,9 +150,7 @@ VALUE MessagePack_Factory_unpacker(int argc, VALUE* argv, VALUE self)
113
150
 
114
151
  msgpack_unpacker_t* uk;
115
152
  Data_Get_Struct(unpacker, msgpack_unpacker_t, uk);
116
-
117
- msgpack_unpacker_ext_registry_destroy(&uk->ext_registry);
118
- msgpack_unpacker_ext_registry_dup(&fc->ukrg, &uk->ext_registry);
153
+ msgpack_unpacker_ext_registry_borrow(fc->ukrg, &uk->ext_registry);
119
154
  uk->optimized_symbol_ext_type = fc->optimized_symbol_ext_type;
120
155
  uk->symbol_ext_type = fc->symbol_ext_type;
121
156
 
@@ -127,12 +162,19 @@ static VALUE Factory_registered_types_internal(VALUE self)
127
162
  FACTORY(self, fc);
128
163
 
129
164
  VALUE uk_mapping = rb_hash_new();
130
- for(int i=0; i < 256; i++) {
131
- if(fc->ukrg.array[i] != Qnil) {
132
- rb_hash_aset(uk_mapping, INT2FIX(i - 128), fc->ukrg.array[i]);
165
+ if (fc->ukrg) {
166
+ for(int i=0; i < 256; i++) {
167
+ if(fc->ukrg->array[i] != Qnil) {
168
+ rb_hash_aset(uk_mapping, INT2FIX(i - 128), fc->ukrg->array[i]);
169
+ }
133
170
  }
134
171
  }
135
- return rb_ary_new3(2, rb_hash_dup(fc->pkrg.hash), uk_mapping);
172
+
173
+ return rb_ary_new3(
174
+ 2,
175
+ RTEST(fc->pkrg.hash) ? rb_hash_dup(fc->pkrg.hash) : rb_hash_new(),
176
+ uk_mapping
177
+ );
136
178
  }
137
179
 
138
180
  static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
@@ -140,6 +182,7 @@ static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
140
182
  FACTORY(self, fc);
141
183
 
142
184
  int ext_type;
185
+ int flags = 0;
143
186
  VALUE ext_module;
144
187
  VALUE options = Qnil;
145
188
  VALUE packer_arg, unpacker_arg;
@@ -168,6 +211,10 @@ static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
168
211
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 2..3)", argc);
169
212
  }
170
213
 
214
+ if (options != Qnil) {
215
+ Check_Type(options, T_HASH);
216
+ }
217
+
171
218
  ext_type = NUM2INT(argv[0]);
172
219
  if(ext_type < -128 || ext_type > 127) {
173
220
  rb_raise(rb_eRangeError, "integer %d too big to convert to `signed char'", ext_type);
@@ -195,16 +242,29 @@ static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
195
242
  }
196
243
  }
197
244
 
198
- msgpack_packer_ext_registry_put(&fc->pkrg, ext_module, ext_type, packer_proc, packer_arg);
199
-
200
- if (ext_module == rb_cSymbol) {
245
+ if(ext_module == rb_cSymbol) {
201
246
  fc->has_symbol_ext_type = true;
202
- if(RB_TEST(options) && RB_TEST(rb_hash_aref(options, ID2SYM(rb_intern("optimized_symbols_parsing"))))) {
247
+ if(RTEST(options) && RTEST(rb_hash_aref(options, ID2SYM(rb_intern("optimized_symbols_parsing"))))) {
203
248
  fc->optimized_symbol_ext_type = true;
204
249
  }
205
250
  }
206
251
 
207
- msgpack_unpacker_ext_registry_put(&fc->ukrg, ext_module, ext_type, unpacker_proc, unpacker_arg);
252
+ if(RTEST(options)) {
253
+ if(RTEST(rb_hash_aref(options, ID2SYM(rb_intern("oversized_integer_extension"))))) {
254
+ if(ext_module == rb_cInteger) {
255
+ fc->has_bigint_ext_type = true;
256
+ } else {
257
+ rb_raise(rb_eArgError, "oversized_integer_extension: true is only for Integer class");
258
+ }
259
+ }
260
+
261
+ if(RTEST(rb_hash_aref(options, ID2SYM(rb_intern("recursive"))))) {
262
+ flags |= MSGPACK_EXT_RECURSIVE;
263
+ }
264
+ }
265
+
266
+ msgpack_packer_ext_registry_put(&fc->pkrg, ext_module, ext_type, flags, packer_proc, packer_arg);
267
+ msgpack_unpacker_ext_registry_put(&fc->ukrg, ext_module, ext_type, flags, unpacker_proc, unpacker_arg);
208
268
 
209
269
  return Qnil;
210
270
  }
@@ -216,6 +276,8 @@ void MessagePack_Factory_module_init(VALUE mMessagePack)
216
276
  rb_define_alloc_func(cMessagePack_Factory, Factory_alloc);
217
277
 
218
278
  rb_define_method(cMessagePack_Factory, "initialize", Factory_initialize, -1);
279
+ rb_define_method(cMessagePack_Factory, "dup", Factory_dup, 0);
280
+ rb_define_method(cMessagePack_Factory, "freeze", Factory_freeze, 0);
219
281
 
220
282
  rb_define_method(cMessagePack_Factory, "packer", MessagePack_Factory_packer, -1);
221
283
  rb_define_method(cMessagePack_Factory, "unpacker", MessagePack_Factory_unpacker, -1);
data/ext/msgpack/packer.c CHANGED
@@ -121,19 +121,56 @@ void msgpack_packer_write_hash_value(msgpack_packer_t* pk, VALUE v)
121
121
  #endif
122
122
  }
123
123
 
124
+ struct msgpack_call_proc_args_t;
125
+ typedef struct msgpack_call_proc_args_t msgpack_call_proc_args_t;
126
+ struct msgpack_call_proc_args_t {
127
+ VALUE proc;
128
+ VALUE arg;
129
+ VALUE packer;
130
+ };
131
+
132
+ VALUE msgpack_packer_try_calling_proc(VALUE value)
133
+ {
134
+ msgpack_call_proc_args_t *args = (msgpack_call_proc_args_t *)value;
135
+ return rb_funcall(args->proc, s_call, 2, args->arg, args->packer);
136
+ }
137
+
124
138
  bool msgpack_packer_try_write_with_ext_type_lookup(msgpack_packer_t* pk, VALUE v)
125
139
  {
126
- int ext_type;
140
+ int ext_type, ext_flags;
127
141
 
128
- VALUE proc = msgpack_packer_ext_registry_lookup(&pk->ext_registry, v, &ext_type);
142
+ VALUE proc = msgpack_packer_ext_registry_lookup(&pk->ext_registry, v, &ext_type, &ext_flags);
129
143
 
130
- if(proc != Qnil) {
144
+ if(proc == Qnil) {
145
+ return false;
146
+ }
147
+
148
+ if(ext_flags & MSGPACK_EXT_RECURSIVE) {
149
+ msgpack_buffer_t parent_buffer = pk->buffer;
150
+ msgpack_buffer_init(PACKER_BUFFER_(pk));
151
+
152
+ int exception_occured = 0;
153
+ msgpack_call_proc_args_t args = { proc, v, pk->to_msgpack_arg };
154
+ rb_protect(msgpack_packer_try_calling_proc, (VALUE)&args, &exception_occured);
155
+
156
+ if (exception_occured) {
157
+ msgpack_buffer_destroy(PACKER_BUFFER_(pk));
158
+ pk->buffer = parent_buffer;
159
+ rb_jump_tag(exception_occured); // re-raise the exception
160
+ } else {
161
+ VALUE payload = msgpack_buffer_all_as_string(PACKER_BUFFER_(pk));
162
+ StringValue(payload);
163
+ msgpack_buffer_destroy(PACKER_BUFFER_(pk));
164
+ pk->buffer = parent_buffer;
165
+ msgpack_packer_write_ext(pk, ext_type, payload);
166
+ }
167
+ } else {
131
168
  VALUE payload = rb_funcall(proc, s_call, 1, v);
132
169
  StringValue(payload);
133
170
  msgpack_packer_write_ext(pk, ext_type, payload);
134
- return true;
135
171
  }
136
- return false;
172
+
173
+ return true;
137
174
  }
138
175
 
139
176
  void msgpack_packer_write_other_value(msgpack_packer_t* pk, VALUE v)
data/ext/msgpack/packer.h CHANGED
@@ -32,6 +32,7 @@ struct msgpack_packer_t {
32
32
  msgpack_buffer_t buffer;
33
33
 
34
34
  bool compatibility_mode;
35
+ bool has_bigint_ext_type;
35
36
  bool has_symbol_ext_type;
36
37
 
37
38
  ID to_msgpack_method;
@@ -56,6 +57,8 @@ void msgpack_packer_destroy(msgpack_packer_t* pk);
56
57
 
57
58
  void msgpack_packer_mark(msgpack_packer_t* pk);
58
59
 
60
+ bool msgpack_packer_try_write_with_ext_type_lookup(msgpack_packer_t* pk, VALUE v);
61
+
59
62
  static inline void msgpack_packer_set_to_msgpack_method(msgpack_packer_t* pk,
60
63
  ID to_msgpack_method, VALUE to_msgpack_arg)
61
64
  {
@@ -469,9 +472,30 @@ static inline void msgpack_packer_write_fixnum_value(msgpack_packer_t* pk, VALUE
469
472
 
470
473
  static inline void msgpack_packer_write_bignum_value(msgpack_packer_t* pk, VALUE v)
471
474
  {
475
+ int leading_zero_bits;
476
+ size_t required_size = rb_absint_size(v, &leading_zero_bits);
477
+
472
478
  if(RBIGNUM_POSITIVE_P(v)) {
479
+ if(required_size > 8 && pk->has_bigint_ext_type) {
480
+ if(msgpack_packer_try_write_with_ext_type_lookup(pk, v)) {
481
+ return;
482
+ }
483
+ // if we didn't return here `msgpack_packer_write_u64` will raise a RangeError
484
+ }
485
+
473
486
  msgpack_packer_write_u64(pk, rb_big2ull(v));
474
487
  } else {
488
+ if(leading_zero_bits == 0) {
489
+ required_size += 1;
490
+ }
491
+
492
+ if(required_size > 8 && pk->has_bigint_ext_type) {
493
+ if(msgpack_packer_try_write_with_ext_type_lookup(pk, v)) {
494
+ return;
495
+ }
496
+ // if we didn't return here `msgpack_packer_write_u64` will raise a RangeError
497
+ }
498
+
475
499
  msgpack_packer_write_long_long(pk, rb_big2ll(v));
476
500
  }
477
501
  }