oj 3.17.1 → 3.17.6

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.
data/ext/oj/fast.c CHANGED
@@ -80,7 +80,7 @@ static void each_leaf(Doc doc, VALUE self);
80
80
  static int move_step(Doc doc, const char *path, int loc);
81
81
  static Leaf get_doc_leaf(Doc doc, const char *path);
82
82
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path);
83
- static void each_value(Doc doc, Leaf leaf);
83
+ static void each_value(Doc doc, Leaf leaf, VALUE self);
84
84
 
85
85
  VALUE oj_doc_class = Qundef;
86
86
 
@@ -92,12 +92,35 @@ inline static void next_non_white(ParseInfo pi) {
92
92
  case '\f':
93
93
  case '\n':
94
94
  case '\r': break;
95
- case '/': skip_comment(pi); break;
95
+ case '/':
96
+ skip_comment(pi);
97
+ // A comment that is not terminated by a newline ends on the null
98
+ // terminator. Stop here so the loop does not step past it.
99
+ if ('\0' == *pi->s) {
100
+ return;
101
+ }
102
+ break;
96
103
  default: return;
97
104
  }
98
105
  }
99
106
  }
100
107
 
108
+ inline static bool is_value_end(char c) {
109
+ switch (c) {
110
+ case '\0':
111
+ case ' ':
112
+ case '\t':
113
+ case '\f':
114
+ case '\n':
115
+ case '\r':
116
+ case '/':
117
+ case ',':
118
+ case ']':
119
+ case '}': return true;
120
+ default: return false;
121
+ }
122
+ }
123
+
101
124
  inline static char *ulong_fill(char *s, size_t num) {
102
125
  char buf[32];
103
126
  char *b = buf + sizeof(buf) - 1;
@@ -210,10 +233,11 @@ static void skip_comment(ParseInfo pi) {
210
233
  if ('*' == *pi->s && '/' == *(pi->s + 1)) {
211
234
  pi->s++;
212
235
  return;
213
- } else if ('\0' == *pi->s) {
214
- raise_error("comment not terminated", pi->str, pi->s);
215
236
  }
216
237
  }
238
+ // The loop only ends on the null terminator so the comment was never
239
+ // closed.
240
+ raise_error("comment not terminated", pi->str, pi->s);
217
241
  } else if ('/' == *pi->s) {
218
242
  for (; 1; pi->s++) {
219
243
  switch (*pi->s) {
@@ -455,27 +479,42 @@ static Leaf read_num(ParseInfo pi) {
455
479
  char *start = pi->s;
456
480
  int type = T_FIXNUM;
457
481
  Leaf leaf;
482
+ int dcnt = 0;
483
+ int ecnt = 1;
458
484
 
459
- if ('-' == *pi->s) {
485
+ // read_next() sends a leading '+' here as well, and Oj.load() takes one.
486
+ if ('-' == *pi->s || '+' == *pi->s) {
460
487
  pi->s++;
461
488
  }
462
489
  // digits
463
490
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
491
+ dcnt++;
464
492
  }
465
493
  if ('.' == *pi->s) {
466
494
  type = T_FLOAT;
467
495
  pi->s++;
468
496
  for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
497
+ dcnt++;
469
498
  }
470
499
  }
471
500
  if ('e' == *pi->s || 'E' == *pi->s) {
501
+ // An exponent makes it a float whether or not a '.' was seen. Without
502
+ // this leaf_fixnum_value() stops reading at the 'e' and the exponent is
503
+ // dropped.
504
+ type = T_FLOAT;
472
505
  pi->s++;
473
506
  if ('-' == *pi->s || '+' == *pi->s) {
474
507
  pi->s++;
475
508
  }
476
- for (; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
509
+ for (ecnt = 0; '0' <= *pi->s && *pi->s <= '9'; pi->s++) {
510
+ ecnt++;
477
511
  }
478
512
  }
513
+ // A float is converted with rb_cstr_to_dbl(), which has to be given
514
+ // something it can convert.
515
+ if (T_FLOAT == type && (0 == dcnt || 0 == ecnt || !is_value_end(*pi->s))) {
516
+ raise_error("invalid number", pi->str, pi->s);
517
+ }
479
518
  leaf = leaf_new(pi->doc, type);
480
519
  leaf->str = start;
481
520
 
@@ -662,7 +701,11 @@ static void doc_free(Doc doc) {
662
701
  static VALUE protect_open_proc(VALUE x) {
663
702
  ParseInfo pi = (ParseInfo)x;
664
703
 
665
- pi->doc->data = read_next(pi); // parse
704
+ pi->doc->data = read_next(pi); // parse
705
+ // read_obj() and read_array() terminate each value where it ends. The top
706
+ // level value has no closing delimiter to do that on, so a number leaf runs
707
+ // to the end of the document unless it is terminated here.
708
+ *pi->s = '\0';
666
709
  *pi->doc->where = pi->doc->data;
667
710
  pi->doc->where = pi->doc->where_path;
668
711
  if (rb_block_given_p()) {
@@ -710,21 +753,23 @@ static void mark_doc(void *ptr) {
710
753
  }
711
754
  #ifdef HAVE_RB_GC_MARK_MOVABLE
712
755
  static void compact_leaf(Leaf leaf) {
713
- switch (leaf->value_type) {
714
- case COL_VAL:
715
- if (NULL != leaf->elements) {
716
- Leaf first = leaf->elements->next;
717
- Leaf e = first;
756
+ if (NULL != leaf) {
757
+ switch (leaf->value_type) {
758
+ case COL_VAL:
759
+ if (NULL != leaf->elements) {
760
+ Leaf first = leaf->elements->next;
761
+ Leaf e = first;
718
762
 
719
- do {
720
- compact_leaf(e);
721
- e = e->next;
722
- } while (e != first);
723
- }
724
- break;
725
- case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
763
+ do {
764
+ compact_leaf(e);
765
+ e = e->next;
766
+ } while (e != first);
767
+ }
768
+ break;
769
+ case RUBY_VAL: leaf->value = rb_gc_location(leaf->value); break;
726
770
 
727
- default: break;
771
+ default: break;
772
+ }
728
773
  }
729
774
  }
730
775
 
@@ -790,14 +835,11 @@ static VALUE parse_json(VALUE clas, char *json, bool given) {
790
835
  doc->self = self;
791
836
  result = rb_protect(protect_open_proc, (VALUE)&pi, &ex);
792
837
  if (given || 0 != ex) {
838
+ // The doc is detached from its wrapper so the GC will not free it.
839
+ // doc_free() releases both the doc and its json buffer (doc->json), so
840
+ // the caller must not free json separately.
793
841
  DATA_PTR(doc->self) = NULL;
794
- // TBD is this needed?
795
- /*
796
842
  doc_free(pi.doc);
797
- if (0 != ex) { // will jump so caller will not free
798
- OJ_R_FREE(json);
799
- }
800
- */
801
843
  } else {
802
844
  result = doc->self;
803
845
  }
@@ -865,7 +907,7 @@ static bool key_match(const char *pat, const char *key, int plen) {
865
907
  static Leaf get_leaf(Leaf *stack, Leaf *lp, const char *path) {
866
908
  Leaf leaf = *lp;
867
909
 
868
- if (MAX_STACK <= lp - stack) {
910
+ if (MAX_STACK <= (lp + 1) - stack) {
869
911
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
870
912
  }
871
913
  if ('\0' != *path) {
@@ -946,6 +988,7 @@ static void each_leaf(Doc doc, VALUE self) {
946
988
 
947
989
  doc->where++;
948
990
  if (MAX_STACK <= doc->where - doc->where_path) {
991
+ doc->where--;
949
992
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
950
993
  }
951
994
  do {
@@ -957,11 +1000,14 @@ static void each_leaf(Doc doc, VALUE self) {
957
1000
  }
958
1001
  } else {
959
1002
  rb_yield(self);
1003
+ if (NULL == DATA_PTR(self)) {
1004
+ rb_raise(rb_eIOError, "Document closed.");
1005
+ }
960
1006
  }
961
1007
  }
962
1008
 
963
1009
  static int move_step(Doc doc, const char *path, int loc) {
964
- if (MAX_STACK <= doc->where - doc->where_path) {
1010
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
965
1011
  rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
966
1012
  }
967
1013
  if ('\0' == *path) {
@@ -969,8 +1015,9 @@ static int move_step(Doc doc, const char *path, int loc) {
969
1015
  } else {
970
1016
  Leaf leaf;
971
1017
 
1018
+ // A document with no root (empty or comment only JSON) has no leaf to
1019
+ // step from so the path can not be located.
972
1020
  if (0 == doc->where || 0 == (leaf = *doc->where)) {
973
- printf("*** Internal error at %s\n", path);
974
1021
  return loc;
975
1022
  }
976
1023
  if ('.' == *path && '.' == *(path + 1)) {
@@ -1050,19 +1097,22 @@ static int move_step(Doc doc, const char *path, int loc) {
1050
1097
  return loc;
1051
1098
  }
1052
1099
 
1053
- static void each_value(Doc doc, Leaf leaf) {
1100
+ static void each_value(Doc doc, Leaf leaf, VALUE self) {
1054
1101
  if (COL_VAL == leaf->value_type) {
1055
1102
  if (0 != leaf->elements) {
1056
1103
  Leaf first = leaf->elements->next;
1057
1104
  Leaf e = first;
1058
1105
 
1059
1106
  do {
1060
- each_value(doc, e);
1107
+ each_value(doc, e, self);
1061
1108
  e = e->next;
1062
1109
  } while (e != first);
1063
1110
  }
1064
1111
  } else {
1065
1112
  rb_yield(leaf_value(doc, leaf));
1113
+ if (NULL == DATA_PTR(self)) {
1114
+ rb_raise(rb_eIOError, "Document closed.");
1115
+ }
1066
1116
  }
1067
1117
  }
1068
1118
 
@@ -1098,12 +1148,7 @@ static VALUE doc_open(VALUE clas, VALUE str) {
1098
1148
 
1099
1149
  memcpy(json, StringValuePtr(str), len);
1100
1150
  obj = parse_json(clas, json, given);
1101
- // TBD is this needed
1102
- /*
1103
- if (given) {
1104
- OJ_R_FREE(json);
1105
- }
1106
- */
1151
+ // json is owned by the doc and freed by doc_free(); do not free it here.
1107
1152
  return obj;
1108
1153
  }
1109
1154
 
@@ -1135,7 +1180,9 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1135
1180
  int given = rb_block_given_p();
1136
1181
 
1137
1182
  path = StringValuePtr(filename);
1138
- if (0 == (f = fopen(path, "r"))) {
1183
+ // Open in binary mode. On Windows a text mode read translates CRLF into LF
1184
+ // so fewer bytes are read than the file size reported by ftell().
1185
+ if (0 == (f = fopen(path, "rb"))) {
1139
1186
  rb_raise(rb_eIOError, "%s", strerror(errno));
1140
1187
  }
1141
1188
  fseek(f, 0, SEEK_END);
@@ -1153,12 +1200,7 @@ static VALUE doc_open_file(VALUE clas, VALUE filename) {
1153
1200
  fclose(f);
1154
1201
  json[len] = '\0';
1155
1202
  obj = parse_json(clas, json, given);
1156
- // TBD is this needed
1157
- /*
1158
- if (given) {
1159
- OJ_R_FREE(json);
1160
- }
1161
- */
1203
+ // json is owned by the doc and freed by doc_free(); do not free it here.
1162
1204
  return obj;
1163
1205
  }
1164
1206
 
@@ -1257,10 +1299,14 @@ static VALUE doc_path(VALUE self) {
1257
1299
  * #=> nil
1258
1300
  */
1259
1301
  static VALUE doc_local_key(VALUE self) {
1260
- Doc doc = self_doc(self);
1261
- Leaf leaf = *doc->where;
1262
- volatile VALUE key = Qnil;
1302
+ Doc doc = self_doc(self);
1303
+ Leaf leaf;
1304
+ volatile VALUE key = Qnil;
1263
1305
 
1306
+ if (NULL == doc->where || NULL == *doc->where) {
1307
+ return Qnil;
1308
+ }
1309
+ leaf = *doc->where;
1264
1310
  if (T_HASH == leaf->parent_type) {
1265
1311
  key = rb_utf8_str_new_cstr(leaf->key);
1266
1312
  } else if (T_ARRAY == leaf->parent_type) {
@@ -1415,6 +1461,9 @@ static VALUE doc_each_leaf(int argc, VALUE *argv, VALUE self) {
1415
1461
  return Qnil;
1416
1462
  }
1417
1463
  }
1464
+ if (NULL == doc->where || NULL == *doc->where) {
1465
+ return Qnil;
1466
+ }
1418
1467
  each_leaf(doc, self);
1419
1468
  if (0 < wlen) {
1420
1469
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1498,12 +1547,19 @@ static VALUE doc_each_child(int argc, VALUE *argv, VALUE self) {
1498
1547
  Leaf first = (*doc->where)->elements->next;
1499
1548
  Leaf e = first;
1500
1549
 
1550
+ if (MAX_STACK <= (doc->where + 1) - doc->where_path) {
1551
+ rb_raise(rb_const_get_at(Oj, rb_intern("DepthError")), "Path too deep. Limit is %d levels.", MAX_STACK);
1552
+ }
1501
1553
  doc->where++;
1502
1554
  do {
1503
1555
  *doc->where = e;
1504
1556
  rb_yield(self);
1557
+ if (NULL == DATA_PTR(self)) {
1558
+ rb_raise(rb_eIOError, "Document closed.");
1559
+ }
1505
1560
  e = e->next;
1506
1561
  } while (e != first);
1562
+ doc->where--;
1507
1563
  }
1508
1564
  if (0 < wlen) {
1509
1565
  memcpy(doc->where_path, save_path, sizeof(Leaf) * (wlen + 1));
@@ -1547,7 +1603,7 @@ static VALUE doc_each_value(int argc, VALUE *argv, VALUE self) {
1547
1603
  path = StringValuePtr(*argv);
1548
1604
  }
1549
1605
  if (0 != (leaf = get_doc_leaf(doc, path))) {
1550
- each_value(doc, leaf);
1606
+ each_value(doc, leaf, self);
1551
1607
  }
1552
1608
  }
1553
1609
  return Qnil;
data/ext/oj/intern.c CHANGED
@@ -69,7 +69,7 @@ static VALUE form_attr(const char *str, size_t len) {
69
69
  memcpy(b + 1, str, len);
70
70
  b[len + 1] = '\0';
71
71
  }
72
- id = rb_intern3(buf, len + 1, oj_utf8_encoding);
72
+ id = rb_intern3(b, len + 1, oj_utf8_encoding);
73
73
  OJ_R_FREE(b);
74
74
  return id;
75
75
  }
@@ -97,6 +97,8 @@ static const rb_data_type_t oj_cache_type = {
97
97
  };
98
98
 
99
99
  void oj_hash_init(void) {
100
+ oj_hash_seed_init();
101
+
100
102
  VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
101
103
  rb_undef_alloc_func(cache_class);
102
104
 
@@ -151,7 +153,7 @@ ID oj_attr_intern(const char *key, size_t len) {
151
153
  static uint64_t hash_calc(const uint8_t *key, size_t len) {
152
154
  const uint8_t *end = key + len;
153
155
  const uint8_t *endless = key + (len & 0xFFFFFFFC);
154
- uint64_t h = (uint64_t)len;
156
+ uint64_t h = (uint64_t)len ^ oj_hash_seed;
155
157
  uint64_t k;
156
158
 
157
159
  while (key < endless) {
@@ -188,6 +190,9 @@ static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define
188
190
 
189
191
  if (rb_const_defined_at(mod, ci)) {
190
192
  clas = rb_const_get_at(mod, ci);
193
+ if (!RB_TYPE_P(clas, T_CLASS) && !RB_TYPE_P(clas, T_MODULE)) {
194
+ clas = Qundef;
195
+ }
191
196
  } else if (auto_define) {
192
197
  clas = rb_define_class_under(mod, classname, oj_bag_class);
193
198
  } else {
data/ext/oj/object.c CHANGED
@@ -31,7 +31,7 @@ inline static long read_long(const char *str, size_t len) {
31
31
  static VALUE calc_hash_key(ParseInfo pi, Val kval, char k1) {
32
32
  volatile VALUE rkey;
33
33
 
34
- if (':' == k1) {
34
+ if (':' == k1 && 0 < kval->klen) {
35
35
  return ID2SYM(rb_intern3(kval->key + 1, kval->klen - 1, oj_utf8_encoding));
36
36
  }
37
37
  if (Yes == pi->options.sym_key) {
@@ -54,7 +54,7 @@ static VALUE str_to_value(ParseInfo pi, const char *str, size_t len, const char
54
54
  } else if (pi->circ_array && 3 <= len && '^' == *orig && 'r' == orig[1]) {
55
55
  long i = read_long(str + 2, len - 2);
56
56
 
57
- if (0 > i) {
57
+ if (0 >= i) {
58
58
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a valid ID number");
59
59
  return Qnil;
60
60
  }
@@ -207,7 +207,7 @@ oj_parse_xml_time(const char *str, int len) {
207
207
 
208
208
  static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t len) {
209
209
  const char *key = kval->key;
210
- int klen = kval->klen;
210
+ size_t klen = kval->klen;
211
211
 
212
212
  if (2 == klen) {
213
213
  switch (key[1]) {
@@ -230,7 +230,12 @@ static int hat_cstr(ParseInfo pi, Val parent, Val kval, const char *str, size_t
230
230
  parent->odd_args = oj_odd_alloc_args(odd);
231
231
  break;
232
232
  }
233
- case 'm': parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding)); break;
233
+ case 'm':
234
+ if (0 == len) {
235
+ return 0;
236
+ }
237
+ parent->val = ID2SYM(rb_intern3(str + 1, len - 1, oj_utf8_encoding));
238
+ break;
234
239
  case 's': parent->val = rb_utf8_str_new(str, len); break;
235
240
  case 'c': // class
236
241
  {
@@ -319,20 +324,26 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
319
324
  e1 = *RARRAY_CONST_PTR(value);
320
325
  // check for anonymous Struct
321
326
  if (T_ARRAY == rb_type(e1)) {
322
- VALUE args[1024];
323
- volatile VALUE rstr;
327
+ // Build the member-name list in a GC-managed Ruby array. Using a
328
+ // fixed C buffer (VALUE args[1024]) here overflowed the stack when
329
+ // the JSON supplied more than 1024 member names.
330
+ volatile VALUE names = rb_ary_new2(RARRAY_LEN(e1));
324
331
  size_t i;
325
332
  size_t cnt = RARRAY_LEN(e1);
326
333
 
327
334
  for (i = 0; i < cnt; i++) {
328
- rstr = RARRAY_AREF(e1, i);
329
- args[i] = rb_funcall(rstr, oj_to_sym_id, 0);
335
+ rb_ary_push(names, rb_funcall(RARRAY_AREF(e1, i), oj_to_sym_id, 0));
330
336
  }
331
- sc = rb_funcall2(rb_cStruct, oj_new_id, (int)cnt, args);
337
+ sc = rb_apply(rb_cStruct, oj_new_id, names);
332
338
  } else {
333
339
  // If struct is not defined then we let this fail and raise an exception.
334
340
  sc = oj_name2struct(pi, *RARRAY_CONST_PTR(value), rb_eArgError);
335
341
  }
342
+ if (Qundef == sc || Qnil == sc) {
343
+ // oj_name2struct already recorded the error; do not pass an
344
+ // unresolved (Qundef) class on to rb_obj_alloc/rb_class_new_instance.
345
+ return 1;
346
+ }
336
347
  if (sc == rb_cRange) {
337
348
  parent->val = rb_class_new_instance((int)(len - 1), RARRAY_CONST_PTR(value) + 1, rb_cRange);
338
349
  } else {
@@ -389,7 +400,7 @@ void oj_set_obj_ivar(Val parent, Val kval, VALUE value) {
389
400
 
390
401
  static void hash_set_cstr(ParseInfo pi, Val kval, const char *str, size_t len, const char *orig) {
391
402
  const char *key = kval->key;
392
- int klen = kval->klen;
403
+ size_t klen = kval->klen;
393
404
  Val parent = stack_peek(&pi->stack);
394
405
  volatile VALUE rval = Qnil;
395
406
 
@@ -431,7 +442,7 @@ WHICH_TYPE:
431
442
  if (0 != oj_odd_set_arg(parent->odd_args, kval->key, kval->klen, rval)) {
432
443
  char buf[256];
433
444
 
434
- if ((int)sizeof(buf) - 1 <= klen) {
445
+ if (sizeof(buf) - 1 <= klen) {
435
446
  klen = sizeof(buf) - 2;
436
447
  }
437
448
  memcpy(buf, key, klen);
@@ -460,7 +471,7 @@ WHICH_TYPE:
460
471
 
461
472
  static void hash_set_num(ParseInfo pi, Val kval, NumInfo ni) {
462
473
  const char *key = kval->key;
463
- int klen = kval->klen;
474
+ size_t klen = kval->klen;
464
475
  Val parent = stack_peek(&pi->stack);
465
476
  volatile VALUE rval = Qnil;
466
477
 
@@ -500,7 +511,7 @@ WHICH_TYPE:
500
511
  if (0 != oj_odd_set_arg(parent->odd_args, key, klen, rval)) {
501
512
  char buf[256];
502
513
 
503
- if ((int)sizeof(buf) - 1 <= klen) {
514
+ if (sizeof(buf) - 1 <= klen) {
504
515
  klen = sizeof(buf) - 2;
505
516
  }
506
517
  memcpy(buf, key, klen);
@@ -529,7 +540,7 @@ WHICH_TYPE:
529
540
 
530
541
  static void hash_set_value(ParseInfo pi, Val kval, VALUE value) {
531
542
  const char *key = kval->key;
532
- int klen = kval->klen;
543
+ size_t klen = kval->klen;
533
544
  Val parent = stack_peek(&pi->stack);
534
545
 
535
546
  WHICH_TYPE:
@@ -585,7 +596,7 @@ WHICH_TYPE:
585
596
  } else if (0 != oj_odd_set_arg(parent->odd_args, key, klen, value)) {
586
597
  char buf[256];
587
598
 
588
- if ((int)sizeof(buf) - 1 <= klen) {
599
+ if (sizeof(buf) - 1 <= klen) {
589
600
  klen = sizeof(buf) - 2;
590
601
  }
591
602
  memcpy(buf, key, klen);
data/ext/oj/odd.c CHANGED
@@ -166,7 +166,11 @@ Odd oj_get_oddc(const char *classname, size_t len) {
166
166
  if (len == odd->clen && 0 == strncmp(classname, odd->classname, len)) {
167
167
  return odd;
168
168
  }
169
- if (odd->is_module && 0 == strncmp(odd->classname, classname, odd->clen) && ':' == classname[odd->clen]) {
169
+ // The module test needs a "::" after the module name, so the name has
170
+ // to be shorter than what it is compared against. Without that the
171
+ // comparison and the character after it both run past len.
172
+ if (odd->is_module && odd->clen < len && 0 == strncmp(odd->classname, classname, odd->clen) &&
173
+ ':' == classname[odd->clen]) {
170
174
  return odd;
171
175
  }
172
176
  }
@@ -195,7 +199,7 @@ int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
195
199
  int i;
196
200
 
197
201
  for (i = args->odd->attr_cnt, np = args->odd->attr_names, vp = args->args; 0 < i; i--, np++, vp++) {
198
- if (0 == strncmp(key, *np, klen) && '\0' == *((*np) + klen)) {
202
+ if (klen == strlen(*np) && 0 == memcmp(key, *np, klen)) {
199
203
  *vp = value;
200
204
  return 0;
201
205
  }