diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 36bc85c3c2..d9d512416e 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -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) } diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index a0a1ebd14f..4f205740c1 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -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 { diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index b027853fb7..8b5f857433 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -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) diff --git a/vlib/os/os_c.v b/vlib/os/os_c.v index 86c156ff24..e7992c488d 100644 --- a/vlib/os/os_c.v +++ b/vlib/os/os_c.v @@ -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