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