oj 3.11.0 → 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 +20 -5
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +48 -38
- data/ext/oj/cache.c +329 -0
- data/ext/oj/cache.h +22 -0
- data/ext/oj/cache8.c +60 -62
- data/ext/oj/cache8.h +8 -7
- data/ext/oj/circarray.c +35 -35
- data/ext/oj/circarray.h +11 -9
- data/ext/oj/code.c +156 -174
- data/ext/oj/code.h +19 -18
- data/ext/oj/compat.c +140 -197
- data/ext/oj/custom.c +737 -879
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +830 -835
- data/ext/oj/dump.h +65 -53
- data/ext/oj/dump_compat.c +566 -642
- data/ext/oj/dump_leaf.c +95 -182
- data/ext/oj/dump_object.c +518 -659
- data/ext/oj/dump_strict.c +301 -334
- data/ext/oj/encode.h +3 -4
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +27 -24
- data/ext/oj/err.h +38 -13
- data/ext/oj/extconf.rb +23 -7
- data/ext/oj/fast.c +1043 -1073
- 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 +449 -423
- data/ext/oj/object.c +530 -576
- data/ext/oj/odd.c +155 -138
- data/ext/oj/odd.h +24 -22
- data/ext/oj/oj.c +1331 -993
- data/ext/oj/oj.h +306 -292
- data/ext/oj/parse.c +934 -938
- data/ext/oj/parse.h +73 -70
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +101 -0
- data/ext/oj/rails.c +795 -845
- data/ext/oj/rails.h +7 -7
- data/ext/oj/reader.c +132 -140
- data/ext/oj/reader.h +67 -78
- data/ext/oj/resolve.c +40 -59
- data/ext/oj/resolve.h +3 -2
- data/ext/oj/rxclass.c +67 -67
- data/ext/oj/rxclass.h +11 -9
- data/ext/oj/saj.c +441 -480
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +78 -111
- data/ext/oj/sparse.c +726 -730
- data/ext/oj/stream_writer.c +146 -165
- data/ext/oj/strict.c +103 -123
- data/ext/oj/string_writer.c +241 -253
- data/ext/oj/trace.c +29 -33
- data/ext/oj/trace.h +41 -11
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.c +103 -103
- data/ext/oj/util.h +3 -2
- data/ext/oj/val_stack.c +60 -49
- data/ext/oj/val_stack.h +79 -85
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +307 -350
- data/lib/oj/active_support_helper.rb +1 -3
- data/lib/oj/bag.rb +8 -1
- data/lib/oj/easy_hash.rb +9 -9
- data/lib/oj/error.rb +1 -2
- data/lib/oj/json.rb +162 -150
- data/lib/oj/mimic.rb +9 -19
- 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 +12 -8
- 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 +17 -43
- data/test/helper.rb +16 -3
- 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 +71 -41
- 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 +12 -0
- 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 +97 -45
- 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 +21 -0
- data/test/test_hash.rb +15 -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 +38 -40
- 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 +75 -127
- data/ext/oj/hash.c +0 -135
- data/ext/oj/hash.h +0 -18
- data/ext/oj/hash_test.c +0 -484
- 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
@@ -1,98 +1,93 @@
|
|
1
1
|
// Copyright (c) 2012 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for
|
3
|
+
// license details.
|
2
4
|
|
3
5
|
#if !IS_WINDOWS
|
4
6
|
#include <sys/resource.h> // for getrlimit() on linux
|
5
7
|
#endif
|
6
|
-
#include <
|
8
|
+
#include <errno.h>
|
9
|
+
#include <math.h>
|
7
10
|
#include <stdio.h>
|
11
|
+
#include <stdlib.h>
|
8
12
|
#include <string.h>
|
9
|
-
#include <math.h>
|
10
|
-
#include <errno.h>
|
11
13
|
|
12
|
-
#include "
|
14
|
+
#include "dump.h"
|
13
15
|
#include "encode.h"
|
16
|
+
#include "mem.h"
|
17
|
+
#include "oj.h"
|
14
18
|
|
15
19
|
// maximum to allocate on the stack, arbitrary limit
|
16
|
-
#define
|
17
|
-
#define MAX_STACK
|
18
|
-
|
19
|
-
#define BATCH_SIZE
|
20
|
+
#define SMALL_JSON 65536
|
21
|
+
#define MAX_STACK 100
|
22
|
+
// #define BATCH_SIZE (4096 / sizeof(struct _leaf) - 1)
|
23
|
+
#define BATCH_SIZE 100
|
24
|
+
|
25
|
+
// Support for compaction
|
26
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
27
|
+
#define mark rb_gc_mark_movable
|
28
|
+
#else
|
29
|
+
#define mark rb_gc_mark
|
30
|
+
#endif
|
20
31
|
|
21
32
|
typedef struct _batch {
|
22
|
-
struct _batch
|
23
|
-
int
|
24
|
-
struct _leaf
|
33
|
+
struct _batch *next;
|
34
|
+
int next_avail;
|
35
|
+
struct _leaf leaves[BATCH_SIZE];
|
25
36
|
} *Batch;
|
26
37
|
|
27
38
|
typedef struct _doc {
|
28
|
-
Leaf
|
29
|
-
Leaf
|
30
|
-
Leaf
|
31
|
-
char
|
32
|
-
unsigned long
|
33
|
-
VALUE
|
34
|
-
Batch
|
35
|
-
struct _batch
|
39
|
+
Leaf data;
|
40
|
+
Leaf *where; // points to current location
|
41
|
+
Leaf where_path[MAX_STACK]; // points to head of path
|
42
|
+
char *json;
|
43
|
+
unsigned long size; // number of leaves/branches in the doc
|
44
|
+
VALUE self;
|
45
|
+
Batch batches;
|
46
|
+
struct _batch batch0;
|
36
47
|
} *Doc;
|
37
48
|
|
38
49
|
typedef struct _parseInfo {
|
39
|
-
char
|
40
|
-
char
|
41
|
-
Doc
|
42
|
-
void
|
50
|
+
char *str; // buffer being read from
|
51
|
+
char *s; // current position in buffer
|
52
|
+
Doc doc;
|
53
|
+
void *stack_min;
|
43
54
|
} *ParseInfo;
|
44
55
|
|
45
|
-
static void
|
46
|
-
static Leaf
|
47
|
-
static void
|
48
|
-
static VALUE
|
49
|
-
static void
|
50
|
-
static void
|
51
|
-
static VALUE
|
52
|
-
static VALUE
|
53
|
-
|
54
|
-
static Leaf
|
55
|
-
static Leaf
|
56
|
-
static Leaf
|
57
|
-
static Leaf
|
58
|
-
static Leaf
|
59
|
-
static Leaf
|
60
|
-
static Leaf
|
61
|
-
static Leaf
|
62
|
-
static void
|
63
|
-
static char*
|
64
|
-
static void
|
65
|
-
|
66
|
-
static VALUE
|
67
|
-
static VALUE
|
68
|
-
static void
|
69
|
-
static int
|
70
|
-
static Leaf
|
71
|
-
static Leaf
|
72
|
-
static void
|
73
|
-
|
74
|
-
|
75
|
-
static void doc_free(Doc doc);
|
76
|
-
static VALUE doc_open(VALUE clas, VALUE str);
|
77
|
-
static VALUE doc_open_file(VALUE clas, VALUE filename);
|
78
|
-
static VALUE doc_where(VALUE self);
|
79
|
-
static VALUE doc_local_key(VALUE self);
|
80
|
-
static VALUE doc_home(VALUE self);
|
81
|
-
static VALUE doc_type(int argc, VALUE *argv, VALUE self);
|
82
|
-
static VALUE doc_fetch(int argc, VALUE *argv, VALUE self);
|
83
|
-
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self);
|
84
|
-
static VALUE doc_move(VALUE self, VALUE str);
|
85
|
-
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self);
|
86
|
-
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self);
|
87
|
-
static VALUE doc_dump(int argc, VALUE *argv, VALUE self);
|
88
|
-
static VALUE doc_size(VALUE self);
|
89
|
-
|
90
|
-
VALUE oj_doc_class = Qundef;
|
56
|
+
static void leaf_init(Leaf leaf, int type);
|
57
|
+
static Leaf leaf_new(Doc doc, int type);
|
58
|
+
static void leaf_append_element(Leaf parent, Leaf element);
|
59
|
+
static VALUE leaf_value(Doc doc, Leaf leaf);
|
60
|
+
static void leaf_fixnum_value(Leaf leaf);
|
61
|
+
static void leaf_float_value(Leaf leaf);
|
62
|
+
static VALUE leaf_array_value(Doc doc, Leaf leaf);
|
63
|
+
static VALUE leaf_hash_value(Doc doc, Leaf leaf);
|
64
|
+
|
65
|
+
static Leaf read_next(ParseInfo pi);
|
66
|
+
static Leaf read_obj(ParseInfo pi);
|
67
|
+
static Leaf read_array(ParseInfo pi);
|
68
|
+
static Leaf read_str(ParseInfo pi);
|
69
|
+
static Leaf read_num(ParseInfo pi);
|
70
|
+
static Leaf read_true(ParseInfo pi);
|
71
|
+
static Leaf read_false(ParseInfo pi);
|
72
|
+
static Leaf read_nil(ParseInfo pi);
|
73
|
+
static void next_non_white(ParseInfo pi);
|
74
|
+
static char *read_quoted_value(ParseInfo pi);
|
75
|
+
static void skip_comment(ParseInfo pi);
|
76
|
+
|
77
|
+
static VALUE protect_open_proc(VALUE x);
|
78
|
+
static VALUE parse_json(VALUE clas, char *json, bool given);
|
79
|
+
static void each_leaf(Doc doc, VALUE self);
|
80
|
+
static int move_step(Doc doc, const char *path, int loc);
|
81
|
+
static Leaf get_doc_leaf(Doc doc, const char *path);
|
82
|
+
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
|
83
|
+
static void each_value(Doc doc, Leaf leaf);
|
84
|
+
|
85
|
+
VALUE oj_doc_class = Qundef;
|
91
86
|
|
92
87
|
// This is only for CentOS 5.4 with Ruby 1.9.3-p0.
|
93
88
|
#ifndef HAVE_STPCPY
|
94
89
|
char *stpcpy(char *dest, const char *src) {
|
95
|
-
size_t
|
90
|
+
size_t cnt = strlen(src);
|
96
91
|
|
97
92
|
strcpy(dest, src);
|
98
93
|
|
@@ -100,89 +95,76 @@ char *stpcpy(char *dest, const char *src) {
|
|
100
95
|
}
|
101
96
|
#endif
|
102
97
|
|
103
|
-
inline static void
|
104
|
-
next_non_white(ParseInfo pi) {
|
98
|
+
inline static void next_non_white(ParseInfo pi) {
|
105
99
|
for (; 1; pi->s++) {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
break;
|
116
|
-
default:
|
117
|
-
return;
|
118
|
-
}
|
100
|
+
switch (*pi->s) {
|
101
|
+
case ' ':
|
102
|
+
case '\t':
|
103
|
+
case '\f':
|
104
|
+
case '\n':
|
105
|
+
case '\r': break;
|
106
|
+
case '/': skip_comment(pi); break;
|
107
|
+
default: return;
|
108
|
+
}
|
119
109
|
}
|
120
110
|
}
|
121
111
|
|
122
|
-
inline static char*
|
123
|
-
|
124
|
-
char
|
125
|
-
char *b = buf + sizeof(buf) - 1;
|
112
|
+
inline static char *ulong_fill(char *s, size_t num) {
|
113
|
+
char buf[32];
|
114
|
+
char *b = buf + sizeof(buf) - 1;
|
126
115
|
|
127
116
|
*b-- = '\0';
|
128
|
-
|
129
|
-
*b = (num % 10) + '0';
|
130
|
-
}
|
131
|
-
b++;
|
117
|
+
b = oj_longlong_to_string((long long)num, false, b);
|
132
118
|
if ('\0' == *b) {
|
133
|
-
|
134
|
-
|
119
|
+
b--;
|
120
|
+
*b = '0';
|
135
121
|
}
|
136
122
|
for (; '\0' != *b; b++, s++) {
|
137
|
-
|
123
|
+
*s = *b;
|
138
124
|
}
|
139
125
|
return s;
|
140
126
|
}
|
141
127
|
|
142
|
-
inline static void
|
143
|
-
|
144
|
-
leaf->
|
145
|
-
leaf->rtype = type;
|
128
|
+
inline static void leaf_init(Leaf leaf, int type) {
|
129
|
+
leaf->next = 0;
|
130
|
+
leaf->rtype = type;
|
146
131
|
leaf->parent_type = T_NONE;
|
147
132
|
switch (type) {
|
148
133
|
case T_ARRAY:
|
149
134
|
case T_HASH:
|
150
|
-
|
151
|
-
|
152
|
-
|
135
|
+
leaf->elements = 0;
|
136
|
+
leaf->value_type = COL_VAL;
|
137
|
+
break;
|
153
138
|
case T_NIL:
|
154
|
-
|
155
|
-
|
156
|
-
|
139
|
+
leaf->value = Qnil;
|
140
|
+
leaf->value_type = RUBY_VAL;
|
141
|
+
break;
|
157
142
|
case T_TRUE:
|
158
|
-
|
159
|
-
|
160
|
-
|
143
|
+
leaf->value = Qtrue;
|
144
|
+
leaf->value_type = RUBY_VAL;
|
145
|
+
break;
|
161
146
|
case T_FALSE:
|
162
|
-
|
163
|
-
|
164
|
-
|
147
|
+
leaf->value = Qfalse;
|
148
|
+
leaf->value_type = RUBY_VAL;
|
149
|
+
break;
|
165
150
|
case T_FIXNUM:
|
166
151
|
case T_FLOAT:
|
167
152
|
case T_STRING:
|
168
|
-
default:
|
169
|
-
leaf->value_type = STR_VAL;
|
170
|
-
break;
|
153
|
+
default: leaf->value_type = STR_VAL; break;
|
171
154
|
}
|
172
155
|
}
|
173
156
|
|
174
|
-
inline static Leaf
|
175
|
-
|
176
|
-
Leaf leaf;
|
157
|
+
inline static Leaf leaf_new(Doc doc, int type) {
|
158
|
+
Leaf leaf;
|
177
159
|
|
178
160
|
if (0 == doc->batches || BATCH_SIZE == doc->batches->next_avail) {
|
179
|
-
|
161
|
+
Batch b = OJ_R_ALLOC(struct _batch);
|
180
162
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
163
|
+
// Initializes all leaves with a NO_VAL value_type
|
164
|
+
memset(b, 0, sizeof(struct _batch));
|
165
|
+
b->next = doc->batches;
|
166
|
+
doc->batches = b;
|
167
|
+
b->next_avail = 0;
|
186
168
|
}
|
187
169
|
leaf = &doc->batches->leaves[doc->batches->next_avail];
|
188
170
|
doc->batches->next_avail++;
|
@@ -191,93 +173,71 @@ leaf_new(Doc doc, int type) {
|
|
191
173
|
return leaf;
|
192
174
|
}
|
193
175
|
|
194
|
-
inline static void
|
195
|
-
leaf_append_element(Leaf parent, Leaf element) {
|
176
|
+
inline static void leaf_append_element(Leaf parent, Leaf element) {
|
196
177
|
if (0 == parent->elements) {
|
197
|
-
|
198
|
-
|
178
|
+
parent->elements = element;
|
179
|
+
element->next = element;
|
199
180
|
} else {
|
200
|
-
|
201
|
-
|
202
|
-
|
181
|
+
element->next = parent->elements->next;
|
182
|
+
parent->elements->next = element;
|
183
|
+
parent->elements = element;
|
203
184
|
}
|
204
185
|
}
|
205
186
|
|
206
|
-
static VALUE
|
207
|
-
leaf_value(Doc doc, Leaf leaf) {
|
187
|
+
static VALUE leaf_value(Doc doc, Leaf leaf) {
|
208
188
|
if (RUBY_VAL != leaf->value_type) {
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
break;
|
225
|
-
case T_STRING:
|
226
|
-
leaf->value = rb_str_new2(leaf->str);
|
227
|
-
leaf->value = oj_encode(leaf->value);
|
228
|
-
leaf->value_type = RUBY_VAL;
|
229
|
-
break;
|
230
|
-
case T_ARRAY:
|
231
|
-
return leaf_array_value(doc, leaf);
|
232
|
-
break;
|
233
|
-
case T_HASH:
|
234
|
-
return leaf_hash_value(doc, leaf);
|
235
|
-
break;
|
236
|
-
default:
|
237
|
-
rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
|
238
|
-
break;
|
239
|
-
}
|
189
|
+
switch (leaf->rtype) {
|
190
|
+
case T_NIL: leaf->value = Qnil; break;
|
191
|
+
case T_TRUE: leaf->value = Qtrue; break;
|
192
|
+
case T_FALSE: leaf->value = Qfalse; break;
|
193
|
+
case T_FIXNUM: leaf_fixnum_value(leaf); break;
|
194
|
+
case T_FLOAT: leaf_float_value(leaf); break;
|
195
|
+
case T_STRING:
|
196
|
+
leaf->value = rb_str_new2(leaf->str);
|
197
|
+
leaf->value = oj_encode(leaf->value);
|
198
|
+
leaf->value_type = RUBY_VAL;
|
199
|
+
break;
|
200
|
+
case T_ARRAY: return leaf_array_value(doc, leaf); break;
|
201
|
+
case T_HASH: return leaf_hash_value(doc, leaf); break;
|
202
|
+
default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
|
203
|
+
}
|
240
204
|
}
|
241
205
|
return leaf->value;
|
242
206
|
}
|
243
207
|
|
244
|
-
inline static Doc
|
245
|
-
|
246
|
-
Doc doc = DATA_PTR(self);
|
208
|
+
inline static Doc self_doc(VALUE self) {
|
209
|
+
Doc doc = DATA_PTR(self);
|
247
210
|
|
248
211
|
if (0 == doc) {
|
249
|
-
|
212
|
+
rb_raise(rb_eIOError, "Document already closed or not open.");
|
250
213
|
}
|
251
214
|
return doc;
|
252
215
|
}
|
253
216
|
|
254
|
-
static void
|
255
|
-
|
256
|
-
pi->s++; // skip first /
|
217
|
+
static void skip_comment(ParseInfo pi) {
|
218
|
+
pi->s++; // skip first /
|
257
219
|
if ('*' == *pi->s) {
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
220
|
+
pi->s++;
|
221
|
+
for (; '\0' != *pi->s; pi->s++) {
|
222
|
+
if ('*' == *pi->s && '/' == *(pi->s + 1)) {
|
223
|
+
pi->s++;
|
224
|
+
return;
|
225
|
+
} else if ('\0' == *pi->s) {
|
226
|
+
raise_error("comment not terminated", pi->str, pi->s);
|
227
|
+
}
|
228
|
+
}
|
267
229
|
} else if ('/' == *pi->s) {
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
}
|
278
|
-
}
|
230
|
+
for (; 1; pi->s++) {
|
231
|
+
switch (*pi->s) {
|
232
|
+
case '\n':
|
233
|
+
case '\r':
|
234
|
+
case '\f':
|
235
|
+
case '\0': return;
|
236
|
+
default: break;
|
237
|
+
}
|
238
|
+
}
|
279
239
|
} else {
|
280
|
-
|
240
|
+
raise_error("invalid comment", pi->str, pi->s);
|
281
241
|
}
|
282
242
|
}
|
283
243
|
|
@@ -287,100 +247,88 @@ skip_comment(ParseInfo pi) {
|
|
287
247
|
#define NUM_MAX (FIXNUM_MAX >> 8)
|
288
248
|
#endif
|
289
249
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
int neg = 0;
|
296
|
-
int big = 0;
|
250
|
+
static void leaf_fixnum_value(Leaf leaf) {
|
251
|
+
char *s = leaf->str;
|
252
|
+
int64_t n = 0;
|
253
|
+
int neg = 0;
|
254
|
+
int big = 0;
|
297
255
|
|
298
256
|
if ('-' == *s) {
|
299
|
-
|
300
|
-
|
257
|
+
s++;
|
258
|
+
neg = 1;
|
301
259
|
} else if ('+' == *s) {
|
302
|
-
|
260
|
+
s++;
|
303
261
|
}
|
304
262
|
for (; '0' <= *s && *s <= '9'; s++) {
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
263
|
+
n = n * 10 + (*s - '0');
|
264
|
+
if (NUM_MAX <= n) {
|
265
|
+
big = 1;
|
266
|
+
}
|
309
267
|
}
|
310
268
|
if (big) {
|
311
|
-
|
269
|
+
char c = *s;
|
312
270
|
|
313
|
-
|
314
|
-
|
315
|
-
|
271
|
+
*s = '\0';
|
272
|
+
leaf->value = rb_cstr_to_inum(leaf->str, 10, 0);
|
273
|
+
*s = c;
|
316
274
|
} else {
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
275
|
+
if (neg) {
|
276
|
+
n = -n;
|
277
|
+
}
|
278
|
+
leaf->value = rb_ll2inum(n);
|
321
279
|
}
|
322
280
|
leaf->value_type = RUBY_VAL;
|
323
281
|
}
|
324
282
|
|
325
|
-
static void
|
326
|
-
|
327
|
-
leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
|
283
|
+
static void leaf_float_value(Leaf leaf) {
|
284
|
+
leaf->value = rb_float_new(rb_cstr_to_dbl(leaf->str, 1));
|
328
285
|
leaf->value_type = RUBY_VAL;
|
329
286
|
}
|
330
287
|
|
331
|
-
static VALUE
|
332
|
-
|
333
|
-
volatile VALUE a = rb_ary_new();
|
288
|
+
static VALUE leaf_array_value(Doc doc, Leaf leaf) {
|
289
|
+
volatile VALUE a = rb_ary_new();
|
334
290
|
|
335
291
|
if (0 != leaf->elements) {
|
336
|
-
|
337
|
-
|
292
|
+
Leaf first = leaf->elements->next;
|
293
|
+
Leaf e = first;
|
338
294
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
295
|
+
do {
|
296
|
+
rb_ary_push(a, leaf_value(doc, e));
|
297
|
+
e = e->next;
|
298
|
+
} while (e != first);
|
343
299
|
}
|
344
300
|
return a;
|
345
301
|
}
|
346
302
|
|
347
|
-
static VALUE
|
348
|
-
|
349
|
-
volatile VALUE h = rb_hash_new();
|
303
|
+
static VALUE leaf_hash_value(Doc doc, Leaf leaf) {
|
304
|
+
volatile VALUE h = rb_hash_new();
|
350
305
|
|
351
306
|
if (0 != leaf->elements) {
|
352
|
-
|
353
|
-
|
354
|
-
|
307
|
+
Leaf first = leaf->elements->next;
|
308
|
+
Leaf e = first;
|
309
|
+
volatile VALUE key;
|
355
310
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
311
|
+
do {
|
312
|
+
key = rb_str_new2(e->key);
|
313
|
+
key = oj_encode(key);
|
314
|
+
rb_hash_aset(h, key, leaf_value(doc, e));
|
315
|
+
e = e->next;
|
316
|
+
} while (e != first);
|
362
317
|
}
|
363
318
|
return h;
|
364
319
|
}
|
365
320
|
|
366
|
-
static Leaf
|
367
|
-
|
368
|
-
Leaf leaf = 0;
|
321
|
+
static Leaf read_next(ParseInfo pi) {
|
322
|
+
Leaf leaf = 0;
|
369
323
|
|
370
|
-
if ((void*)&leaf < pi->stack_min) {
|
371
|
-
|
324
|
+
if ((void *)&leaf < pi->stack_min) {
|
325
|
+
rb_raise(rb_eSysStackError, "JSON is too deeply nested");
|
372
326
|
}
|
373
|
-
next_non_white(pi);
|
327
|
+
next_non_white(pi); // skip white space
|
374
328
|
switch (*pi->s) {
|
375
|
-
case '{':
|
376
|
-
|
377
|
-
|
378
|
-
case '[':
|
379
|
-
leaf = read_array(pi);
|
380
|
-
break;
|
381
|
-
case '"':
|
382
|
-
leaf = read_str(pi);
|
383
|
-
break;
|
329
|
+
case '{': leaf = read_obj(pi); break;
|
330
|
+
case '[': leaf = read_array(pi); break;
|
331
|
+
case '"': leaf = read_str(pi); break;
|
384
332
|
case '+':
|
385
333
|
case '-':
|
386
334
|
case '0':
|
@@ -392,716 +340,724 @@ read_next(ParseInfo pi) {
|
|
392
340
|
case '6':
|
393
341
|
case '7':
|
394
342
|
case '8':
|
395
|
-
case '9':
|
396
|
-
|
397
|
-
|
398
|
-
case '
|
399
|
-
leaf = read_true(pi);
|
400
|
-
break;
|
401
|
-
case 'f':
|
402
|
-
leaf = read_false(pi);
|
403
|
-
break;
|
404
|
-
case 'n':
|
405
|
-
leaf = read_nil(pi);
|
406
|
-
break;
|
343
|
+
case '9': leaf = read_num(pi); break;
|
344
|
+
case 't': leaf = read_true(pi); break;
|
345
|
+
case 'f': leaf = read_false(pi); break;
|
346
|
+
case 'n': leaf = read_nil(pi); break;
|
407
347
|
case '\0':
|
408
|
-
default:
|
409
|
-
break; // returns 0
|
348
|
+
default: break; // returns 0
|
410
349
|
}
|
411
350
|
pi->doc->size++;
|
412
351
|
|
413
352
|
return leaf;
|
414
353
|
}
|
415
354
|
|
416
|
-
static Leaf
|
417
|
-
|
418
|
-
|
419
|
-
char
|
420
|
-
|
421
|
-
Leaf val = 0;
|
355
|
+
static Leaf read_obj(ParseInfo pi) {
|
356
|
+
Leaf h = leaf_new(pi->doc, T_HASH);
|
357
|
+
char *end;
|
358
|
+
const char *key = 0;
|
359
|
+
Leaf val = 0;
|
422
360
|
|
423
361
|
pi->s++;
|
424
362
|
next_non_white(pi);
|
425
363
|
if ('}' == *pi->s) {
|
426
|
-
|
427
|
-
|
364
|
+
pi->s++;
|
365
|
+
return h;
|
428
366
|
}
|
429
367
|
while (1) {
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
368
|
+
next_non_white(pi);
|
369
|
+
key = 0;
|
370
|
+
val = 0;
|
371
|
+
if ('"' != *pi->s || 0 == (key = read_quoted_value(pi))) {
|
372
|
+
raise_error("unexpected character", pi->str, pi->s);
|
373
|
+
}
|
374
|
+
next_non_white(pi);
|
375
|
+
if (':' == *pi->s) {
|
376
|
+
pi->s++;
|
377
|
+
} else {
|
378
|
+
raise_error("invalid format, expected :", pi->str, pi->s);
|
379
|
+
}
|
380
|
+
if (0 == (val = read_next(pi))) {
|
381
|
+
// printf("*** '%s'\n", pi->s);
|
382
|
+
raise_error("unexpected character", pi->str, pi->s);
|
383
|
+
}
|
384
|
+
end = pi->s;
|
385
|
+
val->key = key;
|
386
|
+
val->parent_type = T_HASH;
|
387
|
+
leaf_append_element(h, val);
|
388
|
+
next_non_white(pi);
|
389
|
+
if ('}' == *pi->s) {
|
390
|
+
pi->s++;
|
391
|
+
*end = '\0';
|
392
|
+
break;
|
393
|
+
} else if (',' == *pi->s) {
|
394
|
+
pi->s++;
|
395
|
+
} else {
|
396
|
+
// printf("*** '%s'\n", pi->s);
|
397
|
+
raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
|
398
|
+
}
|
399
|
+
*end = '\0';
|
462
400
|
}
|
463
401
|
return h;
|
464
402
|
}
|
465
403
|
|
466
|
-
static Leaf
|
467
|
-
|
468
|
-
Leaf
|
469
|
-
|
470
|
-
|
471
|
-
int cnt = 0;
|
404
|
+
static Leaf read_array(ParseInfo pi) {
|
405
|
+
Leaf a = leaf_new(pi->doc, T_ARRAY);
|
406
|
+
Leaf e;
|
407
|
+
char *end;
|
408
|
+
int cnt = 0;
|
472
409
|
|
473
410
|
pi->s++;
|
474
411
|
next_non_white(pi);
|
475
412
|
if (']' == *pi->s) {
|
476
|
-
|
477
|
-
|
413
|
+
pi->s++;
|
414
|
+
return a;
|
478
415
|
}
|
479
416
|
while (1) {
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
417
|
+
next_non_white(pi);
|
418
|
+
if (0 == (e = read_next(pi))) {
|
419
|
+
raise_error("unexpected character", pi->str, pi->s);
|
420
|
+
}
|
421
|
+
cnt++;
|
422
|
+
e->index = cnt;
|
423
|
+
e->parent_type = T_ARRAY;
|
424
|
+
leaf_append_element(a, e);
|
425
|
+
end = pi->s;
|
426
|
+
next_non_white(pi);
|
427
|
+
if (',' == *pi->s) {
|
428
|
+
pi->s++;
|
429
|
+
} else if (']' == *pi->s) {
|
430
|
+
pi->s++;
|
431
|
+
*end = '\0';
|
432
|
+
break;
|
433
|
+
} else {
|
434
|
+
raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
|
435
|
+
}
|
436
|
+
*end = '\0';
|
500
437
|
}
|
501
438
|
return a;
|
502
439
|
}
|
503
440
|
|
504
|
-
static Leaf
|
505
|
-
|
506
|
-
Leaf leaf = leaf_new(pi->doc, T_STRING);
|
441
|
+
static Leaf read_str(ParseInfo pi) {
|
442
|
+
Leaf leaf = leaf_new(pi->doc, T_STRING);
|
507
443
|
|
508
444
|
leaf->str = read_quoted_value(pi);
|
509
445
|
|
510
446
|
return leaf;
|
511
447
|
}
|
512
448
|
|
513
|
-
static Leaf
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
Leaf leaf;
|
449
|
+
static Leaf read_num(ParseInfo pi) {
|
450
|
+
char *start = pi->s;
|
451
|
+
int type = T_FIXNUM;
|
452
|
+
Leaf leaf;
|
518
453
|
|
519
454
|
if ('-' == *pi->s) {
|
520
|
-
|
455
|
+
pi->s++;
|
521
456
|
}
|
522
457
|
// digits
|
523
458
|
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
524
459
|
}
|
525
460
|
if ('.' == *pi->s) {
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
461
|
+
type = T_FLOAT;
|
462
|
+
pi->s++;
|
463
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
464
|
+
}
|
530
465
|
}
|
531
466
|
if ('e' == *pi->s || 'E' == *pi->s) {
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
}
|
539
|
-
leaf
|
467
|
+
pi->s++;
|
468
|
+
if ('-' == *pi->s || '+' == *pi->s) {
|
469
|
+
pi->s++;
|
470
|
+
}
|
471
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
472
|
+
}
|
473
|
+
}
|
474
|
+
leaf = leaf_new(pi->doc, type);
|
540
475
|
leaf->str = start;
|
541
476
|
|
542
477
|
return leaf;
|
543
478
|
}
|
544
479
|
|
545
|
-
static Leaf
|
546
|
-
|
547
|
-
Leaf leaf = leaf_new(pi->doc, T_TRUE);
|
480
|
+
static Leaf read_true(ParseInfo pi) {
|
481
|
+
Leaf leaf = leaf_new(pi->doc, T_TRUE);
|
548
482
|
|
549
483
|
pi->s++;
|
550
484
|
if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
|
551
|
-
|
485
|
+
raise_error("invalid format, expected 'true'", pi->str, pi->s);
|
552
486
|
}
|
553
487
|
pi->s += 3;
|
554
488
|
|
555
489
|
return leaf;
|
556
490
|
}
|
557
491
|
|
558
|
-
static Leaf
|
559
|
-
|
560
|
-
Leaf leaf = leaf_new(pi->doc, T_FALSE);
|
492
|
+
static Leaf read_false(ParseInfo pi) {
|
493
|
+
Leaf leaf = leaf_new(pi->doc, T_FALSE);
|
561
494
|
|
562
495
|
pi->s++;
|
563
496
|
if ('a' != *pi->s || 'l' != *(pi->s + 1) || 's' != *(pi->s + 2) || 'e' != *(pi->s + 3)) {
|
564
|
-
|
497
|
+
raise_error("invalid format, expected 'false'", pi->str, pi->s);
|
565
498
|
}
|
566
499
|
pi->s += 4;
|
567
500
|
|
568
501
|
return leaf;
|
569
502
|
}
|
570
503
|
|
571
|
-
static Leaf
|
572
|
-
|
573
|
-
Leaf leaf = leaf_new(pi->doc, T_NIL);
|
504
|
+
static Leaf read_nil(ParseInfo pi) {
|
505
|
+
Leaf leaf = leaf_new(pi->doc, T_NIL);
|
574
506
|
|
575
507
|
pi->s++;
|
576
508
|
if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
|
577
|
-
|
509
|
+
raise_error("invalid format, expected 'nil'", pi->str, pi->s);
|
578
510
|
}
|
579
511
|
pi->s += 3;
|
580
512
|
|
581
513
|
return leaf;
|
582
514
|
}
|
583
515
|
|
584
|
-
static uint32_t
|
585
|
-
|
586
|
-
|
587
|
-
int i;
|
516
|
+
static uint32_t read_4hex(ParseInfo pi, const char *h) {
|
517
|
+
uint32_t b = 0;
|
518
|
+
int i;
|
588
519
|
|
589
520
|
for (i = 0; i < 4; i++, h++) {
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
521
|
+
b = b << 4;
|
522
|
+
if ('0' <= *h && *h <= '9') {
|
523
|
+
b += *h - '0';
|
524
|
+
} else if ('A' <= *h && *h <= 'F') {
|
525
|
+
b += *h - 'A' + 10;
|
526
|
+
} else if ('a' <= *h && *h <= 'f') {
|
527
|
+
b += *h - 'a' + 10;
|
528
|
+
} else {
|
529
|
+
raise_error("invalid hex character", pi->str, pi->s);
|
530
|
+
}
|
600
531
|
}
|
601
532
|
return b;
|
602
533
|
}
|
603
534
|
|
604
|
-
static char*
|
605
|
-
unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
535
|
+
static char *unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
606
536
|
if (0x0000007F >= code) {
|
607
|
-
|
537
|
+
*t++ = (char)code;
|
608
538
|
} else if (0x000007FF >= code) {
|
609
|
-
|
610
|
-
|
539
|
+
*t++ = 0xC0 | (code >> 6);
|
540
|
+
*t++ = 0x80 | (0x3F & code);
|
611
541
|
} else if (0x0000FFFF >= code) {
|
612
|
-
|
613
|
-
|
614
|
-
|
542
|
+
*t++ = 0xE0 | (code >> 12);
|
543
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
544
|
+
*t++ = 0x80 | (0x3F & code);
|
615
545
|
} else if (0x001FFFFF >= code) {
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
546
|
+
*t++ = 0xF0 | (code >> 18);
|
547
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
548
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
549
|
+
*t++ = 0x80 | (0x3F & code);
|
620
550
|
} else if (0x03FFFFFF >= code) {
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
551
|
+
*t++ = 0xF8 | (code >> 24);
|
552
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
553
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
554
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
555
|
+
*t++ = 0x80 | (0x3F & code);
|
626
556
|
} else if (0x7FFFFFFF >= code) {
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
557
|
+
*t++ = 0xFC | (code >> 30);
|
558
|
+
*t++ = 0x80 | ((code >> 24) & 0x3F);
|
559
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
560
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
561
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
562
|
+
*t++ = 0x80 | (0x3F & code);
|
633
563
|
} else {
|
634
|
-
|
564
|
+
raise_error("invalid Unicode character", pi->str, pi->s);
|
635
565
|
}
|
636
566
|
return t;
|
637
567
|
}
|
638
568
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
char
|
645
|
-
char *h = pi->s; // head
|
646
|
-
char *t = h; // tail
|
569
|
+
// Assume the value starts immediately and goes until the quote character is
|
570
|
+
// reached again. Do not read the character after the terminating quote.
|
571
|
+
static char *read_quoted_value(ParseInfo pi) {
|
572
|
+
char *value = 0;
|
573
|
+
char *h = pi->s; // head
|
574
|
+
char *t = h; // tail
|
647
575
|
|
648
|
-
h++;
|
576
|
+
h++; // skip quote character
|
649
577
|
t++;
|
650
578
|
value = h;
|
651
579
|
for (; '"' != *h; h++, t++) {
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
}
|
700
|
-
*t
|
580
|
+
if ('\0' == *h) {
|
581
|
+
pi->s = h;
|
582
|
+
raise_error("quoted string not terminated", pi->str, pi->s);
|
583
|
+
} else if ('\\' == *h) {
|
584
|
+
h++;
|
585
|
+
switch (*h) {
|
586
|
+
case 'n': *t = '\n'; break;
|
587
|
+
case 'r': *t = '\r'; break;
|
588
|
+
case 't': *t = '\t'; break;
|
589
|
+
case 'f': *t = '\f'; break;
|
590
|
+
case 'b': *t = '\b'; break;
|
591
|
+
case '"': *t = '"'; break;
|
592
|
+
case '/': *t = '/'; break;
|
593
|
+
case '\\': *t = '\\'; break;
|
594
|
+
case 'u': {
|
595
|
+
uint32_t code;
|
596
|
+
|
597
|
+
h++;
|
598
|
+
code = read_4hex(pi, h);
|
599
|
+
h += 3;
|
600
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
601
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
602
|
+
uint32_t c2;
|
603
|
+
|
604
|
+
h++;
|
605
|
+
if ('\\' != *h || 'u' != *(h + 1)) {
|
606
|
+
pi->s = h;
|
607
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
608
|
+
}
|
609
|
+
h += 2;
|
610
|
+
c2 = read_4hex(pi, h);
|
611
|
+
h += 3;
|
612
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
613
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
614
|
+
}
|
615
|
+
t = unicode_to_chars(pi, t, code);
|
616
|
+
t--;
|
617
|
+
break;
|
618
|
+
}
|
619
|
+
default:
|
620
|
+
pi->s = h;
|
621
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
622
|
+
break;
|
623
|
+
}
|
624
|
+
} else if (t != h) {
|
625
|
+
*t = *h;
|
626
|
+
}
|
627
|
+
}
|
628
|
+
*t = '\0'; // terminate value
|
701
629
|
pi->s = h + 1;
|
702
630
|
|
703
631
|
return value;
|
704
632
|
}
|
705
633
|
|
706
634
|
// doc support functions
|
707
|
-
inline static void
|
708
|
-
doc_init(Doc doc) {
|
635
|
+
inline static void doc_init(Doc doc) {
|
709
636
|
memset(doc, 0, sizeof(struct _doc));
|
710
|
-
doc->where
|
711
|
-
doc->self
|
637
|
+
doc->where = doc->where_path;
|
638
|
+
doc->self = Qundef;
|
712
639
|
doc->batches = &doc->batch0;
|
713
640
|
}
|
714
641
|
|
715
|
-
static void
|
716
|
-
doc_free(Doc doc) {
|
642
|
+
static void doc_free(Doc doc) {
|
717
643
|
if (0 != doc) {
|
718
|
-
|
644
|
+
Batch b;
|
719
645
|
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
646
|
+
while (0 != (b = doc->batches)) {
|
647
|
+
doc->batches = doc->batches->next;
|
648
|
+
if (&doc->batch0 != b) {
|
649
|
+
OJ_R_FREE(b);
|
650
|
+
}
|
651
|
+
}
|
652
|
+
OJ_R_FREE(doc->json);
|
653
|
+
OJ_R_FREE(doc);
|
727
654
|
}
|
728
655
|
}
|
729
656
|
|
730
|
-
static VALUE
|
731
|
-
|
732
|
-
ParseInfo pi = (ParseInfo)x;
|
657
|
+
static VALUE protect_open_proc(VALUE x) {
|
658
|
+
ParseInfo pi = (ParseInfo)x;
|
733
659
|
|
734
|
-
pi->doc->data
|
660
|
+
pi->doc->data = read_next(pi); // parse
|
735
661
|
*pi->doc->where = pi->doc->data;
|
736
|
-
pi->doc->where
|
662
|
+
pi->doc->where = pi->doc->where_path;
|
737
663
|
if (rb_block_given_p()) {
|
738
|
-
|
664
|
+
return rb_yield(pi->doc->self); // caller processing
|
739
665
|
}
|
740
666
|
return Qnil;
|
741
667
|
}
|
742
668
|
|
743
|
-
static void
|
744
|
-
|
745
|
-
Doc doc = (Doc)x;
|
669
|
+
static void free_doc_cb(void *x) {
|
670
|
+
Doc doc = (Doc)x;
|
746
671
|
|
747
672
|
if (0 != doc) {
|
748
|
-
|
749
|
-
doc_free(doc);
|
673
|
+
doc_free(doc);
|
750
674
|
}
|
751
675
|
}
|
752
676
|
|
753
|
-
static void
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
default:
|
772
|
-
break;
|
677
|
+
static void mark_leaf(Leaf leaf) {
|
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;
|
684
|
+
|
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;
|
692
|
+
|
693
|
+
default: break;
|
694
|
+
}
|
773
695
|
}
|
774
696
|
}
|
775
697
|
|
776
|
-
static void
|
777
|
-
mark_doc(void *ptr) {
|
698
|
+
static void mark_doc(void *ptr) {
|
778
699
|
if (NULL != ptr) {
|
779
|
-
|
700
|
+
Doc doc = (Doc)ptr;
|
780
701
|
|
781
|
-
|
782
|
-
|
702
|
+
mark(doc->self);
|
703
|
+
mark_leaf(doc->data);
|
783
704
|
}
|
784
705
|
}
|
706
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
707
|
+
static void compact_leaf(Leaf leaf) {
|
708
|
+
switch (leaf->value_type) {
|
709
|
+
case COL_VAL:
|
710
|
+
if (NULL != leaf->elements) {
|
711
|
+
Leaf first = leaf->elements->next;
|
712
|
+
Leaf e = first;
|
785
713
|
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
714
|
+
do {
|
715
|
+
compact_leaf(e);
|
716
|
+
e = e->next;
|
717
|
+
} while (e != first);
|
718
|
+
}
|
719
|
+
break;
|
720
|
+
case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
|
793
721
|
|
794
|
-
|
722
|
+
default: break;
|
723
|
+
}
|
724
|
+
}
|
795
725
|
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
726
|
+
static void compact_doc(void *ptr) {
|
727
|
+
Doc doc = (Doc)ptr;
|
728
|
+
|
729
|
+
if (doc) {
|
730
|
+
doc->self = rb_gc_location(doc->self);
|
731
|
+
compact_leaf(doc->data);
|
800
732
|
}
|
801
|
-
|
733
|
+
}
|
734
|
+
#endif
|
735
|
+
|
736
|
+
static const rb_data_type_t oj_doc_type = {
|
737
|
+
"Oj/doc",
|
738
|
+
{
|
739
|
+
mark_doc,
|
740
|
+
free_doc_cb,
|
741
|
+
NULL,
|
742
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
743
|
+
compact_doc,
|
744
|
+
#endif
|
745
|
+
},
|
746
|
+
0,
|
747
|
+
0,
|
748
|
+
};
|
749
|
+
|
750
|
+
static VALUE parse_json(VALUE clas, char *json, bool given) {
|
751
|
+
struct _parseInfo pi;
|
752
|
+
volatile VALUE result = Qnil;
|
753
|
+
Doc doc;
|
754
|
+
int ex = 0;
|
755
|
+
volatile VALUE self;
|
756
|
+
|
757
|
+
doc = OJ_R_ALLOC_N(struct _doc, 1);
|
758
|
+
|
759
|
+
// skip UTF-8 BOM if present
|
802
760
|
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
803
|
-
|
761
|
+
pi.str = json + 3;
|
804
762
|
} else {
|
805
|
-
|
763
|
+
pi.str = json;
|
806
764
|
}
|
807
765
|
pi.s = pi.str;
|
808
766
|
doc_init(doc);
|
809
767
|
pi.doc = doc;
|
810
|
-
#if IS_WINDOWS
|
811
|
-
|
768
|
+
#if IS_WINDOWS || !defined(HAVE_GETRLIMIT)
|
769
|
+
// assume a 1M stack and give half to ruby
|
770
|
+
pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
|
812
771
|
#else
|
813
772
|
{
|
814
|
-
|
773
|
+
struct rlimit lim;
|
815
774
|
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
775
|
+
if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
|
776
|
+
// let 3/4ths of the stack be used only
|
777
|
+
pi.stack_min = (void *)((char *)&lim - (lim.rlim_cur / 4 * 3));
|
778
|
+
} else {
|
779
|
+
pi.stack_min = 0; // indicates not to check stack limit
|
780
|
+
}
|
821
781
|
}
|
822
782
|
#endif
|
823
|
-
// last arg is free func void* func(void*)
|
824
|
-
#ifdef HAVE_RB_DATA_OBJECT_WRAP
|
825
|
-
self = rb_data_object_wrap(clas, doc, mark_doc, free_doc_cb);
|
826
|
-
#else
|
827
|
-
self = rb_data_object_alloc(clas, doc, mark_doc, free_doc_cb);
|
828
|
-
#endif
|
829
|
-
doc->self = self;
|
830
783
|
doc->json = json;
|
831
|
-
|
832
|
-
|
784
|
+
self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
|
785
|
+
doc->self = self;
|
786
|
+
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
833
787
|
if (given || 0 != ex) {
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
788
|
+
DATA_PTR(doc->self) = NULL;
|
789
|
+
// TBD is this needed?
|
790
|
+
/*
|
791
|
+
doc_free(pi.doc);
|
792
|
+
if (0 != ex) { // will jump so caller will not free
|
793
|
+
OJ_R_FREE(json);
|
794
|
+
}
|
795
|
+
*/
|
840
796
|
} else {
|
841
|
-
|
797
|
+
result = doc->self;
|
842
798
|
}
|
843
799
|
if (0 != ex) {
|
844
|
-
|
800
|
+
rb_jump_tag(ex);
|
845
801
|
}
|
846
802
|
return result;
|
847
803
|
}
|
848
804
|
|
849
|
-
static Leaf
|
850
|
-
|
851
|
-
Leaf leaf = *doc->where;
|
805
|
+
static Leaf get_doc_leaf(Doc doc, const char *path) {
|
806
|
+
Leaf leaf = *doc->where;
|
852
807
|
|
853
808
|
if (0 != doc->data && 0 != path) {
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
809
|
+
Leaf stack[MAX_STACK];
|
810
|
+
Leaf *lp;
|
811
|
+
|
812
|
+
if ('/' == *path) {
|
813
|
+
path++;
|
814
|
+
*stack = doc->data;
|
815
|
+
lp = stack;
|
816
|
+
} else if (doc->where == doc->where_path) {
|
817
|
+
*stack = doc->data;
|
818
|
+
lp = stack;
|
819
|
+
} else {
|
820
|
+
size_t cnt = doc->where - doc->where_path;
|
821
|
+
|
822
|
+
if (MAX_STACK <= cnt) {
|
823
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
824
|
+
}
|
825
|
+
memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
|
826
|
+
lp = stack + cnt;
|
827
|
+
}
|
828
|
+
return get_leaf(stack, lp, path);
|
874
829
|
}
|
875
830
|
return leaf;
|
876
831
|
}
|
877
832
|
|
878
|
-
static const char*
|
879
|
-
next_slash(const char *s) {
|
833
|
+
static const char *next_slash(const char *s) {
|
880
834
|
for (; '\0' != *s; s++) {
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
835
|
+
if ('\\' == *s) {
|
836
|
+
s++;
|
837
|
+
if ('\0' == *s) {
|
838
|
+
break;
|
839
|
+
}
|
840
|
+
} else if ('/' == *s) {
|
841
|
+
return s;
|
842
|
+
}
|
889
843
|
}
|
890
844
|
return NULL;
|
891
845
|
}
|
892
846
|
|
893
|
-
static bool
|
894
|
-
key_match(const char *pat, const char *key, int plen) {
|
847
|
+
static bool key_match(const char *pat, const char *key, int plen) {
|
895
848
|
for (; 0 < plen; plen--, pat++, key++) {
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
849
|
+
if ('\\' == *pat) {
|
850
|
+
plen--;
|
851
|
+
pat++;
|
852
|
+
}
|
853
|
+
if (*pat != *key) {
|
854
|
+
return false;
|
855
|
+
}
|
903
856
|
}
|
904
857
|
return '\0' == *key;
|
905
858
|
}
|
906
859
|
|
907
|
-
static Leaf
|
908
|
-
|
909
|
-
Leaf leaf = *lp;
|
860
|
+
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
861
|
+
Leaf leaf = *lp;
|
910
862
|
|
911
863
|
if (MAX_STACK <= lp - stack) {
|
912
|
-
|
864
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
913
865
|
}
|
914
866
|
if ('\0' != *path) {
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
867
|
+
if ('.' == *path && '.' == *(path + 1)) {
|
868
|
+
path += 2;
|
869
|
+
if ('/' == *path) {
|
870
|
+
path++;
|
871
|
+
}
|
872
|
+
if (stack < lp) {
|
873
|
+
leaf = get_leaf(stack, lp - 1, path);
|
874
|
+
} else {
|
875
|
+
return 0;
|
876
|
+
}
|
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) {
|
884
|
+
Leaf first = leaf->elements->next;
|
885
|
+
Leaf e = first;
|
886
|
+
int type = leaf->rtype;
|
887
|
+
|
888
|
+
leaf = NULL;
|
889
|
+
if (T_ARRAY == type) {
|
890
|
+
int cnt = 0;
|
891
|
+
|
892
|
+
for (; '0' <= *path && *path <= '9'; path++) {
|
893
|
+
cnt = cnt * 10 + (*path - '0');
|
894
|
+
}
|
895
|
+
if ('/' == *path) {
|
896
|
+
path++;
|
897
|
+
}
|
898
|
+
do {
|
899
|
+
if (1 >= cnt) {
|
900
|
+
lp++;
|
901
|
+
*lp = e;
|
902
|
+
leaf = get_leaf(stack, lp, path);
|
903
|
+
break;
|
904
|
+
}
|
905
|
+
cnt--;
|
906
|
+
e = e->next;
|
907
|
+
} while (e != first);
|
908
|
+
} else if (T_HASH == type) {
|
909
|
+
const char *key = path;
|
910
|
+
const char *slash = next_slash(path);
|
911
|
+
int klen;
|
912
|
+
|
913
|
+
leaf = NULL;
|
914
|
+
if (0 == slash) {
|
915
|
+
klen = (int)strlen(key);
|
916
|
+
path += klen;
|
917
|
+
} else {
|
918
|
+
klen = (int)(slash - key);
|
919
|
+
path += klen + 1;
|
920
|
+
}
|
921
|
+
do {
|
922
|
+
if (key_match(key, e->key, klen)) {
|
923
|
+
lp++;
|
924
|
+
*lp = e;
|
925
|
+
leaf = get_leaf(stack, lp, path);
|
926
|
+
break;
|
927
|
+
}
|
928
|
+
e = e->next;
|
929
|
+
} while (e != first);
|
930
|
+
}
|
931
|
+
}
|
973
932
|
}
|
974
933
|
return leaf;
|
975
934
|
}
|
976
935
|
|
977
|
-
static void
|
978
|
-
each_leaf(Doc doc, VALUE self) {
|
936
|
+
static void each_leaf(Doc doc, VALUE self) {
|
979
937
|
if (COL_VAL == (*doc->where)->value_type) {
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
938
|
+
if (0 != (*doc->where)->elements) {
|
939
|
+
Leaf first = (*doc->where)->elements->next;
|
940
|
+
Leaf e = first;
|
941
|
+
|
942
|
+
doc->where++;
|
943
|
+
if (MAX_STACK <= doc->where - doc->where_path) {
|
944
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
945
|
+
}
|
946
|
+
do {
|
947
|
+
*doc->where = e;
|
948
|
+
each_leaf(doc, self);
|
949
|
+
e = e->next;
|
950
|
+
} while (e != first);
|
951
|
+
doc->where--;
|
952
|
+
}
|
995
953
|
} else {
|
996
|
-
|
954
|
+
rb_yield(self);
|
997
955
|
}
|
998
956
|
}
|
999
957
|
|
1000
|
-
static int
|
1001
|
-
move_step(Doc doc, const char *path, int loc) {
|
958
|
+
static int move_step(Doc doc, const char *path, int loc) {
|
1002
959
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
1003
|
-
|
960
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
1004
961
|
}
|
1005
962
|
if ('\0' == *path) {
|
1006
|
-
|
963
|
+
loc = 0;
|
1007
964
|
} else {
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
965
|
+
Leaf leaf;
|
966
|
+
|
967
|
+
if (0 == doc->where || 0 == (leaf = *doc->where)) {
|
968
|
+
printf("*** Internal error at %s\n", path);
|
969
|
+
return loc;
|
970
|
+
}
|
971
|
+
if ('.' == *path && '.' == *(path + 1)) {
|
972
|
+
Leaf init = *doc->where;
|
973
|
+
|
974
|
+
path += 2;
|
975
|
+
if (doc->where == doc->where_path) {
|
976
|
+
return loc;
|
977
|
+
}
|
978
|
+
if ('/' == *path) {
|
979
|
+
path++;
|
980
|
+
}
|
981
|
+
*doc->where = 0;
|
982
|
+
doc->where--;
|
983
|
+
loc = move_step(doc, path, loc + 1);
|
984
|
+
if (0 != loc) {
|
985
|
+
*doc->where = init;
|
986
|
+
doc->where++;
|
987
|
+
}
|
988
|
+
} else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
|
989
|
+
Leaf first = leaf->elements->next;
|
990
|
+
Leaf e = first;
|
991
|
+
|
992
|
+
if (T_ARRAY == leaf->rtype) {
|
993
|
+
int cnt = 0;
|
994
|
+
|
995
|
+
for (; '0' <= *path && *path <= '9'; path++) {
|
996
|
+
cnt = cnt * 10 + (*path - '0');
|
997
|
+
}
|
998
|
+
if ('/' == *path) {
|
999
|
+
path++;
|
1000
|
+
} else if ('\0' != *path) {
|
1001
|
+
return loc;
|
1002
|
+
}
|
1003
|
+
do {
|
1004
|
+
if (1 >= cnt) {
|
1005
|
+
doc->where++;
|
1006
|
+
*doc->where = e;
|
1007
|
+
loc = move_step(doc, path, loc + 1);
|
1008
|
+
if (0 != loc) {
|
1009
|
+
*doc->where = 0;
|
1010
|
+
doc->where--;
|
1011
|
+
}
|
1012
|
+
break;
|
1013
|
+
}
|
1014
|
+
cnt--;
|
1015
|
+
e = e->next;
|
1016
|
+
} while (e != first);
|
1017
|
+
} else if (T_HASH == leaf->rtype) {
|
1018
|
+
const char *key = path;
|
1019
|
+
const char *slash = next_slash(path);
|
1020
|
+
int klen;
|
1021
|
+
|
1022
|
+
if (0 == slash) {
|
1023
|
+
klen = (int)strlen(key);
|
1024
|
+
path += klen;
|
1025
|
+
} else {
|
1026
|
+
klen = (int)(slash - key);
|
1027
|
+
path += klen + 1;
|
1028
|
+
}
|
1029
|
+
do {
|
1030
|
+
if (key_match(key, e->key, klen)) {
|
1031
|
+
doc->where++;
|
1032
|
+
*doc->where = e;
|
1033
|
+
loc = move_step(doc, path, loc + 1);
|
1034
|
+
if (0 != loc) {
|
1035
|
+
*doc->where = 0;
|
1036
|
+
doc->where--;
|
1037
|
+
}
|
1038
|
+
break;
|
1039
|
+
}
|
1040
|
+
e = e->next;
|
1041
|
+
} while (e != first);
|
1042
|
+
}
|
1043
|
+
}
|
1087
1044
|
}
|
1088
1045
|
return loc;
|
1089
1046
|
}
|
1090
1047
|
|
1091
|
-
static void
|
1092
|
-
each_value(Doc doc, Leaf leaf) {
|
1048
|
+
static void each_value(Doc doc, Leaf leaf) {
|
1093
1049
|
if (COL_VAL == leaf->value_type) {
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1050
|
+
if (0 != leaf->elements) {
|
1051
|
+
Leaf first = leaf->elements->next;
|
1052
|
+
Leaf e = first;
|
1053
|
+
|
1054
|
+
do {
|
1055
|
+
each_value(doc, e);
|
1056
|
+
e = e->next;
|
1057
|
+
} while (e != first);
|
1058
|
+
}
|
1103
1059
|
} else {
|
1104
|
-
|
1060
|
+
rb_yield(leaf_value(doc, leaf));
|
1105
1061
|
}
|
1106
1062
|
}
|
1107
1063
|
|
@@ -1116,7 +1072,8 @@ each_value(Doc doc, Leaf leaf) {
|
|
1116
1072
|
*
|
1117
1073
|
* @param [String] json JSON document string
|
1118
1074
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1119
|
-
* @yieldreturn [Object] returns the result of the yield as the result of the
|
1075
|
+
* @yieldreturn [Object] returns the result of the yield as the result of the
|
1076
|
+
* method call
|
1120
1077
|
* @example
|
1121
1078
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
1122
1079
|
* # or as an alternative
|
@@ -1124,33 +1081,24 @@ each_value(Doc doc, Leaf leaf) {
|
|
1124
1081
|
* doc.size() #=> 4
|
1125
1082
|
* doc.close()
|
1126
1083
|
*/
|
1127
|
-
static VALUE
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
int given = rb_block_given_p();
|
1133
|
-
int allocate;
|
1084
|
+
static VALUE doc_open(VALUE clas, VALUE str) {
|
1085
|
+
char *json;
|
1086
|
+
size_t len;
|
1087
|
+
volatile VALUE obj;
|
1088
|
+
int given = rb_block_given_p();
|
1134
1089
|
|
1135
1090
|
Check_Type(str, T_STRING);
|
1136
|
-
len
|
1137
|
-
|
1138
|
-
|
1139
|
-
json = ALLOC_N(char, len);
|
1140
|
-
} else {
|
1141
|
-
json = ALLOCA_N(char, len);
|
1142
|
-
}
|
1143
|
-
// It should not be necessaary to stop GC but if it is not stopped and a
|
1144
|
-
// large string is parsed that string is corrupted or freed during
|
1145
|
-
// parsing. I'm not sure what is going on exactly but disabling GC avoids
|
1146
|
-
// the issue.
|
1147
|
-
rb_gc_disable();
|
1091
|
+
len = (int)RSTRING_LEN(str) + 1;
|
1092
|
+
json = OJ_R_ALLOC_N(char, len);
|
1093
|
+
|
1148
1094
|
memcpy(json, StringValuePtr(str), len);
|
1149
|
-
obj = parse_json(clas, json, given
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1095
|
+
obj = parse_json(clas, json, given);
|
1096
|
+
// TBD is this needed
|
1097
|
+
/*
|
1098
|
+
if (given) {
|
1099
|
+
OJ_R_FREE(json);
|
1153
1100
|
}
|
1101
|
+
*/
|
1154
1102
|
return obj;
|
1155
1103
|
}
|
1156
1104
|
|
@@ -1163,7 +1111,8 @@ doc_open(VALUE clas, VALUE str) {
|
|
1163
1111
|
*
|
1164
1112
|
* @param [String] filename name of file that contains a JSON document
|
1165
1113
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1166
|
-
* @yieldreturn [Object] returns the result of the yield as the result of the
|
1114
|
+
* @yieldreturn [Object] returns the result of the yield as the result of the
|
1115
|
+
* method call
|
1167
1116
|
* @example
|
1168
1117
|
* File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
|
1169
1118
|
* Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
|
@@ -1172,65 +1121,59 @@ doc_open(VALUE clas, VALUE str) {
|
|
1172
1121
|
* doc.size() #=> 4
|
1173
1122
|
* doc.close()
|
1174
1123
|
*/
|
1175
|
-
static VALUE
|
1176
|
-
|
1177
|
-
char
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
int allocate;
|
1184
|
-
|
1185
|
-
Check_Type(filename, T_STRING);
|
1124
|
+
static VALUE doc_open_file(VALUE clas, VALUE filename) {
|
1125
|
+
char *path;
|
1126
|
+
char *json;
|
1127
|
+
FILE *f;
|
1128
|
+
size_t len;
|
1129
|
+
volatile VALUE obj;
|
1130
|
+
int given = rb_block_given_p();
|
1131
|
+
|
1186
1132
|
path = StringValuePtr(filename);
|
1187
1133
|
if (0 == (f = fopen(path, "r"))) {
|
1188
|
-
|
1134
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
1189
1135
|
}
|
1190
1136
|
fseek(f, 0, SEEK_END);
|
1191
|
-
len
|
1192
|
-
|
1193
|
-
|
1194
|
-
json = ALLOC_N(char, len + 1);
|
1195
|
-
} else {
|
1196
|
-
json = ALLOCA_N(char, len + 1);
|
1197
|
-
}
|
1137
|
+
len = ftell(f);
|
1138
|
+
json = OJ_R_ALLOC_N(char, len + 1);
|
1139
|
+
|
1198
1140
|
fseek(f, 0, SEEK_SET);
|
1199
1141
|
if (len != fread(json, 1, len, f)) {
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1142
|
+
fclose(f);
|
1143
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
|
1144
|
+
"Failed to read %lu bytes from %s.",
|
1145
|
+
(unsigned long)len,
|
1146
|
+
path);
|
1203
1147
|
}
|
1204
1148
|
fclose(f);
|
1205
1149
|
json[len] = '\0';
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
if (given
|
1210
|
-
|
1150
|
+
obj = parse_json(clas, json, given);
|
1151
|
+
// TBD is this needed
|
1152
|
+
/*
|
1153
|
+
if (given) {
|
1154
|
+
OJ_R_FREE(json);
|
1211
1155
|
}
|
1156
|
+
*/
|
1212
1157
|
return obj;
|
1213
1158
|
}
|
1214
1159
|
|
1215
|
-
static int
|
1216
|
-
|
1217
|
-
int cnt = 0;
|
1160
|
+
static int esc_strlen(const char *s) {
|
1161
|
+
int cnt = 0;
|
1218
1162
|
|
1219
1163
|
for (; '\0' != *s; s++, cnt++) {
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1164
|
+
if ('/' == *s) {
|
1165
|
+
cnt++;
|
1166
|
+
}
|
1223
1167
|
}
|
1224
1168
|
return cnt;
|
1225
1169
|
}
|
1226
1170
|
|
1227
|
-
static char*
|
1228
|
-
append_key(char *p, const char *key) {
|
1171
|
+
static char *append_key(char *p, const char *key) {
|
1229
1172
|
for (; '\0' != *key; p++, key++) {
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1173
|
+
if ('/' == *key) {
|
1174
|
+
*p++ = '\\';
|
1175
|
+
}
|
1176
|
+
*p = *key;
|
1234
1177
|
}
|
1235
1178
|
return p;
|
1236
1179
|
}
|
@@ -1239,68 +1182,85 @@ append_key(char *p, const char *key) {
|
|
1239
1182
|
* @see Oj::Doc.open
|
1240
1183
|
*/
|
1241
1184
|
|
1242
|
-
/* @overload where
|
1185
|
+
/* @overload where() => String
|
1243
1186
|
*
|
1244
1187
|
* Returns a String that describes the absolute path to the current location
|
1245
1188
|
* in the JSON document.
|
1246
1189
|
*/
|
1247
|
-
static VALUE
|
1248
|
-
|
1249
|
-
Doc doc = self_doc(self);
|
1190
|
+
static VALUE doc_where(VALUE self) {
|
1191
|
+
Doc doc = self_doc(self);
|
1250
1192
|
|
1251
1193
|
if (0 == *doc->where_path || doc->where == doc->where_path) {
|
1252
|
-
|
1194
|
+
return oj_slash_string;
|
1253
1195
|
} else {
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1196
|
+
Leaf *lp;
|
1197
|
+
Leaf leaf;
|
1198
|
+
size_t size = 3; // leading / and terminating \0
|
1199
|
+
char *path;
|
1200
|
+
char *p;
|
1201
|
+
|
1202
|
+
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
1203
|
+
leaf = *lp;
|
1204
|
+
if (T_HASH == leaf->parent_type) {
|
1205
|
+
size += esc_strlen((*lp)->key) + 1;
|
1206
|
+
} else if (T_ARRAY == leaf->parent_type) {
|
1207
|
+
size += ((*lp)->index < 100) ? 3 : 11;
|
1208
|
+
}
|
1209
|
+
}
|
1210
|
+
path = ALLOCA_N(char, size);
|
1211
|
+
p = path;
|
1212
|
+
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
1213
|
+
leaf = *lp;
|
1214
|
+
if (T_HASH == leaf->parent_type) {
|
1215
|
+
p = append_key(p, (*lp)->key);
|
1216
|
+
} else if (T_ARRAY == leaf->parent_type) {
|
1217
|
+
p = ulong_fill(p, (*lp)->index);
|
1218
|
+
}
|
1219
|
+
*p++ = '/';
|
1220
|
+
}
|
1221
|
+
*--p = '\0';
|
1222
|
+
|
1223
|
+
return rb_str_new(path, p - path);
|
1282
1224
|
}
|
1283
1225
|
}
|
1284
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
|
+
|
1285
1245
|
/* @overload local_key() => String, Fixnum, nil
|
1286
1246
|
*
|
1287
1247
|
* Returns the final key to the current location.
|
1288
1248
|
* @example
|
1289
|
-
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() }
|
1290
|
-
* Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
|
1291
|
-
*
|
1249
|
+
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
|
1250
|
+
* Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
|
1251
|
+
* "one" Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() }
|
1252
|
+
* #=> nil
|
1292
1253
|
*/
|
1293
|
-
static VALUE
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
volatile VALUE key = Qnil;
|
1254
|
+
static VALUE doc_local_key(VALUE self) {
|
1255
|
+
Doc doc = self_doc(self);
|
1256
|
+
Leaf leaf = *doc->where;
|
1257
|
+
volatile VALUE key = Qnil;
|
1298
1258
|
|
1299
1259
|
if (T_HASH == leaf->parent_type) {
|
1300
|
-
|
1301
|
-
|
1260
|
+
key = rb_str_new2(leaf->key);
|
1261
|
+
key = oj_encode(key);
|
1302
1262
|
} else if (T_ARRAY == leaf->parent_type) {
|
1303
|
-
|
1263
|
+
key = LONG2NUM(leaf->index);
|
1304
1264
|
}
|
1305
1265
|
return key;
|
1306
1266
|
}
|
@@ -1310,14 +1270,14 @@ doc_local_key(VALUE self) {
|
|
1310
1270
|
* Moves the document marker or location to the hoot or home position. The
|
1311
1271
|
* same operation can be performed with a Oj::Doc.move('/').
|
1312
1272
|
* @example
|
1313
|
-
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
|
1273
|
+
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
|
1274
|
+
* #=> '/'
|
1314
1275
|
*/
|
1315
|
-
static VALUE
|
1316
|
-
|
1317
|
-
Doc doc = self_doc(self);
|
1276
|
+
static VALUE doc_home(VALUE self) {
|
1277
|
+
Doc doc = self_doc(self);
|
1318
1278
|
|
1319
1279
|
*doc->where_path = doc->data;
|
1320
|
-
doc->where
|
1280
|
+
doc->where = doc->where_path;
|
1321
1281
|
|
1322
1282
|
return oj_slash_string;
|
1323
1283
|
}
|
@@ -1333,76 +1293,91 @@ doc_home(VALUE self) {
|
|
1333
1293
|
* Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
|
1334
1294
|
* Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
|
1335
1295
|
*/
|
1336
|
-
static VALUE
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
VALUE type = Qnil;
|
1296
|
+
static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
|
1297
|
+
Doc doc = self_doc(self);
|
1298
|
+
Leaf leaf;
|
1299
|
+
const char *path = 0;
|
1300
|
+
VALUE type = Qnil;
|
1342
1301
|
|
1343
1302
|
if (1 <= argc) {
|
1344
|
-
|
1345
|
-
path = StringValuePtr(*argv);
|
1303
|
+
path = StringValuePtr(*argv);
|
1346
1304
|
}
|
1347
1305
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
case T_ARRAY: type = rb_cArray; break;
|
1360
|
-
case T_HASH: type = rb_cHash; break;
|
1361
|
-
default: break;
|
1362
|
-
}
|
1306
|
+
switch (leaf->rtype) {
|
1307
|
+
case T_NIL: type = rb_cNilClass; break;
|
1308
|
+
case T_TRUE: type = rb_cTrueClass; break;
|
1309
|
+
case T_FALSE: type = rb_cFalseClass; break;
|
1310
|
+
case T_STRING: type = rb_cString; break;
|
1311
|
+
case T_FIXNUM: type = rb_cInteger; break;
|
1312
|
+
case T_FLOAT: type = rb_cFloat; break;
|
1313
|
+
case T_ARRAY: type = rb_cArray; break;
|
1314
|
+
case T_HASH: type = rb_cHash; break;
|
1315
|
+
default: break;
|
1316
|
+
}
|
1363
1317
|
}
|
1364
1318
|
return type;
|
1365
1319
|
}
|
1366
1320
|
|
1367
|
-
/* @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,
|
1322
|
+
* Hash
|
1368
1323
|
*
|
1369
1324
|
* Returns the value at the location identified by the path or the current
|
1370
1325
|
* location if the path is nil or not provided. This method will create and
|
1371
1326
|
* return an Array or Hash if that is the type of Object at the location
|
1372
1327
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
1373
|
-
* document.
|
1328
|
+
* document. If a default is provided that is used if no value if found.
|
1374
1329
|
* @param [String] path path to the location to get the type of if provided
|
1375
1330
|
* @example
|
1376
1331
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
1377
1332
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
|
1378
1333
|
*/
|
1379
|
-
static VALUE
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
const char *path = 0;
|
1334
|
+
static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
|
1335
|
+
Doc doc;
|
1336
|
+
Leaf leaf;
|
1337
|
+
volatile VALUE val = Qnil;
|
1338
|
+
const char *path = 0;
|
1385
1339
|
|
1386
1340
|
doc = self_doc(self);
|
1387
1341
|
if (1 <= argc) {
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
}
|
1342
|
+
path = StringValuePtr(*argv);
|
1343
|
+
if (2 == argc) {
|
1344
|
+
val = argv[1];
|
1345
|
+
}
|
1393
1346
|
}
|
1394
1347
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1395
|
-
|
1348
|
+
val = leaf_value(doc, leaf);
|
1396
1349
|
}
|
1397
1350
|
return val;
|
1398
1351
|
}
|
1399
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
|
+
|
1400
1374
|
/* @overload each_leaf(path=nil) => nil
|
1401
1375
|
*
|
1402
1376
|
* Yields to the provided block for each leaf node with the identified
|
1403
1377
|
* location of the JSON document as the root. The parameter passed to the
|
1404
1378
|
* block on yield is the Doc instance after moving to the child location.
|
1405
|
-
* @param [String] path if provided it identified the top of the branch to
|
1379
|
+
* @param [String] path if provided it identified the top of the branch to
|
1380
|
+
* process the leaves of
|
1406
1381
|
* @yieldparam [Doc] Doc at the child location
|
1407
1382
|
* @example
|
1408
1383
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1412,36 +1387,34 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1412
1387
|
* }
|
1413
1388
|
* #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
|
1414
1389
|
*/
|
1415
|
-
static VALUE
|
1416
|
-
doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
1390
|
+
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
1417
1391
|
if (rb_block_given_p()) {
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
}
|
1392
|
+
Leaf save_path[MAX_STACK];
|
1393
|
+
Doc doc = self_doc(self);
|
1394
|
+
const char *path = 0;
|
1395
|
+
size_t wlen;
|
1396
|
+
|
1397
|
+
wlen = doc->where - doc->where_path;
|
1398
|
+
if (0 < wlen) {
|
1399
|
+
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
1400
|
+
}
|
1401
|
+
if (1 <= argc) {
|
1402
|
+
path = StringValuePtr(*argv);
|
1403
|
+
if ('/' == *path) {
|
1404
|
+
doc->where = doc->where_path;
|
1405
|
+
path++;
|
1406
|
+
}
|
1407
|
+
if (0 != move_step(doc, path, 1)) {
|
1408
|
+
if (0 < wlen) {
|
1409
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1410
|
+
}
|
1411
|
+
return Qnil;
|
1412
|
+
}
|
1413
|
+
}
|
1414
|
+
each_leaf(doc, self);
|
1415
|
+
if (0 < wlen) {
|
1416
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1417
|
+
}
|
1445
1418
|
}
|
1446
1419
|
return Qnil;
|
1447
1420
|
}
|
@@ -1452,22 +1425,21 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1452
1425
|
* path or a relative path.
|
1453
1426
|
* @param [String] path path to the location to move to
|
1454
1427
|
* @example
|
1455
|
-
* Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
|
1428
|
+
* Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
|
1429
|
+
* "/one/2"
|
1456
1430
|
*/
|
1457
|
-
static VALUE
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
int loc;
|
1431
|
+
static VALUE doc_move(VALUE self, VALUE str) {
|
1432
|
+
Doc doc = self_doc(self);
|
1433
|
+
const char *path;
|
1434
|
+
int loc;
|
1462
1435
|
|
1463
|
-
Check_Type(str, T_STRING);
|
1464
1436
|
path = StringValuePtr(str);
|
1465
1437
|
if ('/' == *path) {
|
1466
|
-
|
1467
|
-
|
1438
|
+
doc->where = doc->where_path;
|
1439
|
+
path++;
|
1468
1440
|
}
|
1469
1441
|
if (0 != (loc = move_step(doc, path, 1))) {
|
1470
|
-
|
1442
|
+
rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
|
1471
1443
|
}
|
1472
1444
|
return Qnil;
|
1473
1445
|
}
|
@@ -1478,7 +1450,8 @@ doc_move(VALUE self, VALUE str) {
|
|
1478
1450
|
* identified location of the JSON document as the root. The parameter passed
|
1479
1451
|
* to the block on yield is the Doc instance after moving to the child
|
1480
1452
|
* location.
|
1481
|
-
* @param [String] path if provided it identified the top of the branch to
|
1453
|
+
* @param [String] path if provided it identified the top of the branch to
|
1454
|
+
* process the children of
|
1482
1455
|
* @yieldparam [Doc] Doc at the child location
|
1483
1456
|
* @example
|
1484
1457
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1488,46 +1461,50 @@ doc_move(VALUE self, VALUE str) {
|
|
1488
1461
|
* }
|
1489
1462
|
* #=> ["/2/1", "/2/2"]
|
1490
1463
|
*/
|
1491
|
-
static VALUE
|
1492
|
-
doc_each_child(int argc, VALUE *argv, VALUE self) {
|
1464
|
+
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
1493
1465
|
if (rb_block_given_p()) {
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1466
|
+
Leaf save_path[MAX_STACK];
|
1467
|
+
Doc doc = self_doc(self);
|
1468
|
+
const char *path = 0;
|
1469
|
+
size_t wlen;
|
1470
|
+
Leaf *where_orig = doc->where;
|
1471
|
+
|
1472
|
+
wlen = doc->where - doc->where_path;
|
1473
|
+
if (0 < wlen) {
|
1474
|
+
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
1475
|
+
}
|
1476
|
+
if (1 <= argc) {
|
1477
|
+
path = StringValuePtr(*argv);
|
1478
|
+
if ('/' == *path) {
|
1479
|
+
doc->where = doc->where_path;
|
1480
|
+
path++;
|
1481
|
+
}
|
1482
|
+
if (0 != move_step(doc, path, 1)) {
|
1483
|
+
if (0 < wlen) {
|
1484
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1485
|
+
}
|
1486
|
+
doc->where = where_orig;
|
1487
|
+
return Qnil;
|
1488
|
+
}
|
1489
|
+
}
|
1490
|
+
if (NULL == doc->where || NULL == *doc->where) {
|
1491
|
+
return Qnil;
|
1492
|
+
}
|
1493
|
+
if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
|
1494
|
+
Leaf first = (*doc->where)->elements->next;
|
1495
|
+
Leaf e = first;
|
1496
|
+
|
1497
|
+
doc->where++;
|
1498
|
+
do {
|
1499
|
+
*doc->where = e;
|
1500
|
+
rb_yield(self);
|
1501
|
+
e = e->next;
|
1502
|
+
} while (e != first);
|
1503
|
+
}
|
1504
|
+
if (0 < wlen) {
|
1505
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
1506
|
+
}
|
1507
|
+
doc->where = where_orig;
|
1531
1508
|
}
|
1532
1509
|
return Qnil;
|
1533
1510
|
}
|
@@ -1538,7 +1515,8 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1538
1515
|
* of the JSON document. The parameter passed to the block on yield is the
|
1539
1516
|
* value of the leaf. Only those leaves below the element specified by the
|
1540
1517
|
* path parameter are processed.
|
1541
|
-
* @param [String] path if provided it identified the top of the branch to
|
1518
|
+
* @param [String] path if provided it identified the top of the branch to
|
1519
|
+
* process the leaf values of
|
1542
1520
|
* @yieldparam [Object] val each leaf value
|
1543
1521
|
* @example
|
1544
1522
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1555,20 +1533,18 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1555
1533
|
* }
|
1556
1534
|
* #=> [2, 1]
|
1557
1535
|
*/
|
1558
|
-
static VALUE
|
1559
|
-
doc_each_value(int argc, VALUE *argv, VALUE self) {
|
1536
|
+
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
1560
1537
|
if (rb_block_given_p()) {
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
}
|
1538
|
+
Doc doc = self_doc(self);
|
1539
|
+
const char *path = 0;
|
1540
|
+
Leaf leaf;
|
1541
|
+
|
1542
|
+
if (1 <= argc) {
|
1543
|
+
path = StringValuePtr(*argv);
|
1544
|
+
}
|
1545
|
+
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1546
|
+
each_value(doc, leaf);
|
1547
|
+
}
|
1572
1548
|
}
|
1573
1549
|
return Qnil;
|
1574
1550
|
}
|
@@ -1577,52 +1553,48 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1577
1553
|
*
|
1578
1554
|
* Dumps the document or nodes to a new JSON document. It uses the default
|
1579
1555
|
* options for generating the JSON.
|
1580
|
-
* @param path [String] if provided it identified the top of the branch to
|
1581
|
-
*
|
1556
|
+
* @param path [String] if provided it identified the top of the branch to
|
1557
|
+
* dump to JSON
|
1558
|
+
* @param filename [String] if provided it is the filename to write the output
|
1559
|
+
* to
|
1582
1560
|
* @example
|
1583
1561
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
1584
1562
|
* doc.dump('/2')
|
1585
1563
|
* }
|
1586
1564
|
* #=> "[2,1]"
|
1587
1565
|
*/
|
1588
|
-
static VALUE
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
const char
|
1593
|
-
const char *filename = 0;
|
1566
|
+
static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
|
1567
|
+
Doc doc = self_doc(self);
|
1568
|
+
Leaf leaf;
|
1569
|
+
const char *path = 0;
|
1570
|
+
const char *filename = 0;
|
1594
1571
|
|
1595
1572
|
if (1 <= argc) {
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
filename = StringValuePtr(argv[1]);
|
1603
|
-
}
|
1573
|
+
if (Qnil != *argv) {
|
1574
|
+
path = StringValuePtr(*argv);
|
1575
|
+
}
|
1576
|
+
if (2 <= argc) {
|
1577
|
+
filename = StringValuePtr(argv[1]);
|
1578
|
+
}
|
1604
1579
|
}
|
1605
1580
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
rjson = Qnil;
|
1624
|
-
}
|
1625
|
-
return rjson;
|
1581
|
+
volatile VALUE rjson;
|
1582
|
+
|
1583
|
+
if (0 == filename) {
|
1584
|
+
struct _out out;
|
1585
|
+
|
1586
|
+
oj_out_init(&out);
|
1587
|
+
|
1588
|
+
out.omit_nil = oj_default_options.dump_opts.omit_nil;
|
1589
|
+
oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
|
1590
|
+
rjson = rb_str_new2(out.buf);
|
1591
|
+
|
1592
|
+
oj_out_free(&out);
|
1593
|
+
} else {
|
1594
|
+
oj_write_leaf_to_file(leaf, filename, &oj_default_options);
|
1595
|
+
rjson = Qnil;
|
1596
|
+
}
|
1597
|
+
return rjson;
|
1626
1598
|
}
|
1627
1599
|
return Qnil;
|
1628
1600
|
}
|
@@ -1635,9 +1607,10 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1635
1607
|
* @example
|
1636
1608
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
1637
1609
|
*/
|
1638
|
-
static VALUE
|
1639
|
-
|
1640
|
-
|
1610
|
+
static VALUE doc_size(VALUE self) {
|
1611
|
+
Doc d;
|
1612
|
+
TypedData_Get_Struct(self, struct _doc, &oj_doc_type, d);
|
1613
|
+
return ULONG2NUM(d->size);
|
1641
1614
|
}
|
1642
1615
|
|
1643
1616
|
/* @overload close() => nil
|
@@ -1649,16 +1622,13 @@ doc_size(VALUE self) {
|
|
1649
1622
|
* doc.size() #=> 4
|
1650
1623
|
* doc.close()
|
1651
1624
|
*/
|
1652
|
-
static VALUE
|
1653
|
-
|
1654
|
-
Doc doc = self_doc(self);
|
1625
|
+
static VALUE doc_close(VALUE self) {
|
1626
|
+
Doc doc = self_doc(self);
|
1655
1627
|
|
1656
1628
|
rb_gc_unregister_address(&doc->self);
|
1657
|
-
DATA_PTR(doc->self) =
|
1629
|
+
DATA_PTR(doc->self) = NULL;
|
1658
1630
|
if (0 != doc) {
|
1659
|
-
|
1660
|
-
doc_free(doc);
|
1661
|
-
xfree(doc);
|
1631
|
+
doc_free(doc);
|
1662
1632
|
}
|
1663
1633
|
return Qnil;
|
1664
1634
|
}
|
@@ -1667,8 +1637,7 @@ doc_close(VALUE self) {
|
|
1667
1637
|
Oj = rb_define_module("Oj");
|
1668
1638
|
#endif
|
1669
1639
|
|
1670
|
-
static VALUE
|
1671
|
-
doc_not_implemented(VALUE self) {
|
1640
|
+
static VALUE doc_not_implemented(VALUE self) {
|
1672
1641
|
rb_raise(rb_eNotImpError, "Not implemented.");
|
1673
1642
|
return Qnil;
|
1674
1643
|
}
|
@@ -1709,27 +1678,28 @@ doc_not_implemented(VALUE self) {
|
|
1709
1678
|
* # move and get value
|
1710
1679
|
* Oj::Doc.open(json) do |doc|
|
1711
1680
|
* doc.move('/1/two')
|
1712
|
-
* # doc location is now at the 'two' element of the hash that is the first
|
1713
|
-
*
|
1714
|
-
* end
|
1681
|
+
* # doc location is now at the 'two' element of the hash that is the first
|
1682
|
+
* element of the array. doc.fetch() end
|
1715
1683
|
* #=> 2
|
1716
1684
|
*
|
1717
|
-
* # Now try again using a path to Oj::Doc.fetch() directly and not using a
|
1718
|
-
*
|
1719
|
-
* doc.fetch('/2/three') #=> 3
|
1720
|
-
* doc.close()
|
1685
|
+
* # Now try again using a path to Oj::Doc.fetch() directly and not using a
|
1686
|
+
* block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
|
1721
1687
|
*/
|
1722
|
-
void
|
1723
|
-
oj_init_doc() {
|
1688
|
+
void oj_init_doc(void) {
|
1724
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);
|
1725
1692
|
rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
|
1726
1693
|
rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
|
1727
1694
|
rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
|
1728
|
-
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);
|
1729
1698
|
rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
|
1730
1699
|
rb_define_method(oj_doc_class, "home", doc_home, 0);
|
1731
1700
|
rb_define_method(oj_doc_class, "type", doc_type, -1);
|
1732
1701
|
rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
|
1702
|
+
rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
|
1733
1703
|
rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
|
1734
1704
|
rb_define_method(oj_doc_class, "move", doc_move, 1);
|
1735
1705
|
rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);
|