0.5.5 features (#1151)

0.5.5 Disallow multiple `_` in a row in digits, e.g. `1__000`. #1138. Fixed toposort example. Struct/union members now correctly rejects members without storage size #1147. `math::pow` will now correctly promote integer arguments. `math::pow` will now correctly promote integer arguments. Added `new_aligned` and `alloc_aligned` functions to prevent accidental under-alignment when allocating simd. Pointer difference would fail where alignment != size (structs etc) #1150. Add test that overalignment actually works for lists. Fixed array calculation for npot2 vectors. Use native aligned alloc on Windows and POSIX. Deprecates "offset". Simplification of the Allocator interface.
This commit is contained in:
Christoffer Lerno
2024-02-22 17:13:51 +01:00
committed by GitHub
parent b7f4fd9074
commit 7ea3d230bb
39 changed files with 749 additions and 310 deletions

View File

@@ -30,9 +30,11 @@ struct ArenaAllocatorHeader @local
char[*] data;
}
/*
* @require ptr != null
**/
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
@@ -41,29 +43,26 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
self.used -= header.size + ArenaAllocatorHeader.sizeof;
}
}
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require size > 0
**/
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
self.used = end;
void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
@@ -73,21 +72,11 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require old_pointer != null
* @require size > 0
**/
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset);
}
alignment = alignment_for_allocation(alignment);
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
usz total_len = self.data.len;
@@ -95,7 +84,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
usz old_size = header.size;
// Do last allocation and alignment match?
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
{
if (old_size >= size)
{
@@ -111,7 +100,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, false, alignment, offset)!;
void* mem = self.acquire(size, false, alignment, 0)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -59,11 +59,11 @@ struct DynamicArenaChunk @local
}
/**
* @require ptr
* @require self.page `tried to free pointer on invalid allocator`
*/
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
DynamicArenaPage* current_page = self.page;
if (ptr == current_page.current_stack_ptr)
{
@@ -73,19 +73,12 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
}
/**
* @require size > 0 `Resize doesn't support zeroing`
* @require old_pointer != null `Resize doesn't handle null pointers`
* @require self.page `tried to realloc pointer on invalid allocator`
*/
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset);
}
DynamicArenaPage* current_page = self.page;
alignment = alignment_for_allocation(alignment);
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
@@ -109,7 +102,7 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
current_page.used += add_size;
return old_pointer;
}
void* new_mem = self.acquire(size, false, alignment, offset)!;
void* new_mem = self.acquire(size, false, alignment, 0)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
@@ -135,10 +128,10 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
* @require math::is_power_of_2(alignment)
* @require size > 0
*/
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz offset) @local
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof, alignment));
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
@@ -149,7 +142,7 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
@@ -162,11 +155,11 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
}
/**
* @require size > 0 `acquire expects size > 0`
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
void* ptr = {|
@@ -176,14 +169,14 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page) return self._alloc_new(size, alignment, offset);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
if (!page) return self._alloc_new(size, alignment);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
new_used = start + size - page.memory;
if (page.total >= new_used)
{
@@ -193,7 +186,7 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
break ALLOCATE_NEW;
}
}
return self._alloc_new(size, alignment, offset);
return self._alloc_new(size, alignment);
}
page.used = new_used;
assert(start + size == page.memory + page.used);

View File

@@ -23,27 +23,17 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
if (clear)
{
return alignment > 0 ? @aligned_calloc(self._calloc, size, alignment, offset) : self._calloc(size);
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
}
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset);
}
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment, offset)
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
: self._realloc(old_pointer, size);
}

View File

@@ -6,22 +6,36 @@ module std::core::mem::allocator;
import libc;
const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator) = uptr;
module std::core::mem::allocator @if(env::POSIX);
import std::os;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (clear)
{
void* data = alignment ? @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment, offset)!! : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
}
else
{
if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?;
}
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
@@ -31,7 +45,6 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (!new_bytes)
{
self.release(old_ptr, alignment > 0);
@@ -39,11 +52,101 @@ fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
}
if (!old_ptr)
{
return self.acquire(new_bytes, true, alignment, offset);
return self.acquire(new_bytes, false, alignment, 0);
}
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
void* new_ptr;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
$switch
$case env::DARWIN:
usz old_usable_size = darwin::malloc_size(old_ptr);
$case env::LINUX:
usz old_usable_size = linux::malloc_usable_size(old_ptr);
$default:
usz old_usable_size = new_bytes;
$endswitch
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
libc::free(old_ptr);
return new_ptr;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
libc::free(old_ptr);
}
module std::core::mem::allocator @if(env::WIN32);
import std::os::win32;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
if (clear)
{
if (alignment > 0)
{
return win32::_aligned_recalloc(null, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_ptr, new_bytes, alignment, offset)!!;
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
if (aligned)
{
win32::_aligned_free(old_ptr);
return;
}
libc::free(old_ptr);
}
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
if (clear)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;

View File

@@ -54,9 +54,11 @@ struct OnStackAllocatorHeader
char[*] data;
}
/**
* @require old_pointer
**/
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (!old_pointer) return;
if (allocation_in_stack_mem(self, old_pointer)) return;
on_stack_allocator_remove_chunk(self, old_pointer);
self.release(old_pointer, aligned);
@@ -98,44 +100,38 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
/**
* @require size > 0
* @require old_pointer != null
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, OnStackAllocatorExtraChunk.alignof) == offset
**/
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
{
if (!allocation_in_stack_mem(self, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset)!;
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, 0)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, true, alignment, offset)!;
void* mem = self.acquire(size, false, alignment, 0)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
/**
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require offset == 0 || alignment > 0
* @require mem::aligned_offset(offset, OnStackAllocatorHeader.alignof) == offset
* @require size > 0
**/
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
{
if (size == 0) return null;
bool aligned = alignment > 0;
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
Allocator* backing_allocator = self.backing_allocator;
if (end > total_len)
@@ -144,10 +140,9 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
defer catch allocator::free(backing_allocator, chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, 0)!;
}
self.used = end;
void *mem = aligned_pointer_to_offset - offset;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size;
return mem;

