oj 1.0.6 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- data/README.md +8 -2
- data/ext/oj/dump.c +166 -26
- data/ext/oj/fast.c +153 -37
- data/ext/oj/load.c +49 -1
- data/ext/oj/oj.c +361 -49
- data/ext/oj/oj.h +20 -0
- data/lib/oj/version.rb +1 -1
- data/test/perf.rb +1 -0
- data/test/perf_fast.rb +19 -0
- data/test/perf_strict.rb +28 -14
- data/test/test_fast.rb +54 -0
- data/test/test_mimic.rb +164 -0
- data/test/tests.rb +54 -1
- metadata +3 -2
data/README.md
CHANGED
@@ -24,9 +24,15 @@ A fast JSON parser and Object marshaller as a Ruby gem.
|
|
24
24
|
|
25
25
|
## <a name="release">Release Notes</a>
|
26
26
|
|
27
|
-
### Release 1.0
|
27
|
+
### Release 1.1.0
|
28
28
|
|
29
|
-
-
|
29
|
+
- Errors are not longer raised when comments are encountered in JSON documents.
|
30
|
+
|
31
|
+
- Oj can now mimic JSON. With some expections calling JSON.mimic_JSON will allow all JSON calls to use OJ instead of JSON. This gives a speedup of more than 2x on parsing and 5x for generating over the JSON::Ext module.
|
32
|
+
|
33
|
+
- Oj::Doc now allows a document to be left open and then closed with the Oj::Doc.close() class.
|
34
|
+
|
35
|
+
- Changed the default encoding to UTF-8 instead of the Ruby default String encoding.
|
30
36
|
|
31
37
|
## <a name="description">Description</a>
|
32
38
|
|
data/ext/oj/dump.c
CHANGED
@@ -33,14 +33,9 @@
|
|
33
33
|
#include <time.h>
|
34
34
|
#include <stdio.h>
|
35
35
|
#include <string.h>
|
36
|
+
#include <math.h>
|
36
37
|
|
37
38
|
#include "oj.h"
|
38
|
-
#if IVAR_HELPERS
|
39
|
-
#include "ruby/st.h"
|
40
|
-
#else
|
41
|
-
#include "st.h"
|
42
|
-
#endif
|
43
|
-
|
44
39
|
#include "cache8.h"
|
45
40
|
|
46
41
|
typedef unsigned long ulong;
|
@@ -341,8 +336,35 @@ static void
|
|
341
336
|
dump_float(VALUE obj, Out out) {
|
342
337
|
char buf[64];
|
343
338
|
char *b;
|
344
|
-
|
339
|
+
double d = RFLOAT_VALUE(obj);
|
340
|
+
int cnt;
|
345
341
|
|
342
|
+
switch (fpclassify(d)) {
|
343
|
+
case FP_NAN:
|
344
|
+
printf("*** nan\n");
|
345
|
+
cnt = sprintf(buf, "%0.16g", d); // used sprintf due to bug in snprintf
|
346
|
+
break;
|
347
|
+
case FP_INFINITE:
|
348
|
+
b = buf;
|
349
|
+
cnt = 8;
|
350
|
+
if (d < 0.0) {
|
351
|
+
*b++ = '-';
|
352
|
+
cnt++;
|
353
|
+
}
|
354
|
+
strcpy(b, "Infinity");
|
355
|
+
break;
|
356
|
+
case FP_ZERO:
|
357
|
+
b = buf;
|
358
|
+
*b++ = '0';
|
359
|
+
*b++ = '.';
|
360
|
+
*b++ = '0';
|
361
|
+
*b++ = '\0';
|
362
|
+
cnt = 3;
|
363
|
+
break;
|
364
|
+
default:
|
365
|
+
cnt = sprintf(buf, "%0.16g", d); // used sprintf due to bug in snprintf
|
366
|
+
break;
|
367
|
+
}
|
346
368
|
if (out->end - out->cur <= (long)cnt) {
|
347
369
|
grow(out, cnt);
|
348
370
|
}
|
@@ -520,12 +542,30 @@ dump_array(VALUE a, int depth, Out out) {
|
|
520
542
|
if (0 < id) {
|
521
543
|
*out->cur++ = ',';
|
522
544
|
}
|
523
|
-
|
545
|
+
if (0 == out->opts->dump_opts) {
|
546
|
+
size = d2 * out->indent + 2;
|
547
|
+
} else {
|
548
|
+
size = d2 * out->opts->dump_opts->indent_size + out->opts->dump_opts->array_size + 1;
|
549
|
+
}
|
524
550
|
for (; 0 < cnt; cnt--, np++) {
|
525
551
|
if (out->end - out->cur <= (long)size) {
|
526
552
|
grow(out, size);
|
527
553
|
}
|
528
|
-
|
554
|
+
if (0 == out->opts->dump_opts) {
|
555
|
+
fill_indent(out, d2);
|
556
|
+
} else {
|
557
|
+
if (0 < out->opts->dump_opts->array_size) {
|
558
|
+
strcpy(out->cur, out->opts->dump_opts->array_nl);
|
559
|
+
out->cur += out->opts->dump_opts->array_size;
|
560
|
+
}
|
561
|
+
if (0 < out->opts->dump_opts->indent_size) {
|
562
|
+
int i;
|
563
|
+
for (i = d2; 0 < i; i--) {
|
564
|
+
strcpy(out->cur, out->opts->dump_opts->indent);
|
565
|
+
out->cur += out->opts->dump_opts->indent_size;
|
566
|
+
}
|
567
|
+
}
|
568
|
+
}
|
529
569
|
dump_val(*np, d2, out);
|
530
570
|
if (1 < cnt) {
|
531
571
|
*out->cur++ = ',';
|
@@ -535,7 +575,22 @@ dump_array(VALUE a, int depth, Out out) {
|
|
535
575
|
if (out->end - out->cur <= (long)size) {
|
536
576
|
grow(out, size);
|
537
577
|
}
|
538
|
-
|
578
|
+
if (0 == out->opts->dump_opts) {
|
579
|
+
fill_indent(out, depth);
|
580
|
+
} else {
|
581
|
+
//printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
|
582
|
+
if (0 < out->opts->dump_opts->array_size) {
|
583
|
+
strcpy(out->cur, out->opts->dump_opts->array_nl);
|
584
|
+
out->cur += out->opts->dump_opts->array_size;
|
585
|
+
}
|
586
|
+
if (0 < out->opts->dump_opts->indent_size) {
|
587
|
+
int i;
|
588
|
+
for (i = depth; 0 < i; i--) {
|
589
|
+
strcpy(out->cur, out->opts->dump_opts->indent);
|
590
|
+
out->cur += out->opts->dump_opts->indent_size;
|
591
|
+
}
|
592
|
+
}
|
593
|
+
}
|
539
594
|
*out->cur++ = ']';
|
540
595
|
}
|
541
596
|
*out->cur = '\0';
|
@@ -544,19 +599,51 @@ dump_array(VALUE a, int depth, Out out) {
|
|
544
599
|
static int
|
545
600
|
hash_cb_strict(VALUE key, VALUE value, Out out) {
|
546
601
|
int depth = out->depth;
|
547
|
-
long size
|
602
|
+
long size;
|
548
603
|
|
549
|
-
if (
|
550
|
-
|
604
|
+
if (rb_type(key) != T_STRING) {
|
605
|
+
rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings.");
|
551
606
|
}
|
552
|
-
|
553
|
-
|
607
|
+
if (0 == out->opts->dump_opts) {
|
608
|
+
size = depth * out->indent + 1;
|
609
|
+
if (out->end - out->cur <= size) {
|
610
|
+
grow(out, size);
|
611
|
+
}
|
612
|
+
fill_indent(out, depth);
|
554
613
|
dump_str_comp(key, out);
|
555
614
|
*out->cur++ = ':';
|
556
|
-
dump_val(value, depth, out);
|
557
615
|
} else {
|
558
|
-
|
616
|
+
size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
|
617
|
+
if (out->end - out->cur <= size) {
|
618
|
+
grow(out, size);
|
619
|
+
}
|
620
|
+
if (0 < out->opts->dump_opts->hash_size) {
|
621
|
+
strcpy(out->cur, out->opts->dump_opts->hash_nl);
|
622
|
+
out->cur += out->opts->dump_opts->hash_size;
|
623
|
+
}
|
624
|
+
if (0 < out->opts->dump_opts->indent_size) {
|
625
|
+
int i;
|
626
|
+
for (i = depth; 0 < i; i--) {
|
627
|
+
strcpy(out->cur, out->opts->dump_opts->indent);
|
628
|
+
out->cur += out->opts->dump_opts->indent_size;
|
629
|
+
}
|
630
|
+
}
|
631
|
+
dump_str_comp(key, out);
|
632
|
+
size = out->opts->dump_opts->before_size + out->opts->dump_opts->after_size + 2;
|
633
|
+
if (out->end - out->cur <= size) {
|
634
|
+
grow(out, size);
|
635
|
+
}
|
636
|
+
if (0 < out->opts->dump_opts->before_size) {
|
637
|
+
strcpy(out->cur, out->opts->dump_opts->before_sep);
|
638
|
+
out->cur += out->opts->dump_opts->before_size;
|
639
|
+
}
|
640
|
+
*out->cur++ = ':';
|
641
|
+
if (0 < out->opts->dump_opts->after_size) {
|
642
|
+
strcpy(out->cur, out->opts->dump_opts->after_sep);
|
643
|
+
out->cur += out->opts->dump_opts->after_size;
|
644
|
+
}
|
559
645
|
}
|
646
|
+
dump_val(value, depth, out);
|
560
647
|
out->depth = depth;
|
561
648
|
*out->cur++ = ',';
|
562
649
|
|
@@ -566,12 +653,31 @@ hash_cb_strict(VALUE key, VALUE value, Out out) {
|
|
566
653
|
static int
|
567
654
|
hash_cb_compat(VALUE key, VALUE value, Out out) {
|
568
655
|
int depth = out->depth;
|
569
|
-
long size
|
656
|
+
long size;
|
570
657
|
|
571
|
-
if (
|
572
|
-
|
658
|
+
if (0 == out->opts->dump_opts) {
|
659
|
+
size = depth * out->indent + 1;
|
660
|
+
if (out->end - out->cur <= size) {
|
661
|
+
grow(out, size);
|
662
|
+
}
|
663
|
+
fill_indent(out, depth);
|
664
|
+
} else {
|
665
|
+
size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
|
666
|
+
if (out->end - out->cur <= size) {
|
667
|
+
grow(out, size);
|
668
|
+
}
|
669
|
+
if (0 < out->opts->dump_opts->hash_size) {
|
670
|
+
strcpy(out->cur, out->opts->dump_opts->hash_nl);
|
671
|
+
out->cur += out->opts->dump_opts->hash_size;
|
672
|
+
}
|
673
|
+
if (0 < out->opts->dump_opts->indent_size) {
|
674
|
+
int i;
|
675
|
+
for (i = depth; 0 < i; i--) {
|
676
|
+
strcpy(out->cur, out->opts->dump_opts->indent);
|
677
|
+
out->cur += out->opts->dump_opts->indent_size;
|
678
|
+
}
|
679
|
+
}
|
573
680
|
}
|
574
|
-
fill_indent(out, depth);
|
575
681
|
switch (rb_type(key)) {
|
576
682
|
case T_STRING:
|
577
683
|
dump_str_comp(key, out);
|
@@ -583,7 +689,23 @@ hash_cb_compat(VALUE key, VALUE value, Out out) {
|
|
583
689
|
rb_raise(rb_eTypeError, "In :strict mode all Hash keys must be Strings.");
|
584
690
|
break;
|
585
691
|
}
|
586
|
-
|
692
|
+
if (0 == out->opts->dump_opts) {
|
693
|
+
*out->cur++ = ':';
|
694
|
+
} else {
|
695
|
+
size = out->opts->dump_opts->before_size + out->opts->dump_opts->after_size + 2;
|
696
|
+
if (out->end - out->cur <= size) {
|
697
|
+
grow(out, size);
|
698
|
+
}
|
699
|
+
if (0 < out->opts->dump_opts->before_size) {
|
700
|
+
strcpy(out->cur, out->opts->dump_opts->before_sep);
|
701
|
+
out->cur += out->opts->dump_opts->before_size;
|
702
|
+
}
|
703
|
+
*out->cur++ = ':';
|
704
|
+
if (0 < out->opts->dump_opts->after_size) {
|
705
|
+
strcpy(out->cur, out->opts->dump_opts->after_sep);
|
706
|
+
out->cur += out->opts->dump_opts->after_size;
|
707
|
+
}
|
708
|
+
}
|
587
709
|
dump_val(value, depth, out);
|
588
710
|
out->depth = depth;
|
589
711
|
*out->cur++ = ',';
|
@@ -696,10 +818,28 @@ dump_hash(VALUE obj, int depth, int mode, Out out) {
|
|
696
818
|
if (',' == *(out->cur - 1)) {
|
697
819
|
out->cur--; // backup to overwrite last comma
|
698
820
|
}
|
699
|
-
if (
|
700
|
-
|
821
|
+
if (0 == out->opts->dump_opts) {
|
822
|
+
if (out->end - out->cur <= (long)size) {
|
823
|
+
grow(out, size);
|
824
|
+
}
|
825
|
+
fill_indent(out, depth);
|
826
|
+
} else {
|
827
|
+
size = depth * out->opts->dump_opts->indent_size + out->opts->dump_opts->hash_size + 1;
|
828
|
+
if (out->end - out->cur <= (long)size) {
|
829
|
+
grow(out, size);
|
830
|
+
}
|
831
|
+
if (0 < out->opts->dump_opts->hash_size) {
|
832
|
+
strcpy(out->cur, out->opts->dump_opts->hash_nl);
|
833
|
+
out->cur += out->opts->dump_opts->hash_size;
|
834
|
+
}
|
835
|
+
if (0 < out->opts->dump_opts->indent_size) {
|
836
|
+
int i;
|
837
|
+
for (i = depth; 0 < i; i--) {
|
838
|
+
strcpy(out->cur, out->opts->dump_opts->indent);
|
839
|
+
out->cur += out->opts->dump_opts->indent_size;
|
840
|
+
}
|
841
|
+
}
|
701
842
|
}
|
702
|
-
fill_indent(out, depth);
|
703
843
|
*out->cur++ = '}';
|
704
844
|
}
|
705
845
|
*out->cur = '\0';
|
@@ -1034,7 +1174,7 @@ dump_val(VALUE obj, int depth, Out out) {
|
|
1034
1174
|
default: dump_sym_obj(obj, out); break;
|
1035
1175
|
}
|
1036
1176
|
break;
|
1037
|
-
case T_ARRAY: dump_array(obj, depth, out);
|
1177
|
+
case T_ARRAY: dump_array(obj, depth, out); break;
|
1038
1178
|
case T_HASH: dump_hash(obj, depth, out->opts->mode, out); break;
|
1039
1179
|
case T_CLASS:
|
1040
1180
|
switch (out->opts->mode) {
|
data/ext/oj/fast.c
CHANGED
@@ -36,6 +36,8 @@
|
|
36
36
|
|
37
37
|
#include "oj.h"
|
38
38
|
|
39
|
+
// maximum to allocate on the stack, arbitrary limit
|
40
|
+
#define SMALL_XML 65536
|
39
41
|
#define MAX_STACK 100
|
40
42
|
//#define BATCH_SIZE (4096 / sizeof(struct _Leaf) - 1)
|
41
43
|
#define BATCH_SIZE 100
|
@@ -55,6 +57,7 @@ typedef struct _Doc {
|
|
55
57
|
#else
|
56
58
|
void *encoding;
|
57
59
|
#endif
|
60
|
+
char *json;
|
58
61
|
unsigned long size; // number of leaves/branches in the doc
|
59
62
|
VALUE self;
|
60
63
|
Batch batches;
|
@@ -86,9 +89,10 @@ static Leaf read_false(ParseInfo pi);
|
|
86
89
|
static Leaf read_nil(ParseInfo pi);
|
87
90
|
static void next_non_white(ParseInfo pi);
|
88
91
|
static char* read_quoted_value(ParseInfo pi);
|
92
|
+
static void skip_comment(ParseInfo pi);
|
89
93
|
|
90
94
|
static VALUE protect_open_proc(VALUE x);
|
91
|
-
static VALUE parse_json(VALUE clas, char *json);
|
95
|
+
static VALUE parse_json(VALUE clas, char *json, int given, int allocated);
|
92
96
|
static void each_leaf(Doc doc, VALUE self);
|
93
97
|
static int move_step(Doc doc, const char *path, int loc);
|
94
98
|
static Leaf get_doc_leaf(Doc doc, const char *path);
|
@@ -123,6 +127,9 @@ next_non_white(ParseInfo pi) {
|
|
123
127
|
case '\n':
|
124
128
|
case '\r':
|
125
129
|
break;
|
130
|
+
case '/':
|
131
|
+
skip_comment(pi);
|
132
|
+
break;
|
126
133
|
default:
|
127
134
|
return;
|
128
135
|
}
|
@@ -270,6 +277,46 @@ leaf_value(Doc doc, Leaf leaf) {
|
|
270
277
|
return leaf->value;
|
271
278
|
}
|
272
279
|
|
280
|
+
inline static Doc
|
281
|
+
self_doc(VALUE self) {
|
282
|
+
Doc doc = DATA_PTR(self);
|
283
|
+
|
284
|
+
if (0 == doc) {
|
285
|
+
rb_raise(rb_eIOError, "Document already closed or not open.");
|
286
|
+
}
|
287
|
+
return doc;
|
288
|
+
}
|
289
|
+
|
290
|
+
static void
|
291
|
+
skip_comment(ParseInfo pi) {
|
292
|
+
pi->s++; // skip first /
|
293
|
+
if ('*' == *pi->s) {
|
294
|
+
pi->s++;
|
295
|
+
for (; '\0' != *pi->s; pi->s++) {
|
296
|
+
if ('*' == *pi->s && '/' == *(pi->s + 1)) {
|
297
|
+
pi->s++;
|
298
|
+
return;
|
299
|
+
} else if ('\0' == *pi->s) {
|
300
|
+
raise_error("comment not terminated", pi->str, pi->s);
|
301
|
+
}
|
302
|
+
}
|
303
|
+
} else if ('/' == *pi->s) {
|
304
|
+
for (; 1; pi->s++) {
|
305
|
+
switch (*pi->s) {
|
306
|
+
case '\n':
|
307
|
+
case '\r':
|
308
|
+
case '\f':
|
309
|
+
case '\0':
|
310
|
+
return;
|
311
|
+
default:
|
312
|
+
break;
|
313
|
+
}
|
314
|
+
}
|
315
|
+
} else {
|
316
|
+
raise_error("invalid comment", pi->str, pi->s);
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
273
320
|
#ifdef RUBINIUS
|
274
321
|
#define NUM_MAX 0x07FFFFFF
|
275
322
|
#else
|
@@ -733,6 +780,7 @@ doc_init(Doc doc) {
|
|
733
780
|
doc->encoding = 0;
|
734
781
|
#endif
|
735
782
|
doc->size = 0;
|
783
|
+
doc->json = 0;
|
736
784
|
doc->batches = &doc->batch0;
|
737
785
|
doc->batch0.next = 0;
|
738
786
|
doc->batch0.next_avail = 0;
|
@@ -760,30 +808,41 @@ protect_open_proc(VALUE x) {
|
|
760
808
|
pi->doc->data = read_next(pi); // parse
|
761
809
|
*pi->doc->where = pi->doc->data;
|
762
810
|
pi->doc->where = pi->doc->where_path;
|
763
|
-
|
764
|
-
|
811
|
+
if (rb_block_given_p()) {
|
812
|
+
return rb_yield(pi->doc->self); // caller processing
|
813
|
+
}
|
814
|
+
return Qnil;
|
765
815
|
}
|
766
816
|
|
767
817
|
static VALUE
|
768
|
-
parse_json(VALUE clas, char *json) {
|
818
|
+
parse_json(VALUE clas, char *json, int given, int allocated) {
|
769
819
|
struct _ParseInfo pi;
|
770
820
|
VALUE result = Qnil;
|
771
|
-
|
821
|
+
Doc doc;
|
772
822
|
int ex = 0;
|
773
823
|
|
774
|
-
if (
|
775
|
-
|
824
|
+
if (given) {
|
825
|
+
doc = ALLOCA_N(struct _Doc, 1);
|
826
|
+
} else {
|
827
|
+
doc = ALLOC_N(struct _Doc, 1);
|
776
828
|
}
|
777
829
|
pi.str = json;
|
778
830
|
pi.s = pi.str;
|
779
|
-
doc_init(
|
780
|
-
pi.doc =
|
781
|
-
doc
|
782
|
-
|
831
|
+
doc_init(doc);
|
832
|
+
pi.doc = doc;
|
833
|
+
doc->self = rb_obj_alloc(clas);
|
834
|
+
doc->json = json;
|
835
|
+
DATA_PTR(doc->self) = doc;
|
783
836
|
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
784
|
-
|
785
|
-
|
786
|
-
|
837
|
+
if (given || 0 != ex) {
|
838
|
+
DATA_PTR(doc->self) = 0;
|
839
|
+
doc_free(pi.doc);
|
840
|
+
if (allocated) {
|
841
|
+
xfree(json);
|
842
|
+
}
|
843
|
+
} else {
|
844
|
+
result = doc->self;
|
845
|
+
}
|
787
846
|
if (0 != ex) {
|
788
847
|
rb_jump_tag(ex);
|
789
848
|
}
|
@@ -1014,32 +1073,51 @@ each_value(Doc doc, Leaf leaf) {
|
|
1014
1073
|
|
1015
1074
|
/* call-seq: open(json) { |doc| ... } => Object
|
1016
1075
|
*
|
1017
|
-
* Parses a JSON document String and then yields to the provided block
|
1018
|
-
* instance of the Oj::Doc as the single yield parameter.
|
1076
|
+
* Parses a JSON document String and then yields to the provided block if one
|
1077
|
+
* is given with an instance of the Oj::Doc as the single yield parameter. If
|
1078
|
+
* a block is not given then an Oj::Doc instance is returned and must be
|
1079
|
+
* closed with a call to the #close() method when no longer needed.
|
1019
1080
|
*
|
1020
1081
|
* @param [String] json JSON document string
|
1021
1082
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1022
1083
|
* @yieldreturn [Object] returns the result of the yield as the result of the method call
|
1023
1084
|
* @example
|
1024
1085
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
1086
|
+
* # or as an alternative
|
1087
|
+
* doc = Oj::Doc.open('[1,2,3]')
|
1088
|
+
* doc.size() #=> 4
|
1089
|
+
* doc.close()
|
1025
1090
|
*/
|
1026
1091
|
static VALUE
|
1027
1092
|
doc_open(VALUE clas, VALUE str) {
|
1028
1093
|
char *json;
|
1029
1094
|
size_t len;
|
1095
|
+
VALUE obj;
|
1096
|
+
int given = rb_block_given_p();
|
1097
|
+
int allocate;
|
1030
1098
|
|
1031
1099
|
Check_Type(str, T_STRING);
|
1032
1100
|
len = RSTRING_LEN(str) + 1;
|
1033
|
-
|
1101
|
+
allocate = (SMALL_XML < len || !given);
|
1102
|
+
if (allocate) {
|
1103
|
+
json = ALLOC_N(char, len);
|
1104
|
+
} else {
|
1105
|
+
json = ALLOCA_N(char, len);
|
1106
|
+
}
|
1034
1107
|
memcpy(json, StringValuePtr(str), len);
|
1035
|
-
|
1036
|
-
|
1108
|
+
obj = parse_json(clas, json, given, allocate);
|
1109
|
+
if (given && allocate) {
|
1110
|
+
xfree(json);
|
1111
|
+
}
|
1112
|
+
return obj;
|
1037
1113
|
}
|
1038
1114
|
|
1039
1115
|
/* call-seq: open_file(filename) { |doc| ... } => Object
|
1040
1116
|
*
|
1041
|
-
* Parses a JSON document from a file and then yields to the provided block
|
1042
|
-
* with an instance of the Oj::Doc as the single yield
|
1117
|
+
* Parses a JSON document from a file and then yields to the provided block if
|
1118
|
+
* one is given with an instance of the Oj::Doc as the single yield
|
1119
|
+
* parameter. If a block is not given then an Oj::Doc instance is returned and
|
1120
|
+
* must be closed with a call to the #close() method when no longer needed.
|
1043
1121
|
*
|
1044
1122
|
* @param [String] filename name of file that contains a JSON document
|
1045
1123
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
@@ -1047,6 +1125,10 @@ doc_open(VALUE clas, VALUE str) {
|
|
1047
1125
|
* @example
|
1048
1126
|
* File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
|
1049
1127
|
* Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
|
1128
|
+
* # or as an alternative
|
1129
|
+
* doc = Oj::Doc.open_file(filename)
|
1130
|
+
* doc.size() #=> 4
|
1131
|
+
* doc.close()
|
1050
1132
|
*/
|
1051
1133
|
static VALUE
|
1052
1134
|
doc_open_file(VALUE clas, VALUE filename) {
|
@@ -1054,6 +1136,9 @@ doc_open_file(VALUE clas, VALUE filename) {
|
|
1054
1136
|
char *json;
|
1055
1137
|
FILE *f;
|
1056
1138
|
size_t len;
|
1139
|
+
VALUE obj;
|
1140
|
+
int given = rb_block_given_p();
|
1141
|
+
int allocate;
|
1057
1142
|
|
1058
1143
|
Check_Type(filename, T_STRING);
|
1059
1144
|
path = StringValuePtr(filename);
|
@@ -1062,7 +1147,12 @@ doc_open_file(VALUE clas, VALUE filename) {
|
|
1062
1147
|
}
|
1063
1148
|
fseek(f, 0, SEEK_END);
|
1064
1149
|
len = ftell(f);
|
1065
|
-
|
1150
|
+
allocate = (SMALL_XML < len || !given);
|
1151
|
+
if (allocate) {
|
1152
|
+
json = ALLOC_N(char, len + 1);
|
1153
|
+
} else {
|
1154
|
+
json = ALLOCA_N(char, len + 1);
|
1155
|
+
}
|
1066
1156
|
fseek(f, 0, SEEK_SET);
|
1067
1157
|
if (len != fread(json, 1, len, f)) {
|
1068
1158
|
fclose(f);
|
@@ -1070,8 +1160,11 @@ doc_open_file(VALUE clas, VALUE filename) {
|
|
1070
1160
|
}
|
1071
1161
|
fclose(f);
|
1072
1162
|
json[len] = '\0';
|
1073
|
-
|
1074
|
-
|
1163
|
+
obj = parse_json(clas, json, given, allocate);
|
1164
|
+
if (given && allocate) {
|
1165
|
+
xfree(json);
|
1166
|
+
}
|
1167
|
+
return obj;
|
1075
1168
|
}
|
1076
1169
|
|
1077
1170
|
/* Document-method: parse
|
@@ -1085,7 +1178,7 @@ doc_open_file(VALUE clas, VALUE filename) {
|
|
1085
1178
|
*/
|
1086
1179
|
static VALUE
|
1087
1180
|
doc_where(VALUE self) {
|
1088
|
-
Doc doc =
|
1181
|
+
Doc doc = self_doc(self);
|
1089
1182
|
|
1090
1183
|
if (0 == *doc->where_path || doc->where == doc->where_path) {
|
1091
1184
|
return oj_slash_string;
|
@@ -1116,7 +1209,7 @@ doc_where(VALUE self) {
|
|
1116
1209
|
*p++ = '/';
|
1117
1210
|
}
|
1118
1211
|
*--p = '\0';
|
1119
|
-
return
|
1212
|
+
return rb_str_new(path, p - path);
|
1120
1213
|
}
|
1121
1214
|
}
|
1122
1215
|
|
@@ -1130,7 +1223,7 @@ doc_where(VALUE self) {
|
|
1130
1223
|
*/
|
1131
1224
|
static VALUE
|
1132
1225
|
doc_local_key(VALUE self) {
|
1133
|
-
Doc doc =
|
1226
|
+
Doc doc = self_doc(self);
|
1134
1227
|
Leaf leaf = *doc->where;
|
1135
1228
|
VALUE key = Qnil;
|
1136
1229
|
|
@@ -1156,7 +1249,7 @@ doc_local_key(VALUE self) {
|
|
1156
1249
|
*/
|
1157
1250
|
static VALUE
|
1158
1251
|
doc_home(VALUE self) {
|
1159
|
-
Doc doc =
|
1252
|
+
Doc doc = self_doc(self);
|
1160
1253
|
|
1161
1254
|
*doc->where_path = doc->data;
|
1162
1255
|
doc->where = doc->where_path;
|
@@ -1177,7 +1270,7 @@ doc_home(VALUE self) {
|
|
1177
1270
|
*/
|
1178
1271
|
static VALUE
|
1179
1272
|
doc_type(int argc, VALUE *argv, VALUE self) {
|
1180
|
-
Doc doc =
|
1273
|
+
Doc doc = self_doc(self);
|
1181
1274
|
Leaf leaf;
|
1182
1275
|
const char *path = 0;
|
1183
1276
|
VALUE type = Qnil;
|
@@ -1216,7 +1309,7 @@ doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1216
1309
|
*/
|
1217
1310
|
static VALUE
|
1218
1311
|
doc_fetch(int argc, VALUE *argv, VALUE self) {
|
1219
|
-
Doc doc =
|
1312
|
+
Doc doc = self_doc(self);
|
1220
1313
|
Leaf leaf;
|
1221
1314
|
VALUE val = Qnil;
|
1222
1315
|
const char *path = 0;
|
@@ -1253,7 +1346,7 @@ static VALUE
|
|
1253
1346
|
doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
1254
1347
|
if (rb_block_given_p()) {
|
1255
1348
|
Leaf save_path[MAX_STACK];
|
1256
|
-
Doc doc =
|
1349
|
+
Doc doc = self_doc(self);
|
1257
1350
|
const char *path = 0;
|
1258
1351
|
size_t wlen;
|
1259
1352
|
|
@@ -1287,7 +1380,7 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1287
1380
|
*/
|
1288
1381
|
static VALUE
|
1289
1382
|
doc_move(VALUE self, VALUE str) {
|
1290
|
-
Doc doc =
|
1383
|
+
Doc doc = self_doc(self);
|
1291
1384
|
const char *path;
|
1292
1385
|
int loc;
|
1293
1386
|
|
@@ -1323,7 +1416,7 @@ static VALUE
|
|
1323
1416
|
doc_each_child(int argc, VALUE *argv, VALUE self) {
|
1324
1417
|
if (rb_block_given_p()) {
|
1325
1418
|
Leaf save_path[MAX_STACK];
|
1326
|
-
Doc doc =
|
1419
|
+
Doc doc = self_doc(self);
|
1327
1420
|
const char *path = 0;
|
1328
1421
|
size_t wlen;
|
1329
1422
|
|
@@ -1383,7 +1476,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1383
1476
|
static VALUE
|
1384
1477
|
doc_each_value(int argc, VALUE *argv, VALUE self) {
|
1385
1478
|
if (rb_block_given_p()) {
|
1386
|
-
Doc doc =
|
1479
|
+
Doc doc = self_doc(self);
|
1387
1480
|
const char *path = 0;
|
1388
1481
|
Leaf leaf;
|
1389
1482
|
|
@@ -1412,7 +1505,7 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1412
1505
|
*/
|
1413
1506
|
static VALUE
|
1414
1507
|
doc_dump(int argc, VALUE *argv, VALUE self) {
|
1415
|
-
Doc doc =
|
1508
|
+
Doc doc = self_doc(self);
|
1416
1509
|
Leaf leaf;
|
1417
1510
|
const char *path = 0;
|
1418
1511
|
const char *filename = 0;
|
@@ -1457,6 +1550,26 @@ doc_size(VALUE self) {
|
|
1457
1550
|
return ULONG2NUM(((Doc)DATA_PTR(self))->size);
|
1458
1551
|
}
|
1459
1552
|
|
1553
|
+
/* call-seq: close() => nil
|
1554
|
+
*
|
1555
|
+
* Closes an open document. No further calls to the document will be valid
|
1556
|
+
* after closing.
|
1557
|
+
* @example
|
1558
|
+
* doc = Oj::Doc.open('[1,2,3]')
|
1559
|
+
* doc.size() #=> 4
|
1560
|
+
* doc.close()
|
1561
|
+
*/
|
1562
|
+
static VALUE
|
1563
|
+
doc_close(VALUE self) {
|
1564
|
+
Doc doc = self_doc(self);
|
1565
|
+
|
1566
|
+
DATA_PTR(doc->self) = 0;
|
1567
|
+
xfree(doc->json);
|
1568
|
+
doc_free(doc);
|
1569
|
+
|
1570
|
+
return Qnil;
|
1571
|
+
}
|
1572
|
+
|
1460
1573
|
/* Document-class: Oj::Doc
|
1461
1574
|
*
|
1462
1575
|
* The Doc class is used to parse and navigate a JSON document. The model it
|
@@ -1498,8 +1611,10 @@ doc_size(VALUE self) {
|
|
1498
1611
|
* end
|
1499
1612
|
* #=> 2
|
1500
1613
|
*
|
1501
|
-
* # Now try again using a path to Oj::Doc.fetch() directly.
|
1502
|
-
* Oj::Doc.open(json)
|
1614
|
+
* # Now try again using a path to Oj::Doc.fetch() directly and not using a block.
|
1615
|
+
* doc = Oj::Doc.open(json)
|
1616
|
+
* doc.fetch('/2/three') #=> 3
|
1617
|
+
* doc.close()
|
1503
1618
|
*/
|
1504
1619
|
void
|
1505
1620
|
oj_init_doc() {
|
@@ -1518,4 +1633,5 @@ oj_init_doc() {
|
|
1518
1633
|
rb_define_method(oj_doc_class, "each_value", doc_each_value, -1);
|
1519
1634
|
rb_define_method(oj_doc_class, "dump", doc_dump, -1);
|
1520
1635
|
rb_define_method(oj_doc_class, "size", doc_size, 0);
|
1636
|
+
rb_define_method(oj_doc_class, "close", doc_close, 0);
|
1521
1637
|
}
|