oj 3.10.6 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/ext/oj/buf.h +36 -68
- data/ext/oj/cache8.c +59 -62
- data/ext/oj/cache8.h +9 -36
- data/ext/oj/circarray.c +36 -42
- data/ext/oj/circarray.h +12 -13
- data/ext/oj/code.c +172 -179
- data/ext/oj/code.h +22 -24
- data/ext/oj/compat.c +168 -181
- data/ext/oj/custom.c +800 -864
- data/ext/oj/dump.c +774 -776
- data/ext/oj/dump.h +50 -55
- data/ext/oj/dump_compat.c +2 -4
- data/ext/oj/dump_leaf.c +118 -162
- data/ext/oj/dump_object.c +610 -632
- data/ext/oj/dump_strict.c +319 -331
- data/ext/oj/encode.h +4 -33
- data/ext/oj/err.c +40 -29
- data/ext/oj/err.h +25 -44
- data/ext/oj/extconf.rb +2 -1
- data/ext/oj/fast.c +1054 -1081
- data/ext/oj/hash.c +78 -95
- data/ext/oj/hash.h +10 -35
- data/ext/oj/hash_test.c +451 -472
- data/ext/oj/mimic_json.c +415 -402
- data/ext/oj/object.c +588 -532
- data/ext/oj/odd.c +124 -132
- data/ext/oj/odd.h +28 -29
- data/ext/oj/oj.c +1178 -905
- data/ext/oj/oj.h +289 -298
- data/ext/oj/parse.c +946 -870
- data/ext/oj/parse.h +81 -79
- data/ext/oj/rails.c +837 -842
- data/ext/oj/rails.h +8 -11
- data/ext/oj/reader.c +139 -147
- data/ext/oj/reader.h +68 -84
- data/ext/oj/resolve.c +44 -47
- data/ext/oj/resolve.h +4 -6
- data/ext/oj/rxclass.c +69 -73
- data/ext/oj/rxclass.h +13 -14
- data/ext/oj/saj.c +453 -484
- data/ext/oj/scp.c +88 -113
- data/ext/oj/sparse.c +783 -714
- data/ext/oj/stream_writer.c +123 -157
- data/ext/oj/strict.c +133 -106
- data/ext/oj/string_writer.c +199 -247
- data/ext/oj/trace.c +34 -41
- data/ext/oj/trace.h +15 -15
- data/ext/oj/util.c +104 -104
- data/ext/oj/util.h +4 -3
- data/ext/oj/val_stack.c +48 -76
- data/ext/oj/val_stack.h +80 -115
- data/ext/oj/wab.c +317 -328
- data/lib/oj.rb +0 -8
- data/lib/oj/bag.rb +1 -0
- data/lib/oj/easy_hash.rb +5 -4
- data/lib/oj/mimic.rb +45 -13
- data/lib/oj/version.rb +1 -1
- data/pages/Modes.md +1 -0
- data/pages/Options.md +23 -11
- data/test/activerecord/result_test.rb +7 -2
- data/test/foo.rb +8 -40
- data/test/helper.rb +10 -0
- data/test/json_gem/json_common_interface_test.rb +8 -3
- data/test/json_gem/json_generator_test.rb +15 -3
- data/test/json_gem/test_helper.rb +8 -0
- data/test/perf.rb +1 -1
- data/test/perf_scp.rb +11 -10
- data/test/perf_strict.rb +17 -23
- data/test/prec.rb +23 -0
- data/test/sample_json.rb +1 -1
- data/test/test_compat.rb +16 -3
- data/test/test_custom.rb +11 -0
- data/test/test_fast.rb +32 -2
- data/test/test_generate.rb +21 -0
- data/test/test_hash.rb +10 -0
- data/test/test_rails.rb +9 -0
- data/test/test_scp.rb +1 -1
- data/test/test_various.rb +4 -2
- metadata +89 -85
data/ext/oj/resolve.c
CHANGED
@@ -1,79 +1,76 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2012 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
5
3
|
|
6
|
-
#include <stdlib.h>
|
7
4
|
#include <stdio.h>
|
5
|
+
#include <stdlib.h>
|
8
6
|
#include <string.h>
|
9
7
|
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
10
8
|
#include <pthread.h>
|
11
9
|
#endif
|
12
10
|
|
13
|
-
#include "oj.h"
|
14
11
|
#include "err.h"
|
15
|
-
#include "parse.h"
|
16
12
|
#include "hash.h"
|
13
|
+
#include "oj.h"
|
14
|
+
#include "parse.h"
|
17
15
|
|
18
|
-
inline static VALUE
|
19
|
-
|
20
|
-
|
21
|
-
ID ci = rb_intern(classname);
|
16
|
+
inline static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define) {
|
17
|
+
VALUE clas;
|
18
|
+
ID ci = rb_intern(classname);
|
22
19
|
|
23
20
|
if (rb_const_defined_at(mod, ci)) {
|
24
|
-
|
21
|
+
clas = rb_const_get_at(mod, ci);
|
25
22
|
} else if (auto_define) {
|
26
|
-
|
23
|
+
clas = rb_define_class_under(mod, classname, oj_bag_class);
|
27
24
|
} else {
|
28
|
-
|
25
|
+
clas = Qundef;
|
29
26
|
}
|
30
27
|
return clas;
|
31
28
|
}
|
32
29
|
|
33
30
|
static VALUE
|
34
31
|
resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
|
35
|
-
char
|
36
|
-
VALUE
|
37
|
-
char
|
38
|
-
char
|
39
|
-
const char
|
32
|
+
char class_name[1024];
|
33
|
+
VALUE clas;
|
34
|
+
char * end = class_name + sizeof(class_name) - 1;
|
35
|
+
char * s;
|
36
|
+
const char *n = name;
|
40
37
|
|
41
38
|
clas = rb_cObject;
|
42
39
|
for (s = class_name; 0 < len; n++, len--) {
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
40
|
+
if (':' == *n) {
|
41
|
+
*s = '\0';
|
42
|
+
n++;
|
43
|
+
len--;
|
44
|
+
if (':' != *n) {
|
45
|
+
return Qundef;
|
46
|
+
}
|
47
|
+
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
48
|
+
return Qundef;
|
49
|
+
}
|
50
|
+
s = class_name;
|
51
|
+
} else if (end <= s) {
|
52
|
+
return Qundef;
|
53
|
+
} else {
|
54
|
+
*s++ = *n;
|
55
|
+
}
|
59
56
|
}
|
60
57
|
*s = '\0';
|
61
58
|
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class %s is not defined", name);
|
60
|
+
if (Qnil != error_class) {
|
61
|
+
pi->err_class = error_class;
|
62
|
+
}
|
66
63
|
}
|
67
64
|
return clas;
|
68
65
|
}
|
69
66
|
|
70
67
|
VALUE
|
71
68
|
oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
|
72
|
-
VALUE
|
73
|
-
VALUE
|
69
|
+
VALUE clas;
|
70
|
+
VALUE *slot;
|
74
71
|
|
75
72
|
if (No == pi->options.class_cache) {
|
76
|
-
|
73
|
+
return resolve_classpath(pi, name, len, auto_define, error_class);
|
77
74
|
}
|
78
75
|
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
79
76
|
pthread_mutex_lock(&oj_cache_mutex);
|
@@ -81,9 +78,9 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
|
|
81
78
|
rb_mutex_lock(oj_cache_mutex);
|
82
79
|
#endif
|
83
80
|
if (Qnil == (clas = oj_class_hash_get(name, len, &slot))) {
|
84
|
-
|
85
|
-
|
86
|
-
|
81
|
+
if (Qundef != (clas = resolve_classpath(pi, name, len, auto_define, error_class))) {
|
82
|
+
*slot = clas;
|
83
|
+
}
|
87
84
|
}
|
88
85
|
#ifdef HAVE_PTHREAD_MUTEX_INIT
|
89
86
|
pthread_mutex_unlock(&oj_cache_mutex);
|
@@ -95,8 +92,8 @@ oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE
|
|
95
92
|
|
96
93
|
VALUE
|
97
94
|
oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class) {
|
98
|
-
size_t
|
99
|
-
const char
|
95
|
+
size_t len = RSTRING_LEN(nameVal);
|
96
|
+
const char *str = StringValuePtr(nameVal);
|
100
97
|
|
101
98
|
return resolve_classpath(pi, str, len, 0, error_class);
|
102
99
|
}
|
data/ext/oj/resolve.h
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
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.
|
5
3
|
|
6
4
|
#ifndef OJ_RESOLVE_H
|
7
5
|
#define OJ_RESOLVE_H
|
8
6
|
|
9
7
|
#include "ruby.h"
|
10
8
|
|
11
|
-
extern VALUE
|
12
|
-
extern VALUE
|
9
|
+
extern VALUE oj_name2class(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class);
|
10
|
+
extern VALUE oj_name2struct(ParseInfo pi, VALUE nameVal, VALUE error_class);
|
13
11
|
|
14
12
|
#endif /* OJ_RESOLVE_H */
|
data/ext/oj/rxclass.c
CHANGED
@@ -1,13 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2017 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
5
3
|
|
6
|
-
#include <sys/types.h>
|
7
|
-
#include <stdlib.h>
|
8
4
|
#include <errno.h>
|
9
|
-
#include <string.h>
|
10
5
|
#include <stdio.h>
|
6
|
+
#include <stdlib.h>
|
7
|
+
#include <string.h>
|
8
|
+
#include <sys/types.h>
|
11
9
|
#if !IS_WINDOWS
|
12
10
|
#include <regex.h>
|
13
11
|
#endif
|
@@ -15,65 +13,64 @@
|
|
15
13
|
#include "rxclass.h"
|
16
14
|
|
17
15
|
typedef struct _rxC {
|
18
|
-
struct _rxC
|
19
|
-
VALUE
|
16
|
+
struct _rxC *next;
|
17
|
+
VALUE rrx;
|
20
18
|
#if !IS_WINDOWS
|
21
|
-
regex_t
|
19
|
+
regex_t rx;
|
22
20
|
#endif
|
23
|
-
VALUE
|
24
|
-
char
|
25
|
-
} *RxC;
|
21
|
+
VALUE clas;
|
22
|
+
char src[256];
|
23
|
+
} * RxC;
|
26
24
|
|
27
|
-
void
|
28
|
-
oj_rxclass_init(RxClass rc) {
|
25
|
+
void oj_rxclass_init(RxClass rc) {
|
29
26
|
*rc->err = '\0';
|
30
27
|
rc->head = NULL;
|
31
28
|
rc->tail = NULL;
|
32
29
|
}
|
33
30
|
|
34
|
-
void
|
35
|
-
|
36
|
-
RxC rxc;
|
31
|
+
void oj_rxclass_cleanup(RxClass rc) {
|
32
|
+
RxC rxc;
|
37
33
|
|
38
34
|
while (NULL != (rxc = rc->head)) {
|
39
|
-
|
35
|
+
rc->head = rc->head->next;
|
40
36
|
#if !IS_WINDOWS
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
if (Qnil == rxc->rrx) {
|
38
|
+
regfree(&rxc->rx);
|
39
|
+
}
|
40
|
+
xfree(rxc);
|
45
41
|
#endif
|
46
42
|
}
|
47
43
|
}
|
48
44
|
|
49
|
-
void
|
50
|
-
|
51
|
-
RxC rxc = ALLOC_N(struct _rxC, 1);
|
45
|
+
void oj_rxclass_rappend(RxClass rc, VALUE rx, VALUE clas) {
|
46
|
+
RxC rxc = ALLOC_N(struct _rxC, 1);
|
52
47
|
|
53
48
|
memset(rxc, 0, sizeof(struct _rxC));
|
54
|
-
rxc->rrx
|
49
|
+
rxc->rrx = rx;
|
55
50
|
rxc->clas = clas;
|
56
51
|
if (NULL == rc->tail) {
|
57
|
-
|
52
|
+
rc->head = rxc;
|
58
53
|
} else {
|
59
|
-
|
54
|
+
rc->tail->next = rxc;
|
60
55
|
}
|
61
56
|
rc->tail = rxc;
|
62
57
|
}
|
63
58
|
|
64
59
|
// Attempt to compile the expression. If it fails populate the error code..
|
65
|
-
int
|
66
|
-
|
67
|
-
RxC rxc;
|
60
|
+
int oj_rxclass_append(RxClass rc, const char *expr, VALUE clas) {
|
61
|
+
RxC rxc;
|
68
62
|
#if !IS_WINDOWS
|
69
|
-
int
|
70
|
-
int
|
63
|
+
int err;
|
64
|
+
int flags = 0;
|
71
65
|
#endif
|
72
66
|
if (sizeof(rxc->src) <= strlen(expr)) {
|
73
|
-
|
74
|
-
|
67
|
+
snprintf(rc->err,
|
68
|
+
sizeof(rc->err),
|
69
|
+
"expressions must be less than %lu characters",
|
70
|
+
(unsigned long)sizeof(rxc->src));
|
71
|
+
return EINVAL;
|
75
72
|
}
|
76
|
-
rxc
|
73
|
+
rxc = ALLOC_N(struct _rxC, 1);
|
77
74
|
rxc->next = 0;
|
78
75
|
rxc->clas = clas;
|
79
76
|
|
@@ -82,15 +79,15 @@ oj_rxclass_append(RxClass rc, const char *expr, VALUE clas) {
|
|
82
79
|
#else
|
83
80
|
rxc->rrx = Qnil;
|
84
81
|
if (0 != (err = regcomp(&rxc->rx, expr, flags))) {
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
regerror(err, &rxc->rx, rc->err, sizeof(rc->err));
|
83
|
+
free(rxc);
|
84
|
+
return err;
|
88
85
|
}
|
89
86
|
#endif
|
90
87
|
if (NULL == rc->tail) {
|
91
|
-
|
88
|
+
rc->head = rxc;
|
92
89
|
} else {
|
93
|
-
|
90
|
+
rc->tail->next = rxc;
|
94
91
|
}
|
95
92
|
rc->tail = rxc;
|
96
93
|
|
@@ -99,49 +96,48 @@ oj_rxclass_append(RxClass rc, const char *expr, VALUE clas) {
|
|
99
96
|
|
100
97
|
VALUE
|
101
98
|
oj_rxclass_match(RxClass rc, const char *str, int len) {
|
102
|
-
RxC
|
103
|
-
char
|
104
|
-
|
99
|
+
RxC rxc;
|
100
|
+
char buf[4096];
|
101
|
+
|
105
102
|
for (rxc = rc->head; NULL != rxc; rxc = rxc->next) {
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
103
|
+
if (Qnil != rxc->rrx) {
|
104
|
+
// Must use a valiabel for this to work.
|
105
|
+
volatile VALUE rstr = rb_str_new(str, len);
|
106
|
+
|
107
|
+
// if (Qtrue == rb_funcall(rxc->rrx, rb_intern("match?"), 1, rstr)) {
|
108
|
+
if (Qnil != rb_funcall(rxc->rrx, rb_intern("match"), 1, rstr)) {
|
109
|
+
return rxc->clas;
|
110
|
+
}
|
111
|
+
} else if (len < (int)sizeof(buf)) {
|
115
112
|
#if !IS_WINDOWS
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
113
|
+
// string is not \0 terminated so copy and atempt a match
|
114
|
+
memcpy(buf, str, len);
|
115
|
+
buf[len] = '\0';
|
116
|
+
if (0 == regexec(&rxc->rx, buf, 0, NULL, 0)) { // match
|
117
|
+
return rxc->clas;
|
118
|
+
}
|
122
119
|
#endif
|
123
|
-
|
124
|
-
|
125
|
-
|
120
|
+
} else {
|
121
|
+
// TBD allocate a larger buffer and attempt
|
122
|
+
}
|
126
123
|
}
|
127
124
|
return Qnil;
|
128
125
|
}
|
129
126
|
|
130
|
-
void
|
131
|
-
oj_rxclass_copy(RxClass src, RxClass dest) {
|
127
|
+
void oj_rxclass_copy(RxClass src, RxClass dest) {
|
132
128
|
dest->head = NULL;
|
133
129
|
dest->tail = NULL;
|
134
130
|
if (NULL != src->head) {
|
135
|
-
|
131
|
+
RxC rxc;
|
136
132
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
133
|
+
for (rxc = src->head; NULL != rxc; rxc = rxc->next) {
|
134
|
+
if (Qnil != rxc->rrx) {
|
135
|
+
oj_rxclass_rappend(dest, rxc->rrx, rxc->clas);
|
136
|
+
} else {
|
141
137
|
#if !IS_WINDOWS
|
142
|
-
|
138
|
+
oj_rxclass_append(dest, rxc->src, rxc->clas);
|
143
139
|
#endif
|
144
|
-
|
145
|
-
|
140
|
+
}
|
141
|
+
}
|
146
142
|
}
|
147
143
|
}
|
data/ext/oj/rxclass.h
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2017 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
5
3
|
|
6
4
|
#ifndef OJ_RXCLASS_H
|
7
5
|
#define OJ_RXCLASS_H
|
8
6
|
|
9
7
|
#include <stdbool.h>
|
8
|
+
|
10
9
|
#include "ruby.h"
|
11
10
|
|
12
11
|
struct _rxC;
|
13
12
|
|
14
13
|
typedef struct _rxClass {
|
15
|
-
struct _rxC
|
16
|
-
struct _rxC
|
17
|
-
char
|
18
|
-
} *RxClass;
|
14
|
+
struct _rxC *head;
|
15
|
+
struct _rxC *tail;
|
16
|
+
char err[128];
|
17
|
+
} * RxClass;
|
19
18
|
|
20
|
-
extern void
|
21
|
-
extern void
|
22
|
-
extern int
|
23
|
-
extern VALUE
|
24
|
-
extern void
|
25
|
-
extern void
|
19
|
+
extern void oj_rxclass_init(RxClass rc);
|
20
|
+
extern void oj_rxclass_cleanup(RxClass rc);
|
21
|
+
extern int oj_rxclass_append(RxClass rc, const char *expr, VALUE clas);
|
22
|
+
extern VALUE oj_rxclass_match(RxClass rc, const char *str, int len);
|
23
|
+
extern void oj_rxclass_copy(RxClass src, RxClass dest);
|
24
|
+
extern void oj_rxclass_rappend(RxClass rc, VALUE rx, VALUE clas);
|
26
25
|
|
27
26
|
#endif /* OJ_RXCLASS_H */
|
data/ext/oj/saj.c
CHANGED
@@ -1,48 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
* All rights reserved.
|
4
|
-
*/
|
1
|
+
// Copyright (c) 2012 Peter Ohler. All rights reserved.
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for license details.
|
5
3
|
|
6
4
|
#if !IS_WINDOWS
|
7
|
-
#include <sys/resource.h>
|
5
|
+
#include <sys/resource.h> /* for getrlimit() on linux */
|
8
6
|
#endif
|
9
|
-
#include <
|
7
|
+
#include <math.h>
|
10
8
|
#include <stdio.h>
|
9
|
+
#include <stdlib.h>
|
11
10
|
#include <string.h>
|
12
|
-
#include <math.h>
|
13
11
|
#include <sys/types.h>
|
14
12
|
#include <unistd.h>
|
15
13
|
|
16
14
|
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
17
|
-
#define OJ_INFINITY (1.0/0.0)
|
15
|
+
#define OJ_INFINITY (1.0 / 0.0)
|
18
16
|
|
19
|
-
#include "oj.h"
|
20
17
|
#include "encode.h"
|
18
|
+
#include "oj.h"
|
21
19
|
|
22
20
|
typedef struct _parseInfo {
|
23
|
-
char
|
24
|
-
char
|
25
|
-
void
|
26
|
-
VALUE
|
27
|
-
int
|
28
|
-
int
|
29
|
-
int
|
30
|
-
int
|
31
|
-
int
|
32
|
-
int
|
33
|
-
} *ParseInfo;
|
34
|
-
|
35
|
-
static void
|
36
|
-
static void
|
37
|
-
static void
|
38
|
-
static void
|
39
|
-
static void
|
40
|
-
static void
|
41
|
-
static void
|
42
|
-
static void
|
43
|
-
static void
|
44
|
-
static char*
|
45
|
-
static void
|
21
|
+
char *str; /* buffer being read from */
|
22
|
+
char *s; /* current position in buffer */
|
23
|
+
void *stack_min;
|
24
|
+
VALUE handler;
|
25
|
+
int has_hash_start;
|
26
|
+
int has_hash_end;
|
27
|
+
int has_array_start;
|
28
|
+
int has_array_end;
|
29
|
+
int has_add_value;
|
30
|
+
int has_error;
|
31
|
+
} * ParseInfo;
|
32
|
+
|
33
|
+
static void read_next(ParseInfo pi, const char *key);
|
34
|
+
static void read_hash(ParseInfo pi, const char *key);
|
35
|
+
static void read_array(ParseInfo pi, const char *key);
|
36
|
+
static void read_str(ParseInfo pi, const char *key);
|
37
|
+
static void read_num(ParseInfo pi, const char *key);
|
38
|
+
static void read_true(ParseInfo pi, const char *key);
|
39
|
+
static void read_false(ParseInfo pi, const char *key);
|
40
|
+
static void read_nil(ParseInfo pi, const char *key);
|
41
|
+
static void next_non_white(ParseInfo pi);
|
42
|
+
static char *read_quoted_value(ParseInfo pi);
|
43
|
+
static void skip_comment(ParseInfo pi);
|
46
44
|
|
47
45
|
/* This JSON parser is a single pass, destructive, callback parser. It is a
|
48
46
|
* single pass parse since it only make one pass over the characters in the
|
@@ -57,126 +55,108 @@ static void skip_comment(ParseInfo pi);
|
|
57
55
|
* all cases to parse the string.
|
58
56
|
*/
|
59
57
|
|
60
|
-
inline static void
|
61
|
-
|
62
|
-
char
|
63
|
-
|
64
|
-
int
|
65
|
-
int col = 1;
|
58
|
+
inline static void call_error(const char *msg, ParseInfo pi, const char *file, int line) {
|
59
|
+
char buf[128];
|
60
|
+
const char *s = pi->s;
|
61
|
+
int jline = 1;
|
62
|
+
int col = 1;
|
66
63
|
|
67
64
|
for (; pi->str < s && '\n' != *s; s--) {
|
68
|
-
|
65
|
+
col++;
|
69
66
|
}
|
70
67
|
for (; pi->str < s; s--) {
|
71
|
-
|
72
|
-
|
73
|
-
|
68
|
+
if ('\n' == *s) {
|
69
|
+
jline++;
|
70
|
+
}
|
74
71
|
}
|
75
72
|
sprintf(buf, "%s at line %d, column %d [%s:%d]", msg, jline, col, file, line);
|
76
73
|
rb_funcall(pi->handler, oj_error_id, 3, rb_str_new2(buf), LONG2NUM(jline), LONG2NUM(col));
|
77
74
|
}
|
78
75
|
|
79
|
-
inline static void
|
80
|
-
next_non_white(ParseInfo pi) {
|
76
|
+
inline static void next_non_white(ParseInfo pi) {
|
81
77
|
for (; 1; pi->s++) {
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
break;
|
92
|
-
default:
|
93
|
-
return;
|
94
|
-
}
|
78
|
+
switch (*pi->s) {
|
79
|
+
case ' ':
|
80
|
+
case '\t':
|
81
|
+
case '\f':
|
82
|
+
case '\n':
|
83
|
+
case '\r': break;
|
84
|
+
case '/': skip_comment(pi); break;
|
85
|
+
default: return;
|
86
|
+
}
|
95
87
|
}
|
96
88
|
}
|
97
89
|
|
98
|
-
inline static void
|
99
|
-
|
100
|
-
volatile VALUE k;
|
90
|
+
inline static void call_add_value(VALUE handler, VALUE value, const char *key) {
|
91
|
+
volatile VALUE k;
|
101
92
|
|
102
93
|
if (0 == key) {
|
103
|
-
|
94
|
+
k = Qnil;
|
104
95
|
} else {
|
105
|
-
|
106
|
-
|
96
|
+
k = rb_str_new2(key);
|
97
|
+
k = oj_encode(k);
|
107
98
|
}
|
108
99
|
rb_funcall(handler, oj_add_value_id, 2, value, k);
|
109
100
|
}
|
110
101
|
|
111
|
-
inline static void
|
112
|
-
|
113
|
-
volatile VALUE k;
|
102
|
+
inline static void call_no_value(VALUE handler, ID method, const char *key) {
|
103
|
+
volatile VALUE k;
|
114
104
|
|
115
105
|
if (0 == key) {
|
116
|
-
|
106
|
+
k = Qnil;
|
117
107
|
} else {
|
118
|
-
|
119
|
-
|
108
|
+
k = rb_str_new2(key);
|
109
|
+
k = oj_encode(k);
|
120
110
|
}
|
121
111
|
rb_funcall(handler, method, 1, k);
|
122
112
|
}
|
123
113
|
|
124
|
-
static void
|
125
|
-
skip_comment(ParseInfo pi) {
|
114
|
+
static void skip_comment(ParseInfo pi) {
|
126
115
|
pi->s++; /* skip first / */
|
127
116
|
if ('*' == *pi->s) {
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
117
|
+
pi->s++;
|
118
|
+
for (; '\0' != *pi->s; pi->s++) {
|
119
|
+
if ('*' == *pi->s && '/' == *(pi->s + 1)) {
|
120
|
+
pi->s++;
|
121
|
+
return;
|
122
|
+
} else if ('\0' == *pi->s) {
|
123
|
+
if (pi->has_error) {
|
124
|
+
call_error("comment not terminated", pi, __FILE__, __LINE__);
|
125
|
+
} else {
|
126
|
+
raise_error("comment not terminated", pi->str, pi->s);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
141
130
|
} else if ('/' == *pi->s) {
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
}
|
152
|
-
}
|
131
|
+
for (; 1; pi->s++) {
|
132
|
+
switch (*pi->s) {
|
133
|
+
case '\n':
|
134
|
+
case '\r':
|
135
|
+
case '\f':
|
136
|
+
case '\0': return;
|
137
|
+
default: break;
|
138
|
+
}
|
139
|
+
}
|
153
140
|
} else {
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
141
|
+
if (pi->has_error) {
|
142
|
+
call_error("invalid comment", pi, __FILE__, __LINE__);
|
143
|
+
} else {
|
144
|
+
raise_error("invalid comment", pi->str, pi->s);
|
145
|
+
}
|
159
146
|
}
|
160
147
|
}
|
161
148
|
|
162
|
-
static void
|
163
|
-
|
164
|
-
VALUE obj;
|
149
|
+
static void read_next(ParseInfo pi, const char *key) {
|
150
|
+
VALUE obj;
|
165
151
|
|
166
|
-
if ((void*)&obj < pi->stack_min) {
|
167
|
-
|
152
|
+
if ((void *)&obj < pi->stack_min) {
|
153
|
+
rb_raise(rb_eSysStackError, "JSON is too deeply nested");
|
168
154
|
}
|
169
|
-
next_non_white(pi);
|
155
|
+
next_non_white(pi); /* skip white space */
|
170
156
|
switch (*pi->s) {
|
171
|
-
case '{':
|
172
|
-
|
173
|
-
|
174
|
-
case '[':
|
175
|
-
read_array(pi, key);
|
176
|
-
break;
|
177
|
-
case '"':
|
178
|
-
read_str(pi, key);
|
179
|
-
break;
|
157
|
+
case '{': read_hash(pi, key); break;
|
158
|
+
case '[': read_array(pi, key); break;
|
159
|
+
case '"': read_str(pi, key); break;
|
180
160
|
case '+':
|
181
161
|
case '-':
|
182
162
|
case '0':
|
@@ -188,113 +168,104 @@ read_next(ParseInfo pi, const char *key) {
|
|
188
168
|
case '6':
|
189
169
|
case '7':
|
190
170
|
case '8':
|
191
|
-
case '9':
|
192
|
-
|
193
|
-
|
194
|
-
case '
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
read_true(pi, key);
|
199
|
-
break;
|
200
|
-
case 'f':
|
201
|
-
read_false(pi, key);
|
202
|
-
break;
|
203
|
-
case 'n':
|
204
|
-
read_nil(pi, key);
|
205
|
-
break;
|
206
|
-
case '\0':
|
207
|
-
return;
|
208
|
-
default:
|
209
|
-
return;
|
171
|
+
case '9': read_num(pi, key); break;
|
172
|
+
case 'I': read_num(pi, key); break;
|
173
|
+
case 't': read_true(pi, key); break;
|
174
|
+
case 'f': read_false(pi, key); break;
|
175
|
+
case 'n': read_nil(pi, key); break;
|
176
|
+
case '\0': return;
|
177
|
+
default: return;
|
210
178
|
}
|
211
179
|
}
|
212
180
|
|
213
|
-
static void
|
214
|
-
|
215
|
-
|
216
|
-
|
181
|
+
static void read_hash(ParseInfo pi, const char *key) {
|
182
|
+
const char *ks;
|
183
|
+
|
217
184
|
if (pi->has_hash_start) {
|
218
|
-
|
185
|
+
call_no_value(pi->handler, oj_hash_start_id, key);
|
219
186
|
}
|
220
187
|
pi->s++;
|
221
188
|
next_non_white(pi);
|
222
189
|
if ('}' == *pi->s) {
|
223
|
-
|
190
|
+
pi->s++;
|
224
191
|
} else {
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
192
|
+
while (1) {
|
193
|
+
next_non_white(pi);
|
194
|
+
ks = read_quoted_value(pi);
|
195
|
+
next_non_white(pi);
|
196
|
+
if (':' == *pi->s) {
|
197
|
+
pi->s++;
|
198
|
+
} else {
|
199
|
+
if (pi->has_error) {
|
200
|
+
call_error("invalid format, expected :", pi, __FILE__, __LINE__);
|
201
|
+
}
|
202
|
+
raise_error("invalid format, expected :", pi->str, pi->s);
|
203
|
+
}
|
204
|
+
read_next(pi, ks);
|
205
|
+
next_non_white(pi);
|
206
|
+
if ('}' == *pi->s) {
|
207
|
+
pi->s++;
|
208
|
+
break;
|
209
|
+
} else if (',' == *pi->s) {
|
210
|
+
pi->s++;
|
211
|
+
} else {
|
212
|
+
if (pi->has_error) {
|
213
|
+
call_error("invalid format, expected , or } while in an object",
|
214
|
+
pi,
|
215
|
+
__FILE__,
|
216
|
+
__LINE__);
|
217
|
+
}
|
218
|
+
raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
|
219
|
+
}
|
220
|
+
}
|
251
221
|
}
|
252
222
|
if (pi->has_hash_end) {
|
253
|
-
|
223
|
+
call_no_value(pi->handler, oj_hash_end_id, key);
|
254
224
|
}
|
255
225
|
}
|
256
226
|
|
257
|
-
static void
|
258
|
-
read_array(ParseInfo pi, const char *key) {
|
227
|
+
static void read_array(ParseInfo pi, const char *key) {
|
259
228
|
if (pi->has_array_start) {
|
260
|
-
|
229
|
+
call_no_value(pi->handler, oj_array_start_id, key);
|
261
230
|
}
|
262
231
|
pi->s++;
|
263
232
|
next_non_white(pi);
|
264
233
|
if (']' == *pi->s) {
|
265
|
-
|
234
|
+
pi->s++;
|
266
235
|
} else {
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
236
|
+
while (1) {
|
237
|
+
read_next(pi, 0);
|
238
|
+
next_non_white(pi);
|
239
|
+
if (',' == *pi->s) {
|
240
|
+
pi->s++;
|
241
|
+
} else if (']' == *pi->s) {
|
242
|
+
pi->s++;
|
243
|
+
break;
|
244
|
+
} else {
|
245
|
+
if (pi->has_error) {
|
246
|
+
call_error("invalid format, expected , or ] while in an array",
|
247
|
+
pi,
|
248
|
+
__FILE__,
|
249
|
+
__LINE__);
|
250
|
+
}
|
251
|
+
raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
|
252
|
+
}
|
253
|
+
}
|
282
254
|
}
|
283
255
|
if (pi->has_array_end) {
|
284
|
-
|
256
|
+
call_no_value(pi->handler, oj_array_end_id, key);
|
285
257
|
}
|
286
258
|
}
|
287
259
|
|
288
|
-
static void
|
289
|
-
|
290
|
-
char *text;
|
260
|
+
static void read_str(ParseInfo pi, const char *key) {
|
261
|
+
char *text;
|
291
262
|
|
292
263
|
text = read_quoted_value(pi);
|
293
264
|
if (pi->has_add_value) {
|
294
|
-
|
265
|
+
VALUE s = rb_str_new2(text);
|
295
266
|
|
296
|
-
|
297
|
-
|
267
|
+
s = oj_encode(s);
|
268
|
+
call_add_value(pi->handler, s, key);
|
298
269
|
}
|
299
270
|
}
|
300
271
|
|
@@ -304,230 +275,228 @@ read_str(ParseInfo pi, const char *key) {
|
|
304
275
|
#define NUM_MAX (FIXNUM_MAX >> 8)
|
305
276
|
#endif
|
306
277
|
|
307
|
-
static void
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
long
|
312
|
-
long
|
313
|
-
|
314
|
-
int
|
315
|
-
int
|
316
|
-
int big = 0;
|
278
|
+
static void read_num(ParseInfo pi, const char *key) {
|
279
|
+
char * start = pi->s;
|
280
|
+
int64_t n = 0;
|
281
|
+
long a = 0;
|
282
|
+
long div = 1;
|
283
|
+
long e = 0;
|
284
|
+
int neg = 0;
|
285
|
+
int eneg = 0;
|
286
|
+
int big = 0;
|
317
287
|
|
318
288
|
if ('-' == *pi->s) {
|
319
|
-
|
320
|
-
|
289
|
+
pi->s++;
|
290
|
+
neg = 1;
|
321
291
|
} else if ('+' == *pi->s) {
|
322
|
-
|
292
|
+
pi->s++;
|
323
293
|
}
|
324
294
|
if ('I' == *pi->s) {
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
295
|
+
if (0 != strncmp("Infinity", pi->s, 8)) {
|
296
|
+
if (pi->has_error) {
|
297
|
+
call_error("number or other value", pi, __FILE__, __LINE__);
|
298
|
+
}
|
299
|
+
raise_error("number or other value", pi->str, pi->s);
|
300
|
+
}
|
301
|
+
pi->s += 8;
|
302
|
+
if (neg) {
|
303
|
+
if (pi->has_add_value) {
|
304
|
+
call_add_value(pi->handler, rb_float_new(-OJ_INFINITY), key);
|
305
|
+
}
|
306
|
+
} else {
|
307
|
+
if (pi->has_add_value) {
|
308
|
+
call_add_value(pi->handler, rb_float_new(OJ_INFINITY), key);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
return;
|
342
312
|
}
|
343
313
|
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
314
|
+
if (big) {
|
315
|
+
big++;
|
316
|
+
} else {
|
317
|
+
n = n * 10 + (*pi->s - '0');
|
318
|
+
if (NUM_MAX <= n) {
|
319
|
+
big = 1;
|
320
|
+
}
|
321
|
+
}
|
352
322
|
}
|
353
323
|
if ('.' == *pi->s) {
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
324
|
+
pi->s++;
|
325
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
326
|
+
a = a * 10 + (*pi->s - '0');
|
327
|
+
div *= 10;
|
328
|
+
if (NUM_MAX <= div) {
|
329
|
+
big = 1;
|
330
|
+
}
|
331
|
+
}
|
362
332
|
}
|
363
333
|
if ('e' == *pi->s || 'E' == *pi->s) {
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
334
|
+
pi->s++;
|
335
|
+
if ('-' == *pi->s) {
|
336
|
+
pi->s++;
|
337
|
+
eneg = 1;
|
338
|
+
} else if ('+' == *pi->s) {
|
339
|
+
pi->s++;
|
340
|
+
}
|
341
|
+
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
342
|
+
e = e * 10 + (*pi->s - '0');
|
343
|
+
if (NUM_MAX <= e) {
|
344
|
+
big = 1;
|
345
|
+
}
|
346
|
+
}
|
377
347
|
}
|
378
348
|
if (0 == e && 0 == a && 1 == div) {
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
349
|
+
if (big) {
|
350
|
+
char c = *pi->s;
|
351
|
+
|
352
|
+
*pi->s = '\0';
|
353
|
+
if (pi->has_add_value) {
|
354
|
+
call_add_value(pi->handler,
|
355
|
+
rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new2(start)),
|
356
|
+
key);
|
357
|
+
}
|
358
|
+
*pi->s = c;
|
359
|
+
} else {
|
360
|
+
if (neg) {
|
361
|
+
n = -n;
|
362
|
+
}
|
363
|
+
if (pi->has_add_value) {
|
364
|
+
call_add_value(pi->handler, LONG2NUM(n), key);
|
365
|
+
}
|
366
|
+
}
|
367
|
+
return;
|
396
368
|
} else { /* decimal */
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
369
|
+
if (big) {
|
370
|
+
char c = *pi->s;
|
371
|
+
|
372
|
+
*pi->s = '\0';
|
373
|
+
if (pi->has_add_value) {
|
374
|
+
call_add_value(pi->handler,
|
375
|
+
rb_funcall(rb_cObject, oj_bigdecimal_id, 1, rb_str_new2(start)),
|
376
|
+
key);
|
377
|
+
}
|
378
|
+
*pi->s = c;
|
379
|
+
} else {
|
380
|
+
double d = (double)n + (double)a / (double)div;
|
381
|
+
|
382
|
+
if (neg) {
|
383
|
+
d = -d;
|
384
|
+
}
|
385
|
+
if (1 < big) {
|
386
|
+
e += big - 1;
|
387
|
+
}
|
388
|
+
if (0 != e) {
|
389
|
+
if (eneg) {
|
390
|
+
e = -e;
|
391
|
+
}
|
392
|
+
d *= pow(10.0, e);
|
393
|
+
}
|
394
|
+
if (pi->has_add_value) {
|
395
|
+
call_add_value(pi->handler, rb_float_new(d), key);
|
396
|
+
}
|
397
|
+
}
|
424
398
|
}
|
425
399
|
}
|
426
400
|
|
427
|
-
static void
|
428
|
-
read_true(ParseInfo pi, const char *key) {
|
401
|
+
static void read_true(ParseInfo pi, const char *key) {
|
429
402
|
pi->s++;
|
430
403
|
if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
404
|
+
if (pi->has_error) {
|
405
|
+
call_error("invalid format, expected 'true'", pi, __FILE__, __LINE__);
|
406
|
+
}
|
407
|
+
raise_error("invalid format, expected 'true'", pi->str, pi->s);
|
435
408
|
}
|
436
409
|
pi->s += 3;
|
437
410
|
if (pi->has_add_value) {
|
438
|
-
|
411
|
+
call_add_value(pi->handler, Qtrue, key);
|
439
412
|
}
|
440
413
|
}
|
441
414
|
|
442
|
-
static void
|
443
|
-
read_false(ParseInfo pi, const char *key) {
|
415
|
+
static void read_false(ParseInfo pi, const char *key) {
|
444
416
|
pi->s++;
|
445
417
|
if ('a' != *pi->s || 'l' != *(pi->s + 1) || 's' != *(pi->s + 2) || 'e' != *(pi->s + 3)) {
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
418
|
+
if (pi->has_error) {
|
419
|
+
call_error("invalid format, expected 'false'", pi, __FILE__, __LINE__);
|
420
|
+
}
|
421
|
+
raise_error("invalid format, expected 'false'", pi->str, pi->s);
|
450
422
|
}
|
451
423
|
pi->s += 4;
|
452
424
|
if (pi->has_add_value) {
|
453
|
-
|
425
|
+
call_add_value(pi->handler, Qfalse, key);
|
454
426
|
}
|
455
427
|
}
|
456
428
|
|
457
|
-
static void
|
458
|
-
read_nil(ParseInfo pi, const char *key) {
|
429
|
+
static void read_nil(ParseInfo pi, const char *key) {
|
459
430
|
pi->s++;
|
460
431
|
if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
432
|
+
if (pi->has_error) {
|
433
|
+
call_error("invalid format, expected 'null'", pi, __FILE__, __LINE__);
|
434
|
+
}
|
435
|
+
raise_error("invalid format, expected 'null'", pi->str, pi->s);
|
465
436
|
}
|
466
437
|
pi->s += 3;
|
467
438
|
if (pi->has_add_value) {
|
468
|
-
|
439
|
+
call_add_value(pi->handler, Qnil, key);
|
469
440
|
}
|
470
441
|
}
|
471
442
|
|
472
|
-
static uint32_t
|
473
|
-
|
474
|
-
|
475
|
-
int i;
|
443
|
+
static uint32_t read_hex(ParseInfo pi, char *h) {
|
444
|
+
uint32_t b = 0;
|
445
|
+
int i;
|
476
446
|
|
477
447
|
/* TBD this can be made faster with a table */
|
478
448
|
for (i = 0; i < 4; i++, h++) {
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
449
|
+
b = b << 4;
|
450
|
+
if ('0' <= *h && *h <= '9') {
|
451
|
+
b += *h - '0';
|
452
|
+
} else if ('A' <= *h && *h <= 'F') {
|
453
|
+
b += *h - 'A' + 10;
|
454
|
+
} else if ('a' <= *h && *h <= 'f') {
|
455
|
+
b += *h - 'a' + 10;
|
456
|
+
} else {
|
457
|
+
pi->s = h;
|
458
|
+
if (pi->has_error) {
|
459
|
+
call_error("invalid hex character", pi, __FILE__, __LINE__);
|
460
|
+
}
|
461
|
+
raise_error("invalid hex character", pi->str, pi->s);
|
462
|
+
}
|
493
463
|
}
|
494
464
|
return b;
|
495
465
|
}
|
496
466
|
|
497
|
-
static char*
|
498
|
-
unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
467
|
+
static char *unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
499
468
|
if (0x0000007F >= code) {
|
500
|
-
|
469
|
+
*t = (char)code;
|
501
470
|
} else if (0x000007FF >= code) {
|
502
|
-
|
503
|
-
|
471
|
+
*t++ = 0xC0 | (code >> 6);
|
472
|
+
*t = 0x80 | (0x3F & code);
|
504
473
|
} else if (0x0000FFFF >= code) {
|
505
|
-
|
506
|
-
|
507
|
-
|
474
|
+
*t++ = 0xE0 | (code >> 12);
|
475
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
476
|
+
*t = 0x80 | (0x3F & code);
|
508
477
|
} else if (0x001FFFFF >= code) {
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
478
|
+
*t++ = 0xF0 | (code >> 18);
|
479
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
480
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
481
|
+
*t = 0x80 | (0x3F & code);
|
513
482
|
} else if (0x03FFFFFF >= code) {
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
483
|
+
*t++ = 0xF8 | (code >> 24);
|
484
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
485
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
486
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
487
|
+
*t = 0x80 | (0x3F & code);
|
519
488
|
} else if (0x7FFFFFFF >= code) {
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
489
|
+
*t++ = 0xFC | (code >> 30);
|
490
|
+
*t++ = 0x80 | ((code >> 24) & 0x3F);
|
491
|
+
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
492
|
+
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
493
|
+
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
494
|
+
*t = 0x80 | (0x3F & code);
|
526
495
|
} else {
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
496
|
+
if (pi->has_error) {
|
497
|
+
call_error("invalid Unicode", pi, __FILE__, __LINE__);
|
498
|
+
}
|
499
|
+
raise_error("invalid Unicode", pi->str, pi->s);
|
531
500
|
}
|
532
501
|
return t;
|
533
502
|
}
|
@@ -535,119 +504,119 @@ unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
|
535
504
|
/* Assume the value starts immediately and goes until the quote character is
|
536
505
|
* reached again. Do not read the character after the terminating quote.
|
537
506
|
*/
|
538
|
-
static char*
|
539
|
-
|
540
|
-
char
|
541
|
-
char
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
h++; /* skip quote character */
|
507
|
+
static char *read_quoted_value(ParseInfo pi) {
|
508
|
+
char * value = 0;
|
509
|
+
char * h = pi->s; /* head */
|
510
|
+
char * t = h; /* tail */
|
511
|
+
uint32_t code;
|
512
|
+
|
513
|
+
h++; /* skip quote character */
|
546
514
|
t++;
|
547
515
|
value = h;
|
548
516
|
for (; '"' != *h; h++, t++) {
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
}
|
599
|
-
*t
|
517
|
+
if ('\0' == *h) {
|
518
|
+
pi->s = h;
|
519
|
+
raise_error("quoted string not terminated", pi->str, pi->s);
|
520
|
+
} else if ('\\' == *h) {
|
521
|
+
h++;
|
522
|
+
switch (*h) {
|
523
|
+
case 'n': *t = '\n'; break;
|
524
|
+
case 'r': *t = '\r'; break;
|
525
|
+
case 't': *t = '\t'; break;
|
526
|
+
case 'f': *t = '\f'; break;
|
527
|
+
case 'b': *t = '\b'; break;
|
528
|
+
case '"': *t = '"'; break;
|
529
|
+
case '/': *t = '/'; break;
|
530
|
+
case '\\': *t = '\\'; break;
|
531
|
+
case 'u':
|
532
|
+
h++;
|
533
|
+
code = read_hex(pi, h);
|
534
|
+
h += 3;
|
535
|
+
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
536
|
+
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
537
|
+
uint32_t c2;
|
538
|
+
|
539
|
+
h++;
|
540
|
+
if ('\\' != *h || 'u' != *(h + 1)) {
|
541
|
+
pi->s = h;
|
542
|
+
if (pi->has_error) {
|
543
|
+
call_error("invalid escaped character", pi, __FILE__, __LINE__);
|
544
|
+
}
|
545
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
546
|
+
}
|
547
|
+
h += 2;
|
548
|
+
c2 = read_hex(pi, h);
|
549
|
+
h += 3;
|
550
|
+
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
551
|
+
code = ((c1 << 10) | c2) + 0x00010000;
|
552
|
+
}
|
553
|
+
t = unicode_to_chars(pi, t, code);
|
554
|
+
break;
|
555
|
+
default:
|
556
|
+
pi->s = h;
|
557
|
+
if (pi->has_error) {
|
558
|
+
call_error("invalid escaped character", pi, __FILE__, __LINE__);
|
559
|
+
}
|
560
|
+
raise_error("invalid escaped character", pi->str, pi->s);
|
561
|
+
break;
|
562
|
+
}
|
563
|
+
} else if (t != h) {
|
564
|
+
*t = *h;
|
565
|
+
}
|
566
|
+
}
|
567
|
+
*t = '\0'; /* terminate value */
|
600
568
|
pi->s = h + 1;
|
601
569
|
|
602
570
|
return value;
|
603
571
|
}
|
604
572
|
|
605
|
-
static void
|
606
|
-
|
607
|
-
|
608
|
-
struct _parseInfo pi;
|
573
|
+
static void saj_parse(VALUE handler, char *json) {
|
574
|
+
volatile VALUE obj = Qnil;
|
575
|
+
struct _parseInfo pi;
|
609
576
|
|
610
577
|
if (0 == json) {
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
578
|
+
if (pi.has_error) {
|
579
|
+
call_error("Invalid arg, xml string can not be null", &pi, __FILE__, __LINE__);
|
580
|
+
}
|
581
|
+
raise_error("Invalid arg, xml string can not be null", json, 0);
|
615
582
|
}
|
616
583
|
/* skip UTF-8 BOM if present */
|
617
584
|
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
618
|
-
|
585
|
+
json += 3;
|
619
586
|
}
|
620
587
|
/* initialize parse info */
|
621
588
|
pi.str = json;
|
622
|
-
pi.s
|
589
|
+
pi.s = json;
|
623
590
|
#if IS_WINDOWS
|
624
|
-
pi.stack_min = (void*)((char*)&obj -
|
591
|
+
pi.stack_min = (void *)((char *)&obj -
|
592
|
+
(512 * 1024)); /* assume a 1M stack and give half to ruby */
|
625
593
|
#else
|
626
594
|
{
|
627
|
-
|
595
|
+
struct rlimit lim;
|
628
596
|
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
597
|
+
if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
|
598
|
+
pi.stack_min = (void *)((char *)&obj - (lim.rlim_cur / 4 *
|
599
|
+
3)); /* let 3/4ths of the stack be used only */
|
600
|
+
} else {
|
601
|
+
pi.stack_min = 0; /* indicates not to check stack limit */
|
602
|
+
}
|
634
603
|
}
|
635
604
|
#endif
|
636
|
-
pi.handler
|
637
|
-
pi.has_hash_start
|
638
|
-
pi.has_hash_end
|
605
|
+
pi.handler = handler;
|
606
|
+
pi.has_hash_start = rb_respond_to(handler, oj_hash_start_id);
|
607
|
+
pi.has_hash_end = rb_respond_to(handler, oj_hash_end_id);
|
639
608
|
pi.has_array_start = rb_respond_to(handler, oj_array_start_id);
|
640
|
-
pi.has_array_end
|
641
|
-
pi.has_add_value
|
642
|
-
pi.has_error
|
609
|
+
pi.has_array_end = rb_respond_to(handler, oj_array_end_id);
|
610
|
+
pi.has_add_value = rb_respond_to(handler, oj_add_value_id);
|
611
|
+
pi.has_error = rb_respond_to(handler, oj_error_id);
|
643
612
|
read_next(&pi, 0);
|
644
613
|
next_non_white(&pi);
|
645
614
|
if ('\0' != *pi.s) {
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
615
|
+
if (pi.has_error) {
|
616
|
+
call_error("invalid format, extra characters", &pi, __FILE__, __LINE__);
|
617
|
+
} else {
|
618
|
+
raise_error("invalid format, extra characters", pi.str, pi.s);
|
619
|
+
}
|
651
620
|
}
|
652
621
|
}
|
653
622
|
|
@@ -664,48 +633,48 @@ saj_parse(VALUE handler, char *json) {
|
|
664
633
|
*/
|
665
634
|
VALUE
|
666
635
|
oj_saj_parse(int argc, VALUE *argv, VALUE self) {
|
667
|
-
char
|
668
|
-
size_t
|
669
|
-
VALUE
|
636
|
+
char * json = 0;
|
637
|
+
size_t len = 0;
|
638
|
+
VALUE input = argv[1];
|
670
639
|
|
671
640
|
if (argc < 2) {
|
672
|
-
|
641
|
+
rb_raise(rb_eArgError, "Wrong number of arguments to saj_parse.\n");
|
673
642
|
}
|
674
643
|
if (rb_type(input) == T_STRING) {
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
644
|
+
// the json string gets modified so make a copy of it
|
645
|
+
len = RSTRING_LEN(input) + 1;
|
646
|
+
json = ALLOC_N(char, len);
|
647
|
+
strcpy(json, StringValuePtr(input));
|
679
648
|
} else {
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
649
|
+
VALUE clas = rb_obj_class(input);
|
650
|
+
volatile VALUE s;
|
651
|
+
|
652
|
+
if (oj_stringio_class == clas) {
|
653
|
+
s = rb_funcall2(input, oj_string_id, 0, 0);
|
654
|
+
len = RSTRING_LEN(s) + 1;
|
655
|
+
json = ALLOC_N(char, len);
|
656
|
+
strcpy(json, rb_string_value_cstr((VALUE *)&s));
|
688
657
|
#if !IS_WINDOWS
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
658
|
+
} else if (rb_cFile == clas && 0 == FIX2INT(rb_funcall(input, oj_pos_id, 0))) {
|
659
|
+
int fd = FIX2INT(rb_funcall(input, oj_fileno_id, 0));
|
660
|
+
ssize_t cnt;
|
661
|
+
|
662
|
+
len = lseek(fd, 0, SEEK_END);
|
663
|
+
lseek(fd, 0, SEEK_SET);
|
664
|
+
json = ALLOC_N(char, len + 1);
|
665
|
+
if (0 >= (cnt = read(fd, json, len)) || cnt != (ssize_t)len) {
|
666
|
+
rb_raise(rb_eIOError, "failed to read from IO Object.");
|
667
|
+
}
|
668
|
+
json[len] = '\0';
|
700
669
|
#endif
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
670
|
+
} else if (rb_respond_to(input, oj_read_id)) {
|
671
|
+
s = rb_funcall2(input, oj_read_id, 0, 0);
|
672
|
+
len = RSTRING_LEN(s) + 1;
|
673
|
+
json = ALLOC_N(char, len);
|
674
|
+
strcpy(json, rb_string_value_cstr((VALUE *)&s));
|
675
|
+
} else {
|
676
|
+
rb_raise(rb_eArgError, "saj_parse() expected a String or IO Object.");
|
677
|
+
}
|
709
678
|
}
|
710
679
|
saj_parse(*argv, json);
|
711
680
|
xfree(json);
|