oj 3.13.23 → 3.14.2

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/mem.c ADDED
@@ -0,0 +1,324 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <pthread.h>
4
+ #include <stdbool.h>
5
+ #include <stdint.h>
6
+ #include <stdio.h>
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+
10
+ #include <ruby.h>
11
+
12
+ #include "mem.h"
13
+
14
+ typedef struct _rec {
15
+ struct _rec *next;
16
+ const void *ptr;
17
+ size_t size;
18
+ const char *file;
19
+ int line;
20
+ bool ruby;
21
+ } *Rec;
22
+
23
+ typedef struct _rep {
24
+ struct _rep *next;
25
+ size_t size;
26
+ const char *file;
27
+ int line;
28
+ int cnt;
29
+ } *Rep;
30
+
31
+ #ifdef MEM_DEBUG
32
+
33
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
34
+ static Rec recs = NULL;
35
+ static const char mem_pad[] = "--- This is a memory pad and should not change until being freed. ---";
36
+
37
+ void*
38
+ oj_malloc(size_t size, const char *file, int line) {
39
+ void *ptr = malloc(size + sizeof(mem_pad));
40
+
41
+ if (NULL != ptr) {
42
+ Rec r = (Rec)malloc(sizeof(struct _rec));
43
+
44
+ if (NULL != r) {
45
+ strcpy(((char*)ptr) + size, mem_pad);
46
+ r->ptr = ptr;
47
+ r->size = size;
48
+ r->file = file;
49
+ r->line = line;
50
+ r->ruby = false;
51
+ pthread_mutex_lock(&lock);
52
+ r->next = recs;
53
+ recs = r;
54
+ pthread_mutex_unlock(&lock);
55
+ } else {
56
+ free(ptr);
57
+ ptr = NULL;
58
+ }
59
+ }
60
+ return ptr;
61
+ }
62
+
63
+ void*
64
+ oj_realloc(void *orig, size_t size, const char *file, int line) {
65
+ void *ptr = realloc(orig, size + sizeof(mem_pad));
66
+ Rec r;
67
+
68
+ if (NULL != ptr) {
69
+ strcpy(((char*)ptr) + size, mem_pad);
70
+ pthread_mutex_lock(&lock);
71
+ for (r = recs; NULL != r; r = r->next) {
72
+ if (orig == r->ptr) {
73
+ r->ptr = ptr;
74
+ r->size = size;
75
+ r->file = file;
76
+ r->line = line;
77
+ r->ruby = false;
78
+ break;
79
+ }
80
+ }
81
+ pthread_mutex_unlock(&lock);
82
+ if (NULL == r) {
83
+ printf("Realloc at %s:%d (%p) not allocated.\n", file, line, orig);
84
+ }
85
+ }
86
+ return ptr;
87
+ }
88
+
89
+ void*
90
+ oj_calloc(size_t count, size_t size, const char *file, int line) {
91
+ void *ptr;
92
+
93
+ size *= count;
94
+ if (NULL != (ptr = malloc(size + sizeof(mem_pad)))) {
95
+ Rec r = (Rec)malloc(sizeof(struct _rec));
96
+
97
+ if (NULL != r) {
98
+ memset(ptr, 0, size);
99
+ strcpy(((char*)ptr) + size, mem_pad);
100
+ r->ptr = ptr;
101
+ r->size = size;
102
+ r->file = file;
103
+ r->line = line;
104
+ r->ruby = false;
105
+ pthread_mutex_lock(&lock);
106
+ r->next = recs;
107
+ recs = r;
108
+ pthread_mutex_unlock(&lock);
109
+ } else {
110
+ free(ptr);
111
+ ptr = NULL;
112
+ }
113
+ }
114
+ return ptr;
115
+ }
116
+
117
+ void*
118
+ oj_r_alloc(size_t size, const char *file, int line) {
119
+ void *ptr = ruby_xmalloc(size + sizeof(mem_pad));
120
+
121
+ if (NULL != ptr) {
122
+ Rec r = (Rec)malloc(sizeof(struct _rec));
123
+
124
+ if (NULL != r) {
125
+ strcpy(((char*)ptr) + size, mem_pad);
126
+ r->ptr = ptr;
127
+ r->size = size;
128
+ r->file = file;
129
+ r->line = line;
130
+ r->ruby = true;
131
+ pthread_mutex_lock(&lock);
132
+ r->next = recs;
133
+ recs = r;
134
+ pthread_mutex_unlock(&lock);
135
+ } else {
136
+ free(ptr);
137
+ ptr = NULL;
138
+ }
139
+ }
140
+ return ptr;
141
+ }
142
+
143
+ void*
144
+ oj_r_realloc(void *orig, size_t size, const char *file, int line) {
145
+ void *ptr = ruby_xrealloc2(orig, 1, size + sizeof(mem_pad));
146
+ Rec r;
147
+
148
+ if (NULL != ptr) {
149
+ strcpy(((char*)ptr) + size, mem_pad);
150
+ pthread_mutex_lock(&lock);
151
+ for (r = recs; NULL != r; r = r->next) {
152
+ if (orig == r->ptr) {
153
+ r->ptr = ptr;
154
+ r->size = size;
155
+ r->file = file;
156
+ r->line = line;
157
+ r->ruby = true;
158
+ break;
159
+ }
160
+ }
161
+ pthread_mutex_unlock(&lock);
162
+ if (NULL == r) {
163
+ printf("Realloc at %s:%d (%p) not allocated.\n", file, line, orig);
164
+ }
165
+ }
166
+ return ptr;
167
+ }
168
+
169
+
170
+ void
171
+ oj_freed(void *ptr, const char *file, int line, bool ruby) {
172
+ if (NULL != ptr) {
173
+ Rec r = NULL;
174
+ Rec prev = NULL;
175
+
176
+ pthread_mutex_lock(&lock);
177
+ for (r = recs; NULL != r; r = r->next) {
178
+ if (ptr == r->ptr) {
179
+ if (NULL == prev) {
180
+ recs = r->next;
181
+ } else {
182
+ prev->next = r->next;
183
+ }
184
+ break;
185
+ }
186
+ prev = r;
187
+ }
188
+ pthread_mutex_unlock(&lock);
189
+ if (NULL == r) {
190
+ printf("Free at %s:%d (%p) not allocated or already freed.\n", file, line, ptr);
191
+ } else {
192
+ char *pad = (char*)r->ptr + r->size;
193
+
194
+ if (r->ruby != ruby) {
195
+ if (r->ruby) {
196
+ printf("Memory at %s:%d (%p) allocated with Ruby allocator and freed with stdlib free.\n", file, line, ptr);
197
+ } else {
198
+ printf("Memory at %s:%d (%p) allocated with stdlib allocator and freed with Ruby free.\n", file, line, ptr);
199
+ }
200
+ }
201
+ if (0 != strcmp(mem_pad, pad)) {
202
+ uint8_t *p;
203
+ uint8_t *end = (uint8_t*)pad + sizeof(mem_pad);
204
+
205
+ printf("Memory at %s:%d (%p) write outside allocated.\n", file, line, ptr);
206
+ for (p = (uint8_t*)pad; p < end; p++) {
207
+ if (0x20 < *p && *p < 0x7f) {
208
+ printf("%c ", *p);
209
+ } else {
210
+ printf("%02x ", *(uint8_t*)p);
211
+ }
212
+ }
213
+ printf("\n");
214
+ }
215
+ free(r);
216
+ }
217
+ }
218
+ }
219
+
220
+ void
221
+ oj_r_free(void *ptr, const char *file, int line) {
222
+ oj_freed(ptr, file, line, true);
223
+ xfree(ptr);
224
+ }
225
+
226
+
227
+ void
228
+ oj_free(void *ptr, const char *file, int line) {
229
+ oj_freed(ptr, file, line, false);
230
+ free(ptr);
231
+ }
232
+
233
+ char*
234
+ oj_mem_strdup(const char *str, const char *file, int line) {
235
+ size_t size = strlen(str) + 1;
236
+ char *ptr = (char*)malloc(size + sizeof(mem_pad));
237
+
238
+ if (NULL != ptr) {
239
+ Rec r = (Rec)malloc(sizeof(struct _rec));
240
+
241
+ if (NULL != r) {
242
+ strcpy(ptr, str);
243
+ strcpy(((char*)ptr) + size, mem_pad);
244
+ r->ptr = (void*)ptr;
245
+ r->size = size;
246
+ r->file = file;
247
+ r->line = line;
248
+ r->ruby = false;
249
+ pthread_mutex_lock(&lock);
250
+ r->next = recs;
251
+ recs = r;
252
+ pthread_mutex_unlock(&lock);
253
+ } else {
254
+ free(ptr);
255
+ ptr = NULL;
256
+ }
257
+ }
258
+ return ptr;
259
+ }
260
+
261
+ #endif
262
+
263
+ #ifdef MEM_DEBUG
264
+
265
+ static Rep
266
+ update_reps(Rep reps, Rec r) {
267
+ Rep rp = reps;
268
+
269
+ for (; NULL != rp; rp = rp->next) {
270
+ if (rp->line == r->line && (rp->file == r->file || 0 == strcmp(rp->file, r->file))) {
271
+ rp->size += r->size;
272
+ rp->cnt++;
273
+ break;
274
+ }
275
+ }
276
+ if (NULL == rp &&
277
+ NULL != (rp = (Rep)malloc(sizeof(struct _rep)))) {
278
+ rp->size = r->size;
279
+ rp->file = r->file;
280
+ rp->line = r->line;
281
+ rp->cnt = 1;
282
+ rp->next = reps;
283
+ reps = rp;
284
+ }
285
+ return reps;
286
+ }
287
+
288
+ static void
289
+ print_stats() {
290
+ printf("\n--- Memory Usage Report --------------------------------------------------------\n");
291
+ pthread_mutex_lock(&lock);
292
+
293
+ if (NULL == recs) {
294
+ printf("No memory leaks\n");
295
+ } else {
296
+ Rep reps = NULL;
297
+ Rep rp;
298
+ Rec r;
299
+ size_t leaked = 0;
300
+
301
+ for (r = recs; NULL != r; r = r->next) {
302
+ reps = update_reps(reps, r);
303
+ }
304
+ while (NULL != (rp = reps)) {
305
+ reps = rp->next;
306
+ printf("%16s:%3d %8lu bytes over %d occurances allocated and not freed.\n", rp->file, rp->line, rp->size, rp->cnt);
307
+ leaked += rp->size;
308
+ free(rp);
309
+ }
310
+ printf("%lu bytes leaked\n", leaked);
311
+ }
312
+ pthread_mutex_unlock(&lock);
313
+ printf("--------------------------------------------------------------------------------\n");
314
+ }
315
+
316
+ #endif
317
+
318
+ void
319
+ oj_mem_report() {
320
+ #ifdef MEM_DEBUG
321
+ rb_gc();
322
+ print_stats();
323
+ #endif
324
+ }
data/ext/oj/mem.h ADDED
@@ -0,0 +1,53 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef OJ_MEM_H
4
+ #define OJ_MEM_H
5
+
6
+ #include <stdio.h>
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+
10
+ #ifdef MEM_DEBUG
11
+
12
+ #define OJ_MALLOC(size) oj_malloc(size, __FILE__, __LINE__)
13
+ #define OJ_REALLOC(ptr, size) oj_realloc(ptr, size, __FILE__, __LINE__)
14
+ #define OJ_CALLOC(count, size) oj_calloc(count, size, __FILE__, __LINE__)
15
+ #define OJ_FREE(ptr) oj_free(ptr, __FILE__, __LINE__)
16
+
17
+ #define OJ_R_ALLOC(type) oj_r_alloc(sizeof(type), __FILE__, __LINE__)
18
+ #define OJ_R_ALLOC_N(type, n) (type *)oj_r_alloc(sizeof(type) * (n), __FILE__, __LINE__)
19
+ #define OJ_R_REALLOC_N(ptr, type, n) ((ptr) = (type*)oj_r_realloc(ptr, (sizeof(type) * (n)), __FILE__, __LINE__))
20
+ #define OJ_R_FREE(ptr) oj_r_free(ptr, __FILE__, __LINE__)
21
+
22
+ #define OJ_STRDUP(str) oj_mem_strdup(str, __FILE__, __LINE__)
23
+
24
+ extern void* oj_malloc(size_t size, const char *file, int line);
25
+ extern void* oj_realloc(void *ptr, size_t size, const char *file, int line);
26
+ extern void* oj_calloc(size_t count, size_t size, const char *file, int line);
27
+ extern void oj_free(void *ptr, const char *file, int line);
28
+
29
+ extern void* oj_r_alloc(size_t size, const char *file, int line);
30
+ extern void* oj_r_realloc(void *ptr, size_t size, const char *file, int line);
31
+ extern void oj_r_free(void *ptr, const char *file, int line);
32
+
33
+ extern char* oj_mem_strdup(const char *str, const char *file, int line);
34
+
35
+ #else
36
+
37
+ #define OJ_MALLOC(size) malloc(size)
38
+ #define OJ_REALLOC(ptr, size) realloc(ptr, size)
39
+ #define OJ_CALLOC(count, size) calloc(count, size)
40
+ #define OJ_FREE(ptr) free(ptr)
41
+
42
+ #define OJ_R_ALLOC(type) RB_ALLOC(type)
43
+ #define OJ_R_ALLOC_N(type, n) RB_ALLOC_N(type, n)
44
+ #define OJ_R_REALLOC_N(ptr, type, n) RB_REALLOC_N(ptr, type, n)
45
+ #define OJ_R_FREE(ptr) xfree(ptr)
46
+
47
+ #define OJ_STRDUP(str) strdup(str)
48
+
49
+ #endif
50
+
51
+ extern void oj_mem_report();
52
+
53
+ #endif /* OJ_MEM_H */
data/ext/oj/mimic_json.c CHANGED
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) 2012, 2017 Peter Ohler. All rights reserved.
2
2
  // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
