oj 2.18.5 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -226
  3. data/ext/oj/circarray.c +0 -25
  4. data/ext/oj/circarray.h +0 -25
  5. data/ext/oj/code.c +227 -0
  6. data/ext/oj/code.h +40 -0
  7. data/ext/oj/compat.c +126 -38
  8. data/ext/oj/custom.c +1097 -0
  9. data/ext/oj/dump.c +658 -2376
  10. data/ext/oj/dump.h +92 -0
  11. data/ext/oj/dump_compat.c +937 -0
  12. data/ext/oj/dump_leaf.c +254 -0
  13. data/ext/oj/dump_object.c +810 -0
  14. data/ext/oj/dump_rails.c +329 -0
  15. data/ext/oj/dump_strict.c +416 -0
  16. data/ext/oj/err.c +0 -25
  17. data/ext/oj/err.h +8 -2
  18. data/ext/oj/fast.c +24 -24
  19. data/ext/oj/mimic_json.c +817 -0
  20. data/ext/oj/mimic_rails.c +806 -0
  21. data/ext/oj/mimic_rails.h +17 -0
  22. data/ext/oj/object.c +18 -72
  23. data/ext/oj/odd.c +0 -25
  24. data/ext/oj/odd.h +2 -27
  25. data/ext/oj/oj.c +655 -1503
  26. data/ext/oj/oj.h +93 -40
  27. data/ext/oj/parse.c +99 -46
  28. data/ext/oj/parse.h +12 -26
  29. data/ext/oj/reader.c +1 -25
  30. data/ext/oj/reader.h +3 -25
  31. data/ext/oj/resolve.c +9 -11
  32. data/ext/oj/resolve.h +2 -2
  33. data/ext/oj/rxclass.c +133 -0
  34. data/ext/oj/rxclass.h +27 -0
  35. data/ext/oj/saj.c +4 -25
  36. data/ext/oj/scp.c +3 -25
  37. data/ext/oj/sparse.c +89 -13
  38. data/ext/oj/stream_writer.c +301 -0
  39. data/ext/oj/strict.c +4 -27
  40. data/ext/oj/string_writer.c +480 -0
  41. data/ext/oj/val_stack.h +6 -2
  42. data/lib/oj.rb +1 -23
  43. data/lib/oj/easy_hash.rb +12 -4
  44. data/lib/oj/json.rb +172 -0
  45. data/lib/oj/mimic.rb +123 -18
  46. data/lib/oj/state.rb +131 -0
  47. data/lib/oj/version.rb +1 -1
  48. data/pages/Advanced.md +22 -0
  49. data/pages/Compatibility.md +25 -0
  50. data/pages/Custom.md +23 -0
  51. data/pages/Encoding.md +65 -0
  52. data/pages/JsonGem.md +79 -0
  53. data/pages/Modes.md +140 -0
  54. data/pages/Options.md +250 -0
  55. data/pages/Rails.md +60 -0
  56. data/pages/Security.md +20 -0
  57. data/test/activesupport4/decoding_test.rb +105 -0
  58. data/test/activesupport4/encoding_test.rb +531 -0
  59. data/test/activesupport4/test_helper.rb +41 -0
  60. data/test/activesupport5/decoding_test.rb +125 -0
  61. data/test/activesupport5/encoding_test.rb +483 -0
  62. data/test/activesupport5/encoding_test_cases.rb +90 -0
  63. data/test/activesupport5/test_helper.rb +50 -0
  64. data/test/activesupport5/time_zone_test_helpers.rb +24 -0
  65. data/test/json_gem/json_addition_test.rb +216 -0
  66. data/test/json_gem/json_common_interface_test.rb +143 -0
  67. data/test/json_gem/json_encoding_test.rb +109 -0
  68. data/test/json_gem/json_ext_parser_test.rb +20 -0
  69. data/test/json_gem/json_fixtures_test.rb +35 -0
  70. data/test/json_gem/json_generator_test.rb +383 -0
  71. data/test/json_gem/json_generic_object_test.rb +90 -0
  72. data/test/json_gem/json_parser_test.rb +470 -0
  73. data/test/json_gem/json_string_matching_test.rb +42 -0
  74. data/test/json_gem/test_helper.rb +18 -0
  75. data/test/perf_compat.rb +30 -28
  76. data/test/perf_object.rb +1 -1
  77. data/test/perf_strict.rb +18 -1
  78. data/test/sample.rb +0 -1
  79. data/test/test_compat.rb +169 -93
  80. data/test/test_custom.rb +355 -0
  81. data/test/test_file.rb +0 -8
  82. data/test/test_null.rb +376 -0
  83. data/test/test_object.rb +268 -3
  84. data/test/test_scp.rb +22 -1
  85. data/test/test_strict.rb +160 -4
  86. data/test/test_various.rb +52 -620
  87. data/test/tests.rb +14 -0
  88. data/test/tests_mimic.rb +14 -0
  89. data/test/tests_mimic_addition.rb +7 -0
  90. metadata +89 -47
  91. data/test/activesupport_datetime_test.rb +0 -23
  92. data/test/bug.rb +0 -51
  93. data/test/bug2.rb +0 -10
  94. data/test/bug3.rb +0 -46
  95. data/test/bug_fast.rb +0 -32
  96. data/test/bug_load.rb +0 -24
  97. data/test/crash.rb +0 -111
  98. data/test/curl/curl_oj.rb +0 -46
  99. data/test/curl/get_oj.rb +0 -24
  100. data/test/curl/just_curl.rb +0 -31
  101. data/test/curl/just_oj.rb +0 -51
  102. data/test/example.rb +0 -11
  103. data/test/foo.rb +0 -24
  104. data/test/io.rb +0 -48
  105. data/test/isolated/test_mimic_rails_datetime.rb +0 -27
  106. data/test/mod.rb +0 -16
  107. data/test/rails.rb +0 -50
  108. data/test/russian.rb +0 -18
  109. data/test/struct.rb +0 -29
  110. data/test/test_serializer.rb +0 -59
  111. data/test/write_timebars.rb +0 -31
