Skip to content

Commit 2818698

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 6bbaa97 commit 2818698

File tree

4 files changed

+155
-64
lines changed

4 files changed

+155
-64
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: 130 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
static int RAW_TYPE_STRING = 256;
3030
static int RAW_TYPE_BINARY = 257;
31-
static int16_t INITIAL_BUFFER_CAPACITY_MAX = SHRT_MAX;
3231

3332
static msgpack_rmem_t s_stack_rmem;
3433

@@ -39,9 +38,106 @@ static inline VALUE rb_hash_new_capa(long capa)
3938
}
4039
#endif
4140

42-
static inline int16_t initial_buffer_size(long size)
41+
#ifndef HAVE_RB_HASH_BULK_INSERT
42+
void rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash)
4343
{
44-
return (size > INITIAL_BUFFER_CAPACITY_MAX) ? INITIAL_BUFFER_CAPACITY_MAX : size;
44+
long index = 0;
45+
while (index < count) {
46+
VALUE name = pairs[index++];
47+
VALUE value = pairs[index++];
48+
rb_hash_aset(hash, name, value);
49+
}
50+
RB_GC_GUARD(hash);
51+
}
52+
#endif
53+
54+
/* rvalue_stack functions */
55+
56+
static inline void _msgpack_unpacker_rvalue_stack_free(msgpack_rvalue_stack_t* value_stack) {
57+
switch (value_stack->type) {
58+
case STACK_TYPE_UNALLOCATED:
59+
break;
60+
case STACK_TYPE_RMEM:
61+
if (!msgpack_rmem_free(&s_stack_rmem, value_stack->data)) {
62+
rb_bug("Failed to free an rmem pointer, memory leak?");
63+
}
64+
break;
65+
case STACK_TYPE_HEAP:
66+
xfree(value_stack->data);
67+
break;
68+
}
69+
memset(value_stack, 0, sizeof(msgpack_rvalue_stack_t));
70+
}
71+
72+
static void _msgpack_unpacker_rvalue_stack_grow(msgpack_rvalue_stack_t* value_stack) {
73+
switch (value_stack->type) {
74+
case STACK_TYPE_UNALLOCATED: {
75+
value_stack->data = msgpack_rmem_alloc(&s_stack_rmem);
76+
value_stack->capacity = MSGPACK_RMEM_PAGE_SIZE / sizeof(VALUE);
77+
value_stack->type = STACK_TYPE_RMEM;
78+
break;
79+
}
80+
case STACK_TYPE_RMEM: {
81+
size_t new_capacity = value_stack->capacity * 2;
82+
VALUE *new_ptr = ALLOC_N(VALUE, new_capacity);
83+
MEMCPY(new_ptr, value_stack->data, VALUE, value_stack->depth);
84+
if (!msgpack_rmem_free(&s_stack_rmem, value_stack->data)) {
85+
rb_bug("Failed to free an rmem pointer, memory leak?");
86+
}
87+
value_stack->type = STACK_TYPE_HEAP;
88+
value_stack->data = new_ptr;
89+
value_stack->capacity = new_capacity;
90+
break;
91+
}
92+
case STACK_TYPE_HEAP: {
93+
size_t new_capacity = value_stack->capacity * 2;
94+
REALLOC_N(value_stack->data, VALUE, new_capacity);
95+
value_stack->capacity = new_capacity;
96+
break;
97+
}
98+
}
99+
}
100+
101+
static inline void _msgpack_unpacker_rvalue_stack_push(msgpack_unpacker_t* uk, VALUE value) {
102+
size_t free_slots = uk->value_stack.capacity - uk->value_stack.depth;
103+
104+
if (RB_UNLIKELY(free_slots == 0)) {
105+
_msgpack_unpacker_rvalue_stack_grow(&uk->value_stack);
106+
free_slots = uk->value_stack.capacity - uk->value_stack.depth;
107+
}
108+
109+
RB_OBJ_WRITE(uk->self, &uk->value_stack.data[uk->value_stack.depth++], value);
110+
}
111+
112+
static inline VALUE _msgpack_unpacker_rvalue_stack_create_array(msgpack_unpacker_t* uk, size_t length) {
113+
VALUE *data = &uk->value_stack.data[uk->value_stack.depth - length];
114+
115+
VALUE array = rb_ary_new_from_values(length, data);
116+
117+
RB_OBJ_WRITE(uk->self, data, array);
118+
uk->value_stack.depth -= (length - 1);
119+
120+
return RB_GC_GUARD(array);
121+
}
122+
123+
static inline VALUE _msgpack_unpacker_rvalue_stack_create_hash(msgpack_unpacker_t* uk, size_t items_count) {
124+
size_t length = items_count / 2;
125+
VALUE *data = &uk->value_stack.data[uk->value_stack.depth - items_count];
126+
127+
VALUE hash = rb_hash_new_capa(length);
128+
rb_hash_bulk_insert(items_count, data, hash);
129+
130+
RB_OBJ_WRITE(uk->self, data, hash);
131+
uk->value_stack.depth -= (items_count - 1);
132+
133+
return RB_GC_GUARD(hash);
134+
}
135+
136+
void msgpack_unpacker_mark_rvalue_stack(msgpack_rvalue_stack_t* value_stack)
137+
{
138+
if (value_stack->data) {
139+
rb_gc_mark_locations(value_stack->data, value_stack->data + value_stack->depth);
140+
}
45141
}
46142