3
 
4
+ #include "mem.h"
4
5
  #include "dump.h"
5
6
  #include "encode.h"
6
7
  #include "oj.h"
@@ -664,7 +665,7 @@ static VALUE mimic_set_create_id(VALUE self, VALUE id) {
664
665
 
665
666
  if (NULL != oj_default_options.create_id) {
666
667
  if (oj_json_class != oj_default_options.create_id) {
667
- xfree((char *)oj_default_options.create_id);
668
+ OJ_R_FREE((char *)oj_default_options.create_id);
668
669
  }
669
670
  oj_default_options.create_id = NULL;
670
671
  oj_default_options.create_id_len = 0;
@@ -672,7 +673,7 @@ static VALUE mimic_set_create_id(VALUE self, VALUE id) {
672
673
  if (Qnil != id) {
673
674
  size_t len = RSTRING_LEN(id) + 1;
674
675
 
675
- oj_default_options.create_id = ALLOC_N(char, len);
676
+ oj_default_options.create_id = OJ_R_ALLOC_N(char, len);
676
677
  strcpy((char *)oj_default_options.create_id, StringValuePtr(id));
677
678
  oj_default_options.create_id_len = len - 1;
678
679
  }
data/ext/oj/object.c CHANGED
@@ -308,7 +308,7 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
308
308
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "Invalid struct data");
309
309
  return 1;
310
310
  }
311
- e1 = *RARRAY_PTR(value);
311
+ e1 = *RARRAY_CONST_PTR(value);
312
312
  // check for anonymous Struct
313
313
  if (T_ARRAY == rb_type(e1)) {
314
314
  VALUE args[1024];
@@ -322,10 +322,10 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
322
322
  sc = rb_funcall2(rb_cStruct, oj_new_id, cnt, args);
323
323
  } else {
324
324
  // If struct is not defined then we let this fail and raise an exception.
325
- sc = oj_name2struct(pi, *RARRAY_PTR(value), rb_eArgError);
325
+ sc = oj_name2struct(pi, *RARRAY_CONST_PTR(value), rb_eArgError);
326
326
  }
327
327
  if (sc == rb_cRange) {
328
- parent->val = rb_class_new_instance(len - 1, RARRAY_PTR(value) + 1, rb_cRange);
328
+ parent->val = rb_class_new_instance(len - 1, RARRAY_CONST_PTR(value) + 1, rb_cRange);
329
329
  } else {
330
330
  // Create a properly initialized struct instance without calling the initialize method.
331
331
  parent->val = rb_obj_alloc(sc);
@@ -346,20 +346,20 @@ static int hat_value(ParseInfo pi, Val parent, const char *key, size_t klen, vol
346
346
  int i;
347
347
 
348
348
  for (i = 0; i < len - 1; i++) {
349
- rb_struct_aset(parent->val, INT2FIX(i), RARRAY_PTR(value)[i + 1]);
349
+ rb_struct_aset(parent->val, INT2FIX(i), RARRAY_CONST_PTR(value)[i + 1]);
350
350
  }
351
351
  }
352
352
  }
353
353
  return 1;
354
354
  } else if (3 <= klen && '#' == key[1]) {
355
- volatile VALUE *a;
355
+ volatile const VALUE *a;
356
356
 
357
357
  if (2 != len) {
358
358
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
359
359
  return 1;
360
360
  }
361
361
  parent->val = rb_hash_new();
362
- a = RARRAY_PTR(value);
362
+ a = RARRAY_CONST_PTR(value);
363
363
  rb_hash_aset(parent->val, *a, a[1]);
364
364
 
365
365
  return 1;
@@ -546,7 +546,7 @@ WHICH_TYPE:
546
546
  } else {
547
547
  if (3 <= klen && '^' == *key && '#' == key[1] && T_ARRAY == rb_type(value)) {
548
548
  long len = RARRAY_LEN(value);
549
- volatile VALUE *a = RARRAY_PTR(value);
549
+ volatile const VALUE *a = RARRAY_CONST_PTR(value);
550
550
 
551
551
  if (2 != len) {
552
552
  oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "invalid hash pair");
@@ -609,9 +609,7 @@ WHICH_TYPE:
609
609
  }
610
610
 
611
611
  static VALUE start_hash(ParseInfo pi) {
612
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
613
- oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
614
- }
612
+ TRACE_PARSE_IN(pi->options.trace, "start_hash", pi);
615
613
  return Qnil;
616
614
  }
