msgpack 1.4.5 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }
@@ -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); \
@@ -89,17 +94,17 @@ void MessagePack_Buffer_set_options(msgpack_buffer_t* b, VALUE io, VALUE options
89
94
  if(options != Qnil) {
90
95
  VALUE v;
91
96
 
92
- v = rb_hash_aref(options, ID2SYM(rb_intern("read_reference_threshold")));
97
+ v = rb_hash_aref(options, sym_read_reference_threshold);
93
98
  if(v != Qnil) {
94
99
  msgpack_buffer_set_read_reference_threshold(b, NUM2ULONG(v));
95
100
  }
96
101
 
97
- v = rb_hash_aref(options, ID2SYM(rb_intern("write_reference_threshold")));
102
+ v = rb_hash_aref(options, sym_write_reference_threshold);
98
103
  if(v != Qnil) {
99
104
  msgpack_buffer_set_write_reference_threshold(b, NUM2ULONG(v));
100
105
  }
101
106
 
102
- v = rb_hash_aref(options, ID2SYM(rb_intern("io_buffer_size")));
107
+ v = rb_hash_aref(options, sym_io_buffer_size);
103
108
  if(v != Qnil) {
104
109
  msgpack_buffer_set_io_buffer_size(b, NUM2ULONG(v));
105
110
  }
@@ -479,6 +484,10 @@ void MessagePack_Buffer_module_init(VALUE mMessagePack)
479
484
  s_append = rb_intern("<<");
480
485
  s_close = rb_intern("close");
481
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
+
482
491
  msgpack_buffer_static_init();
483
492
 
484
493
  cMessagePack_Buffer = rb_define_class_under(mMessagePack, "Buffer", rb_cObject);
@@ -31,6 +31,7 @@ typedef struct msgpack_factory_t msgpack_factory_t;
31
31
  struct msgpack_factory_t {
32
32
  msgpack_packer_ext_registry_t pkrg;
33
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;
@@ -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;
@@ -145,6 +182,7 @@ static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
145
182
  FACTORY(self, fc);
146
183
 
147
184
  int ext_type;
185
+ int flags = 0;
148
186
  VALUE ext_module;
149
187
  VALUE options = Qnil;
150
188
  VALUE packer_arg, unpacker_arg;
@@ -173,6 +211,10 @@ static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
173
211
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 2..3)", argc);
174
212
  }
175
213
 
214
+ if (options != Qnil) {
215
+ Check_Type(options, T_HASH);
216
+ }
217
+
176
218
  ext_type = NUM2INT(argv[0]);
177
219
  if(ext_type < -128 || ext_type > 127) {
178
220
  rb_raise(rb_eRangeError, "integer %d too big to convert to `signed char'", ext_type);
@@ -200,16 +242,29 @@ static VALUE Factory_register_type(int argc, VALUE* argv, VALUE self)
200
242
  }
201
243
  }
202
244
 
203
- msgpack_packer_ext_registry_put(&fc->pkrg, ext_module, ext_type, packer_proc, packer_arg);
204
-
205
- if (ext_module == rb_cSymbol) {
245
+ if(ext_module == rb_cSymbol) {
206
246
  fc->has_symbol_ext_type = true;
207
- 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"))))) {
208
248
  fc->optimized_symbol_ext_type = true;
209
249
  }
210
250
  }
211
251
 
212
- 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);
213
268
 
214
269
  return Qnil;
215
270
  }
@@ -221,6 +276,8 @@ void MessagePack_Factory_module_init(VALUE mMessagePack)
221
276
  rb_define_alloc_func(cMessagePack_Factory, Factory_alloc);
222
277
 
223
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);
224
281
 
225
282
  rb_define_method(cMessagePack_Factory, "packer", MessagePack_Factory_packer, -1);
226
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
  }
@@ -28,6 +28,8 @@ VALUE cMessagePack_Packer;
28
28
  static ID s_to_msgpack;
29
29
  static ID s_write;
30
30
 
31
+ static VALUE sym_compatibility_mode;
32
+
31
33
  //static VALUE s_packer_value;
32
34
  //static msgpack_packer_t* s_packer;
33
35
 
@@ -68,29 +70,28 @@ VALUE MessagePack_Packer_alloc(VALUE klass)
68
70
 
69
71
  VALUE MessagePack_Packer_initialize(int argc, VALUE* argv, VALUE self)
70
72
  {
73
+ if(argc > 2) {
74
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..2)", argc);
75
+ }
76
+
71
77
  VALUE io = Qnil;
72
78
  VALUE options = Qnil;
73
79
 
