oj 3.12.3 → 3.13.3

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -3
  3. data/ext/oj/buf.h +9 -0
  4. data/ext/oj/cache.c +341 -0
  5. data/ext/oj/cache.h +21 -0
  6. data/ext/oj/compat.c +7 -22
  7. data/ext/oj/custom.c +15 -17
  8. data/ext/oj/debug.c +132 -0
  9. data/ext/oj/dump.c +12 -15
  10. data/ext/oj/dump_compat.c +3 -3
  11. data/ext/oj/dump_object.c +9 -9
  12. data/ext/oj/dump_strict.c +3 -3
  13. data/ext/oj/err.h +19 -0
  14. data/ext/oj/extconf.rb +5 -0
  15. data/ext/oj/fast.c +7 -18
  16. data/ext/oj/intern.c +281 -0
  17. data/ext/oj/intern.h +26 -0
  18. data/ext/oj/mimic_json.c +2 -2
  19. data/ext/oj/object.c +15 -92
  20. data/ext/oj/odd.c +1 -1
  21. data/ext/oj/oj.c +117 -94
  22. data/ext/oj/oj.h +1 -1
  23. data/ext/oj/parse.c +5 -5
  24. data/ext/oj/parser.c +1483 -0
  25. data/ext/oj/parser.h +90 -0
  26. data/ext/oj/rails.c +5 -5
  27. data/ext/oj/resolve.c +2 -20
  28. data/ext/oj/rxclass.c +1 -1
  29. data/ext/oj/saj.c +1 -1
  30. data/ext/oj/saj2.c +348 -0
  31. data/ext/oj/scp.c +1 -1
  32. data/ext/oj/sparse.c +2 -2
  33. data/ext/oj/stream_writer.c +4 -4
  34. data/ext/oj/strict.c +9 -27
  35. data/ext/oj/string_writer.c +2 -2
  36. data/ext/oj/usual.c +1252 -0
  37. data/ext/oj/validate.c +51 -0
  38. data/ext/oj/wab.c +14 -19
  39. data/lib/oj/error.rb +1 -1
  40. data/lib/oj/state.rb +8 -7
  41. data/lib/oj/version.rb +1 -1
  42. data/pages/Options.md +1 -1
  43. data/pages/Parser.md +309 -0
  44. data/pages/Rails.md +2 -2
  45. data/test/json_gem/json_generator_test.rb +1 -1
  46. data/test/mem.rb +33 -0
  47. data/test/perf_once.rb +58 -0
  48. data/test/perf_parser.rb +189 -0
  49. data/test/test_hash.rb +1 -1
  50. data/test/test_parser.rb +27 -0
  51. data/test/test_parser_saj.rb +245 -0
  52. data/test/test_parser_usual.rb +213 -0
  53. metadata +26 -5
  54. data/ext/oj/hash.c +0 -168
  55. data/ext/oj/hash.h +0 -21
  56. data/ext/oj/hash_test.c +0 -491
data/ext/oj/extconf.rb CHANGED
@@ -30,6 +30,11 @@ have_func('rb_ivar_foreach')
30
30
  have_func('rb_gc_mark_movable')
31
31
  have_func('stpcpy')
32
32
  have_func('pthread_mutex_init')
33
+ have_func('rb_enc_associate')
34
+ have_func('rb_enc_interned_str')
35
+ have_func('rb_ext_ractor_safe', 'ruby.h')
36
+ # rb_hash_bulk_insert is deep down in a header not included in normal build and that seems to fool have_func.
37
+ have_func('rb_hash_bulk_insert', 'ruby.h') unless '2' == version[0] && '6' == version[1]
33
38
 
34
39
  dflags['OJ_DEBUG'] = true unless ENV['OJ_DEBUG'].nil?
35
40
 
data/ext/oj/fast.c CHANGED
@@ -200,9 +200,7 @@ static VALUE leaf_value(Doc doc, Leaf leaf) {
200
200
  break;
201
201
  case T_ARRAY: return leaf_array_value(doc, leaf); break;
202
202
  case T_HASH: return leaf_hash_value(doc, leaf); break;
203
- default:
204
- rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype);
205
- break;
203
+ default: rb_raise(rb_const_get_at(Oj, rb_intern("Error")), "Unexpected type %02x.", leaf->rtype); break;
206
204
  }
207
205
  }
208
206
  return leaf->value;
