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