oj 3.11.5 → 3.16.5
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 +1421 -0
- data/README.md +19 -5
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +20 -6
- data/ext/oj/cache.c +329 -0
- data/ext/oj/cache.h +22 -0
- data/ext/oj/cache8.c +10 -9
- data/ext/oj/circarray.c +8 -6
- data/ext/oj/circarray.h +2 -2
- data/ext/oj/code.c +19 -33
- data/ext/oj/code.h +2 -2
- data/ext/oj/compat.c +27 -77
- data/ext/oj/custom.c +86 -179
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +256 -249
- data/ext/oj/dump.h +26 -12
- data/ext/oj/dump_compat.c +565 -642
- data/ext/oj/dump_leaf.c +17 -63
- data/ext/oj/dump_object.c +65 -187
- data/ext/oj/dump_strict.c +27 -51
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +2 -13
- data/ext/oj/err.h +24 -8
- data/ext/oj/extconf.rb +21 -6
- data/ext/oj/fast.c +149 -149
- data/ext/oj/intern.c +313 -0
- data/ext/oj/intern.h +22 -0
- data/ext/oj/mem.c +318 -0
- data/ext/oj/mem.h +53 -0
- data/ext/oj/mimic_json.c +121 -106
- data/ext/oj/object.c +85 -162
- data/ext/oj/odd.c +89 -67
- data/ext/oj/odd.h +15 -15
- data/ext/oj/oj.c +542 -411
- data/ext/oj/oj.h +99 -73
- data/ext/oj/parse.c +175 -187
- data/ext/oj/parse.h +26 -24
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +101 -0
- data/ext/oj/rails.c +112 -159
- data/ext/oj/rails.h +1 -1
- data/ext/oj/reader.c +11 -14
- data/ext/oj/reader.h +4 -2
- data/ext/oj/resolve.c +5 -24
- data/ext/oj/rxclass.c +7 -6
- data/ext/oj/rxclass.h +1 -1
- data/ext/oj/saj.c +22 -33
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +5 -28
- data/ext/oj/sparse.c +28 -72
- data/ext/oj/stream_writer.c +50 -40
- data/ext/oj/strict.c +56 -61
- data/ext/oj/string_writer.c +72 -39
- data/ext/oj/trace.h +31 -4
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.h +1 -1
- data/ext/oj/val_stack.c +14 -3
- data/ext/oj/val_stack.h +8 -7
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +63 -88
- data/lib/oj/active_support_helper.rb +1 -3
- data/lib/oj/bag.rb +7 -1
- data/lib/oj/easy_hash.rb +4 -5
- data/lib/oj/error.rb +1 -2
- data/lib/oj/json.rb +162 -150
- data/lib/oj/mimic.rb +9 -7
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/schandler.rb +5 -4
- data/lib/oj/state.rb +12 -8
- data/lib/oj/version.rb +1 -2
- data/lib/oj.rb +2 -0
- data/pages/Compatibility.md +1 -1
- data/pages/InstallOptions.md +20 -0
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +8 -3
- data/pages/Options.md +43 -5
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +14 -2
- data/test/_test_active.rb +8 -9
- data/test/_test_active_mimic.rb +7 -8
- data/test/_test_mimic_rails.rb +17 -20
- data/test/activerecord/result_test.rb +5 -6
- data/test/activesupport6/encoding_test.rb +63 -28
- data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
- data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
- data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
- data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
- data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
- data/test/files.rb +15 -15
- data/test/foo.rb +16 -45
- data/test/helper.rb +11 -8
- data/test/isolated/shared.rb +3 -2
- data/test/json_gem/json_addition_test.rb +2 -2
- data/test/json_gem/json_common_interface_test.rb +8 -6
- data/test/json_gem/json_encoding_test.rb +0 -0
- data/test/json_gem/json_ext_parser_test.rb +1 -0
- data/test/json_gem/json_fixtures_test.rb +3 -2
- data/test/json_gem/json_generator_test.rb +56 -38
- data/test/json_gem/json_generic_object_test.rb +11 -11
- data/test/json_gem/json_parser_test.rb +54 -47
- data/test/json_gem/json_string_matching_test.rb +9 -9
- data/test/json_gem/test_helper.rb +7 -3
- data/test/mem.rb +34 -0
- data/test/perf.rb +22 -27
- data/test/perf_compat.rb +31 -33
- data/test/perf_dump.rb +50 -0
- data/test/perf_fast.rb +80 -82
- data/test/perf_file.rb +27 -29
- data/test/perf_object.rb +65 -69
- data/test/perf_once.rb +59 -0
- data/test/perf_parser.rb +183 -0
- data/test/perf_saj.rb +46 -54
- data/test/perf_scp.rb +58 -69
- data/test/perf_simple.rb +41 -39
- data/test/perf_strict.rb +74 -82
- data/test/perf_wab.rb +67 -69
- data/test/prec.rb +5 -5
- data/test/sample/change.rb +0 -1
- data/test/sample/dir.rb +0 -1
- data/test/sample/doc.rb +0 -1
- data/test/sample/file.rb +0 -1
- data/test/sample/group.rb +0 -1
- data/test/sample/hasprops.rb +0 -1
- data/test/sample/layer.rb +0 -1
- data/test/sample/rect.rb +0 -1
- data/test/sample/shape.rb +0 -1
- data/test/sample/text.rb +0 -1
- data/test/sample.rb +16 -16
- data/test/sample_json.rb +8 -8
- data/test/test_compat.rb +95 -43
- data/test/test_custom.rb +73 -51
- data/test/test_debian.rb +7 -10
- data/test/test_fast.rb +135 -79
- data/test/test_file.rb +41 -30
- data/test/test_gc.rb +16 -5
- data/test/test_generate.rb +5 -5
- data/test/test_hash.rb +5 -5
- data/test/test_integer_range.rb +9 -9
- data/test/test_null.rb +20 -20
- data/test/test_object.rb +99 -96
- data/test/test_parser.rb +11 -0
- data/test/test_parser_debug.rb +27 -0
- data/test/test_parser_saj.rb +337 -0
- data/test/test_parser_usual.rb +251 -0
- data/test/test_rails.rb +2 -2
- data/test/test_saj.rb +10 -8
- data/test/test_scp.rb +37 -39
- data/test/test_strict.rb +40 -32
- data/test/test_various.rb +165 -84
- data/test/test_wab.rb +48 -44
- data/test/test_writer.rb +47 -47
- data/test/tests.rb +13 -5
- data/test/tests_mimic.rb +12 -3
- data/test/tests_mimic_addition.rb +12 -3
- metadata +74 -128
- data/ext/oj/hash.c +0 -131
- data/ext/oj/hash.h +0 -19
- data/ext/oj/hash_test.c +0 -491
- data/test/activesupport4/decoding_test.rb +0 -108
- data/test/activesupport4/encoding_test.rb +0 -531
- data/test/activesupport4/test_helper.rb +0 -41
- data/test/activesupport5/test_helper.rb +0 -72
- data/test/bar.rb +0 -35
- data/test/baz.rb +0 -16
- data/test/zoo.rb +0 -13
data/ext/oj/fast.c
CHANGED
@@ -11,13 +11,15 @@
|
|
11
11
|
#include <stdlib.h>
|
12
12
|
#include <string.h>
|
13
13
|
|
14
|
+
#include "dump.h"
|
14
15
|
#include "encode.h"
|
16
|
+
#include "mem.h"
|
15
17
|
#include "oj.h"
|
16
18
|
|
17
19
|
// maximum to allocate on the stack, arbitrary limit
|
18
20
|
#define SMALL_JSON 65536
|
19
21
|
#define MAX_STACK 100
|
20
|
-
|
22
|
+
// #define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
|
21
23
|
#define BATCH_SIZE 100
|
22
24
|
|
23
25
|
// Support for compaction
|
@@ -31,25 +33,25 @@ typedef struct _batch {
|
|
31
33
|
struct _batch *next;
|
32
34
|
int next_avail;
|
33
35
|
struct _leaf leaves[BATCH_SIZE];
|
34
|
-
} *
|
36
|
+
} *Batch;
|
35
37
|
|
36
38
|
typedef struct _doc {
|
37
39
|
Leaf data;
|
38
|
-
Leaf
|
40
|
+
Leaf *where; // points to current location
|
39
41
|
Leaf where_path[MAX_STACK]; // points to head of path
|
40
|
-
char
|
42
|
+
char *json;
|
41
43
|
unsigned long size; // number of leaves/branches in the doc
|
42
44
|
VALUE self;
|
43
45
|
Batch batches;
|
44
46
|
struct _batch batch0;
|
45
|
-
} *
|
47
|
+
} *Doc;
|
46
48
|
|
47
49
|
typedef struct _parseInfo {
|
48
50
|
char *str; // buffer being read from
|
49
51
|
char *s; // current position in buffer
|
50
52
|
Doc doc;
|
51
53
|
void *stack_min;
|
52
|
-
} *
|
54
|
+
} *ParseInfo;
|
53
55
|
|
54
56
|
static void leaf_init(Leaf leaf, int type);
|
55
57
|
static Leaf leaf_new(Doc doc, int type);
|
@@ -73,29 +75,13 @@ static char *read_quoted_value(ParseInfo pi);
|
|
73
75
|
static void skip_comment(ParseInfo pi);
|
74
76
|
|
75
77
|
static VALUE protect_open_proc(VALUE x);
|
76
|
-
static VALUE parse_json(VALUE clas, char *json, bool given
|
78
|
+
static VALUE parse_json(VALUE clas, char *json, bool given);
|
77
79
|
static void each_leaf(Doc doc, VALUE self);
|
78
80
|
static int move_step(Doc doc, const char *path, int loc);
|
79
81
|
static Leaf get_doc_leaf(Doc doc, const char *path);
|
80
82
|
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
|
81
83
|
static void each_value(Doc doc, Leaf leaf);
|
82
84
|
|
83
|
-
static void doc_init(Doc doc);
|
84
|
-
static void doc_free(Doc doc);
|
85
|
-
static VALUE doc_open(VALUE clas, VALUE str);
|
86
|
-
static VALUE doc_open_file(VALUE clas, VALUE filename);
|
87
|
-
static VALUE doc_where(VALUE self);
|
88
|
-
static VALUE doc_local_key(VALUE self);
|
89
|
-
static VALUE doc_home(VALUE self);
|
90
|
-
static VALUE doc_type(int argc, VALUE *argv, VALUE self);
|
91
|
-
static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
|
92
|
-
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
|
93
|
-
static VALUE doc_move(VALUE self, VALUE str);
|
94
|
-
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
|
95
|
-
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
|
96
|
-
static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
|
97
|
-
static VALUE doc_size(VALUE self);
|
98
|
-
|
99
85
|
VALUE oj_doc_class = Qundef;
|
100
86
|
|
101
87
|
// This is only for CentOS 5.4 with Ruby 1.9.3-p0.
|
@@ -128,10 +114,7 @@ inline static char *ulong_fill(char *s, size_t num) {
|
|
128
114
|
char *b = buf + sizeof(buf) - 1;
|
129
115
|
|
130
116
|
*b-- = '\0';
|
131
|
-
|
132
|
-
*b = (num % 10) + '0';
|
133
|
-
}
|
134
|
-
b++;
|
117
|
+
b = oj_longlong_to_string((long long)num, false, b);
|
135
118
|
if ('\0' == *b) {
|
136
119
|
b--;
|
137
120
|
*b = '0';
|
@@ -175,7 +158,7 @@ inline static Leaf leaf_new(Doc doc, int type) {
|
|
175
158
|
Leaf leaf;
|
176
159
|
|
177
160
|
if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
|
178
|
-
Batch b =
|
161
|
+
Batch b = OJ_R_ALLOC(struct _batch);
|
179
162
|
|
180
163
|
// Initializes all leaves with a NO_VAL value_type
|
181
164
|
memset(b, 0, sizeof(struct _batch));
|
@@ -216,9 +199,7 @@ static VALUE leaf_value(Doc doc, Leaf leaf) {
|
|
216
199
|
break;
|
217
200
|
case T_ARRAY: return leaf_array_value(doc, leaf); break;
|
218
201
|
case T_HASH: return leaf_hash_value(doc, leaf); break;
|
219
|
-
default:
|
220
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
|
221
|
-
break;
|
202
|
+
default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
|
222
203
|
}
|
223
204
|
}
|
224
205
|
return leaf->value;
|
@@ -267,7 +248,7 @@ static void skip_comment(ParseInfo pi) {
|
|
267
248
|
#endif
|
268
249
|
|
269
250
|
static void leaf_fixnum_value(Leaf leaf) {
|
270
|
-
char
|
251
|
+
char *s = leaf->str;
|
271
252
|
int64_t n = 0;
|
272
253
|
int neg = 0;
|
273
254
|
int big = 0;
|
@@ -373,7 +354,7 @@ static Leaf read_next(ParseInfo pi) {
|
|
373
354
|
|
374
355
|
static Leaf read_obj(ParseInfo pi) {
|
375
356
|
Leaf h = leaf_new(pi->doc, T_HASH);
|
376
|
-
char
|
357
|
+
char *end;
|
377
358
|
const char *key = 0;
|
378
359
|
Leaf val = 0;
|
379
360
|
|
@@ -665,10 +646,11 @@ static void doc_free(Doc doc) {
|
|
665
646
|
while (0 != (b = doc->batches)) {
|
666
647
|
doc->batches = doc->batches->next;
|
667
648
|
if (&doc->batch0 != b) {
|
668
|
-
|
649
|
+
OJ_R_FREE(b);
|
669
650
|
}
|
670
651
|
}
|
671
|
-
|
652
|
+
OJ_R_FREE(doc->json);
|
653
|
+
OJ_R_FREE(doc);
|
672
654
|
}
|
673
655
|
}
|
674
656
|
|
@@ -688,27 +670,28 @@ static void free_doc_cb(void *x) {
|
|
688
670
|
Doc doc = (Doc)x;
|
689
671
|
|
690
672
|
if (0 != doc) {
|
691
|
-
xfree(doc->json);
|
692
673
|
doc_free(doc);
|
693
674
|
}
|
694
675
|
}
|
695
676
|
|
696
677
|
static void mark_leaf(Leaf leaf) {
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
678
|
+
if (NULL != leaf) {
|
679
|
+
switch (leaf->value_type) {
|
680
|
+
case COL_VAL:
|
681
|
+
if (NULL != leaf->elements) {
|
682
|
+
Leaf first = leaf->elements->next;
|
683
|
+
Leaf e = first;
|
702
684
|
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
685
|
+
do {
|
686
|
+
mark_leaf(e);
|
687
|
+
e = e->next;
|
688
|
+
} while (e != first);
|
689
|
+
}
|
690
|
+
break;
|
691
|
+
case RUBY_VAL: mark(leaf->value); break;
|
710
692
|
|
711
|
-
|
693
|
+
default: break;
|
694
|
+
}
|
712
695
|
}
|
713
696
|
}
|
714
697
|
|
@@ -764,20 +747,15 @@ static const rb_data_type_t oj_doc_type = {
|
|
764
747
|
0,
|
765
748
|
};
|
766
749
|
|
767
|
-
static VALUE parse_json(VALUE clas, char *json, bool given
|
750
|
+
static VALUE parse_json(VALUE clas, char *json, bool given) {
|
768
751
|
struct _parseInfo pi;
|
769
752
|
volatile VALUE result = Qnil;
|
770
753
|
Doc doc;
|
771
754
|
int ex = 0;
|
772
755
|
volatile VALUE self;
|
773
756
|
|
774
|
-
|
757
|
+
doc = OJ_R_ALLOC_N(struct _doc, 1);
|
775
758
|
|
776
|
-
if (given) {
|
777
|
-
doc = ALLOCA_N(struct _doc, 1);
|
778
|
-
} else {
|
779
|
-
doc = ALLOC(struct _doc);
|
780
|
-
}
|
781
759
|
// skip UTF-8 BOM if present
|
782
760
|
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
783
761
|
pi.str = json + 3;
|
@@ -787,9 +765,9 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
|
|
787
765
|
pi.s = pi.str;
|
788
766
|
doc_init(doc);
|
789
767
|
pi.doc = doc;
|
790
|
-
#if IS_WINDOWS
|
768
|
+
#if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
|
791
769
|
// assume a 1M stack and give half to ruby
|
792
|
-
pi.stack_min = (void *)((char *)&pi - (
|
770
|
+
pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
|
793
771
|
#else
|
794
772
|
{
|
795
773
|
struct rlimit lim;
|
@@ -802,18 +780,19 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
|
|
802
780
|
}
|
803
781
|
}
|
804
782
|
#endif
|
805
|
-
|
806
|
-
|
807
|
-
doc->
|
808
|
-
|
809
|
-
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
783
|
+
doc->json = json;
|
784
|
+
self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
|
785
|
+
doc->self = self;
|
786
|
+
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
810
787
|
if (given || 0 != ex) {
|
811
788
|
DATA_PTR(doc->self) = NULL;
|
789
|
+
// TBD is this needed?
|
790
|
+
/*
|
812
791
|
doc_free(pi.doc);
|
813
|
-
if (
|
814
|
-
|
792
|
+
if (0 != ex) { // will jump so caller will not free
|
793
|
+
OJ_R_FREE(json);
|
815
794
|
}
|
816
|
-
|
795
|
+
*/
|
817
796
|
} else {
|
818
797
|
result = doc->self;
|
819
798
|
}
|
@@ -841,9 +820,7 @@ static Leaf get_doc_leaf(Doc doc, const char *path) {
|
|
841
820
|
size_t cnt = doc->where - doc->where_path;
|
842
821
|
|
843
822
|
if (MAX_STACK <= cnt) {
|
844
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
845
|
-
"Path too deep. Limit is %d levels.",
|
846
|
-
MAX_STACK);
|
823
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
847
824
|
}
|
848
825
|
memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
|
849
826
|
lp = stack + cnt;
|
@@ -884,9 +861,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
884
861
|
Leaf leaf = *lp;
|
885
862
|
|
886
863
|
if (MAX_STACK <= lp - stack) {
|
887
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
888
|
-
"Path too deep. Limit is %d levels.",
|
889
|
-
MAX_STACK);
|
864
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
890
865
|
}
|
891
866
|
if ('\0' != *path) {
|
892
867
|
if ('.' == *path && '.' == *(path + 1)) {
|
@@ -899,12 +874,18 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
899
874
|
} else {
|
900
875
|
return 0;
|
901
876
|
}
|
902
|
-
} else if (
|
877
|
+
} else if (NULL == leaf->elements) {
|
878
|
+
leaf = NULL;
|
879
|
+
} else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
|
880
|
+
// We are trying to get a children of a leaf, which
|
881
|
+
// doesn't exist.
|
882
|
+
leaf = NULL;
|
883
|
+
} else if (COL_VAL == leaf->value_type) {
|
903
884
|
Leaf first = leaf->elements->next;
|
904
885
|
Leaf e = first;
|
905
886
|
int type = leaf->rtype;
|
906
887
|
|
907
|
-
leaf =
|
888
|
+
leaf = NULL;
|
908
889
|
if (T_ARRAY == type) {
|
909
890
|
int cnt = 0;
|
910
891
|
|
@@ -929,6 +910,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
929
910
|
const char *slash = next_slash(path);
|
930
911
|
int klen;
|
931
912
|
|
913
|
+
leaf = NULL;
|
932
914
|
if (0 == slash) {
|
933
915
|
klen = (int)strlen(key);
|
934
916
|
path += klen;
|
@@ -959,9 +941,7 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
959
941
|
|
960
942
|
doc->where++;
|
961
943
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
962
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
963
|
-
"Path too deep. Limit is %d levels.",
|
964
|
-
MAX_STACK);
|
944
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
965
945
|
}
|
966
946
|
do {
|
967
947
|
*doc->where = e;
|
@@ -977,9 +957,7 @@ static void each_leaf(Doc doc, VALUE self) {
|
|
977
957
|
|
978
958
|
static int move_step(Doc doc, const char *path, int loc) {
|
979
959
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
980
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
|
981
|
-
"Path too deep. Limit is %d levels.",
|
982
|
-
MAX_STACK);
|
960
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
983
961
|
}
|
984
962
|
if ('\0' == *path) {
|
985
963
|
loc = 0;
|
@@ -1104,31 +1082,23 @@ static void each_value(Doc doc, Leaf leaf) {
|
|
1104
1082
|
* doc.close()
|
1105
1083
|
*/
|
1106
1084
|
static VALUE doc_open(VALUE clas, VALUE str) {
|
1107
|
-
char
|
1085
|
+
char *json;
|
1108
1086
|
size_t len;
|
1109
1087
|
volatile VALUE obj;
|
1110
1088
|
int given = rb_block_given_p();
|
1111
|
-
int allocate;
|
1112
1089
|
|
1113
1090
|
Check_Type(str, T_STRING);
|
1114
|
-
len
|
1115
|
-
|
1116
|
-
|
1117
|
-
json = ALLOC_N(char, len);
|
1118
|
-
} else {
|
1119
|
-
json = ALLOCA_N(char, len);
|
1120
|
-
}
|
1121
|
-
// It should not be necessaary to stop GC but if it is not stopped and a
|
1122
|
-
// large string is parsed that string is corrupted or freed during
|
1123
|
-
// parsing. I'm not sure what is going on exactly but disabling GC avoids
|
1124
|
-
// the issue.
|
1125
|
-
rb_gc_disable();
|
1091
|
+
len = (int)RSTRING_LEN(str) + 1;
|
1092
|
+
json = OJ_R_ALLOC_N(char, len);
|
1093
|
+
|
1126
1094
|
memcpy(json, StringValuePtr(str), len);
|
1127
|
-
obj = parse_json(clas, json, given
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1095
|
+
obj = parse_json(clas, json, given);
|
1096
|
+
// TBD is this needed
|
1097
|
+
/*
|
1098
|
+
if (given) {
|
1099
|
+
OJ_R_FREE(json);
|
1131
1100
|
}
|
1101
|
+
*/
|
1132
1102
|
return obj;
|
1133
1103
|
}
|
1134
1104
|
|
@@ -1152,27 +1122,21 @@ static VALUE doc_open(VALUE clas, VALUE str) {
|
|
1152
1122
|
* doc.close()
|
1153
1123
|
*/
|
1154
1124
|
static VALUE doc_open_file(VALUE clas, VALUE filename) {
|
1155
|
-
char
|
1156
|
-
char
|
1157
|
-
FILE
|
1125
|
+
char *path;
|
1126
|
+
char *json;
|
1127
|
+
FILE *f;
|
1158
1128
|
size_t len;
|
1159
1129
|
volatile VALUE obj;
|
1160
1130
|
int given = rb_block_given_p();
|
1161
|
-
int allocate;
|
1162
1131
|
|
1163
|
-
Check_Type(filename, T_STRING);
|
1164
1132
|
path = StringValuePtr(filename);
|
1165
1133
|
if (0 == (f = fopen(path, "r"))) {
|
1166
1134
|
rb_raise(rb_eIOError, "%s", strerror(errno));
|
1167
1135
|
}
|
1168
1136
|
fseek(f, 0, SEEK_END);
|
1169
|
-
len
|
1170
|
-
|
1171
|
-
|
1172
|
-
json = ALLOC_N(char, len + 1);
|
1173
|
-
} else {
|
1174
|
-
json = ALLOCA_N(char, len + 1);
|
1175
|
-
}
|
1137
|
+
len = ftell(f);
|
1138
|
+
json = OJ_R_ALLOC_N(char, len + 1);
|
1139
|
+
|
1176
1140
|
fseek(f, 0, SEEK_SET);
|
1177
1141
|
if (len != fread(json, 1, len, f)) {
|
1178
1142
|
fclose(f);
|
@@ -1183,12 +1147,13 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
|
|
1183
1147
|
}
|
1184
1148
|
fclose(f);
|
1185
1149
|
json[len] = '\0';
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
if (given
|
1190
|
-
|
1150
|
+
obj = parse_json(clas, json, given);
|
1151
|
+
// TBD is this needed
|
1152
|
+
/*
|
1153
|
+
if (given) {
|
1154
|
+
OJ_R_FREE(json);
|
1191
1155
|
}
|
1156
|
+
*/
|
1192
1157
|
return obj;
|
1193
1158
|
}
|
1194
1159
|
|
@@ -1217,7 +1182,7 @@ static char *append_key(char *p, const char *key) {
|
|
1217
1182
|
* @see Oj::Doc.open
|
1218
1183
|
*/
|
1219
1184
|
|
1220
|
-
/* @overload where
|
1185
|
+
/* @overload where() => String
|
1221
1186
|
*
|
1222
1187
|
* Returns a String that describes the absolute path to the current location
|
1223
1188
|
* in the JSON document.
|
@@ -1228,11 +1193,11 @@ static VALUE doc_where(VALUE self) {
|
|
1228
1193
|
if (0 == *doc->where_path || doc->where == doc->where_path) {
|
1229
1194
|
return oj_slash_string;
|
1230
1195
|
} else {
|
1231
|
-
Leaf
|
1196
|
+
Leaf *lp;
|
1232
1197
|
Leaf leaf;
|
1233
1198
|
size_t size = 3; // leading / and terminating \0
|
1234
|
-
char
|
1235
|
-
char
|
1199
|
+
char *path;
|
1200
|
+
char *p;
|
1236
1201
|
|
1237
1202
|
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
1238
1203
|
leaf = *lp;
|
@@ -1259,6 +1224,24 @@ static VALUE doc_where(VALUE self) {
|
|
1259
1224
|
}
|
1260
1225
|
}
|
1261
1226
|
|
1227
|
+
/* @overload where?() => String
|
1228
|
+
* @deprecated
|
1229
|
+
* Returns a String that describes the absolute path to the current location
|
1230
|
+
* in the JSON document.
|
1231
|
+
*/
|
1232
|
+
static VALUE doc_where_q(VALUE self) {
|
1233
|
+
return doc_where(self);
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
/* @overload path() => String
|
1237
|
+
*
|
1238
|
+
* Returns a String that describes the absolute path to the current location
|
1239
|
+
* in the JSON document.
|
1240
|
+
*/
|
1241
|
+
static VALUE doc_path(VALUE self) {
|
1242
|
+
return doc_where(self);
|
1243
|
+
}
|
1244
|
+
|
1262
1245
|
/* @overload local_key() => String, Fixnum, nil
|
1263
1246
|
*
|
1264
1247
|
* Returns the final key to the current location.
|
@@ -1317,7 +1300,6 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1317
1300
|
VALUE type = Qnil;
|
1318
1301
|
|
1319
1302
|
if (1 <= argc) {
|
1320
|
-
Check_Type(*argv, T_STRING);
|
1321
1303
|
path = StringValuePtr(*argv);
|
1322
1304
|
}
|
1323
1305
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
@@ -1326,11 +1308,7 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1326
1308
|
case T_TRUE: type = rb_cTrueClass; break;
|
1327
1309
|
case T_FALSE: type = rb_cFalseClass; break;
|
1328
1310
|
case T_STRING: type = rb_cString; break;
|
1329
|
-
#ifdef RUBY_INTEGER_UNIFICATION
|
1330
1311
|
case T_FIXNUM: type = rb_cInteger; break;
|
1331
|
-
#else
|
1332
|
-
case T_FIXNUM: type = rb_cFixnum; break;
|
1333
|
-
#endif
|
1334
1312
|
case T_FLOAT: type = rb_cFloat; break;
|
1335
1313
|
case T_ARRAY: type = rb_cArray; break;
|
1336
1314
|
case T_HASH: type = rb_cHash; break;
|
@@ -1340,14 +1318,14 @@ static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1340
1318
|
return type;
|
1341
1319
|
}
|
1342
1320
|
|
1343
|
-
/* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array,
|
1321
|
+
/* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
|
1344
1322
|
* Hash
|
1345
1323
|
*
|
1346
1324
|
* Returns the value at the location identified by the path or the current
|
1347
1325
|
* location if the path is nil or not provided. This method will create and
|
1348
1326
|
* return an Array or Hash if that is the type of Object at the location
|
1349
1327
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
1350
|
-
* document.
|
1328
|
+
* document. If a default is provided that is used if no value if found.
|
1351
1329
|
* @param [String] path path to the location to get the type of if provided
|
1352
1330
|
* @example
|
1353
1331
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
@@ -1357,11 +1335,10 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1357
1335
|
Doc doc;
|
1358
1336
|
Leaf leaf;
|
1359
1337
|
volatile VALUE val = Qnil;
|
1360
|
-
const char
|
1338
|
+
const char *path = 0;
|
1361
1339
|
|
1362
1340
|
doc = self_doc(self);
|
1363
1341
|
if (1 <= argc) {
|
1364
|
-
Check_Type(*argv, T_STRING);
|
1365
1342
|
path = StringValuePtr(*argv);
|
1366
1343
|
if (2 == argc) {
|
1367
1344
|
val = argv[1];
|
@@ -1373,6 +1350,27 @@ static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1373
1350
|
return val;
|
1374
1351
|
}
|
1375
1352
|
|
1353
|
+
/* @overload exists?(path) => true, false
|
1354
|
+
*
|
1355
|
+
* Returns true if the value at the location identified by the path exists.
|
1356
|
+
* @param [String] path path to the location
|
1357
|
+
* @example
|
1358
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
|
1359
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
|
1360
|
+
*/
|
1361
|
+
static VALUE doc_exists(VALUE self, VALUE str) {
|
1362
|
+
Doc doc;
|
1363
|
+
Leaf leaf;
|
1364
|
+
|
1365
|
+
doc = self_doc(self);
|
1366
|
+
if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
|
1367
|
+
if (NULL != leaf) {
|
1368
|
+
return Qtrue;
|
1369
|
+
}
|
1370
|
+
}
|
1371
|
+
return Qfalse;
|
1372
|
+
}
|
1373
|
+
|
1376
1374
|
/* @overload each_leaf(path=nil) => nil
|
1377
1375
|
*
|
1378
1376
|
* Yields to the provided block for each leaf node with the identified
|
@@ -1401,7 +1399,6 @@ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1401
1399
|
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
1402
1400
|
}
|
1403
1401
|
if (1 <= argc) {
|
1404
|
-
Check_Type(*argv, T_STRING);
|
1405
1402
|
path = StringValuePtr(*argv);
|
1406
1403
|
if ('/' == *path) {
|
1407
1404
|
doc->where = doc->where_path;
|
@@ -1436,7 +1433,6 @@ static VALUE doc_move(VALUE self, VALUE str) {
|
|
1436
1433
|
const char *path;
|
1437
1434
|
int loc;
|
1438
1435
|
|
1439
|
-
Check_Type(str, T_STRING);
|
1440
1436
|
path = StringValuePtr(str);
|
1441
1437
|
if ('/' == *path) {
|
1442
1438
|
doc->where = doc->where_path;
|
@@ -1455,7 +1451,7 @@ static VALUE doc_move(VALUE self, VALUE str) {
|
|
1455
1451
|
* to the block on yield is the Doc instance after moving to the child
|
1456
1452
|
* location.
|
1457
1453
|
* @param [String] path if provided it identified the top of the branch to
|
1458
|
-
* process the
|
1454
|
+
* process the children of
|
1459
1455
|
* @yieldparam [Doc] Doc at the child location
|
1460
1456
|
* @example
|
1461
1457
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1471,13 +1467,13 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1471
1467
|
Doc doc = self_doc(self);
|
1472
1468
|
const char *path = 0;
|
1473
1469
|
size_t wlen;
|
1470
|
+
Leaf *where_orig = doc->where;
|
1474
1471
|
|
1475
1472
|
wlen = doc->where - doc->where_path;
|
1476
1473
|
if (0 < wlen) {
|
1477
1474
|
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
1478
1475
|
}
|
1479
1476
|
if (1 <= argc) {
|
1480
|
-
Check_Type(*argv, T_STRING);
|
1481
1477
|
path = StringValuePtr(*argv);
|
1482
1478
|
if ('/' == *path) {
|
1483
1479
|
doc->where = doc->where_path;
|
@@ -1487,9 +1483,13 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1487
1483
|
if (0 < wlen) {
|
1488
1484
|
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1489
1485
|
}
|
1486
|
+
doc->where = where_orig;
|
1490
1487
|
return Qnil;
|
1491
1488
|
}
|
1492
1489
|
}
|
1490
|
+
if (NULL == doc->where || NULL == *doc->where) {
|
1491
|
+
return Qnil;
|
1492
|
+
}
|
1493
1493
|
if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
|
1494
1494
|
Leaf first = (*doc->where)->elements->next;
|
1495
1495
|
Leaf e = first;
|
@@ -1504,6 +1504,7 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1504
1504
|
if (0 < wlen) {
|
1505
1505
|
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1506
1506
|
}
|
1507
|
+
doc->where = where_orig;
|
1507
1508
|
}
|
1508
1509
|
return Qnil;
|
1509
1510
|
}
|
@@ -1539,7 +1540,6 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1539
1540
|
Leaf leaf;
|
1540
1541
|
|
1541
1542
|
if (1 <= argc) {
|
1542
|
-
Check_Type(*argv, T_STRING);
|
1543
1543
|
path = StringValuePtr(*argv);
|
1544
1544
|
}
|
1545
1545
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
@@ -1571,11 +1571,9 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1571
1571
|
|
1572
1572
|
if (1 <= argc) {
|
1573
1573
|
if (Qnil != *argv) {
|
1574
|
-
Check_Type(*argv, T_STRING);
|
1575
1574
|
path = StringValuePtr(*argv);
|
1576
1575
|
}
|
1577
1576
|
if (2 <= argc) {
|
1578
|
-
Check_Type(argv[1], T_STRING);
|
1579
1577
|
filename = StringValuePtr(argv[1]);
|
1580
1578
|
}
|
1581
1579
|
}
|
@@ -1583,18 +1581,15 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1583
1581
|
volatile VALUE rjson;
|
1584
1582
|
|
1585
1583
|
if (0 == filename) {
|
1586
|
-
char buf[4096];
|
1587
1584
|
struct _out out;
|
1588
1585
|
|
1589
|
-
out
|
1590
|
-
|
1591
|
-
out.
|
1592
|
-
out.omit_nil = oj_default_options.dump_opts.omit_nil;
|
1586
|
+
oj_out_init(&out);
|
1587
|
+
|
1588
|
+
out.omit_nil = oj_default_options.dump_opts.omit_nil;
|
1593
1589
|
oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
|
1594
1590
|
rjson = rb_str_new2(out.buf);
|
1595
|
-
|
1596
|
-
|
1597
|
-
}
|
1591
|
+
|
1592
|
+
oj_out_free(&out);
|
1598
1593
|
} else {
|
1599
1594
|
oj_write_leaf_to_file(leaf, filename, &oj_default_options);
|
1600
1595
|
rjson = Qnil;
|
@@ -1613,7 +1608,9 @@ static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1613
1608
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
1614
1609
|
*/
|
1615
1610
|
static VALUE doc_size(VALUE self) {
|
1616
|
-
|
1611
|
+
Doc d;
|
1612
|
+
TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
|
1613
|
+
return ULONG2NUM(d->size);
|
1617
1614
|
}
|
1618
1615
|
|
1619
1616
|
/* @overload close() => nil
|
@@ -1629,11 +1626,9 @@ static VALUE doc_close(VALUE self) {
|
|
1629
1626
|
Doc doc = self_doc(self);
|
1630
1627
|
|
1631
1628
|
rb_gc_unregister_address(&doc->self);
|
1632
|
-
DATA_PTR(doc->self) =
|
1629
|
+
DATA_PTR(doc->self) = NULL;
|
1633
1630
|
if (0 != doc) {
|
1634
|
-
xfree(doc->json);
|
1635
1631
|
doc_free(doc);
|
1636
|
-
xfree(doc);
|
1637
1632
|
}
|
1638
1633
|
return Qnil;
|
1639
1634
|
}
|
@@ -1690,16 +1685,21 @@ static VALUE doc_not_implemented(VALUE self) {
|
|
1690
1685
|
* # Now try again using a path to Oj::Doc.fetch() directly and not using a
|
1691
1686
|
* block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
|
1692
1687
|
*/
|
1693
|
-
void oj_init_doc() {
|
1688
|
+
void oj_init_doc(void) {
|
1694
1689
|
oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
|
1690
|
+
rb_gc_register_address(&oj_doc_class);
|
1691
|
+
rb_undef_alloc_func(oj_doc_class);
|
1695
1692
|
rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
|
1696
1693
|
rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
|
1697
1694
|
rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
|
1698
|
-
rb_define_method(oj_doc_class, "where?",
|
1695
|
+
rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
|
1696
|
+
rb_define_method(oj_doc_class, "where", doc_where, 0);
|
1697
|
+
rb_define_method(oj_doc_class, "path", doc_path, 0);
|
1699
1698
|
rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
|
1700
1699
|
rb_define_method(oj_doc_class, "home", doc_home, 0);
|
1701
1700
|
rb_define_method(oj_doc_class, "type", doc_type, -1);
|
1702
1701
|
rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
|
1702
|
+
rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
|
1703
1703
|
rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
|
1704
1704
|
rb_define_method(oj_doc_class, "move", doc_move, 1);
|
1705
1705
|
rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);
|