Skip to content

Commit 6e8fd1d

Browse files
authored
Merge pull request #7 from uvlad7/fixes/copy-native-state
Preserve native wrapper state
2 parents 868cab6 + 1c0663b commit 6e8fd1d

6 files changed

Lines changed: 336 additions & 163 deletions

File tree

‎ext/json_scanner/json_scanner.c‎

Lines changed: 6 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
#include "json_scanner.h"
2+
#include "scan_ctx.h"
3+
#include "options.h"
4+
#include "selector.h"
25

36
VALUE rb_mJsonScanner;
4-
VALUE rb_cJsonScannerSelector;
5-
VALUE rb_cJsonScannerOptions;
67
VALUE rb_eJsonScannerParseError;
78
#define BYTES_CONSUMED "bytes_consumed"
89
ID rb_iv_bytes_consumed;
9-
#define SCAN_KWARGS_SIZE 9
10-
ID scan_kwargs_table[SCAN_KWARGS_SIZE];
11-
12-
#include "options.h"
1310

1411
VALUE null_sym;
1512
VALUE boolean_sym;
@@ -20,89 +17,6 @@ VALUE array_sym;
2017

2118
VALUE any_key_sym;
2219

23-
enum matcher_type
24-
{
25-
MATCHER_KEY,
26-
MATCHER_INDEX,
27-
MATCHER_ANY_KEY,
28-
MATCHER_INDEX_RANGE,
29-
// MATCHER_KEYS_LIST,
30-
// MATCHER_KEY_REGEX,
31-
};
32-
33-
enum path_type
34-
{
35-
PATH_KEY,
36-
PATH_INDEX,
37-
};
38-
39-
typedef struct
40-
{
41-
const char *val;
42-
size_t len;
43-
} hashkey_t;
44-
45-
typedef struct
46-
{
47-
const char *val;
48-
size_t len;
49-
int owned;
50-
} path_key_t;
51-
52-
typedef struct
53-
{
54-
long start;
55-
long end;
56-
} range_t;
57-
58-
typedef struct
59-
{
60-
enum matcher_type type;
61-
union
62-
{
63-
hashkey_t key;
64-
long index;
65-
range_t range;
66-
} value;
67-
} path_matcher_elem_t;
68-
69-
typedef struct
70-
{
71-
enum path_type type;
72-
union
73-
{
74-
path_key_t key;
75-
long index;
76-
} value;
77-
} path_elem_t;
78-
79-
typedef struct
80-
{
81-
path_matcher_elem_t *elems;
82-
int len;
83-
int matched_depth;
84-
} paths_t;
85-
86-
typedef struct
87-
{
88-
int with_path;
89-
int symbolize_path_keys;
90-
int paths_len;
91-
paths_t *paths;
92-
int current_path_len;
93-
int max_path_len;
94-
path_elem_t *current_path;
95-
// Easier to use a Ruby array for result than convert later
96-
// must be supplied by the caller and RB_GC_GUARD-ed if it isn't on the stack
97-
VALUE points_list;
98-
VALUE roots_info_list;
99-
// by depth
100-
size_t *starts;
101-
yajl_handle handle;
102-
size_t yajl_bytes_consumed;
103-
const unsigned char *json_text;
104-
size_t json_text_len;
105-
} scan_ctx;
10620

10721
static inline size_t scan_ctx_get_bytes_consumed(scan_ctx *ctx)
10822
{
@@ -326,6 +240,7 @@ static void scan_ctx_init(scan_ctx *ctx, VALUE path_ary)
326240
// Assign ctx->paths early so ruby_xfree(ctx->paths) will free the arena
327241
// if a Ruby exception happens during the population loop below
328242
ctx->paths = paths;
243+
ctx->paths_arena_size = arena_size;
329244
ctx->paths_len = 0;
330245
ctx->current_path = NULL;
331246
ctx->starts = NULL;
@@ -751,9 +666,6 @@ static int scan_on_end_array(void *ctx)
751666
return true;
752667
}
753668

