Routines for dynamic memory allocation
See also: mem.h
void *alloca (unsigned long Size); |
Allocates memory on the local storage space.
alloca is a function which is not included in the ANSI C standard, but it exists in many C dialects
(including GCC4TI). It allocates a block of Size bytes on the CPU stack (local
storage space), in opposite to malloc which allocates memory on the
memory heap. The space allocated with alloca exists until the containing function returns
(i.e. it will be automatically deallocated at the end of the function). Be aware of the
fact that the size of the hardware stack on TI calculators is limited to 16 kilobytes, so
do not use alloca for allocating large blocks.
alloca is usually used for simulating automatic variable-sized one-dimensional arrays, which will be automatically
deallocated when the function ends (like all automatic variables), so it is sometimes more
preferable than malloc. For example, to simulate
int a[n];
where 'n' is not known in advance, you can use
int *a = alloca (n);
Note, however, that GNU C extensions allows variable-length
arrays, which are more elegant than usage of alloca, and which may also be with more than
one dimension (which is not possible with alloca).
Note: alloca is a built-in (open-coded) function in GNU C, which is translated to a single
instruction which simply adjusts the stack. Some compiler command options (like
'-ansi') prevent alloca from being an open-coded function. In this implementation
of GCC4TI, you cannot use alloca with such options (however, it is very unlikely that you will
ever have such problems).
void *calloc_throw (unsigned short NoOfItems, unsigned short SizeOfItems); |
Performs calloc, and throws an error if not successful.
calloc_throw calls calloc and throws a memory error if there is not enough memory. Otherwise, it returns a pointer to the allocated block.
void *calloc (unsigned short NoOfItems, unsigned short SizeOfItems); |
Allocates a memory block for a given number and size of items.
calloc allocates a block of NoOfItems x SizeOfItems
bytes from the memory heap. On success, calloc returns a pointer to the newly allocated
block of memory. If not enough space exists for the new block, it returns
NULL. The allocated block will be cleared to zero content.
Note: In releases of TIGCCLIB prior to 2.0, calloc was implemented here as a macro,
Now, it is a function. It first calls malloc
with NoOfItems x SizeOfItems as the argument, then calls
memset if the first call was successful.
See also: malloc, realloc, free, calloc_throw
void free (void *Ptr); |
Frees an allocated block.
free deallocates a memory block allocated by a previous call to
malloc or calloc.
Note: free is in fact an ANSI C alias for a TIOS routine originally called
HeapFreePtr.
Do not attempt to use free on a pointer which has changed after it was allocated
with malloc, calloc,
or realloc. For example, the following code
will certainly crash the calculator:
char *ptr = malloc (2); if (ptr) { *(ptr++) = 'A'; // pointer changed here *ptr = 0; ... free (ptr); }
Instead, preserve the original pointer and pass it to free.
See also: malloc, realloc, calloc
short FreeHandles (void); |
Determines the number of free handles.
FreeHandles returns the number of free handles.
HANDLE HeapAlloc (unsigned long Size); |
Allocates memory and returns a handle of allocated block.
HeapAlloc allocates a block of heap memory of Size bytes (all odd sizes are rounded up to be even) and returns its handle. Allocated blocks are kept in a singly-linked list of blocks. Returns H_NULL if there is not enough memory. The maximum size of a single block is 65520 bytes, and the minimum size is 6 bytes. Use HeapDeref to dereference the handle and get a pointer to the actual memory. Note that an unlocked pointer to the heap is valid only as long as heap compression (i.e. garbage collection) is not done. This routine may cause garbage collection.
HANDLE HeapAllocESTACK (unsigned long Size); |
Like HeapAlloc, but reduces the size of the expression stack if necessary.
HeapAllocESTACK works like HeapAlloc, but
reduces the size of the expression stack if necessary (i.e. if there is not
enough memory). Returns H_NULL if there is not enough
memory even after reducing the size of the expression stack.
Note: The information about this routine in releases of TIGCCLIB prior to 2.0
was wrong!
HANDLE HeapAllocHigh (unsigned long Size); |
Allocates memory at the high end of the heap and returns the handle of the allocated block.
HeapAllocHigh allocates a block of Size bytes of heap memory at the high end of the heap,
locks it, and returns its handle. Returns H_NULL if there is not enough memory.
The primary use of this routine is to allocate task
local storage. It also compresses the heap first to (hopefully) move all used
(unlocked) blocks of memory down. This routine will cause garbage collection.
Note: Blocks of memory that are locked for long periods of time should be moved high
in memory so that they do not interfere as much with rest of the system. This routine
ALWAYS compresses the heap before it tries to allocate the requested memory and so is
much slower than the standard HeapAlloc routine. Locking
memory may cause the system to run out of useable memory sooner than if memory is
kept unlocked.
See also: HeapAlloc
HANDLE HeapAllocHighThrow (unsigned long Size); |
Performs HeapAllocHigh, and throws an error if not successful.
HeapAllocHighThrow calls HeapAllocHigh and throws a memory error if there is not enough memory. Otherwise, it returns the handle of the allocated block.
void *HeapAllocPtr (unsigned long Size); |
Allocates memory at the high end of the heap and returns a pointer to the allocated block.
HeapAllocPtr performs HeapAllocHigh, but instead of the handle, it returns
a pointer to the allocated block (NULL in a case of error). It also
gives a necessary information to the heap manager which is needed later for deallocating
the memory using HeapFreePtr command. This routine is principally
equivalent to ANSI C malloc function, so it is aliased here also as
malloc.
Note: If somebody is interested in it, the handle of the allocated block is stored two
bytes below the address returned by HeapAllocPtr.
void *HeapAllocPtrThrow (unsigned long Size); |
Performs HeapAllocPtr, and throws an error if not successful.
HeapAllocPtrThrow calls HeapAllocPtr and throws a memory error if there is not enough memory. Otherwise, it returns the handle of the allocated block.
HANDLE HeapAllocThrow (unsigned long Size); |
Performs HeapAlloc, and throws an error if not successful.
HeapAllocThrow calls HeapAlloc and throws a memory error if there is not enough memory. Otherwise, it returns the handle of the allocated block.
unsigned long HeapAvail (void); |
Determines the size of the heap.
HeapAvail returns the total amount of free bytes in the heap (the sum of all of the individual blocks of memory).
void HeapCompress (void); |
Compresses the heap.
HeapCompress coalesces all used heap blocks, deleting any free blocks from the heap if possible. If there are any locked blocks, the heap may remain fragmented. This routine is called automatically by the system whenever it is needed and usually should not be called by applications.
See also: HeapShuffle
void *HeapDeref (HANDLE Handle); |
Dereferences a handle.
HeapDeref dereferences Handle and returns a pointer to the actual block
of the memory defined by that Handle. Nearly all heap allocation routines
return a "handle" which is an identifier for a block of memory allocated in the heap.
In order to use that memory, the handle must be dereferenced. Once a handle is
dereferenced, that pointer is valid as long as nothing else is done to cause
the heap to be compressed. If the heap is compressed the handle can be
re-dereferenced to make it valid again. If a handle is locked, then the
pointer that references that block of memory is valid even after the heap is
compressed (since locking a handle means the heap manager will never
move the memory associated with that handle).
Note: HeapDeref returns garbage if Handle is H_NULL.
void *HeapEnd (void); |
Determines the end of the heap.
HeapEnd returns a pointer to the end of the heap.
void HeapFree (HANDLE Handle); |
Frees a heap block given its handle.
HeapFree frees a heap block associated with handle Handle.
void HeapFreeIndir (HANDLE *HandlePtr); |
Frees a heap block given a pointer to it.
HeapFreeIndir is like HeapFree except you pass the address
of a variable which keeps the block handle instead of the handle itself. If the handle
that HandlePtr points to is not H_NULL, then it frees that
handle and sets the handle variable that is pointed to by HandlePtr to
H_NULL.
Maybe this sounds a bit confusing to you. In fact, doing
HeapFreeIndir (&handle); // The ampersand ('&') is important
works exactly the same as
if (handle) HeapFree (handle); handle = H_NULL;
Note: In releases of TIGCCLIB prior to 2.2, the information about this routine was quite misleading or even wrong.
void HeapFreePtr (void *Ptr); |
Frees a block allocated using HeapAllocPtr.
HeapFreePtr frees a heap block pointed to by Ptr. The block must be a block
which was allocated using the HeapAllocPtr command.
Do not attempt to use HeapFreePtr on a pointer which has changed after it was allocated
with HeapAllocPtr. For example, the following code
will certainly crash the calculator:
char *ptr = HeapAllocPtr (2); if (ptr) { *(ptr++) = 'A'; // pointer changed here *ptr = 0; ... HeapFreePtr (ptr); }
Instead, preserve the original pointer and pass it to HeapFreePtr.
HANDLE HeapGetHandle (void); |
Gets the next available handle.
HeapGetHandle returns the number of the first unallocated (free) handle, or H_NULL if there are no free handles. Used mainly for internal purposes.
short HeapGetLock (HANDLE Handle); |
Determines whether a block is locked.
Returns non-zero if the block given by Handle is locked, 0 if it is not (i.e. if that memory is free to move on the next heap compression).
HANDLE HeapLock (HANDLE Handle); |
Locks a block.
HeapLock locks the block referenced by Handle so that it is not moved
during garbage collection. Returns Handle if OK, else returns
H_NULL.
Note: Locking memory may cause the system to run out of usable memory
sooner than if memory is kept unlocked.
unsigned long HeapMax (void); |
Determines the size of the largest allocatable block.
HeapMax returns the size of the largest block allocatable on the heap (it will be in the range of 0 through 65520). Note that this may not be equal to HeapAvail due to locked blocks, overhead, and maximum block size. This routine will (always) cause heap compression (garbage collection).
HANDLE HeapMoveHigh (HANDLE Handle); |
Reallocates a block.
HeapMoveHigh tries to reallocate a block referenced by Handle as high in memory as possible. The block must not be locked. If successful, returns the handle passed; otherwise returns H_NULL (in this case, the block is still in the same place as before, so no memory is lost). This routine will cause heap compression (garbage collection).
HANDLE HeapPtrToHandle (void *Ptr); |
Determines the handle associated with a block.
HeapPtrToHandle returns the handle associated to the block whose beginning is
pointed to by Ptr (or H_NULL if Ptr
does not point to the beginning of a block).
This routine works by searching the entire table of handles for the given pointer.
It will return meaningful results only if the block of memory was not moved (e.g. by heap
compression) since the pointer was originally obtained.
Note: PtrToHandle, implemented in the
GCC4TI library, returns the handle associated to the block referenced by the pointer,
even if the pointer does not point to the beginning of the block.
See also: PtrToHandle
HANDLE HeapRealloc (HANDLE Handle, unsigned long NewSize); |
Reallocates a block to a new size.
If Handle is H_NULL, HeapRealloc calls
HeapAlloc. Otherwise, it tries to reallocate the given heap block
to a new size. Returns H_NULL if there is not enough memory (it will
try to call HeapCompress) or if new size is invalid; otherwise
returns Handle (but Handle will still be valid even if there was not enough memory).
If the heap block is locked, then the block will not be moved in
order to reallocate it. However, unlocked blocks above the heap block will be moved.
The contents of the object will be unchanged up to
the lesser of the new and old size. If the new size is larger, the value of the
newly allocated portion of the block is indeterminate.
This routine may cause garbage collection.
Note: Reallocating a locked block may fail very easily, especially if
you have allocated and locked some blocks just shortly before. The reason is
simple: If you allocate memory blocks in sequence, they are usually located
close to each other. If you try to grow a locked block which lies directly in
front of another locked block, the operation will fail because the second
locked block cannot be moved. In general it is a good idea to unlock memory
blocks before using HeapRealloc, and to lock them again afterwards if
necessary. This way the probability to succeed is much higher.
HANDLE HeapReallocThrow (HANDLE Handle, unsigned long NewSize); |
Performs HeapRealloc, and throws an error if not successful.
HeapReallocThrow calls HeapRealloc and throws a memory error if there is not enough memory. Otherwise, it returns the handle of the allocated block.
void HeapShuffle (void); |
Shuffles all unlocked blocks on the heap.
HeapShuffle shuffles all unlocked blocks on the heap.
As a consequence, all dereferenced handles to unlocked blocks of memory in the heap may become invalid.
This function is not the same as HeapCompress.
It is used mainly for debugging the program.
Here is a test example (called "Heap Shuffle"):
// Call HeapShuffle and compare addresses #define USE_TI89 // Compile for TI-89 #define USE_TI92PLUS // Compile for TI-92 Plus #define USE_V200 // Compile for V200 #define OPTIMIZE_ROM_CALLS // Use ROM Call Optimization #define MIN_AMS 200 // Compile for AMS 2.00 or higher #define SAVE_SCREEN // Save/Restore LCD Contents #include <tigcclib.h> // Include All Header Files // Main Function void _main(void) { unsigned char *p1, *p2, *p3, *p4, *p5, *p6; HANDLE h1, h2, h3; short f = FontGetSys (); unsigned char b[50]; h1 = HeapAlloc (0x1000); h2 = HeapAlloc (0x1000); h3 = HeapAlloc (0x1000); if (h1 && h2 && h3) { ClrScr (); p1 = HeapDeref (h1); p3 = HeapDeref (h2); p5 = HLock (h3); HeapShuffle (); p2 = HeapDeref (h1); p4 = HeapDeref (h2); p6 = HeapDeref (h3); FontSetSys (F_4x6); DrawStr (0, 0, "Before/after shuffle:", A_NORMAL); sprintf (b, "Block 1 (unlocked): %lp %lp", p1, p2); DrawStr (0, 10, b, A_NORMAL); sprintf (b, "Block 2 (unlocked): %lp %lp", p3, p4); DrawStr (0, 20, b, A_NORMAL); sprintf (b, "Block 3 (locked): %lp %lp", p5, p6); DrawStr (0, 30, b, A_NORMAL); FontSetSys (f); // Restore previous font GKeyIn (NULL, 0); } HeapFree (h3); HeapFree (h2); HeapFree (h1); }
See also: HeapCompress, HeapWalk
unsigned long HeapSize (HANDLE Handle); |
Determines the size of an allocated block.
HeapSize returns the number of bytes allocated for the heap block referenced by Handle. Due to word alignment and minimum block size, this may not be the amount it was allocated with. Also note that because of locked blocks, it is possible (rare) that a heap block will actually be bumped up a few words by the HeapCompress routine. So never assume that the value returned by HeapSize is the true number of bytes used by the data stored in the heap block.
HANDLE HeapUnlock (HANDLE Handle); |
Unlocks a block.
HeapUnlock unlocks the block referenced by the Handle so that it can be moved during garbage collection. Returns Handle if OK, else returns H_NULL.
short HeapWalk (short function); |
Verifies and dumps the contents of the heap.
HeapWall looks through the heap to verify it is valid.
Then it prints the status of the heap, prints the size of each heap block and its handle, or prints the symbol table, according to the value of function.
HeapWalk uses LIO_SendData to send the output through the link port.
The valid values for function, defined in the enum HeapWalkCmds, are:
H_WALK_VERIFY | The function just verifies the heap, and outputs nothing to the link port. |
H_WALK_STATUS | The function outputs the total free space, maximum size of a free block, number of used and free blocks, and the number of locked blocks. |
H_WALK_DUMP | The function outputs the heap status and the size of the heap block for each handle. |
H_WALK_SYM | AMS 2.04 or higher: The function outputs the entire VAT. |
// Sends the list of all variables and folders through the link port. // This program does what HeapWalk(H_WALK_SYM); does on AMS 2.04 and // later, but also works on any AMS version. #define USE_TI89 // Compile for TI-89 #define USE_TI92PLUS // Compile for TI-92 Plus #define USE_V200 // Compile for V200 #define SAVE_SCREEN // Save/Restore LCD Contents #define OPTIMIZE_ROM_CALLS // Use ROM Call Optimization #define NO_CALC_DETECT #define ENABLE_ERROR_RETURN #define MIN_AMS 100 // Compile for AMS 1.00 or higher #include <tigcclib.h> // Include All Header Files void _main(void) { SYM_ENTRY *symptr; unsigned char buffer[256]; #ifdef SAVE_SYMPG // Saving the SymPG isn't necessary in _main, nobody else does it. SymPG_S save; TRY memcpy(&save, pSymPG, sizeof(SymPG_S)); #endif if ((symptr = SymFindFirst(NULL,2)) != NULL) { strcpy(buffer, "\r\nName/Flags/hVal (dec)\r\n"); LIO_SendData(buffer, strlen((char*)buffer)); do { short flags = symptr->flags.flags_n; if ((flags&SF_FOLDER)) sprintf((char *)buffer, "FOLDER: %-8s %04X %hd\r\n", symptr->name, flags, symptr->handle); else sprintf((char *)buffer, "%8s\\%-8s %04X %hd\r\n", SymFindFolderName(), symptr->name, flags, symptr->handle); LIO_SendData(buffer, strlen((char *)buffer)); symptr = SymFindNext(); } while (symptr != NULL); } #ifdef SAVE_SYMPG // See above. FINALLY memcpy(pSymPG, &save, sizeof(SymPG_S)); ENDFINAL #endif }
See also: HeapShuffle, vat.h, SymFindFolderName, pSymPG, SymPG_S
void *HLock (HANDLE Handle); |
Locks and dereferences a handle.
HLock locks a block referenced by Handle so that it will not
move on the next heap compression, and returns a pointer to the actual memory
block. Returns NULL in a case of error.
Note: Locking memory may cause the system to run out of usable memory
sooner than if memory is kept unlocked.
void hStrAppend (HANDLE h, unsigned char *str); |
Appends a string to a handle that contains a string.
h is the handle to string to be lengthened.
str is the string to append to the handle h.
This routine may cause heap compression, and it throws "Error: memory" if there is not enough memory to expand the handle h.
This routine provides a way to store in the clipboard something you can't paste with DIAMOND+PASTE !
But: you need to take great care when you use this routine: use it only on handles your
program allocated, because writing garbage into system handles usually crashes the calculator !
hStrAppend is normally a ROM_CALL (ROM_CALL 45F) available on AMS 2.xx only, but a wrapper makes the use of this function possible on any AMS version (take great care with very old AMS 1.00 for 92+, though).
hStrAppend is implemented as follows:
void hStrAppend (HANDLE h, unsigned char *str) { unsigned char* str1; unsigned long lstr1, lstr, lbuf; str1 = HeapDeref(h); lstr1 = strlen((char *)str1); /* length of string in handle */ lstr = strlen((char *)str); /* length of string to append */ lbuf = lstr1 + lstr + 1; /* new space requirement */ if (!HeapRealloc(h, lbuf)) /* try to get new space */ ER_throw(670); str1 = (unsigned char *)HeapDeref(h) + lstr1; /* point to end of original string */ memcpy(str1, str, lstr+1); /* append new string */ }
void *malloc_throw (unsigned long Size); |
Performs malloc, and throws an error if not successful.
malloc_throw calls malloc and throws a memory error if there is not enough memory. Otherwise, it returns a pointer to the allocated block.
void *malloc (unsigned long Size); |
Allocates a memory block.
malloc allocates a block of Size bytes from the memory heap.
It allows a program to allocate memory explicitly as it's needed, and in
the exact amounts needed. The heap is used for dynamic allocation of
variable-sized blocks of memory. Many data structures, such as trees and
lists, naturally employ heap memory allocation. On success, malloc returns
a pointer to the newly allocated block of memory. If not enough space
exists for the new block, it returns NULL. The contents
of the block are left unchanged. If the argument Size is zero
malloc also returns NULL.
malloc is in fact an ANSI C alias for a TIOS routine originally called
HeapAllocPtr (see description of it for more
system info).
Note: As the TIOS memory manager assigns a handle to each allocated block, and
as the total number of handles is limited, malloc is not good for algorithms
where you need to allocate a large number of small blocks (as in implementations
of linked lists which are usually seen in various C language books and tutorials).
The same is true for all other TIOS memory management routines.
See also: realloc, calloc, free, malloc_throw
HANDLE PtrToHandle (void *Ptr); |
Determines the handle associated with a block.
PtrToHandle returns the handle associated to the block pointed to by Ptr
(or H_NULL if Ptr does not point within a block).
This routine will return meaningful results only if the block of memory was not moved (e.g.
by heap compression) since the pointer was originally obtained.
The following program shows the difference between HeapPtrToHandle
and PtrToHandle:
// Return the EStack handle. #define USE_TI89 // Compile for TI-89 #define USE_TI92PLUS // Compile for TI-92 Plus #define USE_V200 // Compile for V200 #define RETURN_VALUE // Return Pushed Expression #define MIN_AMS 101 // Compile for AMS 1.01 or higher #include <tigcclib.h> // Include All Header Files // Main Function void _main(void) { push_END_TAG (); push_END_TAG (); // Outside all allocated blocks. push_shortint (HeapPtrToHandle((void *)1)); // Strictly inside a block. push_shortint (HeapPtrToHandle((void *)estack_max_index)); // Strictly inside a block, must be the same handle as estack_max_index. push_shortint (HeapPtrToHandle((void *)top_estack)); // Beginning of a block. push_shortint (PtrToHandle(HeapDeref(1))); push_LIST_TAG (); push_END_TAG (); // Outside all allocated blocks. push_shortint (PtrToHandle((void *)1)); // Strictly inside a block. push_shortint (PtrToHandle((void *)estack_max_index)); // Strictly inside a block, must be the same handle as estack_max_index. push_shortint (PtrToHandle((void *)top_estack)); // Beginning of a block. push_shortint (PtrToHandle(HeapDeref(1))); push_LIST_TAG (); push_LIST_TAG (); }
See also: HeapPtrToHandle
void *realloc_throw (void *Ptr, unsigned long NewSize); |
Performs realloc, and throws an error if not successful.
realloc_throw calls realloc and throws a memory error if there is not enough memory. Otherwise, it returns a pointer to the allocated block.
void *realloc (void *Ptr, unsigned long NewSize); |
Reallocates allocated memory.
realloc attempts to shrink or expand the previously allocated block to NewSize
bytes. The Ptr argument points to a memory block previously obtained by calling
malloc, calloc, or realloc.
It may also be NULL; in this case the function simply calls malloc.
realloc adjusts the size of the allocated block to NewSize, copying the contents to a new location
if necessary (note that the block will not stay in high memory after the reallocation, but
it will still remain locked; see other functions from this header file for more info).
realloc returns the address of the reallocated block, which can be different than the address
of the original block. If the block cannot be reallocated, realloc returns NULL.
Usually realloc keeps the current contents of the block intact; only the portion of the block
which is newly allocated is not initialized and contains random content. However, unlike
HeapRealloc, the function frees the block pointed to by
Ptr if there was an error, so you cannot assume that the data pointed to by Ptr
is still valid. This is done because there is no way to guarantee that Ptr is still
valid after the heap compression which HeapRealloc tries to
perform (this function uses HeapRealloc internally).
Note: realloc is introduced to increase compatibility with ANSI C. It is better to use
the official TIOS function HeapRealloc, which uses a system of handles
to memory blocks.
See also: malloc, calloc, free, realloc_throw
#define H_NULL 0 |
A null-handle value.
H_NULL is a null-handle defined as a constant with value 0.
#define NULL ((void *) 0) |
A null-pointer value.
NULL is a null-pointer value, defined as a pointer to the address 0.
enum Bool {FALSE, TRUE}; |
An enumeration to describe true or false values.
The enum Bool represents a boolean value, that is, either 'true' or 'false'. The constant FALSE equals 0; TRUE equals 1.
typedef unsigned short HANDLE; |
Represents a handle associated with an allocated memory block.
The HANDLE type is mainly used to manage dynamically allocated memory using
functions from alloc.h, but a lot of functions
from other header files take instances of this type as parameters.
A handle points to a word-aligned block of memory, whose current address can
be retrieved using HeapDeref.
See also: HeapAlloc, HeapFree, HLock, HeapUnlock, HeapDeref
enum HeapWalkCmds {H_WALK_VERIFY = 0, H_WALK_STATUS = 1, H_WALK_DUMP = 2, H_WALK_SYM = 3}; |
An enumeration describing the valid parameters for HeapWalk.