@@ -80,15 +80,15 @@ typedef struct
80
80
int current_path_len ;
81
81
int max_path_len ;
82
82
// Easier to use a Ruby array for result than convert later
83
- VALUE points_list ;
83
+ volatile VALUE points_list ;
84
84
// by depth
85
85
size_t * starts ;
86
- // VALUE rb_err;
86
+ // volatile VALUE rb_err;
87
87
yajl_handle handle ;
88
88
} scan_ctx ;
89
89
90
90
// FIXME: This will cause memory leak if ruby_xmalloc raises
91
- scan_ctx * scan_ctx_init (VALUE path_ary , int with_path , int symbolize_path_keys )
91
+ scan_ctx * scan_ctx_init (VALUE path_ary )
92
92
{
93
93
int path_ary_len ;
94
94
scan_ctx * ctx ;
@@ -98,6 +98,7 @@ scan_ctx *scan_ctx_init(VALUE path_ary, int with_path, int symbolize_path_keys)
98
98
path_ary_len = rb_long2int (rb_array_len (path_ary ));
99
99
// Check types early before any allocations, so exception is ok
100
100
// TODO: Fix this, just handle errors
101
+ // I'm not sure if it's possible that another Ruby thread changes path_ary items between these two loops
101
102
for (int i = 0 ; i < path_ary_len ; i ++ )
102
103
{
103
104
int path_len ;
@@ -145,8 +146,6 @@ scan_ctx *scan_ctx_init(VALUE path_ary, int with_path, int symbolize_path_keys)
145
146
146
147
ctx = ruby_xmalloc (sizeof (scan_ctx ));
147
148
148
- ctx -> with_path = with_path ;
149
- ctx -> symbolize_path_keys = symbolize_path_keys ;
150
149
ctx -> max_path_len = 0 ;
151
150
152
151
paths = ruby_xmalloc (sizeof (paths_t ) * path_ary_len );
@@ -216,18 +215,19 @@ scan_ctx *scan_ctx_init(VALUE path_ary, int with_path, int symbolize_path_keys)
216
215
ctx -> paths_len = path_ary_len ;
217
216
ctx -> current_path = ruby_xmalloc2 (sizeof (path_elem_t ), ctx -> max_path_len );
218
217
219
- ctx -> current_path_len = 0 ;
220
- ctx -> points_list = rb_ary_new_capa (path_ary_len );
221
- for (int i = 0 ; i < path_ary_len ; i ++ )
222
- {
223
- rb_ary_push (ctx -> points_list , rb_ary_new ());
224
- }
225
-
226
218
ctx -> starts = ruby_xmalloc2 (sizeof (size_t ), ctx -> max_path_len + 1 );
219
+ return ctx ;
220
+ }
221
+
222
+ // resets temporary values in the config
223
+ void scan_ctx_reset (scan_ctx * ctx , volatile VALUE points_list , int with_path , int symbolize_path_keys )
224
+ {
225
+ ctx -> current_path_len = 0 ;
227
226
// ctx->rb_err = Qnil;
228
227
ctx -> handle = NULL ;
229
-
230
- return ctx ;
228
+ ctx -> points_list = points_list ;
229
+ ctx -> with_path = with_path ;
230
+ ctx -> symbolize_path_keys = symbolize_path_keys ;
231
231
}
232
232
233
233
void scan_ctx_free (scan_ctx * ctx )
@@ -266,10 +266,10 @@ typedef enum
266
266
} value_type ;
267
267
268
268
// noexcept
269
- VALUE create_point (scan_ctx * sctx , value_type type , size_t length , size_t curr_pos )
269
+ volatile VALUE create_point (scan_ctx * sctx , value_type type , size_t length , size_t curr_pos )
270
270
{
271
271
VALUE values [3 ];
272
- VALUE point = rb_ary_new_capa (3 );
272
+ volatile VALUE point = rb_ary_new_capa (3 );
273
273
// noexcept
274
274
values [1 ] = RB_ULONG2NUM (curr_pos );
275
275
switch (type )
@@ -306,12 +306,12 @@ VALUE create_point(scan_ctx *sctx, value_type type, size_t length, size_t curr_p
306
306
}
307
307
308
308
// noexcept
309
- VALUE create_path (scan_ctx * sctx )
309
+ volatile VALUE create_path (scan_ctx * sctx )
310
310
{
311
- VALUE path = rb_ary_new_capa (sctx -> current_path_len );
311
+ volatile VALUE path = rb_ary_new_capa (sctx -> current_path_len );
312
312
for (int i = 0 ; i < sctx -> current_path_len ; i ++ )
313
313
{
314
- VALUE entry ;
314
+ volatile VALUE entry ;
315
315
switch (sctx -> current_path [i ].type )
316
316
{
317
317
case PATH_KEY :
@@ -337,7 +337,7 @@ void save_point(scan_ctx *sctx, value_type type, size_t length)
337
337
// TODO: Abort parsing if all paths are matched and no more mathces are possible: only trivial key/index matchers at the current level
338
338
// TODO: Don't re-compare already matched prefixes; hard to invalidate, though
339
339
// TODO: Might fail in case of no memory
340
- VALUE point = Qundef ;
340
+ volatile VALUE point = Qundef ;
341
341
int match ;
342
342
for (int i = 0 ; i < sctx -> paths_len ; i ++ )
343
343
{
@@ -534,9 +534,9 @@ VALUE scan(int argc, VALUE *argv, VALUE self)
534
534
yajl_handle handle ;
535
535
yajl_status stat ;
536
536
scan_ctx * ctx ;
537
- VALUE err = Qnil , result ;
537
+ volatile VALUE err = Qnil , result ;
538
538
// Turned out callbacks can't raise exceptions
539
- // VALUE callback_err;
539
+ // volatile VALUE callback_err;
540
540
#if RUBY_API_VERSION_MAJOR > 2 || (RUBY_API_VERSION_MAJOR == 2 && RUBY_API_VERSION_MINOR >= 7 )
541
541
rb_scan_args_kw (RB_SCAN_ARGS_LAST_HASH_KEYWORDS , argc , argv , "21:" , & json_str , & path_ary , & with_path_flag , & kwargs );
542
542
#else
@@ -561,7 +561,14 @@ VALUE scan(int argc, VALUE *argv, VALUE self)
561
561
#else
562
562
json_text_len = RSTRING_LEN (json_str );
563
563
#endif
564
- ctx = scan_ctx_init (path_ary , with_path , symbolize_path_keys );
564
+ ctx = scan_ctx_init (path_ary );
565
+ // Need to keep a ref to result array on the stack to prevent it from being GC-ed
566
+ result = rb_ary_new_capa (ctx -> paths_len );
567
+ for (int i = 0 ; i < ctx -> paths_len ; i ++ )
568
+ {
569
+ rb_ary_push (result , rb_ary_new ());
570
+ }
571
+ scan_ctx_reset (ctx , result , with_path , symbolize_path_keys );
565
572
566
573
handle = yajl_alloc (& scan_callbacks , NULL , (void * )ctx );
567
574
if (kwargs != Qnil ) // it's safe to read kwargs_values only if rb_get_kwargs was called
@@ -589,7 +596,6 @@ VALUE scan(int argc, VALUE *argv, VALUE self)
589
596
yajl_free_error (handle , (unsigned char * )str );
590
597
}
591
598
// callback_err = ctx->rb_err;
592
- result = ctx -> points_list ;
593
599
scan_ctx_free (ctx );
594
600
yajl_free (handle );
595
601
if (err != Qnil )
0 commit comments