@@ -773,7 +771,7 @@ static VALUE parse_json(VALUE clas, char *json, bool given, bool allocated) {
773
771
  pi.doc = doc;
774
772
  #if IS_WINDOWS
775
773
  // assume a 1M stack and give half to ruby
776
- pi.stack_min = (void *)((char *)&pi - (512 * 1024));
774
+ pi.stack_min = (void*)((char*)&pi - (512 * 1024));
777
775
  #else
778
776
  {
779
777
  struct rlimit lim;
@@ -825,9 +823,7 @@ static Leaf get_doc_leaf(Doc doc, const char *path) {
825
823
  size_t cnt = doc->where - doc->where_path;
826
824
 
827
825
  if (MAX_STACK <= cnt) {
828
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
829
- "Path too deep. Limit is %d levels.",
830
- MAX_STACK);
826
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
831
827
  }
832
828
  memcpy(stack, doc->where_path, sizeof(Leaf) * (cnt + 1));
833
829
  lp = stack + cnt;
@@ -868,9 +864,7 @@ static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
868
864
  Leaf leaf = *lp;
869
865
 
870
866
  if (MAX_STACK <= lp - stack) {
871
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
872
- "Path too deep. Limit is %d levels.",
873
- MAX_STACK);
867
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
874
868
  }
875
869
  if ('\0' != *path) {
876
870
  if ('.' == *path && '.' == *(path + 1)) {
@@ -946,9 +940,7 @@ static void each_leaf(Doc doc, VALUE self) {
946
940
 
947
941
  doc->where++;
948
942
  if (MAX_STACK <= doc->where - doc->where_path) {
949
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
950
- "Path too deep. Limit is %d levels.",
951
- MAX_STACK);
943
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
952
944
  }
953
945
  do {
954
946
  *doc->where = e;
@@ -964,9 +956,7 @@ static void each_leaf(Doc doc, VALUE self) {
964
956
 
965
957
  static int move_step(Doc doc, const char *path, int loc) {
966
958
  if (MAX_STACK <= doc->where - doc->where_path) {
967
- rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")),
968
- "Path too deep. Limit is %d levels.",
969
- MAX_STACK);
959
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
970
960
  }
971
961
  if ('\0' == *path) {
972
962
  loc = 0;
@@ -1264,7 +1254,6 @@ static VALUE doc_path(VALUE self) {
1264
1254
  return doc_where(self);
1265
1255
  }
1266
1256
 
1267
-
1268
1257
  /* @overload local_key() => String, Fixnum, nil
1269
1258
  *
1270
1259
  * Returns the final key to the current location.
@@ -1483,7 +1472,7 @@ static VALUE doc_move(VALUE self, VALUE str) {
1483
1472
  * to the block on yield is the Doc instance after moving to the child
1484
1473
  * location.
1485
1474
  * @param [String] path if provided it identified the top of the branch to
1486
- * process the chilren of
1475
+ * process the children of
1487
1476
  * @yieldparam [Doc] Doc at the child location
1488
1477
  * @example
1489
1478
  * Oj::Doc.open('[3,[2,1]]') { |doc|
data/ext/oj/intern.c ADDED
@@ -0,0 +1,281 @@
1
+ // Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #include "intern.h"
5
+
6
+ #include <stdint.h>
7
+
8
+ #if HAVE_PTHREAD_MUTEX_INIT
9
+ #include <pthread.h>
10
+ #endif
11
+ #include "cache.h"
12
+ #include "parse.h"
13
+
14
+ // Only used for the class cache so 256 should be sufficient.
15
+ #define HASH_SLOT_CNT ((uint64_t)256)
16
+ #define HASH_MASK (HASH_SLOT_CNT - 1)
17
+
18
+ // almost the Murmur hash algorithm
19
+ #define M 0x5bd1e995
20
+
21
+ typedef struct _keyVal {
22
+ struct _keyVal *next;
23
+ const char * key;
24
+ size_t len;
25
+ VALUE val;
26
+ } * KeyVal;
27
+
28
+ typedef struct _hash {
29
+ struct _keyVal slots[HASH_SLOT_CNT];
30
+ #if HAVE_PTHREAD_MUTEX_INIT
31
+ pthread_mutex_t mutex;
32
+ #else
33
+ VALUE mutex;
34
+ #endif
35
+ } * Hash;
36
+
37
+ struct _hash class_hash;
38
+ struct _hash attr_hash;
39
+
40
+ static struct _cache *str_cache = NULL;
41
+ static VALUE str_cache_obj;
42
+
43
+ static struct _cache *sym_cache = NULL;
44
+ static VALUE sym_cache_obj;
45
+
46
+ static struct _cache *attr_cache = NULL;
47
+ static VALUE attr_cache_obj;
48
+
49
+ static VALUE form_str(const char *str, size_t len) {
50
+ return rb_str_freeze(rb_utf8_str_new(str, len));
51
+ }
52
+
53
+ static VALUE form_sym(const char *str, size_t len) {
54
+ return rb_str_intern(rb_utf8_str_new(str, len));
55
+ }
56
+
57
+ static VALUE form_attr(const char *str, size_t len) {
58
+ char buf[256];
59
+
60
+ if (sizeof(buf) - 2 <= len) {
61
+ char *b = ALLOC_N(char, len + 2);
62
+ ID id;
63
+
64
+ if ('~' == *str) {
65
+ memcpy(b, str + 1, len - 1);
66
+ b[len - 1] = '\0';
67
+ len -= 2;
68
+ } else {
69
+ *b = '@';
70
+ memcpy(b + 1, str, len);
71
+ b[len + 1] = '\0';
72
+ }
73
+ id = rb_intern3(buf, len + 1, oj_utf8_encoding);
74
+ xfree(b);
75
+ return id;
76
+ }
77
+ if ('~' == *str) {
78
+ memcpy(buf, str + 1, len - 1);
79
+ buf[len - 1] = '\0';
80
+ len -= 2;
81
+ } else {
82
+ *buf = '@';
83
+ memcpy(buf + 1, str, len);
84
+ buf[len + 1] = '\0';
85
+ }
86
+ return (VALUE)rb_intern3(buf, len + 1, oj_utf8_encoding);
87
+ }
88
+
89
+ void oj_hash_init() {
90
+ VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
91
+
92
+ str_cache = cache_create(0, form_str, true, true);
93
+ str_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, str_cache);
94
+ rb_gc_register_address(&str_cache_obj);
95
+
96
+ sym_cache = cache_create(0, form_sym, true, true);
97
+ sym_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, sym_cache);
98
+ rb_gc_register_address(&sym_cache_obj);
99
+
100
+ attr_cache = cache_create(0, form_attr, false, true);
101
+ attr_cache_obj = Data_Wrap_Struct(cache_class, cache_mark, cache_free, attr_cache);
102
+ rb_gc_register_address(&attr_cache_obj);
103
+
104
+ memset(class_hash.slots, 0, sizeof(class_hash.slots));
105
+ #if HAVE_PTHREAD_MUTEX_INIT
106
+ pthread_mutex_init(&class_hash.mutex, NULL);
107
+ #else
108
+ class_hash.mutex = rb_mutex_new();
109
+ rb_gc_register_address(&class_hash.mutex);
110
+ #endif
111
+ }
112
+
113
+ VALUE
114
+ oj_str_intern(const char *key, size_t len) {
115
+ return cache_intern(str_cache, key, len);
116
+ }
117
+
118
+ VALUE
119
+ oj_sym_intern(const char *key, size_t len) {
120
+ return cache_intern(sym_cache, key, len);
121
+ }
122
+
123
+ ID
124
+ oj_attr_intern(const char *key, size_t len) {
125
+ return cache_intern(attr_cache, key, len);
126
+ }
127
+
128
+ static uint64_t hash_calc(const uint8_t *key, size_t len) {
129
+ const uint8_t *end = key + len;
130
+ const uint8_t *endless = key + (len & 0xFFFFFFFC);
131
+ uint64_t h = (uint64_t)len;
132
+ uint64_t k;
133
+
134
+ while (key < endless) {
135
+ k = (uint64_t)*key++;
136
+ k |= (uint64_t)*key++ << 8;
137
+ k |= (uint64_t)*key++ << 16;
138
+ k |= (uint64_t)*key++ << 24;
139
+
140
+ k *= M;
141
+ k ^= k >> 24;
142
+ h *= M;
143
+ h ^= k * M;
144
+ }
145
+ if (1 < end - key) {
146
+ uint16_t k16 = (uint16_t)*key++;
147
+
148
+ k16 |= (uint16_t)*key++ << 8;
149
+ h ^= k16 << 8;
150
+ }
151
+ if (key < end) {
152
+ h ^= *key;
153
+ }
154
+ h *= M;
155
+ h ^= h >> 13;
156
+ h *= M;
157
+ h ^= h >> 15;
158
+
159
+ return h;
160
+ }
161
+
162
+ static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define) {
163
+ VALUE clas;
164
+ ID ci = rb_intern(classname);
165
+
166
+ if (rb_const_defined_at(mod, ci)) {
167
+ clas = rb_const_get_at(mod, ci);
168
+ } else if (auto_define) {
169
+ clas = rb_define_class_under(mod, classname, oj_bag_class);
170
+ } else {
171
+ clas = Qundef;
172
+ }
173
+ return clas;
174
+ }
175
+
176
+ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
177
+ char class_name[1024];
178
+ VALUE clas;
179
+ char * end = class_name + sizeof(class_name) - 1;
180
+ char * s;
181
+ const char *n = name;
182
+
183
+ clas = rb_cObject;
184
+ for (s = class_name; 0 < len; n++, len--) {
185
+ if (':' == *n) {
186
+ *s = '\0';
187
+ n++;
188
+ len--;
189
+ if (':' != *n) {
190
+ return Qundef;
191
+ }
192
+ if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
193
+ return Qundef;
194
+ }
195
+ s = class_name;
196
+ } else if (end <= s) {
197
+ return Qundef;
198
+ } else {
199
+ *s++ = *n;
200
+ }
201
+ }
202
+ *s = '\0';
203
+ if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
204
+ oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class %s is not defined", name);
205
+ if (Qnil != error_class) {
206
+ pi->err_class = error_class;
207
+ }
208
+ }
209
+ return clas;
210
+ }
211
+
212
+ VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int auto_define, VALUE error_class) {
213
+ uint64_t h = hash_calc((const uint8_t *)key, len) & HASH_MASK;
214
+ KeyVal bucket = class_hash.slots + h;
215
+ KeyVal b;
216
+
217
+ if (safe) {
218
+ #if HAVE_PTHREAD_MUTEX_INIT
219
+ pthread_mutex_lock(&class_hash.mutex);
220
+ #else
221
+ rb_mutex_lock(class_hash.mutex);
222
+ #endif
223
+ if (NULL != bucket->key) { // not the top slot
224
+ for (b = bucket; 0 != b; b = b->next) {
225
+ if (len == b->len && 0 == strncmp(b->key, key, len)) {
226
+ #if HAVE_PTHREAD_MUTEX_INIT
227
+ pthread_mutex_unlock(&class_hash.mutex);
228
+ #else
229
+ rb_mutex_unlock(class_hash.mutex);
230
+ #endif
231
+ return b->val;
232
+ }
233
+ bucket = b;
234
+ }
235
+ b = ALLOC(struct _keyVal);
236
+ b->next = NULL;
237
+ bucket->next = b;
238
+ bucket = b;
239
+ }
240
+ bucket->key = oj_strndup(key, len);
241
+ bucket->len = len;
242
+ bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
243
+ #if HAVE_PTHREAD_MUTEX_INIT
244
+ pthread_mutex_unlock(&class_hash.mutex);
245
+ #else
246
+ rb_mutex_unlock(class_hash.mutex);
247
+ #endif
248
+ } else {
249
+ if (NULL != bucket->key) {
250
+ for (b = bucket; 0 != b; b = b->next) {
251
+ if (len == b->len && 0 == strncmp(b->key, key, len)) {
252
+ return (ID)b->val;
253
+ }
254
+ bucket = b;
255
+ }
256
+ b = ALLOC(struct _keyVal);
257
+ b->next = NULL;
258
+ bucket->next = b;
259
+ bucket = b;
260
+ }
261
+ bucket->key = oj_strndup(key, len);
262
+ bucket->len = len;
263
+ bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
264
+ }
265
+ return bucket->val;
266
+ }
267
+
268
+ char *oj_strndup(const char *s, size_t len) {
269
+ char *d = ALLOC_N(char, len + 1);
270
+
271
+ memcpy(d, s, len);
272
+ d[len] = '\0';
273
+
274
+ return d;
275
+ }
276
+
277
+ void intern_cleanup() {
278
+ cache_free(str_cache);
279
+ cache_free(sym_cache);
280
+ cache_free(attr_cache);
281
+ }
data/ext/oj/intern.h ADDED
@@ -0,0 +1,26 @@
1
+ // Copyright (c) 2011, 2021 Peter Ohler. All rights reserved.
2
+ // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
+
4
+ #ifndef OJ_INTERN_H
5
+ #define OJ_INTERN_H
6
+
7
+ #include <stdbool.h>
8
+ #include <ruby.h>
9
+
10
+ struct _parseInfo;
11
+
12
+ extern void oj_hash_init();
13
+
14
+ extern VALUE oj_str_intern(const char *key, size_t len);
15
+ extern VALUE oj_sym_intern(const char *key, size_t len);
16
+ extern ID oj_attr_intern(const char *key, size_t len);
17
+ extern VALUE oj_class_intern(const char * key,
18
+ size_t len,
19
+ bool safe,
20
+ struct _parseInfo *pi,
21
+ int auto_define,
22
+ VALUE error_class);
23
+
24
+ extern char *oj_strndup(const char *s, size_t len);
25
+
26
+ #endif /* OJ_INTERN_H */
data/ext/oj/mimic_json.c CHANGED
@@ -682,7 +682,7 @@ static VALUE mimic_set_create_id(VALUE self, VALUE id) {
682
682
  */
683
683
  static VALUE mimic_create_id(VALUE self) {
684
684
  if (NULL != oj_default_options.create_id) {
685
- return oj_encode(rb_str_new_cstr(oj_default_options.create_id));
685
+ return rb_utf8_str_new(oj_default_options.create_id, oj_default_options.create_id_len);
686
686
  }
687
687
  return rb_str_new_cstr(oj_json_class);
688
688
  }
@@ -714,7 +714,7 @@ static struct _options mimic_object_to_json_options = {0, // indent
714
714
  false, // sec_prec_set
715
715
  No, // ignore_under
716
716
  Yes, // cache_keys
717
- 3, // cache_str
717
+ 0, // cache_str
718
718
  0, // int_range_min
719
719
  0, // int_range_max
720
720
  oj_json_class, // create_id
data/ext/oj/object.c CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  #include "encode.h"
9
9
  #include "err.h"
10
- #include "hash.h"
10
+ #include "intern.h"
11
11
  #include "odd.h"
12
12
  #include "oj.h"
13
13
  #include "parse.h"
@@ -30,45 +30,18 @@ inline static long read_long(const char *str, size_t len) {
30
30
 
31
31
  static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
32
32
  volatile VALUE rkey;
33
- #if 0
34
- VALUE *slot;
35
33
 
36
34
  if (':' == k1) {
37
- if (Qnil == (rkey = oj_sym_hash_get(kval->key + 1, kval->klen - 1, &slot))) {
38
- rkey = rb_str_new(kval->key + 1, kval->klen - 1);
39
- rkey = oj_encode(rkey);
40
- rkey = rb_str_intern(rkey);
41
- *slot = rkey;
42
- rb_gc_register_address(slot);
43
- }
44
- } else if (Yes == pi->options.sym_key) {
45
- if (Qnil == (rkey = oj_sym_hash_get(kval->key, kval->klen, &slot))) {
46
- rkey = rb_str_new(kval->key, kval->klen);
47
- rkey = oj_encode(rkey);
48
- rkey = rb_str_intern(rkey);
49
- *slot = rkey;
50
- rb_gc_register_address(slot);
51
- }
52
- } else {
53
- if (Qnil == (rkey = oj_str_hash_get(kval->key, kval->klen, &slot))) {
54
- rkey = rb_str_new(kval->key, kval->klen);
55
- rkey = oj_encode(rkey);
56
- *slot = rkey;
57
- rb_gc_register_address(slot);
58
- }
35
+ return ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
59
36
  }
60
- #else
61
- if (':' == k1) {
62
- rkey = rb_str_new(kval->key + 1, kval->klen - 1);
63
- rkey = oj_encode(rkey);
64
- rkey = rb_str_intern(rkey);
65
- } else {
66
- rkey = rb_str_new(kval->key, kval->klen);
67
- rkey = oj_encode(rkey);
68
- if (Yes == pi->options.sym_key) {
69
- rkey = rb_str_intern(rkey);
70
- }
37
+ if (Yes == pi->options.sym_key) {
38
+ return ID2SYM(rb_intern3(kval->key, kval->klen, oj_utf8_encoding));
71
39
  }
40
+ #if HAVE_RB_ENC_INTERNED_STR
41
+ rkey = rb_enc_interned_str(kval->key, kval->klen, oj_utf8_encoding);
42
+ #else
43
+ rkey = rb_utf8_str_new(kval->key, kval->klen);
44
+ OBJ_FREEZE(rkey);
72
45
  #endif
73
46
  return rkey;
74
47
  }
@@ -77,9 +50,7 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
77
50
  volatile VALUE rstr = Qnil;
78
51
 
79
52
  if (':' == *orig && 0 < len) {
80
- rstr = rb_str_new(str + 1, len - 1);
81
- rstr = oj_encode(rstr);
82
- rstr = rb_funcall(rstr, oj_to_sym_id, 0);
53
+ rstr = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
83
54
  } else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
84
55
  long i = read_long(str + 2, len - 2);
85
56
 
@@ -89,8 +60,7 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
89
60
  }
90
61
  rstr = oj_circ_array_get(pi->circ_array, i);
91
62
  } else {
92
- rstr = rb_str_new(str, len);
93
- rstr = oj_encode(rstr);
63
+ rstr = rb_utf8_str_new(str, len);
94
64
  }
95
65
  return rstr;
96
66
  }
@@ -258,13 +228,10 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
258
228
  parent->odd_args = oj_odd_alloc_args(odd);
259
229
  } break;
260
230
  case 'm':
261
- parent->val = rb_str_new(str + 1, len - 1);
262
- parent->val = oj_encode(parent->val);
263
- parent->val = rb_funcall(parent->val, oj_to_sym_id, 0);
231
+ parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
264
232
  break;
265
233
  case 's':
266
- parent->val = rb_str_new(str, len);
267
- parent->val = oj_encode(parent->val);
234
+ parent->val = rb_utf8_str_new(str, len);
268
235
  break;
269
236
  case 'c': // class
270
237
  {
@@ -305,7 +272,7 @@ static int hat_num(ParseInfo pi, Val parent, Val kval, NumInfo ni) {
305
272
  }
306
273
  if (86400 == ni->exp) { // UTC time
307
274
  parent->val = rb_time_nano_new(ni->i, (long)nsec);
308
- // Since the ruby C routines alway create local time, the
275
+ // Since the ruby C routines always create local time, the
309
276
  // offset and then a conversion to UTC keeps makes the time
310
277
  // match the expected value.
311
278
  parent->val = rb_funcall2(parent->val, oj_utc_id, 0, 0);
@@ -416,51 +383,7 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
416
383
  }
417
384
 
418
385
  void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
419
- const char *key = kval->key;
420
- int klen = kval->klen;
421
- ID var_id;
422
- ID * slot;
423
-
424
- #ifdef HAVE_PTHREAD_MUTEX_INIT
425
- pthread_mutex_lock(&oj_cache_mutex);
426
- #else
427
- rb_mutex_lock(oj_cache_mutex);
428
- #endif
429
- if (0 == (var_id = oj_attr_hash_get(key, klen, &slot))) {
430
- char attr[256];
431
-
432
- if ((int)sizeof(attr) <= klen + 2) {
433
- char *buf = ALLOC_N(char, klen + 2);
434
-
435
- if ('~' == *key) {
436
- memcpy(buf, key + 1, klen - 1);
437
- buf[klen - 1] = '\0';
438
- } else {
439
- *buf = '@';
440
- memcpy(buf + 1, key, klen);
441
- buf[klen + 1] = '\0';
442
- }
443
- var_id = rb_intern(buf);
444
- xfree(buf);
445
- } else {
446
- if ('~' == *key) {
447
- memcpy(attr, key + 1, klen - 1);
448
- attr[klen - 1] = '\0';
449
- } else {
450
- *attr = '@';
451
- memcpy(attr + 1, key, klen);
452
- attr[klen + 1] = '\0';
453
- }
454
- var_id = rb_intern(attr);
455
- }
456
- *slot = var_id;
457
- }
458
- #ifdef HAVE_PTHREAD_MUTEX_INIT
459
- pthread_mutex_unlock(&oj_cache_mutex);
460
- #else
461
- rb_mutex_unlock(oj_cache_mutex);
462
- #endif
463
- rb_ivar_set(parent->val, var_id, value);
386
+ rb_ivar_set(parent->val, oj_attr_intern(kval->key, kval->klen), value);
464
387
  }
465
388
 
466
389
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {