oj 3.7.4 → 3.13.23
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 +1360 -0
- data/README.md +31 -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 +790 -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 +1073 -1088
- data/ext/oj/intern.c +298 -0
- data/ext/oj/intern.h +26 -0
- data/ext/oj/mimic_json.c +469 -436
- data/ext/oj/object.c +532 -599
- data/ext/oj/odd.c +154 -138
- data/ext/oj/odd.h +37 -38
- data/ext/oj/oj.c +1333 -986
- data/ext/oj/oj.h +336 -316
- data/ext/oj/parse.c +1002 -846
- data/ext/oj/parse.h +92 -87
- data/ext/oj/parser.c +1587 -0
- data/ext/oj/parser.h +102 -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 +596 -0
- data/ext/oj/saj2.h +23 -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 +1207 -0
- data/ext/oj/usual.h +68 -0
- data/ext/oj/util.c +136 -0
- data/ext/oj/util.h +20 -0
- data/ext/oj/val_stack.c +60 -68
- 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 +9 -8
- 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 +78 -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 +21 -8
- data/test/json_gem/json_parser_test.rb +8 -1
- 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 +145 -7
- data/test/test_fast.rb +62 -2
- data/test/test_file.rb +23 -7
- data/test/test_gc.rb +11 -0
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +11 -1
- data/test/test_integer_range.rb +1 -2
- data/test/test_object.rb +43 -12
- data/test/test_parser.rb +11 -0
- data/test/test_parser_debug.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 +3 -5
- data/test/test_strict.rb +26 -1
- data/test/test_various.rb +86 -65
- data/test/test_wab.rb +2 -0
- data/test/test_writer.rb +19 -2
- data/test/tests.rb +10 -1
- data/test/tests_mimic.rb +9 -0
- data/test/tests_mimic_addition.rb +9 -0
- data/test/zoo.rb +13 -0
- metadata +63 -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,728 @@ 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
|
-
|
|
679
|
+
static void mark_leaf(Leaf leaf) {
|
|
680
|
+
if (NULL != leaf) {
|
|
681
|
+
switch (leaf->value_type) {
|
|
682
|
+
case COL_VAL:
|
|
683
|
+
if (NULL != leaf->elements) {
|
|
684
|
+
Leaf first = leaf->elements->next;
|
|
685
|
+
Leaf e = first;
|
|
686
|
+
|
|
687
|
+
do {
|
|
688
|
+
mark_leaf(e);
|
|
689
|
+
e = e->next;
|
|
690
|
+
} while (e != first);
|
|
691
|
+
}
|
|
692
|
+
break;
|
|
693
|
+
case RUBY_VAL: mark(leaf->value); break;
|
|
694
|
+
|
|
695
|
+
default: break;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
static void mark_doc(void *ptr) {
|
|
701
|
+
if (NULL != ptr) {
|
|
702
|
+
Doc doc = (Doc)ptr;
|
|
703
|
+
|
|
704
|
+
mark(doc->self);
|
|
705
|
+
mark_leaf(doc->data);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
709
|
+
static void compact_leaf(Leaf leaf) {
|
|
783
710
|
switch (leaf->value_type) {
|
|
784
711
|
case COL_VAL:
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
case RUBY_VAL:
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
default:
|
|
800
|
-
break;
|
|
712
|
+
if (NULL != leaf->elements) {
|
|
713
|
+
Leaf first = leaf->elements->next;
|
|
714
|
+
Leaf e = first;
|
|
715
|
+
|
|
716
|
+
do {
|
|
717
|
+
compact_leaf(e);
|
|
718
|
+
e = e->next;
|
|
719
|
+
} while (e != first);
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
722
|
+
case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
|
|
723
|
+
|
|
724
|
+
default: break;
|
|
801
725
|
}
|
|
802
726
|
}
|
|
803
727
|
|
|
804
|
-
static void
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
mark_leaf(doc->data);
|
|
728
|
+
static void compact_doc(void *ptr) {
|
|
729
|
+
Doc doc = (Doc)ptr;
|
|
730
|
+
|
|
731
|
+
if (doc) {
|
|
732
|
+
doc->self = rb_gc_location(doc->self);
|
|
733
|
+
compact_leaf(doc->data);
|
|
811
734
|
}
|
|
812
735
|
}
|
|
736
|
+
#endif
|
|
737
|
+
|
|
738
|
+
static const rb_data_type_t oj_doc_type = {
|
|
739
|
+
"Oj/doc",
|
|
740
|
+
{
|
|
741
|
+
mark_doc,
|
|
742
|
+
free_doc_cb,
|
|
743
|
+
NULL,
|
|
744
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
745
|
+
compact_doc,
|
|
746
|
+
#endif
|
|
747
|
+
},
|
|
748
|
+
0,
|
|
749
|
+
0,
|
|
750
|
+
};
|
|
813
751
|
|
|
814
|
-
static VALUE
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
752
|
+
static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
|
|
753
|
+
struct _parseInfo pi;
|
|
754
|
+
volatile VALUE result = Qnil;
|
|
755
|
+
Doc doc;
|
|
756
|
+
int ex = 0;
|
|
757
|
+
volatile VALUE self;
|
|
758
|
+
|
|
759
|
+
// TBD are both needed? is stack allocation ever needed?
|
|
821
760
|
|
|
822
761
|
if (given) {
|
|
823
|
-
|
|
762
|
+
doc = ALLOCA_N(struct _doc, 1);
|
|
824
763
|
} else {
|
|
825
|
-
|
|
764
|
+
doc = ALLOC(struct _doc);
|
|
826
765
|
}
|
|
827
|
-
|
|
766
|
+
// skip UTF-8 BOM if present
|
|
828
767
|
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
|
829
|
-
|
|
768
|
+
pi.str = json + 3;
|
|
830
769
|
} else {
|
|
831
|
-
|
|
770
|
+
pi.str = json;
|
|
832
771
|
}
|
|
833
772
|
pi.s = pi.str;
|
|
834
773
|
doc_init(doc);
|
|
835
774
|
pi.doc = doc;
|
|
836
775
|
#if IS_WINDOWS
|
|
837
|
-
|
|
776
|
+
// assume a 1M stack and give half to ruby
|
|
777
|
+
pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
|
|
838
778
|
#else
|
|
839
779
|
{
|
|
840
|
-
|
|
780
|
+
struct rlimit lim;
|
|
841
781
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
782
|
+
if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
|
|
783
|
+
// let 3/4ths of the stack be used only
|
|
784
|
+
pi.stack_min = (void *)((char *)&lim - (lim.rlim_cur / 4 * 3));
|
|
785
|
+
} else {
|
|
786
|
+
pi.stack_min = 0; // indicates not to check stack limit
|
|
787
|
+
}
|
|
847
788
|
}
|
|
848
789
|
#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;
|
|
790
|
+
self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
|
|
791
|
+
doc->self = self;
|
|
792
|
+
doc->json = json;
|
|
857
793
|
DATA_PTR(doc->self) = doc;
|
|
858
|
-
result
|
|
794
|
+
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
|
859
795
|
if (given || 0 != ex) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
796
|
+
DATA_PTR(doc->self) = NULL;
|
|
797
|
+
doc_free(pi.doc);
|
|
798
|
+
if (allocated && 0 != ex) { // will jump so caller will not free
|
|
799
|
+
xfree(json);
|
|
800
|
+
}
|
|
801
|
+
rb_gc_enable();
|
|
865
802
|
} else {
|
|
866
|
-
|
|
803
|
+
result = doc->self;
|
|
867
804
|
}
|
|
868
805
|
if (0 != ex) {
|
|
869
|
-
|
|
806
|
+
rb_jump_tag(ex);
|
|
870
807
|
}
|
|
871
808
|
return result;
|
|
872
809
|
}
|
|
873
810
|
|
|
874
|
-
static Leaf
|
|
875
|
-
|
|
876
|
-
Leaf leaf = *doc->where;
|
|
811
|
+
static Leaf get_doc_leaf(Doc doc, const char *path) {
|
|
812
|
+
Leaf leaf = *doc->where;
|
|
877
813
|
|
|
878
814
|
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
|
-
|
|
815
|
+
Leaf stack[MAX_STACK];
|
|
816
|
+
Leaf *lp;
|
|
817
|
+
|
|
818
|
+
if ('/' == *path) {
|
|
819
|
+
path++;
|
|
820
|
+
*stack = doc->data;
|
|
821
|
+
lp = stack;
|
|
822
|
+
} else if (doc->where == doc->where_path) {
|
|
823
|
+
*stack = doc->data;
|
|
824
|
+
lp = stack;
|
|
825
|
+
} else {
|
|
826
|
+
size_t cnt = doc->where - doc->where_path;
|
|
827
|
+
|
|
828
|
+
if (MAX_STACK <= cnt) {
|
|
829
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
830
|
+
}
|
|
831
|
+
memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
|
|
832
|
+
lp = stack + cnt;
|
|
833
|
+
}
|
|
834
|
+
return get_leaf(stack, lp, path);
|
|
899
835
|
}
|
|
900
836
|
return leaf;
|
|
901
837
|
}
|
|
902
838
|
|
|
903
|
-
static const char*
|
|
904
|
-
next_slash(const char *s) {
|
|
839
|
+
static const char *next_slash(const char *s) {
|
|
905
840
|
for (; '\0' != *s; s++) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
841
|
+
if ('\\' == *s) {
|
|
842
|
+
s++;
|
|
843
|
+
if ('\0' == *s) {
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
} else if ('/' == *s) {
|
|
847
|
+
return s;
|
|
848
|
+
}
|
|
914
849
|
}
|
|
915
850
|
return NULL;
|
|
916
851
|
}
|
|
917
852
|
|
|
918
|
-
static bool
|
|
919
|
-
key_match(const char *pat, const char *key, int plen) {
|
|
853
|
+
static bool key_match(const char *pat, const char *key, int plen) {
|
|
920
854
|
for (; 0 < plen; plen--, pat++, key++) {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
855
|
+
if ('\\' == *pat) {
|
|
856
|
+
plen--;
|
|
857
|
+
pat++;
|
|
858
|
+
}
|
|
859
|
+
if (*pat != *key) {
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
928
862
|
}
|
|
929
863
|
return '\0' == *key;
|
|
930
864
|
}
|
|
931
865
|
|
|
932
|
-
static Leaf
|
|
933
|
-
|
|
934
|
-
Leaf leaf = *lp;
|
|
866
|
+
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
867
|
+
Leaf leaf = *lp;
|
|
935
868
|
|
|
936
869
|
if (MAX_STACK <= lp - stack) {
|
|
937
|
-
|
|
870
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
938
871
|
}
|
|
939
872
|
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
|
-
|
|
873
|
+
if ('.' == *path && '.' == *(path + 1)) {
|
|
874
|
+
path += 2;
|
|
875
|
+
if ('/' == *path) {
|
|
876
|
+
path++;
|
|
877
|
+
}
|
|
878
|
+
if (stack < lp) {
|
|
879
|
+
leaf = get_leaf(stack, lp - 1, path);
|
|
880
|
+
} else {
|
|
881
|
+
return 0;
|
|
882
|
+
}
|
|
883
|
+
} else if (NULL == leaf->elements) {
|
|
884
|
+
leaf = NULL;
|
|
885
|
+
} else if (STR_VAL == leaf->value_type || RUBY_VAL == leaf->value_type) {
|
|
886
|
+
// We are trying to get a children of a leaf, which
|
|
887
|
+
// doesn't exist.
|
|
888
|
+
leaf = NULL;
|
|
889
|
+
} else if (COL_VAL == leaf->value_type) {
|
|
890
|
+
Leaf first = leaf->elements->next;
|
|
891
|
+
Leaf e = first;
|
|
892
|
+
int type = leaf->rtype;
|
|
893
|
+
|
|
894
|
+
leaf = NULL;
|
|
895
|
+
if (T_ARRAY == type) {
|
|
896
|
+
int cnt = 0;
|
|
897
|
+
|
|
898
|
+
for (; '0' <= *path && *path <= '9'; path++) {
|
|
899
|
+
cnt = cnt * 10 + (*path - '0');
|
|
900
|
+
}
|
|
901
|
+
if ('/' == *path) {
|
|
902
|
+
path++;
|
|
903
|
+
}
|
|
904
|
+
do {
|
|
905
|
+
if (1 >= cnt) {
|
|
906
|
+
lp++;
|
|
907
|
+
*lp = e;
|
|
908
|
+
leaf = get_leaf(stack, lp, path);
|
|
909
|
+
break;
|
|
910
|
+
}
|
|
911
|
+
cnt--;
|
|
912
|
+
e = e->next;
|
|
913
|
+
} while (e != first);
|
|
914
|
+
} else if (T_HASH == type) {
|
|
915
|
+
const char *key = path;
|
|
916
|
+
const char *slash = next_slash(path);
|
|
917
|
+
int klen;
|
|
918
|
+
|
|
919
|
+
leaf = NULL;
|
|
920
|
+
if (0 == slash) {
|
|
921
|
+
klen = (int)strlen(key);
|
|
922
|
+
path += klen;
|
|
923
|
+
} else {
|
|
924
|
+
klen = (int)(slash - key);
|
|
925
|
+
path += klen + 1;
|
|
926
|
+
}
|
|
927
|
+
do {
|
|
928
|
+
if (key_match(key, e->key, klen)) {
|
|
929
|
+
lp++;
|
|
930
|
+
*lp = e;
|
|
931
|
+
leaf = get_leaf(stack, lp, path);
|
|
932
|
+
break;
|
|
933
|
+
}
|
|
934
|
+
e = e->next;
|
|
935
|
+
} while (e != first);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
998
938
|
}
|
|
999
939
|
return leaf;
|
|
1000
940
|
}
|
|
1001
941
|
|
|
1002
|
-
static void
|
|
1003
|
-
each_leaf(Doc doc, VALUE self) {
|
|
942
|
+
static void each_leaf(Doc doc, VALUE self) {
|
|
1004
943
|
if (COL_VAL == (*doc->where)->value_type) {
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
944
|
+
if (0 != (*doc->where)->elements) {
|
|
945
|
+
Leaf first = (*doc->where)->elements->next;
|
|
946
|
+
Leaf e = first;
|
|
947
|
+
|
|
948
|
+
doc->where++;
|
|
949
|
+
if (MAX_STACK <= doc->where - doc->where_path) {
|
|
950
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
951
|
+
}
|
|
952
|
+
do {
|
|
953
|
+
*doc->where = e;
|
|
954
|
+
each_leaf(doc, self);
|
|
955
|
+
e = e->next;
|
|
956
|
+
} while (e != first);
|
|
957
|
+
doc->where--;
|
|
958
|
+
}
|
|
1020
959
|
} else {
|
|
1021
|
-
|
|
960
|
+
rb_yield(self);
|
|
1022
961
|
}
|
|
1023
962
|
}
|
|
1024
963
|
|
|
1025
|
-
static int
|
|
1026
|
-
move_step(Doc doc, const char *path, int loc) {
|
|
964
|
+
static int move_step(Doc doc, const char *path, int loc) {
|
|
1027
965
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
|
1028
|
-
|
|
966
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
1029
967
|
}
|
|
1030
968
|
if ('\0' == *path) {
|
|
1031
|
-
|
|
969
|
+
loc = 0;
|
|
1032
970
|
} 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
|
-
|
|
971
|
+
Leaf leaf;
|
|
972
|
+
|
|
973
|
+
if (0 == doc->where || 0 == (leaf = *doc->where)) {
|
|
974
|
+
printf("*** Internal error at %s\n", path);
|
|
975
|
+
return loc;
|
|
976
|
+
}
|
|
977
|
+
if ('.' == *path && '.' == *(path + 1)) {
|
|
978
|
+
Leaf init = *doc->where;
|
|
979
|
+
|
|
980
|
+
path += 2;
|
|
981
|
+
if (doc->where == doc->where_path) {
|
|
982
|
+
return loc;
|
|
983
|
+
}
|
|
984
|
+
if ('/' == *path) {
|
|
985
|
+
path++;
|
|
986
|
+
}
|
|
987
|
+
*doc->where = 0;
|
|
988
|
+
doc->where--;
|
|
989
|
+
loc = move_step(doc, path, loc + 1);
|
|
990
|
+
if (0 != loc) {
|
|
991
|
+
*doc->where = init;
|
|
992
|
+
doc->where++;
|
|
993
|
+
}
|
|
994
|
+
} else if (COL_VAL == leaf->value_type && 0 != leaf->elements) {
|
|
995
|
+
Leaf first = leaf->elements->next;
|
|
996
|
+
Leaf e = first;
|
|
997
|
+
|
|
998
|
+
if (T_ARRAY == leaf->rtype) {
|
|
999
|
+
int cnt = 0;
|
|
1000
|
+
|
|
1001
|
+
for (; '0' <= *path && *path <= '9'; path++) {
|
|
1002
|
+
cnt = cnt * 10 + (*path - '0');
|
|
1003
|
+
}
|
|
1004
|
+
if ('/' == *path) {
|
|
1005
|
+
path++;
|
|
1006
|
+
} else if ('\0' != *path) {
|
|
1007
|
+
return loc;
|
|
1008
|
+
}
|
|
1009
|
+
do {
|
|
1010
|
+
if (1 >= cnt) {
|
|
1011
|
+
doc->where++;
|
|
1012
|
+
*doc->where = e;
|
|
1013
|
+
loc = move_step(doc, path, loc + 1);
|
|
1014
|
+
if (0 != loc) {
|
|
1015
|
+
*doc->where = 0;
|
|
1016
|
+
doc->where--;
|
|
1017
|
+
}
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
cnt--;
|
|
1021
|
+
e = e->next;
|
|
1022
|
+
} while (e != first);
|
|
1023
|
+
} else if (T_HASH == leaf->rtype) {
|
|
1024
|
+
const char *key = path;
|
|
1025
|
+
const char *slash = next_slash(path);
|
|
1026
|
+
int klen;
|
|
1027
|
+
|
|
1028
|
+
if (0 == slash) {
|
|
1029
|
+
klen = (int)strlen(key);
|
|
1030
|
+
path += klen;
|
|
1031
|
+
} else {
|
|
1032
|
+
klen = (int)(slash - key);
|
|
1033
|
+
path += klen + 1;
|
|
1034
|
+
}
|
|
1035
|
+
do {
|
|
1036
|
+
if (key_match(key, e->key, klen)) {
|
|
1037
|
+
doc->where++;
|
|
1038
|
+
*doc->where = e;
|
|
1039
|
+
loc = move_step(doc, path, loc + 1);
|
|
1040
|
+
if (0 != loc) {
|
|
1041
|
+
*doc->where = 0;
|
|
1042
|
+
doc->where--;
|
|
1043
|
+
}
|
|
1044
|
+
break;
|
|
1045
|
+
}
|
|
1046
|
+
e = e->next;
|
|
1047
|
+
} while (e != first);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1112
1050
|
}
|
|
1113
1051
|
return loc;
|
|
1114
1052
|
}
|
|
1115
1053
|
|
|
1116
|
-
static void
|
|
1117
|
-
each_value(Doc doc, Leaf leaf) {
|
|
1054
|
+
static void each_value(Doc doc, Leaf leaf) {
|
|
1118
1055
|
if (COL_VAL == leaf->value_type) {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1056
|
+
if (0 != leaf->elements) {
|
|
1057
|
+
Leaf first = leaf->elements->next;
|
|
1058
|
+
Leaf e = first;
|
|
1059
|
+
|
|
1060
|
+
do {
|
|
1061
|
+
each_value(doc, e);
|
|
1062
|
+
e = e->next;
|
|
1063
|
+
} while (e != first);
|
|
1064
|
+
}
|
|
1128
1065
|
} else {
|
|
1129
|
-
|
|
1066
|
+
rb_yield(leaf_value(doc, leaf));
|
|
1130
1067
|
}
|
|
1131
1068
|
}
|
|
1132
1069
|
|
|
@@ -1141,7 +1078,8 @@ each_value(Doc doc, Leaf leaf) {
|
|
|
1141
1078
|
*
|
|
1142
1079
|
* @param [String] json JSON document string
|
|
1143
1080
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
|
1144
|
-
* @yieldreturn [Object] returns the result of the yield as the result of the
|
|
1081
|
+
* @yieldreturn [Object] returns the result of the yield as the result of the
|
|
1082
|
+
* method call
|
|
1145
1083
|
* @example
|
|
1146
1084
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
|
1147
1085
|
* # or as an alternative
|
|
@@ -1149,26 +1087,31 @@ each_value(Doc doc, Leaf leaf) {
|
|
|
1149
1087
|
* doc.size() #=> 4
|
|
1150
1088
|
* doc.close()
|
|
1151
1089
|
*/
|
|
1152
|
-
static VALUE
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
int
|
|
1158
|
-
int allocate;
|
|
1090
|
+
static VALUE doc_open(VALUE clas, VALUE str) {
|
|
1091
|
+
char * json;
|
|
1092
|
+
size_t len;
|
|
1093
|
+
volatile VALUE obj;
|
|
1094
|
+
int given = rb_block_given_p();
|
|
1095
|
+
int allocate;
|
|
1159
1096
|
|
|
1160
1097
|
Check_Type(str, T_STRING);
|
|
1161
|
-
len
|
|
1162
|
-
allocate = (
|
|
1098
|
+
len = (int)RSTRING_LEN(str) + 1;
|
|
1099
|
+
allocate = (SMALL_JSON < len || !given);
|
|
1163
1100
|
if (allocate) {
|
|
1164
|
-
|
|
1101
|
+
json = ALLOC_N(char, len);
|
|
1165
1102
|
} else {
|
|
1166
|
-
|
|
1103
|
+
json = ALLOCA_N(char, len);
|
|
1167
1104
|
}
|
|
1105
|
+
// It should not be necessaary to stop GC but if it is not stopped and a
|
|
1106
|
+
// large string is parsed that string is corrupted or freed during
|
|
1107
|
+
// parsing. I'm not sure what is going on exactly but disabling GC avoids
|
|
1108
|
+
// the issue.
|
|
1109
|
+
rb_gc_disable();
|
|
1168
1110
|
memcpy(json, StringValuePtr(str), len);
|
|
1169
1111
|
obj = parse_json(clas, json, given, allocate);
|
|
1112
|
+
rb_gc_enable();
|
|
1170
1113
|
if (given && allocate) {
|
|
1171
|
-
|
|
1114
|
+
xfree(json);
|
|
1172
1115
|
}
|
|
1173
1116
|
return obj;
|
|
1174
1117
|
}
|
|
@@ -1182,7 +1125,8 @@ doc_open(VALUE clas, VALUE str) {
|
|
|
1182
1125
|
*
|
|
1183
1126
|
* @param [String] filename name of file that contains a JSON document
|
|
1184
1127
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
|
1185
|
-
* @yieldreturn [Object] returns the result of the yield as the result of the
|
|
1128
|
+
* @yieldreturn [Object] returns the result of the yield as the result of the
|
|
1129
|
+
* method call
|
|
1186
1130
|
* @example
|
|
1187
1131
|
* File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
|
|
1188
1132
|
* Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
|
|
@@ -1191,63 +1135,64 @@ doc_open(VALUE clas, VALUE str) {
|
|
|
1191
1135
|
* doc.size() #=> 4
|
|
1192
1136
|
* doc.close()
|
|
1193
1137
|
*/
|
|
1194
|
-
static VALUE
|
|
1195
|
-
|
|
1196
|
-
char
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
int
|
|
1202
|
-
int allocate;
|
|
1138
|
+
static VALUE doc_open_file(VALUE clas, VALUE filename) {
|
|
1139
|
+
char * path;
|
|
1140
|
+
char * json;
|
|
1141
|
+
FILE * f;
|
|
1142
|
+
size_t len;
|
|
1143
|
+
volatile VALUE obj;
|
|
1144
|
+
int given = rb_block_given_p();
|
|
1145
|
+
int allocate;
|
|
1203
1146
|
|
|
1204
1147
|
Check_Type(filename, T_STRING);
|
|
1205
1148
|
path = StringValuePtr(filename);
|
|
1206
1149
|
if (0 == (f = fopen(path, "r"))) {
|
|
1207
|
-
|
|
1150
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
|
1208
1151
|
}
|
|
1209
1152
|
fseek(f, 0, SEEK_END);
|
|
1210
|
-
len
|
|
1211
|
-
allocate = (
|
|
1153
|
+
len = ftell(f);
|
|
1154
|
+
allocate = (SMALL_JSON < len || !given);
|
|
1212
1155
|
if (allocate) {
|
|
1213
|
-
|
|
1156
|
+
json = ALLOC_N(char, len + 1);
|
|
1214
1157
|
} else {
|
|
1215
|
-
|
|
1158
|
+
json = ALLOCA_N(char, len + 1);
|
|
1216
1159
|
}
|
|
1217
1160
|
fseek(f, 0, SEEK_SET);
|
|
1218
1161
|
if (len != fread(json, 1, len, f)) {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1162
|
+
fclose(f);
|
|
1163
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("LoadError")),
|
|
1164
|
+
"Failed to read %lu bytes from %s.",
|
|
1165
|
+
(unsigned long)len,
|
|
1166
|
+
path);
|
|
1222
1167
|
}
|
|
1223
1168
|
fclose(f);
|
|
1224
1169
|
json[len] = '\0';
|
|
1170
|
+
rb_gc_disable();
|
|
1225
1171
|
obj = parse_json(clas, json, given, allocate);
|
|
1172
|
+
rb_gc_enable();
|
|
1226
1173
|
if (given && allocate) {
|
|
1227
|
-
|
|
1174
|
+
xfree(json);
|
|
1228
1175
|
}
|
|
1229
1176
|
return obj;
|
|
1230
1177
|
}
|
|
1231
1178
|
|
|
1232
|
-
static int
|
|
1233
|
-
|
|
1234
|
-
int cnt = 0;
|
|
1179
|
+
static int esc_strlen(const char *s) {
|
|
1180
|
+
int cnt = 0;
|
|
1235
1181
|
|
|
1236
1182
|
for (; '\0' != *s; s++, cnt++) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1183
|
+
if ('/' == *s) {
|
|
1184
|
+
cnt++;
|
|
1185
|
+
}
|
|
1240
1186
|
}
|
|
1241
1187
|
return cnt;
|
|
1242
1188
|
}
|
|
1243
1189
|
|
|
1244
|
-
static char*
|
|
1245
|
-
append_key(char *p, const char *key) {
|
|
1190
|
+
static char *append_key(char *p, const char *key) {
|
|
1246
1191
|
for (; '\0' != *key; p++, key++) {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1192
|
+
if ('/' == *key) {
|
|
1193
|
+
*p++ = '\\';
|
|
1194
|
+
}
|
|
1195
|
+
*p = *key;
|
|
1251
1196
|
}
|
|
1252
1197
|
return p;
|
|
1253
1198
|
}
|
|
@@ -1256,68 +1201,85 @@ append_key(char *p, const char *key) {
|
|
|
1256
1201
|
* @see Oj::Doc.open
|
|
1257
1202
|
*/
|
|
1258
1203
|
|
|
1259
|
-
/* @overload where
|
|
1204
|
+
/* @overload where() => String
|
|
1260
1205
|
*
|
|
1261
1206
|
* Returns a String that describes the absolute path to the current location
|
|
1262
1207
|
* in the JSON document.
|
|
1263
1208
|
*/
|
|
1264
|
-
static VALUE
|
|
1265
|
-
|
|
1266
|
-
Doc doc = self_doc(self);
|
|
1209
|
+
static VALUE doc_where(VALUE self) {
|
|
1210
|
+
Doc doc = self_doc(self);
|
|
1267
1211
|
|
|
1268
1212
|
if (0 == *doc->where_path || doc->where == doc->where_path) {
|
|
1269
|
-
|
|
1213
|
+
return oj_slash_string;
|
|
1270
1214
|
} 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
|
-
|
|
1215
|
+
Leaf * lp;
|
|
1216
|
+
Leaf leaf;
|
|
1217
|
+
size_t size = 3; // leading / and terminating \0
|
|
1218
|
+
char * path;
|
|
1219
|
+
char * p;
|
|
1220
|
+
|
|
1221
|
+
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
|
1222
|
+
leaf = *lp;
|
|
1223
|
+
if (T_HASH == leaf->parent_type) {
|
|
1224
|
+
size += esc_strlen((*lp)->key) + 1;
|
|
1225
|
+
} else if (T_ARRAY == leaf->parent_type) {
|
|
1226
|
+
size += ((*lp)->index < 100) ? 3 : 11;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
path = ALLOCA_N(char, size);
|
|
1230
|
+
p = path;
|
|
1231
|
+
for (lp = doc->where_path; lp <= doc->where; lp++) {
|
|
1232
|
+
leaf = *lp;
|
|
1233
|
+
if (T_HASH == leaf->parent_type) {
|
|
1234
|
+
p = append_key(p, (*lp)->key);
|
|
1235
|
+
} else if (T_ARRAY == leaf->parent_type) {
|
|
1236
|
+
p = ulong_fill(p, (*lp)->index);
|
|
1237
|
+
}
|
|
1238
|
+
*p++ = '/';
|
|
1239
|
+
}
|
|
1240
|
+
*--p = '\0';
|
|
1241
|
+
|
|
1242
|
+
return rb_str_new(path, p - path);
|
|
1299
1243
|
}
|
|
1300
1244
|
}
|
|
1301
1245
|
|
|
1246
|
+
/* @overload where?() => String
|
|
1247
|
+
* @deprecated
|
|
1248
|
+
* Returns a String that describes the absolute path to the current location
|
|
1249
|
+
* in the JSON document.
|
|
1250
|
+
*/
|
|
1251
|
+
static VALUE doc_where_q(VALUE self) {
|
|
1252
|
+
return doc_where(self);
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/* @overload path() => String
|
|
1256
|
+
*
|
|
1257
|
+
* Returns a String that describes the absolute path to the current location
|
|
1258
|
+
* in the JSON document.
|
|
1259
|
+
*/
|
|
1260
|
+
static VALUE doc_path(VALUE self) {
|
|
1261
|
+
return doc_where(self);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1302
1264
|
/* @overload local_key() => String, Fixnum, nil
|
|
1303
1265
|
*
|
|
1304
1266
|
* Returns the final key to the current location.
|
|
1305
1267
|
* @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
|
-
*
|
|
1268
|
+
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() } #=> 2
|
|
1269
|
+
* Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
|
|
1270
|
+
* "one" Oj::Doc.open('[1,2,3]') { |doc| doc.local_key() }
|
|
1271
|
+
* #=> nil
|
|
1309
1272
|
*/
|
|
1310
|
-
static VALUE
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
volatile VALUE key = Qnil;
|
|
1273
|
+
static VALUE doc_local_key(VALUE self) {
|
|
1274
|
+
Doc doc = self_doc(self);
|
|
1275
|
+
Leaf leaf = *doc->where;
|
|
1276
|
+
volatile VALUE key = Qnil;
|
|
1315
1277
|
|
|
1316
1278
|
if (T_HASH == leaf->parent_type) {
|
|
1317
|
-
|
|
1318
|
-
|
|
1279
|
+
key = rb_str_new2(leaf->key);
|
|
1280
|
+
key = oj_encode(key);
|
|
1319
1281
|
} else if (T_ARRAY == leaf->parent_type) {
|
|
1320
|
-
|
|
1282
|
+
key = LONG2NUM(leaf->index);
|
|
1321
1283
|
}
|
|
1322
1284
|
return key;
|
|
1323
1285
|
}
|
|
@@ -1327,14 +1289,14 @@ doc_local_key(VALUE self) {
|
|
|
1327
1289
|
* Moves the document marker or location to the hoot or home position. The
|
|
1328
1290
|
* same operation can be performed with a Oj::Doc.move('/').
|
|
1329
1291
|
* @example
|
|
1330
|
-
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
|
|
1292
|
+
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.home(); doc.where? }
|
|
1293
|
+
* #=> '/'
|
|
1331
1294
|
*/
|
|
1332
|
-
static VALUE
|
|
1333
|
-
|
|
1334
|
-
Doc doc = self_doc(self);
|
|
1295
|
+
static VALUE doc_home(VALUE self) {
|
|
1296
|
+
Doc doc = self_doc(self);
|
|
1335
1297
|
|
|
1336
1298
|
*doc->where_path = doc->data;
|
|
1337
|
-
doc->where
|
|
1299
|
+
doc->where = doc->where_path;
|
|
1338
1300
|
|
|
1339
1301
|
return oj_slash_string;
|
|
1340
1302
|
}
|
|
@@ -1350,76 +1312,98 @@ doc_home(VALUE self) {
|
|
|
1350
1312
|
* Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
|
|
1351
1313
|
* Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
|
|
1352
1314
|
*/
|
|
1353
|
-
static VALUE
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
VALUE type = Qnil;
|
|
1315
|
+
static VALUE doc_type(int argc, VALUE *argv, VALUE self) {
|
|
1316
|
+
Doc doc = self_doc(self);
|
|
1317
|
+
Leaf leaf;
|
|
1318
|
+
const char *path = 0;
|
|
1319
|
+
VALUE type = Qnil;
|
|
1359
1320
|
|
|
1360
1321
|
if (1 <= argc) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1322
|
+
Check_Type(*argv, T_STRING);
|
|
1323
|
+
path = StringValuePtr(*argv);
|
|
1363
1324
|
}
|
|
1364
1325
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1326
|
+
switch (leaf->rtype) {
|
|
1327
|
+
case T_NIL: type = rb_cNilClass; break;
|
|
1328
|
+
case T_TRUE: type = rb_cTrueClass; break;
|
|
1329
|
+
case T_FALSE: type = rb_cFalseClass; break;
|
|
1330
|
+
case T_STRING: type = rb_cString; break;
|
|
1370
1331
|
#ifdef RUBY_INTEGER_UNIFICATION
|
|
1371
|
-
|
|
1332
|
+
case T_FIXNUM: type = rb_cInteger; break;
|
|
1372
1333
|
#else
|
|
1373
|
-
|
|
1334
|
+
case T_FIXNUM: type = rb_cFixnum; break;
|
|
1374
1335
|
#endif
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1336
|
+
case T_FLOAT: type = rb_cFloat; break;
|
|
1337
|
+
case T_ARRAY: type = rb_cArray; break;
|
|
1338
|
+
case T_HASH: type = rb_cHash; break;
|
|
1339
|
+
default: break;
|
|
1340
|
+
}
|
|
1380
1341
|
}
|
|
1381
1342
|
return type;
|
|
1382
1343
|
}
|
|
1383
1344
|
|
|
1384
|
-
/* @overload fetch(path=nil) => nil, true, false, Fixnum, Float, String, Array,
|
|
1345
|
+
/* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
|
|
1346
|
+
* Hash
|
|
1385
1347
|
*
|
|
1386
1348
|
* Returns the value at the location identified by the path or the current
|
|
1387
1349
|
* location if the path is nil or not provided. This method will create and
|
|
1388
1350
|
* return an Array or Hash if that is the type of Object at the location
|
|
1389
1351
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
|
1390
|
-
* document.
|
|
1352
|
+
* document. If a default is provided that is used if no value if found.
|
|
1391
1353
|
* @param [String] path path to the location to get the type of if provided
|
|
1392
1354
|
* @example
|
|
1393
1355
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
|
1394
1356
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
|
|
1395
1357
|
*/
|
|
1396
|
-
static VALUE
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
const char *path = 0;
|
|
1358
|
+
static VALUE doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
1359
|
+
Doc doc;
|
|
1360
|
+
Leaf leaf;
|
|
1361
|
+
volatile VALUE val = Qnil;
|
|
1362
|
+
const char * path = 0;
|
|
1402
1363
|
|
|
1403
1364
|
doc = self_doc(self);
|
|
1404
1365
|
if (1 <= argc) {
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1366
|
+
Check_Type(*argv, T_STRING);
|
|
1367
|
+
path = StringValuePtr(*argv);
|
|
1368
|
+
if (2 == argc) {
|
|
1369
|
+
val = argv[1];
|
|
1370
|
+
}
|
|
1410
1371
|
}
|
|
1411
1372
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1412
|
-
|
|
1373
|
+
val = leaf_value(doc, leaf);
|
|
1413
1374
|
}
|
|
1414
1375
|
return val;
|
|
1415
1376
|
}
|
|
1416
1377
|
|
|
1378
|
+
/* @overload exists?(path) => true, false
|
|
1379
|
+
*
|
|
1380
|
+
* Returns true if the value at the location identified by the path exists.
|
|
1381
|
+
* @param [String] path path to the location
|
|
1382
|
+
* @example
|
|
1383
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists?('/1') } #=> true
|
|
1384
|
+
* Oj::Doc.open('[1,2]') { |doc| doc.exists?('/3') } #=> false
|
|
1385
|
+
*/
|
|
1386
|
+
static VALUE doc_exists(VALUE self, VALUE str) {
|
|
1387
|
+
Doc doc;
|
|
1388
|
+
Leaf leaf;
|
|
1389
|
+
|
|
1390
|
+
doc = self_doc(self);
|
|
1391
|
+
Check_Type(str, T_STRING);
|
|
1392
|
+
if (0 != (leaf = get_doc_leaf(doc, StringValuePtr(str)))) {
|
|
1393
|
+
if (NULL != leaf) {
|
|
1394
|
+
return Qtrue;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
return Qfalse;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1417
1400
|
/* @overload each_leaf(path=nil) => nil
|
|
1418
1401
|
*
|
|
1419
1402
|
* Yields to the provided block for each leaf node with the identified
|
|
1420
1403
|
* location of the JSON document as the root. The parameter passed to the
|
|
1421
1404
|
* 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
|
|
1405
|
+
* @param [String] path if provided it identified the top of the branch to
|
|
1406
|
+
* process the leaves of
|
|
1423
1407
|
* @yieldparam [Doc] Doc at the child location
|
|
1424
1408
|
* @example
|
|
1425
1409
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
@@ -1429,36 +1413,35 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
|
1429
1413
|
* }
|
|
1430
1414
|
* #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
|
|
1431
1415
|
*/
|
|
1432
|
-
static VALUE
|
|
1433
|
-
doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1416
|
+
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1434
1417
|
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
|
-
|
|
1418
|
+
Leaf save_path[MAX_STACK];
|
|
1419
|
+
Doc doc = self_doc(self);
|
|
1420
|
+
const char *path = 0;
|
|
1421
|
+
size_t wlen;
|
|
1422
|
+
|
|
1423
|
+
wlen = doc->where - doc->where_path;
|
|
1424
|
+
if (0 < wlen) {
|
|
1425
|
+
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
|
1426
|
+
}
|
|
1427
|
+
if (1 <= argc) {
|
|
1428
|
+
Check_Type(*argv, T_STRING);
|
|
1429
|
+
path = StringValuePtr(*argv);
|
|
1430
|
+
if ('/' == *path) {
|
|
1431
|
+
doc->where = doc->where_path;
|
|
1432
|
+
path++;
|
|
1433
|
+
}
|
|
1434
|
+
if (0 != move_step(doc, path, 1)) {
|
|
1435
|
+
if (0 < wlen) {
|
|
1436
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
1437
|
+
}
|
|
1438
|
+
return Qnil;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
each_leaf(doc, self);
|
|
1442
|
+
if (0 < wlen) {
|
|
1443
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
1444
|
+
}
|
|
1462
1445
|
}
|
|
1463
1446
|
return Qnil;
|
|
1464
1447
|
}
|
|
@@ -1469,22 +1452,22 @@ doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
|
1469
1452
|
* path or a relative path.
|
|
1470
1453
|
* @param [String] path path to the location to move to
|
|
1471
1454
|
* @example
|
|
1472
|
-
* Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
|
|
1455
|
+
* Oj::Doc.open('{"one":[1,2]') { |doc| doc.move('/one/2'); doc.where? } #=>
|
|
1456
|
+
* "/one/2"
|
|
1473
1457
|
*/
|
|
1474
|
-
static VALUE
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
int loc;
|
|
1458
|
+
static VALUE doc_move(VALUE self, VALUE str) {
|
|
1459
|
+
Doc doc = self_doc(self);
|
|
1460
|
+
const char *path;
|
|
1461
|
+
int loc;
|
|
1479
1462
|
|
|
1480
1463
|
Check_Type(str, T_STRING);
|
|
1481
1464
|
path = StringValuePtr(str);
|
|
1482
1465
|
if ('/' == *path) {
|
|
1483
|
-
|
|
1484
|
-
|
|
1466
|
+
doc->where = doc->where_path;
|
|
1467
|
+
path++;
|
|
1485
1468
|
}
|
|
1486
1469
|
if (0 != (loc = move_step(doc, path, 1))) {
|
|
1487
|
-
|
|
1470
|
+
rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
|
|
1488
1471
|
}
|
|
1489
1472
|
return Qnil;
|
|
1490
1473
|
}
|
|
@@ -1495,7 +1478,8 @@ doc_move(VALUE self, VALUE str) {
|
|
|
1495
1478
|
* identified location of the JSON document as the root. The parameter passed
|
|
1496
1479
|
* to the block on yield is the Doc instance after moving to the child
|
|
1497
1480
|
* location.
|
|
1498
|
-
* @param [String] path if provided it identified the top of the branch to
|
|
1481
|
+
* @param [String] path if provided it identified the top of the branch to
|
|
1482
|
+
* process the children of
|
|
1499
1483
|
* @yieldparam [Doc] Doc at the child location
|
|
1500
1484
|
* @example
|
|
1501
1485
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
@@ -1505,46 +1489,51 @@ doc_move(VALUE self, VALUE str) {
|
|
|
1505
1489
|
* }
|
|
1506
1490
|
* #=> ["/2/1", "/2/2"]
|
|
1507
1491
|
*/
|
|
1508
|
-
static VALUE
|
|
1509
|
-
doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1492
|
+
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1510
1493
|
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
|
-
|
|
1494
|
+
Leaf save_path[MAX_STACK];
|
|
1495
|
+
Doc doc = self_doc(self);
|
|
1496
|
+
const char *path = 0;
|
|
1497
|
+
size_t wlen;
|
|
1498
|
+
Leaf * where_orig = doc->where;
|
|
1499
|
+
|
|
1500
|
+
wlen = doc->where - doc->where_path;
|
|
1501
|
+
if (0 < wlen) {
|
|
1502
|
+
memcpy(save_path, doc->where_path, sizeof(Leaf) * (wlen + 1));
|
|
1503
|
+
}
|
|
1504
|
+
if (1 <= argc) {
|
|
1505
|
+
Check_Type(*argv, T_STRING);
|
|
1506
|
+
path = StringValuePtr(*argv);
|
|
1507
|
+
if ('/' == *path) {
|
|
1508
|
+
doc->where = doc->where_path;
|
|
1509
|
+
path++;
|
|
1510
|
+
}
|
|
1511
|
+
if (0 != move_step(doc, path, 1)) {
|
|
1512
|
+
if (0 < wlen) {
|
|
1513
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
1514
|
+
}
|
|
1515
|
+
doc->where = where_orig;
|
|
1516
|
+
return Qnil;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (NULL == doc->where || NULL == *doc->where) {
|
|
1520
|
+
return Qnil;
|
|
1521
|
+
}
|
|
1522
|
+
if (COL_VAL == (*doc->where)->value_type && 0 != (*doc->where)->elements) {
|
|
1523
|
+
Leaf first = (*doc->where)->elements->next;
|
|
1524
|
+
Leaf e = first;
|
|
1525
|
+
|
|
1526
|
+
doc->where++;
|
|
1527
|
+
do {
|
|
1528
|
+
*doc->where = e;
|
|
1529
|
+
rb_yield(self);
|
|
1530
|
+
e = e->next;
|
|
1531
|
+
} while (e != first);
|
|
1532
|
+
}
|
|
1533
|
+
if (0 < wlen) {
|
|
1534
|
+
memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
|
|
1535
|
+
}
|
|
1536
|
+
doc->where = where_orig;
|
|
1548
1537
|
}
|
|
1549
1538
|
return Qnil;
|
|
1550
1539
|
}
|
|
@@ -1555,7 +1544,8 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1555
1544
|
* of the JSON document. The parameter passed to the block on yield is the
|
|
1556
1545
|
* value of the leaf. Only those leaves below the element specified by the
|
|
1557
1546
|
* path parameter are processed.
|
|
1558
|
-
* @param [String] path if provided it identified the top of the branch to
|
|
1547
|
+
* @param [String] path if provided it identified the top of the branch to
|
|
1548
|
+
* process the leaf values of
|
|
1559
1549
|
* @yieldparam [Object] val each leaf value
|
|
1560
1550
|
* @example
|
|
1561
1551
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
@@ -1564,7 +1554,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1564
1554
|
* result
|
|
1565
1555
|
* }
|
|
1566
1556
|
* #=> [3, 2, 1]
|
|
1567
|
-
*
|
|
1557
|
+
*
|
|
1568
1558
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
1569
1559
|
* result = []
|
|
1570
1560
|
* doc.each_value('/2') { |v| result << v }
|
|
@@ -1572,20 +1562,19 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1572
1562
|
* }
|
|
1573
1563
|
* #=> [2, 1]
|
|
1574
1564
|
*/
|
|
1575
|
-
static VALUE
|
|
1576
|
-
doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1565
|
+
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1577
1566
|
if (rb_block_given_p()) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1567
|
+
Doc doc = self_doc(self);
|
|
1568
|
+
const char *path = 0;
|
|
1569
|
+
Leaf leaf;
|
|
1570
|
+
|
|
1571
|
+
if (1 <= argc) {
|
|
1572
|
+
Check_Type(*argv, T_STRING);
|
|
1573
|
+
path = StringValuePtr(*argv);
|
|
1574
|
+
}
|
|
1575
|
+
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1576
|
+
each_value(doc, leaf);
|
|
1577
|
+
}
|
|
1589
1578
|
}
|
|
1590
1579
|
return Qnil;
|
|
1591
1580
|
}
|
|
@@ -1594,52 +1583,50 @@ doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
|
1594
1583
|
*
|
|
1595
1584
|
* Dumps the document or nodes to a new JSON document. It uses the default
|
|
1596
1585
|
* options for generating the JSON.
|
|
1597
|
-
* @param path [String] if provided it identified the top of the branch to
|
|
1598
|
-
*
|
|
1586
|
+
* @param path [String] if provided it identified the top of the branch to
|
|
1587
|
+
* dump to JSON
|
|
1588
|
+
* @param filename [String] if provided it is the filename to write the output
|
|
1589
|
+
* to
|
|
1599
1590
|
* @example
|
|
1600
1591
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
1601
1592
|
* doc.dump('/2')
|
|
1602
1593
|
* }
|
|
1603
1594
|
* #=> "[2,1]"
|
|
1604
1595
|
*/
|
|
1605
|
-
static VALUE
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
const char
|
|
1610
|
-
const char *filename = 0;
|
|
1596
|
+
static VALUE doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
1597
|
+
Doc doc = self_doc(self);
|
|
1598
|
+
Leaf leaf;
|
|
1599
|
+
const char *path = 0;
|
|
1600
|
+
const char *filename = 0;
|
|
1611
1601
|
|
|
1612
1602
|
if (1 <= argc) {
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1603
|
+
if (Qnil != *argv) {
|
|
1604
|
+
Check_Type(*argv, T_STRING);
|
|
1605
|
+
path = StringValuePtr(*argv);
|
|
1606
|
+
}
|
|
1607
|
+
if (2 <= argc) {
|
|
1608
|
+
Check_Type(argv[1], T_STRING);
|
|
1609
|
+
filename = StringValuePtr(argv[1]);
|
|
1610
|
+
}
|
|
1621
1611
|
}
|
|
1622
1612
|
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;
|
|
1613
|
+
volatile VALUE rjson;
|
|
1614
|
+
|
|
1615
|
+
if (0 == filename) {
|
|
1616
|
+
struct _out out;
|
|
1617
|
+
|
|
1618
|
+
oj_out_init(&out);
|
|
1619
|
+
|
|
1620
|
+
out.omit_nil = oj_default_options.dump_opts.omit_nil;
|
|
1621
|
+
oj_dump_leaf_to_json(leaf, &oj_default_options, &out);
|
|
1622
|
+
rjson = rb_str_new2(out.buf);
|
|
1623
|
+
|
|
1624
|
+
oj_out_free(&out);
|
|
1625
|
+
} else {
|
|
1626
|
+
oj_write_leaf_to_file(leaf, filename, &oj_default_options);
|
|
1627
|
+
rjson = Qnil;
|
|
1628
|
+
}
|
|
1629
|
+
return rjson;
|
|
1643
1630
|
}
|
|
1644
1631
|
return Qnil;
|
|
1645
1632
|
}
|
|
@@ -1652,8 +1639,7 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
|
1652
1639
|
* @example
|
|
1653
1640
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
|
1654
1641
|
*/
|
|
1655
|
-
static VALUE
|
|
1656
|
-
doc_size(VALUE self) {
|
|
1642
|
+
static VALUE doc_size(VALUE self) {
|
|
1657
1643
|
return ULONG2NUM(((Doc)DATA_PTR(self))->size);
|
|
1658
1644
|
}
|
|
1659
1645
|
|
|
@@ -1666,16 +1652,15 @@ doc_size(VALUE self) {
|
|
|
1666
1652
|
* doc.size() #=> 4
|
|
1667
1653
|
* doc.close()
|
|
1668
1654
|
*/
|
|
1669
|
-
static VALUE
|
|
1670
|
-
|
|
1671
|
-
Doc doc = self_doc(self);
|
|
1655
|
+
static VALUE doc_close(VALUE self) {
|
|
1656
|
+
Doc doc = self_doc(self);
|
|
1672
1657
|
|
|
1673
1658
|
rb_gc_unregister_address(&doc->self);
|
|
1674
1659
|
DATA_PTR(doc->self) = 0;
|
|
1675
1660
|
if (0 != doc) {
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1661
|
+
xfree(doc->json);
|
|
1662
|
+
doc_free(doc);
|
|
1663
|
+
xfree(doc);
|
|
1679
1664
|
}
|
|
1680
1665
|
return Qnil;
|
|
1681
1666
|
}
|
|
@@ -1684,8 +1669,7 @@ doc_close(VALUE self) {
|
|
|
1684
1669
|
Oj = rb_define_module("Oj");
|
|
1685
1670
|
#endif
|
|
1686
1671
|
|
|
1687
|
-
static VALUE
|
|
1688
|
-
doc_not_implemented(VALUE self) {
|
|
1672
|
+
static VALUE doc_not_implemented(VALUE self) {
|
|
1689
1673
|
rb_raise(rb_eNotImpError, "Not implemented.");
|
|
1690
1674
|
return Qnil;
|
|
1691
1675
|
}
|
|
@@ -1697,21 +1681,21 @@ doc_not_implemented(VALUE self) {
|
|
|
1697
1681
|
* extracted. Once the document is closed the document can not longer be
|
|
1698
1682
|
* accessed. This allows the parsing and data extraction to be extremely fast
|
|
1699
1683
|
* compared to other JSON parses.
|
|
1700
|
-
*
|
|
1684
|
+
*
|
|
1701
1685
|
* An Oj::Doc class is not created directly but the _open()_ class method is
|
|
1702
1686
|
* used to open a document and the yield parameter to the block of the #open()
|
|
1703
1687
|
* call is the Doc instance. The Doc instance can be moved across, up, and
|
|
1704
1688
|
* down the JSON document. At each element the data associated with the
|
|
1705
1689
|
* element can be extracted. It is also possible to just provide a path to the
|
|
1706
1690
|
* data to be extracted and retrieve the data in that manner.
|
|
1707
|
-
*
|
|
1691
|
+
*
|
|
1708
1692
|
* For many of the methods a path is used to describe the location of an
|
|
1709
1693
|
* element. Paths follow a subset of the XPath syntax. The slash ('/')
|
|
1710
1694
|
* character is the separator. Each step in the path identifies the next
|
|
1711
1695
|
* branch to take through the document. A JSON object will expect a key string
|
|
1712
1696
|
* while an array will expect a positive index. A .. step indicates a move up
|
|
1713
1697
|
* the JSON document.
|
|
1714
|
-
*
|
|
1698
|
+
*
|
|
1715
1699
|
* @example
|
|
1716
1700
|
* json = %{[
|
|
1717
1701
|
* {
|
|
@@ -1725,28 +1709,29 @@ doc_not_implemented(VALUE self) {
|
|
|
1725
1709
|
* ]}
|
|
1726
1710
|
* # move and get value
|
|
1727
1711
|
* 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
|
|
1712
|
+
* doc.move('/1/two')
|
|
1713
|
+
* # doc location is now at the 'two' element of the hash that is the first
|
|
1714
|
+
* element of the array. doc.fetch() end
|
|
1732
1715
|
* #=> 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()
|
|
1716
|
+
*
|
|
1717
|
+
* # Now try again using a path to Oj::Doc.fetch() directly and not using a
|
|
1718
|
+
* block. doc = Oj::Doc.open(json) doc.fetch('/2/three') #=> 3 doc.close()
|
|
1738
1719
|
*/
|
|
1739
|
-
void
|
|
1740
|
-
oj_init_doc() {
|
|
1720
|
+
void oj_init_doc(void) {
|
|
1741
1721
|
oj_doc_class = rb_define_class_under(Oj, "Doc", rb_cObject);
|
|
1722
|
+
rb_gc_register_address(&oj_doc_class);
|
|
1723
|
+
rb_undef_alloc_func(oj_doc_class);
|
|
1742
1724
|
rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
|
|
1743
1725
|
rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
|
|
1744
1726
|
rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
|
|
1745
|
-
rb_define_method(oj_doc_class, "where?",
|
|
1727
|
+
rb_define_method(oj_doc_class, "where?", doc_where_q, 0);
|
|
1728
|
+
rb_define_method(oj_doc_class, "where", doc_where, 0);
|
|
1729
|
+
rb_define_method(oj_doc_class, "path", doc_path, 0);
|
|
1746
1730
|
rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
|
|
1747
1731
|
rb_define_method(oj_doc_class, "home", doc_home, 0);
|
|
1748
1732
|
rb_define_method(oj_doc_class, "type", doc_type, -1);
|
|
1749
1733
|
rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
|
|
1734
|
+
rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
|
|
1750
1735
|
rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
|
|
1751
1736
|
rb_define_method(oj_doc_class, "move", doc_move, 1);
|
|
1752
1737
|
rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);
|