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