/* fastheap.c */ /* Copyright 1997 by Eberhard Mattes Donated to the public domain. No warranty. 1997-07-22 Initial version */ #include #include #include #include "libemfw.h" #include "fastheap.h" extern void *xmalloc (size_t); typedef struct fastheap_header header; /* Initialize a fast heap. Allocate at least CHUNK_SIZE bytes (including the header) for automatically added chunks. */ void fastheap_init (fastheap *heap, size_t chunk_size) { DEBUG_ASSERT (heap != NULL); heap->first = heap->cur = NULL; heap->add = &heap->first; heap->chunk_size = chunk_size; heap->cur_avail = 0; heap->cur_ptr = NULL; } /* Add a chunk to a fast heap. SIZE includes the header. In a future version, we might want to record whether the chunk is allocated statically or dynamically. */ static void add_chunk (fastheap *heap, void *buffer, size_t size) { header *chunk = (header *)buffer; chunk->next = NULL; DEBUG_ASSERT (size >= sizeof (header)); chunk->size = size - sizeof (header); DEBUG_ASSERT (heap->add != NULL); *heap->add = chunk; heap->add = &chunk->next; } /* Add a static chunk to a fast heap. The buffer pointed to by BUFFER must be properly aligned for `header'. Hint: use a union to force the buffer to be aligned properly. */ void fastheap_add_static (fastheap *heap, void *buffer, size_t size) { /* A chunk cannot be smaller than its header. Moreover, there's no point in adding small static chunks. */ DEBUG_ASSERT (heap != NULL); DEBUG_ASSERT (buffer != NULL); if (size >= 2 * sizeof (header)) add_chunk (heap, buffer, size); } /* Reset a fast heap. Memory allocation will restart from the first chunk. Note that chunks won't be deallocated, the heap never shrinks! */ void fastheap_reset (fastheap *heap) { DEBUG_ASSERT (heap != NULL); heap->cur = NULL; heap->cur_avail = 0; heap->cur_ptr = NULL; } /* Select the chunk pointed to by CHUNK as `current chunk'. It is assumed that the `next' and `size' members of the chunk header are correctly initialized, that is, that the chunk was added by add_chunk(). */ static void select_chunk (fastheap *heap, header *chunk) { DEBUG_ASSERT (heap != NULL); DEBUG_ASSERT (chunk != NULL); heap->cur = chunk; heap->cur_ptr = (char *)chunk + sizeof (header); heap->cur_avail = chunk->size; } /* And now for the heart of the fast heap implementation. The fastheap_alloc() macro calls this helper function if there's not enough space in the current chunk (or if there is no current chunk). */ void *fastheap_alloc1 (fastheap *heap, size_t size) { void *p; size_t chunk_size; header *chunk; /* First, just try again to allocate from the current chunk, in case someone calls this function directly. */ DEBUG_ASSERT (heap != NULL); DEBUG_ASSERT (heap->cur == NULL || heap->cur_avail <= heap->cur->size); if (size < heap->cur_avail) goto simple; /* If there's no current chunk (this happens after initializing or resetting), try to use the first chunk if there is any. */ if (heap->cur == NULL && heap->first != NULL) { select_chunk (heap, heap->first); if (size < heap->cur_avail) goto simple; } /* Move on to the next chunk if there is any. */ if (heap->cur != NULL && heap->cur->next != NULL) { select_chunk (heap, heap->cur->next); if (size < heap->cur_avail) goto simple; } /* We need a new chunk. Allocate and add one. */ DEBUG_ASSERT (size < UINT_MAX - sizeof (header)); /* Assume size_t >= int */ chunk_size = size + sizeof (header); if (chunk_size < heap->chunk_size) chunk_size = heap->chunk_size; chunk = (header *)xmalloc (chunk_size); DEBUG_ASSERT (chunk != NULL); add_chunk (heap, chunk, chunk_size); select_chunk (heap, chunk); /* There is guaranteed to be enough space in the current chunk. */ simple: DEBUG_ASSERT (heap->cur_avail >= size); DEBUG_ASSERT (heap->cur != NULL); DEBUG_ASSERT (heap->cur_avail <= heap->cur->size); DEBUG_ASSERT (heap->cur_ptr + heap->cur_avail == (char *)heap->cur + sizeof (header) + heap->cur->size); heap->cur_avail -= size; p = heap->cur_ptr; heap->cur_ptr += size; return p; } /* Create on the fastheap pointed to by HEAP a duplicate of the array of N characters pointed to by S. */ void *fastheap_memdup (fastheap *heap, const void *s, size_t n) { void *p; DEBUG_ASSERT (heap != NULL); DEBUG_ASSERT (s != NULL); p = fastheap_alloc (heap, n); DEBUG_ASSERT (p != NULL); memcpy (p, s, n); return p; } /* Create on the fastheap pointed to by HEAP a duplicate of the array of N characters pointed to by S. The duplicate will be null-terminated. */ char *fastheap_strndup (fastheap *heap, const char *s, size_t n) { char *p; DEBUG_ASSERT (heap != NULL); DEBUG_ASSERT (s != NULL); p = fastheap_alloc (heap, n + 1); DEBUG_ASSERT (p != NULL); memcpy (p, s, n); p[n] = 0; return p; } #ifndef NDEBUG /* This function is defined only if DEBUG_ASSERT() is not disabled. If NDEBUG is defined, there's no point in this function as DEBUG_ASSERT() would be a no-op. This happens when compiling fastheap.c with NDEBUG undefined and the application with NDEBUG defined. We want to get a linker error in that case. */ void fastheap_alloc_assert (fastheap *heap) { DEBUG_ASSERT (heap != NULL); if (heap->cur == NULL) DEBUG_ASSERT (heap->cur_avail == 0); else { DEBUG_ASSERT (heap->cur_avail <= heap->cur->size); DEBUG_ASSERT (heap->cur_ptr + heap->cur_avail == (char *)heap->cur + sizeof (header) + heap->cur->size); } } #endif