oj 3.9.1 → 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 +4 -4
- data/CHANGELOG.md +1452 -0
- data/README.md +21 -6
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +50 -68
- 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 +9 -36
- data/ext/oj/circarray.c +38 -42
- data/ext/oj/circarray.h +12 -13
- data/ext/oj/code.c +158 -179
- data/ext/oj/code.h +20 -22
- data/ext/oj/compat.c +145 -205
- data/ext/oj/custom.c +740 -880
- data/ext/oj/debug.c +126 -0
- data/ext/oj/dump.c +1145 -844
- data/ext/oj/dump.h +71 -57
- data/ext/oj/dump_compat.c +575 -655
- data/ext/oj/dump_leaf.c +96 -186
- data/ext/oj/dump_object.c +533 -660
- data/ext/oj/dump_strict.c +306 -340
- data/ext/oj/encode.h +4 -33
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +28 -28
- data/ext/oj/err.h +39 -42
- data/ext/oj/extconf.rb +28 -7
- data/ext/oj/fast.c +1052 -1113
- 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 +471 -430
- data/ext/oj/object.c +532 -580
- data/ext/oj/odd.c +156 -142
- data/ext/oj/odd.h +25 -26
- data/ext/oj/oj.c +1346 -961
- data/ext/oj/oj.h +307 -290
- data/ext/oj/parse.c +954 -858
- data/ext/oj/parse.h +74 -72
- data/ext/oj/parser.c +1600 -0
- data/ext/oj/parser.h +103 -0
- data/ext/oj/rails.c +819 -836
- data/ext/oj/rails.h +8 -11
- data/ext/oj/reader.c +136 -147
- data/ext/oj/reader.h +69 -83
- data/ext/oj/resolve.c +41 -63
- data/ext/oj/resolve.h +4 -6
- data/ext/oj/rxclass.c +69 -72
- data/ext/oj/rxclass.h +12 -13
- data/ext/oj/saj.c +440 -485
- data/ext/oj/saj2.c +584 -0
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +79 -118
- data/ext/oj/simd.h +10 -0
- data/ext/oj/sparse.c +739 -709
- data/ext/oj/stream_writer.c +141 -175
- data/ext/oj/strict.c +103 -128
- data/ext/oj/string_writer.c +244 -261
- data/ext/oj/trace.c +34 -41
- data/ext/oj/trace.h +42 -15
- data/ext/oj/usual.c +1218 -0
- data/ext/oj/usual.h +69 -0
- data/ext/oj/util.c +107 -107
- data/ext/oj/util.h +4 -3
- data/ext/oj/val_stack.c +61 -78
- data/ext/oj/val_stack.h +80 -114
- data/ext/oj/validate.c +46 -0
- data/ext/oj/wab.c +316 -361
- 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 +54 -20
- 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 -8
- data/pages/Compatibility.md +1 -1
- data/pages/Encoding.md +1 -1
- data/pages/InstallOptions.md +20 -0
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +9 -3
- data/pages/Options.md +62 -12
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +73 -22
- metadata +68 -192
- 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/activerecord/result_test.rb +0 -27
- 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/decoding_test.rb +0 -125
- data/test/activesupport5/encoding_test.rb +0 -485
- data/test/activesupport5/encoding_test_cases.rb +0 -90
- data/test/activesupport5/test_helper.rb +0 -50
- data/test/activesupport5/time_zone_test_helpers.rb +0 -24
- data/test/bar.rb +0 -25
- data/test/files.rb +0 -29
- data/test/foo.rb +0 -21
- data/test/helper.rb +0 -26
- data/test/isolated/shared.rb +0 -308
- 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_redefine.rb +0 -15
- data/test/json_gem/json_addition_test.rb +0 -216
- data/test/json_gem/json_common_interface_test.rb +0 -148
- data/test/json_gem/json_encoding_test.rb +0 -107
- data/test/json_gem/json_ext_parser_test.rb +0 -20
- data/test/json_gem/json_fixtures_test.rb +0 -35
- data/test/json_gem/json_generator_test.rb +0 -383
- data/test/json_gem/json_generic_object_test.rb +0 -90
- data/test/json_gem/json_parser_test.rb +0 -470
- data/test/json_gem/json_string_matching_test.rb +0 -42
- data/test/json_gem/test_helper.rb +0 -18
- data/test/perf.rb +0 -107
- data/test/perf_compat.rb +0 -130
- 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 -145
- data/test/perf_wab.rb +0 -131
- 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 -54
- data/test/sample_json.rb +0 -37
- data/test/test_compat.rb +0 -509
- data/test/test_custom.rb +0 -503
- data/test/test_debian.rb +0 -53
- data/test/test_fast.rb +0 -470
- data/test/test_file.rb +0 -239
- data/test/test_gc.rb +0 -49
- data/test/test_hash.rb +0 -29
- data/test/test_integer_range.rb +0 -73
- data/test/test_null.rb +0 -376
- data/test/test_object.rb +0 -1018
- data/test/test_saj.rb +0 -186
- data/test/test_scp.rb +0 -433
- data/test/test_strict.rb +0 -410
- data/test/test_various.rb +0 -741
- data/test/test_wab.rb +0 -307
- data/test/test_writer.rb +0 -380
- data/test/tests.rb +0 -24
- data/test/tests_mimic.rb +0 -14
- data/test/tests_mimic_addition.rb +0 -7
- data/test/zoo.rb +0 -13
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
|
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
|
48
31
|
|
49
32
|
typedef struct _batch {
|
50
|
-
struct _batch
|
51
|
-
int
|
52
|
-
struct _leaf
|
33
|
+
struct _batch *next;
|
34
|
+
int next_avail;
|
35
|
+
struct _leaf leaves[BATCH_SIZE];
|
53
36
|
} *Batch;
|
54
37
|
|
55
38
|
typedef struct _doc {
|
56
|
-
Leaf
|
57
|
-
Leaf
|
58
|
-
Leaf
|
59
|
-
char
|
60
|
-
unsigned long
|
61
|
-
VALUE
|
62
|
-
Batch
|
63
|
-
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;
|
64
47
|
} *Doc;
|
65
48
|
|
66
49
|
typedef struct _parseInfo {
|
67
|
-
char
|
68
|
-
char
|
69
|
-
Doc
|
70
|
-
void
|
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 = 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;
|
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
|
-
volatile 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
|
-
volatile 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,716 +338,724 @@ 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
|
-
doc_init(Doc doc) {
|
633
|
+
inline static void doc_init(Doc doc) {
|
737
634
|
memset(doc, 0, sizeof(struct _doc));
|
738
|
-
doc->where
|
739
|
-
doc->self
|
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 void
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
default:
|
800
|
-
break;
|
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
|
+
}
|
801
693
|
}
|
802
694
|
}
|
803
695
|
|
804
|
-
static void
|
805
|
-
mark_doc(void *ptr) {
|
696
|
+
static void mark_doc(void *ptr) {
|
806
697
|
if (NULL != ptr) {
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
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;
|
811
721
|
}
|
812
722
|
}
|
813
723
|
|
814
|
-
static
|
815
|
-
|
816
|
-
struct _parseInfo pi;
|
817
|
-
volatile VALUE result = Qnil;
|
818
|
-
Doc doc;
|
819
|
-
int ex = 0;
|
820
|
-
volatile VALUE self;
|
724
|
+
static void compact_doc(void *ptr) {
|
725
|
+
Doc doc = (Doc)ptr;
|
821
726
|
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
doc = ALLOCA_N(struct _doc, 1);
|
826
|
-
} else {
|
827
|
-
doc = ALLOC(struct _doc);
|
727
|
+
if (doc) {
|
728
|
+
doc->self = rb_gc_location(doc->self);
|
729
|
+
compact_leaf(doc->data);
|
828
730
|
}
|
829
|
-
|
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
|
830
758
|
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
831
|
-
|
759
|
+
pi.str = json + 3;
|
832
760
|
} else {
|
833
|
-
|
761
|
+
pi.str = json;
|
834
762
|
}
|
835
763
|
pi.s = pi.str;
|
836
764
|
doc_init(doc);
|
837
765
|
pi.doc = doc;
|
838
|
-
#if IS_WINDOWS
|
839
|
-
|
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));
|
840
769
|
#else
|
841
770
|
{
|
842
|
-
|
771
|
+
struct rlimit lim;
|
843
772
|
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
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
|
+
}
|
849
779
|
}
|
850
780
|
#endif
|
851
|
-
// last arg is free func void* func(void*)
|
852
|
-
#ifdef HAVE_RB_DATA_OBJECT_WRAP
|
853
|
-
self = rb_data_object_wrap(clas, doc, mark_doc, free_doc_cb);
|
854
|
-
#else
|
855
|
-
self = rb_data_object_alloc(clas, doc, mark_doc, free_doc_cb);
|
856
|
-
#endif
|
857
|
-
doc->self = self;
|
858
781
|
doc->json = json;
|
859
|
-
|
860
|
-
|
782
|
+
self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
|
783
|
+
doc->self = self;
|
784
|
+
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
861
785
|
if (given || 0 != ex) {
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
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
|
+
*/
|
868
794
|
} else {
|
869
|
-
|
795
|
+
result = doc->self;
|
870
796
|
}
|
871
797
|
if (0 != ex) {
|
872
|
-
|
798
|
+
rb_jump_tag(ex);
|
873
799
|
}
|
874
800
|
return result;
|
875
801
|
}
|
876
802
|
|
877
|
-
static Leaf
|
878
|
-
|
879
|
-
Leaf leaf = *doc->where;
|
803
|
+
static Leaf get_doc_leaf(Doc doc, const char *path) {
|
804
|
+
Leaf leaf = *doc->where;
|
880
805
|
|
881
806
|
if (0 != doc->data && 0 != path) {
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
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);
|
902
827
|
}
|
903
828
|
return leaf;
|
904
829
|
}
|
905
830
|
|
906
|
-
static const char*
|
907
|
-
next_slash(const char *s) {
|
831
|
+
static const char *next_slash(const char *s) {
|
908
832
|
for (; '\0' != *s; s++) {
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
833
|
+
if ('\\' == *s) {
|
834
|
+
s++;
|
835
|
+
if ('\0' == *s) {
|
836
|
+
break;
|
837
|
+
}
|
838
|
+
} else if ('/' == *s) {
|
839
|
+
return s;
|
840
|
+
}
|
917
841
|
}
|
918
842
|
return NULL;
|
919
843
|
}
|
920
844
|
|
921
|
-
static bool
|
922
|
-
key_match(const char *pat, const char *key, int plen) {
|
845
|
+
static bool key_match(const char *pat, const char *key, int plen) {
|
923
846
|
for (; 0 < plen; plen--, pat++, key++) {
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
847
|
+
if ('\\' == *pat) {
|
848
|
+
plen--;
|
849
|
+
pat++;
|
850
|
+
}
|
851
|
+
if (*pat != *key) {
|
852
|
+
return false;
|
853
|
+
}
|
931
854
|
}
|
932
855
|
return '\0' == *key;
|
933
856
|
}
|
934
857
|
|
935
|
-
static Leaf
|
936
|
-
|
937
|
-
Leaf leaf = *lp;
|
858
|
+
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
859
|
+
Leaf leaf = *lp;
|
938
860
|
|
939
861
|
if (MAX_STACK <= lp - stack) {
|
940
|
-
|
862
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
941
863
|
}
|
942
864
|
if ('\0' != *path) {
|
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
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
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
|
+
}
|
1001
930
|
}
|
1002
931
|
return leaf;
|
1003
932
|
}
|
1004
933
|
|
1005
|
-
static void
|
1006
|
-
each_leaf(Doc doc, VALUE self) {
|
934
|
+
static void each_leaf(Doc doc, VALUE self) {
|
1007
935
|
if (COL_VAL == (*doc->where)->value_type) {
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
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
|
+
}
|
1023
951
|
} else {
|
1024
|
-
|
952
|
+
rb_yield(self);
|
1025
953
|
}
|
1026
954
|
}
|
1027
955
|
|
1028
|
-
static int
|
1029
|
-
move_step(Doc doc, const char *path, int loc) {
|
956
|
+
static int move_step(Doc doc, const char *path, int loc) {
|
1030
957
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
1031
|
-
|
958
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
1032
959
|
}
|
1033
960
|
if ('\0' == *path) {
|
1034
|
-
|
961
|
+
loc = 0;
|
1035
962
|
} else {
|
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
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
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
|
+
}
|
1115
1042
|
}
|
1116
1043
|
return loc;
|
1117
1044
|
}
|
1118
1045
|
|
1119
|
-
static void
|
1120
|
-
each_value(Doc doc, Leaf leaf) {
|
1046
|
+
static void each_value(Doc doc, Leaf leaf) {
|
1121
1047
|
if (COL_VAL == leaf->value_type) {
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
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
|
+
}
|
1131
1057
|
} else {
|
1132
|
-
|
1058
|
+
rb_yield(leaf_value(doc, leaf));
|
1133
1059
|
}
|
1134
1060
|
}
|
1135
1061
|
|
@@ -1144,7 +1070,8 @@ each_value(Doc doc, Leaf leaf) {
|
|
1144
1070
|
*
|
1145
1071
|
* @param [String] json JSON document string
|
1146
1072
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1147
|
-
* @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
|
1148
1075
|
* @example
|
1149
1076
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
1150
1077
|
* # or as an alternative
|
@@ -1152,33 +1079,24 @@ each_value(Doc doc, Leaf leaf) {
|
|
1152
1079
|
* doc.size() #=> 4
|
1153
1080
|
* doc.close()
|
1154
1081
|
*/
|
1155
|
-
static VALUE
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
int given = rb_block_given_p();
|
1161
|
-
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();
|
1162
1087
|
|
1163
1088
|
Check_Type(str, T_STRING);
|
1164
|
-
len
|
1165
|
-
|
1166
|
-
|
1167
|
-
json = ALLOC_N(char, len);
|
1168
|
-
} else {
|
1169
|
-
json = ALLOCA_N(char, len);
|
1170
|
-
}
|
1171
|
-
// It should not be necessaary to stop GC but if it is not stopped and a
|
1172
|
-
// large string is parsed that string is corrupted or freed during
|
1173
|
-
// parsing. I'm not sure what is going on exactly but disabling GC avoids
|
1174
|
-
// the issue.
|
1175
|
-
rb_gc_disable();
|
1089
|
+
len = RSTRING_LEN(str) + 1;
|
1090
|
+
json = OJ_R_ALLOC_N(char, len);
|
1091
|
+
|
1176
1092
|
memcpy(json, StringValuePtr(str), len);
|
1177
|
-
obj = parse_json(clas, json, given
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1093
|
+
obj = parse_json(clas, json, given);
|
1094
|
+
// TBD is this needed
|
1095
|
+
/*
|
1096
|
+
if (given) {
|
1097
|
+
OJ_R_FREE(json);
|
1181
1098
|
}
|
1099
|
+
*/
|
1182
1100
|
return obj;
|
1183
1101
|
}
|
1184
1102
|
|
@@ -1191,7 +1109,8 @@ doc_open(VALUE clas, VALUE str) {
|
|
1191
1109
|
*
|
1192
1110
|
* @param [String] filename name of file that contains a JSON document
|
1193
1111
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
1194
|
-
* @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
|
1195
1114
|
* @example
|
1196
1115
|
* File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
|
1197
1116
|
* Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
|
@@ -1200,65 +1119,59 @@ doc_open(VALUE clas, VALUE str) {
|
|
1200
1119
|
* doc.size() #=> 4
|
1201
1120
|
* doc.close()
|
1202
1121
|
*/
|
1203
|
-
static VALUE
|
1204
|
-
|
1205
|
-
char
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
int allocate;
|
1212
|
-
|
1213
|
-
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
|
+
|
1214
1130
|
path = StringValuePtr(filename);
|
1215
1131
|
if (0 == (f = fopen(path, "r"))) {
|
1216
|
-
|
1132
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
1217
1133
|
}
|
1218
1134
|
fseek(f, 0, SEEK_END);
|
1219
|
-
len
|
1220
|
-
|
1221
|
-
|
1222
|
-
json = ALLOC_N(char, len + 1);
|
1223
|
-
} else {
|
1224
|
-
json = ALLOCA_N(char, len + 1);
|
1225
|
-
}
|
1135
|
+
len = ftell(f);
|
1136
|
+
json = OJ_R_ALLOC_N(char, len + 1);
|
1137
|
+
|
1226
1138
|
fseek(f, 0, SEEK_SET);
|
1227
1139
|
if (len != fread(json, 1, len, f)) {
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
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);
|
1231
1145
|
}
|
1232
1146
|
fclose(f);
|
1233
1147
|
json[len] = '\0';
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
if (given
|
1238
|
-
|
1148
|
+
obj = parse_json(clas, json, given);
|
1149
|
+
// TBD is this needed
|
1150
|
+
/*
|
1151
|
+
if (given) {
|
1152
|
+
OJ_R_FREE(json);
|
1239
1153
|
}
|
1154
|
+
*/
|
1240
1155
|
return obj;
|
1241
1156
|
}
|
1242
1157
|
|
1243
|
-
static int
|
1244
|
-
|
1245
|
-
int cnt = 0;
|
1158
|
+
static int esc_strlen(const char *s) {
|
1159
|
+
int cnt = 0;
|
1246
1160
|
|
1247
1161
|
for (; '\0' != *s; s++, cnt++) {
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1162
|
+
if ('/' == *s) {
|
1163
|
+
cnt++;
|
1164
|
+
}
|
1251
1165
|
}
|
1252
1166
|
return cnt;
|
1253
1167
|
}
|
1254
1168
|
|
1255
|
-
static char*
|
1256
|
-
append_key(char *p, const char *key) {
|
1169
|
+
static char *append_key(char *p, const char *key) {
|
1257
1170
|
for (; '\0' != *key; p++, key++) {
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1171
|
+
if ('/' == *key) {
|
1172
|
+
*p++ = '\\';
|
1173
|
+
}
|
1174
|
+
*p = *key;
|
1262
1175
|
}
|
1263
1176
|
return p;
|
1264
1177
|
}
|
@@ -1267,68 +1180,84 @@ append_key(char *p, const char *key) {
|
|
1267
1180
|
* @see Oj::Doc.open
|
1268
1181
|
*/
|
1269
1182
|
|
1270
|
-
/* @overload where
|
1183
|
+
/* @overload where() => String
|
1271
1184
|
*
|
1272
1185
|
* Returns a String that describes the absolute path to the current location
|
1273
1186
|
* in the JSON document.
|
1274
1187
|
*/
|
1275
|
-
static VALUE
|
1276
|
-
|
1277
|
-
Doc doc = self_doc(self);
|
1188
|
+
static VALUE doc_where(VALUE self) {
|
1189
|
+
Doc doc = self_doc(self);
|
1278
1190
|
|
1279
1191
|
if (0 == *doc->where_path || doc->where == doc->where_path) {
|
1280
|
-
|
1192
|
+
return oj_slash_string;
|
1281
1193
|
} else {
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
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);
|
1310
1222
|
}
|
1311
1223
|
}
|
1312
1224
|
|
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
|
+
|
1313
1243
|
/* @overload local_key() => String, Fixnum, nil
|
1314
1244
|
*
|
1315
1245
|
* Returns the final key to the current location.
|
1316
1246
|
* @example
|
1317
|
-
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() }
|
1318
|
-
* Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
|
1319
|
-
*
|
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
|
1320
1251
|
*/
|
1321
|
-
static VALUE
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
volatile 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;
|
1326
1256
|
|
1327
1257
|
if (T_HASH == leaf->parent_type) {
|
1328
|
-
|
1329
|
-
key = oj_encode(key);
|
1258
|
+
key = rb_utf8_str_new_cstr(leaf->key);
|
1330
1259
|
} else if (T_ARRAY == leaf->parent_type) {
|
1331
|
-
|
1260
|
+
key = LONG2NUM(leaf->index);
|
1332
1261
|
}
|
1333
1262
|
return key;
|
1334
1263
|
}
|
@@ -1338,14 +1267,14 @@ doc_local_key(VALUE self) {
|
|
1338
1267
|
* Moves the document marker or location to the hoot or home position. The
|
1339
1268
|
* same operation can be performed with a Oj::Doc.move('/').
|
1340
1269
|
* @example
|
1341
|
-
* 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
|
+
* #=> '/'
|
1342
1272
|
*/
|
1343
|
-
static VALUE
|
1344
|
-
|
1345
|
-
Doc doc = self_doc(self);
|
1273
|
+
static VALUE doc_home(VALUE self) {
|
1274
|
+
Doc doc = self_doc(self);
|
1346
1275
|
|
1347
1276
|
*doc->where_path = doc->data;
|
1348
|
-
doc->where
|
1277
|
+
doc->where = doc->where_path;
|
1349
1278
|
|
1350
1279
|
return oj_slash_string;
|
1351
1280
|
}
|
@@ -1361,76 +1290,91 @@ doc_home(VALUE self) {
|
|
1361
1290
|
* Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
|
1362
1291
|
* Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
|
1363
1292
|
*/
|
1364
|
-
static VALUE
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
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;
|
1370
1298
|
|
1371
1299
|
if (1 <= argc) {
|
1372
|
-
|
1373
|
-
path = StringValuePtr(*argv);
|
1300
|
+
path = StringValuePtr(*argv);
|
1374
1301
|
}
|
1375
1302
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
case T_ARRAY: type = rb_cArray; break;
|
1388
|
-
case T_HASH: type = rb_cHash; break;
|
1389
|
-
default: break;
|
1390
|
-
}
|
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
|
+
}
|
1391
1314
|
}
|
1392
1315
|
return type;
|
1393
1316
|
}
|
1394
1317
|
|
1395
|
-
/* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array,
|
1318
|
+
/* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
|
1319
|
+
* Hash
|
1396
1320
|
*
|
1397
1321
|
* Returns the value at the location identified by the path or the current
|
1398
1322
|
* location if the path is nil or not provided. This method will create and
|
1399
1323
|
* return an Array or Hash if that is the type of Object at the location
|
1400
1324
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
1401
|
-
* document.
|
1325
|
+
* document. If a default is provided that is used if no value if found.
|
1402
1326
|
* @param [String] path path to the location to get the type of if provided
|
1403
1327
|
* @example
|
1404
1328
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
1405
1329
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
|
1406
1330
|
*/
|
1407
|
-
static VALUE
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
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;
|
1413
1336
|
|
1414
1337
|
doc = self_doc(self);
|
1415
1338
|
if (1 <= argc) {
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
}
|
1339
|
+
path = StringValuePtr(*argv);
|
1340
|
+
if (2 == argc) {
|
1341
|
+
val = argv[1];
|
1342
|
+
}
|
1421
1343
|
}
|
1422
1344
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1423
|
-
|
1345
|
+
val = leaf_value(doc, leaf);
|
1424
1346
|
}
|
1425
1347
|
return val;
|
1426
1348
|
}
|
1427
1349
|
|
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
|
+
|
1428
1371
|
/* @overload each_leaf(path=nil) => nil
|
1429
1372
|
*
|
1430
1373
|
* Yields to the provided block for each leaf node with the identified
|
1431
1374
|
* location of the JSON document as the root. The parameter passed to the
|
1432
1375
|
* block on yield is the Doc instance after moving to the child location.
|
1433
|
-
* @param [String] path if provided it identified the top of the branch to
|
1376
|
+
* @param [String] path if provided it identified the top of the branch to
|
1377
|
+
* process the leaves of
|
1434
1378
|
* @yieldparam [Doc] Doc at the child location
|
1435
1379
|
* @example
|
1436
1380
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1440,36 +1384,34 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1440
1384
|
* }
|
1441
1385
|
* #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
|
1442
1386
|
*/
|
1443
|
-
static VALUE
|
1444
|
-
doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
1387
|
+
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
1445
1388
|
if (rb_block_given_p()) {
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
|
1472
|
-
}
|
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
|
+
}
|
1473
1415
|
}
|
1474
1416
|
return Qnil;
|
1475
1417
|
}
|
@@ -1480,22 +1422,21 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1480
1422
|
* path or a relative path.
|
1481
1423
|
* @param [String] path path to the location to move to
|
1482
1424
|
* @example
|
1483
|
-
* 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"
|
1484
1427
|
*/
|
1485
|
-
static VALUE
|
1486
|
-
|
1487
|
-
|
1488
|
-
|
1489
|
-
int loc;
|
1428
|
+
static VALUE doc_move(VALUE self, VALUE str) {
|
1429
|
+
Doc doc = self_doc(self);
|
1430
|
+
const char *path;
|
1431
|
+
int loc;
|
1490
1432
|
|
1491
|
-
Check_Type(str, T_STRING);
|
1492
1433
|
path = StringValuePtr(str);
|
1493
1434
|
if ('/' == *path) {
|
1494
|
-
|
1495
|
-
|
1435
|
+
doc->where = doc->where_path;
|
1436
|
+
path++;
|
1496
1437
|
}
|
1497
1438
|
if (0 != (loc = move_step(doc, path, 1))) {
|
1498
|
-
|
1439
|
+
rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
|
1499
1440
|
}
|
1500
1441
|
return Qnil;
|
1501
1442
|
}
|
@@ -1506,7 +1447,8 @@ doc_move(VALUE self, VALUE str) {
|
|
1506
1447
|
* identified location of the JSON document as the root. The parameter passed
|
1507
1448
|
* to the block on yield is the Doc instance after moving to the child
|
1508
1449
|
* location.
|
1509
|
-
* @param [String] path if provided it identified the top of the branch to
|
1450
|
+
* @param [String] path if provided it identified the top of the branch to
|
1451
|
+
* process the children of
|
1510
1452
|
* @yieldparam [Doc] Doc at the child location
|
1511
1453
|
* @example
|
1512
1454
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1516,46 +1458,50 @@ doc_move(VALUE self, VALUE str) {
|
|
1516
1458
|
* }
|
1517
1459
|
* #=> ["/2/1", "/2/2"]
|
1518
1460
|
*/
|
1519
|
-
static VALUE
|
1520
|
-
doc_each_child(int argc, VALUE *argv, VALUE self) {
|
1461
|
+
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
1521
1462
|
if (rb_block_given_p()) {
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
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;
|
1559
1505
|
}
|
1560
1506
|
return Qnil;
|
1561
1507
|
}
|
@@ -1566,7 +1512,8 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1566
1512
|
* of the JSON document. The parameter passed to the block on yield is the
|
1567
1513
|
* value of the leaf. Only those leaves below the element specified by the
|
1568
1514
|
* path parameter are processed.
|
1569
|
-
* @param [String] path if provided it identified the top of the branch to
|
1515
|
+
* @param [String] path if provided it identified the top of the branch to
|
1516
|
+
* process the leaf values of
|
1570
1517
|
* @yieldparam [Object] val each leaf value
|
1571
1518
|
* @example
|
1572
1519
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
@@ -1575,7 +1522,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1575
1522
|
* result
|
1576
1523
|
* }
|
1577
1524
|
* #=> [3, 2, 1]
|
1578
|
-
*
|
1525
|
+
*
|
1579
1526
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
1580
1527
|
* result = []
|
1581
1528
|
* doc.each_value('/2') { |v| result << v }
|
@@ -1583,20 +1530,18 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1583
1530
|
* }
|
1584
1531
|
* #=> [2, 1]
|
1585
1532
|
*/
|
1586
|
-
static VALUE
|
1587
|
-
doc_each_value(int argc, VALUE *argv, VALUE self) {
|
1533
|
+
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
1588
1534
|
if (rb_block_given_p()) {
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
}
|
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
|
+
}
|
1600
1545
|
}
|
1601
1546
|
return Qnil;
|
1602
1547
|
}
|
@@ -1605,52 +1550,48 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1605
1550
|
*
|
1606
1551
|
* Dumps the document or nodes to a new JSON document. It uses the default
|
1607
1552
|
* options for generating the JSON.
|
1608
|
-
* @param path [String] if provided it identified the top of the branch to
|
1609
|
-
*
|
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
|
1610
1557
|
* @example
|
1611
1558
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
1612
1559
|
* doc.dump('/2')
|
1613
1560
|
* }
|
1614
1561
|
* #=> "[2,1]"
|
1615
1562
|
*/
|
1616
|
-
static VALUE
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
const char
|
1621
|
-
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;
|
1622
1568
|
|
1623
1569
|
if (1 <= argc) {
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
filename = StringValuePtr(argv[1]);
|
1631
|
-
}
|
1570
|
+
if (Qnil != *argv) {
|
1571
|
+
path = StringValuePtr(*argv);
|
1572
|
+
}
|
1573
|
+
if (2 <= argc) {
|
1574
|
+
filename = StringValuePtr(argv[1]);
|
1575
|
+
}
|
1632
1576
|
}
|
1633
1577
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
rjson = Qnil;
|
1652
|
-
}
|
1653
|
-
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;
|
1654
1595
|
}
|
1655
1596
|
return Qnil;
|
1656
1597
|
}
|
@@ -1663,9 +1604,10 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1663
1604
|
* @example
|
1664
1605
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
1665
1606
|
*/
|
1666
|
-
static VALUE
|
1667
|
-
|
1668
|
-
|
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);
|
1669
1611
|
}
|
1670
1612
|
|
1671
1613
|
/* @overload close() => nil
|
@@ -1677,16 +1619,13 @@ doc_size(VALUE self) {
|
|
1677
1619
|
* doc.size() #=> 4
|
1678
1620
|
* doc.close()
|
1679
1621
|
*/
|
1680
|
-
static VALUE
|
1681
|
-
|
1682
|
-
Doc doc = self_doc(self);
|
1622
|
+
static VALUE doc_close(VALUE self) {
|
1623
|
+
Doc doc = self_doc(self);
|
1683
1624
|
|
1684
1625
|
rb_gc_unregister_address(&doc->self);
|
1685
|
-
DATA_PTR(doc->self) =
|
1626
|
+
DATA_PTR(doc->self) = NULL;
|
1686
1627
|
if (0 != doc) {
|
1687
|
-
|
1688
|
-
doc_free(doc);
|
1689
|
-
xfree(doc);
|
1628
|
+
doc_free(doc);
|
1690
1629
|
}
|
1691
1630
|
return Qnil;
|
1692
1631
|
}
|
@@ -1695,8 +1634,7 @@ doc_close(VALUE self) {
|
|
1695
1634
|
Oj = rb_define_module("Oj");
|
1696
1635
|
#endif
|
1697
1636
|
|
1698
|
-
static VALUE
|
1699
|
-
doc_not_implemented(VALUE self) {
|
1637
|
+
static VALUE doc_not_implemented(VALUE self) {
|
1700
1638
|
rb_raise(rb_eNotImpError, "Not implemented.");
|
1701
1639
|
return Qnil;
|
1702
1640
|
}
|
@@ -1708,21 +1646,21 @@ doc_not_implemented(VALUE self) {
|
|
1708
1646
|
* extracted. Once the document is closed the document can not longer be
|
1709
1647
|
* accessed. This allows the parsing and data extraction to be extremely fast
|
1710
1648
|
* compared to other JSON parses.
|
1711
|
-
*
|
1649
|
+
*
|
1712
1650
|
* An Oj::Doc class is not created directly but the _open()_ class method is
|
1713
1651
|
* used to open a document and the yield parameter to the block of the #open()
|
1714
1652
|
* call is the Doc instance. The Doc instance can be moved across, up, and
|
1715
1653
|
* down the JSON document. At each element the data associated with the
|
1716
1654
|
* element can be extracted. It is also possible to just provide a path to the
|
1717
1655
|
* data to be extracted and retrieve the data in that manner.
|
1718
|
-
*
|
1656
|
+
*
|
1719
1657
|
* For many of the methods a path is used to describe the location of an
|
1720
1658
|
* element. Paths follow a subset of the XPath syntax. The slash ('/')
|
1721
1659
|
* character is the separator. Each step in the path identifies the next
|
1722
1660
|
* branch to take through the document. A JSON object will expect a key string
|
1723
1661
|
* while an array will expect a positive index. A .. step indicates a move up
|
1724
1662
|
* the JSON document.
|
1725
|
-
*
|
1663
|
+
*
|
1726
1664
|
* @example
|
1727
1665
|
* json = %{[
|
1728
1666
|
* {
|
@@ -1736,28 +1674,29 @@ doc_not_implemented(VALUE self) {
|
|
1736
1674
|
* ]}
|
1737
1675
|
* # move and get value
|
1738
1676
|
* Oj::Doc.open(json) do |doc|
|
1739
|
-
* doc.move('/1/two')
|
1740
|
-
* # doc location is now at the 'two' element of the hash that is the first
|
1741
|
-
*
|
1742
|
-
* 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
|
1743
1680
|
* #=> 2
|
1744
|
-
*
|
1745
|
-
* # Now try again using a path to Oj::Doc.fetch() directly and not using a
|
1746
|
-
*
|
1747
|
-
* doc.fetch('/2/three') #=> 3
|
1748
|
-
* 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()
|
1749
1684
|
*/
|
1750
|
-
void
|
1751
|
-
oj_init_doc() {
|
1685
|
+
void oj_init_doc(void) {
|
1752
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);
|
1753
1689
|
rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
|
1754
1690
|
rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
|
1755
1691
|
rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
|
1756
|
-
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);
|
1757
1695
|
rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
|
1758
1696
|
rb_define_method(oj_doc_class, "home", doc_home, 0);
|
1759
1697
|
rb_define_method(oj_doc_class, "type", doc_type, -1);
|
1760
1698
|
rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
|
1699
|
+
rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
|
1761
1700
|
rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
|
1762
1701
|
rb_define_method(oj_doc_class, "move", doc_move, 1);
|
1763
1702
|
rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);
|