74
- if(argc == 0 || (argc == 1 && argv[0] == Qnil)) {
75
- /* Qnil */
76
-
77
- } else if(argc == 1) {
78
- VALUE v = argv[0];
79
- if(rb_type(v) == T_HASH) {
80
- options = v;
81
- } else {
82
- io = v;
83
- }
84
-
85
- } else if(argc == 2) {
80
+ if(argc >= 1) {
86
81
  io = argv[0];
82
+ }
83
+
84
+ if(argc == 2) {
87
85
  options = argv[1];
88
- if(rb_type(options) != T_HASH) {
89
- rb_raise(rb_eArgError, "expected Hash but found %s.", rb_obj_classname(options));
90
- }
86
+ }
91
87
 
92
- } else {
93
- rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..2)", argc);
88
+ if (options == Qnil && rb_type(io) == T_HASH) {
89
+ options = io;
90
+ io = Qnil;
91
+ }
92
+
93
+ if(options != Qnil) {
94
+ Check_Type(options, T_HASH);
94
95
  }
95
96
 
96
97
  PACKER(self, pk);
@@ -103,7 +104,7 @@ VALUE MessagePack_Packer_initialize(int argc, VALUE* argv, VALUE self)
103
104
  if(options != Qnil) {
104
105
  VALUE v;
105
106
 
106
- v = rb_hash_aref(options, ID2SYM(rb_intern("compatibility_mode")));
107
+ v = rb_hash_aref(options, sym_compatibility_mode);
107
108
  msgpack_packer_set_compat(pk, RTEST(v));
108
109
  }
109
110
 
@@ -380,7 +381,7 @@ static VALUE Packer_register_type(int argc, VALUE* argv, VALUE self)
380
381
  rb_raise(rb_eArgError, "expected Module/Class but found %s.", rb_obj_classname(ext_module));
381
382
  }
382
383
 
383
- msgpack_packer_ext_registry_put(&pk->ext_registry, ext_module, ext_type, proc, arg);
384
+ msgpack_packer_ext_registry_put(&pk->ext_registry, ext_module, ext_type, 0, proc, arg);
384
385
 
385
386
  if (ext_module == rb_cSymbol) {
386
387
  pk->has_symbol_ext_type = true;
@@ -412,6 +413,8 @@ void MessagePack_Packer_module_init(VALUE mMessagePack)
412
413
  s_to_msgpack = rb_intern("to_msgpack");
413
414
  s_write = rb_intern("write");
414
415
 
416
+ sym_compatibility_mode = ID2SYM(rb_intern("compatibility_mode"));
417
+
415
418
  msgpack_packer_static_init();
416
419
  msgpack_packer_ext_registry_static_init();
417
420
 
@@ -43,12 +43,18 @@ void msgpack_packer_ext_registry_mark(msgpack_packer_ext_registry_t* pkrg)
43
43
  void msgpack_packer_ext_registry_dup(msgpack_packer_ext_registry_t* src,
44
44
  msgpack_packer_ext_registry_t* dst)
45
45
  {
46
- dst->hash = RTEST(src->hash) ? rb_hash_dup(src->hash) : Qnil;
47
- dst->cache = RTEST(src->cache) ? rb_hash_dup(src->cache): Qnil;
46
+ if(RTEST(src->hash) && !rb_obj_frozen_p(src->hash)) {
47
+ dst->hash = rb_hash_dup(src->hash);
48
+ dst->cache = RTEST(src->cache) ? rb_hash_dup(src->cache) : Qnil;
49
+ } else {
50
+ // If the type registry is frozen we can safely share it, and share the cache as well.
51
+ dst->hash = src->hash;
52
+ dst->cache = src->cache;
53
+ }
48
54
  }
49
55
 
50
56
  VALUE msgpack_packer_ext_registry_put(msgpack_packer_ext_registry_t* pkrg,
51
- VALUE ext_module, int ext_type, VALUE proc, VALUE arg)
57
+ VALUE ext_module, int ext_type, int flags, VALUE proc, VALUE arg)
52
58
  {
53
59
  if (!RTEST(pkrg->hash)) {
54
60
  pkrg->hash = rb_hash_new();
@@ -58,5 +64,8 @@ VALUE msgpack_packer_ext_registry_put(msgpack_packer_ext_registry_t* pkrg,
58
64
  /* clear lookup cache not to miss added type */
59
65
  rb_hash_clear(pkrg->cache);
60
66
  }
61
- return rb_hash_aset(pkrg->hash, ext_module, rb_ary_new3(3, INT2FIX(ext_type), proc, arg));
67
+
68
+ // TODO: Ruby embeded array limit is 3, merging `proc` and `arg` would be good.
69
+ VALUE entry = rb_ary_new3(4, INT2FIX(ext_type), proc, arg, INT2FIX(flags));
70
+ return rb_hash_aset(pkrg->hash, ext_module, entry);
62
71
  }
@@ -21,6 +21,8 @@
21
21
  #include "compat.h"
22
22
  #include "ruby.h"
23
23
 
24
+ #define MSGPACK_EXT_RECURSIVE 0b0001
25
+
24
26
  struct msgpack_packer_ext_registry_t;
25
27
  typedef struct msgpack_packer_ext_registry_t msgpack_packer_ext_registry_t;
26
28
 
@@ -44,7 +46,7 @@ void msgpack_packer_ext_registry_dup(msgpack_packer_ext_registry_t* src,
44
46
  msgpack_packer_ext_registry_t* dst);
45
47
 
46
48
  VALUE msgpack_packer_ext_registry_put(msgpack_packer_ext_registry_t* pkrg,
47
- VALUE ext_module, int ext_type, VALUE proc, VALUE arg);
49
+ VALUE ext_module, int ext_type, int flags, VALUE proc, VALUE arg);
48
50
 
