msgpack 1.5.4 → 1.8.4

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +80 -1
  3. data/README.md +49 -13
  4. data/ext/java/org/msgpack/jruby/Buffer.java +3 -3
  5. data/ext/java/org/msgpack/jruby/Decoder.java +15 -7
  6. data/ext/java/org/msgpack/jruby/ExtensionRegistry.java +11 -20
  7. data/ext/java/org/msgpack/jruby/ExtensionValue.java +1 -1
  8. data/ext/java/org/msgpack/jruby/Factory.java +11 -50
  9. data/ext/java/org/msgpack/jruby/Packer.java +9 -24
  10. data/ext/java/org/msgpack/jruby/Unpacker.java +15 -32
  11. data/ext/msgpack/buffer.c +101 -58
  12. data/ext/msgpack/buffer.h +138 -44
  13. data/ext/msgpack/buffer_class.c +132 -31
  14. data/ext/msgpack/buffer_class.h +1 -0
  15. data/ext/msgpack/extconf.rb +20 -30
  16. data/ext/msgpack/factory_class.c +75 -86
  17. data/ext/msgpack/packer.c +13 -16
  18. data/ext/msgpack/packer.h +18 -21
  19. data/ext/msgpack/packer_class.c +72 -98
  20. data/ext/msgpack/packer_class.h +11 -0
  21. data/ext/msgpack/packer_ext_registry.c +31 -28
  22. data/ext/msgpack/packer_ext_registry.h +10 -14
  23. data/ext/msgpack/rbinit.c +1 -1
  24. data/ext/msgpack/rmem.c +3 -4
  25. data/ext/msgpack/sysdep.h +5 -2
  26. data/ext/msgpack/unpacker.c +241 -218
  27. data/ext/msgpack/unpacker.h +29 -31
  28. data/ext/msgpack/unpacker_class.c +87 -92
  29. data/ext/msgpack/unpacker_class.h +11 -0
  30. data/ext/msgpack/unpacker_ext_registry.c +4 -16
  31. data/ext/msgpack/unpacker_ext_registry.h +3 -7
  32. data/lib/msgpack/buffer.rb +9 -0
  33. data/lib/msgpack/factory.rb +90 -63
  34. data/lib/msgpack/packer.rb +10 -1
  35. data/lib/msgpack/unpacker.rb +14 -1
  36. data/lib/msgpack/version.rb +1 -1
  37. data/lib/msgpack.rb +1 -0
  38. data/msgpack.gemspec +13 -12
  39. metadata +13 -152
  40. data/.github/workflows/ci.yaml +0 -57
  41. data/.gitignore +0 -23
  42. data/.rubocop.yml +0 -36
  43. data/Gemfile +0 -9
  44. data/Rakefile +0 -70
  45. data/appveyor.yml +0 -18
  46. data/bench/bench.rb +0 -78
  47. data/doclib/msgpack/buffer.rb +0 -193
  48. data/doclib/msgpack/core_ext.rb +0 -101
  49. data/doclib/msgpack/error.rb +0 -19
  50. data/doclib/msgpack/extension_value.rb +0 -9
  51. data/doclib/msgpack/factory.rb +0 -145
  52. data/doclib/msgpack/packer.rb +0 -209
  53. data/doclib/msgpack/time.rb +0 -22
  54. data/doclib/msgpack/timestamp.rb +0 -44
  55. data/doclib/msgpack/unpacker.rb +0 -183
  56. data/doclib/msgpack.rb +0 -87
  57. data/msgpack.org.md +0 -46
  58. data/spec/bigint_spec.rb +0 -26
  59. data/spec/cases.json +0 -1
  60. data/spec/cases.msg +0 -0
  61. data/spec/cases_compact.msg +0 -0
  62. data/spec/cases_spec.rb +0 -39
  63. data/spec/cruby/buffer_io_spec.rb +0 -255
  64. data/spec/cruby/buffer_packer.rb +0 -29
  65. data/spec/cruby/buffer_spec.rb +0 -575
  66. data/spec/cruby/buffer_unpacker.rb +0 -19
  67. data/spec/cruby/unpacker_spec.rb +0 -70
  68. data/spec/ext_value_spec.rb +0 -99
  69. data/spec/exttypes.rb +0 -51
  70. data/spec/factory_spec.rb +0 -664
  71. data/spec/format_spec.rb +0 -301
  72. data/spec/jruby/benchmarks/shootout_bm.rb +0 -73
  73. data/spec/jruby/benchmarks/symbolize_keys_bm.rb +0 -25
  74. data/spec/jruby/unpacker_spec.rb +0 -186
  75. data/spec/msgpack_spec.rb +0 -214
  76. data/spec/pack_spec.rb +0 -61
  77. data/spec/packer_spec.rb +0 -575
  78. data/spec/random_compat.rb +0 -24
  79. data/spec/spec_helper.rb +0 -71
  80. data/spec/timestamp_spec.rb +0 -159
  81. data/spec/unpack_spec.rb +0 -57
  82. data/spec/unpacker_spec.rb +0 -859
