oj 3.7.12

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.
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
+ }