home *** CD-ROM | disk | FTP | other *** search
- /*
- #### # # # #
- # # # # # The FreeWare C library for
- # # ## ### # # # # ### RISC OS machines
- # # # # # # # # # # # ___________________________________
- # # #### ### ## # # # #
- # # # # # # # # # # Please refer to the accompanying
- #### ### #### # # ##### # ### documentation for conditions of use
- ________________________________________________________________________
-
- File: Mem.c
- Author: Copyright © 1993, 1994 Jason Williams
- Version: 1.01 (30 May 1994)
- Purpose: Dynamic memory management
-
- * Details for use of Mem are in Mem.h
- * Ideas that may be added to Mem.c in future are in Docs.ModuleNote.Mem
- *
- * NOTE that some functions and variables are Mem__xxxx: These are NOT
- * intended for external use outside Mem_ Functions, and are only externs
- * so that Mem does not need to be linked with as one huge chunk.
- *
- * Details of how this code works:
- * Mem memory chunks are held in a 'heap' of memory above the area grabbed
- * by the SharedCLibrary for mallocs and stackspace, extending up to the
- * end of the WimpSlot, which is adjusted up & down as necessary.
- *
- * The Mem heap is simple, and is *exactly* filled by a contiguous set of
- * chunks. This is laid out as follows:
- *
- * [H] [H|--data--:P] ... [H|-----data-----:P] [H]
- *
- * Where H is a chunk header containing
- * + the size of the previous chunk, including H and P
- * (for moving backwards through the chain of chunks)
- *
- * + the size of this chunk, including H and P
- * (for moving forwards through the chain of chunks)
- *
- * + the size of the user data held in this chunk
- * Note that userdatasize + H + P does not necessarily == chunksize
- *
- * + the anchor of this chunk
- * (a pointer to the user's pointer to the chunk)
- * This is used for 2 things: To update the user's anchor when the chunk
- * is moved, and also to check that all accesses are legal.
- *
- * P is padding to word-align the block. Note that the padding may be
- * anywhere between 0 and 20 bytes in size - normally it will be 0..3 to
- * get word alignment, but if a block is allocated into a free space which
- * is not big enough for that block plus a new free block, we'll swallow
- * the whole freespace into the newly allocated block to ensure the continued
- * viability of the heap. Note also that during some operations, chunks
- * may temporarily exist with P fields of any size at all.
- */
-
- #include <stdlib.h>
- #include <string.h>
-
- #include "kernel.h"
-
- #define __dl_mem_c
- #include "MemDefs.h"
-
- #include "DeskLib:WimpSWIs.h"
-
-
- /* --- External interface - global variables ------------------------------- */
- int mem_autocompact = mem_FASTCOMPACT; /* Enable/disable auto-compaction */
-
-
- /* --- Internal data ------------------------------------------------------- */
- mem_header *mem__lastchunk = NULL;
- int mem__heap = -1; /* Base address of heap (malloc high water) */
- int mem__heapsize = -1; /* Size of the heap */
- BOOL mem__iscompact = TRUE; /* The heap does not need compacting */
-
-
- /* --- Internal Function definitions --------------------------------------- */
-
- extern int Mem__HeapSize(int heapsize)
- /* Reads/Writes the heap size value
- * pass in heapsize == -1 to just read the current value
- */
- {
- int nextslot = -1;
- int freepool = -1;
- int slot = heapsize;
-
- if (mem__heap == -1) /* Remember initial slot size */
- {
- Wimp_SlotSize(&mem__heap, &nextslot, &freepool);
- mem__heap = WORDALIGN(mem__heap);
-
- mem__heap += 0x8000; /* Add application base address to get heap base */
- }
-
- if (slot >= 0) slot += mem__heap; /* Work out new wimp slot size */
- Wimp_SlotSize(&slot, &nextslot, &freepool);
-
- return(slot - mem__heap); /* Return new heap size */
- }
-
-
-
- extern void Mem__ReduceSlot(void)
- /* Attempts to shrink the heap by returning all free memory in the lastchunk
- * back to the Wimp.
- */
- {
- if (mem__lastchunk->realsize > sizeof(mem_header) + 16)
- {
- mem__heapsize = Mem__HeapSize((int) mem__lastchunk +
- sizeof(mem_header) + 16 - mem__heap);
- mem__lastchunk->realsize = mem__heap + mem__heapsize - (int) mem__lastchunk;
- }
- }
-
-
-
- extern mem_header *Mem__NextChunk(mem_header *chunk)
- /* Returns a pointer to the next chunk in the heap. Note that if
- * 'chunk' is the last chunk in the heap, it will return 'chunk'.
- */
- {
- if (chunk == mem__lastchunk)
- return(chunk);
-
- return((mem_header *) ((int) chunk + chunk->realsize));
- }
-
-
- extern mem_header *Mem__PrevChunk(mem_header *chunk)
- /* Returns a pointer to the previous chunk in the heap. Note that if
- * 'chunk' is the first chunk in the heap, it will return 'chunk'.
- */
- {
- return((mem_header *) ((int) chunk - chunk->prevrealsize));
- }
-
-
- extern BOOL Mem__FindChunk(mem_anchor *anchor, mem_header **chunk)
- /* Checks that the header is OK by comparing the handle to the
- * (supposedly identical) handle stored in the chunk header.
- * Returns the pointer to the chunk header, and TRUE if it is OK.
- */
- {
- if (*anchor == NULL) return(FALSE);
-
- *chunk = (mem_header *) (((int) *anchor) - sizeof(mem_header));
- return((*chunk)->handle == anchor);
- }
-
-
-
- static void Mem__Amalgamate(mem_header **chunk)
- /* Tries to merge this free chunk with the previous chunk, if it's free.
- */
- {
- mem_header *this, *prev, *next;
-
- this = *chunk;
- if (!ISFREE(this) || (int) this <= mem__heap) return;
-
- prev = Mem__PrevChunk(this);
- if (prev != this && ISFREE(prev)) /* Amalgamate free chunks */
- {
- prev->realsize += this->realsize; /* Extend prev chunk */
- *chunk = prev; /* Return new free chunk */
-
- if (this == mem__lastchunk) /* Update lastchunk ptr */
- mem__lastchunk = prev;
- else
- { /* Fix backward 'link' */
- next = Mem__NextChunk(prev);
- next->prevrealsize = prev->realsize;
- }
- }
- }
-
-
- extern void Mem__SplitOffFreeChunk(mem_header *chunk)
- /* Checks if it is possible to split off enough memory from the end of this
- * chunk to create a new free chunk - if there is, it does the split.
- */
- {
- if (ISFREE(chunk)) return;
-
- mem__iscompact = FALSE; /* Compaction is probably necessary now */
- if (chunk->realsize - chunk->datasize >= sizeof(mem_header) + 16)
- {
- mem_header *free;
- int chunksize = chunk->realsize;
-
- /* There is enough wastage at the end of the chunk to create a new free
- * chunk of 16 or more bytes dataspace at the end of it
- */
-
- free = (mem_header *) WORDALIGN( ((int) chunk) +
- sizeof(mem_header) + chunk->datasize);
-
- chunk->realsize = (int) free - (int) chunk;
- free->realsize = (((int) chunk) + chunksize) - (int) free;
- free->prevrealsize = chunk->realsize;
- free->datasize = 0;
- free->handle = NULL;
-
- if (mem__lastchunk == chunk)
- mem__lastchunk = free;
- else
- {
- chunk = Mem__NextChunk(free);
- chunk->prevrealsize = free->realsize;
-
- Mem__Amalgamate(&chunk); /* Ensure not 2 free chunks in a row */
- }
- }
-
- if (mem_autocompact == mem_FULLCOMPACT)
- Mem_Compact();
- }
-
-
-
- /* --- External Function definitions --------------------------------------- */
-
- extern BOOL Mem_Alloc(mem_anchor *anchor, int numbytes)
- /* Allocates 'numbytes' of memory, placing a pointer to this data in
- * the given anchor. The memory returned always starts at a word-aligned
- * address, though need not be an integral number of words long.
- *
- * Note that this implementation provides a 'best fit' strategy, that is
- * it searches for the free chunk of the *closest* size to that needed
- * which can be slow if there are a lot of free chunks. However, this
- * is still pretty quick, especially if you call Mem_Compact reasonably
- * often to ensure the heap is kept compact.
- *
- * Alternative strategies that you might prefer to implement for yourself
- * are:
- * + First fit: Find the first large enough free space
- * Faster than best fit, but generally less effective.
- *
- * + Grab: Just alloc at the end of the heap, and rely on (compacts
- * helping to keep) enough free memory in the Wimp Pool
- * to always be able to satisfy memory requests.
- * (This is very fast, and minimises the code in this function)
- * [RECOMPILE with FASTALLOC defined for this scheme]
- */
- {
- mem_header *chunk, *bestfit = NULL;
-
- *anchor = NULL;
- if (numbytes <= 0) return(FALSE);
-
- numbytes += sizeof(mem_header);
-
- #ifndef FASTALLOC
- /*
- * Find the best fit free-chunk in the heap. (i.e. search for the smallest
- * chunk which is >= numbytes)
- */
- chunk = (mem_header *) mem__heap;
-
- while (chunk != mem__lastchunk)
- {
- if (ISFREE(chunk) && chunk->realsize >= numbytes)
- {
- if (bestfit == NULL || bestfit->realsize > chunk->realsize)
- {
- bestfit = chunk;
-
- if (bestfit->realsize == numbytes) /* Don't bother continuing */
- break;
- }
- }
- chunk = Mem__NextChunk(chunk);
- }
-
- if (bestfit == NULL) /* No large enough chunks - must extend the heap */
- #endif
-
- {
- int needed = numbytes;
-
- if (ISFREE(mem__lastchunk))
- needed -= mem__lastchunk->realsize;
-
- if (needed > 0)
- mem__heapsize = Mem__HeapSize(mem__heapsize + needed + 16);
-
- mem__lastchunk->realsize = mem__heap + mem__heapsize - (int) mem__lastchunk;
-
- Mem__SplitOffFreeChunk(mem__lastchunk);
- if (mem__lastchunk->realsize >= numbytes)
- bestfit = mem__lastchunk;
- }
-
- if (bestfit == NULL) /* Unable to allocate the memory */
- return(FALSE);
-
- *anchor = (void *) ((int) bestfit + sizeof(mem_header));
- bestfit->handle = anchor;
- bestfit->datasize = numbytes - sizeof(mem_header);
-
- Mem__SplitOffFreeChunk(bestfit); /* Return any free space for use */
-
- return(TRUE);
- }
-
-
- extern void Mem_Free(mem_anchor *anchor)
- {
- mem_header *chunk;
-
- if (Mem__FindChunk(anchor, &chunk))
- {
- chunk->datasize = 0; /* Mark this chunk free */
- *anchor = NULL; /* Kill the user's anchor */
-
- Mem__Amalgamate(&chunk);
- if (chunk != mem__lastchunk)
- {
- chunk = Mem__NextChunk(chunk);
- Mem__Amalgamate(&chunk);
- }
- }
-
- if (mem_autocompact == mem_FULLCOMPACT)
- Mem_Compact();
- Mem__ReduceSlot();
- }
-
-
- #pragma no_check_stack
- extern int Mem_DontBudge(int n, void **a)
- /* Function to register with _kernel_register_slotextend to refuse to allow
- * SharedCLib to allocate more memory. (identical to Flex's flex_dontbudge)
- * Don't call this function - pass to _k*_r*_slotextend instead. Note that
- * Mem_Initialise does this for you automatically anyway, so unless you have
- * a Mem_Budge function, you shouldn't have to worry about this at all.
- */
- {
- return(0); /* number of bytes allocated */
- }
- #pragma check_stack
-
-
- extern BOOL Mem_Initialise(void)
- /* Initialises the Mem memory manager
- * Returns FALSE if it fails - if this occurs, Mem will not operate and
- * you should not try calling any Mem FNs. Basically you're stuffed.
- * (Generally you should report the "Insufficient memory or not within
- * *desktop world" error message and exit)
- */
- {
- _kernel_register_slotextend(Mem_DontBudge);
-
- if (mem__heap == -1) /* This will be > 0 if we have already initialised */
- {
- mem__heapsize = Mem__HeapSize(sizeof(mem_header) + 16);
- if (mem__heapsize < sizeof(mem_header))
- return(FALSE);
-
- mem__lastchunk = (mem_header *) mem__heap;
- mem__lastchunk->realsize = mem__heapsize;
- mem__lastchunk->datasize = 0;
- mem__lastchunk->prevrealsize = 0;
- mem__lastchunk->handle = NULL;
- }
-
- return(TRUE);
- }
-