data/ext/msgpack/buffer.c CHANGED
@@ -19,21 +19,15 @@
19
19
  #include "buffer.h"
20
20
  #include "rmem.h"
21
21
 
22
- #ifndef HAVE_RB_STR_REPLACE
23
- static ID s_replace;
24
- #endif
25
-
26
22
  int msgpack_rb_encindex_utf8;
27
23
  int msgpack_rb_encindex_usascii;
28
24
  int msgpack_rb_encindex_ascii8bit;
29
25
 
30
26
  ID s_uminus;
31
27
 
32
- #ifndef DISABLE_RMEM
33
28
  static msgpack_rmem_t s_rmem;
34
- #endif
35
29
 
36
- void msgpack_buffer_static_init()
30
+ void msgpack_buffer_static_init(void)
37
31
  {
38
32
  s_uminus = rb_intern("-@");
39
33
 
@@ -41,20 +35,12 @@ void msgpack_buffer_static_init()
41
35
  msgpack_rb_encindex_usascii = rb_usascii_encindex();
42
36
  msgpack_rb_encindex_ascii8bit = rb_ascii8bit_encindex();
43
37
 
44
- #ifndef DISABLE_RMEM
45
38
  msgpack_rmem_init(&s_rmem);
46
- #endif
47
-
48
- #ifndef HAVE_RB_STR_REPLACE
49
- s_replace = rb_intern("replace");
50
- #endif
51
39
  }
52
40
 
53
- void msgpack_buffer_static_destroy()
41
+ void msgpack_buffer_static_destroy(void)
54
42
  {
55
- #ifndef DISABLE_RMEM
56
43
  msgpack_rmem_destroy(&s_rmem);
57
- #endif
58
44
  }
59
45
 
60
46
  void msgpack_buffer_init(msgpack_buffer_t* b)
@@ -72,16 +58,16 @@ void msgpack_buffer_init(msgpack_buffer_t* b)
72
58
  static void _msgpack_buffer_chunk_destroy(msgpack_buffer_chunk_t* c)
73
59
  {
74
60
  if(c->mem != NULL) {
75
- #ifndef DISABLE_RMEM
76
- if(!msgpack_rmem_free(&s_rmem, c->mem)) {
61
+ if(c->rmem) {
62
+ if(!msgpack_rmem_free(&s_rmem, c->mem)) {
63
+ rb_bug("Failed to free an rmem pointer, memory leak?");
64
+ }
65
+ } else {
77
66
  xfree(c->mem);
78
67
  }
79
68
  /* no needs to update rmem_owner because chunks will not be
80
69
  * free()ed (left in free_list) and thus *rmem_owner is
81
70
  * always valid. */
82
- #else
83
- xfree(c->mem);
84
- #endif
85
71
  }
86
72
  c->first = NULL;
87
73
  c->last = NULL;
@@ -108,8 +94,25 @@ void msgpack_buffer_destroy(msgpack_buffer_t* b)
108
94
  }
109
95
  }
110
96
 