47143
void msgpack_unpacker_static_init(void)
@@ -88,27 +184,15 @@ static inline void _msgpack_unpacker_free_stack(msgpack_unpacker_stack_t* stack)
88184
void _msgpack_unpacker_destroy(msgpack_unpacker_t* uk)
89185
{
90186
_msgpack_unpacker_free_stack(uk->stack);
187+
_msgpack_unpacker_rvalue_stack_free(&uk->value_stack);
91188
msgpack_buffer_destroy(UNPACKER_BUFFER_(uk));
92189
}
93190

94-
void msgpack_unpacker_mark_stack(msgpack_unpacker_stack_t* stack)
95-
{
96-
while (stack) {
97-
msgpack_unpacker_stack_entry_t* s = stack->data;
98-
msgpack_unpacker_stack_entry_t* send = stack->data + stack->depth;
99-
for(; s < send; s++) {
100-
rb_gc_mark(s->object);
101-
rb_gc_mark(s->key);
102-
}
103-
stack = stack->parent;
104-
}
105-
}
106-
107191
void msgpack_unpacker_mark(msgpack_unpacker_t* uk)
108192
{
109193
rb_gc_mark(uk->last_object);
110194
rb_gc_mark(uk->reading_raw);
111-
msgpack_unpacker_mark_stack(uk->stack);
195+
msgpack_unpacker_mark_rvalue_stack(&uk->value_stack);
112196
/* See MessagePack_Buffer_wrap */
113197
/* msgpack_buffer_mark(UNPACKER_BUFFER_(uk)); */
114198
rb_gc_mark(uk->buffer_ref);
@@ -160,13 +244,15 @@ static inline int object_complete(msgpack_unpacker_t* uk, VALUE object)
160244
}
161245

162246
uk->last_object = object;
247+
_msgpack_unpacker_rvalue_stack_push(uk, object);
163248
reset_head_byte(uk);
164249
return PRIMITIVE_OBJECT_COMPLETE;
165250
}
166251

167252
static inline int object_complete_symbol(msgpack_unpacker_t* uk, VALUE object)
168253
{
169254
uk->last_object = object;
255+
_msgpack_unpacker_rvalue_stack_push(uk, object);
170256
reset_head_byte(uk);
171257
return PRIMITIVE_OBJECT_COMPLETE;
172258
}
@@ -204,7 +290,7 @@ static inline msgpack_unpacker_stack_entry_t* _msgpack_unpacker_stack_entry_top(
204290
return &uk->stack->data[uk->stack->depth-1];
205291
}
206292

