--- /dev/null
+#include <efi.h>
+#include <efiapi.h>
+#include <HashVector.h>
+
+#define LARGE_HEAP_SIZE (1048576)
+
+#define CEILX(V,D) ((int)(((V)+(D)-1)/(D)))
+#define CEIL(V,D) (CEILX(V,D)*(D))
+
+extern EFI_BOOT_SERVICES *boot_services;
+
+static void *real_malloc(size_t size) {
+ size_t pages = CEILX( size, EFI_PAGE_SIZE );
+ EFI_PHYSICAL_ADDRESS p = 0;
+ if ( boot_services->AllocatePages(
+ AllocateAnyPages, EfiLoaderData, pages, &p ) != EFI_SUCCESS ) {
+ return 0;
+ }
+ return (void*) p;
+}
+
+static void real_free(void *p,size_t size) {
+ size_t pages = CEILX( size, EFI_PAGE_SIZE );
+ (void) boot_services->FreePages( (EFI_PHYSICAL_ADDRESS) p, pages );
+}
+
+static void memclear(char *p,size_t size) {
+ char *end = p + size;
+ while ( p < end ) {
+ *(p++) = 0;
+ }
+}
+
+typedef struct ChunkHead {
+ size_t size;
+ struct ChunkHead *data;
+} ChunkHead;
+
+static ChunkHead *free_chunks;
+
+void* malloc(size_t size) {
+ // Adjust allocation size to include a preceding size count
+ size_t asize = CEIL( size + sizeof( size_t ), sizeof( ChunkHead ) );
+ if ( asize >= LARGE_HEAP_SIZE ) {
+ // Managed without subdivision
+ ChunkHead *p = real_malloc( asize );
+ p->size = asize;
+ return (void*) &p->data;
+ }
+ // Cut out a chunk by subdivision adjusted to an even number of
+ // ChunkHead;
+ ChunkHead *this = free_chunks;
+ ChunkHead *prev = 0;
+ for ( ; this ; prev = this, this = this->data ) {
+ if ( this->size < asize ) {
+ continue;
+ }
+ // Cut out an allocation chunk here,
+ size_t rest = this->size - asize;
+ ChunkHead *next = this->data;
+ if ( rest >= sizeof( ChunkHead ) ) {
+ next = (ChunkHead *) (((char*)this) + asize);
+ next->data = this->data;
+ next->size = rest;
+ }
+ if ( prev ) {
+ prev->data = next;
+ } else {
+ free_chunks = next;
+ }
+ memclear( (char*) this, sizeof( ChunkHead ) );
+ this->size = asize;
+ return (void*) ((char*)this) + sizeof( size_t );
+ }
+ // No subdivision available; add a new subdivision page and try
+ // again. Note that this contains its own ChunkHead.
+ this = (ChunkHead *) real_malloc( LARGE_HEAP_SIZE );
+ memclear( (char*) this, LARGE_HEAP_SIZE );
+ this->size = LARGE_HEAP_SIZE;
+ this->data = free_chunks;
+ free_chunks = this;
+ return malloc( size );
+}
+
+void free(void *p) {
+ if ( p == 0 ) {
+ return;
+ }
+ ChunkHead *chp = (ChunkHead *) ( ((char*) p) - sizeof( size_t ) );
+ if ( chp->size >= LARGE_HEAP_SIZE ) {
+ real_free( p, chp->size );
+ return;
+ }
+ // find nearest ChunkHead in the free_chunks list.
+ ChunkHead *this = free_chunks;
+ ChunkHead *prev = 0;
+ for ( ; this; prev = this, this = this->data ) {
+ if ( (void*) this >= p ) {
+ break;
+ }
+ }
+ this = chp;
+ if ( prev == 0 ) {
+ this->data = free_chunks;
+ free_chunks = this;
+ } else {
+ this->data = prev->data;
+ }
+ if ( ((char*)prev->data) + prev->size == (char*)this ) {
+ // collapse into single area
+ prev->size += this->size;
+ this = prev;
+ }
+ // Maybe this emptied a subdivision block, except the last?
+ if ( this->data && ((char*)this) + this->size == (char*)(this->data) ) {
+ this->size += this->data->size;
+ }
+ // Yes. That should also be freed
+ if ( this->size == LARGE_HEAP_SIZE || this->data ) {
+ this = free_chunks;
+ prev = 0;
+ for ( ; this; prev = this, this = this->data ) {
+ if ( this == chp ) {
+ if ( prev ) {
+ prev->data = this->data;
+ } else {
+ free_chunks = this->data;
+ }
+ real_free( this, this->size );
+ return;
+ }
+ }
+ }
+}