754-
#include "selector.h"
755-
756-
757669
static yajl_callbacks scan_callbacks = {
758670
scan_on_null,
759671
scan_on_boolean,
@@ -871,6 +783,8 @@ static VALUE scan(int argc, VALUE *argv, VALUE self)
871783
{
872784
free_ctx = false;
873785
TypedData_Get_Struct(path_ary, scan_ctx, &selector_type, ctx);
786+
if (!ctx->paths)
787+
rb_raise(rb_eRuntimeError, "selector is not initialized");
874788
}
875789
else
876790
{
@@ -966,13 +880,4 @@ Init_json_scanner(void)
966880
string_sym = rb_id2sym(rb_intern("string"));
967881
object_sym = rb_id2sym(rb_intern("object"));
968882
array_sym = rb_id2sym(rb_intern("array"));
969-
scan_kwargs_table[0] = rb_intern("with_path");
970-
scan_kwargs_table[1] = rb_intern("verbose_error");
971-
scan_kwargs_table[2] = rb_intern("allow_comments");
972-
scan_kwargs_table[3] = rb_intern("dont_validate_strings");
973-
scan_kwargs_table[4] = rb_intern("allow_trailing_garbage");
974-
scan_kwargs_table[5] = rb_intern("allow_multiple_values");
975-
scan_kwargs_table[6] = rb_intern("allow_partial_values");
976-
scan_kwargs_table[7] = rb_intern("symbolize_path_keys");
977-
scan_kwargs_table[8] = rb_intern("with_roots_info");
978883
}

‎ext/json_scanner/options.h‎

Lines changed: 88 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,47 @@ typedef struct
1313
int symbolize_path_keys;
1414
int with_roots_info;
1515
} scan_options;
16+
1617
#define SCAN_OPTION_VALUE_MASK 1
1718
#define SCAN_OPTION_SET_MASK (1 << 1)
1819
#define SCAN_OPTION(options, field) ((options)->field & SCAN_OPTION_VALUE_MASK)
1920
#define SCAN_OPTION_IS_SET(options, field) ((options)->field & SCAN_OPTION_SET_MASK)
2021
#define SCAN_OPTION_SET(options, field, value) ((options)->field = ((value) & SCAN_OPTION_VALUE_MASK) | SCAN_OPTION_SET_MASK)
2122
#define SCAN_OPTION_FALSE(options, field) \
2223
(!SCAN_OPTION(options, field) && ((options)->field & SCAN_OPTION_SET_MASK))
24+
#define OPTIONS_FIELDS(X) \
25+
X(with_path) \
26+
X(verbose_error) \
27+
X(allow_comments) \
28+
X(dont_validate_strings) \
29+
X(allow_trailing_garbage) \
30+
X(allow_multiple_values) \
31+
X(allow_partial_values) \
32+
X(symbolize_path_keys) \
33+
X(with_roots_info)
34+
35+
static VALUE rb_cJsonScannerOptions;
36+
enum
37+
{
38+
#define SCAN_KWARG_ENUM(field) SCAN_KWARG_##field,
39+
OPTIONS_FIELDS(SCAN_KWARG_ENUM)
40+
#undef SCAN_KWARG_ENUM
41+
SCAN_KWARGS_SIZE,
42+
};
43+
static ID scan_kwargs_table[SCAN_KWARGS_SIZE];
2344

