1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use crate::rubysys::types::{c_int, c_void, size_t, ssize_t, CallbackPtr, Value};
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
// void
// rb_gc_adjust_memory_usage(ssize_t diff)
pub fn rb_gc_adjust_memory_usage(diff: ssize_t);
// size_t
// rb_gc_count(void)
pub fn rb_gc_count() -> size_t;
// VALUE
// rb_gc_disable(void)
pub fn rb_gc_disable() -> Value;
// VALUE
// rb_gc_enable(void)
pub fn rb_gc_enable() -> Value;
// void
// rb_gc_mark(VALUE ptr)
pub fn rb_gc_mark(value: Value);
// void
// rb_gc_mark_movable(VALUE ptr)
pub fn rb_gc_mark_movable(value: Value);
// VALUE
// rb_gc_location(VALUE value)
pub fn rb_gc_location(value: Value) -> Value;
// void
// rb_gc_mark_maybe(VALUE obj)
pub fn rb_gc_mark_maybe(obj: Value);
// void
// rb_gc_register_address(VALUE *addr)
pub fn rb_gc_register_address(addr: CallbackPtr);
// void
// rb_gc_register_mark_object(VALUE obj)
pub fn rb_gc_register_mark_object(obj: Value);
// VALUE
// rb_gc_start(void)
pub fn rb_gc_start() -> Value;
// size_t
// rb_gc_stat(VALUE key)
pub fn rb_gc_stat(key: Value) -> size_t;
// void
// rb_gc_unregister_address(VALUE *addr)
pub fn rb_gc_unregister_address(addr: CallbackPtr);
// VALUE
// rb_define_finalizer(VALUE obj, VALUE block)
pub fn rb_define_finalizer(object: Value, block: Value) -> Value;
// VALUE
// rb_gc_latest_gc_info(VALUE key)
//
// `key` is a Hash to fill or a Symbol to look up.
pub fn rb_gc_latest_gc_info(key: Value) -> Value;
// void
// rb_gc_writebarrier(VALUE a, VALUE b)
pub fn rb_gc_writebarrier(parent: Value, child: Value);
// void
// rb_gc_writebarrier_unprotect(VALUE obj)
pub fn rb_gc_writebarrier_unprotect(object: Value);
// VALUE
// rb_memory_id(VALUE obj)
pub fn rb_memory_id(object: Value) -> Value;
// VALUE
// rb_undefine_finalizer(VALUE obj)
pub fn rb_undefine_finalizer(object: Value) -> Value;
}
// Ruby's allocator (`ruby/internal/xmalloc.h`): memory counted by the GC,
// which runs a collection and retries before raising `NoMemoryError`. Ruby
// frees what it allocates for C code with `ruby_xfree`, and memory given to
// Ruby to free must come from these.
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
// void *
// ruby_xmalloc(size_t size)
pub fn ruby_xmalloc(size: size_t) -> *mut c_void;
// void *
// ruby_xmalloc2(size_t nelems, size_t elemsiz)
//
// Raises `ArgumentError` when `nelems * elemsiz` overflows.
pub fn ruby_xmalloc2(nelems: size_t, elemsiz: size_t) -> *mut c_void;
// void *
// ruby_xcalloc(size_t nelems, size_t elemsiz)
//
// Zeroed.
pub fn ruby_xcalloc(nelems: size_t, elemsiz: size_t) -> *mut c_void;
// void *
// ruby_xrealloc(void *ptr, size_t newsiz)
pub fn ruby_xrealloc(ptr: *mut c_void, newsiz: size_t) -> *mut c_void;
// void *
// ruby_xrealloc2(void *ptr, size_t newelems, size_t newsiz)
pub fn ruby_xrealloc2(ptr: *mut c_void, newelems: size_t, newsiz: size_t) -> *mut c_void;
// void
// ruby_xfree(void *ptr)
pub fn ruby_xfree(ptr: *mut c_void);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xmalloc_family() {
crate::on_ruby_thread(|| unsafe {
let bytes = ruby_xmalloc(4) as *mut u8;
bytes.copy_from(b"ruby".as_ptr(), 4);
let bytes = ruby_xrealloc(bytes as *mut c_void, 8) as *mut u8;
assert_eq!(std::slice::from_raw_parts(bytes, 4), b"ruby");
ruby_xfree(bytes as *mut c_void);
let words = ruby_xcalloc(3, 8) as *mut u64;
assert_eq!(std::slice::from_raw_parts(words, 3), &[0, 0, 0]);
let words = ruby_xrealloc2(words as *mut c_void, 6, 8) as *mut u64;
assert_eq!(std::slice::from_raw_parts(words, 3), &[0, 0, 0]);
ruby_xfree(words as *mut c_void);
let pairs = ruby_xmalloc2(2, 16) as *mut [u64; 2];
pairs.write([1, 2]);
assert_eq!(*pairs, [1, 2]);
ruby_xfree(pairs as *mut c_void);
});
}
}