Skip to content

Commit 2ffc3c9

Browse files
committed
Use batch APIs to create arrays and hashes
Basically a port of ruby/json#678 Rather than to allocate the container and push elements one by one, we accumulate them on a stack and then use the faster batch APIs to directly create the final container. The original action stack remains, as we need to keep track of what we're currently parsing and how big it is. But for the recursive cases, we no longer need to create a child stack.
1 parent f028dca commit 2ffc3c9

File tree

4 files changed

+164
-73
lines changed

4 files changed

+164
-73
lines changed

ext/msgpack/extconf.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
have_func("rb_enc_interned_str", "ruby.h") # Ruby 3.0+
44
have_func("rb_hash_new_capa", "ruby.h") # Ruby 3.2+
55
have_func("rb_proc_call_with_block", "ruby.h") # CRuby (TruffleRuby doesn't have it)
6+
have_func("rb_hash_bulk_insert", "ruby.h") # Ruby 2.6+ and missing on TruffleRuby 24.0
67

78
append_cflags([
89
"-fvisibility=hidden",

ext/msgpack/unpacker.c

Lines changed: 137 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ static VALUE protected_proc_call(VALUE proc, int argc, VALUE *argv, int *raised)
4949

5050
static int RAW_TYPE_STRING = 256;
5151
static int RAW_TYPE_BINARY = 257;
52-
static int16_t INITIAL_BUFFER_CAPACITY_MAX = SHRT_MAX;
5352

5453
static msgpack_rmem_t s_stack_rmem;
5554

@@ -60,9 +59,106 @@ static inline VALUE rb_hash_new_capa(long capa)
6059
}
6160
#endif
6261

63-
static inline int16_t initial_buffer_size(long size)
62+
#ifndef HAVE_RB_HASH_BULK_INSERT
63+
void rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash)
6464
{
65-
return (size > INITIAL_BUFFER_CAPACITY_MAX) ? INITIAL_BUFFER_CAPACITY_MAX : size;
65+
long index = 0;
66+
while (index < count) {
67+
VALUE name = pairs[index++];
68+
VALUE value = pairs[index++];
69+
rb_hash_aset(hash, name, value);
70+
}
71+
RB_GC_GUARD(hash);
72+
}
73+
#endif
74+
75+
/* rvalue_stack functions */
76+
77+
static inline void _msgpack_unpacker_rvalue_stack_free(msgpack_rvalue_stack_t* value_stack) {
78+
switch (value_stack->type) {
79+
case STACK_TYPE_UNALLOCATED:
80+
break;
81+
case STACK_TYPE_RMEM:
82+
if (!msgpack_rmem_free(&s_stack_rmem, value_stack->data)) {
83+
rb_bug("Failed to free an rmem pointer, memory leak?");
84+
}
85+
break;
86+
case STACK_TYPE_HEAP:
87+
xfree(value_stack->data);
88+
break;
89+
}
90+
memset(value_stack, 0, sizeof(msgpack_rvalue_stack_t));
91+
}
92+
93+
static void _msgpack_unpacker_rvalue_stack_grow(msgpack_rvalue_stack_t* value_stack) {
94+
switch (value_stack->type) {
95+
case STACK_TYPE_UNALLOCATED: {
96+
value_stack->data = msgpack_rmem_alloc(&s_stack_rmem);
97+
value_stack->capacity = MSGPACK_RMEM_PAGE_SIZE / sizeof(VALUE);
98+
value_stack->type = STACK_TYPE_RMEM;
99+
break;
100+
}
101+
case STACK_TYPE_RMEM: {
102+
size_t new_capacity = value_stack->capacity * 2;
103+
VALUE *new_ptr = ALLOC_N(VALUE, new_capacity);
104+
MEMCPY(new_ptr, value_stack->data, VALUE, value_stack->depth);
105+
if (!msgpack_rmem_free(&s_stack_rmem, value_stack->data)) {
106+
rb_bug("Failed to free an rmem pointer, memory leak?");
107+
}
108+
value_stack->type = STACK_TYPE_HEAP;
109+
value_stack->data = new_ptr;
110+
value_stack->capacity = new_capacity;
111+
break;
112+
}
113+
case STACK_TYPE_HEAP: {
114+
size_t new_capacity = value_stack->capacity * 2;
115+
REALLOC_N(value_stack->data, VALUE, new_capacity);
116+
value_stack->capacity = new_capacity;
117+
break;
118+
}
119+
}
120+
}
121+
122+
static inline void _msgpack_unpacker_rvalue_stack_push(msgpack_unpacker_t* uk, VALUE value) {
123+
size_t free_slots = uk->value_stack.capacity - uk->value_stack.depth;
124+
125+
if (RB_UNLIKELY(free_slots == 0)) {
126+
_msgpack_unpacker_rvalue_stack_grow(&uk->value_stack);
127+
free_slots = uk->value_stack.capacity - uk->value_stack.depth;
128+
}
129+
130+
RB_OBJ_WRITE(uk->self, &uk->value_stack.data[uk->value_stack.depth++], value);
131+
}
132+
133+
static inline VALUE _msgpack_unpacker_rvalue_stack_create_array(msgpack_unpacker_t* uk, size_t length) {
134+
VALUE *data = &uk->value_stack.data[uk->value_stack.depth - length];
135+
136+
VALUE array = rb_ary_new_from_values(length, data);
137+
138+
RB_OBJ_WRITE(uk->self, data, array);
139+
uk->value_stack.depth -= (length - 1);
140+
141+
return RB_GC_GUARD(array);
142+
}
143+
144+
static inline VALUE _msgpack_unpacker_rvalue_stack_create_hash(msgpack_unpacker_t* uk, size_t items_count) {
145+
size_t length = items_count / 2;
146+
VALUE *data = &uk->value_stack.data[uk->value_stack.depth - items_count];
147+
148+
VALUE hash = rb_hash_new_capa(length);
149+
rb_hash_bulk_insert(items_count, data, hash);
150+
151+
RB_OBJ_WRITE(uk->self, data, hash);
152+
uk->value_stack.depth -= (items_count - 1);
153+
154+
return RB_GC_GUARD(hash);
155+
}
156+
157+
void msgpack_unpacker_mark_rvalue_stack(msgpack_rvalue_stack_t* value_stack)
158+
{
159+
if (value_stack->data) {
160+
rb_gc_mark_locations(value_stack->data, value_stack->data + value_stack->depth);
161+
}
66162
}
67163