207-
static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stack_type_t type, size_t count, VALUE object)
293+
static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stack_type_t type, size_t count)
208294
{
209295
reset_head_byte(uk);
210296

@@ -214,22 +300,20 @@ static inline int _msgpack_unpacker_stack_push(msgpack_unpacker_t* uk, enum stac
214300

215301
msgpack_unpacker_stack_entry_t* next = &uk->stack->data[uk->stack->depth];
216302
next->count = count;
303+
next->size = count;
217304
next->type = type;
218-
next->object = object;
219-
next->key = Qnil;
220-
221305
uk->stack->depth++;
222306
return PRIMITIVE_CONTAINER_START;
223307
}
224308

225-
static inline VALUE msgpack_unpacker_stack_pop(msgpack_unpacker_t* uk)
309+
static inline size_t msgpack_unpacker_stack_pop(msgpack_unpacker_t* uk)
226310
{
227311
return --uk->stack->depth;
228312
}
229313

230314
static inline bool msgpack_unpacker_stack_is_empty(msgpack_unpacker_t* uk)
231315
{
232-
return uk->stack->depth == 0;
316+
return uk->stack->depth == 0 || _msgpack_unpacker_stack_entry_top(uk)->type == STACK_TYPE_RECURSIVE;
233317
}
234318

235319
#ifdef USE_CASE_RANGE
@@ -259,9 +343,7 @@ static inline bool is_reading_map_key(msgpack_unpacker_t* uk)
259343
{
260344
if(uk->stack->depth > 0) {
261345
msgpack_unpacker_stack_entry_t* top = _msgpack_unpacker_stack_entry_top(uk);
262-
if(top->type == STACK_TYPE_MAP_KEY) {
263-
return true;
264-
}
346+
return top->type == STACK_TYPE_MAP && (top->count % 2 == 0);
265347
}
266348
return false;
267349
}
@@ -312,14 +394,9 @@ static inline int read_raw_body_begin(msgpack_unpacker_t* uk, int raw_type)
312394
reset_head_byte(uk);
313395
uk->reading_raw_remaining = 0;
314396

315-
msgpack_unpacker_stack_t* child_stack = _msgpack_unpacker_new_stack();
316-
child_stack->parent = uk->stack;
317-
uk->stack = child_stack;
318-
397+
_msgpack_unpacker_stack_push(uk, STACK_TYPE_RECURSIVE, 1);
319398
obj = rb_proc_call_with_block(proc, 1, &uk->self, Qnil);
320-
321-
uk->stack = child_stack->parent;
322-
_msgpack_unpacker_free_stack(child_stack);
399+
msgpack_unpacker_stack_pop(uk);
323400

324401
return object_complete(uk, obj);
325402
}
@@ -382,14 +459,14 @@ static int read_primitive(msgpack_unpacker_t* uk)
382459
if(count == 0) {
383460
return object_complete(uk, rb_ary_new());
384461
}
385-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
462+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
386463

387464
SWITCH_RANGE(b, 0x80, 0x8f) // FixMap
388465
int count = b & 0x0f;
389466
if(count == 0) {
390467
return object_complete(uk, rb_hash_new());
391468
}
392-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
469+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
393470

394471
SWITCH_RANGE(b, 0xc0, 0xdf) // Variable
395472
switch(b) {
@@ -612,7 +689,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
612689
if(count == 0) {
613690
return object_complete(uk, rb_ary_new());
614691
}
615-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
692+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
616693
}
617694

618695
case 0xdd: // array 32
@@ -622,7 +699,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
622699
if(count == 0) {
623700
return object_complete(uk, rb_ary_new());
624701
}
625-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count, rb_ary_new2(initial_buffer_size(count)));
702+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_ARRAY, count);
626703
}
627704

628705
case 0xde: // map 16
@@ -632,7 +709,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
632709
if(count == 0) {
633710
return object_complete(uk, rb_hash_new());
634711
}
635-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
712+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
636713
}
637714

638715
case 0xdf: // map 32
@@ -642,7 +719,7 @@ static int read_primitive(msgpack_unpacker_t* uk)
642719
if(count == 0) {
643720
return object_complete(uk, rb_hash_new());
644721
}
645-
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP_KEY, count*2, rb_hash_new_capa(initial_buffer_size(count)));
722+
return _msgpack_unpacker_stack_push(uk, STACK_TYPE_MAP, count*2);
646723
}
647724

