oj 3.17.7 → 3.17.8
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +54 -0
- data/ext/oj/cache.c +64 -47
- data/ext/oj/cache.h +1 -1
- data/ext/oj/code.c +38 -28
- data/ext/oj/code.h +2 -0
- data/ext/oj/compat.c +29 -20
- data/ext/oj/custom.c +22 -5
- data/ext/oj/dump.c +93 -29
- data/ext/oj/dump_compat.c +8 -11
- data/ext/oj/dump_object.c +18 -2
- data/ext/oj/dump_strict.c +2 -1
- data/ext/oj/extconf.rb +0 -1
- data/ext/oj/fast.c +46 -29
- data/ext/oj/intern.c +17 -46
- data/ext/oj/mimic_json.c +4 -28
- data/ext/oj/object.c +21 -2
- data/ext/oj/oj.c +80 -21
- data/ext/oj/oj.h +1 -0
- data/ext/oj/parse.c +53 -3
- data/ext/oj/parse.h +9 -0
- data/ext/oj/parser.c +57 -16
- data/ext/oj/rails.c +8 -3
- data/ext/oj/reader.c +4 -0
- data/ext/oj/reader.h +1 -0
- data/ext/oj/resolve.c +5 -4
- data/ext/oj/resolve.h +9 -0
- data/ext/oj/saj.c +14 -7
- data/ext/oj/saj2.c +44 -28
- data/ext/oj/simd.h +3 -5
- data/ext/oj/sparse.c +7 -0
- data/ext/oj/stream_writer.c +3 -0
- data/ext/oj/string_writer.c +17 -3
- data/ext/oj/usual.c +4 -1
- data/ext/oj/val_stack.c +0 -22
- data/ext/oj/val_stack.h +4 -34
- data/ext/oj/wab.c +3 -0
- data/lib/oj/generic_object.rb +82 -0
- data/lib/oj/json.rb +1 -71
- data/lib/oj/mimic.rb +0 -1
- data/lib/oj/version.rb +1 -1
- metadata +16 -15
data/ext/oj/resolve.h
CHANGED
|
@@ -4,9 +4,18 @@
|
|
|
4
4
|
#ifndef OJ_RESOLVE_H
|
|
5
5
|
#define OJ_RESOLVE_H
|
|
6
6
|
|
|
7
|
+
#include <stdbool.h>
|
|
8
|
+
#include <string.h>
|
|
9
|
+
|
|
7
10
|
#include "ruby.h"
|
|
8
11
|
|
|
9
12
|
extern VALUE oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class);
|
|
10
13
|
extern VALUE oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class);
|
|
11
14
|
|
|
15
|
+
inline static bool oj_is_backtrace_name(const char *name, size_t len) {
|
|
16
|
+
static const char backtrace[] = "Thread::Backtrace";
|
|
17
|
+
|
|
18
|
+
return sizeof(backtrace) - 1 == len && 0 == memcmp(backtrace, name, len);
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
#endif /* OJ_RESOLVE_H */
|
data/ext/oj/saj.c
CHANGED
|
@@ -646,13 +646,14 @@ static VALUE protect_saj_parse(VALUE x) {
|
|
|
646
646
|
*/
|
|
647
647
|
VALUE
|
|
648
648
|
oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
649
|
-
char *json
|
|
650
|
-
size_t len
|
|
651
|
-
VALUE input
|
|
649
|
+
char *json = 0;
|
|
650
|
+
size_t len = 0;
|
|
651
|
+
VALUE input;
|
|
652
652
|
|
|
653
653
|
if (argc < 2) {
|
|
654
654
|
rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
|
|
655
655
|
}
|
|
656
|
+
input = argv[1];
|
|
656
657
|
if (rb_type(input) == T_STRING) {
|
|
657
658
|
// the json string gets modified so make a copy of it
|
|
658
659
|
len = RSTRING_LEN(input) + 1;
|
|
@@ -663,16 +664,20 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
663
664
|
volatile VALUE s;
|
|
664
665
|
|
|
665
666
|
if (oj_stringio_class == clas) {
|
|
666
|
-
s
|
|
667
|
+
s = rb_funcall2(input, oj_string_id, 0, 0);
|
|
668
|
+
StringValueCStr(s);
|
|
667
669
|
len = RSTRING_LEN(s) + 1;
|
|
668
670
|
json = OJ_R_ALLOC_N(char, len);
|
|
669
|
-
strcpy(json,
|
|
671
|
+
strcpy(json, RSTRING_PTR(s));
|
|
670
672
|
#if !IS_WINDOWS
|
|
671
673
|
} else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
|
|
672
674
|
int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
|
|
673
675
|
ssize_t cnt;
|
|
674
676
|
|
|
675
677
|
len = lseek(fd, 0, SEEK_END);
|
|
678
|
+
if ((size_t)-1 == len) {
|
|
679
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
680
|
+
}
|
|
676
681
|
lseek(fd, 0, SEEK_SET);
|
|
677
682
|
json = OJ_R_ALLOC_N(char, len + 1);
|
|
678
683
|
{
|
|
@@ -681,6 +686,7 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
681
686
|
while (total < len) {
|
|
682
687
|
cnt = read(fd, json + total, len - total);
|
|
683
688
|
if (cnt <= 0) {
|
|
689
|
+
OJ_R_FREE(json);
|
|
684
690
|
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
|
685
691
|
}
|
|
686
692
|
total += cnt;
|
|
@@ -689,10 +695,11 @@ oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
|
|
689
695
|
json[len] = '\0';
|
|
690
696
|
#endif
|
|
691
697
|
} else if (rb_respond_to(input, oj_read_id)) {
|
|
692
|
-
s
|
|
698
|
+
s = rb_funcall2(input, oj_read_id, 0, 0);
|
|
699
|
+
StringValueCStr(s);
|
|
693
700
|
len = RSTRING_LEN(s) + 1;
|
|
694
701
|
json = OJ_R_ALLOC_N(char, len);
|
|
695
|
-
strcpy(json,
|
|
702
|
+
strcpy(json, RSTRING_PTR(s));
|
|
696
703
|
} else {
|
|
697
704
|
rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
|
|
698
705
|
}
|
data/ext/oj/saj2.c
CHANGED
|
@@ -33,9 +33,30 @@ static void push_key(Saj d, VALUE key) {
|
|
|
33
33
|
d->tail++;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
static VALUE pop_key(Saj d) {
|
|
37
|
+
if (d->tail <= d->keys) {
|
|
38
|
+
rb_raise(rb_eIndexError, "accessing key stack");
|
|
39
|
+
}
|
|
40
|
+
d->tail--;
|
|
41
|
+
|
|
42
|
+
return *d->tail;
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
static void noop(ojParser p) {
|
|
37
46
|
}
|
|
38
47
|
|
|
48
|
+
static void open_push_key(ojParser p) {
|
|
49
|
+
push_key((Saj)p->ctx, get_key(p));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static void close_pop_key(ojParser p) {
|
|
53
|
+
Saj d = (Saj)p->ctx;
|
|
54
|
+
|
|
55
|
+
if (d->keys < d->tail) {
|
|
56
|
+
d->tail--;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
39
60
|
static void open_object(ojParser p) {
|
|
40
61
|
rb_funcall(((Saj)p->ctx)->handler, oj_hash_start_id, 1, Qnil);
|
|
41
62
|
}
|
|
@@ -89,11 +110,7 @@ static void close_object(ojParser p) {
|
|
|
89
110
|
VALUE key = Qnil;
|
|
90
111
|
|
|
91
112
|
if (OBJECT_FUN == p->stack[p->depth]) {
|
|
92
|
-
d
|
|
93
|
-
if (d->tail < d->keys) {
|
|
94
|
-
rb_raise(rb_eIndexError, "accessing key stack");
|
|
95
|
-
}
|
|
96
|
-
key = *d->tail;
|
|
113
|
+
key = pop_key(d);
|
|
97
114
|
}
|
|
98
115
|
rb_funcall(d->handler, oj_hash_end_id, 1, key);
|
|
99
116
|
}
|
|
@@ -103,11 +120,7 @@ static void close_object_loc(ojParser p) {
|
|
|
103
120
|
VALUE key = Qnil;
|
|
104
121
|
|
|
105
122
|
if (OBJECT_FUN == p->stack[p->depth]) {
|
|
106
|
-
d
|
|
107
|
-
if (d->tail < d->keys) {
|
|
108
|
-
rb_raise(rb_eIndexError, "accessing key stack");
|
|
109
|
-
}
|
|
110
|
-
key = *d->tail;
|
|
123
|
+
key = pop_key(d);
|
|
111
124
|
}
|
|
112
125
|
rb_funcall(d->handler, oj_hash_end_id, 3, key, LONG2FIX(p->line), LONG2FIX(p->cur - p->col));
|
|
113
126
|
}
|
|
@@ -117,11 +130,7 @@ static void close_array(ojParser p) {
|
|
|
117
130
|
VALUE key = Qnil;
|
|
118
131
|
|
|
119
132
|
if (OBJECT_FUN == p->stack[p->depth]) {
|
|
120
|
-
d
|
|
121
|
-
if (d->tail < d->keys) {
|
|
122
|
-
rb_raise(rb_eIndexError, "accessing key stack");
|
|
123
|
-
}
|
|
124
|
-
key = *d->tail;
|
|
133
|
+
key = pop_key(d);
|
|
125
134
|
}
|
|
126
135
|
rb_funcall(d->handler, oj_array_end_id, 1, key);
|
|
127
136
|
}
|
|
@@ -131,11 +140,7 @@ static void close_array_loc(ojParser p) {
|
|
|
131
140
|
VALUE key = Qnil;
|
|
132
141
|
|
|
133
142
|
if (OBJECT_FUN == p->stack[p->depth]) {
|
|
134
|
-
d
|
|
135
|
-
if (d->tail < d->keys) {
|
|
136
|
-
rb_raise(rb_eIndexError, "accessing key stack");
|
|
137
|
-
}
|
|
138
|
-
key = *d->tail;
|
|
143
|
+
key = pop_key(d);
|
|
139
144
|
}
|
|
140
145
|
rb_funcall(d->handler, oj_array_end_id, 3, key, LONG2FIX(p->line), LONG2FIX(p->cur - p->col));
|
|
141
146
|
}
|
|
@@ -380,10 +385,15 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
|
380
385
|
return d->handler;
|
|
381
386
|
}
|
|
382
387
|
if (0 == strcmp(key, "handler=")) {
|
|
388
|
+
int hash_start = rb_respond_to(value, oj_hash_start_id);
|
|
389
|
+
int hash_end = rb_respond_to(value, oj_hash_end_id);
|
|
390
|
+
int array_start = rb_respond_to(value, oj_array_start_id);
|
|
391
|
+
int array_end = rb_respond_to(value, oj_array_end_id);
|
|
392
|
+
|
|
383
393
|
d->tail = d->keys;
|
|
384
394
|
d->handler = value;
|
|
385
395
|
reset(p);
|
|
386
|
-
if (
|
|
396
|
+
if (hash_start) {
|
|
387
397
|
if (1 == rb_obj_method_arity(value, oj_hash_start_id)) {
|
|
388
398
|
p->funcs[TOP_FUN].open_object = open_object;
|
|
389
399
|
p->funcs[ARRAY_FUN].open_object = open_object;
|
|
@@ -393,8 +403,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
|
393
403
|
p->funcs[ARRAY_FUN].open_object = open_object_loc;
|
|
394
404
|
p->funcs[OBJECT_FUN].open_object = open_object_loc_key;
|
|
395
405
|
}
|
|
406
|
+
} else if (hash_end) {
|
|
407
|
+
p->funcs[OBJECT_FUN].open_object = open_push_key;
|
|
396
408
|
}
|
|
397
|
-
if (
|
|
409
|
+
if (array_start) {
|
|
398
410
|
if (1 == rb_obj_method_arity(value, oj_array_start_id)) {
|
|
399
411
|
p->funcs[TOP_FUN].open_array = open_array;
|
|
400
412
|
p->funcs[ARRAY_FUN].open_array = open_array;
|
|
@@ -404,8 +416,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
|
404
416
|
p->funcs[ARRAY_FUN].open_array = open_array_loc;
|
|
405
417
|
p->funcs[OBJECT_FUN].open_array = open_array_loc_key;
|
|
406
418
|
}
|
|
419
|
+
} else if (array_end) {
|
|
420
|
+
p->funcs[OBJECT_FUN].open_array = open_push_key;
|
|
407
421
|
}
|
|
408
|
-
if (
|
|
422
|
+
if (hash_end) {
|
|
409
423
|
if (1 == rb_obj_method_arity(value, oj_hash_end_id)) {
|
|
410
424
|
p->funcs[TOP_FUN].close_object = close_object;
|
|
411
425
|
p->funcs[ARRAY_FUN].close_object = close_object;
|
|
@@ -415,8 +429,10 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
|
415
429
|
p->funcs[ARRAY_FUN].close_object = close_object_loc;
|
|
416
430
|
p->funcs[OBJECT_FUN].close_object = close_object_loc;
|
|
417
431
|
}
|
|
432
|
+
} else if (hash_start) {
|
|
433
|
+
p->funcs[OBJECT_FUN].close_object = close_pop_key;
|
|
418
434
|
}
|
|
419
|
-
if (
|
|
435
|
+
if (array_end) {
|
|
420
436
|
if (1 == rb_obj_method_arity(value, oj_array_end_id)) {
|
|
421
437
|
p->funcs[TOP_FUN].close_array = close_array;
|
|
422
438
|
p->funcs[ARRAY_FUN].close_array = close_array;
|
|
@@ -426,6 +442,8 @@ static VALUE option(ojParser p, const char *key, VALUE value) {
|
|
|
426
442
|
p->funcs[ARRAY_FUN].close_array = close_array_loc;
|
|
427
443
|
p->funcs[OBJECT_FUN].close_array = close_array_loc;
|
|
428
444
|
}
|
|
445
|
+
} else if (array_start) {
|
|
446
|
+
p->funcs[OBJECT_FUN].close_array = close_pop_key;
|
|
429
447
|
}
|
|
430
448
|
if (rb_respond_to(value, oj_add_value_id)) {
|
|
431
449
|
if (2 == rb_obj_method_arity(value, oj_add_value_id)) {
|
|
@@ -547,10 +565,8 @@ static void mark(ojParser p) {
|
|
|
547
565
|
if (Qnil != d->handler) {
|
|
548
566
|
rb_gc_mark(d->handler);
|
|
549
567
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
rb_gc_mark(*kp);
|
|
553
|
-
}
|
|
568
|
+
for (kp = d->keys; kp < d->tail; kp++) {
|
|
569
|
+
rb_gc_mark(*kp);
|
|
554
570
|
}
|
|
555
571
|
}
|
|
556
572
|
|
data/ext/oj/simd.h
CHANGED
|
@@ -98,12 +98,10 @@ static inline int oj_ctz64_fallback(uint64_t x) {
|
|
|
98
98
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
99
99
|
// GCC/Clang: check for header availability and include them
|
|
100
100
|
// We include headers but use target attributes to enable instructions per-function
|
|
101
|
-
// Include cpuid.h for __get_cpuid fallback when __builtin_cpu_supports is unavailable
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
// Include cpuid.h for __get_cpuid fallback when __builtin_cpu_supports is unavailable.
|
|
102
|
+
// cpuid.h is available on all GCC since 4.3 and all Clang versions, so include
|
|
103
|
+
// it unconditionally rather than gating on __has_include (which GCC < 5 lacks).
|
|
104
104
|
#include <cpuid.h>
|
|
105
|
-
#endif
|
|
106
|
-
#endif
|
|
107
105
|
#if defined(__SSE4_2__) || defined(__SSE2__)
|
|
108
106
|
// If any SSE is enabled globally, x86intrin.h should be available
|
|
109
107
|
#include <x86intrin.h>
|
data/ext/oj/sparse.c
CHANGED
|
@@ -274,6 +274,10 @@ static void read_escaped_str(ParseInfo pi) {
|
|
|
274
274
|
buf_cleanup(&buf);
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
|
+
} else if ((unsigned char)c < 0x20 && (StrictMode == pi->options.mode || CompatMode == pi->options.mode)) {
|
|
278
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
|
|
279
|
+
buf_cleanup(&buf);
|
|
280
|
+
return;
|
|
277
281
|
} else {
|
|
278
282
|
buf_append(&buf, c);
|
|
279
283
|
}
|
|
@@ -341,6 +345,9 @@ static void read_str(ParseInfo pi) {
|
|
|
341
345
|
read_escaped_str(pi);
|
|
342
346
|
reader_release(&pi->rd);
|
|
343
347
|
return;
|
|
348
|
+
} else if ((unsigned char)c < 0x20 && (StrictMode == pi->options.mode || CompatMode == pi->options.mode)) {
|
|
349
|
+
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid control character in string");
|
|
350
|
+
return;
|
|
344
351
|
}
|
|
345
352
|
}
|
|
346
353
|
if (0 == parent) { // simple add
|
data/ext/oj/stream_writer.c
CHANGED
|
@@ -16,6 +16,9 @@ static void stream_writer_free(void *ptr) {
|
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
sw = (StreamWriter)ptr;
|
|
19
|
+
if (NULL != sw->sw.out.circ_cache) {
|
|
20
|
+
oj_cache8_delete(sw->sw.out.circ_cache);
|
|
21
|
+
}
|
|
19
22
|
oj_options_release(&sw->sw.opts);
|
|
20
23
|
OJ_R_FREE(sw->sw.out.buf);
|
|
21
24
|
OJ_R_FREE(sw->sw.types);
|
data/ext/oj/string_writer.c
CHANGED
|
@@ -169,6 +169,11 @@ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
|
|
|
169
169
|
*out->cur++ = ':';
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
+
if (NULL != out->circ_cache) {
|
|
173
|
+
oj_cache8_delete(out->circ_cache);
|
|
174
|
+
out->circ_cache = NULL;
|
|
175
|
+
}
|
|
176
|
+
out->circ_cnt = 0;
|
|
172
177
|
switch (out->opts->mode) {
|
|
173
178
|
case StrictMode: oj_dump_strict_val(val, sw->depth, out); break;
|
|
174
179
|
case NullMode: oj_dump_null_val(val, sw->depth, out); break;
|
|
@@ -178,6 +183,10 @@ void oj_str_writer_push_value(StrWriter sw, VALUE val, const char *key) {
|
|
|
178
183
|
case CustomMode: oj_dump_custom_val(val, sw->depth, out, true); break;
|
|
179
184
|
default: oj_dump_custom_val(val, sw->depth, out, true); break;
|
|
180
185
|
}
|
|
186
|
+
if (NULL != out->circ_cache) {
|
|
187
|
+
oj_cache8_delete(out->circ_cache);
|
|
188
|
+
out->circ_cache = NULL;
|
|
189
|
+
}
|
|
181
190
|
}
|
|
182
191
|
|
|
183
192
|
void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
|
|
@@ -203,16 +212,17 @@ void oj_str_writer_push_json(StrWriter sw, const char *json, const char *key) {
|
|
|
203
212
|
|
|
204
213
|
void oj_str_writer_pop(StrWriter sw) {
|
|
205
214
|
long size;
|
|
206
|
-
DumpType type
|
|
215
|
+
DumpType type;
|
|
207
216
|
|
|
208
217
|
if (sw->keyWritten) {
|
|
209
218
|
sw->keyWritten = 0;
|
|
210
219
|
rb_raise(rb_eStandardError, "Can not pop after writing a key but no value.");
|
|
211
220
|
}
|
|
212
|
-
sw->depth
|
|
213
|
-
if (0 > sw->depth) {
|
|
221
|
+
if (0 >= sw->depth) {
|
|
214
222
|
rb_raise(rb_eStandardError, "Can not pop with no open array or object.");
|
|
215
223
|
}
|
|
224
|
+
type = sw->types[sw->depth];
|
|
225
|
+
sw->depth--;
|
|
216
226
|
size = sw->depth * sw->out.indent + 2;
|
|
217
227
|
assure_size(&sw->out, size);
|
|
218
228
|
fill_indent(&sw->out, sw->depth);
|
|
@@ -241,6 +251,10 @@ static void string_writer_free(void *ptr) {
|
|
|
241
251
|
}
|
|
242
252
|
sw = (StrWriter)ptr;
|
|
243
253
|
|
|
254
|
+
if (NULL != sw->out.circ_cache) {
|
|
255
|
+
oj_cache8_delete(sw->out.circ_cache);
|
|
256
|
+
sw->out.circ_cache = NULL;
|
|
257
|
+
}
|
|
244
258
|
oj_out_free(&sw->out);
|
|
245
259
|
oj_options_release(&sw->opts);
|
|
246
260
|
|
data/ext/oj/usual.c
CHANGED
|
@@ -85,6 +85,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, bool auto_defin
|
|
|
85
85
|
|
|
86
86
|
if (rb_const_defined_at(mod, ci)) {
|
|
87
87
|
clas = rb_const_get_at(mod, ci);
|
|
88
|
+
if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
|
|
89
|
+
clas = Qundef;
|
|
90
|
+
}
|
|
88
91
|
} else if (auto_define) {
|
|
89
92
|
clas = rb_define_class_under(mod, classname, oj_bag_class);
|
|
90
93
|
} else {
|
|
@@ -814,7 +817,7 @@ static VALUE opt_class_cache_set(ojParser p, VALUE value) {
|
|
|
814
817
|
|
|
815
818
|
if (Qtrue == value) {
|
|
816
819
|
if (NULL == d->class_cache) {
|
|
817
|
-
d->class_cache = cache_create(0, form_class_auto,
|
|
820
|
+
d->class_cache = cache_create(0, form_class_auto, true, false);
|
|
818
821
|
}
|
|
819
822
|
} else if (NULL != d->class_cache) {
|
|
820
823
|
cache_free(d->class_cache);
|
data/ext/oj/val_stack.c
CHANGED
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
#include "val_stack.h"
|
|
5
5
|
|
|
6
|
-
#include <string.h>
|
|
7
|
-
|
|
8
6
|
#include "odd.h"
|
|
9
7
|
#include "oj.h"
|
|
10
8
|
|
|
@@ -15,12 +13,6 @@ static void stack_mark(void *ptr) {
|
|
|
15
13
|
if (NULL == ptr) {
|
|
16
14
|
return;
|
|
17
15
|
}
|
|
18
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
19
|
-
pthread_mutex_lock(&stack->mutex);
|
|
20
|
-
#else
|
|
21
|
-
rb_mutex_lock(stack->mutex);
|
|
22
|
-
rb_gc_mark(stack->mutex);
|
|
23
|
-
#endif
|
|
24
16
|
{
|
|
25
17
|
size_t i;
|
|
26
18
|
|
|
@@ -46,11 +38,6 @@ static void stack_mark(void *ptr) {
|
|
|
46
38
|
}
|
|
47
39
|
}
|
|
48
40
|
}
|
|
49
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
50
|
-
pthread_mutex_unlock(&stack->mutex);
|
|
51
|
-
#else
|
|
52
|
-
rb_mutex_unlock(stack->mutex);
|
|
53
|
-
#endif
|
|
54
41
|
}
|
|
55
42
|
|
|
56
43
|
static const rb_data_type_t oj_stack_type = {
|
|
@@ -66,15 +53,6 @@ static const rb_data_type_t oj_stack_type = {
|
|
|
66
53
|
|
|
67
54
|
VALUE
|
|
68
55
|
oj_stack_init(ValStack stack) {
|
|
69
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
70
|
-
int err;
|
|
71
|
-
|
|
72
|
-
if (0 != (err = pthread_mutex_init(&stack->mutex, 0))) {
|
|
73
|
-
rb_raise(rb_eException, "failed to initialize a mutex. %s", strerror(err));
|
|
74
|
-
}
|
|
75
|
-
#else
|
|
76
|
-
stack->mutex = rb_mutex_new();
|
|
77
|
-
#endif
|
|
78
56
|
stack->head = stack->base;
|
|
79
57
|
stack->end = stack->base + sizeof(stack->base) / sizeof(struct _val);
|
|
80
58
|
stack->tail = stack->head;
|
data/ext/oj/val_stack.h
CHANGED
|
@@ -9,9 +9,6 @@
|
|
|
9
9
|
#include "mem.h"
|
|
10
10
|
#include "odd.h"
|
|
11
11
|
#include "ruby.h"
|
|
12
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
13
|
-
#include <pthread.h>
|
|
14
|
-
#endif
|
|
15
12
|
|
|
16
13
|
#define STACK_INC 64
|
|
17
14
|
|
|
@@ -58,12 +55,6 @@ typedef struct _valStack {
|
|
|
58
55
|
Val head; // current stack
|
|
59
56
|
Val end; // stack end
|
|
60
57
|
Val tail; // pointer to one past last element name on stack
|
|
61
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
62
|
-
pthread_mutex_t mutex;
|
|
63
|
-
#else
|
|
64
|
-
VALUE mutex;
|
|
65
|
-
#endif
|
|
66
|
-
|
|
67
58
|
} *ValStack;
|
|
68
59
|
|
|
69
60
|
extern VALUE oj_stack_init(ValStack stack);
|
|
@@ -102,8 +93,8 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
|
|
|
102
93
|
const char *old = (const char *)stack->head;
|
|
103
94
|
size_t i;
|
|
104
95
|
|
|
105
|
-
// A realloc can trigger a GC
|
|
106
|
-
//
|
|
96
|
+
// A realloc can trigger a GC that marks the stack, so change the
|
|
97
|
+
// pointers only after it returns.
|
|
107
98
|
if (stack->base == stack->head) {
|
|
108
99
|
head = OJ_R_ALLOC_N(struct _val, len + STACK_INC);
|
|
109
100
|
memcpy(head, stack->base, sizeof(struct _val) * len);
|
|
@@ -117,19 +108,9 @@ inline static void stack_push(ValStack stack, VALUE val, ValNext next) {
|
|
|
117
108
|
head[i].key = head[i].karray;
|
|
118
109
|
}
|
|
119
110
|
}
|
|
120
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
121
|
-
pthread_mutex_lock(&stack->mutex);
|
|
122
|
-
#else
|
|
123
|
-
rb_mutex_lock(stack->mutex);
|
|
124
|
-
#endif
|
|
125
111
|
stack->head = head;
|
|
126
112
|
stack->tail = stack->head + toff;
|
|
127
113
|
stack->end = stack->head + len + STACK_INC;
|
|
128
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
129
|
-
pthread_mutex_unlock(&stack->mutex);
|
|
130
|
-
#else
|
|
131
|
-
rb_mutex_unlock(stack->mutex);
|
|
132
|
-
#endif
|
|
133
114
|
}
|
|
134
115
|
stack->tail->val = val;
|
|
135
116
|
stack->tail->next = next;
|
|
@@ -183,9 +164,8 @@ inline static Val stack_pop(ValStack stack) {
|
|
|
183
164
|
}
|
|
184
165
|
|
|
185
166
|
// Append one key/value pair to the pair buffer. The same GC discipline as
|
|
186
|
-
// stack_push: the allocation
|
|
187
|
-
//
|
|
188
|
-
// under the mutex.
|
|
167
|
+
// stack_push: the allocation can trigger a GC that marks the pairs, so the
|
|
168
|
+
// buffer pointer only changes after it returns.
|
|
189
169
|
inline static void stack_pair_push(ValStack stack, VALUE key, VALUE value) {
|
|
190
170
|
if (stack->pend <= stack->pcnt + 2) {
|
|
191
171
|
size_t cnt = stack->pend * 2;
|
|
@@ -198,18 +178,8 @@ inline static void stack_pair_push(ValStack stack, VALUE key, VALUE value) {
|
|
|
198
178
|
pairs = stack->pairs;
|
|
199
179
|
OJ_R_REALLOC_N(pairs, VALUE, cnt);
|
|
200
180
|
}
|
|
201
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
202
|
-
pthread_mutex_lock(&stack->mutex);
|
|
203
|
-
#else
|
|
204
|
-
rb_mutex_lock(stack->mutex);
|
|
205
|
-
#endif
|
|
206
181
|
stack->pairs = pairs;
|
|
207
182
|
stack->pend = cnt;
|
|
208
|
-
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
|
209
|
-
pthread_mutex_unlock(&stack->mutex);
|
|
210
|
-
#else
|
|
211
|
-
rb_mutex_unlock(stack->mutex);
|
|
212
|
-
#endif
|
|
213
183
|
}
|
|
214
184
|
stack->pairs[stack->pcnt] = key;
|
|
215
185
|
stack->pairs[stack->pcnt + 1] = value;
|
data/ext/oj/wab.c
CHANGED
|
@@ -40,6 +40,7 @@ static VALUE resolve_wab_uuid_class(void) {
|
|
|
40
40
|
volatile VALUE wab_module;
|
|
41
41
|
|
|
42
42
|
wab_uuid_clas = Qnil;
|
|
43
|
+
rb_gc_register_address(&wab_uuid_clas);
|
|
43
44
|
if (rb_const_defined_at(rb_cObject, rb_intern("WAB"))) {
|
|
44
45
|
wab_module = rb_const_get_at(rb_cObject, rb_intern("WAB"));
|
|
45
46
|
if (rb_const_defined_at(wab_module, rb_intern("UUID"))) {
|
|
@@ -53,6 +54,7 @@ static VALUE resolve_wab_uuid_class(void) {
|
|
|
53
54
|
static VALUE resolve_uri_class(void) {
|
|
54
55
|
if (Qundef == uri_clas) {
|
|
55
56
|
uri_clas = Qnil;
|
|
57
|
+
rb_gc_register_address(&uri_clas);
|
|
56
58
|
if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
|
|
57
59
|
uri_clas = rb_const_get_at(rb_cObject, rb_intern("URI"));
|
|
58
60
|
}
|
|
@@ -65,6 +67,7 @@ static VALUE resolve_uri_http_class(void) {
|
|
|
65
67
|
volatile VALUE uri_module;
|
|
66
68
|
|
|
67
69
|
uri_http_clas = Qnil;
|
|
70
|
+
rb_gc_register_address(&uri_http_clas);
|
|
68
71
|
if (rb_const_defined_at(rb_cObject, rb_intern("URI"))) {
|
|
69
72
|
uri_module = rb_const_get_at(rb_cObject, rb_intern("URI"));
|
|
70
73
|
if (rb_const_defined_at(uri_module, rb_intern("HTTP"))) {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Loaded on first use of JSON::GenericObject so that requiring Oj does not
|
|
4
|
+
# also require ostruct, which is no longer a default gem as of Ruby 4.0.
|
|
5
|
+
begin
|
|
6
|
+
require 'ostruct'
|
|
7
|
+
rescue LoadError
|
|
8
|
+
raise LoadError.new("JSON::GenericObject requires 'ostruct'. Add it to your Gemfile or install it with `gem install ostruct`.")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module JSON
|
|
12
|
+
# Taken directly from the json gem. Shamelessly copied. It is similar in
|
|
13
|
+
# some ways to the Oj::Bag class or the Oj::EasyHash class.
|
|
14
|
+
class GenericObject < OpenStruct
|
|
15
|
+
class << self
|
|
16
|
+
alias [] new
|
|
17
|
+
|
|
18
|
+
def json_creatable?
|
|
19
|
+
@json_creatable
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_writer :json_creatable
|
|
23
|
+
|
|
24
|
+
def json_create(data)
|
|
25
|
+
data = data.dup
|
|
26
|
+
data.delete JSON.create_id
|
|
27
|
+
self[data]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def from_hash(object)
|
|
31
|
+
case
|
|
32
|
+
when object.respond_to?(:to_hash)
|
|
33
|
+
result = new
|
|
34
|
+
object.to_hash.each do |key, value|
|
|
35
|
+
result[key] = from_hash(value)
|
|
36
|
+
end
|
|
37
|
+
result
|
|
38
|
+
when object.respond_to?(:to_ary)
|
|
39
|
+
object.to_ary.map { |a| from_hash(a) }
|
|
40
|
+
else
|
|
41
|
+
object
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def load(source, proc = nil, opts = {})
|
|
46
|
+
result = ::JSON.load(source, proc, opts.merge(:object_class => self))
|
|
47
|
+
result.nil? ? new : result
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def dump(obj, *args)
|
|
51
|
+
::JSON.dump(obj, *args)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end # self
|
|
55
|
+
|
|
56
|
+
self.json_creatable = false
|
|
57
|
+
|
|
58
|
+
def to_hash
|
|
59
|
+
table
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def [](name)
|
|
63
|
+
__send__(name)
|
|
64
|
+
end unless method_defined?(:[])
|
|
65
|
+
|
|
66
|
+
def []=(name, value)
|
|
67
|
+
__send__("#{name}=", value)
|
|
68
|
+
end unless method_defined?(:[]=)
|
|
69
|
+
|
|
70
|
+
def |(other)
|
|
71
|
+
self.class[other.to_hash.merge(to_hash)]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def as_json(*)
|
|
75
|
+
{ JSON.create_id => self.class.name }.merge to_hash
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_json(*a)
|
|
79
|
+
as_json.to_json(*a)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end # JSON
|
data/lib/oj/json.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require 'ostruct'
|
|
2
1
|
require 'oj/state'
|
|
3
2
|
|
|
4
3
|
if defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
|
@@ -113,76 +112,7 @@ unless defined?(JSON::PRETTY_STATE_PROTOTYPE)
|
|
|
113
112
|
self.parser = ::JSON::Ext::Parser
|
|
114
113
|
self.generator = ::JSON::Ext::Generator
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
# some ways to the Oj::Bag class or the Oj::EasyHash class.
|
|
118
|
-
class GenericObject < OpenStruct
|
|
119
|
-
class << self
|
|
120
|
-
alias [] new
|
|
121
|
-
|
|
122
|
-
def json_creatable?
|
|
123
|
-
@json_creatable
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
attr_writer :json_creatable
|
|
127
|
-
|
|
128
|
-
def json_create(data)
|
|
129
|
-
data = data.dup
|
|
130
|
-
data.delete JSON.create_id
|
|
131
|
-
self[data]
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def from_hash(object)
|
|
135
|
-
case
|
|
136
|
-
when object.respond_to?(:to_hash)
|
|
137
|
-
result = new
|
|
138
|
-
object.to_hash.each do |key, value|
|
|
139
|
-
result[key] = from_hash(value)
|
|
140
|
-
end
|
|
141
|
-
result
|
|
142
|
-
when object.respond_to?(:to_ary)
|
|
143
|
-
object.to_ary.map { |a| from_hash(a) }
|
|
144
|
-
else
|
|
145
|
-
object
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def load(source, proc = nil, opts = {})
|
|
150
|
-
result = ::JSON.load(source, proc, opts.merge(:object_class => self))
|
|
151
|
-
result.nil? ? new : result
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def dump(obj, *args)
|
|
155
|
-
::JSON.dump(obj, *args)
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
end # self
|
|
159
|
-
|
|
160
|
-
self.json_creatable = false
|
|
161
|
-
|
|
162
|
-
def to_hash
|
|
163
|
-
table
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def [](name)
|
|
167
|
-
__send__(name)
|
|
168
|
-
end unless method_defined?(:[])
|
|
169
|
-
|
|
170
|
-
def []=(name, value)
|
|
171
|
-
__send__("#{name}=", value)
|
|
172
|
-
end unless method_defined?(:[]=)
|
|
173
|
-
|
|
174
|
-
def |(other)
|
|
175
|
-
self.class[other.to_hash.merge(to_hash)]
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def as_json(*)
|
|
179
|
-
{ JSON.create_id => self.class.name }.merge to_hash
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def to_json(*a)
|
|
183
|
-
as_json.to_json(*a)
|
|
184
|
-
end
|
|
185
|
-
end
|
|
115
|
+
autoload :GenericObject, 'oj/generic_object'
|
|
186
116
|
|
|
187
117
|
end # JSON
|
|
188
118
|
end
|