oj 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +17 -23
  3. data/README.md +74 -425
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +4 -0
  6. data/ext/oj/circarray.c +68 -0
  7. data/ext/oj/circarray.h +23 -0
  8. data/ext/oj/code.c +227 -0
  9. data/ext/oj/code.h +40 -0
  10. data/ext/oj/compat.c +243 -0
  11. data/ext/oj/custom.c +1097 -0
  12. data/ext/oj/dump.c +766 -1534
  13. data/ext/oj/dump.h +92 -0
  14. data/ext/oj/dump_compat.c +937 -0
  15. data/ext/oj/dump_leaf.c +254 -0
  16. data/ext/oj/dump_object.c +810 -0
  17. data/ext/oj/dump_rails.c +329 -0
  18. data/ext/oj/dump_strict.c +416 -0
  19. data/ext/oj/encode.h +51 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +17 -7
  23. data/ext/oj/fast.c +213 -180
  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 +817 -0
  28. data/ext/oj/mimic_rails.c +806 -0
  29. data/ext/oj/mimic_rails.h +17 -0
  30. data/ext/oj/object.c +752 -0
  31. data/ext/oj/odd.c +230 -0
  32. data/ext/oj/odd.h +44 -0
  33. data/ext/oj/oj.c +1288 -929
  34. data/ext/oj/oj.h +240 -69
  35. data/ext/oj/parse.c +1014 -0
  36. data/ext/oj/parse.h +92 -0
  37. data/ext/oj/reader.c +223 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +127 -0
  40. data/ext/oj/{cache.h → resolve.h} +6 -13
  41. data/ext/oj/rxclass.c +133 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +77 -175
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +911 -0
  46. data/ext/oj/stream_writer.c +301 -0
  47. data/ext/oj/strict.c +162 -0
  48. data/ext/oj/string_writer.c +480 -0
  49. data/ext/oj/val_stack.c +98 -0
  50. data/ext/oj/val_stack.h +188 -0
  51. data/lib/oj/active_support_helper.rb +41 -0
  52. data/lib/oj/bag.rb +6 -10
  53. data/lib/oj/easy_hash.rb +52 -0
  54. data/lib/oj/json.rb +172 -0
  55. data/lib/oj/mimic.rb +260 -5
  56. data/lib/oj/saj.rb +13 -10
  57. data/lib/oj/schandler.rb +142 -0
  58. data/lib/oj/state.rb +131 -0
  59. data/lib/oj/version.rb +1 -1
  60. data/lib/oj.rb +11 -23
  61. data/pages/Advanced.md +22 -0
  62. data/pages/Compatibility.md +25 -0
  63. data/pages/Custom.md +23 -0
  64. data/pages/Encoding.md +65 -0
  65. data/pages/JsonGem.md +79 -0
  66. data/pages/Modes.md +140 -0
  67. data/pages/Options.md +250 -0
  68. data/pages/Rails.md +60 -0
  69. data/pages/Security.md +20 -0
  70. data/test/_test_active.rb +76 -0
  71. data/test/_test_active_mimic.rb +96 -0
  72. data/test/_test_mimic_rails.rb +126 -0
  73. data/test/activesupport4/decoding_test.rb +105 -0
  74. data/test/activesupport4/encoding_test.rb +531 -0
  75. data/test/activesupport4/test_helper.rb +41 -0
  76. data/test/activesupport5/decoding_test.rb +125 -0
  77. data/test/activesupport5/encoding_test.rb +483 -0
  78. data/test/activesupport5/encoding_test_cases.rb +90 -0
  79. data/test/activesupport5/test_helper.rb +50 -0
  80. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  81. data/test/helper.rb +27 -0
  82. data/test/isolated/shared.rb +310 -0
  83. data/test/isolated/test_mimic_after.rb +13 -0
  84. data/test/isolated/test_mimic_alone.rb +12 -0
  85. data/test/isolated/test_mimic_as_json.rb +45 -0
  86. data/test/isolated/test_mimic_before.rb +13 -0
  87. data/test/isolated/test_mimic_define.rb +28 -0
  88. data/test/isolated/test_mimic_rails_after.rb +22 -0
  89. data/test/isolated/test_mimic_rails_before.rb +21 -0
  90. data/test/isolated/test_mimic_redefine.rb +15 -0
  91. data/test/json_gem/json_addition_test.rb +216 -0
  92. data/test/json_gem/json_common_interface_test.rb +143 -0
  93. data/test/json_gem/json_encoding_test.rb +109 -0
  94. data/test/json_gem/json_ext_parser_test.rb +20 -0
  95. data/test/json_gem/json_fixtures_test.rb +35 -0
  96. data/test/json_gem/json_generator_test.rb +383 -0
  97. data/test/json_gem/json_generic_object_test.rb +90 -0
  98. data/test/json_gem/json_parser_test.rb +470 -0
  99. data/test/json_gem/json_string_matching_test.rb +42 -0
  100. data/test/json_gem/test_helper.rb +18 -0
  101. data/test/perf_compat.rb +130 -0
  102. data/test/perf_fast.rb +9 -9
  103. data/test/perf_file.rb +64 -0
  104. data/test/{perf_obj.rb → perf_object.rb} +24 -10
  105. data/test/perf_scp.rb +151 -0
  106. data/test/perf_strict.rb +32 -113
  107. data/test/sample.rb +2 -3
  108. data/test/test_compat.rb +474 -0
  109. data/test/test_custom.rb +355 -0
  110. data/test/test_debian.rb +53 -0
  111. data/test/test_fast.rb +66 -16
  112. data/test/test_file.rb +237 -0
  113. data/test/test_gc.rb +49 -0
  114. data/test/test_hash.rb +29 -0
  115. data/test/test_null.rb +376 -0
  116. data/test/test_object.rb +1010 -0
  117. data/test/test_saj.rb +16 -16
  118. data/test/test_scp.rb +417 -0
  119. data/test/test_strict.rb +410 -0
  120. data/test/test_various.rb +815 -0
  121. data/test/test_writer.rb +308 -0
  122. data/test/tests.rb +9 -902
  123. data/test/tests_mimic.rb +14 -0
  124. data/test/tests_mimic_addition.rb +7 -0
  125. metadata +253 -38
  126. data/ext/oj/cache.c +0 -148
  127. data/ext/oj/foo.rb +0 -6
  128. data/ext/oj/load.c +0 -1049
  129. data/test/a.rb +0 -38
  130. data/test/perf1.rb +0 -64
  131. data/test/perf2.rb +0 -76
  132. data/test/perf_obj_old.rb +0 -213
  133. data/test/test_mimic.rb +0 -208
