oj 3.13.9 → 3.16.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +101 -0
- data/README.md +13 -2
- data/ext/oj/buf.h +11 -6
- data/ext/oj/cache.c +25 -24
- data/ext/oj/cache8.c +10 -9
- data/ext/oj/circarray.c +8 -6
- data/ext/oj/circarray.h +2 -2
- data/ext/oj/code.c +19 -33
- data/ext/oj/code.h +2 -2
- data/ext/oj/compat.c +20 -60
- data/ext/oj/custom.c +76 -155
- data/ext/oj/debug.c +3 -9
- data/ext/oj/dump.c +203 -213
- data/ext/oj/dump.h +26 -12
- data/ext/oj/dump_compat.c +565 -642
- data/ext/oj/dump_leaf.c +17 -63
- data/ext/oj/dump_object.c +59 -181
- data/ext/oj/dump_strict.c +24 -48
- data/ext/oj/encoder.c +43 -0
- data/ext/oj/err.c +2 -13
- data/ext/oj/err.h +9 -12
- data/ext/oj/extconf.rb +18 -7
- data/ext/oj/fast.c +83 -108
- data/ext/oj/intern.c +52 -50
- data/ext/oj/intern.h +4 -8
- data/ext/oj/mem.c +318 -0
- data/ext/oj/mem.h +53 -0
- data/ext/oj/mimic_json.c +104 -81
- data/ext/oj/object.c +50 -67
- data/ext/oj/odd.c +89 -67
- data/ext/oj/odd.h +15 -15
- data/ext/oj/oj.c +171 -106
- data/ext/oj/oj.h +96 -74
- data/ext/oj/parse.c +169 -189
- data/ext/oj/parse.h +23 -24
- data/ext/oj/parser.c +89 -34
- data/ext/oj/parser.h +20 -9
- data/ext/oj/rails.c +86 -151
- data/ext/oj/rails.h +1 -1
- data/ext/oj/reader.c +12 -15
- data/ext/oj/reader.h +4 -2
- data/ext/oj/resolve.c +3 -4
- data/ext/oj/rxclass.c +6 -5
- data/ext/oj/rxclass.h +1 -1
- data/ext/oj/saj.c +21 -32
- data/ext/oj/saj2.c +329 -93
- data/ext/oj/saj2.h +23 -0
- data/ext/oj/scp.c +3 -14
- data/ext/oj/sparse.c +26 -70
- data/ext/oj/stream_writer.c +12 -22
- data/ext/oj/strict.c +20 -52
- data/ext/oj/string_writer.c +21 -22
- data/ext/oj/trace.h +31 -4
- data/ext/oj/usual.c +105 -150
- data/ext/oj/usual.h +68 -0
- data/ext/oj/util.h +1 -1
- data/ext/oj/val_stack.c +1 -1
- data/ext/oj/val_stack.h +8 -7
- data/ext/oj/validate.c +21 -26
- data/ext/oj/wab.c +32 -69
- data/lib/oj/active_support_helper.rb +1 -3
- data/lib/oj/bag.rb +7 -1
- data/lib/oj/easy_hash.rb +4 -5
- data/lib/oj/error.rb +0 -1
- data/lib/oj/json.rb +162 -150
- data/lib/oj/mimic.rb +6 -2
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/state.rb +9 -6
- data/lib/oj/version.rb +1 -2
- data/lib/oj.rb +2 -0
- data/pages/Compatibility.md +1 -1
- data/pages/InstallOptions.md +20 -0
- data/pages/JsonGem.md +15 -0
- data/pages/Modes.md +6 -3
- data/pages/Options.md +10 -0
- data/pages/Rails.md +12 -0
- data/test/_test_active.rb +8 -9
- data/test/_test_active_mimic.rb +7 -8
- data/test/_test_mimic_rails.rb +17 -20
- data/test/activerecord/result_test.rb +5 -6
- data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
- data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
- data/test/{activesupport5 → activesupport7}/encoding_test.rb +20 -34
- data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
- data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
- data/test/files.rb +15 -15
- data/test/foo.rb +15 -15
- data/test/helper.rb +11 -8
- data/test/isolated/shared.rb +3 -2
- data/test/json_gem/json_addition_test.rb +2 -2
- data/test/json_gem/json_common_interface_test.rb +8 -6
- data/test/json_gem/json_encoding_test.rb +0 -0
- data/test/json_gem/json_ext_parser_test.rb +1 -0
- data/test/json_gem/json_fixtures_test.rb +3 -2
- data/test/json_gem/json_generator_test.rb +49 -37
- data/test/json_gem/json_generic_object_test.rb +11 -11
- data/test/json_gem/json_parser_test.rb +54 -47
- data/test/json_gem/json_string_matching_test.rb +9 -9
- data/test/json_gem/test_helper.rb +7 -3
- data/test/mem.rb +13 -12
- data/test/perf.rb +21 -26
- data/test/perf_compat.rb +31 -33
- data/test/perf_dump.rb +50 -0
- data/test/perf_fast.rb +80 -82
- data/test/perf_file.rb +27 -29
- data/test/perf_object.rb +65 -69
- data/test/perf_once.rb +12 -11
- data/test/perf_parser.rb +42 -48
- data/test/perf_saj.rb +46 -54
- data/test/perf_scp.rb +57 -69
- data/test/perf_simple.rb +41 -39
- data/test/perf_strict.rb +68 -70
- data/test/perf_wab.rb +67 -69
- data/test/prec.rb +3 -3
- data/test/sample/change.rb +0 -1
- data/test/sample/dir.rb +0 -1
- data/test/sample/doc.rb +0 -1
- data/test/sample/file.rb +0 -1
- data/test/sample/group.rb +0 -1
- data/test/sample/hasprops.rb +0 -1
- data/test/sample/layer.rb +0 -1
- data/test/sample/rect.rb +0 -1
- data/test/sample/shape.rb +0 -1
- data/test/sample/text.rb +0 -1
- data/test/sample.rb +16 -16
- data/test/sample_json.rb +8 -8
- data/test/test_compat.rb +95 -43
- data/test/test_custom.rb +72 -51
- data/test/test_debian.rb +7 -10
- data/test/test_fast.rb +102 -87
- data/test/test_file.rb +41 -30
- data/test/test_gc.rb +16 -5
- data/test/test_generate.rb +5 -5
- data/test/test_hash.rb +4 -4
- data/test/test_integer_range.rb +9 -9
- data/test/test_null.rb +20 -20
- data/test/test_object.rb +85 -96
- data/test/test_parser.rb +6 -22
- data/test/test_parser_debug.rb +27 -0
- data/test/test_parser_saj.rb +115 -23
- data/test/test_parser_usual.rb +6 -6
- data/test/test_rails.rb +2 -2
- data/test/test_saj.rb +10 -8
- data/test/test_scp.rb +37 -39
- data/test/test_strict.rb +40 -32
- data/test/test_various.rb +163 -84
- data/test/test_wab.rb +48 -44
- data/test/test_writer.rb +47 -47
- data/test/tests.rb +13 -5
- data/test/tests_mimic.rb +12 -3
- data/test/tests_mimic_addition.rb +12 -3
- metadata +34 -144
- data/test/activesupport4/decoding_test.rb +0 -108
- data/test/activesupport4/encoding_test.rb +0 -531
- data/test/activesupport4/test_helper.rb +0 -41
- data/test/activesupport5/test_helper.rb +0 -72
- data/test/bar.rb +0 -16
- data/test/baz.rb +0 -16
- data/test/bug.rb +0 -16
- data/test/zoo.rb +0 -13
data/ext/oj/intern.c
CHANGED
@@ -8,7 +8,9 @@
|
|
8
8
|
#if HAVE_PTHREAD_MUTEX_INIT
|
9
9
|
#include <pthread.h>
|
10
10
|
#endif
|
11
|
+
|
11
12
|
#include "cache.h"
|
13
|
+
#include "mem.h"
|
12
14
|
#include "parse.h"
|
13
15
|
|
14
16
|
// Only used for the class cache so 256 should be sufficient.
|
@@ -20,10 +22,10 @@
|
|
20
22
|
|
21
23
|
typedef struct _keyVal {
|
22
24
|
struct _keyVal *next;
|
23
|
-
const char
|
25
|
+
const char *key;
|
24
26
|
size_t len;
|
25
27
|
VALUE val;
|
26
|
-
} *
|
28
|
+
} *KeyVal;
|
27
29
|
|
28
30
|
typedef struct _hash {
|
29
31
|
struct _keyVal slots[HASH_SLOT_CNT];
|
@@ -32,19 +34,16 @@ typedef struct _hash {
|
|
32
34
|
#else
|
33
35
|
VALUE mutex;
|
34
36
|
#endif
|
35
|
-
} *
|
37
|
+
} *Hash;
|
36
38
|
|
37
39
|
struct _hash class_hash;
|
38
40
|
struct _hash attr_hash;
|
39
41
|
|
40
|
-
static
|
41
|
-
static VALUE str_cache_obj;
|
42
|
+
static VALUE str_cache_obj;
|
42
43
|
|
43
|
-
static
|
44
|
-
static VALUE sym_cache_obj;
|
44
|
+
static VALUE sym_cache_obj;
|
45
45
|
|
46
|
-
static
|
47
|
-
static VALUE attr_cache_obj;
|
46
|
+
static VALUE attr_cache_obj;
|
48
47
|
|
49
48
|
static VALUE form_str(const char *str, size_t len) {
|
50
49
|
return rb_str_freeze(rb_utf8_str_new(str, len));
|
@@ -58,47 +57,48 @@ static VALUE form_attr(const char *str, size_t len) {
|
|
58
57
|
char buf[256];
|
59
58
|
|
60
59
|
if (sizeof(buf) - 2 <= len) {
|
61
|
-
char *b =
|
60
|
+
char *b = OJ_R_ALLOC_N(char, len + 2);
|
62
61
|
ID id;
|
63
62
|
|
64
63
|
if ('~' == *str) {
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
64
|
+
memcpy(b, str + 1, len - 1);
|
65
|
+
b[len - 1] = '\0';
|
66
|
+
len -= 2;
|
67
|
+
} else {
|
68
|
+
*b = '@';
|
69
|
+
memcpy(b + 1, str, len);
|
70
|
+
b[len + 1] = '\0';
|
71
|
+
}
|
73
72
|
id = rb_intern3(buf, len + 1, oj_utf8_encoding);
|
74
|
-
|
73
|
+
OJ_R_FREE(b);
|
75
74
|
return id;
|
76
75
|
}
|
77
76
|
if ('~' == *str) {
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
memcpy(buf, str + 1, len - 1);
|
78
|
+
buf[len - 1] = '\0';
|
79
|
+
len -= 2;
|
81
80
|
} else {
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
*buf = '@';
|
82
|
+
memcpy(buf + 1, str, len);
|
83
|
+
buf[len + 1] = '\0';
|
85
84
|
}
|
86
85
|
return (VALUE)rb_intern3(buf, len + 1, oj_utf8_encoding);
|
87
86
|
}
|
88
87
|
|
89
|
-
void oj_hash_init() {
|
88
|
+
void oj_hash_init(void) {
|
90
89
|
VALUE cache_class = rb_define_class_under(Oj, "Cache", rb_cObject);
|
90
|
+
rb_undef_alloc_func(cache_class);
|
91
91
|
|
92
|
-
str_cache
|
93
|
-
str_cache_obj
|
92
|
+
struct _cache *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
94
|
rb_gc_register_address(&str_cache_obj);
|
95
95
|
|
96
|
-
sym_cache
|
97
|
-
sym_cache_obj
|
96
|
+
struct _cache *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
98
|
rb_gc_register_address(&sym_cache_obj);
|
99
99
|
|
100
|
-
attr_cache
|
101
|
-
attr_cache_obj
|
100
|
+
struct _cache *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
102
|
rb_gc_register_address(&attr_cache_obj);
|
103
103
|
|
104
104
|
memset(class_hash.slots, 0, sizeof(class_hash.slots));
|
@@ -118,18 +118,17 @@ oj_str_intern(const char *key, size_t len) {
|
|
118
118
|
#if HAVE_RB_ENC_INTERNED_STR && 0
|
119
119
|
return rb_enc_interned_str(key, len, rb_utf8_encoding());
|
120
120
|
#else
|
121
|
-
return cache_intern(
|
121
|
+
return cache_intern(DATA_PTR(str_cache_obj), key, len);
|
122
122
|
#endif
|
123
123
|
}
|
124
124
|
|
125
125
|
VALUE
|
126
126
|
oj_sym_intern(const char *key, size_t len) {
|
127
|
-
return cache_intern(
|
127
|
+
return cache_intern(DATA_PTR(sym_cache_obj), key, len);
|
128
128
|
}
|
129
129
|
|
130
|
-
ID
|
131
|
-
|
132
|
-
return cache_intern(attr_cache, key, len);
|
130
|
+
ID oj_attr_intern(const char *key, size_t len) {
|
131
|
+
return cache_intern(DATA_PTR(attr_cache_obj), key, len);
|
133
132
|
}
|
134
133
|
|
135
134
|
static uint64_t hash_calc(const uint8_t *key, size_t len) {
|
@@ -183,10 +182,10 @@ static VALUE resolve_classname(VALUE mod, const char *classname, int auto_define
|
|
183
182
|
static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int auto_define, VALUE error_class) {
|
184
183
|
char class_name[1024];
|
185
184
|
VALUE clas;
|
186
|
-
char
|
187
|
-
char
|
188
|
-
const char *n
|
189
|
-
size_t
|
185
|
+
char *end = class_name + sizeof(class_name) - 1;
|
186
|
+
char *s;
|
187
|
+
const char *n = name;
|
188
|
+
size_t nlen = len;
|
190
189
|
|
191
190
|
clas = rb_cObject;
|
192
191
|
for (s = class_name; 0 < len; n++, len--) {
|
@@ -209,11 +208,11 @@ static VALUE resolve_classpath(ParseInfo pi, const char *name, size_t len, int a
|
|
209
208
|
}
|
210
209
|
*s = '\0';
|
211
210
|
if (Qundef == (clas = resolve_classname(clas, class_name, auto_define))) {
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
211
|
+
if (sizeof(class_name) <= nlen) {
|
212
|
+
nlen = sizeof(class_name) - 1;
|
213
|
+
}
|
214
|
+
strncpy(class_name, name, nlen);
|
215
|
+
class_name[nlen] = '\0';
|
217
216
|
oj_set_error_at(pi, error_class, __FILE__, __LINE__, "class '%s' is not defined", class_name);
|
218
217
|
if (Qnil != error_class) {
|
219
218
|
pi->err_class = error_class;
|
@@ -245,7 +244,7 @@ VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int
|
|
245
244
|
}
|
246
245
|
bucket = b;
|
247
246
|
}
|
248
|
-
b =
|
247
|
+
b = OJ_R_ALLOC(struct _keyVal);
|
249
248
|
b->next = NULL;
|
250
249
|
bucket->next = b;
|
251
250
|
bucket = b;
|
@@ -266,7 +265,7 @@ VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int
|
|
266
265
|
}
|
267
266
|
bucket = b;
|
268
267
|
}
|
269
|
-
b =
|
268
|
+
b = OJ_R_ALLOC(struct _keyVal);
|
270
269
|
b->next = NULL;
|
271
270
|
bucket->next = b;
|
272
271
|
bucket = b;
|
@@ -275,11 +274,12 @@ VALUE oj_class_intern(const char *key, size_t len, bool safe, ParseInfo pi, int
|
|
275
274
|
bucket->len = len;
|
276
275
|
bucket->val = resolve_classpath(pi, key, len, auto_define, error_class);
|
277
276
|
}
|
277
|
+
rb_gc_register_mark_object(bucket->val);
|
278
278
|
return bucket->val;
|
279
279
|
}
|
280
280
|
|
281
281
|
char *oj_strndup(const char *s, size_t len) {
|
282
|
-
char *d =
|
282
|
+
char *d = OJ_R_ALLOC_N(char, len + 1);
|
283
283
|
|
284
284
|
memcpy(d, s, len);
|
285
285
|
d[len] = '\0';
|
@@ -287,8 +287,10 @@ char *oj_strndup(const char *s, size_t len) {
|
|
287
287
|
return d;
|
288
288
|
}
|
289
289
|
|
290
|
-
|
290
|
+
/*
|
291
|
+
void intern_cleanup(void) {
|
291
292
|
cache_free(str_cache);
|
292
293
|
cache_free(sym_cache);
|
293
294
|
cache_free(attr_cache);
|
294
295
|
}
|
296
|
+
*/
|
data/ext/oj/intern.h
CHANGED
@@ -4,22 +4,18 @@
|
|
4
4
|
#ifndef OJ_INTERN_H
|
5
5
|
#define OJ_INTERN_H
|
6
6
|
|
7
|
-
#include <stdbool.h>
|
8
7
|
#include <ruby.h>
|
8
|
+
#include <stdbool.h>
|
9
9
|
|
10
10
|
struct _parseInfo;
|
11
11
|
|
12
|
-
extern void oj_hash_init();
|
12
|
+
extern void oj_hash_init(void);
|
13
13
|
|
14
14
|
extern VALUE oj_str_intern(const char *key, size_t len);
|
15
15
|
extern VALUE oj_sym_intern(const char *key, size_t len);
|
16
16
|
extern ID oj_attr_intern(const char *key, size_t len);
|
17
|
-
extern VALUE
|
18
|
-
|
19
|
-
bool safe,
|
20
|
-
struct _parseInfo *pi,
|
21
|
-
int auto_define,
|
22
|
-
VALUE error_class);
|
17
|
+
extern VALUE
|
18
|
+
oj_class_intern(const char *key, size_t len, bool safe, struct _parseInfo *pi, int auto_define, VALUE error_class);
|
23
19
|
|
24
20
|
extern char *oj_strndup(const char *s, size_t len);
|
25
21
|
|
data/ext/oj/mem.c
ADDED
@@ -0,0 +1,318 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include "mem.h"
|
4
|
+
|
5
|
+
#include <pthread.h>
|
6
|
+
#include <ruby.h>
|
7
|
+
#include <stdbool.h>
|
8
|
+
#include <stdint.h>
|
9
|
+
#include <stdio.h>
|
10
|
+
#include <stdlib.h>
|
11
|
+
#include <string.h>
|
12
|
+
|
13
|
+
typedef struct _rec {
|
14
|
+
struct _rec *next;
|
15
|
+
const void *ptr;
|
16
|
+
size_t size;
|
17
|
+
const char *file;
|
18
|
+
int line;
|
19
|
+
bool ruby;
|
20
|
+
} *Rec;
|
21
|
+
|
22
|
+
typedef struct _rep {
|
23
|
+
struct _rep *next;
|
24
|
+
size_t size;
|
25
|
+
const char *file;
|
26
|
+
int line;
|
27
|
+
int cnt;
|
28
|
+
} *Rep;
|
29
|
+
|
30
|
+
#ifdef MEM_DEBUG
|
31
|
+
|
32
|
+
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
33
|
+
static Rec recs = NULL;
|
34
|
+
static const char mem_pad[] = "--- This is a memory pad and should not change until being freed. ---";
|
35
|
+
|
36
|
+
void *oj_malloc(size_t size, const char *file, int line) {
|
37
|
+
void *ptr = malloc(size + sizeof(mem_pad));
|
38
|
+
|
39
|
+
if (NULL != ptr) {
|
40
|
+
Rec r = (Rec)malloc(sizeof(struct _rec));
|
41
|
+
|
42
|
+
if (NULL != r) {
|
43
|
+
strcpy(((char *)ptr) + size, mem_pad);
|
44
|
+
r->ptr = ptr;
|
45
|
+
r->size = size;
|
46
|
+
r->file = file;
|
47
|
+
r->line = line;
|
48
|
+
r->ruby = false;
|
49
|
+
pthread_mutex_lock(&lock);
|
50
|
+
r->next = recs;
|
51
|
+
recs = r;
|
52
|
+
pthread_mutex_unlock(&lock);
|
53
|
+
} else {
|
54
|
+
free(ptr);
|
55
|
+
ptr = NULL;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
return ptr;
|
59
|
+
}
|
60
|
+
|
61
|
+
void *oj_realloc(void *orig, size_t size, const char *file, int line) {
|
62
|
+
void *ptr = realloc(orig, size + sizeof(mem_pad));
|
63
|
+
Rec r;
|
64
|
+
|
65
|
+
if (NULL != ptr) {
|
66
|
+
strcpy(((char *)ptr) + size, mem_pad);
|
67
|
+
pthread_mutex_lock(&lock);
|
68
|
+
for (r = recs; NULL != r; r = r->next) {
|
69
|
+
if (orig == r->ptr) {
|
70
|
+
r->ptr = ptr;
|
71
|
+
r->size = size;
|
72
|
+
r->file = file;
|
73
|
+
r->line = line;
|
74
|
+
r->ruby = false;
|
75
|
+
break;
|
76
|
+
}
|
77
|
+
}
|
78
|
+
pthread_mutex_unlock(&lock);
|
79
|
+
if (NULL == r) {
|
80
|
+
printf("Realloc at %s:%d (%p) not allocated.\n", file, line, orig);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
return ptr;
|
84
|
+
}
|
85
|
+
|
86
|
+
void *oj_calloc(size_t count, size_t size, const char *file, int line) {
|
87
|
+
void *ptr;
|
88
|
+
|
89
|
+
size *= count;
|
90
|
+
if (NULL != (ptr = malloc(size + sizeof(mem_pad)))) {
|
91
|
+
Rec r = (Rec)malloc(sizeof(struct _rec));
|
92
|
+
|
93
|
+
if (NULL != r) {
|
94
|
+
memset(ptr, 0, size);
|
95
|
+
strcpy(((char *)ptr) + size, mem_pad);
|
96
|
+
r->ptr = ptr;
|
97
|
+
r->size = size;
|
98
|
+
r->file = file;
|
99
|
+
r->line = line;
|
100
|
+
r->ruby = false;
|
101
|
+
pthread_mutex_lock(&lock);
|
102
|
+
r->next = recs;
|
103
|
+
recs = r;
|
104
|
+
pthread_mutex_unlock(&lock);
|
105
|
+
} else {
|
106
|
+
free(ptr);
|
107
|
+
ptr = NULL;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
return ptr;
|
111
|
+
}
|
112
|
+
|
113
|
+
void *oj_r_alloc(size_t size, const char *file, int line) {
|
114
|
+
void *ptr = ruby_xmalloc(size + sizeof(mem_pad));
|
115
|
+
|
116
|
+
if (NULL != ptr) {
|
117
|
+
Rec r = (Rec)malloc(sizeof(struct _rec));
|
118
|
+
|
119
|
+
if (NULL != r) {
|
120
|
+
strcpy(((char *)ptr) + size, mem_pad);
|
121
|
+
r->ptr = ptr;
|
122
|
+
r->size = size;
|
123
|
+
r->file = file;
|
124
|
+
r->line = line;
|
125
|
+
r->ruby = true;
|
126
|
+
pthread_mutex_lock(&lock);
|
127
|
+
r->next = recs;
|
128
|
+
recs = r;
|
129
|
+
pthread_mutex_unlock(&lock);
|
130
|
+
} else {
|
131
|
+
free(ptr);
|
132
|
+
ptr = NULL;
|
133
|
+
}
|
134
|
+
}
|
135
|
+
return ptr;
|
136
|
+
}
|
137
|
+
|
138
|
+
void *oj_r_realloc(void *orig, size_t size, const char *file, int line) {
|
139
|
+
void *ptr = ruby_xrealloc2(orig, 1, size + sizeof(mem_pad));
|
140
|
+
Rec r;
|
141
|
+
|
142
|
+
if (NULL != ptr) {
|
143
|
+
strcpy(((char *)ptr) + size, mem_pad);
|
144
|
+
pthread_mutex_lock(&lock);
|
145
|
+
for (r = recs; NULL != r; r = r->next) {
|
146
|
+
if (orig == r->ptr) {
|
147
|
+
r->ptr = ptr;
|
148
|
+
r->size = size;
|
149
|
+
r->file = file;
|
150
|
+
r->line = line;
|
151
|
+
r->ruby = true;
|
152
|
+
break;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
pthread_mutex_unlock(&lock);
|
156
|
+
if (NULL == r) {
|
157
|
+
printf("Realloc at %s:%d (%p) not allocated.\n", file, line, orig);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
return ptr;
|
161
|
+
}
|
162
|
+
|
163
|
+
void oj_freed(void *ptr, const char *file, int line, bool ruby) {
|
164
|
+
if (NULL != ptr) {
|
165
|
+
Rec r = NULL;
|
166
|
+
Rec prev = NULL;
|
167
|
+
|
168
|
+
pthread_mutex_lock(&lock);
|
169
|
+
for (r = recs; NULL != r; r = r->next) {
|
170
|
+
if (ptr == r->ptr) {
|
171
|
+
if (NULL == prev) {
|
172
|
+
recs = r->next;
|
173
|
+
} else {
|
174
|
+
prev->next = r->next;
|
175
|
+
}
|
176
|
+
break;
|
177
|
+
}
|
178
|
+
prev = r;
|
179
|
+
}
|
180
|
+
pthread_mutex_unlock(&lock);
|
181
|
+
if (NULL == r) {
|
182
|
+
printf("Free at %s:%d (%p) not allocated or already freed.\n", file, line, ptr);
|
183
|
+
} else {
|
184
|
+
char *pad = (char *)r->ptr + r->size;
|
185
|
+
|
186
|
+
if (r->ruby != ruby) {
|
187
|
+
if (r->ruby) {
|
188
|
+
printf("Memory at %s:%d (%p) allocated with Ruby allocator and freed with stdlib free.\n",
|
189
|
+
file,
|
190
|
+
line,
|
191
|
+
ptr);
|
192
|
+
} else {
|
193
|
+
printf("Memory at %s:%d (%p) allocated with stdlib allocator and freed with Ruby free.\n",
|
194
|
+
file,
|
195
|
+
line,
|
196
|
+
ptr);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
if (0 != strcmp(mem_pad, pad)) {
|
200
|
+
uint8_t *p;
|
201
|
+
uint8_t *end = (uint8_t *)pad + sizeof(mem_pad);
|
202
|
+
|
203
|
+
printf("Memory at %s:%d (%p) write outside allocated.\n", file, line, ptr);
|
204
|
+
for (p = (uint8_t *)pad; p < end; p++) {
|
205
|
+
if (0x20 < *p && *p < 0x7f) {
|
206
|
+
printf("%c ", *p);
|
207
|
+
} else {
|
208
|
+
printf("%02x ", *(uint8_t *)p);
|
209
|
+
}
|
210
|
+
}
|
211
|
+
printf("\n");
|
212
|
+
}
|
213
|
+
free(r);
|
214
|
+
}
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
void oj_r_free(void *ptr, const char *file, int line) {
|
219
|
+
oj_freed(ptr, file, line, true);
|
220
|
+
xfree(ptr);
|
221
|
+
}
|
222
|
+
|
223
|
+
void oj_free(void *ptr, const char *file, int line) {
|
224
|
+
oj_freed(ptr, file, line, false);
|
225
|
+
free(ptr);
|
226
|
+
}
|
227
|
+
|
228
|
+
char *oj_mem_strdup(const char *str, const char *file, int line) {
|
229
|
+
size_t size = strlen(str) + 1;
|
230
|
+
char *ptr = (char *)malloc(size + sizeof(mem_pad));
|
231
|
+
|
232
|
+
if (NULL != ptr) {
|
233
|
+
Rec r = (Rec)malloc(sizeof(struct _rec));
|
234
|
+
|
235
|
+
if (NULL != r) {
|
236
|
+
strcpy(ptr, str);
|
237
|
+
strcpy(((char *)ptr) + size, mem_pad);
|
238
|
+
r->ptr = (void *)ptr;
|
239
|
+
r->size = size;
|
240
|
+
r->file = file;
|
241
|
+
r->line = line;
|
242
|
+
r->ruby = false;
|
243
|
+
pthread_mutex_lock(&lock);
|
244
|
+
r->next = recs;
|
245
|
+
recs = r;
|
246
|
+
pthread_mutex_unlock(&lock);
|
247
|
+
} else {
|
248
|
+
free(ptr);
|
249
|
+
ptr = NULL;
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return ptr;
|
253
|
+
}
|
254
|
+
|
255
|
+
#endif
|
256
|
+
|
257
|
+
#ifdef MEM_DEBUG
|
258
|
+
|
259
|
+
static Rep update_reps(Rep reps, Rec r) {
|
260
|
+
Rep rp = reps;
|
261
|
+
|
262
|
+
for (; NULL != rp; rp = rp->next) {
|
263
|
+
if (rp->line == r->line && (rp->file == r->file || 0 == strcmp(rp->file, r->file))) {
|
264
|
+
rp->size += r->size;
|
265
|
+
rp->cnt++;
|
266
|
+
break;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
if (NULL == rp && NULL != (rp = (Rep)malloc(sizeof(struct _rep)))) {
|
270
|
+
rp->size = r->size;
|
271
|
+
rp->file = r->file;
|
272
|
+
rp->line = r->line;
|
273
|
+
rp->cnt = 1;
|
274
|
+
rp->next = reps;
|
275
|
+
reps = rp;
|
276
|
+
}
|
277
|
+
return reps;
|
278
|
+
}
|
279
|
+
|
280
|
+
static void print_stats() {
|
281
|
+
printf("\n--- Memory Usage Report --------------------------------------------------------\n");
|
282
|
+
pthread_mutex_lock(&lock);
|
283
|
+
|
284
|
+
if (NULL == recs) {
|
285
|
+
printf("No memory leaks\n");
|
286
|
+
} else {
|
287
|
+
Rep reps = NULL;
|
288
|
+
Rep rp;
|
289
|
+
Rec r;
|
290
|
+
size_t leaked = 0;
|
291
|
+
|
292
|
+
for (r = recs; NULL != r; r = r->next) {
|
293
|
+
reps = update_reps(reps, r);
|
294
|
+
}
|
295
|
+
while (NULL != (rp = reps)) {
|
296
|
+
reps = rp->next;
|
297
|
+
printf("%16s:%3d %8lu bytes over %d occurances allocated and not freed.\n",
|
298
|
+
rp->file,
|
299
|
+
rp->line,
|
300
|
+
rp->size,
|
301
|
+
rp->cnt);
|
302
|
+
leaked += rp->size;
|
303
|
+
free(rp);
|
304
|
+
}
|
305
|
+
printf("%lu bytes leaked\n", leaked);
|
306
|
+
}
|
307
|
+
pthread_mutex_unlock(&lock);
|
308
|
+
printf("--------------------------------------------------------------------------------\n");
|
309
|
+
}
|
310
|
+
|
311
|
+
#endif
|
312
|
+
|
313
|
+
void oj_mem_report(void) {
|
314
|
+
#ifdef MEM_DEBUG
|
315
|
+
rb_gc();
|
316
|
+
print_stats();
|
317
|
+
#endif
|
318
|
+
}
|
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 */
|