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