asm_dip/toolchain/fasm2/source/malloc.inc
2024-11-25 00:04:53 -05:00

344 lines
8.6 KiB
PHP

; a very basic implementation of malloc/realloc
; for porting fasmg to systems that do not have such API natively
struct MemoryHeader
size dd ? ; total size of this block, the lowest bit set for blocks in use
preceding_size dd ? ; total size of the block that precedes this one in memory (zero if this is an initial block of address range)
ends
struct FreeMemory
header MemoryHeader
right dd ? ; next free block in cyclic list
left dd ? ; previous free block in cyclic list
ends
assert defined valloc
; in: ecx = requested minimum size
; out: eax - allocated block, ecx = allocated size, zero if failed
; preserves: ebx, esi, edi
; note:
; valloc is called to request raw memory from the OS;
; it is encouraged to allocate more memory than requested (even entire available memory),
; the obtained memory is kept indefinitely in the pool for malloc
; and should be released by OS automatically when the process ends;
; if the OS does not do it automatically, additional list of the memory areas
; may need to be maintained to release them before exit
malloc:
malloc_fixed:
malloc_growable:
; in: ecx = requested size
; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return)
; preserves: ebx, esi, edi
add ecx,sizeof.MemoryHeader-1
jc out_of_memory
and ecx,(-1) shl 2
add ecx,1 shl 2
jc out_of_memory
cmp ecx,sizeof.FreeMemory
jae malloc_size_ok
mov ecx,sizeof.FreeMemory
malloc_size_ok:
mov eax,[malloc_freelist]
test eax,eax
jz malloc_new
find_fit:
cmp ecx,[eax+MemoryHeader.size]
jbe malloc_use_block
mov eax,[eax+FreeMemory.left]
cmp eax,[malloc_freelist]
jne find_fit
malloc_new:
push ecx
add ecx,sizeof.MemoryHeader
jc out_of_memory
call valloc
test ecx,ecx
jz out_of_memory
xor edx,edx
mov [eax+MemoryHeader.preceding_size],edx
pop edx
push eax
sub ecx,edx
cmp ecx,sizeof.FreeMemory+sizeof.MemoryHeader
jb no_space_for_free_block
mov [eax+MemoryHeader.size],edx
add eax,edx
mov [eax+MemoryHeader.preceding_size],edx
mov edx,ecx
sub edx,sizeof.MemoryHeader
dec edx
and edx,(-1) shl 2
add edx,1 shl 2
mov [eax+MemoryHeader.size],edx
mov ecx,[malloc_freelist]
jecxz freelist_empty
mov [eax+FreeMemory.left],ecx
mov edx,eax
xchg edx,[ecx+FreeMemory.right]
mov [eax+FreeMemory.right],edx
mov [edx+FreeMemory.left],eax
mov edx,[eax+MemoryHeader.size]
jmp free_block_ready
no_space_for_free_block:
sub ecx,sizeof.MemoryHeader
add edx,ecx
mov [eax+MemoryHeader.size],edx
jmp append_limiter
freelist_empty:
mov [eax+FreeMemory.left],eax
mov [eax+FreeMemory.right],eax
free_block_ready:
mov [malloc_freelist],eax
append_limiter:
add eax,edx
mov [eax+MemoryHeader.preceding_size],edx
mov [eax+MemoryHeader.size],sizeof.MemoryHeader or 1 ; cannot be freed
pop eax
finish_malloc:
mov ecx,[eax+MemoryHeader.size]
or [eax+MemoryHeader.size],1
add eax,sizeof.MemoryHeader
sub ecx,sizeof.MemoryHeader
retn
malloc_use_block:
mov edx,[eax+MemoryHeader.size]
sub edx,ecx
cmp edx,sizeof.FreeMemory
jb use_whole_block
mov [eax+MemoryHeader.size],ecx
mov [eax+ecx+MemoryHeader.preceding_size],ecx
add ecx,eax
mov [malloc_freelist],ecx
mov [ecx+MemoryHeader.size],edx
mov [ecx+edx+MemoryHeader.preceding_size],edx
mov edx,[eax+FreeMemory.right]
cmp edx,eax
je update_free_singleton
mov [ecx+FreeMemory.right],edx
mov [edx+FreeMemory.left],ecx
mov edx,[eax+FreeMemory.left]
mov [ecx+FreeMemory.left],edx
mov [edx+FreeMemory.right],ecx
jmp finish_malloc
update_free_singleton:
mov [ecx+FreeMemory.left],ecx
mov [ecx+FreeMemory.right],ecx
jmp finish_malloc
use_whole_block:
mov edx,[eax+FreeMemory.right]
cmp edx,eax
je depleted_freelist
mov ecx,[eax+FreeMemory.left]
mov [ecx+FreeMemory.right],edx
mov [edx+FreeMemory.left],ecx
mov [malloc_freelist],edx
jmp finish_malloc
depleted_freelist:
and [malloc_freelist],0
jmp finish_malloc
mfree:
; in: eax - memory block
; out: cf set on error
; preserves: ebx, esi, edi
; note: eax may have value 0 or -1, it should be treated as invalid input then
test eax,eax
jz mfree_error
cmp eax,-1
je mfree_error
sub eax,sizeof.MemoryHeader
mov ecx,[eax+MemoryHeader.size]
btr ecx,0
jnc mfree_error
cmp ecx,sizeof.FreeMemory
jb mfree_error
cmp [eax+ecx+MemoryHeader.preceding_size],ecx
jne mfree_error
mov [eax+MemoryHeader.size],ecx
mov edx,eax
sub edx,[eax+MemoryHeader.preceding_size]
cmp edx,eax
je no_preceding_block
test [edx+MemoryHeader.size],1
jz coalesce_with_preceding_block
no_preceding_block:
test [eax+ecx+MemoryHeader.size],1
jz coalesce_with_following_block
mov ecx,[malloc_freelist]
jecxz mfree_init_freelist
mov edx,[ecx+FreeMemory.right]
mov [eax+FreeMemory.left],ecx
mov [edx+FreeMemory.left],eax
mov [eax+FreeMemory.right],edx
mov [ecx+FreeMemory.right],eax
mfree_ok:
mov [malloc_freelist],eax
clc
retn
mfree_init_freelist:
mov [eax+FreeMemory.left],eax
mov [eax+FreeMemory.right],eax
jmp mfree_ok
mfree_error:
stc
retn
coalesce_with_preceding_block:
add ecx,[edx+MemoryHeader.size]
test [edx+ecx+MemoryHeader.size],1
jz coalesce_on_both_ends
mov [edx+MemoryHeader.size],ecx
mov [edx+ecx+MemoryHeader.preceding_size],ecx
clc
retn
coalesce_on_both_ends:
lea eax,[edx+ecx]
add ecx,[eax+MemoryHeader.size]
mov [edx+MemoryHeader.size],ecx
mov [edx+ecx+MemoryHeader.preceding_size],ecx
mov [malloc_freelist],edx
mov ecx,[eax+FreeMemory.left]
mov edx,[eax+FreeMemory.right]
mov [edx+FreeMemory.left],ecx
mov [ecx+FreeMemory.right],edx
clc
retn
coalesce_with_following_block:
push ebx
lea ebx,[eax+ecx]
add ecx,[ebx+MemoryHeader.size]
mov [eax+MemoryHeader.size],ecx
mov [eax+ecx+MemoryHeader.preceding_size],ecx
mov ecx,[ebx+FreeMemory.left]
mov edx,[ebx+FreeMemory.right]
mov [ecx+FreeMemory.right],eax
mov [edx+FreeMemory.left],eax
mov ecx,[ebx+FreeMemory.left]
mov edx,[ebx+FreeMemory.right]
mov [eax+FreeMemory.left],ecx
mov [eax+FreeMemory.right],edx
pop ebx
jmp mfree_ok
realloc:
; in: eax - memory block, ecx = requested size
; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return)
; preserves: ebx, esi, edi
add ecx,sizeof.MemoryHeader-1
jc out_of_memory
and ecx,(-1) shl 2
add ecx,1 shl 2
jc out_of_memory
sub eax,sizeof.MemoryHeader
mov edx,[eax+MemoryHeader.size]
and edx,not 1
cmp ecx,edx
jbe realloc_retain
test [eax+edx+MemoryHeader.size],1
jnz realloc_and_copy
add edx,[eax+edx+MemoryHeader.size]
cmp ecx,edx
ja realloc_and_copy
sub edx,ecx
cmp edx,sizeof.FreeMemory
jb append_whole_block
push esi edi
push ecx edx
lea edi,[eax+ecx]
xchg ecx,[eax+MemoryHeader.size]
and ecx,not 1
lea esi,[eax+ecx]
mov ecx,[esi+FreeMemory.left]
mov edx,[esi+FreeMemory.right]
mov [edx+FreeMemory.left],edi
mov [ecx+FreeMemory.right],edi
mov ecx,[esi+FreeMemory.left]
mov edx,[esi+FreeMemory.right]
mov [edi+FreeMemory.left],ecx
mov [edi+FreeMemory.right],edx
mov [malloc_freelist],edi
pop edx ecx
mov [edi+MemoryHeader.size],edx
mov [edi+edx+MemoryHeader.preceding_size],edx
mov [edi+MemoryHeader.preceding_size],ecx
pop edi esi
jmp finish_malloc
append_whole_block:
add edx,ecx
mov [eax+edx+MemoryHeader.preceding_size],edx
xchg edx,[eax+MemoryHeader.size]
and edx,not 1
add edx,eax
mov ecx,[edx+FreeMemory.left]
cmp ecx,edx
je depleted_freelist
mov edx,[edx+FreeMemory.right]
mov [ecx+FreeMemory.right],edx
mov [edx+FreeMemory.left],ecx
mov [malloc_freelist],ecx
jmp finish_malloc
realloc_retain:
and [eax+MemoryHeader.size],not 1
jmp finish_malloc
realloc_and_copy:
push esi edi
lea esi,[eax+sizeof.MemoryHeader]
call malloc_growable
push eax ecx
mov edi,eax
mov eax,esi
mov ecx,[esi-sizeof.MemoryHeader+MemoryHeader.size]
shr ecx,2
rep movsd
call mfree
pop ecx eax
pop edi esi
retn
if used mcheck
mcheck:
pushf
push eax ebx ecx edx
mov eax,[malloc_freelist]
test eax,eax
jz integrity_verified
verify_freelist:
mov ebx,eax
verify_preceding_blocks:
mov ecx,[ebx+MemoryHeader.preceding_size]
jecxz preceding_blocks_ok
sub ebx,ecx
mov edx,[ebx+MemoryHeader.size]
and edx,not 1
cmp ecx,edx
je verify_preceding_blocks
jmp internal_error
preceding_blocks_ok:
mov ebx,eax
verify_following_blocks:
mov ecx,[ebx+MemoryHeader.size]
and ecx,not 1
cmp ecx,sizeof.MemoryHeader
je following_blocks_ok
add ebx,ecx
cmp ecx,[ebx+MemoryHeader.preceding_size]
je verify_following_blocks
jmp internal_error
following_blocks_ok:
mov edx,[eax+FreeMemory.right]
cmp eax,[edx+FreeMemory.left]
je verify_next
jmp internal_error
verify_next:
mov eax,edx
cmp eax,[malloc_freelist]
jne verify_freelist
integrity_verified:
pop edx ecx ebx eax
popf
retn
end if