2445
static void scan_options_init(scan_options *options, VALUE kwargs)
2546
{
26-
options->with_path = 0;
27-
options->verbose_error = 0;
28-
options->allow_comments = 0;
29-
options->dont_validate_strings = 0;
30-
options->allow_trailing_garbage = 0;
31-
options->allow_multiple_values = 0;
32-
options->allow_partial_values = 0;
33-
options->symbolize_path_keys = 0;
34-
options->with_roots_info = 0;
47+
memset(options, 0, sizeof(*options));
3548
if (kwargs != Qnil)
3649
{
3750
VALUE kwargs_values[SCAN_KWARGS_SIZE];
3851
rb_get_kwargs(kwargs, scan_kwargs_table, 0, SCAN_KWARGS_SIZE, kwargs_values);
39-
if (kwargs_values[0] != Qundef)
40-
SCAN_OPTION_SET(options, with_path, RTEST(kwargs_values[0]));
41-
if (kwargs_values[1] != Qundef)
42-
SCAN_OPTION_SET(options, verbose_error, RTEST(kwargs_values[1]));
43-
if (kwargs_values[2] != Qundef)
44-
SCAN_OPTION_SET(options, allow_comments, RTEST(kwargs_values[2]));
45-
if (kwargs_values[3] != Qundef)
46-
SCAN_OPTION_SET(options, dont_validate_strings, RTEST(kwargs_values[3]));
47-
if (kwargs_values[4] != Qundef)
48-
SCAN_OPTION_SET(options, allow_trailing_garbage, RTEST(kwargs_values[4]));
49-
if (kwargs_values[5] != Qundef)
50-
SCAN_OPTION_SET(options, allow_multiple_values, RTEST(kwargs_values[5]));
51-
if (kwargs_values[6] != Qundef)
52-
SCAN_OPTION_SET(options, allow_partial_values, RTEST(kwargs_values[6]));
53-
if (kwargs_values[7] != Qundef)
54-
SCAN_OPTION_SET(options, symbolize_path_keys, RTEST(kwargs_values[7]));
55-
if (kwargs_values[8] != Qundef)
56-
SCAN_OPTION_SET(options, with_roots_info, RTEST(kwargs_values[8]));
52+
#define SCAN_OPTION_SET_KWARG(field) \
53+
if (kwargs_values[SCAN_KWARG_##field] != Qundef) \
54+
SCAN_OPTION_SET(options, field, RTEST(kwargs_values[SCAN_KWARG_##field]));
55+
OPTIONS_FIELDS(SCAN_OPTION_SET_KWARG)
56+
#undef SCAN_OPTION_SET_KWARG
5757
}
5858
}
5959

@@ -68,12 +68,17 @@ static const rb_data_type_t options_type = {
6868
.dfree = RUBY_DEFAULT_FREE,
6969
.dsize = options_size,
7070
},
71-
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
71+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
72+
#ifdef RUBY_TYPED_FROZEN_SHAREABLE
73+
| RUBY_TYPED_FROZEN_SHAREABLE
74+
#endif
75+
,
7276
};
7377

7478
static VALUE options_alloc(VALUE self)
7579
{
7680
scan_options *options;
81+
// TypedData_Make_Struct zeroes options.
7782
return TypedData_Make_Struct(self, scan_options, &options_type, options);
7883
}
7984

@@ -91,6 +96,50 @@ static VALUE options_m_initialize(int argc, VALUE *argv, VALUE self)
9196
return self;
9297
}
9398

99+
static VALUE options_m_initialize_copy(VALUE self, VALUE other)
100+
{
101+
scan_options *options, *other_options;
102+
rb_call_super(1, &other);
103+
TypedData_Get_Struct(self, scan_options, &options_type, options);
104+
TypedData_Get_Struct(other, scan_options, &options_type, other_options);
105+
*options = *other_options;
106+
return self;
107+
}
108+
109+
#define DEFINE_OPTIONS_ACCESSORS(field) \
110+
static VALUE options_m_##field(VALUE self) \
111+
{ \
112+
scan_options *options; \
113+
TypedData_Get_Struct(self, scan_options, &options_type, options); \
114+
if (!SCAN_OPTION_IS_SET(options, field)) \
115+
return Qnil; \
116+
return SCAN_OPTION(options, field) ? Qtrue : Qfalse; \
117+
} \
118+
static VALUE options_m_##field##_set(VALUE self, VALUE value) \
119+
{ \
120+
scan_options *options; \
121+
rb_check_frozen(self); \
122+
TypedData_Get_Struct(self, scan_options, &options_type, options); \
123+
SCAN_OPTION_SET(options, field, RTEST(value)); \
124+
return value; \
125+
}
126+
OPTIONS_FIELDS(DEFINE_OPTIONS_ACCESSORS)
127+
128+
static VALUE options_m_equal(VALUE self, VALUE other)
129+
{
130+
scan_options *options, *other_options;
131+
if (!rb_obj_is_kind_of(other, rb_cJsonScannerOptions))
132+
return Qfalse;
133+
TypedData_Get_Struct(self, scan_options, &options_type, options);
134+
TypedData_Get_Struct(other, scan_options, &options_type, other_options);
135+
#define OPTIONS_EQUAL(field) \
136+
if (options->field != other_options->field) \
137+
return Qfalse;
138+
OPTIONS_FIELDS(OPTIONS_EQUAL)
139+
#undef OPTIONS_EQUAL
140+
return Qtrue;
141+
}
142+
94143
static VALUE options_m_inspect(VALUE self)
95144
{
96145
VALUE res;
@@ -100,15 +149,7 @@ static VALUE options_m_inspect(VALUE self)
100149
#define APPEND_OPTION_INSPECT(field) \
101150
if (SCAN_OPTION_IS_SET(options, field)) \
102151
rb_str_catf(res, #field ": %s, ", SCAN_OPTION(options, field) ? "true" : "false");
103-
APPEND_OPTION_INSPECT(with_path)
104-
APPEND_OPTION_INSPECT(verbose_error)
105-
APPEND_OPTION_INSPECT(allow_comments)
106-
APPEND_OPTION_INSPECT(dont_validate_strings)
107-
APPEND_OPTION_INSPECT(allow_trailing_garbage)
108-
APPEND_OPTION_INSPECT(allow_multiple_values)
109-
APPEND_OPTION_INSPECT(allow_partial_values)
110-
APPEND_OPTION_INSPECT(symbolize_path_keys)
111-
APPEND_OPTION_INSPECT(with_roots_info)
152+
OPTIONS_FIELDS(APPEND_OPTION_INSPECT)
112153
#undef APPEND_OPTION_INSPECT
113154
if (RSTRING_END(res)[-1] == ' ')
114155
rb_str_resize(res, RSTRING_LEN(res) - 2);
@@ -121,7 +162,17 @@ static void init_options(VALUE json_scanner_module)
121162
rb_cJsonScannerOptions = rb_define_class_under(json_scanner_module, "Options", rb_cObject);
122163
rb_define_alloc_func(rb_cJsonScannerOptions, options_alloc);
123164
rb_define_method(rb_cJsonScannerOptions, "initialize", options_m_initialize, -1);
165+
rb_define_method(rb_cJsonScannerOptions, "initialize_copy", options_m_initialize_copy, 1);
166+
#define DEFINE_OPTIONS_ACCESSOR_METHODS(field) \
167+
rb_define_method(rb_cJsonScannerOptions, #field, options_m_##field, 0); \
168+
rb_define_method(rb_cJsonScannerOptions, #field "=", options_m_##field##_set, 1);
169+
OPTIONS_FIELDS(DEFINE_OPTIONS_ACCESSOR_METHODS)
170+
#undef DEFINE_OPTIONS_ACCESSOR_METHODS
171+
rb_define_method(rb_cJsonScannerOptions, "==", options_m_equal, 1);
124172
rb_define_method(rb_cJsonScannerOptions, "inspect", options_m_inspect, 0);
173+
#define INIT_SCAN_KWARG(field) scan_kwargs_table[SCAN_KWARG_##field] = rb_intern(#field);
174+
OPTIONS_FIELDS(INIT_SCAN_KWARG)
175+
#undef INIT_SCAN_KWARG
125176
}
126177

127178
#endif /* JSON_SCANNER_OPTIONS_H */

0 commit comments

Comments
 (0)