68164
void msgpack_unpacker_static_init(void)
@@ -108,33 +204,16 @@ static inline void _msgpack_unpacker_free_stack(msgpack_unpacker_stack_t* stack)
108204

109205
void _msgpack_unpacker_destroy(msgpack_unpacker_t* uk)
110206
{
111-
msgpack_unpacker_stack_t *stack;
112-
while ((stack = uk->stack)) {
113-
uk->stack = stack->parent;
114-
_msgpack_unpacker_free_stack(stack);
115-
}
116-
207+
_msgpack_unpacker_free_stack(uk->stack);
208+
_msgpack_unpacker_rvalue_stack_free(&uk->value_stack);
117209
msgpack_buffer_destroy(UNPACKER_BUFFER_(uk));
118210
}
119211

120-
void msgpack_unpacker_mark_stack(msgpack_unpacker_stack_t* stack)
121-
{
122-
while (stack) {
123-
msgpack_unpacker_stack_entry_t* s = stack->data;
124-
msgpack_unpacker_stack_entry_t* send = stack->data + stack->depth;
125-
for(; s < send; s++) {
126-
rb_gc_mark(s->object);
127-
rb_gc_mark(s->key);
128-
}
129-
stack = stack->parent;
130-
}
131-
}
132-
133212
void msgpack_unpacker_mark(msgpack_unpacker_t* uk)
134213
{
135214
rb_gc_mark(uk->last_object);
136215
rb_gc_mark(uk->reading_raw);
137-
msgpack_unpacker_mark_stack(uk->stack);
216+
msgpack_unpacker_mark_rvalue_stack(&uk->value_stack);
138217
/* See MessagePack_Buffer_wrap */
139218
/* msgpack_buffer_mark(UNPACKER_BUFFER_(uk)); */
140219
rb_gc_mark(uk->buffer_ref);
@@ -149,6 +228,7 @@ void _msgpack_unpacker_reset(msgpack_unpacker_t* uk)
149228

150229
/*memset(uk->stack, 0, sizeof(msgpack_unpacker_t) * uk->stack->depth);*/
151230
uk->stack->depth = 0;
231+
uk->value_stack.depth = 0;
152232
uk->last_object = Qnil;
153233
uk->reading_raw = Qnil;
154234
uk->reading_raw_remaining = 0;
@@ -186,13 +266,15 @@ static inline int object_complete(msgpack_unpacker_t* uk, VALUE object)
186266
}
187267

