-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: move the base malloc library to _malloc
_malloc.corth contains the base procedures and the macros in malloc.corth expands to those procedures so no run-time performance is lost while using the regular dynamic memory management procedures. This change is made to allow adding debugging tools to these procedures.
- Loading branch information
1 parent
b508b4b
commit 94acbae
Showing
3 changed files
with
244 additions
and
236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
// Before this library is included, malloc:ARRAY-SIZE and malloc:AVAIL-STACK-SIZE must be defined. | ||
// Before using any of the procedures or macros, malloc:init must be called. | ||
|
||
// malloc:array is a linked list with items called 'blocks'. | ||
|
||
// Block structure: | ||
// int* next-block | ||
// byte[] container | ||
|
||
// malloc:avail-stack is a sorted stack that contains the pointers of the unused blocks. | ||
|
||
include "collections/stack64.corth" | ||
include "collections/sorted64.corth" | ||
|
||
memory malloc:array malloc:ARRAY-SIZE and | ||
malloc:avail-stack-array malloc:AVAIL-STACK-SIZE and | ||
malloc:avail-stack-length sizeof(int) end | ||
|
||
namespace malloc | ||
|
||
macro avail-stack malloc:avail-stack-array malloc:avail-stack-length endmacro | ||
|
||
|
||
proc _init -> in | ||
// Set the available stack length to 0. | ||
0 malloc:avail-stack-length !64 | ||
|
||
// Create the first block. | ||
malloc:array malloc:ARRAY-SIZE + malloc:array !64 | ||
|
||
// Append the block as available to the available stack. | ||
malloc:array malloc:avail-stack stack64:append | ||
end | ||
|
||
|
||
// ptr: block -> ptr: next-block | ||
macro next-block | ||
@64 | ||
endmacro | ||
|
||
|
||
// ptr: next-block ptr: block -> | ||
macro set-next-block | ||
!64 | ||
endmacro | ||
|
||
|
||
// ptr: container -> ptr: block | ||
macro block | ||
8 - | ||
endmacro | ||
|
||
|
||
// ptr: block -> ptr: container | ||
macro container | ||
8 + | ||
endmacro | ||
|
||
|
||
// ptr: block -> int: size | ||
// Returns the size of the block. | ||
// The size of the block is equal to the difference between the next block and the current block. | ||
macro block-size let _block_ in | ||
_block_ malloc:next-block _block_ - | ||
end endmacro | ||
|
||
|
||
// ptr: block -> int: size | ||
// Returns the size of the block. | ||
// The size of the container is equal to the size of the block that contains the container minus eight. | ||
macro container-size | ||
malloc:block-size 8 - | ||
endmacro | ||
|
||
|
||
proc find-min-available | ||
// int: expected-size -> ptr: available-group int: group-size int: group-id | ||
int -> ptr int int | ||
in let size in | ||
memory available-group sizeof(ptr) and | ||
available-size sizeof(int) and | ||
available-id sizeof(int) in | ||
|
||
// available-id is the ID of stack item that points to the group. | ||
// group = stack[id] | ||
// size = *group - group | ||
|
||
NULLPTR available-group !64 | ||
MAX-INT available-size !64 | ||
|
||
// Find the group with the smallest size that has enough space. | ||
0 while dup malloc:avail-stack-length @64 < do let id in | ||
id malloc:avail-stack stack64:get let group in | ||
group malloc:block-size let group-size in | ||
group-size size 8 + = if | ||
// If group-size is equal to size + 8, then we have found the best possible block, no need to keep iterating. | ||
group group-size id return | ||
end | ||
|
||
group-size size 16 + >= if | ||
// If the group has enough space, that is, if the group has at least expected size plus sixteen bytes of free space. | ||
group-size available-size @64 < if | ||
// Find the smallest group. | ||
group available-group !64 | ||
group-size available-size !64 | ||
id available-id !64 | ||
end | ||
end | ||
end | ||
end | ||
id end inc end drop | ||
|
||
available-group @64 available-size @64 available-id @64 | ||
end | ||
end end | ||
|
||
endnamespace | ||
|
||
|
||
proc _malloc | ||
// int: length -> ptr: addr | ||
int -> ptr | ||
// Allocates memory with a specific size, allows dynamic memory management. | ||
// Always allocates the smallest availiable segment for memory efficiency. | ||
in let size in | ||
// Check if the size argument is valid. | ||
size is-neg if | ||
NULLPTR return | ||
end | ||
|
||
// Find the block with the smallest size that has enough space. | ||
size malloc:find-min-available let available-block available-size available-id in | ||
|
||
// Could not find a block with enough size. | ||
available-block malloc:next-block is-null if NULLPTR return end | ||
|
||
size 8 + available-size = if | ||
// Size was equal to available space, therefore the block should be kept intact. | ||
|
||
available-id malloc:avail-stack stack64:pop drop | ||
else | ||
// Size was less than available, therefore the block should be split in two. | ||
|
||
// ____________________________ | ||
// / \ | ||
// |-------|---------------------|-------|------| | ||
// | &next |######container######| &next |@cont@| | ||
// |-------|---------------------|-------|------| | ||
|
||
// Will be split as: | ||
|
||
// _____________ _____________ | ||
// / \/ \ | ||
// |-------|------|-------|------|-------|------| | ||
// | &next |@cont@| &next |#cont#| &next |@cont@| | ||
// |-------|------|-------|------|-------|------| | ||
|
||
// (# represents unused space, @ represents used space.) | ||
|
||
// This means that even if the containers are empty, we need 16 bytes of free space for the pointers. | ||
// To split a block into two sub-blocks with one of the sub-blocks having L bytes of space, the block must have at least L + 16 bytes of free space. | ||
|
||
available-block malloc:container size + let new-next in | ||
|
||
// Set the new block's next-block to the selected-block's next-block. | ||
available-block malloc:next-block new-next malloc:set-next-block | ||
|
||
// Set the next-block as the next block. | ||
new-next available-block malloc:set-next-block | ||
|
||
// Update the available blocks table. | ||
new-next available-id malloc:avail-stack stack64:set | ||
end | ||
end | ||
|
||
// Return the container position. | ||
available-block malloc:container | ||
end | ||
end end | ||
|
||
|
||
proc _mfree | ||
// ptr: container -> bool: successful | ||
ptr -> bool | ||
// Frees space up allocated by malloc. | ||
in malloc:block let block in | ||
block malloc:avail-stack-array malloc:avail-stack-array malloc:avail-stack-length @64 8 * + sorted64:available if drop | ||
// If that block was already specified as free. | ||
false return | ||
end | ||
|
||
malloc:avail-stack-array - 8 / let index in | ||
// Add the block to the available blocks table. | ||
block index malloc:avail-stack stack64:insert | ||
|
||
// Merge the next block, if it is unused. | ||
index inc malloc:avail-stack-length < if | ||
block malloc:next-block index inc malloc:avail-stack stack64:get = if | ||
// Remove the next block from the available blocks table. | ||
index inc malloc:avail-stack stack64:pop drop | ||
|
||
// Set the next-next block as the next block. | ||
block malloc:next-block malloc:next-block block malloc:set-next-block | ||
end | ||
end | ||
|
||
index is-pos if | ||
index dec malloc:avail-stack stack64:get let prev-block in | ||
// Merge the previous block, if it is unused. | ||
prev-block malloc:next-block block = if | ||
// Set the next block as the next block of the previous block. | ||
block malloc:next-block prev-block malloc:set-next-block | ||
|
||
// Remove the block from the available blocks table. | ||
// TODO: There is the possibility of performing two consecutive pop operations, which causes a call to memcpy Twice. This could be made more efficient by calling memcpy only once. | ||
index malloc:avail-stack stack64:pop drop | ||
end | ||
end | ||
end | ||
end | ||
end true end | ||
|
||
|
||
// Deallocates both the array and ALL of its sub items. | ||
macro _mfree-deep let _array_ in // ptr: array -> bool: success | ||
_array_ while dup _array_ malloc:block malloc:next-block < do | ||
dup @64 mfree drop | ||
8 + end drop | ||
|
||
_array_ mfree | ||
end endmacro | ||
|
||
|
||
macro _mlength | ||
// ptr: addr -> int: length | ||
// Returns the length of an object created using malloc. | ||
malloc:block malloc:container-size | ||
endmacro |
Oops, something went wrong.