111
- void msgpack_buffer_mark(msgpack_buffer_t* b)
97
+ size_t msgpack_buffer_memsize(const msgpack_buffer_t* b)
98
+ {
99
+ size_t memsize = 0;
100
+ msgpack_buffer_chunk_t* c = b->head;
101
+
102
+ while(c) {
103
+ memsize += sizeof(msgpack_buffer_chunk_t);
104
+ if(c->mapped_string != NO_MAPPED_STRING) {
105
+ memsize += (c->last - c->first);
106
+ }
107
+ c = c->next;
108
+ }
109
+
110
+ return memsize;
111
+ }
112
+
113
+ void msgpack_buffer_mark(void *ptr)
112
114
  {
115
+ msgpack_buffer_t* b = ptr;
113
116
  /* head is always available */
114
117
  msgpack_buffer_chunk_t* c = b->head;
115
118
  while(c != &b->tail) {
@@ -120,12 +123,19 @@ void msgpack_buffer_mark(msgpack_buffer_t* b)
120
123
 
121
124
  rb_gc_mark(b->io);
122
125
  rb_gc_mark(b->io_buffer);
123
-
124
- rb_gc_mark(b->owner);
125
126
  }
126
127
 
127
128
  bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b)
128
129
  {
130
+ if(b->rmem_owner == &b->head->mem) {
131
+ /* the chunk that owns the rmem page is going away and takes the page
132
+ * with it. don't carve the remaining space out of a page that is
133
+ * already back in the pool. */
134
+ b->rmem_end = NULL;
135
+ b->rmem_last = NULL;
136
+ b->rmem_owner = NULL;
137
+ }
138
+
129
139
  _msgpack_buffer_chunk_destroy(b->head);
130
140
 
131
141
  if(b->head == &b->tail) {
@@ -133,6 +143,9 @@ bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b)
133
143
  * because head should be always available */
134
144
  b->tail_buffer_end = NULL;
135
145
  b->read_buffer = NULL;
146
+ b->rmem_end = NULL;
147
+ b->rmem_last = NULL;
148
+ b->rmem_owner = NULL;
136
149
  return false;
137
150
  }
138
151
 
@@ -158,7 +171,6 @@ size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string,
158
171
  {
159
172
  size_t avail = msgpack_buffer_top_readable_size(b);
160
173
 
161
- #ifndef DISABLE_BUFFER_READ_REFERENCE_OPTIMIZE
162
174
  /* optimize */
163
175
  if(length <= avail && RSTRING_LEN(string) == 0 &&
164
176
  b->head->mapped_string != NO_MAPPED_STRING &&
@@ -170,7 +182,6 @@ size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string,
170
182
  _msgpack_buffer_consumed(b, length);
171
183
  return length;
172
184
  }
173
- #endif
174
185
 
175
186
  size_t const length_orig = length;
176
187
 
@@ -252,12 +263,26 @@ bool _msgpack_buffer_read_all2(msgpack_buffer_t* b, char* buffer, size_t length)
252
263
 
253
264
  static inline msgpack_buffer_chunk_t* _msgpack_buffer_alloc_new_chunk(msgpack_buffer_t* b)
254
265
  {
255
- msgpack_buffer_chunk_t* reuse = b->free_list;
256
- if(reuse == NULL) {
257
- return xmalloc(sizeof(msgpack_buffer_chunk_t));
266
+ msgpack_buffer_chunk_t* chunk = b->free_list;
267
+ if (chunk) {
268
+ b->free_list = b->free_list->next;
269
+ } else {
270
+ chunk = xmalloc(sizeof(msgpack_buffer_chunk_t));
271
+ }
272
+ memset(chunk, 0, sizeof(msgpack_buffer_chunk_t));
273
+ return chunk;
274
+ }
275
+
276
+ /* b->tail is copied into nc and then rebuilt, so the rmem page pointer moves
277
+ * to nc. the owner has to follow it: if it kept pointing at b->tail.mem, the
278
+ * transfer in _msgpack_buffer_chunk_malloc would degenerate into a
279
+ * self-assignment and the page would be released by nc while the rebuilt tail
280
+ * is still reading from it. */
281
+ static inline void _msgpack_buffer_transfer_rmem_owner(msgpack_buffer_t* b, msgpack_buffer_chunk_t* nc)
282
+ {
283
+ if(b->rmem_owner == &b->tail.mem) {
284
+ b->rmem_owner = &nc->mem;
258
285
  }
259
- b->free_list = b->free_list->next;
260
- return reuse;
261
286
  }
262
287
 
263
288
  static inline void _msgpack_buffer_add_new_chunk(msgpack_buffer_t* b)
@@ -271,6 +296,7 @@ static inline void _msgpack_buffer_add_new_chunk(msgpack_buffer_t* b)
271
296
  msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b);
272
297
 
273
298
  *nc = b->tail;
299
+ _msgpack_buffer_transfer_rmem_owner(b, nc);
274
300
  b->head = nc;
275
301
  nc->next = &b->tail;
276
302
 
@@ -283,36 +309,61 @@ static inline void _msgpack_buffer_add_new_chunk(msgpack_buffer_t* b)
283
309
 
284
310
  msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b);