data/ext/oj/buf.h ADDED
@@ -0,0 +1,103 @@
1
+ /* buf.h
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
+ #ifndef __OJ_BUF_H__
32
+ #define __OJ_BUF_H__
33
+
34
+ #include "ruby.h"
35
+
36
+ typedef struct _Buf {
37
+ char *head;
38
+ char *end;
39
+ char *tail;
40
+ char base[1024];
41
+ } *Buf;
42
+
43
+ inline static void
44
+ buf_init(Buf buf) {
45
+ buf->head = buf->base;
46
+ buf->end = buf->base + sizeof(buf->base) - 1;
47
+ buf->tail = buf->head;
48
+ }
49
+
50
+ inline static void
51
+ buf_cleanup(Buf buf) {
52
+ if (buf->base != buf->head) {
53
+ xfree(buf->head);
54
+ }
55
+ }
56
+
57
+ inline static size_t
58
+ buf_len(Buf buf) {
59
+ return buf->tail - buf->head;
60
+ }
61
+
62
+ inline static void
63
+ buf_append_string(Buf buf, const char *s, size_t slen) {
64
+ if (buf->end <= buf->tail + slen) {
65
+ size_t len = buf->end - buf->head;
66
+ size_t toff = buf->tail - buf->head;
67
+ size_t new_len = len + slen + len / 2;
68
+
69
+ if (buf->base == buf->head) {
70
+ buf->head = ALLOC_N(char, new_len);
71
+ memcpy(buf->head, buf->base, len);
72
+ } else {
73
+ REALLOC_N(buf->head, char, new_len);
74
+ }
75
+ buf->tail = buf->head + toff;
76
+ buf->end = buf->head + new_len - 1;
77
+ }
78
+ memcpy(buf->tail, s, slen);
79
+ buf->tail += slen;
80
+ }
81
+
82
+ inline static void
83
+ buf_append(Buf buf, char c) {
84
+ if (buf->end <= buf->tail) {
85
+ size_t len = buf->end - buf->head;
86
+ size_t toff = buf->tail - buf->head;
87
+ size_t new_len = len + len / 2;
88
+
89
+ if (buf->base == buf->head) {
90
+ buf->head = ALLOC_N(char, new_len);
91
+ memcpy(buf->head, buf->base, len);
92
+ } else {
93
+ REALLOC_N(buf->head, char, new_len);
94
+ }
95
+ buf->tail = buf->head + toff;
96
+ buf->end = buf->head + new_len - 1;
97
+ }
98
+ *buf->tail = c;
99
+ buf->tail++;
100
+ //*buf->tail = '\0'; // for debugging
101
+ }
102
+
103
+ #endif /* __OJ_BUF_H__ */
data/ext/oj/cache8.c CHANGED
@@ -94,7 +94,11 @@ slot_print(Cache8 c, sid_t key, unsigned int depth) {
94
94
  k = (k8 << BITS) | i;
95
95
  /*printf("*** key: 0x%016llx depth: %u i: %u\n", k, depth, i); */
96
96
  if (DEPTH - 1 == depth) {
97
+ #if IS_WINDOWS
98
+ printf("0x%016lx: %4lu\n", (long unsigned int)k, (long unsigned int)b->value);
99
+ #else
97
100
  printf("0x%016llx: %4llu\n", (long long unsigned int)k, (long long unsigned int)b->value);
101
+ #endif
98
102
  } else {
99
103
  slot_print(b->child, k, depth + 1);
100
104
  }
@@ -0,0 +1,68 @@
1
+ /* circarray.c
2
+ * Copyright (c) 2012, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "circarray.h"
7
+
8
+ CircArray
9
+ oj_circ_array_new() {
10
+ CircArray ca;
11
+
12
+ if (0 == (ca = ALLOC(struct _CircArray))) {
13
+ rb_raise(rb_eNoMemError, "not enough memory\n");
14
+ }
15
+ ca->objs = ca->obj_array;
16
+ ca->size = sizeof(ca->obj_array) / sizeof(VALUE);
17
+ ca->cnt = 0;
18
+
19
+ return ca;
20
+ }
21
+
22
+ void
23
+ oj_circ_array_free(CircArray ca) {
24
+ if (ca->objs != ca->obj_array) {
25
+ xfree(ca->objs);
26
+ }
27
+ xfree(ca);
28
+ }
29
+
30
+ void
31
+ oj_circ_array_set(CircArray ca, VALUE obj, unsigned long id) {
32
+ if (0 < id && 0 != ca) {
33
+ unsigned long i;
34
+
35
+ if (ca->size < id) {
36
+ unsigned long cnt = id + 512;
37
+
38
+ if (ca->objs == ca->obj_array) {
39
+ if (0 == (ca->objs = ALLOC_N(VALUE, cnt))) {
40
+ rb_raise(rb_eNoMemError, "not enough memory\n");
41
+ }
42
+ memcpy(ca->objs, ca->obj_array, sizeof(VALUE) * ca->cnt);
43
+ } else {
44
+ REALLOC_N(ca->objs, VALUE, cnt);
45
+ }
46
+ ca->size = cnt;
47
+ }
48
+ id--;
49
+ for (i = ca->cnt; i < id; i++) {
50
+ ca->objs[i] = Qnil;
51
+ }
52
+ ca->objs[id] = obj;
53
+ if (ca->cnt <= id) {
54
+ ca->cnt = id + 1;
55
+ }
56
+ }
57
+ }
58
+
59
+ VALUE
60
+ oj_circ_array_get(CircArray ca, unsigned long id) {
61
+ VALUE obj = Qnil;
62
+
63
+ if (id <= ca->cnt && 0 != ca) {
64
+ obj = ca->objs[id - 1];
65
+ }
66
+ return obj;
67
+ }
68
+
@@ -0,0 +1,23 @@
1
+ /* circarray.h
2
+ * Copyright (c) 2012, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #ifndef __OJ_CIRCARRAY_H__
7
+ #define __OJ_CIRCARRAY_H__
8
+
9
+ #include "ruby.h"
10
+
11
+ typedef struct _CircArray {
12
+ VALUE obj_array[1024];
13
+ VALUE *objs;
14
+ unsigned long size; // allocated size or initial array size
15
+ unsigned long cnt;
16
+ } *CircArray;
17
+
18
+ extern CircArray oj_circ_array_new(void);
19
+ extern void oj_circ_array_free(CircArray ca);
20
+ extern void oj_circ_array_set(CircArray ca, VALUE obj, unsigned long id);
21
+ extern VALUE oj_circ_array_get(CircArray ca, unsigned long id);
22
+
23
+ #endif /* __OJ_CIRCARRAY_H__ */
data/ext/oj/code.c ADDED
@@ -0,0 +1,227 @@
1
+ /* code.c
2
+ * Copyright (c) 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "code.h"
7
+ #include "dump.h"
8
+
9
+ inline static VALUE
10
+ resolve_classname(VALUE mod, const char *classname) {
11
+ VALUE clas = Qundef;
12
+ ID ci = rb_intern(classname);
13
+
14
+ if (rb_const_defined_at(mod, ci)) {
15
+ clas = rb_const_get_at(mod, ci);
16
+ }
17
+ return clas;
18
+ }
19
+
20
+ static VALUE
21
+ path2class(const char *name) {
22
+ char class_name[1024];
23
+ VALUE clas;
24
+ char *end = class_name + sizeof(class_name) - 1;
25
+ char *s;
26
+ const char *n = name;
27
+
28
+ clas = rb_cObject;
29
+ for (s = class_name; '\0' != *n; n++) {
30
+ if (':' == *n) {
31
+ *s = '\0';
32
+ n++;
33
+ if (':' != *n) {
34
+ return Qundef;
35
+ }
36
+ if (Qundef == (clas = resolve_classname(clas, class_name))) {
37
+ return Qundef;
38
+ }
39
+ s = class_name;
40
+ } else if (end <= s) {
41
+ return Qundef;
42
+ } else {
43
+ *s++ = *n;
44
+ }
45
+ }
46
+ *s = '\0';
47
+
48
+ return resolve_classname(clas, class_name);
49
+ }
50
+
51
+ bool
52
+ oj_code_dump(Code codes, VALUE obj, int depth, Out out) {
53
+ VALUE clas = rb_obj_class(obj);
54
+ Code c = codes;
55
+
56
+ for (; NULL != c->name; c++) {
57
+ if (Qundef == c->clas) { // indicates not defined
58
+ continue;
59
+ }
60
+ if (Qnil == c->clas) {
61
+ c->clas = path2class(c->name);
62
+ }
63
+ if (clas == c->clas && c->active) {
64
+ c->encode(obj, depth, out);
65
+ return true;
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+
71
+ VALUE
72
+ oj_code_load(Code codes, VALUE clas, VALUE args) {
73
+ Code c = codes;
74
+
75
+ for (; NULL != c->name; c++) {
76
+ if (Qundef == c->clas) { // indicates not defined
77
+ continue;
78
+ }
79
+ if (Qnil == c->clas) {
80
+ c->clas = path2class(c->name);
81
+ }
82
+ if (clas == c->clas) {
83
+ if (NULL == c->decode) {
84
+ break;
85
+ }
86
+ return c->decode(clas, args);
87
+ }
88
+ }
89
+ return Qnil;
90
+ }
91
+
92
+ void
93
+ oj_code_set_active(Code codes, VALUE clas, bool active) {
94
+ Code c = codes;
95
+
96
+ for (; NULL != c->name; c++) {
97
+ if (Qundef == c->clas) { // indicates not defined
98
+ continue;
99
+ }
100
+ if (Qnil == c->clas) {
101
+ c->clas = path2class(c->name);
102
+ }
103
+ if (clas == c->clas || Qnil == clas) {
104
+ c->active = active;
105
+ if (Qnil != clas) {
106
+ break;
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ bool
113
+ oj_code_has(Code codes, VALUE clas, bool encode) {
114
+ Code c = codes;
115
+
116
+ for (; NULL != c->name; c++) {
117
+ if (Qundef == c->clas) { // indicates not defined
118
+ continue;
119
+ }
120
+ if (Qnil == c->clas) {
121
+ c->clas = path2class(c->name);
122
+ }
123
+ if (clas == c->clas) {
124
+ if (encode) {
125
+ return c->active && NULL != c->encode;
126
+ } else {
127
+ return c->active && NULL != c->decode;
128
+ }
129
+ }
130
+ }
131
+ return false;
132
+ }
133
+
134
+ void
135
+ oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out) {
136
+ int d2 = depth + 1;
137
+ int d3 = d2 + 1;
138
+ size_t sep_len = out->opts->dump_opts.before_size + out->opts->dump_opts.after_size + 2;
139
+ const char *classname = rb_obj_classname(obj);
140
+ size_t len = strlen(classname);
141
+ size_t size = d2 * out->indent + 10 + len + out->opts->create_id_len + sep_len;
142
+
143
+ assure_size(out, size);
144
+ *out->cur++ = '{';
145
+ fill_indent(out, d2);
146
+ *out->cur++ = '"';
147
+ memcpy(out->cur, out->opts->create_id, out->opts->create_id_len);
148
+ out->cur += out->opts->create_id_len;
149
+ *out->cur++ = '"';
150
+ if (0 < out->opts->dump_opts.before_size) {
151
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
152
+ out->cur += out->opts->dump_opts.before_size;
153
+ }
154
+ *out->cur++ = ':';
155
+ if (0 < out->opts->dump_opts.after_size) {
156
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
157
+ out->cur += out->opts->dump_opts.after_size;
158
+ }
159
+ *out->cur++ = '"';
160
+ memcpy(out->cur, classname, len);
161
+ out->cur += len;
162
+ *out->cur++ = '"';
163
+
164
+ size = d3 * out->indent + 2;
165
+ for (; NULL != attrs->name; attrs++) {
166
+ assure_size(out, size + attrs->len + sep_len + 2);
167
+ *out->cur++ = ',';
168
+ fill_indent(out, d3);
169
+ *out->cur++ = '"';
170
+ memcpy(out->cur, attrs->name, attrs->len);
171
+ out->cur += attrs->len;
172
+ *out->cur++ = '"';
173
+ if (0 < out->opts->dump_opts.before_size) {
174
+ strcpy(out->cur, out->opts->dump_opts.before_sep);
175
+ out->cur += out->opts->dump_opts.before_size;
176
+ }
177
+ *out->cur++ = ':';
178
+ if (0 < out->opts->dump_opts.after_size) {
179
+ strcpy(out->cur, out->opts->dump_opts.after_sep);
180
+ out->cur += out->opts->dump_opts.after_size;
181
+ }
182
+ if (Qundef == attrs->value) {
183
+ if (Qundef != attrs->time) {
184
+ switch (out->opts->time_format) {
185
+ case RubyTime: oj_dump_ruby_time(attrs->time, out); break;
186
+ case XmlTime: oj_dump_xml_time(attrs->time, out); break;
187
+ case UnixZTime: oj_dump_time(attrs->time, out, true); break;
188
+ case UnixTime:
189
+ default: oj_dump_time(attrs->time, out, false); break;
190
+ }
191
+ } else {
192
+ char buf[32];
193
+ char *b = buf + sizeof(buf) - 1;
194
+ int neg = 0;
195
+ long num = attrs->num;
196
+
197
+ if (0 > num) {
198
+ neg = 1;
199
+ num = -num;
200
+ }
201
+ *b-- = '\0';
202
+ if (0 < num) {
203
+ for (; 0 < num; num /= 10, b--) {
204
+ *b = (num % 10) + '0';
205
+ }
206
+ if (neg) {
207
+ *b = '-';
208
+ } else {
209
+ b++;
210
+ }
211
+ } else {
212
+ *b = '0';
213
+ }
214
+ assure_size(out, (sizeof(buf) - (b - buf)));
215
+ for (; '\0' != *b; b++) {
216
+ *out->cur++ = *b;
217
+ }
218
+ }
219
+ } else {
220
+ oj_dump_compat_val(attrs->value, d3, out, true);
221
+ }
222
+ }
223
+ assure_size(out, depth * out->indent + 2);
224
+ fill_indent(out, depth);
225
+ *out->cur++ = '}';
226
+ *out->cur = '\0';
227
+ }
data/ext/oj/code.h ADDED
@@ -0,0 +1,40 @@
1
+ /* code.h
2
+ * Copyright (c) 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #ifndef __OJ_CODE_H__
7
+ #define __OJ_CODE_H__
8
+
9
+ #include <ruby.h>
10
+
11
+ #include "oj.h"
12
+
13
+ typedef void (*EncodeFunc)(VALUE obj, int depth, Out out);
14
+ typedef VALUE (*DecodeFunc)(VALUE clas, VALUE args);
15
+
16
+ typedef struct _Code {
17
+ const char *name;
18
+ VALUE clas;
19
+ EncodeFunc encode;
20
+ DecodeFunc decode;
21
+ bool active; // For compat mode.
22
+ } *Code;
23
+
24
+ // Used by encode functions.
25
+ typedef struct _Attr {
26
+ const char *name;
27
+ int len;
28
+ VALUE value;
29
+ long num;
30
+ VALUE time;
31
+ } *Attr;
32
+
33
+ extern bool oj_code_dump(Code codes, VALUE obj, int depth, Out out);
34
+ extern VALUE oj_code_load(Code codes, VALUE clas, VALUE args);
35
+ extern void oj_code_set_active(Code codes, VALUE clas, bool active);
36
+ extern bool oj_code_has(Code codes, VALUE clas, bool encode);
37
+
38
+ extern void oj_code_attrs(VALUE obj, Attr attrs, int depth, Out out);
39
+
40
+ #endif /* __OJ_CODE_H__ */
data/ext/oj/compat.c ADDED
@@ -0,0 +1,243 @@
1
+ /* compat.c
2
+ * Copyright (c) 2012, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <stdio.h>
7
+
8
+ #include "oj.h"
9
+ #include "err.h"
10
+ #include "parse.h"
11
+ #include "resolve.h"
12
+ #include "hash.h"
13
+ #include "encode.h"
14
+
15
+ static void
16
+ hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
17
+ const char *key = kval->key;
18
+ int klen = kval->klen;
19
+ Val parent = stack_peek(&pi->stack);
20
+ volatile VALUE rkey = kval->key_val;
21
+
22
+ if (Qundef == rkey &&
23
+ Yes == pi->options.create_ok &&
24
+ NULL != pi->options.create_id &&
25
+ *pi->options.create_id == *key &&
26
+ (int)pi->options.create_id_len == klen &&
27
+ 0 == strncmp(pi->options.create_id, key, klen)) {
28
+
29
+ parent->classname = oj_strndup(str, len);
30
+ parent->clen = len;
31
+ } else {
32
+ volatile VALUE rstr = rb_str_new(str, len);
33
+
34
+ if (Qundef == rkey) {
35
+ rkey = rb_str_new(key, klen);
36
+ rstr = oj_encode(rstr);
37
+ rkey = oj_encode(rkey);
38
+ if (Yes == pi->options.sym_key) {
39
+ rkey = rb_str_intern(rkey);
40
+ }
41
+ }
42
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
43
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
44
+
45
+ if (Qnil != clas) {
46
+ rstr = rb_funcall(clas, oj_json_create_id, 1, rstr);
47
+ }
48
+ }
49
+ if (rb_cHash != rb_obj_class(parent->val)) {
50
+ // The rb_hash_set would still work but the unit tests for the
51
+ // json gem require the less efficient []= method be called to set
52
+ // values. Even using the store method to set the values will fail
53
+ // the unit tests.
54
+ rb_funcall(parent->val, rb_intern("[]="), 2, rkey, rstr);
55
+ } else {
56
+ rb_hash_aset(parent->val, rkey, rstr);
57
+ }
58
+ }
59
+ }
60
+
61
+ static VALUE
62
+ start_hash(ParseInfo pi) {
63
+ volatile VALUE h;
64
+
65
+ if (Qnil != pi->options.hash_class) {
66
+ h = rb_class_new_instance(0, NULL, pi->options.hash_class);
67
+ } else {
68
+ h = rb_hash_new();
69
+ }
70
+ return h;
71
+ }
72
+
73
+ static void
74
+ end_hash(struct _ParseInfo *pi) {
75
+ Val parent = stack_peek(&pi->stack);
76
+
77
+ if (0 != parent->classname) {
78
+ volatile VALUE clas;
79
+
80
+ clas = oj_name2class(pi, parent->classname, parent->clen, 0, rb_eArgError);
81
+ if (Qundef != clas) { // else an error
82
+ ID creatable = rb_intern("json_creatable?");
83
+
84
+ if (!rb_respond_to(clas, creatable) || Qtrue == rb_funcall(clas, creatable, 0)) {
85
+ parent->val = rb_funcall(clas, oj_json_create_id, 1, parent->val);
86
+ }
87
+ }
88
+ if (0 != parent->classname) {
89
+ xfree((char*)parent->classname);
90
+ parent->classname = 0;
91
+ }
92
+ }
93
+ }
94
+
95
+ static VALUE
96
+ calc_hash_key(ParseInfo pi, Val parent) {
97
+ volatile VALUE rkey = parent->key_val;
98
+
99
+ if (Qundef == rkey) {
100
+ rkey = rb_str_new(parent->key, parent->klen);
101
+ }
102
+ rkey = oj_encode(rkey);
103
+ if (Yes == pi->options.sym_key) {
104
+ rkey = rb_str_intern(rkey);
105
+ }
106
+ return rkey;
107
+ }
108
+
109
+ static void
110
+ add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
111
+ volatile VALUE rstr = rb_str_new(str, len);
112
+
113
+ rstr = oj_encode(rstr);
114
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
115
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
116
+
117
+ if (Qnil != clas) {
118
+ pi->stack.head->val = rb_funcall(clas, oj_json_create_id, 1, rstr);
119
+ return;
120
+ }
121
+ }
122
+ pi->stack.head->val = rstr;
123
+ }
124
+
125
+ static void
126
+ add_num(ParseInfo pi, NumInfo ni) {
127
+ pi->stack.head->val = oj_num_as_value(ni);
128
+ }
129
+
130
+ static void
131
+ hash_set_num(struct _ParseInfo *pi, Val parent, NumInfo ni) {
132
+ if (!oj_use_hash_alt && rb_cHash != rb_obj_class(parent->val)) {
133
+ // The rb_hash_set would still work but the unit tests for the
134
+ // json gem require the less efficient []= method be called to set
135
+ // values. Even using the store method to set the values will fail
136
+ // the unit tests.
137
+ rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), oj_num_as_value(ni));
138
+ } else {
139
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), oj_num_as_value(ni));
140
+ }
141
+ }
142
+
143
+ static void
144
+ hash_set_value(ParseInfo pi, Val parent, VALUE value) {
145
+ if (rb_cHash != rb_obj_class(parent->val)) {
146
+ // The rb_hash_set would still work but the unit tests for the
147
+ // json gem require the less efficient []= method be called to set
148
+ // values. Even using the store method to set the values will fail
149
+ // the unit tests.
150
+ rb_funcall(stack_peek(&pi->stack)->val, rb_intern("[]="), 2, calc_hash_key(pi, parent), value);
151
+ } else {
152
+ rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
153
+ }
154
+ }
155
+
156
+ static VALUE
157
+ start_array(ParseInfo pi) {
158
+ if (Qnil != pi->options.array_class) {
159
+ return rb_class_new_instance(0, NULL, pi->options.array_class);
160
+ }
161
+ return rb_ary_new();
162
+ }
163
+
164
+ static void
165
+ array_append_num(ParseInfo pi, NumInfo ni) {
166
+ Val parent = stack_peek(&pi->stack);
167
+
168
+ if (!oj_use_array_alt && rb_cArray != rb_obj_class(parent->val)) {
169
+ // The rb_ary_push would still work but the unit tests for the json
170
+ // gem require the less efficient << method be called to push the
171
+ // values.
172
+ rb_funcall(parent->val, rb_intern("<<"), 1, oj_num_as_value(ni));
173
+ } else {
174
+ rb_ary_push(parent->val, oj_num_as_value(ni));
175
+ }
176
+ }
177
+
178
+ static void
179
+ array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
180
+ volatile VALUE rstr = rb_str_new(str, len);
181
+
182
+ rstr = oj_encode(rstr);
183
+ if (Yes == pi->options.create_ok && NULL != pi->options.str_rx.head) {
184
+ VALUE clas = oj_rxclass_match(&pi->options.str_rx, str, len);
185
+
186
+ if (Qnil != clas) {
187
+ rb_ary_push(stack_peek(&pi->stack)->val, rb_funcall(clas, oj_json_create_id, 1, rstr));
188
+ return;
189
+ }
190
+ }
191
+ rb_ary_push(stack_peek(&pi->stack)->val, rstr);
192
+ }
193
+
194
+ void
195
+ oj_set_compat_callbacks(ParseInfo pi) {
196
+ oj_set_strict_callbacks(pi);
197
+ pi->start_hash = start_hash;
198
+ pi->end_hash = end_hash;
199
+ pi->hash_set_cstr = hash_set_cstr;
200
+ pi->hash_set_num = hash_set_num;
201
+ pi->hash_set_value = hash_set_value;
202
+ pi->add_num = add_num;
203
+ pi->add_cstr = add_cstr;
204
+ pi->array_append_cstr = array_append_cstr;
205
+ pi->start_array = start_array;
206
+ pi->array_append_num = array_append_num;
207
+ }
208
+
209
+ VALUE
210
+ oj_compat_parse(int argc, VALUE *argv, VALUE self) {
211
+ struct _ParseInfo pi;
212
+
213
+ parse_info_init(&pi);
214
+ pi.options = oj_default_options;
215
+ pi.handler = Qnil;
216
+ pi.err_class = Qnil;
217
+ pi.max_depth = 0;
218
+ pi.options.allow_nan = Yes;
219
+ pi.options.nilnil = Yes;
220
+ oj_set_compat_callbacks(&pi);
221
+
222
+ if (T_STRING == rb_type(*argv)) {
223
+ return oj_pi_parse(argc, argv, &pi, 0, 0, false);
224
+ } else {
225
+ return oj_pi_sparse(argc, argv, &pi, 0);
226
+ }
227
+ }
228
+
229
+ VALUE
230
+ oj_compat_parse_cstr(int argc, VALUE *argv, char *json, size_t len) {
231
+ struct _ParseInfo pi;
232
+
233
+ parse_info_init(&pi);
234
+ pi.options = oj_default_options;
235
+ pi.handler = Qnil;
236
+ pi.err_class = Qnil;
237
+ pi.max_depth = 0;
238
+ pi.options.allow_nan = Yes;
239
+ pi.options.nilnil = Yes;
240
+ oj_set_compat_callbacks(&pi);
241
+
242
+ return oj_pi_parse(argc, argv, &pi, json, len, false);
243
+ }