@@ -0,0 +1,254 @@
1
+ /* dump_leaf.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include <errno.h>
7
+
8
+ #include "oj.h"
9
+ #include "dump.h"
10
+
11
+ static void dump_leaf(Leaf leaf, int depth, Out out);
12
+
13
+ extern void dump_cstr(const char *str, size_t cnt, int is_sym, int escape1, Out out);
14
+
15
+ static void
16
+ grow(Out out, size_t len) {
17
+ size_t size = out->end - out->buf;
18
+ long pos = out->cur - out->buf;
19
+ char *buf;
20
+
21
+ size *= 2;
22
+ if (size <= len * 2 + pos) {
23
+ size += len;
24
+ }
25
+ if (out->allocated) {
26
+ buf = REALLOC_N(out->buf, char, (size + BUFFER_EXTRA));
27
+ } else {
28
+ buf = ALLOC_N(char, (size + BUFFER_EXTRA));
29
+ out->allocated = 1;
30
+ memcpy(buf, out->buf, out->end - out->buf + BUFFER_EXTRA);
31
+ }
32
+ if (0 == buf) {
33
+ rb_raise(rb_eNoMemError, "Failed to create string. [%d:%s]\n", ENOSPC, strerror(ENOSPC));
34
+ }
35
+ out->buf = buf;
36
+ out->end = buf + size;
37
+ out->cur = out->buf + pos;
38
+ }
39
+
40
+
41
+ inline static void
42
+ dump_chars(const char *s, size_t size, Out out) {
43
+ if (out->end - out->cur <= (long)size) {
44
+ grow(out, size);
45
+ }
46
+ memcpy(out->cur, s, size);
47
+ out->cur += size;
48
+ *out->cur = '\0';
49
+ }
50
+
51
+ static void
52
+ dump_leaf_str(Leaf leaf, Out out) {
53
+ switch (leaf->value_type) {
54
+ case STR_VAL:
55
+ dump_cstr(leaf->str, strlen(leaf->str), 0, 0, out);
56
+ break;
57
+ case RUBY_VAL:
58
+ dump_cstr(rb_string_value_cstr(&leaf->value), RSTRING_LEN(leaf->value), 0, 0, out);
59
+ break;
60
+ case COL_VAL:
61
+ default:
62
+ rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
63
+ break;
64
+ }
65
+ }
66
+
67
+ static void
68
+ dump_leaf_fixnum(Leaf leaf, Out out) {
69
+ switch (leaf->value_type) {
70
+ case STR_VAL:
71
+ dump_chars(leaf->str, strlen(leaf->str), out);
72
+ break;
73
+ case RUBY_VAL:
74
+ if (T_BIGNUM == rb_type(leaf->value)) {
75
+ oj_dump_bignum(leaf->value, 0, out, false);
76
+ } else {
77
+ oj_dump_fixnum(leaf->value, 0, out, false);
78
+ }
79
+ break;
80
+ case COL_VAL:
81
+ default:
82
+ rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
83
+ break;
84
+ }
85
+ }
86
+
87
+ static void
88
+ dump_leaf_float(Leaf leaf, Out out) {
89
+ switch (leaf->value_type) {
90
+ case STR_VAL:
91
+ dump_chars(leaf->str, strlen(leaf->str), out);
92
+ break;
93
+ case RUBY_VAL:
94
+ oj_dump_float(leaf->value, 0, out, false);
95
+ break;
96
+ case COL_VAL:
97
+ default:
98
+ rb_raise(rb_eTypeError, "Unexpected value type %02x.\n", leaf->value_type);
99
+ break;
100
+ }
101
+ }
102
+
103
+ static void
104
+ dump_leaf_array(Leaf leaf, int depth, Out out) {
105
+ size_t size;
106
+ int d2 = depth + 1;
107
+
108
+ size = 2;
109
+ if (out->end - out->cur <= (long)size) {
110
+ grow(out, size);
111
+ }
112
+ *out->cur++ = '[';
113
+ if (0 == leaf->elements) {
114
+ *out->cur++ = ']';
115
+ } else {
116
+ Leaf first = leaf->elements->next;
117
+ Leaf e = first;
118
+
119
+ size = d2 * out->indent + 2;
120
+ do {
121
+ if (out->end - out->cur <= (long)size) {
122
+ grow(out, size);
123
+ }
124
+ fill_indent(out, d2);
125
+ dump_leaf(e, d2, out);
126
+ if (e->next != first) {
127
+ *out->cur++ = ',';
128
+ }
129
+ e = e->next;
130
+ } while (e != first);
131
+ size = depth * out->indent + 1;
132
+ if (out->end - out->cur <= (long)size) {
133
+ grow(out, size);
134
+ }
135
+ fill_indent(out, depth);
136
+ *out->cur++ = ']';
137
+ }
138
+ *out->cur = '\0';
139
+ }
140
+
141
+ static void
142
+ dump_leaf_hash(Leaf leaf, int depth, Out out) {
143
+ size_t size;
144
+ int d2 = depth + 1;
145
+
146
+ size = 2;
147
+ if (out->end - out->cur <= (long)size) {
148
+ grow(out, size);
149
+ }
150
+ *out->cur++ = '{';
151
+ if (0 == leaf->elements) {
152
+ *out->cur++ = '}';
153
+ } else {
154
+ Leaf first = leaf->elements->next;
155
+ Leaf e = first;
156
+
157
+ size = d2 * out->indent + 2;
158
+ do {
159
+ if (out->end - out->cur <= (long)size) {
160
+ grow(out, size);
161
+ }
162
+ fill_indent(out, d2);
163
+ dump_cstr(e->key, strlen(e->key), 0, 0, out);
164
+ *out->cur++ = ':';
165
+ dump_leaf(e, d2, out);
166
+ if (e->next != first) {
167
+ *out->cur++ = ',';
168
+ }
169
+ e = e->next;
170
+ } while (e != first);
171
+ size = depth * out->indent + 1;
172
+ if (out->end - out->cur <= (long)size) {
173
+ grow(out, size);
174
+ }
175
+ fill_indent(out, depth);
176
+ *out->cur++ = '}';
177
+ }
178
+ *out->cur = '\0';
179
+ }
180
+
181
+ static void
182
+ dump_leaf(Leaf leaf, int depth, Out out) {
183
+ switch (leaf->rtype) {
184
+ case T_NIL:
185
+ oj_dump_nil(Qnil, 0, out, false);
186
+ break;
187
+ case T_TRUE:
188
+ oj_dump_true(Qtrue, 0, out, false);
189
+ break;
190
+ case T_FALSE:
191
+ oj_dump_false(Qfalse, 0, out, false);
192
+ break;
193
+ case T_STRING:
194
+ dump_leaf_str(leaf, out);
195
+ break;
196
+ case T_FIXNUM:
197
+ dump_leaf_fixnum(leaf, out);
198
+ break;
199
+ case T_FLOAT:
200
+ dump_leaf_float(leaf, out);
201
+ break;
202
+ case T_ARRAY:
203
+ dump_leaf_array(leaf, depth, out);
204
+ break;
205
+ case T_HASH:
206
+ dump_leaf_hash(leaf, depth, out);
207
+ break;
208
+ default:
209
+ rb_raise(rb_eTypeError, "Unexpected type %02x.\n", leaf->rtype);
210
+ break;
211
+ }
212
+ }
213
+
214
+ void
215
+ oj_dump_leaf_to_json(Leaf leaf, Options copts, Out out) {
216
+ if (0 == out->buf) {
217
+ out->buf = ALLOC_N(char, 4096);
218
+ out->end = out->buf + 4095 - BUFFER_EXTRA; // 1 less than end plus extra for possible errors
219
+ out->allocated = 1;
220
+ }
221
+ out->cur = out->buf;
222
+ out->circ_cnt = 0;
223
+ out->opts = copts;
224
+ out->hash_cnt = 0;
225
+ out->indent = copts->indent;
226
+ dump_leaf(leaf, 0, out);
227
+ }
228
+
229
+ void
230
+ oj_write_leaf_to_file(Leaf leaf, const char *path, Options copts) {
231
+ char buf[4096];
232
+ struct _Out out;
233
+ size_t size;
234
+ FILE *f;
235
+
236
+ out.buf = buf;
237
+ out.end = buf + sizeof(buf) - BUFFER_EXTRA;
238
+ out.allocated = 0;
239
+ out.omit_nil = copts->dump_opts.omit_nil;
240
+ oj_dump_leaf_to_json(leaf, copts, &out);
241
+ size = out.cur - out.buf;
242
+ if (0 == (f = fopen(path, "w"))) {
243
+ rb_raise(rb_eIOError, "%s\n", strerror(errno));
244
+ }
245
+ if (size != fwrite(out.buf, 1, size, f)) {
246
+ int err = ferror(f);
247
+
248
+ rb_raise(rb_eIOError, "Write failed. [%d:%s]\n", err, strerror(err));
249
+ }
250
+ if (out.allocated) {
251
+ xfree(out.buf);
252
+ }
253
+ fclose(f);
254
+ }
@@ -0,0 +1,810 @@
1
+ /* dump_object.c
2
+ * Copyright (c) 2012, 2017, Peter Ohler
3
+ * All rights reserved.
4
+ */
5
+
6
+ #include "dump.h"
7
+ #include "odd.h"
8
+
9
+ static const char hex_chars[17] = "0123456789abcdef";
10
+
11
+ static void dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out);
12
+
13
+ static void
14
+ dump_time(VALUE obj, Out out) {
15
+ switch (out->opts->time_format) {
16
+ case RubyTime:
17
+ case XmlTime: oj_dump_xml_time(obj, out); break;
18
+ case UnixZTime: oj_dump_time(obj, out, 1); break;
19
+ case UnixTime:
20
+ default: oj_dump_time(obj, out, 0); break;
21
+ }
22
+ }
23
+
24
+ static void
25
+ dump_data(VALUE obj, int depth, Out out, bool as_ok) {
26
+ VALUE clas = rb_obj_class(obj);
27
+
28
+ if (rb_cTime == clas) {
29
+ assure_size(out, 6);
30
+ *out->cur++ = '{';
31
+ *out->cur++ = '"';
32
+ *out->cur++ = '^';
33
+ *out->cur++ = 't';
34
+ *out->cur++ = '"';
35
+ *out->cur++ = ':';
36
+ dump_time(obj, out);
37
+ *out->cur++ = '}';
38
+ *out->cur = '\0';
39
+ } else {
40
+ if (oj_bigdecimal_class == clas) {
41
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
42
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
43
+ int len = RSTRING_LEN(rstr);
44
+
45
+ if (Yes == out->opts->bigdec_as_num) {
46
+ oj_dump_raw(str, len, out);
47
+ } else if (0 == strcasecmp("Infinity", str)) {
48
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
49
+ oj_dump_raw(str, len, out);
50
+ } else if (0 == strcasecmp("-Infinity", str)) {
51
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
52
+ oj_dump_raw(str, len, out);
53
+ } else {
54
+ oj_dump_cstr(str, len, 0, 0, out);
55
+ }
56
+ } else {
57
+ long id = oj_check_circular(obj, out);
58
+
59
+ if (0 <= id) {
60
+ dump_obj_attrs(obj, clas, id, depth, out);
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ static void
67
+ dump_obj(VALUE obj, int depth, Out out, bool as_ok) {
68
+ VALUE clas = rb_obj_class(obj);
69
+
70
+ if (oj_bigdecimal_class == clas) {
71
+ volatile VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
72
+ const char *str = rb_string_value_ptr((VALUE*)&rstr);
73
+ int len = RSTRING_LEN(rstr);
74
+
75
+ if (0 == strcasecmp("Infinity", str)) {
76
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, true, &len);
77
+ oj_dump_raw(str, len, out);
78
+ } else if (0 == strcasecmp("-Infinity", str)) {
79
+ str = oj_nan_str(obj, out->opts->dump_opts.nan_dump, out->opts->mode, false, &len);
80
+ oj_dump_raw(str, len, out);
81
+ } else {
82
+ oj_dump_raw(str, len, out);
83
+ }
84
+ } else {
85
+ long id = oj_check_circular(obj, out);
86
+
87
+ if (0 <= id) {
88
+ dump_obj_attrs(obj, clas, id, depth, out);
89
+ }
90
+ }
91
+ }
92
+
93
+ static void
94
+ dump_class(VALUE obj, int depth, Out out, bool as_ok) {
95
+ const char *s = rb_class2name(obj);
96
+ size_t len = strlen(s);
97
+
98
+ assure_size(out, 6);
99
+ *out->cur++ = '{';
100
+ *out->cur++ = '"';
101
+ *out->cur++ = '^';
102
+ *out->cur++ = 'c';
103
+ *out->cur++ = '"';
104
+ *out->cur++ = ':';
105
+ oj_dump_cstr(s, len, 0, 0, out);
106
+ *out->cur++ = '}';
107
+ *out->cur = '\0';
108
+ }
109
+
110
+ static void
111
+ dump_array_class(VALUE a, VALUE clas, int depth, Out out) {
112
+ size_t size;
113
+ int i, cnt;
114
+ int d2 = depth + 1;
115
+ long id = oj_check_circular(a, out);
116
+
117
+ if (id < 0) {
118
+ return;
119
+ }
120
+ if (Qundef != clas && rb_cArray != clas && ObjectMode == out->opts->mode) {
121
+ dump_obj_attrs(a, clas, 0, depth, out);
122
+ return;
123
+ }
124
+ cnt = (int)RARRAY_LEN(a);
125
+ *out->cur++ = '[';
126
+ if (0 < id) {
127
+ assure_size(out, d2 * out->indent + 16);
128
+ fill_indent(out, d2);
129
+ *out->cur++ = '"';
130
+ *out->cur++ = '^';
131
+ *out->cur++ = 'i';
132
+ dump_ulong(id, out);
133
+ *out->cur++ = '"';
134
+ }
135
+ size = 2;
136
+ assure_size(out, 2);
137
+ if (0 == cnt) {
138
+ *out->cur++ = ']';
139
+ } else {
140
+ if (0 < id) {
141
+ *out->cur++ = ',';
142
+ }
143
+ if (out->opts->dump_opts.use) {
144
+ size = d2 * out->opts->dump_opts.indent_size + out->opts->dump_opts.array_size + 1;
145
+ } else {
146
+ size = d2 * out->indent + 2;
147
+ }
148
+ cnt--;
149
+ for (i = 0; i <= cnt; i++) {
150
+ assure_size(out, size);
151
+ if (out->opts->dump_opts.use) {
152
+ if (0 < out->opts->dump_opts.array_size) {
153
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
154
+ out->cur += out->opts->dump_opts.array_size;
155
+ }
156
+ if (0 < out->opts->dump_opts.indent_size) {
157
+ int i;
158
+ for (i = d2; 0 < i; i--) {
159
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
160
+ out->cur += out->opts->dump_opts.indent_size;
161
+ }
162
+ }
163
+ } else {
164
+ fill_indent(out, d2);
165
+ }
166
+ oj_dump_obj_val(rb_ary_entry(a, i), d2, out);
167
+ if (i < cnt) {
168
+ *out->cur++ = ',';
169
+ }
170
+ }
171
+ size = depth * out->indent + 1;
172
+ assure_size(out, size);
173
+ if (out->opts->dump_opts.use) {
174
+ //printf("*** d2: %u indent: %u '%s'\n", d2, out->opts->dump_opts->indent_size, out->opts->dump_opts->indent);
175
+ if (0 < out->opts->dump_opts.array_size) {
176
+ strcpy(out->cur, out->opts->dump_opts.array_nl);
177
+ out->cur += out->opts->dump_opts.array_size;
178
+ }
179
+ if (0 < out->opts->dump_opts.indent_size) {
180
+ int i;
181
+
182
+ for (i = depth; 0 < i; i--) {
183
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
184
+ out->cur += out->opts->dump_opts.indent_size;
185
+ }
186
+ }
187
+ } else {
188
+ fill_indent(out, depth);
189
+ }
190
+ *out->cur++ = ']';
191
+ }
192
+ *out->cur = '\0';
193
+ }
194
+
195
+ static void
196
+ dump_array(VALUE obj, int depth, Out out, bool as_ok) {
197
+ dump_array_class(obj, rb_obj_class(obj), depth, out);
198
+ }
199
+
200
+ static void
201
+ dump_str_class(VALUE obj, VALUE clas, int depth, Out out) {
202
+ if (Qundef != clas && rb_cString != clas) {
203
+ dump_obj_attrs(obj, clas, 0, depth, out);
204
+ } else {
205
+ const char *s = rb_string_value_ptr((VALUE*)&obj);
206
+ size_t len = RSTRING_LEN(obj);
207
+ char s1 = s[1];
208
+
209
+ oj_dump_cstr(s, len, 0, (':' == *s || ('^' == *s && ('r' == s1 || 'i' == s1))), out);
210
+ }
211
+ }
212
+
213
+ static void
214
+ dump_str(VALUE obj, int depth, Out out, bool as_ok) {
215
+ dump_str_class(obj, rb_obj_class(obj), depth, out);
216
+ }
217
+
218
+ static void
219
+ dump_sym(VALUE obj, int depth, Out out, bool as_ok) {
220
+ const char *sym = rb_id2name(SYM2ID(obj));
221
+
222
+ oj_dump_cstr(sym, strlen(sym), 1, 0, out);
223
+ }
224
+
225
+ static int
226
+ hash_cb(VALUE key, VALUE value, Out out) {
227
+ int depth = out->depth;
228
+ long size = depth * out->indent + 1;
229
+
230
+ if (out->omit_nil && Qnil == value) {
231
+ return ST_CONTINUE;
232
+ }
233
+ assure_size(out, size);
234
+ fill_indent(out, depth);
235
+ if (rb_type(key) == T_STRING) {
236
+ dump_str_class(key, Qundef, depth, out);
237
+ *out->cur++ = ':';
238
+ oj_dump_obj_val(value, depth, out);
239
+ } else if (rb_type(key) == T_SYMBOL) {
240
+ dump_sym(key, 0, out, false);
241
+ *out->cur++ = ':';
242
+ oj_dump_obj_val(value, depth, out);
243
+ } else {
244
+ int d2 = depth + 1;
245
+ long s2 = size + out->indent + 1;
246
+ int i;
247
+ int started = 0;
248
+ uint8_t b;
249
+
250
+ assure_size(out, s2 + 15);
251
+ *out->cur++ = '"';
252
+ *out->cur++ = '^';
253
+ *out->cur++ = '#';
254
+ out->hash_cnt++;
255
+ for (i = 28; 0 <= i; i -= 4) {
256
+ b = (uint8_t)((out->hash_cnt >> i) & 0x0000000F);
257
+ if ('\0' != b) {
258
+ started = 1;
259
+ }
260
+ if (started) {
261
+ *out->cur++ = hex_chars[b];
262
+ }
263
+ }
264
+ *out->cur++ = '"';
265
+ *out->cur++ = ':';
266
+ *out->cur++ = '[';
267
+ fill_indent(out, d2);
268
+ oj_dump_obj_val(key, d2, out);
269
+ assure_size(out, s2);
270
+ *out->cur++ = ',';
271
+ fill_indent(out, d2);
272
+ oj_dump_obj_val(value, d2, out);
273
+ assure_size(out, size);
274
+ fill_indent(out, depth);
275
+ *out->cur++ = ']';
276
+ }
277
+ out->depth = depth;
278
+ *out->cur++ = ',';
279
+
280
+ return ST_CONTINUE;
281
+ }
282
+
283
+ static void
284
+ dump_hash_class(VALUE obj, VALUE clas, int depth, Out out) {
285
+ int cnt;
286
+ size_t size;
287
+
288
+ if (Qundef != clas && rb_cHash != clas) {
289
+ dump_obj_attrs(obj, clas, 0, depth, out);
290
+ return;
291
+ }
292
+ cnt = (int)RHASH_SIZE(obj);
293
+ size = depth * out->indent + 2;
294
+ assure_size(out, 2);
295
+ if (0 == cnt) {
296
+ *out->cur++ = '{';
297
+ *out->cur++ = '}';
298
+ } else {
299
+ long id = oj_check_circular(obj, out);
300
+
301
+ if (0 > id) {
302
+ return;
303
+ }
304
+ *out->cur++ = '{';
305
+ if (0 < id) {
306
+ assure_size(out, size + 16);
307
+ fill_indent(out, depth + 1);
308
+ *out->cur++ = '"';
309
+ *out->cur++ = '^';
310
+ *out->cur++ = 'i';
311
+ *out->cur++ = '"';
312
+ *out->cur++ = ':';
313
+ dump_ulong(id, out);
314
+ *out->cur++ = ',';
315
+ }
316
+ out->depth = depth + 1;
317
+ rb_hash_foreach(obj, hash_cb, (VALUE)out);
318
+ if (',' == *(out->cur - 1)) {
319
+ out->cur--; // backup to overwrite last comma
320
+ }
321
+ if (!out->opts->dump_opts.use) {
322
+ assure_size(out, size);
323
+ fill_indent(out, depth);
324
+ } else {
325
+ size = depth * out->opts->dump_opts.indent_size + out->opts->dump_opts.hash_size + 1;
326
+ assure_size(out, size);
327
+ if (0 < out->opts->dump_opts.hash_size) {
328
+ strcpy(out->cur, out->opts->dump_opts.hash_nl);
329
+ out->cur += out->opts->dump_opts.hash_size;
330
+ }
331
+ if (0 < out->opts->dump_opts.indent_size) {
332
+ int i;
333
+
334
+ for (i = depth; 0 < i; i--) {
335
+ strcpy(out->cur, out->opts->dump_opts.indent_str);
336
+ out->cur += out->opts->dump_opts.indent_size;
337
+ }
338
+ }
339
+ }
340
+ *out->cur++ = '}';
341
+ }
342
+ *out->cur = '\0';
343
+ }
344
+
345
+ #if HAS_IVAR_HELPERS
346
+ static int
347
+ dump_attr_cb(ID key, VALUE value, Out out) {
348
+ int depth = out->depth;
349
+ size_t size = depth * out->indent + 1;
350
+ const char *attr = rb_id2name(key);
351
+
352
+ if (out->omit_nil && Qnil == value) {
353
+ return ST_CONTINUE;
354
+ }
355
+ #if HAS_EXCEPTION_MAGIC
356
+ if (0 == strcmp("bt", attr) || 0 == strcmp("mesg", attr)) {
357
+ return ST_CONTINUE;
358
+ }
359
+ #endif
360
+ assure_size(out, size);
361
+ fill_indent(out, depth);
362
+ if ('@' == *attr) {
363
+ attr++;
364
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
365
+ } else {
366
+ char buf[32];
367
+
368
+ *buf = '~';
369
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
370
+ buf[sizeof(buf) - 1] = '\0';
371
+ oj_dump_cstr(buf, strlen(buf), 0, 0, out);
372
+ }
373
+ *out->cur++ = ':';
374
+ oj_dump_obj_val(value, depth, out);
375
+ out->depth = depth;
376
+ *out->cur++ = ',';
377
+
378
+ return ST_CONTINUE;
379
+ }
380
+ #endif
381
+
382
+ static void
383
+ dump_hash(VALUE obj, int depth, Out out, bool as_ok) {
384
+ dump_hash_class(obj, rb_obj_class(obj), depth, out);
385
+ }
386
+
387
+ static void
388
+ dump_odd(VALUE obj, Odd odd, VALUE clas, int depth, Out out) {
389
+ ID *idp;
390
+ AttrGetFunc *fp;
391
+ volatile VALUE v;
392
+ const char *name;
393
+ size_t size;
394
+ int d2 = depth + 1;
395
+
396
+ assure_size(out, 2);
397
+ *out->cur++ = '{';
398
+ if (Qundef != clas) {
399
+ const char *class_name = rb_class2name(clas);
400
+ int clen = (int)strlen(class_name);
401
+
402
+ size = d2 * out->indent + clen + 10;
403
+ assure_size(out, size);
404
+ fill_indent(out, d2);
405
+ *out->cur++ = '"';
406
+ *out->cur++ = '^';
407
+ *out->cur++ = 'O';
408
+ *out->cur++ = '"';
409
+ *out->cur++ = ':';
410
+ oj_dump_cstr(class_name, clen, 0, 0, out);
411
+ *out->cur++ = ',';
412
+ }
413
+ if (odd->raw) {
414
+ v = rb_funcall(obj, *odd->attrs, 0);
415
+ if (Qundef == v || T_STRING != rb_type(v)) {
416
+ rb_raise(rb_eEncodingError, "Invalid type for raw JSON.\n");
417
+ } else {
418
+ const char *s = rb_string_value_ptr((VALUE*)&v);
419
+ int len = RSTRING_LEN(v);
420
+ const char *name = rb_id2name(*odd->attrs);
421
+ size_t nlen = strlen(name);
422
+
423
+ size = len + d2 * out->indent + nlen + 10;
424
+ assure_size(out, size);
425
+ fill_indent(out, d2);
426
+ *out->cur++ = '"';
427
+ memcpy(out->cur, name, nlen);
428
+ out->cur += nlen;
429
+ *out->cur++ = '"';
430
+ *out->cur++ = ':';
431
+ memcpy(out->cur, s, len);
432
+ out->cur += len;
433
+ *out->cur = '\0';
434
+ }
435
+ } else {
436
+ size = d2 * out->indent + 1;
437
+ for (idp = odd->attrs, fp = odd->attrFuncs; 0 != *idp; idp++, fp++) {
438
+ size_t nlen;
439
+
440
+ assure_size(out, size);
441
+ name = rb_id2name(*idp);
442
+ nlen = strlen(name);
443
+ if (0 != *fp) {
444
+ v = (*fp)(obj);
445
+ } else if (0 == strchr(name, '.')) {
446
+ v = rb_funcall(obj, *idp, 0);
447
+ } else {
448
+ char nbuf[256];
449
+ char *n2 = nbuf;
450
+ char *n;
451
+ char *end;
452
+ ID i;
453
+
454
+ if (sizeof(nbuf) <= nlen) {
455
+ n2 = strdup(name);
456
+ } else {
457
+ strcpy(n2, name);
458
+ }
459
+ n = n2;
460
+ v = obj;
461
+ while (0 != (end = strchr(n, '.'))) {
462
+ *end = '\0';
463
+ i = rb_intern(n);
464
+ v = rb_funcall(v, i, 0);
465
+ n = end + 1;
466
+ }
467
+ i = rb_intern(n);
468
+ v = rb_funcall(v, i, 0);
469
+ if (nbuf != n2) {
470
+ free(n2);
471
+ }
472
+ }
473
+ fill_indent(out, d2);
474
+ oj_dump_cstr(name, nlen, 0, 0, out);
475
+ *out->cur++ = ':';
476
+ oj_dump_obj_val(v, d2, out);
477
+ assure_size(out, 2);
478
+ *out->cur++ = ',';
479
+ }
480
+ out->cur--;
481
+ }
482
+ *out->cur++ = '}';
483
+ *out->cur = '\0';
484
+ }
485
+
486
+ static void
487
+ dump_obj_attrs(VALUE obj, VALUE clas, slot_t id, int depth, Out out) {
488
+ size_t size = 0;
489
+ int d2 = depth + 1;
490
+ int type = rb_type(obj);
491
+ Odd odd;
492
+
493
+ if (0 != (odd = oj_get_odd(clas))) {
494
+ dump_odd(obj, odd, clas, depth + 1, out);
495
+ return;
496
+ }
497
+ assure_size(out, 2);
498
+ *out->cur++ = '{';
499
+ if (Qundef != clas) {
500
+ const char *class_name = rb_class2name(clas);
501
+ int clen = (int)strlen(class_name);
502
+
503
+ assure_size(out, d2 * out->indent + clen + 10);
504
+ fill_indent(out, d2);
505
+ *out->cur++ = '"';
506
+ *out->cur++ = '^';
507
+ *out->cur++ = 'o';
508
+ *out->cur++ = '"';
509
+ *out->cur++ = ':';
510
+ oj_dump_cstr(class_name, clen, 0, 0, out);
511
+ }
512
+ if (0 < id) {
513
+ assure_size(out, d2 * out->indent + 16);
514
+ *out->cur++ = ',';
515
+ fill_indent(out, d2);
516
+ *out->cur++ = '"';
517
+ *out->cur++ = '^';
518
+ *out->cur++ = 'i';
519
+ *out->cur++ = '"';
520
+ *out->cur++ = ':';
521
+ dump_ulong(id, out);
522
+ }
523
+ switch (type) {
524
+ case T_STRING:
525
+ assure_size(out, d2 * out->indent + 14);
526
+ *out->cur++ = ',';
527
+ fill_indent(out, d2);
528
+ *out->cur++ = '"';
529
+ *out->cur++ = 's';
530
+ *out->cur++ = 'e';
531
+ *out->cur++ = 'l';
532
+ *out->cur++ = 'f';
533
+ *out->cur++ = '"';
534
+ *out->cur++ = ':';
535
+ oj_dump_cstr(rb_string_value_ptr((VALUE*)&obj), RSTRING_LEN(obj), 0, 0, out);
536
+ break;
537
+ case T_ARRAY:
538
+ assure_size(out, d2 * out->indent + 14);
539
+ *out->cur++ = ',';
540
+ fill_indent(out, d2);
541
+ *out->cur++ = '"';
542
+ *out->cur++ = 's';
543
+ *out->cur++ = 'e';
544
+ *out->cur++ = 'l';
545
+ *out->cur++ = 'f';
546
+ *out->cur++ = '"';
547
+ *out->cur++ = ':';
548
+ dump_array_class(obj, Qundef, depth + 1, out);
549
+ break;
550
+ case T_HASH:
551
+ assure_size(out, d2 * out->indent + 14);
552
+ *out->cur++ = ',';
553
+ fill_indent(out, d2);
554
+ *out->cur++ = '"';
555
+ *out->cur++ = 's';
556
+ *out->cur++ = 'e';
557
+ *out->cur++ = 'l';
558
+ *out->cur++ = 'f';
559
+ *out->cur++ = '"';
560
+ *out->cur++ = ':';
561
+ dump_hash_class(obj, Qundef, depth + 1, out);
562
+ break;
563
+ default:
564
+ break;
565
+ }
566
+ {
567
+ int cnt;
568
+ #if HAS_IVAR_HELPERS
569
+ cnt = (int)rb_ivar_count(obj);
570
+ #else
571
+ volatile VALUE vars = rb_funcall2(obj, oj_instance_variables_id, 0, 0);
572
+ VALUE *np = RARRAY_PTR(vars);
573
+ ID vid;
574
+ const char *attr;
575
+ int i;
576
+ int first = 1;
577
+
578
+ cnt = (int)RARRAY_LEN(vars);
579
+ #endif
580
+ if (Qundef != clas && 0 < cnt) {
581
+ *out->cur++ = ',';
582
+ }
583
+ if (0 == cnt && Qundef == clas) {
584
+ // Might be something special like an Enumerable.
585
+ if (Qtrue == rb_obj_is_kind_of(obj, oj_enumerable_class)) {
586
+ out->cur--;
587
+ oj_dump_obj_val(rb_funcall(obj, rb_intern("entries"), 0), depth, out);
588
+ return;
589
+ }
590
+ }
591
+ out->depth = depth + 1;
592
+ #if HAS_IVAR_HELPERS
593
+ rb_ivar_foreach(obj, dump_attr_cb, (VALUE)out);
594
+ if (',' == *(out->cur - 1)) {
595
+ out->cur--; // backup to overwrite last comma
596
+ }
597
+ #else
598
+ size = d2 * out->indent + 1;
599
+ for (i = cnt; 0 < i; i--, np++) {
600
+ VALUE value;
601
+
602
+ vid = rb_to_id(*np);
603
+ attr = rb_id2name(vid);
604
+ value = rb_ivar_get(obj, vid);
605
+ if (out->omit_nil && Qnil == value) {
606
+ continue;
607
+ }
608
+ if (first) {
609
+ first = 0;
610
+ } else {
611
+ *out->cur++ = ',';
612
+ }
613
+ assure_size(out, size);
614
+ fill_indent(out, d2);
615
+ if ('@' == *attr) {
616
+ attr++;
617
+ oj_dump_cstr(attr, strlen(attr), 0, 0, out);
618
+ } else {
619
+ char buf[32];
620
+
621
+ *buf = '~';
622
+ strncpy(buf + 1, attr, sizeof(buf) - 2);
623
+ buf[sizeof(buf) - 1] = '\0';
624
+ oj_dump_cstr(buf, strlen(attr) + 1, 0, 0, out);
625
+ }
626
+ *out->cur++ = ':';
627
+ oj_dump_obj_val(value, d2, out, 0, 0, true);
628
+ assure_size(out, 2);
629
+ }
630
+ #endif
631
+ #if HAS_EXCEPTION_MAGIC
632
+ if (rb_obj_is_kind_of(obj, rb_eException)) {
633
+ volatile VALUE rv;
634
+
635
+ if (',' != *(out->cur - 1)) {
636
+ *out->cur++ = ',';
637
+ }
638
+ // message
639
+ assure_size(out, size);
640
+ fill_indent(out, d2);
641
+ oj_dump_cstr("~mesg", 5, 0, 0, out);
642
+ *out->cur++ = ':';
643
+ rv = rb_funcall2(obj, rb_intern("message"), 0, 0);
644
+ oj_dump_obj_val(rv, d2, out);
645
+ assure_size(out, 2);
646
+ *out->cur++ = ',';
647
+ // backtrace
648
+ assure_size(out, size);
649
+ fill_indent(out, d2);
650
+ oj_dump_cstr("~bt", 3, 0, 0, out);
651
+ *out->cur++ = ':';
652
+ rv = rb_funcall2(obj, rb_intern("backtrace"), 0, 0);
653
+ oj_dump_obj_val(rv, d2, out);
654
+ assure_size(out, 2);
655
+ }
656
+ #endif
657
+ out->depth = depth;
658
+ }
659
+ fill_indent(out, depth);
660
+ *out->cur++ = '}';
661
+ *out->cur = '\0';
662
+ }
663
+
664
+ static void
665
+ dump_regexp(VALUE obj, int depth, Out out, bool as_ok) {
666
+ dump_obj_attrs(obj, rb_obj_class(obj), 0, depth, out);
667
+ }
668
+
669
+ static void
670
+ dump_struct(VALUE obj, int depth, Out out, bool as_ok) {
671
+ VALUE clas = rb_obj_class(obj);
672
+ const char *class_name = rb_class2name(clas);
673
+ int i;
674
+ int d2 = depth + 1;
675
+ int d3 = d2 + 1;
676
+ size_t len = strlen(class_name);
677
+ size_t size = d2 * out->indent + d3 * out->indent + 10 + len;
678
+
679
+ assure_size(out, size);
680
+ *out->cur++ = '{';
681
+ fill_indent(out, d2);
682
+ *out->cur++ = '"';
683
+ *out->cur++ = '^';
684
+ *out->cur++ = 'u';
685
+ *out->cur++ = '"';
686
+ *out->cur++ = ':';
687
+ *out->cur++ = '[';
688
+ #if HAS_STRUCT_MEMBERS
689
+ if ('#' == *class_name) {
690
+ VALUE ma = rb_struct_s_members(clas);
691
+ const char *name;
692
+ int cnt = (int)RARRAY_LEN(ma);
693
+
694
+ *out->cur++ = '[';
695
+ for (i = 0; i < cnt; i++) {
696
+ name = rb_id2name(SYM2ID(rb_ary_entry(ma, i)));
697
+ len = strlen(name);
698
+ size = len + 3;
699
+ assure_size(out, size);
700
+ if (0 < i) {
701
+ *out->cur++ = ',';
702
+ }
703
+ *out->cur++ = '"';
704
+ memcpy(out->cur, name, len);
705
+ out->cur += len;
706
+ *out->cur++ = '"';
707
+ }
708
+ *out->cur++ = ']';
709
+ } else {
710
+ #else
711
+ if (true) {
712
+ #endif
713
+ fill_indent(out, d3);
714
+ *out->cur++ = '"';
715
+ memcpy(out->cur, class_name, len);
716
+ out->cur += len;
717
+ *out->cur++ = '"';
718
+ }
719
+ *out->cur++ = ',';
720
+ size = d3 * out->indent + 2;
721
+ #ifdef RSTRUCT_LEN
722
+ {
723
+ VALUE v;
724
+ int cnt;
725
+ #if UNIFY_FIXNUM_AND_BIGNUM
726
+ cnt = (int)NUM2LONG(RSTRUCT_LEN(obj));
727
+ #else // UNIFY_FIXNUM_AND_INTEGER
728
+ cnt = (int)RSTRUCT_LEN(obj);
729
+ #endif // UNIFY_FIXNUM_AND_INTEGER
730
+
731
+ for (i = 0; i < cnt; i++) {
732
+ v = RSTRUCT_GET(obj, i);
733
+ assure_size(out, size);
734
+ fill_indent(out, d3);
735
+ oj_dump_obj_val(v, d3, out);
736
+ *out->cur++ = ',';
737
+ }
738
+ }
739
+ #else
740
+ {
741
+ // This is a bit risky as a struct in C ruby is not the same as a Struct
742
+ // class in interpreted Ruby so length() may not be defined.
743
+ int slen = FIX2INT(rb_funcall2(obj, oj_length_id, 0, 0));
744
+
745
+ for (i = 0; i < slen; i++) {
746
+ assure_size(out, size);
747
+ fill_indent(out, d3);
748
+ oj_dump_obj_val(rb_struct_aref(obj, INT2FIX(i)), d3, out, 0, 0, true);
749
+ *out->cur++ = ',';
750
+ }
751
+ }
752
+ #endif
753
+ out->cur--;
754
+ *out->cur++ = ']';
755
+ *out->cur++ = '}';
756
+ *out->cur = '\0';
757
+ }
758
+
759
+ static void
760
+ dump_complex(VALUE obj, int depth, Out out, bool as_ok) {
761
+ dump_obj_attrs(obj, rb_obj_class(obj), 0, depth, out);
762
+ }
763
+
764
+ static void
765
+ dump_rational(VALUE obj, int depth, Out out, bool as_ok) {
766
+ dump_obj_attrs(obj, rb_obj_class(obj), 0, depth, out);
767
+ }
768
+
769
+ static DumpFunc obj_funcs[] = {
770
+ NULL, // RUBY_T_NONE = 0x00,
771
+ dump_obj, // RUBY_T_OBJECT = 0x01,
772
+ dump_class, // RUBY_T_CLASS = 0x02,
773
+ dump_class, // RUBY_T_MODULE = 0x03,
774
+ oj_dump_float, // RUBY_T_FLOAT = 0x04,
775
+ dump_str, // RUBY_T_STRING = 0x05,
776
+ dump_regexp, // RUBY_T_REGEXP = 0x06,
777
+ dump_array, // RUBY_T_ARRAY = 0x07,
778
+ dump_hash, // RUBY_T_HASH = 0x08,
779
+ dump_struct, // RUBY_T_STRUCT = 0x09,
780
+ oj_dump_bignum, // RUBY_T_BIGNUM = 0x0a,
781
+ NULL, // RUBY_T_FILE = 0x0b,
782
+ dump_data, // RUBY_T_DATA = 0x0c,
783
+ NULL, // RUBY_T_MATCH = 0x0d,
784
+ dump_complex, // RUBY_T_COMPLEX = 0x0e,
785
+ dump_rational, // RUBY_T_RATIONAL = 0x0f,
786
+ NULL, // 0x10
787
+ oj_dump_nil, // RUBY_T_NIL = 0x11,
788
+ oj_dump_true, // RUBY_T_TRUE = 0x12,
789
+ oj_dump_false, // RUBY_T_FALSE = 0x13,
790
+ dump_sym, // RUBY_T_SYMBOL = 0x14,
791
+ oj_dump_fixnum, // RUBY_T_FIXNUM = 0x15,
792
+ };
793
+
794
+ void
795
+ oj_dump_obj_val(VALUE obj, int depth, Out out) {
796
+ int type = rb_type(obj);
797
+
798
+ if (MAX_DEPTH < depth) {
799
+ rb_raise(rb_eNoMemError, "Too deeply nested.\n");
800
+ }
801
+ if (0 < type && type <= RUBY_T_FIXNUM) {
802
+ DumpFunc f = obj_funcs[type];
803
+
804
+ if (NULL != f) {
805
+ f(obj, depth, out, false);
806
+ return;
807
+ }
808
+ }
809
+ oj_dump_nil(Qnil, depth, out, false);
810
+ }