285
311
 
286
- #ifndef DISABLE_RMEM
287
- #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
288
312
  if(b->rmem_last == b->tail_buffer_end) {
289
313
  /* reuse unused rmem space */
290
314
  size_t unused = b->tail_buffer_end - b->tail.last;
291
315
  b->rmem_last -= unused;
292
316
  }
293
- #endif
294
- #endif
295
317
 
296
318
  /* rebuild tail */
297
319
  *nc = b->tail;
320
+ _msgpack_buffer_transfer_rmem_owner(b, nc);
298
321
  before_tail->next = nc;
299
322
  nc->next = &b->tail;
300
323
  }
301
324
  }
302
325
 
303
- void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string)
326
+ static inline void _msgpack_buffer_append_reference(msgpack_buffer_t* b, VALUE string)
304
327
  {
305
- size_t length = RSTRING_LEN(string);
328
+ VALUE mapped_string;
329
+ if(ENCODING_GET_INLINED(string) == msgpack_rb_encindex_ascii8bit && RB_OBJ_FROZEN_RAW(string)) {
330
+ mapped_string = string;
331
+ } else {
332
+ mapped_string = rb_str_dup(string);
333
+ ENCODING_SET(mapped_string, msgpack_rb_encindex_ascii8bit);
334
+ }
335
+
336
+ _msgpack_buffer_add_new_chunk(b);
337
+
338
+ char* data;
339
+ size_t length;
340
+ RSTRING_GETMEM(mapped_string, data, length);
341
+
342
+ b->tail.first = (char*) data;
343
+ b->tail.last = (char*) data + length;
344
+ b->tail.mapped_string = mapped_string;
345
+ b->tail.mem = NULL;
346
+
347
+ /* msgpack_buffer_writable_size should return 0 for mapped chunk */
348
+ b->tail_buffer_end = b->tail.last;
349
+
350
+ /* consider read_buffer */
351
+ if(b->head == &b->tail) {
352
+ b->read_buffer = b->tail.first;
353
+ }
354
+ }
306
355
 
