msgpack 1.4.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }