oj 2.0.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +17 -23
- data/README.md +74 -425
- data/ext/oj/buf.h +103 -0
- data/ext/oj/cache8.c +4 -0
- data/ext/oj/circarray.c +68 -0
- data/ext/oj/circarray.h +23 -0
- data/ext/oj/code.c +227 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +243 -0
- data/ext/oj/custom.c +1097 -0
- data/ext/oj/dump.c +766 -1534
- data/ext/oj/dump.h +92 -0
- data/ext/oj/dump_compat.c +937 -0
- data/ext/oj/dump_leaf.c +254 -0
- data/ext/oj/dump_object.c +810 -0
- data/ext/oj/dump_rails.c +329 -0
- data/ext/oj/dump_strict.c +416 -0
- data/ext/oj/encode.h +51 -0
- data/ext/oj/err.c +57 -0
- data/ext/oj/err.h +70 -0
- data/ext/oj/extconf.rb +17 -7
- data/ext/oj/fast.c +213 -180
- data/ext/oj/hash.c +163 -0
- data/ext/oj/hash.h +46 -0
- data/ext/oj/hash_test.c +512 -0
- data/ext/oj/mimic_json.c +817 -0
- data/ext/oj/mimic_rails.c +806 -0
- data/ext/oj/mimic_rails.h +17 -0
- data/ext/oj/object.c +752 -0
- data/ext/oj/odd.c +230 -0
- data/ext/oj/odd.h +44 -0
- data/ext/oj/oj.c +1288 -929
- data/ext/oj/oj.h +240 -69
- data/ext/oj/parse.c +1014 -0
- data/ext/oj/parse.h +92 -0
- data/ext/oj/reader.c +223 -0
- data/ext/oj/reader.h +151 -0
- data/ext/oj/resolve.c +127 -0
- data/ext/oj/{cache.h → resolve.h} +6 -13
- data/ext/oj/rxclass.c +133 -0
- data/ext/oj/rxclass.h +27 -0
- data/ext/oj/saj.c +77 -175
- data/ext/oj/scp.c +224 -0
- data/ext/oj/sparse.c +911 -0
- data/ext/oj/stream_writer.c +301 -0
- data/ext/oj/strict.c +162 -0
- data/ext/oj/string_writer.c +480 -0
- data/ext/oj/val_stack.c +98 -0
- data/ext/oj/val_stack.h +188 -0
- data/lib/oj/active_support_helper.rb +41 -0
- data/lib/oj/bag.rb +6 -10
- data/lib/oj/easy_hash.rb +52 -0
- data/lib/oj/json.rb +172 -0
- data/lib/oj/mimic.rb +260 -5
- data/lib/oj/saj.rb +13 -10
- data/lib/oj/schandler.rb +142 -0
- data/lib/oj/state.rb +131 -0
- data/lib/oj/version.rb +1 -1
- data/lib/oj.rb +11 -23
- data/pages/Advanced.md +22 -0
- data/pages/Compatibility.md +25 -0
- data/pages/Custom.md +23 -0
- data/pages/Encoding.md +65 -0
- data/pages/JsonGem.md +79 -0
- data/pages/Modes.md +140 -0
- data/pages/Options.md +250 -0
- data/pages/Rails.md +60 -0
- data/pages/Security.md +20 -0
- data/test/_test_active.rb +76 -0
- data/test/_test_active_mimic.rb +96 -0
- data/test/_test_mimic_rails.rb +126 -0
- data/test/activesupport4/decoding_test.rb +105 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/decoding_test.rb +125 -0
- data/test/activesupport5/encoding_test.rb +483 -0
- data/test/activesupport5/encoding_test_cases.rb +90 -0
- data/test/activesupport5/test_helper.rb +50 -0
- data/test/activesupport5/time_zone_test_helpers.rb +24 -0
- data/test/helper.rb +27 -0
- data/test/isolated/shared.rb +310 -0
- data/test/isolated/test_mimic_after.rb +13 -0
- data/test/isolated/test_mimic_alone.rb +12 -0
- data/test/isolated/test_mimic_as_json.rb +45 -0
- data/test/isolated/test_mimic_before.rb +13 -0
- data/test/isolated/test_mimic_define.rb +28 -0
- data/test/isolated/test_mimic_rails_after.rb +22 -0
- data/test/isolated/test_mimic_rails_before.rb +21 -0
- data/test/isolated/test_mimic_redefine.rb +15 -0
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +143 -0
- data/test/json_gem/json_encoding_test.rb +109 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +383 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +18 -0
- data/test/perf_compat.rb +130 -0
- data/test/perf_fast.rb +9 -9
- data/test/perf_file.rb +64 -0
- data/test/{perf_obj.rb → perf_object.rb} +24 -10
- data/test/perf_scp.rb +151 -0
- data/test/perf_strict.rb +32 -113
- data/test/sample.rb +2 -3
- data/test/test_compat.rb +474 -0
- data/test/test_custom.rb +355 -0
- data/test/test_debian.rb +53 -0
- data/test/test_fast.rb +66 -16
- data/test/test_file.rb +237 -0
- data/test/test_gc.rb +49 -0
- data/test/test_hash.rb +29 -0
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +1010 -0
- data/test/test_saj.rb +16 -16
- data/test/test_scp.rb +417 -0
- data/test/test_strict.rb +410 -0
- data/test/test_various.rb +815 -0
- data/test/test_writer.rb +308 -0
- data/test/tests.rb +9 -902
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- metadata +253 -38
- data/ext/oj/cache.c +0 -148
- data/ext/oj/foo.rb +0 -6
- data/ext/oj/load.c +0 -1049
- data/test/a.rb +0 -38
- data/test/perf1.rb +0 -64
- data/test/perf2.rb +0 -76
- data/test/perf_obj_old.rb +0 -213
- data/test/test_mimic.rb +0 -208
data/ext/oj/fast.c
CHANGED
@@ -38,6 +38,7 @@
|
|
38
38
|
#include <errno.h>
|
39
39
|
|
40
40
|
#include "oj.h"
|
41
|
+
#include "encode.h"
|
41
42
|
|
42
43
|
// maximum to allocate on the stack, arbitrary limit
|
43
44
|
#define SMALL_XML 65536
|
@@ -146,23 +147,6 @@ next_non_white(ParseInfo pi) {
|
|
146
147
|
}
|
147
148
|
}
|
148
149
|
|
149
|
-
inline static void
|
150
|
-
next_white(ParseInfo pi) {
|
151
|
-
for (; 1; pi->s++) {
|
152
|
-
switch(*pi->s) {
|
153
|
-
case ' ':
|
154
|
-
case '\t':
|
155
|
-
case '\f':
|
156
|
-
case '\n':
|
157
|
-
case '\r':
|
158
|
-
case '\0':
|
159
|
-
return;
|
160
|
-
default:
|
161
|
-
break;
|
162
|
-
}
|
163
|
-
}
|
164
|
-
}
|
165
|
-
|
166
150
|
inline static char*
|
167
151
|
ulong_fill(char *s, size_t num) {
|
168
152
|
char buf[32];
|
@@ -186,7 +170,7 @@ ulong_fill(char *s, size_t num) {
|
|
186
170
|
inline static void
|
187
171
|
leaf_init(Leaf leaf, int type) {
|
188
172
|
leaf->next = 0;
|
189
|
-
leaf->
|
173
|
+
leaf->rtype = type;
|
190
174
|
leaf->parent_type = T_NONE;
|
191
175
|
switch (type) {
|
192
176
|
case T_ARRAY:
|
@@ -222,6 +206,8 @@ leaf_new(Doc doc, int type) {
|
|
222
206
|
if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
|
223
207
|
Batch b = ALLOC(struct _Batch);
|
224
208
|
|
209
|
+
// Initializes all leaves with a NO_VAL value_type
|
210
|
+
memset(b, 0, sizeof(struct _Batch));
|
225
211
|
b->next = doc->batches;
|
226
212
|
doc->batches = b;
|
227
213
|
b->next_avail = 0;
|
@@ -248,7 +234,7 @@ leaf_append_element(Leaf parent, Leaf element) {
|
|
248
234
|
static VALUE
|
249
235
|
leaf_value(Doc doc, Leaf leaf) {
|
250
236
|
if (RUBY_VAL != leaf->value_type) {
|
251
|
-
switch (leaf->
|
237
|
+
switch (leaf->rtype) {
|
252
238
|
case T_NIL:
|
253
239
|
leaf->value = Qnil;
|
254
240
|
break;
|
@@ -266,9 +252,7 @@ leaf_value(Doc doc, Leaf leaf) {
|
|
266
252
|
break;
|
267
253
|
case T_STRING:
|
268
254
|
leaf->value = rb_str_new2(leaf->str);
|
269
|
-
|
270
|
-
rb_enc_associate(leaf->value, oj_utf8_encoding);
|
271
|
-
#endif
|
255
|
+
leaf->value = oj_encode(leaf->value);
|
272
256
|
leaf->value_type = RUBY_VAL;
|
273
257
|
break;
|
274
258
|
case T_ARRAY:
|
@@ -278,7 +262,7 @@ leaf_value(Doc doc, Leaf leaf) {
|
|
278
262
|
return leaf_hash_value(doc, leaf);
|
279
263
|
break;
|
280
264
|
default:
|
281
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->
|
265
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
|
282
266
|
break;
|
283
267
|
}
|
284
268
|
}
|
@@ -361,84 +345,16 @@ leaf_fixnum_value(Leaf leaf) {
|
|
361
345
|
if (neg) {
|
362
346
|
n = -n;
|
363
347
|
}
|
364
|
-
leaf->value =
|
348
|
+
leaf->value = rb_ll2inum(n);
|
365
349
|
}
|
366
350
|
leaf->value_type = RUBY_VAL;
|
367
351
|
}
|
368
352
|
|
369
|
-
#ifdef JRUBY_RUBY
|
370
|
-
static void
|
371
|
-
leaf_float_value(Leaf leaf) {
|
372
|
-
char *s = leaf->str;
|
373
|
-
int64_t n = 0;
|
374
|
-
long a = 0;
|
375
|
-
long div = 1;
|
376
|
-
long e = 0;
|
377
|
-
int neg = 0;
|
378
|
-
int eneg = 0;
|
379
|
-
int big = 0;
|
380
|
-
|
381
|
-
if ('-' == *s) {
|
382
|
-
s++;
|
383
|
-
neg = 1;
|
384
|
-
} else if ('+' == *s) {
|
385
|
-
s++;
|
386
|
-
}
|
387
|
-
for (; '0' <= *s && *s <= '9'; s++) {
|
388
|
-
n = n * 10 + (*s - '0');
|
389
|
-
if (NUM_MAX <= n) {
|
390
|
-
big = 1;
|
391
|
-
}
|
392
|
-
}
|
393
|
-
if (big) {
|
394
|
-
char c = *s;
|
395
|
-
|
396
|
-
*s = '\0';
|
397
|
-
leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
|
398
|
-
*s = c;
|
399
|
-
} else {
|
400
|
-
double d;
|
401
|
-
|
402
|
-
if ('.' == *s) {
|
403
|
-
s++;
|
404
|
-
for (; '0' <= *s && *s <= '9'; s++) {
|
405
|
-
a = a * 10 + (*s - '0');
|
406
|
-
div *= 10;
|
407
|
-
}
|
408
|
-
}
|
409
|
-
if ('e' == *s || 'E' == *s) {
|
410
|
-
s++;
|
411
|
-
if ('-' == *s) {
|
412
|
-
s++;
|
413
|
-
eneg = 1;
|
414
|
-
} else if ('+' == *s) {
|
415
|
-
s++;
|
416
|
-
}
|
417
|
-
for (; '0' <= *s && *s <= '9'; s++) {
|
418
|
-
e = e * 10 + (*s - '0');
|
419
|
-
}
|
420
|
-
}
|
421
|
-
d = (double)n + (double)a / (double)div;
|
422
|
-
if (neg) {
|
423
|
-
d = -d;
|
424
|
-
}
|
425
|
-
if (0 != e) {
|
426
|
-
if (eneg) {
|
427
|
-
e = -e;
|
428
|
-
}
|
429
|
-
d *= pow(10.0, e);
|
430
|
-
}
|
431
|
-
leaf->value = rb_float_new(d);
|
432
|
-
}
|
433
|
-
leaf->value_type = RUBY_VAL;
|
434
|
-
}
|
435
|
-
#else
|
436
353
|
static void
|
437
354
|
leaf_float_value(Leaf leaf) {
|
438
355
|
leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
|
439
356
|
leaf->value_type = RUBY_VAL;
|
440
357
|
}
|
441
|
-
#endif
|
442
358
|
|
443
359
|
static VALUE
|
444
360
|
leaf_array_value(Doc doc, Leaf leaf) {
|
@@ -467,9 +383,7 @@ leaf_hash_value(Doc doc, Leaf leaf) {
|
|
467
383
|
|
468
384
|
do {
|
469
385
|
key = rb_str_new2(e->key);
|
470
|
-
|
471
|
-
rb_enc_associate(key, oj_utf8_encoding);
|
472
|
-
#endif
|
386
|
+
key = oj_encode(key);
|
473
387
|
rb_hash_aset(h, key, leaf_value(doc, e));
|
474
388
|
e = e->next;
|
475
389
|
} while (e != first);
|
@@ -695,33 +609,59 @@ read_nil(ParseInfo pi) {
|
|
695
609
|
return leaf;
|
696
610
|
}
|
697
611
|
|
698
|
-
static
|
699
|
-
|
700
|
-
|
612
|
+
static uint32_t
|
613
|
+
read_4hex(ParseInfo pi, const char *h) {
|
614
|
+
uint32_t b = 0;
|
615
|
+
int i;
|
616
|
+
|
617
|
+
for (i = 0; i < 4; i++, h++) {
|
618
|
+
b = b << 4;
|
619
|
+
if ('0' <= *h && *h <= '9') {
|
620
|
+
b += *h - '0';
|
621
|
+
} else if ('A' <= *h && *h <= 'F') {
|
622
|
+
b += *h - 'A' + 10;
|
623
|
+
} else if ('a' <= *h && *h <= 'f') {
|
624
|
+
b += *h - 'a' + 10;
|
625
|
+
} else {
|
626
|
+
raise_error("invalid hex character", pi->str, pi->s);
|
627
|
+
}
|
628
|
+
}
|
629
|
+
return b;
|
630
|
+
}
|
701
631
|
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
} else if (
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
} else if (
|
719
|
-
|
632
|
+
static char*
|
633
|
+
unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
634
|
+
if (0x0000007F >= code) {
|
635
|
+
*t++ = (char)code;
|
636
|
+
} else if (0x000007FF >= code) {
|
637
|
+
*t++ = 0xC0 | (code >> 6);
|
638
|
+
*t++ = 0x80 | (0x3F & code);
|
639
|
+
} else if (0x0000FFFF >= code) {
|
640
|
+
*t++ = 0xE0 | (code >> 12);
|
641
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
642
|
+
*t++ = 0x80 | (0x3F & code);
|
643
|
+
} else if (0x001FFFFF >= code) {
|
644
|
+
*t++ = 0xF0 | (code >> 18);
|
645
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
646
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
647
|
+
*t++ = 0x80 | (0x3F & code);
|
648
|
+
} else if (0x03FFFFFF >= code) {
|
649
|
+
*t++ = 0xF8 | (code >> 24);
|
650
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
651
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
652
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
653
|
+
*t++ = 0x80 | (0x3F & code);
|
654
|
+
} else if (0x7FFFFFFF >= code) {
|
655
|
+
*t++ = 0xFC | (code >> 30);
|
656
|
+
*t++ = 0x80 | ((code >> 24) & 0x3F);
|
657
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
658
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
659
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
660
|
+
*t++ = 0x80 | (0x3F & code);
|
720
661
|
} else {
|
721
|
-
pi->s
|
722
|
-
raise_error("invalid hex character", pi->str, pi->s);
|
662
|
+
raise_error("invalid Unicode character", pi->str, pi->s);
|
723
663
|
}
|
724
|
-
return
|
664
|
+
return t;
|
725
665
|
}
|
726
666
|
|
727
667
|
/* Assume the value starts immediately and goes until the quote character is
|
@@ -751,16 +691,31 @@ read_quoted_value(ParseInfo pi) {
|
|
751
691
|
case '"': *t = '"'; break;
|
752
692
|
case '/': *t = '/'; break;
|
753
693
|
case '\\': *t = '\\'; break;
|
754
|
-
case 'u':
|
694
|
+
case 'u': {
|
695
|
+
uint32_t code;
|
696
|
+
|
755
697
|
h++;
|
756
|
-
|
757
|
-
h +=
|
758
|
-
if (
|
759
|
-
|
698
|
+
code = read_4hex(pi, h);
|
699
|
+
h += 3;
|
700
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
701
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
702
|
+
uint32_t c2;
|
703
|
+
|
704
|
+
h++;
|
705
|
+
if ('\\' != *h || 'u' != *(h + 1)) {
|
706
|
+
pi->s = h;
|
707
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
708
|
+
}
|
709
|
+
h += 2;
|
710
|
+
c2 = read_4hex(pi, h);
|
711
|
+
h += 3;
|
712
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
713
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
760
714
|
}
|
761
|
-
|
762
|
-
|
715
|
+
t = unicode_to_chars(pi, t, code);
|
716
|
+
t--;
|
763
717
|
break;
|
718
|
+
}
|
764
719
|
default:
|
765
720
|
pi->s = h;
|
766
721
|
raise_error("invalid escaped character", pi->str, pi->s);
|
@@ -779,15 +734,10 @@ read_quoted_value(ParseInfo pi) {
|
|
779
734
|
// doc support functions
|
780
735
|
inline static void
|
781
736
|
doc_init(Doc doc) {
|
737
|
+
memset(doc, 0, sizeof(struct _Doc));
|
782
738
|
doc->where = doc->where_path;
|
783
|
-
*doc->where = 0;
|
784
|
-
doc->data = 0;
|
785
739
|
doc->self = Qundef;
|
786
|
-
doc->size = 0;
|
787
|
-
doc->json = 0;
|
788
740
|
doc->batches = &doc->batch0;
|
789
|
-
doc->batch0.next = 0;
|
790
|
-
doc->batch0.next_avail = 0;
|
791
741
|
}
|
792
742
|
|
793
743
|
static void
|
@@ -863,7 +813,11 @@ parse_json(VALUE clas, char *json, int given, int allocated) {
|
|
863
813
|
}
|
864
814
|
#endif
|
865
815
|
// last arg is free func void* func(void*)
|
816
|
+
#if HAS_DATA_OBJECT_WRAP
|
817
|
+
doc->self = rb_data_object_wrap(clas, doc, 0, free_doc_cb);
|
818
|
+
#else
|
866
819
|
doc->self = rb_data_object_alloc(clas, doc, 0, free_doc_cb);
|
820
|
+
#endif
|
867
821
|
rb_gc_register_address(&doc->self);
|
868
822
|
doc->json = json;
|
869
823
|
DATA_PTR(doc->self) = doc;
|
@@ -902,7 +856,10 @@ get_doc_leaf(Doc doc, const char *path) {
|
|
902
856
|
} else {
|
903
857
|
size_t cnt = doc->where - doc->where_path;
|
904
858
|
|
905
|
-
|
859
|
+
if (MAX_STACK <= cnt) {
|
860
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
861
|
+
}
|
862
|
+
memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
|
906
863
|
lp = stack + cnt;
|
907
864
|
}
|
908
865
|
return get_leaf(stack, lp, path);
|
@@ -910,6 +867,35 @@ get_doc_leaf(Doc doc, const char *path) {
|
|
910
867
|
return leaf;
|
911
868
|
}
|
912
869
|
|
870
|
+
static const char*
|
871
|
+
next_slash(const char *s) {
|
872
|
+
for (; '\0' != *s; s++) {
|
873
|
+
if ('\\' == *s) {
|
874
|
+
s++;
|
875
|
+
if ('\0' == *s) {
|
876
|
+
break;
|
877
|
+
}
|
878
|
+
} else if ('/' == *s) {
|
879
|
+
return s;
|
880
|
+
}
|
881
|
+
}
|
882
|
+
return NULL;
|
883
|
+
}
|
884
|
+
|
885
|
+
static bool
|
886
|
+
key_match(const char *pat, const char *key, int plen) {
|
887
|
+
for (; 0 < plen; plen--, pat++, key++) {
|
888
|
+
if ('\\' == *pat) {
|
889
|
+
plen--;
|
890
|
+
pat++;
|
891
|
+
}
|
892
|
+
if (*pat != *key) {
|
893
|
+
return false;
|
894
|
+
}
|
895
|
+
}
|
896
|
+
return '\0' == *key;
|
897
|
+
}
|
898
|
+
|
913
899
|
static Leaf
|
914
900
|
get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
915
901
|
Leaf leaf = *lp;
|
@@ -931,7 +917,7 @@ get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
931
917
|
} else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
|
932
918
|
Leaf first = leaf->elements->next;
|
933
919
|
Leaf e = first;
|
934
|
-
int type = leaf->
|
920
|
+
int type = leaf->rtype;
|
935
921
|
|
936
922
|
leaf = 0;
|
937
923
|
if (T_ARRAY == type) {
|
@@ -955,7 +941,7 @@ get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
955
941
|
} while (e != first);
|
956
942
|
} else if (T_HASH == type) {
|
957
943
|
const char *key = path;
|
958
|
-
const char *slash =
|
944
|
+
const char *slash = next_slash(path);
|
959
945
|
int klen;
|
960
946
|
|
961
947
|
if (0 == slash) {
|
@@ -966,7 +952,7 @@ get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
966
952
|
path += klen + 1;
|
967
953
|
}
|
968
954
|
do {
|
969
|
-
if (
|
955
|
+
if (key_match(key, e->key, klen)) {
|
970
956
|
lp++;
|
971
957
|
*lp = e;
|
972
958
|
leaf = get_leaf(stack, lp, path);
|
@@ -988,11 +974,15 @@ each_leaf(Doc doc, VALUE self) {
|
|
988
974
|
Leaf e = first;
|
989
975
|
|
990
976
|
doc->where++;
|
977
|
+
if (MAX_STACK <= doc->where - doc->where_path) {
|
978
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
979
|
+
}
|
991
980
|
do {
|
992
981
|
*doc->where = e;
|
993
982
|
each_leaf(doc, self);
|
994
983
|
e = e->next;
|
995
984
|
} while (e != first);
|
985
|
+
doc->where--;
|
996
986
|
}
|
997
987
|
} else {
|
998
988
|
rb_yield(self);
|
@@ -1034,7 +1024,7 @@ move_step(Doc doc, const char *path, int loc) {
|
|
1034
1024
|
Leaf first = leaf->elements->next;
|
1035
1025
|
Leaf e = first;
|
1036
1026
|
|
1037
|
-
if (T_ARRAY == leaf->
|
1027
|
+
if (T_ARRAY == leaf->rtype) {
|
1038
1028
|
int cnt = 0;
|
1039
1029
|
|
1040
1030
|
for (; '0' <= *path && *path <= '9'; path++) {
|
@@ -1059,9 +1049,9 @@ move_step(Doc doc, const char *path, int loc) {
|
|
1059
1049
|
cnt--;
|
1060
1050
|
e = e->next;
|
1061
1051
|
} while (e != first);
|
1062
|
-
} else if (T_HASH == leaf->
|
1052
|
+
} else if (T_HASH == leaf->rtype) {
|
1063
1053
|
const char *key = path;
|
1064
|
-
const char *slash =
|
1054
|
+
const char *slash = next_slash(path);
|
1065
1055
|
int klen;
|
1066
1056
|
|
1067
1057
|
if (0 == slash) {
|
@@ -1072,7 +1062,7 @@ move_step(Doc doc, const char *path, int loc) {
|
|
1072
1062
|
path += klen + 1;
|
1073
1063
|
}
|
1074
1064
|
do {
|
1075
|
-
if (
|
1065
|
+
if (key_match(key, e->key, klen)) {
|
1076
1066
|
doc->where++;
|
1077
1067
|
*doc->where = e;
|
1078
1068
|
loc = move_step(doc, path, loc + 1);
|
@@ -1109,14 +1099,14 @@ each_value(Doc doc, Leaf leaf) {
|
|
1109
1099
|
|
1110
1100
|
// doc functions
|
1111
1101
|
|
1112
|
-
/*
|
1102
|
+
/* @overload open(json) { |doc| ... } => Object
|
1113
1103
|
*
|
1114
1104
|
* Parses a JSON document String and then yields to the provided block if one
|
1115
1105
|
* is given with an instance of the Oj::Doc as the single yield parameter. If
|
1116
1106
|
* a block is not given then an Oj::Doc instance is returned and must be
|
1117
1107
|
* closed with a call to the #close() method when no longer needed.
|
1118
1108
|
*
|
1119
|
-
*
|
1109
|
+
* @param [String] json JSON document string
|
1120
1110
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1121
1111
|
* @yieldreturn [Object] returns the result of the yield as the result of the method call
|
1122
1112
|
* @example
|
@@ -1150,14 +1140,14 @@ doc_open(VALUE clas, VALUE str) {
|
|
1150
1140
|
return obj;
|
1151
1141
|
}
|
1152
1142
|
|
1153
|
-
/*
|
1143
|
+
/* @overload open_file(filename) { |doc| ... } => Object
|
1154
1144
|
*
|
1155
1145
|
* Parses a JSON document from a file and then yields to the provided block if
|
1156
1146
|
* one is given with an instance of the Oj::Doc as the single yield
|
1157
1147
|
* parameter. If a block is not given then an Oj::Doc instance is returned and
|
1158
1148
|
* must be closed with a call to the #close() method when no longer needed.
|
1159
1149
|
*
|
1160
|
-
*
|
1150
|
+
* @param [String] filename name of file that contains a JSON document
|
1161
1151
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1162
1152
|
* @yieldreturn [Object] returns the result of the yield as the result of the method call
|
1163
1153
|
* @example
|
@@ -1206,11 +1196,34 @@ doc_open_file(VALUE clas, VALUE filename) {
|
|
1206
1196
|
return obj;
|
1207
1197
|
}
|
1208
1198
|
|
1199
|
+
static int
|
1200
|
+
esc_strlen(const char *s) {
|
1201
|
+
int cnt = 0;
|
1202
|
+
|
1203
|
+
for (; '\0' != *s; s++, cnt++) {
|
1204
|
+
if ('/' == *s) {
|
1205
|
+
cnt++;
|
1206
|
+
}
|
1207
|
+
}
|
1208
|
+
return cnt;
|
1209
|
+
}
|
1210
|
+
|
1211
|
+
static char*
|
1212
|
+
append_key(char *p, const char *key) {
|
1213
|
+
for (; '\0' != *key; p++, key++) {
|
1214
|
+
if ('/' == *key) {
|
1215
|
+
*p++ = '\\';
|
1216
|
+
}
|
1217
|
+
*p = *key;
|
1218
|
+
}
|
1219
|
+
return p;
|
1220
|
+
}
|
1221
|
+
|
1209
1222
|
/* Document-method: parse
|
1210
1223
|
* @see Oj::Doc.open
|
1211
1224
|
*/
|
1212
1225
|
|
1213
|
-
/*
|
1226
|
+
/* @overload where?() => String
|
1214
1227
|
*
|
1215
1228
|
* Returns a String that describes the absolute path to the current location
|
1216
1229
|
* in the JSON document.
|
@@ -1231,7 +1244,7 @@ doc_where(VALUE self) {
|
|
1231
1244
|
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
1232
1245
|
leaf = *lp;
|
1233
1246
|
if (T_HASH == leaf->parent_type) {
|
1234
|
-
size +=
|
1247
|
+
size += esc_strlen((*lp)->key) + 1;
|
1235
1248
|
} else if (T_ARRAY == leaf->parent_type) {
|
1236
1249
|
size += ((*lp)->index < 100) ? 3 : 11;
|
1237
1250
|
}
|
@@ -1241,7 +1254,7 @@ doc_where(VALUE self) {
|
|
1241
1254
|
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
1242
1255
|
leaf = *lp;
|
1243
1256
|
if (T_HASH == leaf->parent_type) {
|
1244
|
-
p =
|
1257
|
+
p = append_key(p, (*lp)->key);
|
1245
1258
|
} else if (T_ARRAY == leaf->parent_type) {
|
1246
1259
|
p = ulong_fill(p, (*lp)->index);
|
1247
1260
|
}
|
@@ -1252,7 +1265,7 @@ doc_where(VALUE self) {
|
|
1252
1265
|
}
|
1253
1266
|
}
|
1254
1267
|
|
1255
|
-
/*
|
1268
|
+
/* @overload local_key() => String, Fixnum, nil
|
1256
1269
|
*
|
1257
1270
|
* Returns the final key to the current location.
|
1258
1271
|
* @example
|
@@ -1268,16 +1281,14 @@ doc_local_key(VALUE self) {
|
|
1268
1281
|
|
1269
1282
|
if (T_HASH == leaf->parent_type) {
|
1270
1283
|
key = rb_str_new2(leaf->key);
|
1271
|
-
|
1272
|
-
rb_enc_associate(key, oj_utf8_encoding);
|
1273
|
-
#endif
|
1284
|
+
key = oj_encode(key);
|
1274
1285
|
} else if (T_ARRAY == leaf->parent_type) {
|
1275
1286
|
key = LONG2NUM(leaf->index);
|
1276
1287
|
}
|
1277
1288
|
return key;
|
1278
1289
|
}
|
1279
1290
|
|
1280
|
-
/*
|
1291
|
+
/* @overload home() => nil
|
1281
1292
|
*
|
1282
1293
|
* Moves the document marker or location to the hoot or home position. The
|
1283
1294
|
* same operation can be performed with a Oj::Doc.move('/').
|
@@ -1294,13 +1305,13 @@ doc_home(VALUE self) {
|
|
1294
1305
|
return oj_slash_string;
|
1295
1306
|
}
|
1296
1307
|
|
1297
|
-
/*
|
1308
|
+
/* @overload type(path=nil) => Class
|
1298
1309
|
*
|
1299
1310
|
* Returns the Class of the data value at the location identified by the path
|
1300
1311
|
* or the current location if the path is nil or not provided. This method
|
1301
1312
|
* does not create the Ruby Object at the location specified so the overhead
|
1302
1313
|
* is low.
|
1303
|
-
*
|
1314
|
+
* @param [String] path path to the location to get the type of if provided
|
1304
1315
|
* @example
|
1305
1316
|
* Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
|
1306
1317
|
* Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
|
@@ -1317,12 +1328,16 @@ doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1317
1328
|
path = StringValuePtr(*argv);
|
1318
1329
|
}
|
1319
1330
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1320
|
-
switch (leaf->
|
1331
|
+
switch (leaf->rtype) {
|
1321
1332
|
case T_NIL: type = rb_cNilClass; break;
|
1322
1333
|
case T_TRUE: type = rb_cTrueClass; break;
|
1323
1334
|
case T_FALSE: type = rb_cFalseClass; break;
|
1324
1335
|
case T_STRING: type = rb_cString; break;
|
1336
|
+
#ifdef RUBY_INTEGER_UNIFICATION
|
1337
|
+
case T_FIXNUM: type = rb_cInteger; break;
|
1338
|
+
#else
|
1325
1339
|
case T_FIXNUM: type = rb_cFixnum; break;
|
1340
|
+
#endif
|
1326
1341
|
case T_FLOAT: type = rb_cFloat; break;
|
1327
1342
|
case T_ARRAY: type = rb_cArray; break;
|
1328
1343
|
case T_HASH: type = rb_cHash; break;
|
@@ -1332,14 +1347,14 @@ doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1332
1347
|
return type;
|
1333
1348
|
}
|
1334
1349
|
|
1335
|
-
/*
|
1350
|
+
/* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array, Hash
|
1336
1351
|
*
|
1337
1352
|
* Returns the value at the location identified by the path or the current
|
1338
1353
|
* location if the path is nil or not provided. This method will create and
|
1339
1354
|
* return an Array or Hash if that is the type of Object at the location
|
1340
1355
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
1341
1356
|
* document.
|
1342
|
-
*
|
1357
|
+
* @param [String] path path to the location to get the type of if provided
|
1343
1358
|
* @example
|
1344
1359
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
1345
1360
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
|
@@ -1365,12 +1380,12 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1365
1380
|
return val;
|
1366
1381
|
}
|
1367
1382
|
|
1368
|
-
/*
|
1383
|
+
/* @overload each_leaf(path=nil) => nil
|
1369
1384
|
*
|
1370
1385
|
* Yields to the provided block for each leaf node with the identified
|
1371
1386
|
* location of the JSON document as the root. The parameter passed to the
|
1372
1387
|
* block on yield is the Doc instance after moving to the child location.
|
1373
|
-
*
|
1388
|
+
* @param [String] path if provided it identified the top of the branch to process the leaves of
|
1374
1389
|
* @yieldparam [Doc] Doc at the child location
|
1375
1390
|
* @example
|
1376
1391
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1390,7 +1405,7 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1390
1405
|
|
1391
1406
|
wlen = doc->where - doc->where_path;
|
1392
1407
|
if (0 < wlen) {
|
1393
|
-
memcpy(save_path, doc->where_path, sizeof(Leaf) * wlen);
|
1408
|
+
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
1394
1409
|
}
|
1395
1410
|
if (1 <= argc) {
|
1396
1411
|
Check_Type(*argv, T_STRING);
|
@@ -1401,24 +1416,24 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1401
1416
|
}
|
1402
1417
|
if (0 != move_step(doc, path, 1)) {
|
1403
1418
|
if (0 < wlen) {
|
1404
|
-
memcpy(doc->where_path, save_path, sizeof(Leaf) * wlen);
|
1419
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1405
1420
|
}
|
1406
1421
|
return Qnil;
|
1407
1422
|
}
|
1408
1423
|
}
|
1409
1424
|
each_leaf(doc, self);
|
1410
1425
|
if (0 < wlen) {
|
1411
|
-
memcpy(doc->where_path, save_path, sizeof(Leaf) * wlen);
|
1426
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1412
1427
|
}
|
1413
1428
|
}
|
1414
1429
|
return Qnil;
|
1415
1430
|
}
|
1416
1431
|
|
1417
|
-
/*
|
1432
|
+
/* @overload move(path) => nil
|
1418
1433
|
*
|
1419
1434
|
* Moves the document marker to the path specified. The path can an absolute
|
1420
1435
|
* path or a relative path.
|
1421
|
-
*
|
1436
|
+
* @param [String] path path to the location to move to
|
1422
1437
|
* @example
|
1423
1438
|
* Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=> "/one/2"
|
1424
1439
|
*/
|
@@ -1440,13 +1455,13 @@ doc_move(VALUE self, VALUE str) {
|
|
1440
1455
|
return Qnil;
|
1441
1456
|
}
|
1442
1457
|
|
1443
|
-
/*
|
1458
|
+
/* @overload each_child(path=nil) { |doc| ... } => nil
|
1444
1459
|
*
|
1445
1460
|
* Yields to the provided block for each immediate child node with the
|
1446
1461
|
* identified location of the JSON document as the root. The parameter passed
|
1447
1462
|
* to the block on yield is the Doc instance after moving to the child
|
1448
1463
|
* location.
|
1449
|
-
*
|
1464
|
+
* @param [String] path if provided it identified the top of the branch to process the chilren of
|
1450
1465
|
* @yieldparam [Doc] Doc at the child location
|
1451
1466
|
* @example
|
1452
1467
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1466,7 +1481,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1466
1481
|
|
1467
1482
|
wlen = doc->where - doc->where_path;
|
1468
1483
|
if (0 < wlen) {
|
1469
|
-
memcpy(save_path, doc->where_path, sizeof(Leaf) * wlen);
|
1484
|
+
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
1470
1485
|
}
|
1471
1486
|
if (1 <= argc) {
|
1472
1487
|
Check_Type(*argv, T_STRING);
|
@@ -1477,7 +1492,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1477
1492
|
}
|
1478
1493
|
if (0 != move_step(doc, path, 1)) {
|
1479
1494
|
if (0 < wlen) {
|
1480
|
-
memcpy(doc->where_path, save_path, sizeof(Leaf) * wlen);
|
1495
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1481
1496
|
}
|
1482
1497
|
return Qnil;
|
1483
1498
|
}
|
@@ -1494,19 +1509,19 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1494
1509
|
} while (e != first);
|
1495
1510
|
}
|
1496
1511
|
if (0 < wlen) {
|
1497
|
-
memcpy(doc->where_path, save_path, sizeof(Leaf) * wlen);
|
1512
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1498
1513
|
}
|
1499
1514
|
}
|
1500
1515
|
return Qnil;
|
1501
1516
|
}
|
1502
1517
|
|
1503
|
-
/*
|
1518
|
+
/* @overload each_value(path=nil) { |val| ... } => nil
|
1504
1519
|
*
|
1505
1520
|
* Yields to the provided block for each leaf value in the identified location
|
1506
1521
|
* of the JSON document. The parameter passed to the block on yield is the
|
1507
1522
|
* value of the leaf. Only those leaves below the element specified by the
|
1508
1523
|
* path parameter are processed.
|
1509
|
-
*
|
1524
|
+
* @param [String] path if provided it identified the top of the branch to process the leaf values of
|
1510
1525
|
* @yieldparam [Object] val each leaf value
|
1511
1526
|
* @example
|
1512
1527
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1541,12 +1556,12 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1541
1556
|
return Qnil;
|
1542
1557
|
}
|
1543
1558
|
|
1544
|
-
/*
|
1559
|
+
/* @overload dump(path, filename)
|
1545
1560
|
*
|
1546
1561
|
* Dumps the document or nodes to a new JSON document. It uses the default
|
1547
1562
|
* options for generating the JSON.
|
1548
|
-
*
|
1549
|
-
*
|
1563
|
+
* @param path [String] if provided it identified the top of the branch to dump to JSON
|
1564
|
+
* @param filename [String] if provided it is the filename to write the output to
|
1550
1565
|
* @example
|
1551
1566
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
1552
1567
|
* doc.dump('/2')
|
@@ -1571,13 +1586,21 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1571
1586
|
}
|
1572
1587
|
}
|
1573
1588
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1574
|
-
char *json;
|
1575
1589
|
VALUE rjson;
|
1576
1590
|
|
1577
1591
|
if (0 == filename) {
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1592
|
+
char buf[4096];
|
1593
|
+
struct _Out out;
|
1594
|
+
|
1595
|
+
out.buf = buf;
|
1596
|
+
out.end = buf + sizeof(buf) - 10;
|
1597
|
+
out.allocated = 0;
|
1598
|
+
out.omit_nil = oj_default_options.dump_opts.omit_nil;
|
1599
|
+
oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
|
1600
|
+
rjson = rb_str_new2(out.buf);
|
1601
|
+
if (out.allocated) {
|
1602
|
+
xfree(out.buf);
|
1603
|
+
}
|
1581
1604
|
} else {
|
1582
1605
|
oj_write_leaf_to_file(leaf, filename, &oj_default_options);
|
1583
1606
|
rjson = Qnil;
|
@@ -1587,7 +1610,7 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1587
1610
|
return Qnil;
|
1588
1611
|
}
|
1589
1612
|
|
1590
|
-
/*
|
1613
|
+
/* @overload size() => Fixnum
|
1591
1614
|
*
|
1592
1615
|
* Returns the number of nodes in the JSON document where a node is any one of
|
1593
1616
|
* the basic JSON components.
|
@@ -1600,7 +1623,7 @@ doc_size(VALUE self) {
|
|
1600
1623
|
return ULONG2NUM(((Doc)DATA_PTR(self))->size);
|
1601
1624
|
}
|
1602
1625
|
|
1603
|
-
/*
|
1626
|
+
/* @overload close() => nil
|
1604
1627
|
*
|
1605
1628
|
* Closes an open document. No further calls to the document will be valid
|
1606
1629
|
* after closing.
|
@@ -1618,6 +1641,7 @@ doc_close(VALUE self) {
|
|
1618
1641
|
if (0 != doc) {
|
1619
1642
|
xfree(doc->json);
|
1620
1643
|
doc_free(doc);
|
1644
|
+
xfree(doc);
|
1621
1645
|
}
|
1622
1646
|
return Qnil;
|
1623
1647
|
}
|
@@ -1626,6 +1650,12 @@ doc_close(VALUE self) {
|
|
1626
1650
|
Oj = rb_define_module("Oj");
|
1627
1651
|
#endif
|
1628
1652
|
|
1653
|
+
static VALUE
|
1654
|
+
doc_not_implemented(VALUE self) {
|
1655
|
+
rb_raise(rb_eNotImpError, "Not implemented.");
|
1656
|
+
return Qnil;
|
1657
|
+
}
|
1658
|
+
|
1629
1659
|
/* Document-class: Oj::Doc
|
1630
1660
|
*
|
1631
1661
|
* The Doc class is used to parse and navigate a JSON document. The model it
|
@@ -1690,4 +1720,7 @@ oj_init_doc() {
|
|
1690
1720
|
rb_define_method(oj_doc_class, "dump", doc_dump, -1);
|
1691
1721
|
rb_define_method(oj_doc_class, "size", doc_size, 0);
|
1692
1722
|
rb_define_method(oj_doc_class, "close", doc_close, 0);
|
1723
|
+
|
1724
|
+
rb_define_method(oj_doc_class, "clone", doc_not_implemented, 0);
|
1725
|
+
rb_define_method(oj_doc_class, "dup", doc_not_implemented, 0);
|
1693
1726
|
}
|