![]() |
![]() |
![]() |
![]() |
20 Memory Allocation (memory.hhf)
The memory unit (header file is memory.hhf) contains the routines used to allocate and deallocate dynamic storage on the heap. There are a set of routines that allocate storage for general objects and a set of routines used to specifically allocate storage for strings.
As of HLA v1.69, the "allocation granularity" is eight bytes (that is, these routines always allocate data in multiples of eight byte chunks) and there is a 24-byte metadata overhead associated with each allocation. Therefore, you should avoid doing a large number of small allocations if you want to use memory efficiently. Note that these values are subject to change in future versions of the library.
These memory allocation routines associate a reference counter with each block. Whenever you first allocate a block on the heap, the reference counter is initialized with one. A "mem.newref" call instructs the heap management routines to increment this reference counter. The reference counter tracks how many different pointers in an application are referring to a single block of memory in the heap. When you call the mem.free routine to return storage to the heap, the heap management code will decrement the reference counter and only free up the storage when the reference counter decrements to zero. This can help avoid dangling pointers if you use the mem.newref routine in an appropriate fashion.
Note: none of the memory management routines are thread-safe (nor are any routines that wind up calling these routines). If you want to allocate or deallocate memory in a multi-threaded application, you should create a set of "wrapper" routines that protect calls to the memory allocation routines via a semaphore.
20.1 Generic Memory Allocation
mem.alloc( size:dword ); @returns( "eax" ); malloc( size:dword ); @returns( "eax" ); // Depreciated nameThe mem.alloc routine allocates the requested number of bytes. If successful, this routine returns a pointer to the allocated storage in the EAX register. This routine raises an exception if it fails. "malloc" is an older name for this same routine; newer programs should all use "mem.alloc" rather than "malloc" as the older name may go away at some point.
mem.zalloc( size:dword ); @returns( "eax" );The mem.zalloc routine allocates the requested number of bytes and zeros out the data storage allocated. If successful, this routine returns a pointer to the allocated storage in the EAX register. This routine raises an exception if it fails.
mem.free( memptr:dword ); free( memptr:dword ); // Depreciated nameThis function frees up storage previously allocated by the mem.alloc routine. A pointer returned from mem.alloc must be passed as the parameter to this function. This routine actually decrements a reference counter and only frees the storage when the reference counter becomes zero. See the discussion of mem.newref for more details. Note that "free" is an older name for this same function; newer software should use the full "mem.free" name as the older name may go away at some point or another.
mem.realloc( memptr:dword; newsize:dword ); @returns( "eax" ); realloc( memptr:dword; newsize:dword ); @returns( "eax" ); // Depreciated nameThe mem.realloc routine resizes a previous allocated block of memory. The first parameter is the pointer to the original block, the second parameter is the new size. If the new block is smaller, this routine truncates the data beyond the new size. If the new block is larger, this routine will copy the data if it cannot expand the block in-place. Note that "realloc" is an older name for this same function; newer software should use the full "mem.realloc" name as the older name may go away at some point or another.
If the address of the block does not change, then the block created by mem.realloc inherits the reference counter value from the original block. However, if the mem.realloc function must create a new block and copy the data to that new block, then the reference counter of the new block is set to one. If the reference counter of the original block was not one prior to the realloc operation, then the system simply decrements the original reference counter and does not deallocate the original storage. It is important to realize that the mem.realloc operation may leave two allocated blocks and any previous pointers (noted by mem.newref calls) are still valid and still point at the original data. The pointer returned by mem.realloc points at the new block.
mem.talloc( size ); (returns "eax" as macro result) talloc( size ); (returns "eax" as macro result) // Depreciated nameThis is a macro that "temporarily" allocates the specified storage. This macro allocates the specified storage on the stack and returns the address of the storage (i.e., the ESP value) in the EAX register. The address is always dword aligned; talloc will allocate up to three additional bytes to ensure dword alignment.
You may use the talloc call anywhere a single instruction is legal (including using talloc as an operand to another instruction).
There is no corresponding "tfree" routine since leaving the current procedure automatically deallocates the storage. That is, when a standard procedure exits, it resets the stack pointer, automatically removing the talloc'd data.
Warning: do not use this routine in a procedure that does not have a standard activation record (e.g., those routines with the @noframe option). Also keep in mind that you should not use this routine after pushing data on the stack since you will not be able to retrieve that data unless you first (manually) remove the talloc'd data.
Obviously, you cannot continue referencing the data allocated by mem.talloc once the enclosing procedure returns.
mem.isInHeap( memptr:dword ); isInHeap( memptr:dword ); // Depreciated nameThis function returns false in EAX if memptr does not point at a valid (allocated) object on the heap. It returns a pointer to the start of the data block on the heap if memptr does point within the data area of a valid block. You can use this function to determine whether an object was previously allocated via a call to mem.alloc (and should be free'd via a call to mem.free). Note that this function only returns non-NULL if the block is currently allocated. If you've free'd all instances of the block, this function will return NULL. Note that "isInHeap" is an older name for this same function; newer software should use the full "mem.isInHeap" name as the older name may go away at some point or another. In older versions of this routine, the function simply returned true or false. Assuming older code treated false as zero and true as anything else, that code will continue to function with this new version of the routine.
mem.stat;This funtion returns statistics concerning the heap space in use by the memory allocation routines. This function returns the following values:
EAX - Total amount of space currently in use by the heap (this may not be contiguous!).
EBX - Total amount of free space in the heap.
ECX - Largest block of contiguous free space in the heap.
EDX - Number of blocks on the heap (free and in use).
EDI - Number of free blocks on the heap.
Note that the value in EBX, the total amount of free space in the heap, does not indicate the maximum amount of space that you can allocate. This simply indicates the amount of space that was previously allocated and has been freed. Generally, it is quite possible to allocate more storage than is available in the heap at any one time. Indeed, prior to the first mem.alloc operation, the mem.stat function will return zero in all these registers.
mem.newref( memblk:dword );This funtion increments a reference counter for the memory block whose address you pass as the parameter (this must be a block allocated by mem.alloc). The heap routines will not deallocate storage for a block of memory until you've called mem.free the number of times specified by the reference counter. The mem.alloc call initializes the reference counter to one, calls to mem.newref increment this value by one, calls to mem.free decrement this value by one (and frees the storage once the reference counter hits zero).
mem.getref( memblk:dword );This funtion returns the reference counter value for the specified memory block. This function raises an exception if memblk does not point within a valid memory block. Note that if the block has been deallocated, this function returns zero, it does not raise an exception.
iterator mem.blockInHeap;This is an iterator (used in a foreach loop) that returns the following information for each block (free and in-use) in the heap, one block per iteration:
ECX - Reference count for block
iterator mem.freeBlockInHeap;This iterator is similar to mem.blockInHeap except it only iterates over the free blocks in the heap.
20.2 String Memory Allocation
str.alloc( size:dword ); @returns( "eax" ); str.realloc( theStr:dword; size:dword ); @returns( "eax" ); str.free( memptr:dword ); str.isInHeap( memptr:dword ); @returns( "eax" ); stralloc( size:dword ); @returns( "eax" ); // Depreciated name strrealloc( theStr:dword; size:dword ); @returns( "eax" ); // Depreciated name strfree( memptr:dword ); // Depreciated name strIsInHeap( memptr:dword ); @returns( "eax" ); // Depreciated nameThe string allocation routines are used just like the general memory allocation routines except they allocate storage for a string variable and initialize the maxlength and length fields of the string data structure. The "depreciated" names are older names for these routines that newer software should avoid. These names may go away at some point. Note that the str.isInHeap function returns a pointer to the start of the string's data (the first character in the string) if it determines that the string has been allocated on the heap. See the discussion of mem.realloc to understand how str.realloc affects the reference counter for a string on the heap.
str.talloc( size ); (returns pointer to new string in EAX ). tstralloc( size ); (returns pointer to new string in EAX ). // Depreciated nameThis is a macro that initializes storage on the stack for a string capable of holding size characters. This routine has the same benefits and drawbacks as the talloc routine.
Note that the size parameter is the actual number of characters needed. the str.talloc routine automatically bumps this value up by nine to make room for the length, MaxStrLen, and zero terminator fields of the string object. This macro also ensures that the stack (and, therefore, the string) is dword aligned in memory (it does this by adding up to three additional bytes to the string). Note that "tstralloc" is an older name for this same function; newer software should use the full "str.talloc" name as the older name may go away at some point or another.
str.newref( memblk:dword );This funtion increments a reference counter for the memory block whose address you pass as the parameter (this must be a block allocated by str.alloc). The heap routines will not deallocate storage for a block of memory until you've called str.free the number of times specified by the reference counter. The str.alloc call initializes the reference counter to one, calls to str.newref increment this value by one, calls to str.free decrement this value by one (and frees the storage once the reference counter hits zero).
str.getref( memblk:dword );This funtion returns the reference counter value for the specified string memory block. This function raises an exception if memblk does not point within a valid memory block allocated for a string. Note that if the string has been deallocated, this function returns zero, it does not raise an exception.
![]() |
![]() |
![]() |
![]() |
![]() |