oj 2.0.14 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of oj might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +47 -332
- data/ext/oj/buf.h +103 -0
- data/ext/oj/circarray.c +93 -0
- data/ext/oj/circarray.h +48 -0
- data/ext/oj/compat.c +112 -0
- data/ext/oj/dump.c +2 -1
- data/ext/oj/err.c +82 -0
- data/ext/oj/err.h +64 -0
- data/ext/oj/extconf.rb +1 -0
- data/ext/oj/hash.c +149 -0
- data/ext/oj/{cache.h → hash.h} +9 -10
- data/ext/oj/hash_test.c +501 -0
- data/ext/oj/object.c +514 -0
- data/ext/oj/odd.c +159 -0
- data/ext/oj/odd.h +61 -0
- data/ext/oj/oj.c +235 -305
- data/ext/oj/oj.h +18 -23
- data/ext/oj/parse.c +798 -0
- data/ext/oj/parse.h +88 -0
- data/ext/oj/resolve.c +117 -0
- data/ext/oj/resolve.h +38 -0
- data/ext/oj/saj.c +58 -86
- data/ext/oj/scp.c +308 -0
- data/ext/oj/strict.c +166 -0
- data/ext/oj/val_stack.c +48 -0
- data/ext/oj/val_stack.h +167 -0
- data/lib/oj.rb +1 -0
- data/lib/oj/saj.rb +11 -8
- data/lib/oj/schandler.rb +70 -0
- data/lib/oj/version.rb +1 -1
- data/test/bug.rb +14 -22
- data/test/perf_compat.rb +128 -0
- data/test/{perf_obj.rb → perf_object.rb} +18 -6
- data/test/perf_scp.rb +151 -0
- data/test/perf_strict.rb +23 -122
- data/test/sample.rb +2 -2
- data/test/test_compat.rb +342 -0
- data/test/test_object.rb +390 -0
- data/test/test_saj.rb +1 -1
- data/test/test_scp.rb +224 -0
- data/test/test_strict.rb +250 -0
- data/test/tests.rb +8 -18
- metadata +31 -10
- data/ext/oj/cache.c +0 -148
- data/ext/oj/load.c +0 -1089
- data/test/perf1.rb +0 -64
- data/test/perf2.rb +0 -76
- data/test/perf_obj_old.rb +0 -213
data/test/tests.rb
CHANGED
@@ -120,7 +120,6 @@ class Juice < ::Test::Unit::TestCase
|
|
120
120
|
:time_format=>:unix,
|
121
121
|
:bigdecimal_as_decimal=>true,
|
122
122
|
:bigdecimal_load=>false,
|
123
|
-
:max_stack=>65536,
|
124
123
|
:create_id=>'json_class'}, opts)
|
125
124
|
end
|
126
125
|
|
@@ -137,7 +136,6 @@ class Juice < ::Test::Unit::TestCase
|
|
137
136
|
:time_format=>:unix,
|
138
137
|
:bigdecimal_as_decimal=>true,
|
139
138
|
:bigdecimal_load=>false,
|
140
|
-
:max_stack=>65536,
|
141
139
|
:create_id=>'json_class'}
|
142
140
|
o2 = {
|
143
141
|
:indent=>4,
|
@@ -151,7 +149,6 @@ class Juice < ::Test::Unit::TestCase
|
|
151
149
|
:time_format=>:ruby,
|
152
150
|
:bigdecimal_as_decimal=>false,
|
153
151
|
:bigdecimal_load=>true,
|
154
|
-
:max_stack=>4000,
|
155
152
|
:create_id=>nil}
|
156
153
|
o3 = { :indent => 4 }
|
157
154
|
Oj.default_options = o2
|
@@ -237,10 +234,11 @@ class Juice < ::Test::Unit::TestCase
|
|
237
234
|
def test_symbol_strict
|
238
235
|
begin
|
239
236
|
Oj.dump(:abc, :mode => :strict)
|
240
|
-
assert(false)
|
241
237
|
rescue Exception
|
242
238
|
assert(true)
|
239
|
+
return
|
243
240
|
end
|
241
|
+
assert(false, "*** expected an exception")
|
244
242
|
end
|
245
243
|
def test_symbol_null
|
246
244
|
json = Oj.dump(:abc, :mode => :null)
|
@@ -578,12 +576,6 @@ class Juice < ::Test::Unit::TestCase
|
|
578
576
|
obj = Orange.new(true, 58)
|
579
577
|
json = Oj.dump(obj, :indent => 2)
|
580
578
|
assert(!json.nil?)
|
581
|
-
=begin
|
582
|
-
assert_equal(%{{
|
583
|
-
"json_class":"Orange",
|
584
|
-
"x":true,
|
585
|
-
"y":58}}, json)
|
586
|
-
=end
|
587
579
|
dump_and_load(obj, false)
|
588
580
|
end
|
589
581
|
|
@@ -930,14 +922,12 @@ class Juice < ::Test::Unit::TestCase
|
|
930
922
|
|
931
923
|
# Stream Deeply Nested
|
932
924
|
def test_deep_nest
|
933
|
-
unless 'jruby' == RUBY_DESCRIPTION.split(' ')[0]
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
assert(e.class == SystemStackError)
|
940
|
-
end
|
925
|
+
#unless 'jruby' == RUBY_DESCRIPTION.split(' ')[0]
|
926
|
+
begin
|
927
|
+
n = 10000
|
928
|
+
Oj.strict_load('[' * n + ']' * n)
|
929
|
+
rescue Exception => e
|
930
|
+
assert(false, e.message)
|
941
931
|
end
|
942
932
|
end
|
943
933
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oj
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Ohler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 'The fastest JSON parser and object serializer. '
|
14
14
|
email: peter@ohler.com
|
@@ -22,31 +22,48 @@ files:
|
|
22
22
|
- lib/oj/error.rb
|
23
23
|
- lib/oj/mimic.rb
|
24
24
|
- lib/oj/saj.rb
|
25
|
+
- lib/oj/schandler.rb
|
25
26
|
- lib/oj/version.rb
|
26
27
|
- lib/oj.rb
|
27
28
|
- ext/oj/extconf.rb
|
28
|
-
- ext/oj/
|
29
|
+
- ext/oj/buf.h
|
29
30
|
- ext/oj/cache8.h
|
31
|
+
- ext/oj/circarray.h
|
32
|
+
- ext/oj/err.h
|
33
|
+
- ext/oj/hash.h
|
34
|
+
- ext/oj/odd.h
|
30
35
|
- ext/oj/oj.h
|
31
|
-
- ext/oj/
|
36
|
+
- ext/oj/parse.h
|
37
|
+
- ext/oj/resolve.h
|
38
|
+
- ext/oj/val_stack.h
|
32
39
|
- ext/oj/cache8.c
|
40
|
+
- ext/oj/circarray.c
|
41
|
+
- ext/oj/compat.c
|
33
42
|
- ext/oj/dump.c
|
43
|
+
- ext/oj/err.c
|
34
44
|
- ext/oj/fast.c
|
35
|
-
- ext/oj/
|
45
|
+
- ext/oj/hash.c
|
46
|
+
- ext/oj/hash_test.c
|
47
|
+
- ext/oj/object.c
|
48
|
+
- ext/oj/odd.c
|
36
49
|
- ext/oj/oj.c
|
50
|
+
- ext/oj/parse.c
|
51
|
+
- ext/oj/resolve.c
|
37
52
|
- ext/oj/saj.c
|
53
|
+
- ext/oj/scp.c
|
54
|
+
- ext/oj/strict.c
|
55
|
+
- ext/oj/val_stack.c
|
38
56
|
- test/a.rb
|
39
57
|
- test/bug.rb
|
40
58
|
- test/files.rb
|
41
59
|
- test/foo.rb
|
42
60
|
- test/mj.rb
|
43
61
|
- test/perf.rb
|
44
|
-
- test/
|
45
|
-
- test/perf2.rb
|
62
|
+
- test/perf_compat.rb
|
46
63
|
- test/perf_fast.rb
|
47
|
-
- test/
|
48
|
-
- test/perf_obj_old.rb
|
64
|
+
- test/perf_object.rb
|
49
65
|
- test/perf_saj.rb
|
66
|
+
- test/perf_scp.rb
|
50
67
|
- test/perf_simple.rb
|
51
68
|
- test/perf_strict.rb
|
52
69
|
- test/sample/change.rb
|
@@ -63,10 +80,14 @@ files:
|
|
63
80
|
- test/sample/text.rb
|
64
81
|
- test/sample.rb
|
65
82
|
- test/sample_json.rb
|
83
|
+
- test/test_compat.rb
|
66
84
|
- test/test_fast.rb
|
67
85
|
- test/test_mimic.rb
|
68
86
|
- test/test_mimic_after.rb
|
87
|
+
- test/test_object.rb
|
69
88
|
- test/test_saj.rb
|
89
|
+
- test/test_scp.rb
|
90
|
+
- test/test_strict.rb
|
70
91
|
- test/tests.rb
|
71
92
|
- LICENSE
|
72
93
|
- README.md
|
@@ -92,7 +113,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
113
|
version: '0'
|
93
114
|
requirements: []
|
94
115
|
rubyforge_project: oj
|
95
|
-
rubygems_version: 2.0.
|
116
|
+
rubygems_version: 2.0.2
|
96
117
|
signing_key:
|
97
118
|
specification_version: 4
|
98
119
|
summary: A fast JSON parser and serializer.
|
data/ext/oj/cache.c
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
/* cache.c
|
2
|
-
* Copyright (c) 2011, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*
|
5
|
-
* Redistribution and use in source and binary forms, with or without
|
6
|
-
* modification, are permitted provided that the following conditions are met:
|
7
|
-
*
|
8
|
-
* - Redistributions of source code must retain the above copyright notice, this
|
9
|
-
* list of conditions and the following disclaimer.
|
10
|
-
*
|
11
|
-
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
|
-
* this list of conditions and the following disclaimer in the documentation
|
13
|
-
* and/or other materials provided with the distribution.
|
14
|
-
*
|
15
|
-
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
|
-
* used to endorse or promote products derived from this software without
|
17
|
-
* specific prior written permission.
|
18
|
-
*
|
19
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
-
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
-
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
-
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
-
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
-
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
-
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
-
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
-
*/
|
30
|
-
|
31
|
-
#include <stdlib.h>
|
32
|
-
#include <errno.h>
|
33
|
-
#include <stdio.h>
|
34
|
-
#include <string.h>
|
35
|
-
#include <stdarg.h>
|
36
|
-
|
37
|
-
#include "cache.h"
|
38
|
-
|
39
|
-
struct _Cache {
|
40
|
-
char *key; // only set if the node has a value, and it is not an exact match
|
41
|
-
VALUE value;
|
42
|
-
struct _Cache *slots[16];
|
43
|
-
};
|
44
|
-
|
45
|
-
static void slot_print(Cache cache, unsigned int depth);
|
46
|
-
|
47
|
-
void
|
48
|
-
oj_cache_new(Cache *cache) {
|
49
|
-
if (0 == (*cache = ALLOC(struct _Cache))) {
|
50
|
-
rb_raise(rb_eNoMemError, "not enough memory\n");
|
51
|
-
}
|
52
|
-
(*cache)->key = 0;
|
53
|
-
(*cache)->value = Qundef;
|
54
|
-
memset((*cache)->slots, 0, sizeof((*cache)->slots));
|
55
|
-
}
|
56
|
-
|
57
|
-
VALUE
|
58
|
-
oj_cache_get(Cache cache, const char *key, VALUE **slot) {
|
59
|
-
unsigned char *k = (unsigned char*)key;
|
60
|
-
Cache *cp;
|
61
|
-
|
62
|
-
for (; '\0' != *k; k++) {
|
63
|
-
cp = cache->slots + (unsigned int)(*k >> 4); // upper 4 bits
|
64
|
-
if (0 == *cp) {
|
65
|
-
oj_cache_new(cp);
|
66
|
-
}
|
67
|
-
cache = *cp;
|
68
|
-
cp = cache->slots + (unsigned int)(*k & 0x0F); // lower 4 bits
|
69
|
-
if (0 == *cp) {
|
70
|
-
oj_cache_new(cp);
|
71
|
-
cache = *cp;
|
72
|
-
cache->key = ('\0' == *(k + 1)) ? 0 : strdup(key);
|
73
|
-
break;
|
74
|
-
} else {
|
75
|
-
cache = *cp;
|
76
|
-
if (Qundef != cache->value && 0 != cache->key) {
|
77
|
-
unsigned char *ck = (unsigned char*)(cache->key + (unsigned int)(k - (unsigned char*)key + 1));
|
78
|
-
|
79
|
-
if (0 == strcmp((char*)ck, (char*)(k + 1))) {
|
80
|
-
break;
|
81
|
-
} else {
|
82
|
-
Cache *cp2 = cp;
|
83
|
-
|
84
|
-
// if value was set along with the key then there are no slots filled yet
|
85
|
-
cp2 = (*cp2)->slots + (*ck >> 4);
|
86
|
-
oj_cache_new(cp2);
|
87
|
-
cp2 = (*cp2)->slots + (*ck & 0x0F);
|
88
|
-
oj_cache_new(cp2);
|
89
|
-
if ('\0' == *(ck + 1)) {
|
90
|
-
xfree(cache->key);
|
91
|
-
} else {
|
92
|
-
(*cp2)->key = cache->key;
|
93
|
-
}
|
94
|
-
(*cp2)->value = cache->value;
|
95
|
-
cache->key = 0;
|
96
|
-
cache->value = Qundef;
|
97
|
-
}
|
98
|
-
}
|
99
|
-
}
|
100
|
-
}
|
101
|
-
*slot = &cache->value;
|
102
|
-
|
103
|
-
return cache->value;
|
104
|
-
}
|
105
|
-
|
106
|
-
void
|
107
|
-
oj_cache_print(Cache cache) {
|
108
|
-
//printf("-------------------------------------------\n");
|
109
|
-
slot_print(cache, 0);
|
110
|
-
}
|
111
|
-
|
112
|
-
static void
|
113
|
-
slot_print(Cache c, unsigned int depth) {
|
114
|
-
char indent[256];
|
115
|
-
Cache *cp;
|
116
|
-
unsigned int i;
|
117
|
-
|
118
|
-
if (sizeof(indent) - 1 < depth) {
|
119
|
-
depth = ((int)sizeof(indent) - 1);
|
120
|
-
}
|
121
|
-
memset(indent, ' ', depth);
|
122
|
-
indent[depth] = '\0';
|
123
|
-
for (i = 0, cp = c->slots; i < 16; i++, cp++) {
|
124
|
-
if (0 == *cp) {
|
125
|
-
//printf("%s%02u:\n", indent, i);
|
126
|
-
} else {
|
127
|
-
if (0 == (*cp)->key && Qundef == (*cp)->value) {
|
128
|
-
printf("%s%02u:\n", indent, i);
|
129
|
-
} else {
|
130
|
-
const char *key = (0 == (*cp)->key) ? "*" : (*cp)->key;
|
131
|
-
const char *vs;
|
132
|
-
const char *clas;
|
133
|
-
|
134
|
-
if (Qundef == (*cp)->value) {
|
135
|
-
vs = "undefined";
|
136
|
-
clas = "";
|
137
|
-
} else {
|
138
|
-
VALUE rs = rb_funcall2((*cp)->value, rb_intern("to_s"), 0, 0);
|
139
|
-
|
140
|
-
vs = StringValuePtr(rs);
|
141
|
-
clas = rb_class2name(rb_obj_class((*cp)->value));
|
142
|
-
}
|
143
|
-
printf("%s%02u: %s = %s (%s)\n", indent, i, key, vs, clas);
|
144
|
-
}
|
145
|
-
slot_print(*cp, depth + 2);
|
146
|
-
}
|
147
|
-
}
|
148
|
-
}
|
data/ext/oj/load.c
DELETED
@@ -1,1089 +0,0 @@
|
|
1
|
-
/* load.c
|
2
|
-
* Copyright (c) 2012, Peter Ohler
|
3
|
-
* All rights reserved.
|
4
|
-
*
|
5
|
-
* Redistribution and use in source and binary forms, with or without
|
6
|
-
* modification, are permitted provided that the following conditions are met:
|
7
|
-
*
|
8
|
-
* - Redistributions of source code must retain the above copyright notice, this
|
9
|
-
* list of conditions and the following disclaimer.
|
10
|
-
*
|
11
|
-
* - Redistributions in binary form must reproduce the above copyright notice,
|
12
|
-
* this list of conditions and the following disclaimer in the documentation
|
13
|
-
* and/or other materials provided with the distribution.
|
14
|
-
*
|
15
|
-
* - Neither the name of Peter Ohler nor the names of its contributors may be
|
16
|
-
* used to endorse or promote products derived from this software without
|
17
|
-
* specific prior written permission.
|
18
|
-
*
|
19
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
-
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
-
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
-
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
-
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
-
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
-
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
-
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
-
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
-
*/
|
30
|
-
|
31
|
-
#if SAFE_CACHE
|
32
|
-
#include <pthread.h>
|
33
|
-
#endif
|
34
|
-
#if !IS_WINDOWS
|
35
|
-
#include <sys/resource.h> // for getrlimit() on linux
|
36
|
-
#endif
|
37
|
-
#include <stdlib.h>
|
38
|
-
#include <stdio.h>
|
39
|
-
#include <string.h>
|
40
|
-
#include <math.h>
|
41
|
-
|
42
|
-
// Workaround in case INFINITY is not defined in math.h or if the OS is CentOS
|
43
|
-
#define OJ_INFINITY (1.0/0.0)
|
44
|
-
|
45
|
-
#include "oj.h"
|
46
|
-
|
47
|
-
enum {
|
48
|
-
TIME_HINT = 0x0100,
|
49
|
-
};
|
50
|
-
|
51
|
-
typedef struct _CircArray {
|
52
|
-
VALUE obj_array[1024];
|
53
|
-
VALUE *objs;
|
54
|
-
unsigned long size; // allocated size or initial array size
|
55
|
-
unsigned long cnt;
|
56
|
-
} *CircArray;
|
57
|
-
|
58
|
-
typedef struct _ParseInfo {
|
59
|
-
char *str; /* buffer being read from */
|
60
|
-
char *s; /* current position in buffer */
|
61
|
-
CircArray circ_array;
|
62
|
-
Options options;
|
63
|
-
char *stack_min;
|
64
|
-
} *ParseInfo;
|
65
|
-
|
66
|
-
static CircArray circ_array_new(void);
|
67
|
-
static void circ_array_free(CircArray ca);
|
68
|
-
static void circ_array_set(CircArray ca, VALUE obj, unsigned long id);
|
69
|
-
static VALUE circ_array_get(CircArray ca, unsigned long id);
|
70
|
-
|
71
|
-
static VALUE classname2class(const char *name, ParseInfo pi);
|
72
|
-
static VALUE read_next(ParseInfo pi, int hint);
|
73
|
-
static VALUE read_obj(ParseInfo pi);
|
74
|
-
static VALUE read_array(ParseInfo pi, int hint);
|
75
|
-
static VALUE read_str(ParseInfo pi, int hint);
|
76
|
-
static VALUE read_num(ParseInfo pi);
|
77
|
-
static VALUE read_time(ParseInfo pi);
|
78
|
-
static VALUE read_true(ParseInfo pi);
|
79
|
-
static VALUE read_false(ParseInfo pi);
|
80
|
-
static VALUE read_nil(ParseInfo pi);
|
81
|
-
static void next_non_white(ParseInfo pi);
|
82
|
-
static char* read_quoted_value(ParseInfo pi);
|
83
|
-
static void skip_comment(ParseInfo pi);
|
84
|
-
|
85
|
-
|
86
|
-
/* This JSON parser is a single pass, destructive, callback parser. It is a
|
87
|
-
* single pass parse since it only make one pass over the characters in the
|
88
|
-
* JSON document string. It is destructive because it re-uses the content of
|
89
|
-
* the string for values in the callback and places \0 characters at various
|
90
|
-
* places to mark the end of tokens and strings. It is a callback parser like
|
91
|
-
* a SAX parser because it uses callback when document elements are
|
92
|
-
* encountered.
|
93
|
-
*
|
94
|
-
* Parsing is very tolerant. Lack of headers and even mispelled element
|
95
|
-
* endings are passed over without raising an error. A best attempt is made in
|
96
|
-
* all cases to parse the string.
|
97
|
-
*/
|
98
|
-
|
99
|
-
inline static void
|
100
|
-
next_non_white(ParseInfo pi) {
|
101
|
-
for (; 1; pi->s++) {
|
102
|
-
switch(*pi->s) {
|
103
|
-
case ' ':
|
104
|
-
case '\t':
|
105
|
-
case '\f':
|
106
|
-
case '\n':
|
107
|
-
case '\r':
|
108
|
-
break;
|
109
|
-
case '/':
|
110
|
-
skip_comment(pi);
|
111
|
-
break;
|
112
|
-
default:
|
113
|
-
return;
|
114
|
-
}
|
115
|
-
}
|
116
|
-
}
|
117
|
-
|
118
|
-
inline static void
|
119
|
-
next_white(ParseInfo pi) {
|
120
|
-
for (; 1; pi->s++) {
|
121
|
-
switch(*pi->s) {
|
122
|
-
case ' ':
|
123
|
-
case '\t':
|
124
|
-
case '\f':
|
125
|
-
case '\n':
|
126
|
-
case '\r':
|
127
|
-
case '\0':
|
128
|
-
return;
|
129
|
-
default:
|
130
|
-
break;
|
131
|
-
}
|
132
|
-
}
|
133
|
-
}
|
134
|
-
|
135
|
-
inline static VALUE
|
136
|
-
resolve_classname(VALUE mod, const char *class_name, int auto_define) {
|
137
|
-
VALUE clas;
|
138
|
-
ID ci = rb_intern(class_name);
|
139
|
-
|
140
|
-
if (rb_const_defined_at(mod, ci)) {
|
141
|
-
clas = rb_const_get_at(mod, ci);
|
142
|
-
} else if (auto_define) {
|
143
|
-
clas = rb_define_class_under(mod, class_name, oj_bag_class);
|
144
|
-
} else {
|
145
|
-
clas = Qundef;
|
146
|
-
}
|
147
|
-
return clas;
|
148
|
-
}
|
149
|
-
|
150
|
-
inline static VALUE
|
151
|
-
classname2obj(const char *name, ParseInfo pi) {
|
152
|
-
VALUE clas = classname2class(name, pi);
|
153
|
-
|
154
|
-
if (Qundef == clas) {
|
155
|
-
return Qnil;
|
156
|
-
} else {
|
157
|
-
return rb_obj_alloc(clas);
|
158
|
-
}
|
159
|
-
}
|
160
|
-
|
161
|
-
static VALUE
|
162
|
-
resolve_classpath(const char *name, ParseInfo pi) {
|
163
|
-
char class_name[1024];
|
164
|
-
VALUE clas;
|
165
|
-
int auto_define = (Yes == pi->options->auto_define);
|
166
|
-
char *end = class_name + sizeof(class_name) - 1;
|
167
|
-
char *s;
|
168
|
-
const char *n = name;
|
169
|
-
|
170
|
-
clas = rb_cObject;
|
171
|
-
for (s = class_name; '\0' != *n; n++) {
|
172
|
-
if (':' == *n) {
|
173
|
-
*s = '\0';
|
174
|
-
n++;
|
175
|
-
if (':' != *n) {
|
176
|
-
raise_error("Invalid classname, expected another ':'", pi->str, pi->s);
|
177
|
-
}
|
178
|
-
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
179
|
-
char buf[1024];
|
180
|
-
|
181
|
-
snprintf(buf, sizeof(buf) - 1, "Class %s not defined", class_name);
|
182
|
-
raise_error(buf, pi->str, pi->s);
|
183
|
-
}
|
184
|
-
s = class_name;
|
185
|
-
} else if (end <= s) {
|
186
|
-
raise_error("Invalid classname, limit is 1024 characters", pi->str, pi->s);
|
187
|
-
} else {
|
188
|
-
*s++ = *n;
|
189
|
-
}
|
190
|
-
}
|
191
|
-
*s = '\0';
|
192
|
-
return resolve_classname(clas, class_name, auto_define);
|
193
|
-
}
|
194
|
-
|
195
|
-
static VALUE
|
196
|
-
classname2class(const char *name, ParseInfo pi) {
|
197
|
-
VALUE clas;
|
198
|
-
VALUE *slot;
|
199
|
-
|
200
|
-
if (No == pi->options->class_cache) {
|
201
|
-
return resolve_classpath(name, pi);
|
202
|
-
}
|
203
|
-
#if SAFE_CACHE
|
204
|
-
pthread_mutex_lock(&oj_cache_mutex);
|
205
|
-
#endif
|
206
|
-
if (Qundef == (clas = oj_cache_get(oj_class_cache, name, &slot))) {
|
207
|
-
if (Qundef != (clas = resolve_classpath(name, pi))) {
|
208
|
-
*slot = clas;
|
209
|
-
}
|
210
|
-
}
|
211
|
-
#if SAFE_CACHE
|
212
|
-
pthread_mutex_unlock(&oj_cache_mutex);
|
213
|
-
#endif
|
214
|
-
return clas;
|
215
|
-
}
|
216
|
-
|
217
|
-
#if HAS_RSTRUCT
|
218
|
-
inline static VALUE
|
219
|
-
structname2obj(const char *name) {
|
220
|
-
VALUE ost;
|
221
|
-
|
222
|
-
ost = rb_const_get(oj_struct_class, rb_intern(name));
|
223
|
-
// use encoding as the indicator for Ruby 1.8.7 or 1.9.x
|
224
|
-
#if HAS_ENCODING_SUPPORT
|
225
|
-
return rb_struct_alloc_noinit(ost);
|
226
|
-
#else
|
227
|
-
return rb_struct_new(ost);
|
228
|
-
#endif
|
229
|
-
}
|
230
|
-
#endif
|
231
|
-
|
232
|
-
inline static unsigned long
|
233
|
-
read_ulong(const char *s, ParseInfo pi) {
|
234
|
-
unsigned long n = 0;
|
235
|
-
|
236
|
-
for (; '\0' != *s; s++) {
|
237
|
-
if ('0' <= *s && *s <= '9') {
|
238
|
-
n = n * 10 + (*s - '0');
|
239
|
-
} else {
|
240
|
-
raise_error("Not a valid ID number", pi->str, pi->s);
|
241
|
-
}
|
242
|
-
}
|
243
|
-
return n;
|
244
|
-
}
|
245
|
-
|
246
|
-
static CircArray
|
247
|
-
circ_array_new() {
|
248
|
-
CircArray ca;
|
249
|
-
|
250
|
-
if (0 == (ca = ALLOC(struct _CircArray))) {
|
251
|
-
rb_raise(rb_eNoMemError, "not enough memory\n");
|
252
|
-
}
|
253
|
-
ca->objs = ca->obj_array;
|
254
|
-
ca->size = sizeof(ca->obj_array) / sizeof(VALUE);
|
255
|
-
ca->cnt = 0;
|
256
|
-
|
257
|
-
return ca;
|
258
|
-
}
|
259
|
-
|
260
|
-
static void
|
261
|
-
circ_array_free(CircArray ca) {
|
262
|
-
if (ca->objs != ca->obj_array) {
|
263
|
-
xfree(ca->objs);
|
264
|
-
}
|
265
|
-
xfree(ca);
|
266
|
-
}
|
267
|
-
|
268
|
-
static void
|
269
|
-
circ_array_set(CircArray ca, VALUE obj, unsigned long id) {
|
270
|
-
if (0 < id && 0 != ca) {
|
271
|
-
unsigned long i;
|
272
|
-
|
273
|
-
if (ca->size < id) {
|
274
|
-
unsigned long cnt = id + 512;
|
275
|
-
|
276
|
-
if (ca->objs == ca->obj_array) {
|
277
|
-
if (0 == (ca->objs = ALLOC_N(VALUE, cnt))) {
|
278
|
-
rb_raise(rb_eNoMemError, "not enough memory\n");
|
279
|
-
}
|
280
|
-
memcpy(ca->objs, ca->obj_array, sizeof(VALUE) * ca->cnt);
|
281
|
-
} else {
|
282
|
-
REALLOC_N(ca->objs, VALUE, cnt);
|
283
|
-
}
|
284
|
-
ca->size = cnt;
|
285
|
-
}
|
286
|
-
id--;
|
287
|
-
for (i = ca->cnt; i < id; i++) {
|
288
|
-
ca->objs[i] = Qnil;
|
289
|
-
}
|
290
|
-
ca->objs[id] = obj;
|
291
|
-
if (ca->cnt <= id) {
|
292
|
-
ca->cnt = id + 1;
|
293
|
-
}
|
294
|
-
}
|
295
|
-
}
|
296
|
-
|
297
|
-
static VALUE
|
298
|
-
circ_array_get(CircArray ca, unsigned long id) {
|
299
|
-
VALUE obj = Qnil;
|
300
|
-
|
301
|
-
if (id <= ca->cnt && 0 != ca) {
|
302
|
-
obj = ca->objs[id - 1];
|
303
|
-
}
|
304
|
-
return obj;
|
305
|
-
}
|
306
|
-
|
307
|
-
static void
|
308
|
-
skip_comment(ParseInfo pi) {
|
309
|
-
pi->s++; // skip first /
|
310
|
-
if ('*' == *pi->s) {
|
311
|
-
pi->s++;
|
312
|
-
for (; '\0' != *pi->s; pi->s++) {
|
313
|
-
if ('*' == *pi->s && '/' == *(pi->s + 1)) {
|
314
|
-
pi->s++;
|
315
|
-
return;
|
316
|
-
} else if ('\0' == *pi->s) {
|
317
|
-
raise_error("comment not terminated", pi->str, pi->s);
|
318
|
-
}
|
319
|
-
}
|
320
|
-
} else if ('/' == *pi->s) {
|
321
|
-
for (; 1; pi->s++) {
|
322
|
-
switch (*pi->s) {
|
323
|
-
case '\n':
|
324
|
-
case '\r':
|
325
|
-
case '\f':
|
326
|
-
case '\0':
|
327
|
-
return;
|
328
|
-
default:
|
329
|
-
break;
|
330
|
-
}
|
331
|
-
}
|
332
|
-
} else {
|
333
|
-
raise_error("invalid comment", pi->str, pi->s);
|
334
|
-
}
|
335
|
-
}
|
336
|
-
|
337
|
-
static VALUE
|
338
|
-
read_next(ParseInfo pi, int hint) {
|
339
|
-
VALUE obj;
|
340
|
-
|
341
|
-
if ((char*)&obj < pi->stack_min) {
|
342
|
-
rb_raise(rb_eSysStackError, "JSON is too deeply nested");
|
343
|
-
}
|
344
|
-
next_non_white(pi); // skip white space
|
345
|
-
switch (*pi->s) {
|
346
|
-
case '{':
|
347
|
-
obj = read_obj(pi);
|
348
|
-
break;
|
349
|
-
case '[':
|
350
|
-
obj = read_array(pi, hint);
|
351
|
-
break;
|
352
|
-
case '"':
|
353
|
-
obj = read_str(pi, hint);
|
354
|
-
break;
|
355
|
-
case '+':
|
356
|
-
case '-':
|
357
|
-
case '0':
|
358
|
-
case '1':
|
359
|
-
case '2':
|
360
|
-
case '3':
|
361
|
-
case '4':
|
362
|
-
case '5':
|
363
|
-
case '6':
|
364
|
-
case '7':
|
365
|
-
case '8':
|
366
|
-
case '9':
|
367
|
-
if (TIME_HINT == hint) {
|
368
|
-
obj = read_time(pi);
|
369
|
-
} else {
|
370
|
-
obj = read_num(pi);
|
371
|
-
}
|
372
|
-
break;
|
373
|
-
case 'I':
|
374
|
-
obj = read_num(pi);
|
375
|
-
break;
|
376
|
-
case 't':
|
377
|
-
obj = read_true(pi);
|
378
|
-
break;
|
379
|
-
case 'f':
|
380
|
-
obj = read_false(pi);
|
381
|
-
break;
|
382
|
-
case 'n':
|
383
|
-
obj = read_nil(pi);
|
384
|
-
break;
|
385
|
-
case '\0':
|
386
|
-
obj = Qundef;
|
387
|
-
break;
|
388
|
-
default:
|
389
|
-
obj = Qundef;
|
390
|
-
break;
|
391
|
-
}
|
392
|
-
return obj;
|
393
|
-
}
|
394
|
-
|
395
|
-
static VALUE
|
396
|
-
read_obj(ParseInfo pi) {
|
397
|
-
VALUE obj = Qundef;
|
398
|
-
VALUE key = Qundef;
|
399
|
-
VALUE val = Qundef;
|
400
|
-
const char *ks;
|
401
|
-
int obj_type = T_NONE;
|
402
|
-
const char *json_class_name = 0;
|
403
|
-
Mode mode = pi->options->mode;
|
404
|
-
Odd odd = 0;
|
405
|
-
VALUE odd_args[MAX_ODD_ARGS];
|
406
|
-
VALUE *vp;
|
407
|
-
|
408
|
-
pi->s++;
|
409
|
-
next_non_white(pi);
|
410
|
-
if ('}' == *pi->s) {
|
411
|
-
pi->s++;
|
412
|
-
return rb_hash_new();
|
413
|
-
}
|
414
|
-
while (1) {
|
415
|
-
next_non_white(pi);
|
416
|
-
ks = 0;
|
417
|
-
key = Qundef;
|
418
|
-
val = Qundef;
|
419
|
-
if ('"' != *pi->s || Qundef == (key = read_str(pi, 0))) {
|
420
|
-
raise_error("unexpected character", pi->str, pi->s);
|
421
|
-
}
|
422
|
-
next_non_white(pi);
|
423
|
-
if (':' == *pi->s) {
|
424
|
-
pi->s++;
|
425
|
-
} else {
|
426
|
-
raise_error("invalid format, expected :", pi->str, pi->s);
|
427
|
-
}
|
428
|
-
if (T_STRING == rb_type(key)) {
|
429
|
-
ks = StringValuePtr(key);
|
430
|
-
} else {
|
431
|
-
ks = 0;
|
432
|
-
}
|
433
|
-
if (0 != ks && Qundef == obj && ObjectMode == mode) {
|
434
|
-
if ('^' == *ks && '\0' == ks[2]) { // special directions
|
435
|
-
switch (ks[1]) {
|
436
|
-
case 't': // Time
|
437
|
-
obj = read_next(pi, TIME_HINT); // raises if can not convert to Time
|
438
|
-
key = Qundef;
|
439
|
-
break;
|
440
|
-
case 'c': // Class
|
441
|
-
obj = read_next(pi, T_CLASS);
|
442
|
-
key = Qundef;
|
443
|
-
break;
|
444
|
-
case 's': // String
|
445
|
-
obj = read_next(pi, T_STRING);
|
446
|
-
key = Qundef;
|
447
|
-
break;
|
448
|
-
case 'm': // Symbol
|
449
|
-
obj = read_next(pi, T_SYMBOL);
|
450
|
-
key = Qundef;
|
451
|
-
break;
|
452
|
-
case 'o': // Object
|
453
|
-
obj = read_next(pi, T_OBJECT);
|
454
|
-
obj_type = T_OBJECT;
|
455
|
-
key = Qundef;
|
456
|
-
break;
|
457
|
-
case 'O': // Odd class
|
458
|
-
if (0 == (odd = oj_get_odd(read_next(pi, T_CLASS)))) {
|
459
|
-
raise_error("Not a valid build in class.", pi->str, pi->s);
|
460
|
-
}
|
461
|
-
obj = Qundef;
|
462
|
-
key = Qundef;
|
463
|
-
for (vp = odd_args + MAX_ODD_ARGS - 1; odd_args <=vp; vp--) {
|
464
|
-
*vp = Qnil;
|
465
|
-
}
|
466
|
-
break;
|
467
|
-
case 'u': // Struct
|
468
|
-
obj = read_next(pi, T_STRUCT);
|
469
|
-
obj_type = T_STRUCT;
|
470
|
-
key = Qundef;
|
471
|
-
break;
|
472
|
-
default:
|
473
|
-
// handle later
|
474
|
-
break;
|
475
|
-
}
|
476
|
-
}
|
477
|
-
}
|
478
|
-
if (Qundef != key) {
|
479
|
-
if (Qundef == val && Qundef == (val = read_next(pi, 0))) {
|
480
|
-
raise_error("unexpected character", pi->str, pi->s);
|
481
|
-
}
|
482
|
-
if (Qundef == obj && 0 == odd) {
|
483
|
-
obj = rb_hash_new();
|
484
|
-
obj_type = T_HASH;
|
485
|
-
}
|
486
|
-
if (ObjectMode == mode && 0 != ks && '^' == *ks) {
|
487
|
-
int val_type = rb_type(val);
|
488
|
-
|
489
|
-
if ('i' == ks[1] && '\0' == ks[2] && T_FIXNUM == val_type) {
|
490
|
-
circ_array_set(pi->circ_array, obj, NUM2ULONG(val));
|
491
|
-
key = Qundef;
|
492
|
-
} else if ('#' == ks[1] &&
|
493
|
-
(T_NONE == obj_type || T_HASH == obj_type) &&
|
494
|
-
T_ARRAY == val_type && 2 == RARRAY_LEN(val)) { // Hash entry
|
495
|
-
VALUE *np = RARRAY_PTR(val);
|
496
|
-
|
497
|
-
key = *np;
|
498
|
-
val = *(np + 1);
|
499
|
-
}
|
500
|
-
}
|
501
|
-
if (Qundef != key) {
|
502
|
-
if (0 != odd) {
|
503
|
-
ID *idp;
|
504
|
-
|
505
|
-
for (idp = odd->attrs, vp = odd_args; 0 != *idp; idp++, vp++) {
|
506
|
-
if (0 == strcmp(rb_id2name(*idp), ks)) {
|
507
|
-
*vp = val;
|
508
|
-
break;
|
509
|
-
}
|
510
|
-
}
|
511
|
-
if (odd_args + MAX_ODD_ARGS <= vp) {
|
512
|
-
raise_error("invalid attribute", pi->str, pi->s);
|
513
|
-
}
|
514
|
-
} else if (T_OBJECT == obj_type) {
|
515
|
-
VALUE *slot;
|
516
|
-
ID var_id;
|
517
|
-
|
518
|
-
#if SAFE_CACHE
|
519
|
-
pthread_mutex_lock(&oj_cache_mutex);
|
520
|
-
#endif
|
521
|
-
if (Qundef == (var_id = oj_cache_get(oj_attr_cache, ks, &slot))) {
|
522
|
-
char attr[1024];
|
523
|
-
|
524
|
-
if ('~' == *ks) {
|
525
|
-
strncpy(attr, ks + 1, sizeof(attr) - 1);
|
526
|
-
} else {
|
527
|
-
*attr = '@';
|
528
|
-
strncpy(attr + 1, ks, sizeof(attr) - 2);
|
529
|
-
}
|
530
|
-
attr[sizeof(attr) - 1] = '\0';
|
531
|
-
var_id = rb_intern(attr);
|
532
|
-
*slot = var_id;
|
533
|
-
}
|
534
|
-
#if SAFE_CACHE
|
535
|
-
pthread_mutex_unlock(&oj_cache_mutex);
|
536
|
-
#endif
|
537
|
-
#if HAS_EXCEPTION_MAGIC
|
538
|
-
if ('~' == *ks && Qtrue == rb_obj_is_kind_of(obj, rb_eException)) {
|
539
|
-
if (0 == strcmp("~mesg", ks)) {
|
540
|
-
VALUE args[1];
|
541
|
-
|
542
|
-
args[0] = val;
|
543
|
-
obj = rb_class_new_instance(1, args, rb_class_of(obj));
|
544
|
-
} else if (0 == strcmp("~bt", ks)) {
|
545
|
-
rb_funcall(obj, rb_intern("set_backtrace"), 1, val);
|
546
|
-
}
|
547
|
-
} else {
|
548
|
-
rb_ivar_set(obj, var_id, val);
|
549
|
-
}
|
550
|
-
#else
|
551
|
-
rb_ivar_set(obj, var_id, val);
|
552
|
-
#endif
|
553
|
-
} else if (T_HASH == obj_type) {
|
554
|
-
if (Yes == pi->options->sym_key && T_STRING == rb_type(key)) {
|
555
|
-
rb_hash_aset(obj, rb_str_intern(key), val);
|
556
|
-
} else {
|
557
|
-
rb_hash_aset(obj, key, val);
|
558
|
-
}
|
559
|
-
if ((CompatMode == mode || ObjectMode == mode) &&
|
560
|
-
0 == json_class_name && 0 != ks &&
|
561
|
-
0 != pi->options->create_id && *pi->options->create_id == *ks && 0 == strcmp(pi->options->create_id, ks) &&
|
562
|
-
T_STRING == rb_type(val)) {
|
563
|
-
json_class_name = StringValuePtr(val);
|
564
|
-
}
|
565
|
-
} else {
|
566
|
-
raise_error("invalid Object format, too many Hash entries.", pi->str, pi->s);
|
567
|
-
}
|
568
|
-
}
|
569
|
-
}
|
570
|
-
next_non_white(pi);
|
571
|
-
if ('}' == *pi->s) {
|
572
|
-
pi->s++;
|
573
|
-
break;
|
574
|
-
} else if (',' == *pi->s) {
|
575
|
-
pi->s++;
|
576
|
-
} else {
|
577
|
-
//printf("*** '%s'\n", pi->s);
|
578
|
-
raise_error("invalid format, expected , or } while in an object", pi->str, pi->s);
|
579
|
-
}
|
580
|
-
}
|
581
|
-
if (0 != odd) {
|
582
|
-
obj = rb_funcall2(odd->create_obj, odd->create_op, odd->attr_cnt, odd_args);
|
583
|
-
} else if (0 != json_class_name) {
|
584
|
-
VALUE clas = classname2class(json_class_name, pi);
|
585
|
-
VALUE args[1];
|
586
|
-
|
587
|
-
*args = obj;
|
588
|
-
obj = rb_funcall2(clas, oj_json_create_id, 1, args);
|
589
|
-
}
|
590
|
-
return obj;
|
591
|
-
}
|
592
|
-
|
593
|
-
static VALUE
|
594
|
-
read_array(ParseInfo pi, int hint) {
|
595
|
-
VALUE a = Qundef;
|
596
|
-
VALUE e;
|
597
|
-
int type = T_NONE;
|
598
|
-
int cnt = 0;
|
599
|
-
int a_str;
|
600
|
-
#if HAS_RSTRUCT
|
601
|
-
long slen = 0;
|
602
|
-
#endif
|
603
|
-
|
604
|
-
pi->s++;
|
605
|
-
next_non_white(pi);
|
606
|
-
if (']' == *pi->s) {
|
607
|
-
pi->s++;
|
608
|
-
return rb_ary_new();
|
609
|
-
}
|
610
|
-
while (1) {
|
611
|
-
next_non_white(pi);
|
612
|
-
a_str = ('"' == *pi->s);
|
613
|
-
if (Qundef == (e = read_next(pi, 0))) {
|
614
|
-
raise_error("unexpected character", pi->str, pi->s);
|
615
|
-
}
|
616
|
-
#if HAS_RSTRUCT
|
617
|
-
if (Qundef == a && T_STRUCT == hint && T_STRING == rb_type(e)) {
|
618
|
-
a = structname2obj(StringValuePtr(e));
|
619
|
-
type = T_STRUCT;
|
620
|
-
slen = RSTRUCT_LEN(a);
|
621
|
-
e = Qundef;
|
622
|
-
}
|
623
|
-
#endif
|
624
|
-
if (Qundef == a) {
|
625
|
-
a = rb_ary_new();
|
626
|
-
type = T_ARRAY;
|
627
|
-
}
|
628
|
-
if (a_str && T_FIXNUM == rb_type(e)) {
|
629
|
-
circ_array_set(pi->circ_array, a, NUM2ULONG(e));
|
630
|
-
e = Qundef;
|
631
|
-
}
|
632
|
-
if (Qundef != e) {
|
633
|
-
if (T_STRUCT == type) {
|
634
|
-
#if HAS_RSTRUCT
|
635
|
-
if (slen <= cnt) {
|
636
|
-
raise_error("Too many elements for Struct", pi->str, pi->s);
|
637
|
-
}
|
638
|
-
RSTRUCT_PTR(a)[cnt] = e;
|
639
|
-
#else
|
640
|
-
raise_error("Ruby structs not supported with this version of Ruby", pi->str, pi->s);
|
641
|
-
#endif
|
642
|
-
} else {
|
643
|
-
rb_ary_push(a, e);
|
644
|
-
}
|
645
|
-
cnt++;
|
646
|
-
}
|
647
|
-
next_non_white(pi);
|
648
|
-
if (',' == *pi->s) {
|
649
|
-
pi->s++;
|
650
|
-
} else if (']' == *pi->s) {
|
651
|
-
pi->s++;
|
652
|
-
break;
|
653
|
-
} else {
|
654
|
-
raise_error("invalid format, expected , or ] while in an array", pi->str, pi->s);
|
655
|
-
}
|
656
|
-
}
|
657
|
-
return a;
|
658
|
-
}
|
659
|
-
|
660
|
-
static VALUE
|
661
|
-
read_str(ParseInfo pi, int hint) {
|
662
|
-
char *text;
|
663
|
-
VALUE obj;
|
664
|
-
int escaped;
|
665
|
-
|
666
|
-
escaped = ('\\' == pi->s[1]);
|
667
|
-
text = read_quoted_value(pi);
|
668
|
-
if (ObjectMode != pi->options->mode) {
|
669
|
-
hint = T_STRING;
|
670
|
-
}
|
671
|
-
switch (hint) {
|
672
|
-
case T_CLASS:
|
673
|
-
obj = classname2class(text, pi);
|
674
|
-
break;
|
675
|
-
case T_OBJECT:
|
676
|
-
obj = classname2obj(text, pi);
|
677
|
-
break;
|
678
|
-
case T_STRING:
|
679
|
-
obj = rb_str_new2(text);
|
680
|
-
#if HAS_ENCODING_SUPPORT
|
681
|
-
rb_enc_associate(obj, oj_utf8_encoding);
|
682
|
-
#endif
|
683
|
-
break;
|
684
|
-
case T_SYMBOL:
|
685
|
-
#if HAS_ENCODING_SUPPORT
|
686
|
-
obj = rb_str_new2(text);
|
687
|
-
rb_enc_associate(obj, oj_utf8_encoding);
|
688
|
-
obj = rb_funcall(obj, oj_to_sym_id, 0);
|
689
|
-
#else
|
690
|
-
obj = ID2SYM(rb_intern(text));
|
691
|
-
#endif
|
692
|
-
break;
|
693
|
-
case 0:
|
694
|
-
default:
|
695
|
-
obj = Qundef;
|
696
|
-
if (':' == *text && !escaped) { // Symbol
|
697
|
-
#if HAS_ENCODING_SUPPORT
|
698
|
-
obj = rb_str_new2(text + 1);
|
699
|
-
rb_enc_associate(obj, oj_utf8_encoding);
|
700
|
-
obj = rb_funcall(obj, oj_to_sym_id, 0);
|
701
|
-
#else
|
702
|
-
obj = ID2SYM(rb_intern(text + 1));
|
703
|
-
#endif
|
704
|
-
} else if (ObjectMode == pi->options->mode && '^' == *text && '\0' != text[2]) {
|
705
|
-
char c1 = text[1];
|
706
|
-
|
707
|
-
if ('r' == c1 && 0 != pi->circ_array) {
|
708
|
-
obj = circ_array_get(pi->circ_array, read_ulong(text + 2, pi));
|
709
|
-
} else if ('i' == c1) {
|
710
|
-
obj = ULONG2NUM(read_ulong(text + 2, pi));
|
711
|
-
}
|
712
|
-
}
|
713
|
-
if (Qundef == obj) {
|
714
|
-
obj = rb_str_new2(text);
|
715
|
-
#if HAS_ENCODING_SUPPORT
|
716
|
-
rb_enc_associate(obj, oj_utf8_encoding);
|
717
|
-
#endif
|
718
|
-
}
|
719
|
-
break;
|
720
|
-
}
|
721
|
-
return obj;
|
722
|
-
}
|
723
|
-
|
724
|
-
#ifdef RUBINIUS_RUBY
|
725
|
-
#define NUM_MAX 0x07FFFFFF
|
726
|
-
#else
|
727
|
-
#define NUM_MAX (FIXNUM_MAX >> 8)
|
728
|
-
#endif
|
729
|
-
|
730
|
-
static VALUE
|
731
|
-
read_num(ParseInfo pi) {
|
732
|
-
char *start = pi->s;
|
733
|
-
int64_t n = 0;
|
734
|
-
long a = 0;
|
735
|
-
long div = 1;
|
736
|
-
long e = 0;
|
737
|
-
int neg = 0;
|
738
|
-
int eneg = 0;
|
739
|
-
int big = 0;
|
740
|
-
|
741
|
-
if ('-' == *pi->s) {
|
742
|
-
pi->s++;
|
743
|
-
neg = 1;
|
744
|
-
} else if ('+' == *pi->s) {
|
745
|
-
pi->s++;
|
746
|
-
}
|
747
|
-
if ('I' == *pi->s) {
|
748
|
-
if (0 != strncmp("Infinity", pi->s, 8)) {
|
749
|
-
raise_error("number or other value", pi->str, pi->s);
|
750
|
-
}
|
751
|
-
pi->s += 8;
|
752
|
-
if (Yes == pi->options->bigdec_load) {
|
753
|
-
char c = *pi->s;
|
754
|
-
VALUE num;
|
755
|
-
|
756
|
-
*pi->s = '\0';
|
757
|
-
num = rb_funcall(oj_bigdecimal_class, oj_new_id, 1, rb_str_new2(start));
|
758
|
-
*pi->s = c;
|
759
|
-
|
760
|
-
return num;
|
761
|
-
} else {
|
762
|
-
if (neg) {
|
763
|
-
return rb_float_new(-OJ_INFINITY);
|
764
|
-
} else {
|
765
|
-
return rb_float_new(OJ_INFINITY);
|
766
|
-
}
|
767
|
-
}
|
768
|
-
}
|
769
|
-
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
770
|
-
if (big) {
|
771
|
-
big++;
|
772
|
-
} else {
|
773
|
-
n = n * 10 + (*pi->s - '0');
|
774
|
-
if (NUM_MAX <= n) {
|
775
|
-
big = 1;
|
776
|
-
}
|
777
|
-
}
|
778
|
-
}
|
779
|
-
if ('.' == *pi->s) {
|
780
|
-
pi->s++;
|
781
|
-
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
782
|
-
a = a * 10 + (*pi->s - '0');
|
783
|
-
div *= 10;
|
784
|
-
if (NUM_MAX <= div) {
|
785
|
-
big = 1;
|
786
|
-
}
|
787
|
-
}
|
788
|
-
}
|
789
|
-
if ('e' == *pi->s || 'E' == *pi->s) {
|
790
|
-
pi->s++;
|
791
|
-
if ('-' == *pi->s) {
|
792
|
-
pi->s++;
|
793
|
-
eneg = 1;
|
794
|
-
} else if ('+' == *pi->s) {
|
795
|
-
pi->s++;
|
796
|
-
}
|
797
|
-
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
798
|
-
e = e * 10 + (*pi->s - '0');
|
799
|
-
if (NUM_MAX <= e) {
|
800
|
-
big = 1;
|
801
|
-
}
|
802
|
-
}
|
803
|
-
}
|
804
|
-
if (0 == e && 0 == a && 1 == div) {
|
805
|
-
if (big) {
|
806
|
-
char c = *pi->s;
|
807
|
-
VALUE num;
|
808
|
-
|
809
|
-
*pi->s = '\0';
|
810
|
-
num = rb_cstr_to_inum(start, 10, 0);
|
811
|
-
*pi->s = c;
|
812
|
-
|
813
|
-
return num;
|
814
|
-
} else {
|
815
|
-
if (neg) {
|
816
|
-
n = -n;
|
817
|
-
}
|
818
|
-
return LONG2NUM(n);
|
819
|
-
}
|
820
|
-
} else { // decimal
|
821
|
-
if (big || Yes == pi->options->bigdec_load) {
|
822
|
-
char c = *pi->s;
|
823
|
-
VALUE num;
|
824
|
-
|
825
|
-
*pi->s = '\0';
|
826
|
-
num = rb_funcall(oj_bigdecimal_class, oj_new_id, 1, rb_str_new2(start));
|
827
|
-
*pi->s = c;
|
828
|
-
|
829
|
-
return num;
|
830
|
-
} else {
|
831
|
-
double d = (double)n + (double)a / (double)div;
|
832
|
-
|
833
|
-
if (neg) {
|
834
|
-
d = -d;
|
835
|
-
}
|
836
|
-
if (1 < big) {
|
837
|
-
e += big - 1;
|
838
|
-
}
|
839
|
-
if (0 != e) {
|
840
|
-
if (eneg) {
|
841
|
-
e = -e;
|
842
|
-
}
|
843
|
-
d *= pow(10.0, e);
|
844
|
-
}
|
845
|
-
return rb_float_new(d);
|
846
|
-
}
|
847
|
-
}
|
848
|
-
}
|
849
|
-
|
850
|
-
static VALUE
|
851
|
-
read_time(ParseInfo pi) {
|
852
|
-
time_t v = 0;
|
853
|
-
long v2 = 0;
|
854
|
-
int neg = 0;
|
855
|
-
|
856
|
-
if ('-' == *pi->s) {
|
857
|
-
pi->s++;
|
858
|
-
neg = 1;
|
859
|
-
}
|
860
|
-
for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
|
861
|
-
v = v * 10 + (*pi->s - '0');
|
862
|
-
}
|
863
|
-
if ('.' == *pi->s) {
|
864
|
-
int cnt;
|
865
|
-
|
866
|
-
pi->s++;
|
867
|
-
for (cnt = 9; 0 < cnt && '0' <= *pi->s && *pi->s <= '9'; pi->s++, cnt--) {
|
868
|
-
v2 = v2 * 10 + (*pi->s - '0');
|
869
|
-
}
|
870
|
-
for (; 0 < cnt; cnt--) {
|
871
|
-
v2 *= 10;
|
872
|
-
}
|
873
|
-
}
|
874
|
-
if (neg) {
|
875
|
-
v = -v;
|
876
|
-
if (0 < v2) {
|
877
|
-
v--;
|
878
|
-
v2 = 1000000000 - v2;
|
879
|
-
}
|
880
|
-
}
|
881
|
-
#if HAS_NANO_TIME
|
882
|
-
return rb_time_nano_new(v, v2);
|
883
|
-
#else
|
884
|
-
return rb_time_new(v, v2 / 1000);
|
885
|
-
#endif
|
886
|
-
}
|
887
|
-
|
888
|
-
static VALUE
|
889
|
-
read_true(ParseInfo pi) {
|
890
|
-
pi->s++;
|
891
|
-
if ('r' != *pi->s || 'u' != *(pi->s + 1) || 'e' != *(pi->s + 2)) {
|
892
|
-
raise_error("invalid format, expected 'true'", pi->str, pi->s);
|
893
|
-
}
|
894
|
-
pi->s += 3;
|
895
|
-
|
896
|
-
return Qtrue;
|
897
|
-
}
|
898
|
-
|
899
|
-
static VALUE
|
900
|
-
read_false(ParseInfo pi) {
|
901
|
-
pi->s++;
|
902
|
-
if ('a' != *pi->s || 'l' != *(pi->s + 1) || 's' != *(pi->s + 2) || 'e' != *(pi->s + 3)) {
|
903
|
-
raise_error("invalid format, expected 'false'", pi->str, pi->s);
|
904
|
-
}
|
905
|
-
pi->s += 4;
|
906
|
-
|
907
|
-
return Qfalse;
|
908
|
-
}
|
909
|
-
|
910
|
-
static VALUE
|
911
|
-
read_nil(ParseInfo pi) {
|
912
|
-
pi->s++;
|
913
|
-
if ('u' != *pi->s || 'l' != *(pi->s + 1) || 'l' != *(pi->s + 2)) {
|
914
|
-
raise_error("invalid format, expected 'nil'", pi->str, pi->s);
|
915
|
-
}
|
916
|
-
pi->s += 3;
|
917
|
-
|
918
|
-
return Qnil;
|
919
|
-
}
|
920
|
-
|
921
|
-
static uint32_t
|
922
|
-
read_hex(ParseInfo pi, char *h) {
|
923
|
-
uint32_t b = 0;
|
924
|
-
int i;
|
925
|
-
|
926
|
-
// TBD this can be made faster with a table
|
927
|
-
for (i = 0; i < 4; i++, h++) {
|
928
|
-
b = b << 4;
|
929
|
-
if ('0' <= *h && *h <= '9') {
|
930
|
-
b += *h - '0';
|
931
|
-
} else if ('A' <= *h && *h <= 'F') {
|
932
|
-
b += *h - 'A' + 10;
|
933
|
-
} else if ('a' <= *h && *h <= 'f') {
|
934
|
-
b += *h - 'a' + 10;
|
935
|
-
} else {
|
936
|
-
pi->s = h;
|
937
|
-
raise_error("invalid hex character", pi->str, pi->s);
|
938
|
-
}
|
939
|
-
}
|
940
|
-
return b;
|
941
|
-
}
|
942
|
-
|
943
|
-
static char*
|
944
|
-
unicode_to_chars(ParseInfo pi, char *t, uint32_t code) {
|
945
|
-
if (0x0000007F >= code) {
|
946
|
-
*t = (char)code;
|
947
|
-
} else if (0x000007FF >= code) {
|
948
|
-
*t++ = 0xC0 | (code >> 6);
|
949
|
-
*t = 0x80 | (0x3F & code);
|
950
|
-
} else if (0x0000FFFF >= code) {
|
951
|
-
*t++ = 0xE0 | (code >> 12);
|
952
|
-
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
953
|
-
*t = 0x80 | (0x3F & code);
|
954
|
-
} else if (0x001FFFFF >= code) {
|
955
|
-
*t++ = 0xF0 | (code >> 18);
|
956
|
-
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
957
|
-
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
958
|
-
*t = 0x80 | (0x3F & code);
|
959
|
-
} else if (0x03FFFFFF >= code) {
|
960
|
-
*t++ = 0xF8 | (code >> 24);
|
961
|
-
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
962
|
-
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
963
|
-
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
964
|
-
*t = 0x80 | (0x3F & code);
|
965
|
-
} else if (0x7FFFFFFF >= code) {
|
966
|
-
*t++ = 0xFC | (code >> 30);
|
967
|
-
*t++ = 0x80 | ((code >> 24) & 0x3F);
|
968
|
-
*t++ = 0x80 | ((code >> 18) & 0x3F);
|
969
|
-
*t++ = 0x80 | ((code >> 12) & 0x3F);
|
970
|
-
*t++ = 0x80 | ((code >> 6) & 0x3F);
|
971
|
-
*t = 0x80 | (0x3F & code);
|
972
|
-
} else {
|
973
|
-
raise_error("invalid Unicode", pi->str, pi->s);
|
974
|
-
}
|
975
|
-
return t;
|
976
|
-
}
|
977
|
-
|
978
|
-
/* Assume the value starts immediately and goes until the quote character is
|
979
|
-
* reached again. Do not read the character after the terminating quote.
|
980
|
-
*/
|
981
|
-
static char*
|
982
|
-
read_quoted_value(ParseInfo pi) {
|
983
|
-
char *value = 0;
|
984
|
-
char *h = pi->s; // head
|
985
|
-
char *t = h; // tail
|
986
|
-
uint32_t code;
|
987
|
-
|
988
|
-
h++; // skip quote character
|
989
|
-
t++;
|
990
|
-
value = h;
|
991
|
-
for (; '"' != *h; h++, t++) {
|
992
|
-
if ('\0' == *h) {
|
993
|
-
pi->s = h;
|
994
|
-
raise_error("quoted string not terminated", pi->str, pi->s);
|
995
|
-
} else if ('\\' == *h) {
|
996
|
-
h++;
|
997
|
-
switch (*h) {
|
998
|
-
case 'n': *t = '\n'; break;
|
999
|
-
case 'r': *t = '\r'; break;
|
1000
|
-
case 't': *t = '\t'; break;
|
1001
|
-
case 'f': *t = '\f'; break;
|
1002
|
-
case 'b': *t = '\b'; break;
|
1003
|
-
case '"': *t = '"'; break;
|
1004
|
-
case '/': *t = '/'; break;
|
1005
|
-
case '\\': *t = '\\'; break;
|
1006
|
-
case 'u':
|
1007
|
-
h++;
|
1008
|
-
code = read_hex(pi, h);
|
1009
|
-
h += 3;
|
1010
|
-
if (0x0000D800 <= code && code <= 0x0000DFFF) {
|
1011
|
-
uint32_t c1 = (code - 0x0000D800) & 0x000003FF;
|
1012
|
-
uint32_t c2;
|
1013
|
-
|
1014
|
-
h++;
|
1015
|
-
if ('\\' != *h || 'u' != *(h + 1)) {
|
1016
|
-
pi->s = h;
|
1017
|
-
raise_error("invalid escaped character", pi->str, pi->s);
|
1018
|
-
}
|
1019
|
-
h += 2;
|
1020
|
-
c2 = read_hex(pi, h);
|
1021
|
-
h += 3;
|
1022
|
-
c2 = (c2 - 0x0000DC00) & 0x000003FF;
|
1023
|
-
code = ((c1 << 10) | c2) + 0x00010000;
|
1024
|
-
}
|
1025
|
-
t = unicode_to_chars(pi, t, code);
|
1026
|
-
break;
|
1027
|
-
default:
|
1028
|
-
pi->s = h;
|
1029
|
-
raise_error("invalid escaped character", pi->str, pi->s);
|
1030
|
-
break;
|
1031
|
-
}
|
1032
|
-
} else if (t != h) {
|
1033
|
-
*t = *h;
|
1034
|
-
}
|
1035
|
-
}
|
1036
|
-
*t = '\0'; // terminate value
|
1037
|
-
pi->s = h + 1;
|
1038
|
-
|
1039
|
-
return value;
|
1040
|
-
}
|
1041
|
-
|
1042
|
-
VALUE
|
1043
|
-
oj_parse(char *json, Options options) {
|
1044
|
-
VALUE obj;
|
1045
|
-
struct _ParseInfo pi;
|
1046
|
-
|
1047
|
-
if (0 == json) {
|
1048
|
-
raise_error("Invalid arg, xml string can not be null", json, 0);
|
1049
|
-
}
|
1050
|
-
/* skip UTF-8 BOM if present */
|
1051
|
-
if (0xEF == (uint8_t)*json && 0xBB == (uint8_t)json[1] && 0xBF == (uint8_t)json[2]) {
|
1052
|
-
json += 3;
|
1053
|
-
}
|
1054
|
-
/* initialize parse info */
|
1055
|
-
pi.str = json;
|
1056
|
-
pi.s = json;
|
1057
|
-
pi.circ_array = 0;
|
1058
|
-
if (Yes == options->circular) {
|
1059
|
-
pi.circ_array = circ_array_new();
|
1060
|
-
}
|
1061
|
-
pi.options = options;
|
1062
|
-
#if IS_WINDOWS
|
1063
|
-
pi.stack_min = (char*)&obj - (512 * 1024); // assume a 1M stack and give half to ruby
|
1064
|
-
#else
|
1065
|
-
{
|
1066
|
-
struct rlimit lim;
|
1067
|
-
|
1068
|
-
// When run under make on linux the limit is not reported corrected and is infinity even though
|
1069
|
-
// the return code indicates no error. That forces the rlim_cur value as well as the return code.
|
1070
|
-
if (0 == getrlimit(RLIMIT_STACK, &lim) && RLIM_INFINITY != lim.rlim_cur) {
|
1071
|
-
pi.stack_min = (void*)((char*)&obj - (lim.rlim_cur / 4 * 3)); // let 3/4ths of the stack be used only
|
1072
|
-
} else {
|
1073
|
-
pi.stack_min = 0; // indicates not to check stack limit
|
1074
|
-
}
|
1075
|
-
}
|
1076
|
-
#endif
|
1077
|
-
obj = read_next(&pi, 0);
|
1078
|
-
if (Yes == options->circular) {
|
1079
|
-
circ_array_free(pi.circ_array);
|
1080
|
-
}
|
1081
|
-
if (Qundef == obj) {
|
1082
|
-
raise_error("no object read", pi.str, pi.s);
|
1083
|
-
}
|
1084
|
-
next_non_white(&pi);
|
1085
|
-
if ('\0' != *pi.s) {
|
1086
|
-
raise_error("invalid format, extra characters", pi.str, pi.s);
|
1087
|
-
}
|
1088
|
-
return obj;
|
1089
|
-
}
|