648725
default:
@@ -730,28 +807,23 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth)
730807
container_completed:
731808
{
732809
msgpack_unpacker_stack_entry_t* top = _msgpack_unpacker_stack_entry_top(uk);
733-
switch(top->type) {
734-
case STACK_TYPE_ARRAY:
735-
rb_ary_push(top->object, uk->last_object);
736-
break;
737-
case STACK_TYPE_MAP_KEY:
738-
top->key = uk->last_object;
739-
top->type = STACK_TYPE_MAP_VALUE;
740-
break;
741-
case STACK_TYPE_MAP_VALUE:
742-
if(uk->symbolize_keys && rb_type(top->key) == T_STRING) {
743-
/* here uses rb_str_intern instead of rb_intern so that Ruby VM can GC unused symbols */
744-
rb_hash_aset(top->object, rb_str_intern(top->key), uk->last_object);
745-
} else {
746-
rb_hash_aset(top->object, top->key, uk->last_object);
747-
}
748-
top->type = STACK_TYPE_MAP_KEY;
749-
break;
750-
}
751810
size_t count = --top->count;
752811

753812
if(count == 0) {
754-
object_complete(uk, top->object);
813+
switch(top->type) {
814+
case STACK_TYPE_ARRAY:
815+
_msgpack_unpacker_rvalue_stack_create_array(uk, top->size);
816+
break;
817+
case STACK_TYPE_MAP:
818+
_msgpack_unpacker_rvalue_stack_create_hash(uk, top->size);
819+
break;
820+
case STACK_TYPE_RECURSIVE:
821+
object_complete(uk, _msgpack_unpacker_rvalue_stack_pop(uk));
822+
return PRIMITIVE_OBJECT_COMPLETE;
823+
break;
824+
}
825+
826+
object_complete(uk, _msgpack_unpacker_rvalue_stack_pop(uk));
755827
if(msgpack_unpacker_stack_pop(uk) <= target_stack_depth) {
756828
return PRIMITIVE_OBJECT_COMPLETE;
757829
}

ext/msgpack/unpacker.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,42 @@
2626
struct msgpack_unpacker_t;
2727
typedef struct msgpack_unpacker_t msgpack_unpacker_t;
2828
typedef struct msgpack_unpacker_stack_t msgpack_unpacker_stack_t;
29+
typedef struct msgpack_rvalue_stack_t msgpack_rvalue_stack_t;
30+
31+
enum rvalue_stack_type_t {
32+
STACK_TYPE_UNALLOCATED = 0,
33+
STACK_TYPE_RMEM = 1,
34+
STACK_TYPE_HEAP = 2,
35+
};
36+
37+
struct msgpack_rvalue_stack_t {
38+
enum rvalue_stack_type_t type;
39+
size_t depth;
40+
size_t capacity;
41+
VALUE *data;
42+
};
2943

3044
enum stack_type_t {
3145
STACK_TYPE_ARRAY,
32-
STACK_TYPE_MAP_KEY,
33-
STACK_TYPE_MAP_VALUE,
46+
STACK_TYPE_MAP,
47+
STACK_TYPE_RECURSIVE,
3448
};
3549

3650
typedef struct {
3751
size_t count;
52+
size_t size;
3853
enum stack_type_t type;
39-
VALUE object;
40-
VALUE key;
4154
} msgpack_unpacker_stack_entry_t;
4255

4356
struct msgpack_unpacker_stack_t {
4457
size_t depth;
4558
size_t capacity;
4659
msgpack_unpacker_stack_entry_t *data;
47-
msgpack_unpacker_stack_t *parent;
4860
};
4961

5062
struct msgpack_unpacker_t {
5163
msgpack_buffer_t buffer;
64+
msgpack_rvalue_stack_t value_stack;
5265
msgpack_unpacker_stack_t *stack;
5366
unsigned int head_byte;
5467

@@ -124,9 +137,13 @@ int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth);
124137

125138
int msgpack_unpacker_skip(msgpack_unpacker_t* uk, size_t target_stack_depth);
126139

140+
static inline VALUE _msgpack_unpacker_rvalue_stack_pop(msgpack_unpacker_t* uk) {
141+
return uk->value_stack.data[--uk->value_stack.depth];
142+
}
143+
127144
static inline VALUE msgpack_unpacker_get_last_object(msgpack_unpacker_t* uk)
128145
{
129-
return uk->last_object;
146+
return _msgpack_unpacker_rvalue_stack_pop(uk);
130147
}
131148

132149

0 commit comments

Comments
 (0)