builtin: fix `-d debug_realloc`, add realloc_data/3

pull/9296/head
Delyan Angelov 2021-03-14 14:54:35 +02:00
parent ddbe57e00a
commit 125be84e3d
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
4 changed files with 59 additions and 27 deletions

View File

@ -86,7 +86,6 @@ fn new_array_from_c_array_no_alloc(len int, cap int, elm_size int, c_array voidp
}
// Private function. Doubles array capacity if needed.
[inline]
fn (mut a array) ensure_cap(required int) {
if required <= a.cap {
return
@ -98,7 +97,7 @@ fn (mut a array) ensure_cap(required int) {
new_size := cap * a.element_size
mut new_data := byteptr(0)
if a.cap > 0 {
new_data = unsafe { v_realloc(a.data, new_size) }
new_data = unsafe { realloc_data(a.data, a.cap * a.element_size, new_size) }
} else {
new_data = vcalloc(new_size)
}

View File

@ -209,31 +209,55 @@ pub fn v_realloc(b byteptr, n int) byteptr {
C.memcpy(new_ptr, b, n)
}
} $else {
$if debug_realloc ? {
// NB: this is slower, but helps debugging memory problems.
// The main idea is to always force reallocating:
// 1) allocate a new memory block
// 2) copy the old to the new
// 3) fill the old with 0x57 (`W`)
// 4) free the old block
// => if there is still a pointer to the old block somewhere
// it will point to memory that is now filled with 0x57.
unsafe {
new_ptr = malloc(n)
C.memcpy(new_ptr, b, n)
C.memset(b, 0x57, n)
C.free(b)
}
} $else {
new_ptr = unsafe { C.realloc(b, n) }
if new_ptr == 0 {
panic('realloc($n) failed')
}
new_ptr = unsafe { C.realloc(b, n) }
if new_ptr == 0 {
panic('realloc($n) failed')
}
}
return new_ptr
}
// realloc_data resizes the memory block pointed by `old_data` to `new_size`
// bytes. `old_data` must be a pointer to an existing memory block, previously
// allocated with `malloc`, `v_calloc` or `vcalloc`, of size `old_data`.
// realloc_data returns a pointer to the new location of the block.
// NB: if you know the old data size, it is preferable to call `realloc_data`,
// instead of `v_realloc`, at least during development, because `realloc_data`
// can make debugging easier, when you compile your program with
// `-d debug_realloc`.
[unsafe]
pub fn realloc_data(old_data byteptr, old_size int, new_size int) byteptr {
$if prealloc {
unsafe {
new_ptr := malloc(new_size)
C.memcpy(new_ptr, old_data, old_size)
return new_ptr
}
}
$if debug_realloc ? {
// NB: this is slower, but helps debugging memory problems.
// The main idea is to always force reallocating:
// 1) allocate a new memory block
// 2) copy the old to the new
// 3) fill the old with 0x57 (`W`)
// 4) free the old block
// => if there is still a pointer to the old block somewhere
// it will point to memory that is now filled with 0x57.
unsafe {
new_ptr := malloc(new_size)
C.memcpy(new_ptr, old_data, old_size)
C.memset(old_data, 0x57, old_size)
C.free(old_data)
return new_ptr
}
}
nptr := unsafe { C.realloc(old_data, new_size) }
if nptr == 0 {
panic('realloc_data($new_size) failed')
}
return nptr
}
// v_calloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
// v_calloc returns a `byteptr` pointing to the memory address of the allocated space.
pub fn v_calloc(n int) byteptr {

View File

@ -142,12 +142,14 @@ fn (d &DenseArray) has_index(i int) bool {
// The growth-factor is roughly 1.125 `(x + (x >> 3))`
[inline]
fn (mut d DenseArray) expand() int {
old_cap := d.cap
old_size := d.slot_bytes * old_cap
if d.cap == d.len {
d.cap += d.cap >> 3
unsafe {
d.data = v_realloc(d.data, d.slot_bytes * d.cap)
d.data = realloc_data(d.data, old_size, d.slot_bytes * d.cap)
if d.deletes != 0 {
d.all_deleted = v_realloc(d.all_deleted, d.cap)
d.all_deleted = realloc_data(d.all_deleted, old_cap, d.cap)
C.memset(d.all_deleted + d.len, 0, d.cap - d.len)
}
}
@ -185,9 +187,10 @@ fn (mut d DenseArray) zeros_to_end() {
free(d.all_deleted)
}
d.len = count
old_cap := d.cap
d.cap = if count < 8 { 8 } else { count }
unsafe {
d.data = v_realloc(d.data, d.slot_bytes * d.cap)
d.data = realloc_data(d.data, d.slot_bytes * old_cap, d.slot_bytes * d.cap)
}
}
@ -406,10 +409,12 @@ fn (mut m map) meta_greater(_index u32, _metas u32, kvi u32) {
[inline]
fn (mut m map) ensure_extra_metas(probe_count u32) {
if (probe_count << 1) == m.extra_metas {
size_of_u32 := sizeof(u32)
old_mem_size := (m.even_index + 2 + m.extra_metas)
m.extra_metas += extra_metas_inc
mem_size := (m.even_index + 2 + m.extra_metas)
unsafe {
x := v_realloc(byteptr(m.metas), int(sizeof(u32) * mem_size))
x := realloc_data(byteptr(m.metas), int(size_of_u32 * old_mem_size), int(size_of_u32 * mem_size))
m.metas = &u32(x)
C.memset(m.metas + mem_size - extra_metas_inc, 0, int(sizeof(u32) * extra_metas_inc))
}
@ -477,6 +482,7 @@ fn (mut m map) expand() {
fn (mut m map) rehash() {
meta_bytes := sizeof(u32) * (m.even_index + 2 + m.extra_metas)
unsafe {
// TODO: use realloc_data here too
x := v_realloc(byteptr(m.metas), int(meta_bytes))
m.metas = &u32(x)
C.memset(m.metas, 0, meta_bytes)

View File

@ -445,6 +445,7 @@ pub fn get_raw_stdin() []byte {
$if windows {
unsafe {
block_bytes := 512
mut old_size := block_bytes
mut buf := malloc(block_bytes)
h_input := C.GetStdHandle(C.STD_INPUT_HANDLE)
mut bytes_read := 0
@ -456,7 +457,9 @@ pub fn get_raw_stdin() []byte {
if !res {
break
}
buf = v_realloc(buf, offset + block_bytes + (block_bytes - bytes_read))
new_size := offset + block_bytes + (block_bytes - bytes_read)
buf = realloc_data(buf, old_size, new_size)
old_size = new_size
}
return array{
element_size: 1