617
615
 
@@ -627,9 +625,7 @@ static void end_hash(ParseInfo pi) {
627
625
  oj_odd_free(oa);
628
626
  parent->odd_args = NULL;
629
627
  }
630
- if (RB_UNLIKELY(Yes == pi->options.trace)) {
631
- oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
632
- }
628
+ TRACE_PARSE_HASH_END(pi->options.trace, pi);
633
629
  }
634
630
 
635
631
  static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
data/ext/oj/odd.c CHANGED
@@ -1,6 +1,7 @@
1
1
  // Copyright (c) 2011 Peter Ohler. All rights reserved.
2
2
  // Licensed under the MIT License. See LICENSE file in the project root for license details.
3
3
 
4
+ #include "mem.h"
4
5
  #include "odd.h"
5
6
 
6
7
  #include <string.h>
@@ -68,7 +69,7 @@ void print_all_odds(const char *label) {
68
69
  }
69
70
 
70
71
  static Odd odd_create(void) {
71
- Odd odd = ALLOC(struct _odd);
72
+ Odd odd = OJ_R_ALLOC(struct _odd);
72
73
 
73
74
  memset(odd, 0, sizeof(struct _odd));
74
75
  odd->next = odds;
@@ -172,7 +173,7 @@ Odd oj_get_oddc(const char *classname, size_t len) {
172
173
  }
173
174
 
174
175
  OddArgs oj_odd_alloc_args(Odd odd) {
175
- OddArgs oa = ALLOC_N(struct _oddArgs, 1);
176
+ OddArgs oa = OJ_R_ALLOC_N(struct _oddArgs, 1);
176
177
  VALUE *a;
177
178
  int i;
178
179
 
@@ -184,7 +185,7 @@ OddArgs oj_odd_alloc_args(Odd odd) {
184
185
  }
185
186
 
186
187
  void oj_odd_free(OddArgs args) {
187
- xfree(args);
188
+ OJ_R_FREE(args);
188
189
  }
189
190
 
190
191
  int oj_odd_set_arg(OddArgs args, const char *key, size_t klen, VALUE value) {
@@ -210,7 +211,7 @@ void oj_reg_odd(VALUE clas, VALUE create_object, VALUE create_method, int mcnt,
210
211
  odd = odd_create();
211
212
  odd->clas = clas;
212
213
  rb_gc_register_mark_object(odd->clas);
213
- if (NULL == (odd->classname = strdup(rb_class2name(clas)))) {
214
+ if (NULL == (odd->classname = OJ_STRDUP(rb_class2name(clas)))) {
214
215
  rb_raise(rb_eNoMemError, "for class name.");
215
216
  }
216
217
  odd->clen = strlen(odd->classname);
@@ -224,13 +225,13 @@ void oj_reg_odd(VALUE clas, VALUE create_object, VALUE create_method, int mcnt,
224
225
  *fp = 0;
225
226
  switch (rb_type(*members)) {
226
227
  case T_STRING:
227
- if (NULL == (*np = strdup(RSTRING_PTR(*members)))) {
228
+ if (NULL == (*np = OJ_STRDUP(RSTRING_PTR(*members)))) {
228
229
  rb_raise(rb_eNoMemError, "for attribute name.");
229
230
  }
230
231
  break;
231
232
  case T_SYMBOL:
232
233
  // The symbol can move and invalidate the name so make a copy.
233
- if (NULL == (*np = strdup(rb_id2name(SYM2ID(*members))))) {
234
+ if (NULL == (*np = OJ_STRDUP(rb_id2name(SYM2ID(*members))))) {
234
235
  rb_raise(rb_eNoMemError, "for attribute name.");
235
236
  }
236
237
  break;
data/ext/oj/oj.c CHANGED
@@ -11,6 +11,7 @@
11
11
  #include <sys/types.h>
12
12
  #include <unistd.h>
13
13
 
14
+ #include "mem.h"
14
15
  #include "dump.h"
15
16
  #include "encode.h"
16
17
  #include "intern.h"
@@ -782,7 +783,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
782
783
  } else if (create_id_sym == k) {
783
784
  if (Qnil == v) {
784
785
  if (oj_json_class != oj_default_options.create_id && NULL != copts->create_id) {
785
- xfree((char *)oj_default_options.create_id);
786
+ OJ_R_FREE((char *)oj_default_options.create_id);
786
787
  }
787
788
  copts->create_id = NULL;
788
789
  copts->create_id_len = 0;
@@ -791,7 +792,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
791
792
 
792
793
  len = RSTRING_LEN(v);
793
794
  if (len != copts->create_id_len || 0 != strcmp(copts->create_id, str)) {
794
- copts->create_id = ALLOC_N(char, len + 1);
795
+ copts->create_id = OJ_R_ALLOC_N(char, len + 1);
795
796
  strcpy((char *)copts->create_id, str);
796
797
  copts->create_id_len = len;
797
798
  }
@@ -911,7 +912,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
911
912
  copts->array_class = v;
912
913
  }
913
914
  } else if (ignore_sym == k) {
914
- xfree(copts->ignore);
915
+ OJ_R_FREE(copts->ignore);
915
916
  copts->ignore = NULL;
916
917
  if (Qnil != v) {
917
918
  int cnt;
@@ -921,7 +922,7 @@ static int parse_options_cb(VALUE k, VALUE v, VALUE opts) {
921
922
  if (0 < cnt) {
922
923
  int i;
923
924
 
924
- copts->ignore = ALLOC_N(VALUE, cnt + 1);
925
+ copts->ignore = OJ_R_ALLOC_N(VALUE, cnt + 1);
925
926
  for (i = 0; i < cnt; i++) {
926
927
  copts->ignore[i] = RARRAY_AREF(v, i);
927
928
  }
@@ -1159,7 +1160,7 @@ static VALUE load_file(int argc, VALUE *argv, VALUE self) {
1159
1160
  WCHAR *wide_path;
1160
1161
  wide_path = rb_w32_mbstr_to_wstr(CP_UTF8, path, -1, NULL);
1161
1162
  fd = rb_w32_wopen(wide_path, O_RDONLY);
1162
- free(wide_path);
1163
+ OJ_FREE(wide_path);
1163
1164
  }
1164
1165
  #else
1165
1166
  fd = open(path, O_RDONLY);
@@ -1724,6 +1725,10 @@ debug_odd(VALUE self, VALUE label) {
1724
1725
  return Qnil;
1725
1726
  }
1726
1727
 
1728
+ static VALUE mem_report(VALUE self) {
1729
+ oj_mem_report();
1730
+ return Qnil;
1731
+ }
1727
1732
 
1728
1733
  /* Document-module: Oj
1729
1734
  *
@@ -1812,6 +1817,8 @@ void Init_oj(void) {
1812
1817
 
1813
1818
  rb_define_module_function(Oj, "optimize_rails", oj_optimize_rails, 0);
1814
1819
 
1820
+ rb_define_module_function(Oj, "mem_report", mem_report, 0);
1821
+
1815
1822
  oj_add_value_id = rb_intern("add_value");
1816
1823
  oj_array_append_id = rb_intern("array_append");
1817
1824
  oj_array_end_id = rb_intern("array_end");
data/ext/oj/oj.h CHANGED
@@ -379,6 +379,12 @@ extern bool oj_use_hash_alt;
379
379
  extern bool oj_use_array_alt;
380
380
  extern bool string_writer_optimized;
381
381
 
382
+ static inline VALUE oj_safe_string_convert(VALUE obj) {
383
+ VALUE rstr = rb_funcall(obj, oj_to_s_id, 0);
384
+ StringValue(rstr);
385
+ return rstr;
386
+ }
387
+
382
388
  #define APPEND_CHARS(buffer, chars, size) \
383
389
  { \
384
390
  memcpy(buffer, chars, size); \