188268
uk->last_object = object;
269+
_msgpack_unpacker_rvalue_stack_push(uk, object);
189270
reset_head_byte(uk);
190271
return PRIMITIVE_OBJECT_COMPLETE;
191272
}
192273

193274
static inline int object_complete_symbol(msgpack_unpacker_t* uk, VALUE object)
194275
{
195276
uk->last_object = object;
277+
_msgpack_unpacker_rvalue_stack_push(uk, object);
196278
reset_head_byte(uk);
197279
return PRIMITIVE_OBJECT_COMPLETE;
198280
}
@@ -212,12 +294,14 @@ static inline int object_complete_ext(msgpack_unpacker_t* uk, int ext_type, VALU
212294
if(proc != Qnil) {
213295
VALUE obj;
214296
VALUE arg = (str == Qnil ? rb_str_buf_new(0) : str);
297+
215298
int raised;
216299
obj = protected_proc_call(proc, 1, &arg, &raised);
217300
if (raised) {
218301
uk->last_object = rb_errinfo();
219302
return PRIMITIVE_RECURSIVE_RAISED;
220303
}
304+
221305
return object_complete(uk, obj);
222306
}
223307

@@ -235,7 +319,7 @@ static inline msgpack_unpacker_stack_entry_t* _msgpack_unpacker_stack_entry_top(
235319
return &uk->stack->data[uk->stack->depth-1];
236320
}
237321

238-
static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stack_type_t type, size_t count, VALUE object)
322+
static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stack_type_t type, size_t count)
239323
{
240324
reset_head_byte(uk);
241325

@@ -245,22 +329,20 @@ static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stac
245329

246330
msgpack_unpacker_stack_entry_t* next = &uk->stack->data[uk->stack->depth];
247331
next->count = count;
332+
next->size = count;
248333
next->type = type;
249-
next->object = object;
250-
next->key = Qnil;
251-
252334
uk->stack->depth++;
253335
return PRIMITIVE_CONTAINER_START;
254336
}
255337

256-
static inline VALUE msgpack_unpacker_stack_pop(msgpack_unpacker_t* uk)
338+
static inline size_t msgpack_unpacker_stack_pop(msgpack_unpacker_t* uk)
257339
{
258340
return --uk->stack->depth;
259341
}
260342

261343
static inline bool msgpack_unpacker_stack_is_empty(msgpack_unpacker_t* uk)
262344
{
263-
return uk->stack->depth == 0;
345+
return uk->stack->depth == 0 || _msgpack_unpacker_stack_entry_top(uk)->type == STACK_TYPE_RECURSIVE;
264346
}
265347

266348
#ifdef USE_CASE_RANGE
@@ -290,9 +372,7 @@ static inline bool is_reading_map_key(msgpack_unpacker_t* uk)
290372
{
291373
if(uk->stack->depth > 0) {
292374
msgpack_unpacker_stack_entry_t* top = _msgpack_unpacker_stack_entry_top(uk);
293-
if(top->type == STACK_TYPE_MAP_KEY) {
294-
return true;
295-
}
375+
return top->type == STACK_TYPE_MAP && (top->count % 2 == 0);
296376
}
297377
return false;
298378
}
@@ -343,17 +423,17 @@ static inline int read_raw_body_begin(msgpack_unpacker_t* uk, int raw_type)
343423
reset_head_byte(uk);
344424
uk->reading_raw_remaining = 0;
345425

346-
msgpack_unpacker_stack_t* child_stack = _msgpack_unpacker_new_stack();
347-
child_stack->parent = uk->stack;
348-
uk->stack = child_stack;
349426