49
51
  static int msgpack_packer_ext_find_superclass(VALUE key, VALUE value, VALUE arg)
50
52
  {
@@ -60,12 +62,13 @@ static int msgpack_packer_ext_find_superclass(VALUE key, VALUE value, VALUE arg)
60
62
  }
61
63
 
62
64
  static inline VALUE msgpack_packer_ext_registry_fetch(msgpack_packer_ext_registry_t* pkrg,
63
- VALUE lookup_class, int* ext_type_result)
65
+ VALUE lookup_class, int* ext_type_result, int* ext_flags_result)
64
66
  {
65
67
  // fetch lookup_class from hash, which is a hash to register classes
66
68
  VALUE type = rb_hash_lookup(pkrg->hash, lookup_class);
67
69
  if(type != Qnil) {
68
70
  *ext_type_result = FIX2INT(rb_ary_entry(type, 0));
71
+ *ext_flags_result = FIX2INT(rb_ary_entry(type, 3));
69
72
  return rb_ary_entry(type, 1);
70
73
  }
71
74
 
@@ -74,6 +77,7 @@ static inline VALUE msgpack_packer_ext_registry_fetch(msgpack_packer_ext_registr
74
77
  VALUE type_inht = rb_hash_lookup(pkrg->cache, lookup_class);
75
78
  if(type_inht != Qnil) {
76
79
  *ext_type_result = FIX2INT(rb_ary_entry(type_inht, 0));
80
+ *ext_flags_result = FIX2INT(rb_ary_entry(type_inht, 3));
77
81
  return rb_ary_entry(type_inht, 1);
78
82
  }
79
83
  }
@@ -82,7 +86,7 @@ static inline VALUE msgpack_packer_ext_registry_fetch(msgpack_packer_ext_registr
82
86
  }
83
87
 
84
88
  static inline VALUE msgpack_packer_ext_registry_lookup(msgpack_packer_ext_registry_t* pkrg,
85
- VALUE instance, int* ext_type_result)
89
+ VALUE instance, int* ext_type_result, int* ext_flags_result)
86
90
  {
87
91
  VALUE type;
88
92
 
@@ -97,7 +101,7 @@ static inline VALUE msgpack_packer_ext_registry_lookup(msgpack_packer_ext_regist
97
101
  * `rb_class_of` returns the singleton_class if the object has one, or the "real class" otherwise.
98
102
  */
99
103
  VALUE lookup_class = rb_class_of(instance);
100
- type = msgpack_packer_ext_registry_fetch(pkrg, lookup_class, ext_type_result);
104
+ type = msgpack_packer_ext_registry_fetch(pkrg, lookup_class, ext_type_result, ext_flags_result);
101
105
  if(type != Qnil) {
102
106
  return type;
103
107
  }
@@ -108,7 +112,7 @@ static inline VALUE msgpack_packer_ext_registry_lookup(msgpack_packer_ext_regist
108
112
  */
109
113
  VALUE real_class = rb_obj_class(instance);
110
114
  if(lookup_class != real_class) {
111
- type = msgpack_packer_ext_registry_fetch(pkrg, real_class, ext_type_result);
115
+ type = msgpack_packer_ext_registry_fetch(pkrg, real_class, ext_type_result, ext_flags_result);
112
116
  if(type != Qnil) {
113
117
  return type;
114
118
  }
@@ -130,6 +134,7 @@ static inline VALUE msgpack_packer_ext_registry_lookup(msgpack_packer_ext_regist
130
134
  }
131
135
  rb_hash_aset(pkrg->cache, lookup_class, superclass_type);
132
136
  *ext_type_result = FIX2INT(rb_ary_entry(superclass_type, 0));
137
+ *ext_flags_result = FIX2INT(rb_ary_entry(superclass_type, 3));
133
138
  return rb_ary_entry(superclass_type, 1);
134
139
  }
135
140
 
@@ -166,14 +166,17 @@ static inline int object_complete_ext(msgpack_unpacker_t* uk, int ext_type, VALU
166
166
  return object_complete_symbol(uk, rb_str_intern(str));
167
167
  }
168
168
 
169
- VALUE proc = msgpack_unpacker_ext_registry_lookup(uk->ext_registry, ext_type);
169
+ int ext_flags;
170
+ VALUE proc = msgpack_unpacker_ext_registry_lookup(uk->ext_registry, ext_type, &ext_flags);
171
+
170
172
  if(proc != Qnil) {
171
- VALUE obj = rb_funcall(proc, s_call, 1, str);
173
+ VALUE obj;
174
+ obj = rb_funcall(proc, s_call, 1, str == Qnil ? rb_str_buf_new(0) : str);
172
175
  return object_complete(uk, obj);
173
176
  }
174
177
 
175
178
  if(uk->allow_unknown_ext) {
176
- VALUE obj = MessagePack_ExtensionValue_new(ext_type, str);
179
+ VALUE obj = MessagePack_ExtensionValue_new(ext_type, str == Qnil ? rb_str_buf_new(0) : str);
177
180
  return object_complete(uk, obj);
178
181
  }
179
182
 
@@ -283,6 +286,23 @@ static inline int read_raw_body_begin(msgpack_unpacker_t* uk, int raw_type)
283
286
  {
284
287
  /* assuming uk->reading_raw == Qnil */
285
288
 
289
+ int ext_flags;
290
+ VALUE proc;
291
+
292
+ if(!(raw_type == RAW_TYPE_STRING || raw_type == RAW_TYPE_BINARY)) {
293
+ proc = msgpack_unpacker_ext_registry_lookup(uk->ext_registry, raw_type, &ext_flags);
294
+ if(proc != Qnil && ext_flags & MSGPACK_EXT_RECURSIVE) {
295
+ VALUE obj;
296
+ uk->last_object = Qnil;
297
+ reset_head_byte(uk);
298
+ size_t ext_size = uk->reading_raw_remaining;
299
+ uk->reading_raw_remaining = 0;
300
+ obj = rb_funcall(proc, s_call, 1, uk->buffer.owner);
301
+ msgpack_buffer_skip(UNPACKER_BUFFER_(uk), ext_size);
302
+ return object_complete(uk, obj);
303
+ }
304
+ }
305
+
286
306
  /* try optimized read */
287
307
  size_t length = uk->reading_raw_remaining;
288
308
  if(length <= msgpack_buffer_top_readable_size(UNPACKER_BUFFER_(uk))) {
@@ -371,7 +391,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
371
391
  uint8_t length = cb->u8;
372
392
  int ext_type = (signed char) cb->buffer[1];
373
393
  if(length == 0) {
374
- return object_complete_ext(uk, ext_type, rb_str_buf_new(0));
394
+ return object_complete_ext(uk, ext_type, Qnil);
375
395
  }
376
396
  uk->reading_raw_remaining = length;
377
397
  return read_raw_body_begin(uk, ext_type);
@@ -383,7 +403,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
383
403
  uint16_t length = _msgpack_be16(cb->u16);
384
404
  int ext_type = (signed char) cb->buffer[2];
385
405
  if(length == 0) {
386
- return object_complete_ext(uk, ext_type, rb_str_buf_new(0));
406
+ return object_complete_ext(uk, ext_type, Qnil);
387
407
  }
388
408
  uk->reading_raw_remaining = length;
389
409
  return read_raw_body_begin(uk, ext_type);
@@ -395,7 +415,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
395
415
  uint32_t length = _msgpack_be32(cb->u32);
396
416
  int ext_type = (signed char) cb->buffer[4];
397
417
  if(length == 0) {
398
- return object_complete_ext(uk, ext_type, rb_str_buf_new(0));
418
+ return object_complete_ext(uk, ext_type, Qnil);
399
419
  }
400
420
  uk->reading_raw_remaining = length;
401
421
  return read_raw_body_begin(uk, ext_type);