oj 2.18.3 → 3.13.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1324 -0
- data/README.md +51 -204
- data/RELEASE_NOTES.md +61 -0
- data/ext/oj/buf.h +49 -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 -68
- data/ext/oj/circarray.h +16 -42
- data/ext/oj/code.c +221 -0
- data/ext/oj/code.h +40 -0
- data/ext/oj/compat.c +231 -107
- data/ext/oj/custom.c +1125 -0
- data/ext/oj/debug.c +132 -0
- data/ext/oj/dump.c +935 -2513
- data/ext/oj/dump.h +108 -0
- data/ext/oj/dump_compat.c +936 -0
- data/ext/oj/dump_leaf.c +164 -0
- data/ext/oj/dump_object.c +761 -0
- data/ext/oj/dump_strict.c +410 -0
- data/ext/oj/encode.h +7 -42
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +40 -54
- data/ext/oj/err.h +52 -46
- data/ext/oj/extconf.rb +21 -30
- data/ext/oj/fast.c +1097 -1080
- data/ext/oj/intern.c +301 -0
- data/ext/oj/intern.h +26 -0
- data/ext/oj/mimic_json.c +893 -0
- data/ext/oj/object.c +549 -620
- data/ext/oj/odd.c +155 -167
- data/ext/oj/odd.h +37 -63
- data/ext/oj/oj.c +1661 -2063
- data/ext/oj/oj.h +341 -270
- data/ext/oj/parse.c +974 -737
- data/ext/oj/parse.h +105 -97
- data/ext/oj/parser.c +1526 -0
- data/ext/oj/parser.h +90 -0
- data/ext/oj/rails.c +1504 -0
- data/ext/oj/rails.h +18 -0
- data/ext/oj/reader.c +141 -163
- data/ext/oj/reader.h +75 -113
- data/ext/oj/resolve.c +45 -93
- data/ext/oj/resolve.h +7 -34
- data/ext/oj/rxclass.c +143 -0
- data/ext/oj/rxclass.h +26 -0
- data/ext/oj/saj.c +447 -511
- data/ext/oj/saj2.c +348 -0
- data/ext/oj/scp.c +91 -138
- data/ext/oj/sparse.c +793 -644
- data/ext/oj/stream_writer.c +331 -0
- data/ext/oj/strict.c +145 -109
- data/ext/oj/string_writer.c +493 -0
- data/ext/oj/trace.c +72 -0
- data/ext/oj/trace.h +28 -0
- 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 +62 -70
- data/ext/oj/val_stack.h +95 -129
- data/ext/oj/validate.c +51 -0
- data/ext/oj/wab.c +622 -0
- data/lib/oj/bag.rb +1 -0
- data/lib/oj/easy_hash.rb +17 -8
- data/lib/oj/error.rb +10 -11
- data/lib/oj/json.rb +176 -0
- data/lib/oj/mimic.rb +158 -19
- data/lib/oj/state.rb +132 -0
- data/lib/oj/version.rb +2 -2
- data/lib/oj.rb +1 -31
- data/pages/Advanced.md +22 -0
- data/pages/Compatibility.md +25 -0
- data/pages/Custom.md +23 -0
- data/pages/Encoding.md +65 -0
- data/pages/JsonGem.md +94 -0
- data/pages/Modes.md +161 -0
- data/pages/Options.md +327 -0
- data/pages/Parser.md +309 -0
- data/pages/Rails.md +167 -0
- data/pages/Security.md +20 -0
- data/pages/WAB.md +13 -0
- data/test/activerecord/result_test.rb +32 -0
- data/test/activesupport4/decoding_test.rb +108 -0
- data/test/activesupport4/encoding_test.rb +531 -0
- data/test/activesupport4/test_helper.rb +41 -0
- data/test/activesupport5/abstract_unit.rb +45 -0
- data/test/activesupport5/decoding_test.rb +133 -0
- data/test/activesupport5/encoding_test.rb +500 -0
- data/test/activesupport5/encoding_test_cases.rb +98 -0
- data/test/activesupport5/test_helper.rb +72 -0
- data/test/activesupport5/time_zone_test_helpers.rb +39 -0
- 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 +9 -0
- data/test/baz.rb +16 -0
- data/test/bug.rb +11 -46
- data/test/foo.rb +69 -16
- data/test/helper.rb +10 -1
- data/test/isolated/shared.rb +12 -8
- data/test/isolated/test_mimic_rails_after.rb +3 -3
- data/test/isolated/test_mimic_rails_before.rb +3 -3
- data/test/json_gem/json_addition_test.rb +216 -0
- data/test/json_gem/json_common_interface_test.rb +153 -0
- data/test/json_gem/json_encoding_test.rb +107 -0
- data/test/json_gem/json_ext_parser_test.rb +20 -0
- data/test/json_gem/json_fixtures_test.rb +35 -0
- data/test/json_gem/json_generator_test.rb +397 -0
- data/test/json_gem/json_generic_object_test.rb +90 -0
- data/test/json_gem/json_parser_test.rb +470 -0
- data/test/json_gem/json_string_matching_test.rb +42 -0
- data/test/json_gem/test_helper.rb +26 -0
- data/test/mem.rb +33 -0
- data/test/perf.rb +1 -1
- data/test/perf_compat.rb +30 -28
- data/test/perf_dump.rb +50 -0
- data/test/perf_object.rb +1 -1
- 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 +30 -19
- data/test/perf_wab.rb +131 -0
- data/test/prec.rb +23 -0
- data/test/sample.rb +0 -1
- data/test/sample_json.rb +1 -1
- data/test/test_compat.rb +219 -102
- data/test/test_custom.rb +533 -0
- data/test/test_fast.rb +107 -35
- data/test/test_file.rb +19 -25
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +11 -1
- data/test/test_integer_range.rb +72 -0
- data/test/test_null.rb +376 -0
- data/test/test_object.rb +357 -70
- data/test/test_parser.rb +27 -0
- data/test/test_parser_saj.rb +245 -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 +39 -2
- data/test/test_strict.rb +186 -7
- data/test/test_various.rb +160 -774
- data/test/test_wab.rb +307 -0
- data/test/test_writer.rb +90 -2
- data/test/tests.rb +24 -0
- data/test/tests_mimic.rb +14 -0
- data/test/tests_mimic_addition.rb +7 -0
- data/test/zoo.rb +13 -0
- metadata +194 -56
- data/ext/oj/hash.c +0 -163
- data/ext/oj/hash.h +0 -46
- data/ext/oj/hash_test.c +0 -512
- data/test/activesupport_datetime_test.rb +0 -23
- data/test/bug2.rb +0 -10
- data/test/bug3.rb +0 -46
- data/test/bug_fast.rb +0 -32
- data/test/bug_load.rb +0 -24
- data/test/crash.rb +0 -111
- data/test/curl/curl_oj.rb +0 -46
- data/test/curl/get_oj.rb +0 -24
- data/test/curl/just_curl.rb +0 -31
- data/test/curl/just_oj.rb +0 -51
- data/test/example.rb +0 -11
- data/test/io.rb +0 -48
- data/test/isolated/test_mimic_rails_datetime.rb +0 -27
- data/test/mod.rb +0 -16
- data/test/rails.rb +0 -50
- data/test/russian.rb +0 -18
- data/test/struct.rb +0 -29
- data/test/test_serializer.rb +0 -59
- data/test/write_timebars.rb +0 -31
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 = 0;
|
|
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
|
-
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
|
-
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,695 +342,742 @@ 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);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
static void mark_leaf(Leaf leaf) {
|
|
680
|
+
switch (leaf->value_type) {
|
|
681
|
+
case COL_VAL:
|
|
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;
|
|
778
695
|
}
|
|
779
696
|
}
|
|
780
697
|
|
|
781
|
-
static
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
698
|
+
static void mark_doc(void *ptr) {
|
|
699
|
+
if (NULL != ptr) {
|
|
700
|
+
Doc doc = (Doc)ptr;
|
|
701
|
+
|
|
702
|
+
mark(doc->self);
|
|
703
|
+
mark_leaf(doc->data);
|
|
704
|
+
}
|
|
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;
|
|
756
|
+
|
|
757
|
+
// TBD are both needed? is stack allocation ever needed?
|
|
787
758
|
|
|
788
759
|
if (given) {
|
|
789
|
-
|
|
760
|
+
doc = ALLOCA_N(struct _doc, 1);
|
|
790
761
|
} else {
|
|
791
|
-
|
|
762
|
+
doc = ALLOC(struct _doc);
|
|
792
763
|
}
|
|
793
|
-
|
|
764
|
+
// skip UTF-8 BOM if present
|
|
794
765
|
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
|
795
|
-
|
|
766
|
+
pi.str = json + 3;
|
|
796
767
|
} else {
|
|
797
|
-
|
|
768
|
+
pi.str = json;
|
|
798
769
|
}
|
|
799
770
|
pi.s = pi.str;
|
|
800
771
|
doc_init(doc);
|
|
801
772
|
pi.doc = doc;
|
|
802
773
|
#if IS_WINDOWS
|
|
803
|
-
|
|
774
|
+
// assume a 1M stack and give half to ruby
|
|
775
|
+
pi.stack_min = (void *)((char *)&pi - (512L * 1024L));
|
|
804
776
|
#else
|
|
805
777
|
{
|
|
806
|
-
|
|
778
|
+
struct rlimit lim;
|
|
807
779
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
+
}
|
|
813
786
|
}
|
|
814
787
|
#endif
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
doc->
|
|
818
|
-
#else
|
|
819
|
-
doc->self = rb_data_object_alloc(clas, doc, 0, free_doc_cb);
|
|
820
|
-
#endif
|
|
821
|
-
rb_gc_register_address(&doc->self);
|
|
822
|
-
doc->json = json;
|
|
788
|
+
self = TypedData_Wrap_Struct(clas, &oj_doc_type, doc);
|
|
789
|
+
doc->self = self;
|
|
790
|
+
doc->json = json;
|
|
823
791
|
DATA_PTR(doc->self) = doc;
|
|
824
|
-
result
|
|
792
|
+
result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
|
|
825
793
|
if (given || 0 != ex) {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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();
|
|
832
800
|
} else {
|
|
833
|
-
|
|
801
|
+
result = doc->self;
|
|
834
802
|
}
|
|
835
803
|
if (0 != ex) {
|
|
836
|
-
|
|
804
|
+
rb_jump_tag(ex);
|
|
837
805
|
}
|
|
838
806
|
return result;
|
|
839
807
|
}
|
|
840
808
|
|
|
841
|
-
static Leaf
|
|
842
|
-
|
|
843
|
-
Leaf leaf = *doc->where;
|
|
809
|
+
static Leaf get_doc_leaf(Doc doc, const char *path) {
|
|
810
|
+
Leaf leaf = *doc->where;
|
|
844
811
|
|
|
845
812
|
if (0 != doc->data && 0 != path) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
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);
|
|
866
833
|
}
|
|
867
834
|
return leaf;
|
|
868
835
|
}
|
|
869
836
|
|
|
870
|
-
static const char*
|
|
871
|
-
next_slash(const char *s) {
|
|
837
|
+
static const char *next_slash(const char *s) {
|
|
872
838
|
for (; '\0' != *s; s++) {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
839
|
+
if ('\\' == *s) {
|
|
840
|
+
s++;
|
|
841
|
+
if ('\0' == *s) {
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
} else if ('/' == *s) {
|
|
845
|
+
return s;
|
|
846
|
+
}
|
|
881
847
|
}
|
|
882
848
|
return NULL;
|
|
883
849
|
}
|
|
884
850
|
|
|
885
|
-
static bool
|
|
886
|
-
key_match(const char *pat, const char *key, int plen) {
|
|
851
|
+
static bool key_match(const char *pat, const char *key, int plen) {
|
|
887
852
|
for (; 0 < plen; plen--, pat++, key++) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
853
|
+
if ('\\' == *pat) {
|
|
854
|
+
plen--;
|
|
855
|
+
pat++;
|
|
856
|
+
}
|
|
857
|
+
if (*pat != *key) {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
895
860
|
}
|
|
896
861
|
return '\0' == *key;
|
|
897
862
|
}
|
|
898
863
|
|
|
899
|
-
static Leaf
|
|
900
|
-
|
|
901
|
-
Leaf leaf = *lp;
|
|
864
|
+
static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
|
|
865
|
+
Leaf leaf = *lp;
|
|
902
866
|
|
|
903
867
|
if (MAX_STACK <= lp - stack) {
|
|
904
|
-
|
|
868
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
905
869
|
}
|
|
906
870
|
if ('\0' != *path) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
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
|
+
}
|
|
965
936
|
}
|
|
966
937
|
return leaf;
|
|
967
938
|
}
|
|
968
939
|
|
|
969
|
-
static void
|
|
970
|
-
each_leaf(Doc doc, VALUE self) {
|
|
940
|
+
static void each_leaf(Doc doc, VALUE self) {
|
|
971
941
|
if (COL_VAL == (*doc->where)->value_type) {
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
+
}
|
|
987
957
|
} else {
|
|
988
|
-
|
|
958
|
+
rb_yield(self);
|
|
989
959
|
}
|
|
990
960
|
}
|
|
991
961
|
|
|
992
|
-
static int
|
|
993
|
-
move_step(Doc doc, const char *path, int loc) {
|
|
962
|
+
static int move_step(Doc doc, const char *path, int loc) {
|
|
994
963
|
if (MAX_STACK <= doc->where - doc->where_path) {
|
|
995
|
-
|
|
964
|
+
rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
|
|
996
965
|
}
|
|
997
966
|
if ('\0' == *path) {
|
|
998
|
-
|
|
967
|
+
loc = 0;
|
|
999
968
|
} else {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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
|
+
}
|
|
1079
1048
|
}
|
|
1080
1049
|
return loc;
|
|
1081
1050
|
}
|
|
1082
1051
|
|
|
1083
|
-
static void
|
|
1084
|
-
each_value(Doc doc, Leaf leaf) {
|
|
1052
|
+
static void each_value(Doc doc, Leaf leaf) {
|
|
1085
1053
|
if (COL_VAL == leaf->value_type) {
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
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
|
+
}
|
|
1095
1063
|
} else {
|
|
1096
|
-
|
|
1064
|
+
rb_yield(leaf_value(doc, leaf));
|
|
1097
1065
|
}
|
|
1098
1066
|
}
|
|
1099
1067
|
|
|
1100
1068
|
// doc functions
|
|
1101
1069
|
|
|
1102
|
-
/*
|
|
1070
|
+
/* @overload open(json) { |doc| ... } => Object
|
|
1103
1071
|
*
|
|
1104
1072
|
* Parses a JSON document String and then yields to the provided block if one
|
|
1105
1073
|
* is given with an instance of the Oj::Doc as the single yield parameter. If
|
|
1106
1074
|
* a block is not given then an Oj::Doc instance is returned and must be
|
|
1107
1075
|
* closed with a call to the #close() method when no longer needed.
|
|
1108
1076
|
*
|
|
1109
|
-
*
|
|
1077
|
+
* @param [String] json JSON document string
|
|
1110
1078
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
|
1111
|
-
* @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
|
|
1112
1081
|
* @example
|
|
1113
1082
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
|
1114
1083
|
* # or as an alternative
|
|
@@ -1116,40 +1085,46 @@ each_value(Doc doc, Leaf leaf) {
|
|
|
1116
1085
|
* doc.size() #=> 4
|
|
1117
1086
|
* doc.close()
|
|
1118
1087
|
*/
|
|
1119
|
-
static VALUE
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
int
|
|
1125
|
-
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;
|
|
1126
1094
|
|
|
1127
1095
|
Check_Type(str, T_STRING);
|
|
1128
|
-
len
|
|
1129
|
-
allocate = (
|
|
1096
|
+
len = (int)RSTRING_LEN(str) + 1;
|
|
1097
|
+
allocate = (SMALL_JSON < len || !given);
|
|
1130
1098
|
if (allocate) {
|
|
1131
|
-
|
|
1099
|
+
json = ALLOC_N(char, len);
|
|
1132
1100
|
} else {
|
|
1133
|
-
|
|
1101
|
+
json = ALLOCA_N(char, len);
|
|
1134
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();
|
|
1135
1108
|
memcpy(json, StringValuePtr(str), len);
|
|
1136
1109
|
obj = parse_json(clas, json, given, allocate);
|
|
1110
|
+
rb_gc_enable();
|
|
1137
1111
|
if (given && allocate) {
|
|
1138
|
-
|
|
1112
|
+
xfree(json);
|
|
1139
1113
|
}
|
|
1140
1114
|
return obj;
|
|
1141
1115
|
}
|
|
1142
1116
|
|
|
1143
|
-
/*
|
|
1117
|
+
/* @overload open_file(filename) { |doc| ... } => Object
|
|
1144
1118
|
*
|
|
1145
1119
|
* Parses a JSON document from a file and then yields to the provided block if
|
|
1146
1120
|
* one is given with an instance of the Oj::Doc as the single yield
|
|
1147
1121
|
* parameter. If a block is not given then an Oj::Doc instance is returned and
|
|
1148
1122
|
* must be closed with a call to the #close() method when no longer needed.
|
|
1149
1123
|
*
|
|
1150
|
-
*
|
|
1124
|
+
* @param [String] filename name of file that contains a JSON document
|
|
1151
1125
|
* @yieldparam [Oj::Doc] doc parsed JSON document
|
|
1152
|
-
* @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
|
|
1153
1128
|
* @example
|
|
1154
1129
|
* File.open('array.json', 'w') { |f| f.write('[1,2,3]') }
|
|
1155
1130
|
* Oj::Doc.open_file(filename) { |doc| doc.size() } #=> 4
|
|
@@ -1158,63 +1133,64 @@ doc_open(VALUE clas, VALUE str) {
|
|
|
1158
1133
|
* doc.size() #=> 4
|
|
1159
1134
|
* doc.close()
|
|
1160
1135
|
*/
|
|
1161
|
-
static VALUE
|
|
1162
|
-
|
|
1163
|
-
char
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
int
|
|
1169
|
-
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;
|
|
1170
1144
|
|
|
1171
1145
|
Check_Type(filename, T_STRING);
|
|
1172
1146
|
path = StringValuePtr(filename);
|
|
1173
1147
|
if (0 == (f = fopen(path, "r"))) {
|
|
1174
|
-
|
|
1148
|
+
rb_raise(rb_eIOError, "%s", strerror(errno));
|
|
1175
1149
|
}
|
|
1176
1150
|
fseek(f, 0, SEEK_END);
|
|
1177
|
-
len
|
|
1178
|
-
allocate = (
|
|
1151
|
+
len = ftell(f);
|
|
1152
|
+
allocate = (SMALL_JSON < len || !given);
|
|
1179
1153
|
if (allocate) {
|
|
1180
|
-
|
|
1154
|
+
json = ALLOC_N(char, len + 1);
|
|
1181
1155
|
} else {
|
|
1182
|
-
|
|
1156
|
+
json = ALLOCA_N(char, len + 1);
|
|
1183
1157
|
}
|
|
1184
1158
|
fseek(f, 0, SEEK_SET);
|
|
1185
1159
|
if (len != fread(json, 1, len, f)) {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
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);
|
|
1189
1165
|
}
|
|
1190
1166
|
fclose(f);
|
|
1191
1167
|
json[len] = '\0';
|
|
1168
|
+
rb_gc_disable();
|
|
1192
1169
|
obj = parse_json(clas, json, given, allocate);
|
|
1170
|
+
rb_gc_enable();
|
|
1193
1171
|
if (given && allocate) {
|
|
1194
|
-
|
|
1172
|
+
xfree(json);
|
|
1195
1173
|
}
|
|
1196
1174
|
return obj;
|
|
1197
1175
|
}
|
|
1198
1176
|
|
|
1199
|
-
static int
|
|
1200
|
-
|
|
1201
|
-
int cnt = 0;
|
|
1177
|
+
static int esc_strlen(const char *s) {
|
|
1178
|
+
int cnt = 0;
|
|
1202
1179
|
|
|
1203
1180
|
for (; '\0' != *s; s++, cnt++) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1181
|
+
if ('/' == *s) {
|
|
1182
|
+
cnt++;
|
|
1183
|
+
}
|
|
1207
1184
|
}
|
|
1208
1185
|
return cnt;
|
|
1209
1186
|
}
|
|
1210
1187
|
|
|
1211
|
-
static char*
|
|
1212
|
-
append_key(char *p, const char *key) {
|
|
1188
|
+
static char *append_key(char *p, const char *key) {
|
|
1213
1189
|
for (; '\0' != *key; p++, key++) {
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1190
|
+
if ('/' == *key) {
|
|
1191
|
+
*p++ = '\\';
|
|
1192
|
+
}
|
|
1193
|
+
*p = *key;
|
|
1218
1194
|
}
|
|
1219
1195
|
return p;
|
|
1220
1196
|
}
|
|
@@ -1223,169 +1199,209 @@ append_key(char *p, const char *key) {
|
|
|
1223
1199
|
* @see Oj::Doc.open
|
|
1224
1200
|
*/
|
|
1225
1201
|
|
|
1226
|
-
/*
|
|
1202
|
+
/* @overload where() => String
|
|
1227
1203
|
*
|
|
1228
1204
|
* Returns a String that describes the absolute path to the current location
|
|
1229
1205
|
* in the JSON document.
|
|
1230
1206
|
*/
|
|
1231
|
-
static VALUE
|
|
1232
|
-
|
|
1233
|
-
Doc doc = self_doc(self);
|
|
1207
|
+
static VALUE doc_where(VALUE self) {
|
|
1208
|
+
Doc doc = self_doc(self);
|
|
1234
1209
|
|
|
1235
1210
|
if (0 == *doc->where_path || doc->where == doc->where_path) {
|
|
1236
|
-
|
|
1211
|
+
return oj_slash_string;
|
|
1237
1212
|
} else {
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
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);
|
|
1265
1241
|
}
|
|
1266
1242
|
}
|
|
1267
1243
|
|
|
1268
|
-
/*
|
|
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
|
+
|
|
1262
|
+
/* @overload local_key() => String, Fixnum, nil
|
|
1269
1263
|
*
|
|
1270
1264
|
* Returns the final key to the current location.
|
|
1271
1265
|
* @example
|
|
1272
|
-
* Oj::Doc.open('[1,2,3]') { |doc| doc.move('/2'); doc.local_key() }
|
|
1273
|
-
* Oj::Doc.open('{"one":3}') { |doc| doc.move('/one'); doc.local_key() } #=>
|
|
1274
|
-
*
|
|
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
|
|
1275
1270
|
*/
|
|
1276
|
-
static VALUE
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
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;
|
|
1281
1275
|
|
|
1282
1276
|
if (T_HASH == leaf->parent_type) {
|
|
1283
|
-
|
|
1284
|
-
|
|
1277
|
+
key = rb_str_new2(leaf->key);
|
|
1278
|
+
key = oj_encode(key);
|
|
1285
1279
|
} else if (T_ARRAY == leaf->parent_type) {
|
|
1286
|
-
|
|
1280
|
+
key = LONG2NUM(leaf->index);
|
|
1287
1281
|
}
|
|
1288
1282
|
return key;
|
|
1289
1283
|
}
|
|
1290
1284
|
|
|
1291
|
-
/*
|
|
1285
|
+
/* @overload home() => nil
|
|
1292
1286
|
*
|
|
1293
1287
|
* Moves the document marker or location to the hoot or home position. The
|
|
1294
1288
|
* same operation can be performed with a Oj::Doc.move('/').
|
|
1295
1289
|
* @example
|
|
1296
|
-
* 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
|
+
* #=> '/'
|
|
1297
1292
|
*/
|
|
1298
|
-
static VALUE
|
|
1299
|
-
|
|
1300
|
-
Doc doc = self_doc(self);
|
|
1293
|
+
static VALUE doc_home(VALUE self) {
|
|
1294
|
+
Doc doc = self_doc(self);
|
|
1301
1295
|
|
|
1302
1296
|
*doc->where_path = doc->data;
|
|
1303
|
-
doc->where
|
|
1297
|
+
doc->where = doc->where_path;
|
|
1304
1298
|
|
|
1305
1299
|
return oj_slash_string;
|
|
1306
1300
|
}
|
|
1307
1301
|
|
|
1308
|
-
/*
|
|
1302
|
+
/* @overload type(path=nil) => Class
|
|
1309
1303
|
*
|
|
1310
1304
|
* Returns the Class of the data value at the location identified by the path
|
|
1311
1305
|
* or the current location if the path is nil or not provided. This method
|
|
1312
1306
|
* does not create the Ruby Object at the location specified so the overhead
|
|
1313
1307
|
* is low.
|
|
1314
|
-
*
|
|
1308
|
+
* @param [String] path path to the location to get the type of if provided
|
|
1315
1309
|
* @example
|
|
1316
1310
|
* Oj::Doc.open('[1,2]') { |doc| doc.type() } #=> Array
|
|
1317
1311
|
* Oj::Doc.open('[1,2]') { |doc| doc.type('/1') } #=> Fixnum
|
|
1318
1312
|
*/
|
|
1319
|
-
static VALUE
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
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;
|
|
1325
1318
|
|
|
1326
1319
|
if (1 <= argc) {
|
|
1327
|
-
|
|
1328
|
-
|
|
1320
|
+
Check_Type(*argv, T_STRING);
|
|
1321
|
+
path = StringValuePtr(*argv);
|
|
1329
1322
|
}
|
|
1330
1323
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
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;
|
|
1336
1329
|
#ifdef RUBY_INTEGER_UNIFICATION
|
|
1337
|
-
|
|
1330
|
+
case T_FIXNUM: type = rb_cInteger; break;
|
|
1338
1331
|
#else
|
|
1339
|
-
|
|
1332
|
+
case T_FIXNUM: type = rb_cFixnum; break;
|
|
1340
1333
|
#endif
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
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
|
+
}
|
|
1346
1339
|
}
|
|
1347
1340
|
return type;
|
|
1348
1341
|
}
|
|
1349
1342
|
|
|
1350
|
-
/*
|
|
1343
|
+
/* @overload fetch(path=nil,default=nil) => nil, true, false, Fixnum, Float, String, Array,
|
|
1344
|
+
* Hash
|
|
1351
1345
|
*
|
|
1352
1346
|
* Returns the value at the location identified by the path or the current
|
|
1353
1347
|
* location if the path is nil or not provided. This method will create and
|
|
1354
1348
|
* return an Array or Hash if that is the type of Object at the location
|
|
1355
1349
|
* specified. This is more expensive than navigating to the leaves of the JSON
|
|
1356
|
-
* document.
|
|
1357
|
-
*
|
|
1350
|
+
* document. If a default is provided that is used if no value if found.
|
|
1351
|
+
* @param [String] path path to the location to get the type of if provided
|
|
1358
1352
|
* @example
|
|
1359
1353
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch() } #=> [1, 2]
|
|
1360
1354
|
* Oj::Doc.open('[1,2]') { |doc| doc.fetch('/1') } #=> 1
|
|
1361
1355
|
*/
|
|
1362
|
-
static VALUE
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
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;
|
|
1368
1361
|
|
|
1369
1362
|
doc = self_doc(self);
|
|
1370
1363
|
if (1 <= argc) {
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1364
|
+
Check_Type(*argv, T_STRING);
|
|
1365
|
+
path = StringValuePtr(*argv);
|
|
1366
|
+
if (2 == argc) {
|
|
1367
|
+
val = argv[1];
|
|
1368
|
+
}
|
|
1376
1369
|
}
|
|
1377
1370
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1378
|
-
|
|
1371
|
+
val = leaf_value(doc, leaf);
|
|
1379
1372
|
}
|
|
1380
1373
|
return val;
|
|
1381
1374
|
}
|
|
1382
1375
|
|
|
1383
|
-
/*
|
|
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
|
+
|
|
1398
|
+
/* @overload each_leaf(path=nil) => nil
|
|
1384
1399
|
*
|
|
1385
1400
|
* Yields to the provided block for each leaf node with the identified
|
|
1386
1401
|
* location of the JSON document as the root. The parameter passed to the
|
|
1387
1402
|
* block on yield is the Doc instance after moving to the child location.
|
|
1388
|
-
*
|
|
1403
|
+
* @param [String] path if provided it identified the top of the branch to
|
|
1404
|
+
* process the leaves of
|
|
1389
1405
|
* @yieldparam [Doc] Doc at the child location
|
|
1390
1406
|
* @example
|
|
1391
1407
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
@@ -1395,73 +1411,73 @@ doc_fetch(int argc, VALUE *argv, VALUE self) {
|
|
|
1395
1411
|
* }
|
|
1396
1412
|
* #=> ["/1" => 3, "/2/1" => 2, "/2/2" => 1]
|
|
1397
1413
|
*/
|
|
1398
|
-
static VALUE
|
|
1399
|
-
doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1414
|
+
static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
|
|
1400
1415
|
if (rb_block_given_p()) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
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
|
+
}
|
|
1428
1443
|
}
|
|
1429
1444
|
return Qnil;
|
|
1430
1445
|
}
|
|
1431
1446
|
|
|
1432
|
-
/*
|
|
1447
|
+
/* @overload move(path) => nil
|
|
1433
1448
|
*
|
|
1434
1449
|
* Moves the document marker to the path specified. The path can an absolute
|
|
1435
1450
|
* path or a relative path.
|
|
1436
|
-
*
|
|
1451
|
+
* @param [String] path path to the location to move to
|
|
1437
1452
|
* @example
|
|
1438
|
-
* 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"
|
|
1439
1455
|
*/
|
|
1440
|
-
static VALUE
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
int loc;
|
|
1456
|
+
static VALUE doc_move(VALUE self, VALUE str) {
|
|
1457
|
+
Doc doc = self_doc(self);
|
|
1458
|
+
const char *path;
|
|
1459
|
+
int loc;
|
|
1445
1460
|
|
|
1446
1461
|
Check_Type(str, T_STRING);
|
|
1447
1462
|
path = StringValuePtr(str);
|
|
1448
1463
|
if ('/' == *path) {
|
|
1449
|
-
|
|
1450
|
-
|
|
1464
|
+
doc->where = doc->where_path;
|
|
1465
|
+
path++;
|
|
1451
1466
|
}
|
|
1452
1467
|
if (0 != (loc = move_step(doc, path, 1))) {
|
|
1453
|
-
|
|
1468
|
+
rb_raise(rb_eArgError, "Failed to locate element %d of the path %s.", loc, path);
|
|
1454
1469
|
}
|
|
1455
1470
|
return Qnil;
|
|
1456
1471
|
}
|
|
1457
1472
|
|
|
1458
|
-
/*
|
|
1473
|
+
/* @overload each_child(path=nil) { |doc| ... } => nil
|
|
1459
1474
|
*
|
|
1460
1475
|
* Yields to the provided block for each immediate child node with the
|
|
1461
1476
|
* identified location of the JSON document as the root. The parameter passed
|
|
1462
1477
|
* to the block on yield is the Doc instance after moving to the child
|
|
1463
1478
|
* location.
|
|
1464
|
-
*
|
|
1479
|
+
* @param [String] path if provided it identified the top of the branch to
|
|
1480
|
+
* process the children of
|
|
1465
1481
|
* @yieldparam [Doc] Doc at the child location
|
|
1466
1482
|
* @example
|
|
1467
1483
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
@@ -1471,57 +1487,63 @@ doc_move(VALUE self, VALUE str) {
|
|
|
1471
1487
|
* }
|
|
1472
1488
|
* #=> ["/2/1", "/2/2"]
|
|
1473
1489
|
*/
|
|
1474
|
-
static VALUE
|
|
1475
|
-
doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1490
|
+
static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
1476
1491
|
if (rb_block_given_p()) {
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
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;
|
|
1514
1535
|
}
|
|
1515
1536
|
return Qnil;
|
|
1516
1537
|
}
|
|
1517
1538
|
|
|
1518
|
-
/*
|
|
1539
|
+
/* @overload each_value(path=nil) { |val| ... } => nil
|
|
1519
1540
|
*
|
|
1520
1541
|
* Yields to the provided block for each leaf value in the identified location
|
|
1521
1542
|
* of the JSON document. The parameter passed to the block on yield is the
|
|
1522
1543
|
* value of the leaf. Only those leaves below the element specified by the
|
|
1523
1544
|
* path parameter are processed.
|
|
1524
|
-
*
|
|
1545
|
+
* @param [String] path if provided it identified the top of the branch to
|
|
1546
|
+
* process the leaf values of
|
|
1525
1547
|
* @yieldparam [Object] val each leaf value
|
|
1526
1548
|
* @example
|
|
1527
1549
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
@@ -1530,7 +1552,7 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1530
1552
|
* result
|
|
1531
1553
|
* }
|
|
1532
1554
|
* #=> [3, 2, 1]
|
|
1533
|
-
*
|
|
1555
|
+
*
|
|
1534
1556
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
1535
1557
|
* result = []
|
|
1536
1558
|
* doc.each_value('/2') { |v| result << v }
|
|
@@ -1538,79 +1560,76 @@ doc_each_child(int argc, VALUE *argv, VALUE self) {
|
|
|
1538
1560
|
* }
|
|
1539
1561
|
* #=> [2, 1]
|
|
1540
1562
|
*/
|
|
1541
|
-
static VALUE
|
|
1542
|
-
doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1563
|
+
static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
|
|
1543
1564
|
if (rb_block_given_p()) {
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
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
|
+
}
|
|
1555
1576
|
}
|
|
1556
1577
|
return Qnil;
|
|
1557
1578
|
}
|
|
1558
1579
|
|
|
1559
|
-
/*
|
|
1580
|
+
/* @overload dump(path, filename)
|
|
1560
1581
|
*
|
|
1561
1582
|
* Dumps the document or nodes to a new JSON document. It uses the default
|
|
1562
1583
|
* options for generating the JSON.
|
|
1563
|
-
*
|
|
1564
|
-
*
|
|
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
|
|
1565
1588
|
* @example
|
|
1566
1589
|
* Oj::Doc.open('[3,[2,1]]') { |doc|
|
|
1567
1590
|
* doc.dump('/2')
|
|
1568
1591
|
* }
|
|
1569
1592
|
* #=> "[2,1]"
|
|
1570
1593
|
*/
|
|
1571
|
-
static VALUE
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
const char
|
|
1576
|
-
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;
|
|
1577
1599
|
|
|
1578
1600
|
if (1 <= argc) {
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
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
|
+
}
|
|
1587
1609
|
}
|
|
1588
1610
|
if (0 != (leaf = get_doc_leaf(doc, path))) {
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
rjson = Qnil;
|
|
1607
|
-
}
|
|
1608
|
-
return rjson;
|
|
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;
|
|
1609
1628
|
}
|
|
1610
1629
|
return Qnil;
|
|
1611
1630
|
}
|
|
1612
1631
|
|
|
1613
|
-
/*
|
|
1632
|
+
/* @overload size() => Fixnum
|
|
1614
1633
|
*
|
|
1615
1634
|
* Returns the number of nodes in the JSON document where a node is any one of
|
|
1616
1635
|
* the basic JSON components.
|
|
@@ -1618,12 +1637,11 @@ doc_dump(int argc, VALUE *argv, VALUE self) {
|
|
|
1618
1637
|
* @example
|
|
1619
1638
|
* Oj::Doc.open('[1,2,3]') { |doc| doc.size() } #=> 4
|
|
1620
1639
|
*/
|
|
1621
|
-
static VALUE
|
|
1622
|
-
doc_size(VALUE self) {
|
|
1640
|
+
static VALUE doc_size(VALUE self) {
|
|
1623
1641
|
return ULONG2NUM(((Doc)DATA_PTR(self))->size);
|
|
1624
1642
|
}
|
|
1625
1643
|
|
|
1626
|
-
/*
|
|
1644
|
+
/* @overload close() => nil
|
|
1627
1645
|
*
|
|
1628
1646
|
* Closes an open document. No further calls to the document will be valid
|
|
1629
1647
|
* after closing.
|
|
@@ -1632,16 +1650,15 @@ doc_size(VALUE self) {
|
|
|
1632
1650
|
* doc.size() #=> 4
|
|
1633
1651
|
* doc.close()
|
|
1634
1652
|
*/
|
|
1635
|
-
static VALUE
|
|
1636
|
-
|
|
1637
|
-
Doc doc = self_doc(self);
|
|
1653
|
+
static VALUE doc_close(VALUE self) {
|
|
1654
|
+
Doc doc = self_doc(self);
|
|
1638
1655
|
|
|
1639
1656
|
rb_gc_unregister_address(&doc->self);
|
|
1640
1657
|
DATA_PTR(doc->self) = 0;
|
|
1641
1658
|
if (0 != doc) {
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1659
|
+
xfree(doc->json);
|
|
1660
|
+
doc_free(doc);
|
|
1661
|
+
xfree(doc);
|
|
1645
1662
|
}
|
|
1646
1663
|
return Qnil;
|
|
1647
1664
|
}
|
|
@@ -1650,8 +1667,7 @@ doc_close(VALUE self) {
|
|
|
1650
1667
|
Oj = rb_define_module("Oj");
|
|
1651
1668
|
#endif
|
|
1652
1669
|
|
|
1653
|
-
static VALUE
|
|
1654
|
-
doc_not_implemented(VALUE self) {
|
|
1670
|
+
static VALUE doc_not_implemented(VALUE self) {
|
|
1655
1671
|
rb_raise(rb_eNotImpError, "Not implemented.");
|
|
1656
1672
|
return Qnil;
|
|
1657
1673
|
}
|
|
@@ -1663,21 +1679,21 @@ doc_not_implemented(VALUE self) {
|
|
|
1663
1679
|
* extracted. Once the document is closed the document can not longer be
|
|
1664
1680
|
* accessed. This allows the parsing and data extraction to be extremely fast
|
|
1665
1681
|
* compared to other JSON parses.
|
|
1666
|
-
*
|
|
1682
|
+
*
|
|
1667
1683
|
* An Oj::Doc class is not created directly but the _open()_ class method is
|
|
1668
1684
|
* used to open a document and the yield parameter to the block of the #open()
|
|
1669
1685
|
* call is the Doc instance. The Doc instance can be moved across, up, and
|
|
1670
1686
|
* down the JSON document. At each element the data associated with the
|
|
1671
1687
|
* element can be extracted. It is also possible to just provide a path to the
|
|
1672
1688
|
* data to be extracted and retrieve the data in that manner.
|
|
1673
|
-
*
|
|
1689
|
+
*
|
|
1674
1690
|
* For many of the methods a path is used to describe the location of an
|
|
1675
1691
|
* element. Paths follow a subset of the XPath syntax. The slash ('/')
|
|
1676
1692
|
* character is the separator. Each step in the path identifies the next
|
|
1677
1693
|
* branch to take through the document. A JSON object will expect a key string
|
|
1678
1694
|
* while an array will expect a positive index. A .. step indicates a move up
|
|
1679
1695
|
* the JSON document.
|
|
1680
|
-
*
|
|
1696
|
+
*
|
|
1681
1697
|
* @example
|
|
1682
1698
|
* json = %{[
|
|
1683
1699
|
* {
|
|
@@ -1691,28 +1707,29 @@ doc_not_implemented(VALUE self) {
|
|
|
1691
1707
|
* ]}
|
|
1692
1708
|
* # move and get value
|
|
1693
1709
|
* Oj::Doc.open(json) do |doc|
|
|
1694
|
-
* doc.move('/1/two')
|
|
1695
|
-
* # doc location is now at the 'two' element of the hash that is the first
|
|
1696
|
-
*
|
|
1697
|
-
* end
|
|
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
|
|
1698
1713
|
* #=> 2
|
|
1699
|
-
*
|
|
1700
|
-
* # Now try again using a path to Oj::Doc.fetch() directly and not using a
|
|
1701
|
-
*
|
|
1702
|
-
* doc.fetch('/2/three') #=> 3
|
|
1703
|
-
* doc.close()
|
|
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()
|
|
1704
1717
|
*/
|
|
1705
|
-
void
|
|
1706
|
-
oj_init_doc() {
|
|
1718
|
+
void oj_init_doc(void) {
|
|
1707
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);
|
|
1708
1722
|
rb_define_singleton_method(oj_doc_class, "open", doc_open, 1);
|
|
1709
1723
|
rb_define_singleton_method(oj_doc_class, "open_file", doc_open_file, 1);
|
|
1710
1724
|
rb_define_singleton_method(oj_doc_class, "parse", doc_open, 1);
|
|
1711
|
-
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);
|
|
1712
1728
|
rb_define_method(oj_doc_class, "local_key", doc_local_key, 0);
|
|
1713
1729
|
rb_define_method(oj_doc_class, "home", doc_home, 0);
|
|
1714
1730
|
rb_define_method(oj_doc_class, "type", doc_type, -1);
|
|
1715
1731
|
rb_define_method(oj_doc_class, "fetch", doc_fetch, -1);
|
|
1732
|
+
rb_define_method(oj_doc_class, "exists?", doc_exists, 1);
|
|
1716
1733
|
rb_define_method(oj_doc_class, "each_leaf", doc_each_leaf, -1);
|
|
1717
1734
|
rb_define_method(oj_doc_class, "move", doc_move, 1);
|
|
1718
1735
|
rb_define_method(oj_doc_class, "each_child", doc_each_child, -1);
|