427+
_msgpack_unpacker_stack_push(uk, STACK_TYPE_RECURSIVE, 1);
428+
size_t value_stack_depth = uk->value_stack.depth;
350429
int raised;
351430
obj = protected_proc_call(proc, 1, &uk->self, &raised);
352-
uk->stack = child_stack->parent;
353-
_msgpack_unpacker_free_stack(child_stack);
431+
uk->value_stack.depth = value_stack_depth;
432+
msgpack_unpacker_stack_pop(uk);
354433

355434
if (raised) {
356435
uk->last_object = rb_errinfo();
436+
_msgpack_unpacker_rvalue_stack_push(uk, Qnil);
357437
return PRIMITIVE_RECURSIVE_RAISED;
358438
}
359439

@@ -418,14 +498,14 @@ static int read_primitive(msgpack_unpacker_t* uk)
418498
if(count == 0) {
419499
return object_complete(uk, rb_ary_new());
420500
}
421-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
501+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
422502

423503
SWITCH_RANGE(b, 0x80, 0x8f) // FixMap
424504
int count = b & 0x0f;
425505
if(count == 0) {
426506
return object_complete(uk, rb_hash_new());
427507
}
428-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
508+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
429509

430510
SWITCH_RANGE(b, 0xc0, 0xdf) // Variable
431511
switch(b) {
@@ -648,7 +728,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
648728
if(count == 0) {
649729
return object_complete(uk, rb_ary_new());
650730
}
651-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
731+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
652732
}
653733

654734
case 0xdd: // array 32
@@ -658,7 +738,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
658738
if(count == 0) {
659739
return object_complete(uk, rb_ary_new());
660740
}
661-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
741+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
662742
}
663743

664744
case 0xde: // map 16
@@ -668,7 +748,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
668748
if(count == 0) {
669749
return object_complete(uk, rb_hash_new());
670750
}
671-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
751+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
672752
}
673753

674754
case 0xdf: // map 32
@@ -678,7 +758,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
678758
if(count == 0) {
679759
return object_complete(uk, rb_hash_new());
680760
}
681-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
761+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
682762
}
683763

684764
default:
@@ -766,28 +846,23 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
766846
container_completed:
767847
{
768848
msgpack_unpacker_stack_entry_t* top = _msgpack_unpacker_stack_entry_top(uk);
769-
switch(top->type) {
770-
case STACK_TYPE_ARRAY:
771-
rb_ary_push(top->object, uk->last_object);
772-
break;
773-
case STACK_TYPE_MAP_KEY:
774-
top->key = uk->last_object;
775-
top->type = STACK_TYPE_MAP_VALUE;
776-
break;
777-
case STACK_TYPE_MAP_VALUE:
778-
if(uk->symbolize_keys && rb_type(top->key) == T_STRING) {
779-
/* here uses rb_str_intern instead of rb_intern so that Ruby VM can GC unused symbols */
780-
rb_hash_aset(top->object, rb_str_intern(top->key), uk->last_object);
781-
} else {
782-
rb_hash_aset(top->object, top->key, uk->last_object);
783-
}
784-
top->type = STACK_TYPE_MAP_KEY;
785-
break;
786-
}
787849
size_t count = --top->count;
788850

789851
if(count == 0) {
790-
object_complete(uk, top->object);
852+
switch(top->type) {
853+
case STACK_TYPE_ARRAY:
854+
_msgpack_unpacker_rvalue_stack_create_array(uk, top->size);
855+
break;
856+
case STACK_TYPE_MAP:
857+
_msgpack_unpacker_rvalue_stack_create_hash(uk, top->size);
858+
break;
859+
case STACK_TYPE_RECURSIVE:
860+
object_complete(uk, _msgpack_unpacker_rvalue_stack_pop(uk));
861+
return PRIMITIVE_OBJECT_COMPLETE;
862+
break;
863+
}
864+
865+
object_complete(uk, _msgpack_unpacker_rvalue_stack_pop(uk));
791866
if(msgpack_unpacker_stack_pop(uk) <= target_stack_depth) {
792867
return PRIMITIVE_OBJECT_COMPLETE;
793868
}

0 commit comments

Comments
 (0)