oj 3.7.12

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