View File

@@ -79,7 +79,7 @@ fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -94,46 +94,37 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, size > page_size, alignment, offset)!;
void* data = self.acquire(size, size > page_size, alignment, 0)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz deprecated) @dynamic
{
if (!size)
{
self.release(pointer, alignment > 0);
return null;
}
if (!pointer)
{
return self.acquire(size, true, alignment, offset);
}
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
{
assert(self.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment, offset);
return self._realloc_page(page, size, alignment);
}
// TODO optimize last allocation
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset)!;
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, 0)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
/**
* @require size > 0
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
**/
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
void* starting_ptr = start_mem + self.used;
@@ -141,7 +132,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
if (alignment > TempAllocatorChunk.alignof)
{
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
mem = mem::aligned_pointer(mem, alignment);
}
usz new_usage = (usz)(mem - start_mem) + size;
@@ -159,19 +150,20 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
TempAllocatorPage* page;
// We have something we need to align.
if (alignment > mem::DEFAULT_MEM_ALIGNMENT || offset)
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = TempAllocatorPage.sizeof + size;
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
if (clear)
{
page = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
else
{
page = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
page.start = page;
page = (TempAllocatorPage*)mem - 1;
page.start = mem;
page.size = size | PAGE_IS_ALIGNED;
}
else

View File

@@ -79,48 +79,36 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
**/
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
{
void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
void* data = self.inner_allocator.acquire(size, clear, alignment, 0)!;
self.allocs_total++;
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
if (data)
{
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment, offset)!;
if (old_pointer)
{
self.map.remove((uptr)old_pointer);
}
if (data)
{
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
}
void* data = self.inner_allocator.resize(old_pointer, size, alignment, 0)!;
self.map.remove((uptr)old_pointer);
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
return data;
}
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
{
if (old_pointer)
{
if (catch self.map.remove((uptr)old_pointer))
{
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
}
}
if (catch self.map.remove((uptr)old_pointer))
{
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
}
self.inner_allocator.release(old_pointer, is_aligned);
}

View File

@@ -253,11 +253,12 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if $inlined:
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
$endif
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
}
macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset_inline(dst, (char)0, $len, $is_volatile, $dst_align);
}
/**
@@ -269,17 +270,30 @@ macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = fal
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
* @param $inlined "True if this copy should never call the OS memcpy."
*
* @require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
**/
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if $inlined:
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
$else
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
$endif
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
}
/**
* Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
* will always be inlined and never call memcopy
*
* @param [&out] dst "The destination to copy to"
* @param [&in] src "The source to copy from"
* @param $len "The number of bytes to copy"
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
*
* @require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
**/
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
$$memcpy_inline(dst, src, $len, $is_volatile, $dst_align, $src_align);
}
/**
@@ -305,19 +319,29 @@ macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
* @param len "The number of bytes to copy"
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
* @param $inlined "True if this copy should never call the OS memset."
*
* @ensure !len || (dst[0] == val && dst[len - 1] == val)
**/
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false)
{
$if $inlined:
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else
$$memset(dst, val, len, $is_volatile, $dst_align);
$endif
$$memset(dst, val, len, $is_volatile, $dst_align);
}
/**
* Sets all memory in a region to that of the provided byte. Never calls OS memset.
*
* @param [&out] dst "The destination to copy to"
* @param val "The value to copy into memory"
* @param $len "The number of bytes to copy"
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
*
* @ensure !$len || (dst[0] == val && dst[$len - 1] == val)
**/
macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
}
/**
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
@@ -530,12 +554,14 @@ fn void* malloc(usz size) @builtin @inline @nodiscard
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
{
return allocator::temp().acquire(size, false, alignment, offset)!!;
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro new($Type, ...) @nodiscard
{
@@ -548,11 +574,40 @@ macro new($Type, ...) @nodiscard
$endif
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
**/
macro new_aligned($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
$else
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
*val = $vaexpr(0);
return val;
$endif
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro alloc($Type) @nodiscard
{
return ($Type*)malloc($Type.sizeof);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro alloc_aligned($Type) @nodiscard
{
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
}
macro new_clear($Type) @deprecated("Use mem::new")
{
return new($Type);
@@ -588,16 +643,42 @@ macro new_temp_clear($Type) @deprecated("use mem::temp_new")
return tcalloc($Type.sizeof);
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
**/
macro new_array($Type, usz elements) @nodiscard
{
return allocator::new_array(allocator::heap(), $Type, elements);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro new_array_aligned($Type, usz elements) @nodiscard
{
return allocator::new_array_aligned(allocator::heap(), $Type, elements);
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
**/
macro alloc_array($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro alloc_array_aligned($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
}
macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
{
return temp_alloc_array($Type, elements);
@@ -633,9 +714,15 @@ fn void* calloc(usz size) @builtin @inline @nodiscard
return allocator::calloc(allocator::heap(), size);
}
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
}
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
{
return allocator::temp().acquire(size, false, alignment, offset)!!;
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
@@ -643,13 +730,25 @@ fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
return allocator::realloc(allocator::heap(), ptr, new_size);
}
fn void* realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline @nodiscard
{
return allocator::realloc_aligned(allocator::heap(), ptr, new_size, alignment)!!;
}
fn void free(void* ptr) @builtin @inline
{
return allocator::free(allocator::heap(), ptr);
}
fn void free_aligned(void* ptr) @builtin @inline
{
return allocator::free_aligned(allocator::heap(), ptr);
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
{
if (!size) return null;
if (!ptr) return tmalloc(size, alignment);
return allocator::temp().resize(ptr, size, alignment, 0)!!;
}

View File

@@ -15,8 +15,24 @@ interface Allocator
{
fn void reset(usz mark) @optional;
fn usz mark() @optional;
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset == 0 `offset no longer supported`
* @require size > 0
**/
fn void*! acquire(usz size, bool clear, usz alignment, usz offset);
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset == 0 `offset no longer supported`
* @require ptr != null
* @require new_size > 0
**/
fn void*! resize(void* ptr, usz new_size, usz alignment, usz offset);
/**
* @require ptr != null
**/
fn void release(void* ptr, bool aligned);
}
@@ -40,6 +56,7 @@ macro void* malloc(Allocator* allocator, usz size) @nodiscard
macro void*! malloc_try(Allocator* allocator, usz size) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, false, 0, 0)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
@@ -56,6 +73,7 @@ macro void* calloc(Allocator* allocator, usz size) @nodiscard
macro void*! calloc_try(Allocator* allocator, usz size) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, true, 0, 0);
}
@@ -66,19 +84,27 @@ macro void* realloc(Allocator* allocator, void* ptr, usz new_size) @nodiscard
macro void*! realloc_try(Allocator* allocator, void* ptr, usz new_size) @nodiscard
{
if (!new_size)
{
free(allocator, ptr);
return null;
}
if (!ptr) return allocator.acquire(new_size, false, 0, 0);
return allocator.resize(ptr, new_size, 0, 0);
}
macro void free(Allocator* allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, false);
}
macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, false, alignment, offset)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
@@ -90,18 +116,29 @@ macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz o
macro void*! calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, true, alignment, offset);
}
macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz alignment, usz offset = 0) @nodiscard
{
if (!new_size)
{
free_aligned(allocator, ptr);
return null;
}
if (!ptr)
{
return malloc_aligned(allocator, new_size, alignment);
}
return allocator.resize(ptr, new_size, alignment, offset);
}
macro void free_aligned(Allocator* allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, true);
}
@@ -166,11 +203,21 @@ macro new_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
}
macro new_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
{
return ((Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
macro alloc_array(Allocator* allocator, $Type, usz elements) @nodiscard
{
return alloc_array_try(allocator, $Type, elements)!!;
}
macro alloc_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
{
return ((Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
macro alloc_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
{
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
@@ -193,23 +240,17 @@ fn any* clone_any(Allocator* allocator, any* value) @nodiscard
macro void*! Allocator.alloc_checked(&self, usz size) @deprecated("Use allocator::malloc_try")
{
$if env::TESTING:
char* data = self.acquire(size, false, 0, 0)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return self.acquire(size, false, 0, 0);
$endif
return malloc_try(self, size);
}
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
{
return self.acquire(size, true, 0, 0);
return calloc_try(self, size);
}
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) @deprecated("Use allocator::realloc_try")
{
return self.resize(ptr, new_size, 0, 0);
return realloc_try(ptr, new_size);
}
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array")
@@ -258,86 +299,60 @@ macro Allocator.clone(&self, value) @deprecated("Use allocator::clone")
return x;
}
macro void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
fn void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
{
return self.alloc_checked(size)!!;
return malloc(self, size);
}
macro void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
fn void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
{
return self.acquire(size, true, 0, 0)!!;
return calloc(self, size);
}
macro void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
fn void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
{
return self.resize(ptr, new_size, 0, 0)!!;
return realloc(self, ptr, new_size);
}
macro void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::alloc_aligned")
fn void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::malloc_aligned")
{
$if env::TESTING:
char* data = self.acquire(size, false, alignment, offset)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return self.acquire(size, false, alignment, offset);
$endif
}
macro void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
{
return self.acquire(size, true, alignment, offset);
}
macro void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
{
return self.resize(ptr, new_size, alignment, offset);
return malloc_aligned(self, size, alignment, 0);
}
macro void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
fn void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
{
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, false);
return calloc_aligned(self, size, alignment, 0);
}
macro void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
fn void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
{
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, true);
return realloc_aligned(self, ptr, new_size, alignment, 0);
}
fn void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
{
free(self, ptr);
}
fn void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
{
free_aligned(self, ptr);
}
/**
* @require bytes > 0
* @require alignment > 0
* @require bytes <= isz.max
**/
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
if (alignment < void*.alignof) alignment = void*.alignof;
usz header = AlignedBlock.sizeof + alignment;
usz alignsize = bytes + header;
$if @typekind(#alloc_fn(bytes)) == OPTIONAL:
void* data = #alloc_fn(header + bytes)!;
void* data = #alloc_fn(alignsize)!;
$else
void* data = #alloc_fn(header + bytes);
void* data = #alloc_fn(alignsize);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
assert(mem > data);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if @typekind(#calloc_fn(bytes)) == OPTIONAL:
void* data = #calloc_fn(header + bytes)!;
$else
void* data = #calloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
@@ -364,12 +379,12 @@ macro void! @aligned_free(#free_fn, void* old_pointer)
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment, usz offset)
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
void* new_data = @aligned_alloc(#calloc_fn, bytes, alignment)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
$if @typekind(#free_fn(data_start)) == OPTIONAL:
#free_fn(data_start)!;
$else

View File

@@ -97,10 +97,10 @@ def complex_identity = complex::identity(<double>);
def Quaternionf = Quaternion(<float>);
def Quaternion = Quaternion(<double>);
def quaternionf_identity = quaternion::identity(<float>);
def quaternion_identity = quaternion::identity(<double>);
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
def quaternion_identity = quaternion::identity(<double>);
def quaternionf_identity = quaternion::identity(<float>);
def Matrix2f = Matrix2x2(<float>);
def Matrix2 = Matrix2x2(<double>);
@@ -120,6 +120,14 @@ def MATRIX3F_IDENTITY = matrix::IDENTITY3(<float>);
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>);
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>);
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
macro deg_to_rad(x) {
return x * PI / 180;
}
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
@@ -392,14 +400,14 @@ macro nearbyint(x) => $$nearbyint(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require $assignable(exp, $typeof(x)) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
* @require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
**/
macro pow(x, exp)
{
$if types::is_floatlike($typeof(exp)):
return $$pow(x, ($typeof(x))exp);
return $$pow(values::promote_int(x), ($typeof(values::promote_int(x)))exp);
$else
return $$pow_int(x, exp);
return $$pow_int(values::promote_int(x), exp);
$endif
}
@@ -441,6 +449,16 @@ macro rint(x) => $$rint(x);
**/
macro round(x) => $$round(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro round_to_decimals(x, int decimal_places)
{
var div = $$pow_int(($typeof(x))10, decimal_places);
return round(div * x) / div;
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/

View File

@@ -9,8 +9,9 @@ union Quaternion
Real[<4>] v;
}
macro Quaternion identity() => { 0, 0, 0, 1 };
const Quaternion IDENTITY = { 0, 0, 0, 1 };
macro Quaternion identity() @deprecated("Replaced with QUATERNION_IDENTITY constant") => { 0, 0, 0, 1 };
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
@@ -70,33 +71,16 @@ fn Quaternion Quaternion.mul(a, Quaternion b)
macro into_matrix(Quaternion* q, $Type) @private
{
Quaternion q_norm = q.normalize();
Quaternion rotation = q.normalize();
var x = rotation.i;
var y = rotation.j;
var z = rotation.k;
var w = rotation.l;
var x = q_norm.i;
var y = q_norm.j;
var z = q_norm.k;
var w = q_norm.l;
var xx = x + x;
var yy = y + y;
var zz = z + z;
var xxx = xx * x;
var xxy = xx * y;
var xxz = xx * z;
var yyy = yy * y;
var yyz = yy * z;
var zzz = zz * z;
var yyw = yy * w;
var zzw = zz * w;
var xxw = xx * w;
return $Type {
1 - yyy - zzz, xxy - zzw, xxz + yyw, 0,
xxy + zzw, 1 - xxx - zzz, yyz - xxw, 0,
xxz - yyw, yyz + zzw, 1.0 - xxx - yyy, 0,
0, 0, 0, 1,
};
return $Type {
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
0.0, 0.0, 0.0, 1.0,
};
}

View File

@@ -206,7 +206,7 @@ macro matrix_look_at($Type, eye, target, up) @private
vx[0], vx[1], vx[2], - vx.dot(eye),
vy[0], vy[1], vy[2], - vy.dot(eye),
vz[0], vz[1], vz[2], - vz.dot(eye),
0, 0, 0, 1
0.0, 0.0, 0.0, 1
};
}

2
lib/std/os/linux/heap.c3 Normal file
View File

@@ -0,0 +1,2 @@
module std::os::linux @if(env::LINUX);
extern fn usz malloc_usable_size(void* ptr);

3
lib/std/os/macos/heap.c3 Normal file
View File

@@ -0,0 +1,3 @@
module std::os::darwin @if(env::DARWIN);
extern fn usz malloc_size(void* ptr);

3
lib/std/os/posix/heap.c3 Normal file
View File

@@ -0,0 +1,3 @@
module std::os::posix @if(env::POSIX);
extern fn CInt posix_memalign(void **memptr, usz alignment, usz size);

11
lib/std/os/win32/heap.c3 Normal file
View File

@@ -0,0 +1,11 @@
module std::os::win32 @if(env::WIN32);
extern fn void* _aligned_malloc(usz size, usz alignment);
extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment);
extern fn void* _aligned_recalloc(void* memblock, usz size, usz alignment);
extern fn void _aligned_free(void* memblock);
extern fn void _aligned_msize(void* memblock, usz alignment, usz offset);
extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_recalloc(void* memblock, usz size, usz alignment, usz offset);
extern fn usz _msize(void* memblock);