356
+ void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string)
357
+ {
307
358
  if(b->io != Qnil) {
308
359
  msgpack_buffer_flush(b);
309
- if (ENCODING_GET(string) == msgpack_rb_encindex_ascii8bit) {
360
+ if (ENCODING_GET_INLINED(string) == msgpack_rb_encindex_ascii8bit) {
310
361
  rb_funcall(b->io, b->io_write_all_method, 1, string);
311
362
  } else {
312
- msgpack_buffer_append(b, RSTRING_PTR(string), length);
363
+ msgpack_buffer_append(b, RSTRING_PTR(string), RSTRING_LEN(string));
313
364
  }
314
365
  } else {
315
- msgpack_buffer_append(b, RSTRING_PTR(string), length);
366
+ _msgpack_buffer_append_reference(b, string);
316
367
  }
317
368
  }
318
369
 
@@ -320,11 +371,10 @@ static inline void* _msgpack_buffer_chunk_malloc(
320
371
  msgpack_buffer_t* b, msgpack_buffer_chunk_t* c,
321
372
  size_t required_size, size_t* allocated_size)
322
373
  {
323
- #ifndef DISABLE_RMEM
324
374
  if(required_size <= MSGPACK_RMEM_PAGE_SIZE) {
325
- #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
375
+ c->rmem = true;
376
+
326
377
  if((size_t)(b->rmem_end - b->rmem_last) < required_size) {
327
- #endif
328
378
  /* alloc new rmem page */
329
379
  *allocated_size = MSGPACK_RMEM_PAGE_SIZE;
330
380
  char* buffer = msgpack_rmem_alloc(&s_rmem);
@@ -335,8 +385,6 @@ static inline void* _msgpack_buffer_chunk_malloc(
335
385
  b->rmem_last = b->rmem_end = buffer + MSGPACK_RMEM_PAGE_SIZE;
336
386
 
337
387
  return buffer;
338
-
339
- #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT
340
388
  } else {
341
389
  /* reuse unused rmem */
342
390
  *allocated_size = (size_t)(b->rmem_end - b->rmem_last);
@@ -350,18 +398,13 @@ static inline void* _msgpack_buffer_chunk_malloc(
350
398
 
351
399
  return buffer;
352
400
  }
353
- #endif
354
- }
355
- #else
356
- if(required_size < 72) {
357
- required_size = 72;
358
401
  }
359
- #endif
360
402
 
361
403
  // TODO alignment?
362
404
  *allocated_size = required_size;
363
405
  void* mem = xmalloc(required_size);
364
406
  c->mem = mem;
407
+ c->rmem = false;
365
408
  return mem;
366
409
  }
367
410
 
@@ -411,11 +454,7 @@ void _msgpack_buffer_expand(msgpack_buffer_t* b, const char* data, size_t length
411
454
  size_t capacity = b->tail.last - b->tail.first;
412
455
 
413
456
  /* can't realloc mapped chunk or rmem page */
414
- if(b->tail.mapped_string != NO_MAPPED_STRING
415
- #ifndef DISABLE_RMEM
416
- || capacity <= MSGPACK_RMEM_PAGE_SIZE
417
- #endif
418
- ) {
457
+ if(b->tail.mapped_string != NO_MAPPED_STRING || capacity <= MSGPACK_RMEM_PAGE_SIZE) {
419
458
  /* allocate new chunk */
420
459
  _msgpack_buffer_add_new_chunk(b);
421
460
 
@@ -613,9 +652,11 @@ size_t _msgpack_buffer_feed_from_io(msgpack_buffer_t* b)
613
652
 
614
653
  size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string, size_t length)
615
654
  {
655
+ #define MIN(x, y) (((x) < (y)) ? (x) : (y))
656
+
616
657
  if(RSTRING_LEN(string) == 0) {
617
658
  /* direct read */
618
- VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, SIZET2NUM(length), string);
659
+ VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, SIZET2NUM(MIN(b->io_buffer_size, length)), string);
619
660
  if(ret == Qnil) {
620
661
  return 0;
621
662
  }
@@ -627,7 +668,7 @@ size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string,
627
668
  b->io_buffer = rb_str_buf_new(0);
628
669
  }
629
670
 
630
- VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, SIZET2NUM(length), b->io_buffer);
671
+ VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, SIZET2NUM(MIN(b->io_buffer_size, length)), b->io_buffer);
631
672
  if(ret == Qnil) {
632
673
  return 0;
633
674
  }
@@ -635,6 +676,8 @@ size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string,
635
676
 
636
677
  rb_str_buf_cat(string, (const void*)RSTRING_PTR(b->io_buffer), rl);
637
678
  return rl;
679
+
680
+ #undef MIN
638
681
  }
639
682
 
640
683
  size_t _msgpack_buffer_skip_from_io(msgpack_buffer_t* b, size_t length)
data/ext/msgpack/buffer.h CHANGED
@@ -78,20 +78,7 @@ struct msgpack_buffer_chunk_t {
78
78
  void* mem;
79
79
  msgpack_buffer_chunk_t* next;
80
80
  VALUE mapped_string; /* RBString or NO_MAPPED_STRING */
81
- };
82
-
83
- union msgpack_buffer_cast_block_t {
84
- char buffer[8];
85
- uint8_t u8;
86
- uint16_t u16;
87
- uint32_t u32;
88
- uint64_t u64;
89
- int8_t i8;
90
- int16_t i16;
91
- int32_t i32;
92
- int64_t i64;
93
- float f;
94
- double d;
81
+ bool rmem;
95
82
  };
96
83
 
97
84
  struct msgpack_buffer_t {
@@ -102,13 +89,9 @@ struct msgpack_buffer_t {
102
89
  msgpack_buffer_chunk_t* head;
103
90
  msgpack_buffer_chunk_t* free_list;
104
91
 
105
- #ifndef DISABLE_RMEM
106
92
  char* rmem_last;
107
93
  char* rmem_end;
108
94
  void** rmem_owner;
109
- #endif
110
-
111
- union msgpack_buffer_cast_block_t cast_block;
112
95
 
113
96
  VALUE io;
114
97
  VALUE io_buffer;
@@ -118,25 +101,25 @@ struct msgpack_buffer_t {
118
101
  size_t write_reference_threshold;
119
102
  size_t read_reference_threshold;
120
103
  size_t io_buffer_size;
121
-
122
- VALUE owner;
123
104
  };
124
105
 
125
106
  /*
126
107
  * initialization functions
127
108
  */
128
- void msgpack_buffer_static_init();
109
+ void msgpack_buffer_static_init(void);
129
110
 
130
- void msgpack_buffer_static_destroy();
111
+ void msgpack_buffer_static_destroy(void);
131
112
 
132
113
  void msgpack_buffer_init(msgpack_buffer_t* b);
133
114
 
134
115
  void msgpack_buffer_destroy(msgpack_buffer_t* b);
135
116
 
136
- void msgpack_buffer_mark(msgpack_buffer_t* b);
117
+ void msgpack_buffer_mark(void* b);
137
118
 
138
119
  void msgpack_buffer_clear(msgpack_buffer_t* b);
139
120
 
121
+ size_t msgpack_buffer_memsize(const msgpack_buffer_t* b);
122
+
140
123
  static inline void msgpack_buffer_set_write_reference_threshold(msgpack_buffer_t* b, size_t length)
141
124
  {
142
125
  if(length < MSGPACK_BUFFER_STRING_WRITE_REFERENCE_MINIMUM) {
@@ -254,13 +237,14 @@ void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string);
254
237
 
255
238
  static inline size_t msgpack_buffer_append_string(msgpack_buffer_t* b, VALUE string)
256
239
  {
257
- size_t length = RSTRING_LEN(string);
240
+ size_t length;
241
+ char *ptr;
242
+ RSTRING_GETMEM(string, ptr, length);
258
243
 
259
244
  if(length > b->write_reference_threshold) {
260
245
  _msgpack_buffer_append_long_string(b, string);
261
-
262
246
  } else {
263
- msgpack_buffer_append(b, RSTRING_PTR(string), length);
247
+ msgpack_buffer_append(b, ptr, length);
264
248
  }
265
249
 
266
250
  return length;
@@ -269,14 +253,9 @@ static inline size_t msgpack_buffer_append_string(msgpack_buffer_t* b, VALUE str
269
253
  static inline size_t msgpack_buffer_append_string_reference(msgpack_buffer_t* b, VALUE string)
270
254
  {
271
255
  size_t length = RSTRING_LEN(string);
272
-
273
- if(length > MSGPACK_BUFFER_STRING_WRITE_REFERENCE_MINIMUM) {
256
+ if (length > 0) {
274
257
  _msgpack_buffer_append_long_string(b, string);
275
-
276
- } else {
277
- msgpack_buffer_append(b, RSTRING_PTR(string), length);
278
258
  }
279
-
280
259
  return length;
281
260
  }
282
261
 
@@ -389,14 +368,6 @@ static inline size_t msgpack_buffer_skip_nonblock(msgpack_buffer_t* b, size_t le
389
368
  return length;
390
369
  }
391
370
 
392
- static inline union msgpack_buffer_cast_block_t* msgpack_buffer_read_cast_block(msgpack_buffer_t* b, size_t n)
393
- {
394
- if(!msgpack_buffer_read_all(b, b->cast_block.buffer, n)) {
395
- return NULL;
396
- }
397
- return &b->cast_block;
398
- }
399
-
400
371
  size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string, size_t length);
401
372
 
402
373
  static inline size_t msgpack_buffer_read_to_string(msgpack_buffer_t* b, VALUE string, size_t length)
@@ -444,7 +415,6 @@ static inline VALUE _msgpack_buffer_refer_head_mapped_string(msgpack_buffer_t* b
444
415
 
445
416
  static inline VALUE msgpack_buffer_read_top_as_string(msgpack_buffer_t* b, size_t length, bool will_be_frozen, bool utf8)
446
417
  {
447
- #ifndef DISABLE_BUFFER_READ_REFERENCE_OPTIMIZE
448
418
  /* optimize */
449
419
  if(!will_be_frozen &&
450
420
  b->head->mapped_string != NO_MAPPED_STRING &&
@@ -454,7 +424,6 @@ static inline VALUE msgpack_buffer_read_top_as_string(msgpack_buffer_t* b, size_
454
424
  _msgpack_buffer_consumed(b, length);
455
425
  return result;
456
426
  }
457
- #endif
458
427
 
459
428
  VALUE result;
460
429
 
@@ -483,7 +452,6 @@ static inline VALUE msgpack_buffer_read_top_as_string(msgpack_buffer_t* b, size_
483
452
  result = rb_str_new(b->read_buffer, length);
484
453
  }
485
454
 
486
- #if STR_UMINUS_DEDUPE
487
455
  if (will_be_frozen) {
488
456
  #if STR_UMINUS_DEDUPE_FROZEN
489
457
  // Starting from MRI 2.8 it is preferable to freeze the string
@@ -495,7 +463,6 @@ static inline VALUE msgpack_buffer_read_top_as_string(msgpack_buffer_t* b, size_
495
463
  // frozen.
496
464
  result = rb_funcall(result, s_uminus, 0);
497
465
  }
498
- #endif // STR_UMINUS_DEDUPE
499
466
  _msgpack_buffer_consumed(b, length);
500
467
  return result;
501
468
 
@@ -507,4 +474,131 @@ static inline VALUE msgpack_buffer_read_top_as_symbol(msgpack_buffer_t* b, size_
507
474
  return rb_str_intern(msgpack_buffer_read_top_as_string(b, length, true, utf8));
508
475
  }
509
476
 
477
+ // Hash keys are likely to be repeated, and are frozen.
478
+ // As such we can re-use them if we keep a cache of the ones we've seen so far,
479
+ // and save much more expensive lookups into the global fstring table.
480
+ // This cache implementation is deliberately simple, as we're optimizing for compactness,
481
+ // to be able to fit easily embeded inside msgpack_unpacker_t.
482
+ // As such, binary search into a sorted array gives a good tradeoff between compactness and
483
+ // performance.
484
+ #define MSGPACK_KEY_CACHE_CAPACITY 63
485
+
486
+ typedef struct msgpack_key_cache_t msgpack_key_cache_t;
487
+ struct msgpack_key_cache_t {
488
+ int length;
489
+ VALUE entries[MSGPACK_KEY_CACHE_CAPACITY];
490
+ };
491
+
492
+ static inline VALUE build_interned_string(const char *str, const long length)
493
+ {
494
+ # ifdef HAVE_RB_ENC_INTERNED_STR
495
+ return rb_enc_interned_str(str, length, rb_utf8_encoding());
496
+ # else
497
+ VALUE rstring = rb_utf8_str_new(str, length);
498
+ return rb_funcall(rb_str_freeze(rstring), s_uminus, 0);
499
+ # endif
500
+ }
501
+
502
+ static inline VALUE build_symbol(const char *str, const long length)
503
+ {
504
+ return rb_str_intern(build_interned_string(str, length));
505
+ }
506
+
507
+ static void rvalue_cache_insert_at(msgpack_key_cache_t *cache, int index, VALUE rstring)
508
+ {
509
+ MEMMOVE(&cache->entries[index + 1], &cache->entries[index], VALUE, cache->length - index);
510
+ cache->length++;
511
+ cache->entries[index] = rstring;
512
+ }
513
+
514
+ static inline int rstring_cache_cmp(const char *str, const long length, VALUE rstring)
515
+ {
516
+ long rstring_length = RSTRING_LEN(rstring);
517
+ if (length == rstring_length) {
518
+ return memcmp(str, RSTRING_PTR(rstring), length);
519
+ } else {
520
+ return (int)(length - rstring_length);
521
+ }
522
+ }
523
+
524
+ static VALUE rstring_cache_fetch(msgpack_key_cache_t *cache, const char *str, const long length)
525
+ {
526
+ int low = 0;
527
+ int high = cache->length - 1;
528
+ int mid = 0;
529
+ int last_cmp = 0;
530
+
531
+ while (low <= high) {
532
+ mid = (high + low) >> 1;
533
+ VALUE entry = cache->entries[mid];
534
+ last_cmp = rstring_cache_cmp(str, length, entry);
535
+
536
+ if (last_cmp == 0) {
537
+ return entry;
538
+ } else if (last_cmp > 0) {
539
+ low = mid + 1;
540
+ } else {
541
+ high = mid - 1;
542
+ }
543
+ }
544
+
545
+ VALUE rstring = build_interned_string(str, length);
546
+
547
+ if (cache->length < MSGPACK_KEY_CACHE_CAPACITY) {
548
+ if (last_cmp > 0) {
549
+ mid += 1;
550
+ }
551
+
552
+ rvalue_cache_insert_at(cache, mid, rstring);
553
+ }
554
+ return rstring;
555
+ }
556
+
557
+ static VALUE rsymbol_cache_fetch(msgpack_key_cache_t *cache, const char *str, const long length)
558
+ {
559
+ int low = 0;
560
+ int high = cache->length - 1;
561
+ int mid = 0;
562
+ int last_cmp = 0;
563
+
564
+ while (low <= high) {
565
+ mid = (high + low) >> 1;
566
+ VALUE entry = cache->entries[mid];
567
+ last_cmp = rstring_cache_cmp(str, length, rb_sym2str(entry));
568
+
569
+ if (last_cmp == 0) {
570
+ return entry;
571
+ } else if (last_cmp > 0) {
572
+ low = mid + 1;
573
+ } else {
574
+ high = mid - 1;
575
+ }
576
+ }
577
+
578
+ VALUE rsymbol = build_symbol(str, length);
579
+
580
+ if (cache->length < MSGPACK_KEY_CACHE_CAPACITY) {
581
+ if (last_cmp > 0) {
582
+ mid += 1;
583
+ }
584
+
585
+ rvalue_cache_insert_at(cache, mid, rsymbol);
586
+ }
587
+ return rsymbol;
588
+ }
589
+
590
+ static inline VALUE msgpack_buffer_read_top_as_interned_symbol(msgpack_buffer_t* b, msgpack_key_cache_t *cache, size_t length)
591
+ {
592
+ VALUE result = rsymbol_cache_fetch(cache, b->read_buffer, length);
593
+ _msgpack_buffer_consumed(b, length);
594
+ return result;
595
+ }
596
+
597
+ static inline VALUE msgpack_buffer_read_top_as_interned_string(msgpack_buffer_t* b, msgpack_key_cache_t *cache, size_t length)
598
+ {
599
+ VALUE result = rstring_cache_fetch(cache, b->read_buffer, length);
600
+ _msgpack_buffer_consumed(b, length);
601
+ return result;
602
+ }
603
+
510
604
  #endif