#load "std/memory.bl"
Toolset for manipulation with the system memory.
Memory allocators are used across the entire program to manage allocated memory resources used in runtime. Since memory allocation can be an expensive operation in some cases, it's good to provide an explicit API, giving information enough to fix bugs, memory leaks and performance issues.
Memory allocator in BL world is just some context Allocator structure used by an allocator handler function.
Functions like alloc and free internally use allocator set in global executable
context application_context
variable. Global context allocator is by default set to default_allocator
and can be changed as needed.
DEFAULT_ALIGNMENT : usize :
Default memory allocation alignment.
File: memory.bl
default_allocator ::
Default memory allocator using libc malloc internally.
- [x] Allocate
- [x] Reallocate
- [x] Free
- [ ] Release
- [ ] Reset
File: memory.bl
default_temporary_allocator ::
Default temporary allocator instance using pool internally (by default set into the application context).
The temporary allocator (used i.e. by tprint function) is useful in cases we don't need to keep allocated memory for a long period of time. In general in such a case we allocate, use and free the memory.
Temporary allocated memory does not need to be explicitly freed (in fact temporary allocator does not
free individual allocations at all even if free
is called). A large memory block is preallocated instead
and every allocation lands into this block. Later (at some safe point of execution) when
temporary_reset is called, all previous allocations done via temporary allocator became
invalid (marked as a free space in preallocated block) and can be reused lated.
When temporary allocator is not needed anymore, temporary_release shall be called to free all internally preallocated blocks.
Underlying allocator works with thread-local data storage (it's safe to use it in threads without any synchronization
required). But, when used from thread, each thread-local instance must be terminated by temporary_release
call. In case you use async
module the release is called automatically when internal threads exits.
main :: fn () s32 {
// Release allocated memory at the end of the scope.
defer temporary_release();
loop i := 0; i < 1000; i += 1 {
// Reset the allocator here (all previous alocation are invalid since now).
defer temporary_reset();
// Allocate memory using temporary allocator.
int_ptr := cast(*s32) alloc(sizeof(s32), alignof(s32), &default_temporary_allocator);
// Do something here with allocated memory.
@int_ptr = i;
// There is no need to free allocated memory here.
}
return 0;
}
Note: The temporary allocator internally use pool allocator, see the documentation for more details.
File: memory.bl
AllocOp :: enum {
ALLOCATE;
REALLOCATE;
FREE;
RESET;
RELEASE;
}
Specify allocator opratation.
ALLOCATE
- Allocation of new memory block is required.REALLOCATE
- Reallocate previously allocated memory.FREE
- Free of previously allocated memory. This operation is optional (i.e. for pools).RESET
- Reset the allocated resources, but keep them for later use.RELEASE
- Release all resources but keep the allocator instance in initialized state.File: memory.bl
AllocFn :: *fn (ctx: *Allocator, operation: AllocOp, ptr: *u8, size: usize, alignment: usize, file: string_view, line: s32) (mem: *u8, err: Error)
Allocator handle function type.
File: memory.bl
Allocator :: struct {
handler: AllocFn;
}
Default allocator context base. This structure can be used as a base structure for any allocator implementation.
handler
- Pointer to the main allocator handler function.File: memory.bl
alloc :: fn (size: usize, alignment :: DEFAULT_ALIGNMENT, preferred_allocator : *Allocator: null, loc :: #call_location) (mem: *u8, err: Error) #inline
Allocate memory using specified preferred_allocator
. In case the preferred_allocator
is null, default application context
allocator is used.
Returns pointer to the newly allocated memory capable to handle size
bytes, or fails with Error
in case the
allocation is not possible.
The allocation size
must be at least 1 byte and the optional alignment
must be value of power of two. Use alignof
helper function to resolve the best memory alignment for the required type.
File: memory.bl
realloc :: fn (ptr: *u8, size: usize, alignment :: DEFAULT_ALIGNMENT, preferred_allocator : *Allocator: null, loc :: #call_location) (mem: *u8, err: Error) #inline
Reallocate previously allocated memory using the preferred_allocator
. In case the preferred_allocator
is null,
default application context allocator is used.
Behavior depends on allocator being used. Usually when the previous allocation pointer ptr
is specified, the
implementation should try to resize already allocated block of memory to the requested size
. In case resize is not
possible, or is not supported by the allocator, new memory block is allocated to handle size
of bytes, and data from the
previous block are copied (memory area with size equal the lesser of the new and the old allocation sizes) into the newly
allocated block. The previous allocated block is freed.
The allocation size
must be at least 1 byte and the optional alignment
must be value of power of two. Use alignof
helper function to resolve the best memory alignment for the required type.
In case the ptr
is null
, behavior is supposed to be the same as alloc.
File: memory.bl
new :: fn (T: type, noinit :: false, preferred_allocator : *Allocator: null, loc :: #call_location) (ptr: *T, err: Error) #inline
Allocates new object of type T
on heap using preferred_allocator
, in case the allocator is not specified, the current context
allocator is used.
Newly allocated memory block is zero initialized by default unless the noinit
is true
.
Use free to release allocated memory when it's not needed anymore.
File: memory.bl
new_slice :: fn (TElement: type, element_count: s64, noinit :: false, preferred_allocator : *Allocator: null, loc :: #call_location) (slice: []TElement, err: Error)
Allocates new slice of 'element_count' elements of 'TElement' type.
Newly allocated memory block is zero initialized by default unless the noinit
is true
.
Use free_slice to release allocated memory when it's not needed anymore.
File: memory.bl
free :: fn (ptr: *u8, preferred_allocator : *Allocator: null, loc :: #call_location) #inline
Free memory previously allocated by specific preferred_allocator
. In case the preferred_allocator
is null,
the default application context allocator is used. The ptr
can be null.
File: memory.bl
free_slice :: fn (slice: *[]?T, allocator : *Allocator: null, loc :: #call_location)
Release slice memory allocated by alloc_slice or new_slice call. The input slice is set to the zero initialized state.
Warning:
The allocator
must match the allocator used by 'alloc_slice' or 'new_slice'.
File: memory.bl
reset_allocator :: fn (allocator: *Allocator, loc :: #call_location) #inline
Invoke the reset operation on the allocator
. If supported, the allocator
should reset its internal state (make
all allocation invalid) and reuse already allocated memory eventually for following allocations.
File: memory.bl
release_allocator :: fn (allocator: *Allocator, loc :: #call_location) #inline
Invoke the release operation on the allocator
. If the operation is supported, the allocator should release all resources and free
all internally allocated memory.
File: memory.bl
memcpy :: fn (destination: *u8, source: *u8, size: usize)
Copy memory of defined size
from source
to destination
. Destination and source size must be at least
size
bytes.
File: memory.bl
memset :: fn (destination: *u8, value: u8, size: usize) *u8
Set memory to desired value and return destination
pointer. Destination size must be at least size
bytes.
File: memory.bl
memmove :: fn (destination: *u8, source: *u8, size: usize) *u8
Copy 'size' bytes of data from memory location at 'source' to the 'destination' and returns 'destination'.
File: memory.bl
zeromem :: fn (destination: *u8, size: usize) *u8
Zero out destination
memory of size
and return the original destination
pointer.
This function is internally optimized to zero the destination
memory in 64 bit blocks if possible.
File: memory.bl
zeroinit :: fn (ptr: *?T) *T #inline
Zero initialize memory block at ptr
and sizeof(T)
. Returns passed ptr
this might be useful in case of
"inline chaining".
foo :: zeroinit(array_push(&my_array));
File: memory.bl
zero_slice :: fn (slice: []?T) #inline
File: memory.bl
swap :: fn (first: *?T, second: *T) #inline
Swaps content of memory at address first
and second
.
File: memory.bl
is_aligned :: fn (ptr: *?T, alignment: usize) bool #inline
Checks whether passed pointer ptr
is properly aligned by alignment
.
File: memory.bl
align_ptr_up :: fn (p: *u8, alignment: usize) (p: *u8, adjustment: usize)
Align pointer p
to alignment
and return adjusted pointer and number of bytes needed for
adjustment.
Warning: Cause panic when alignment is not power of two.
File: memory.bl
alloc_slice :: fn (slice: *[]?T, n: s64, zero_initialized :: true, allocator : *Allocator: null, loc :: #call_location) Error
Allocate heap memory for n
elements in the slice
. Newly allocated slice can be zero initialized
by setting zero_init
to true
. Custom allocator can be provided as allocator
, the application context
allocator is used in case the allocator
is null.
Allocated memory must be released by free_slice call.
main :: fn () s32 {
// Allocate slice of 10 numbers
sl: []s32;
alloc_slice(&sl, 10);
loop i := 0; i < sl.len; i += 1 {
sl[i] = i;
}
// Release allocated memory
free_slice(&sl);
return 0;
}
Note: Zero initialization of allocated memory block can be expensive in case of large number of elements.
File: memory.bl
slice_range :: fn (slice: []?T, start: s64, end : s64: -1) []T #inline
Create slice subset defined as range <start-index, end-index)
. Notice that end
index
is excluded from range, so slice_range(other, 0, other.len)
is valid and returns
new slice pointing to the same data as other
slice, and with same size.
Indexing rules:
start >= 0
start < slice.len
end >= 0
end <= slice.len
Warning:
Function cause panic in case combination of start
and end
is out of slice
range.
File: memory.bl
temporary_reset :: fn () #inline
Reduce allocated memory in application context temporary allocator storage, but keeps biggest allocated chunk for the later use.
Warning: All resources previously allocated by this allocator became invalid after reset.
Note: Call this method i.e. in every event loop (update) iteration.
File: memory.bl
temporary_release :: fn () #inline
Release all memory allocated by the application context temporary allocator.
Warning: All resources previously allocated by this allocator became invalid after release.
Note: This method is implicitly called at exit of executable (after main).
File: memory.bl
ptr_shift_bytes :: fn (ptr: *?T, bytes: s64) *T #inline
Produce right-shift of input ptr
by count of bytes
.
File: memory.bl
ptr_diff :: fn (a: *?T1, b: *?T2) s64 #inline
Calculates pointer difference a
- b
.
File: memory.bl