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