mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
186 lines
4.6 KiB
C++
186 lines
4.6 KiB
C++
// Copyright 2024 Justine Alexandra Roberts Tunney
|
|
//
|
|
// Permission to use, copy, modify, and/or distribute this software for
|
|
// any purpose with or without fee is hereby granted, provided that the
|
|
// above copyright notice and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
#include "libc/assert.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/stdalign.h"
|
|
#include "libc/stdckdint.h"
|
|
#include "libc/str/str.h"
|
|
|
|
#ifndef TINYMALLOC_MAX_BYTES
|
|
#define TINYMALLOC_MAX_BYTES 1073741824
|
|
#endif
|
|
|
|
#ifndef TINYMALLOC_MAX_ALIGN
|
|
#define TINYMALLOC_MAX_ALIGN sizeof(max_align_t)
|
|
#endif
|
|
|
|
#pragma GCC push_options
|
|
#pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
|
|
#pragma GCC diagnostic ignored "-Wanalyzer-use-after-free"
|
|
|
|
static struct {
|
|
alignas(max_align_t) char bits[TINYMALLOC_MAX_BYTES];
|
|
char *memory;
|
|
int once;
|
|
size_t size, used, last, free;
|
|
} heap;
|
|
|
|
static void tinymalloc_init(void) {
|
|
int align;
|
|
if (heap.once)
|
|
return;
|
|
align = TINYMALLOC_MAX_ALIGN;
|
|
heap.memory = (char *)(((uintptr_t)heap.bits + align - 1) & -align);
|
|
heap.size = sizeof(heap.bits) - (heap.memory - heap.bits);
|
|
heap.once = 1;
|
|
}
|
|
|
|
static inline int isheap(char *mem) {
|
|
return heap.memory <= mem && mem < heap.memory + heap.used;
|
|
}
|
|
|
|
void free(void *ptr) {
|
|
char *mem;
|
|
size_t base;
|
|
if (ptr) {
|
|
mem = (char *)ptr;
|
|
unassert(isheap(mem));
|
|
base = mem - heap.memory;
|
|
*(size_t *)mem = heap.free;
|
|
heap.free = base;
|
|
}
|
|
}
|
|
|
|
size_t malloc_usable_size(void *ptr) {
|
|
char *mem = (char *)ptr;
|
|
unassert(isheap(mem));
|
|
return ((size_t *)mem)[-1];
|
|
}
|
|
|
|
void *memalign(size_t align, size_t need) {
|
|
char *res;
|
|
size_t next, next2, base, toto, *link, *link2;
|
|
tinymalloc_init();
|
|
|
|
// normalize arguments
|
|
while (align & (align - 1))
|
|
++align;
|
|
if (need < sizeof(size_t))
|
|
need = sizeof(size_t);
|
|
if (align < sizeof(size_t))
|
|
align = sizeof(size_t);
|
|
if (align > TINYMALLOC_MAX_ALIGN)
|
|
goto InvalidArgument;
|
|
// TODO(jart): refactor append*() to not need size_t*2 granularity
|
|
if (ckd_add(&need, need, sizeof(size_t) * 2 - 1))
|
|
goto OutOfMemory;
|
|
need &= -sizeof(size_t);
|
|
|
|
// allocate from free list
|
|
next = heap.free;
|
|
link = &heap.free;
|
|
while (next) {
|
|
next2 = *(size_t *)(heap.memory + next);
|
|
link2 = (size_t *)(heap.memory + next);
|
|
if (need <= ((size_t *)(heap.memory + next))[-1]) {
|
|
*link = next2;
|
|
return (void *)(heap.memory + next);
|
|
}
|
|
next = next2;
|
|
link = link2;
|
|
}
|
|
|
|
// allocate new static memory
|
|
base = heap.used;
|
|
base += sizeof(size_t);
|
|
base += align - 1;
|
|
base &= -align;
|
|
if (ckd_add(&toto, base, need))
|
|
goto OutOfMemory;
|
|
if (toto > heap.size)
|
|
goto OutOfMemory;
|
|
res = heap.memory + base;
|
|
((size_t *)res)[-1] = need;
|
|
heap.used = toto;
|
|
heap.last = base;
|
|
return res;
|
|
|
|
// we require more vespene gas
|
|
OutOfMemory:
|
|
errno = ENOMEM;
|
|
return 0;
|
|
InvalidArgument:
|
|
errno = EINVAL;
|
|
return 0;
|
|
}
|
|
|
|
void *malloc(size_t need) {
|
|
return memalign(sizeof(max_align_t), need);
|
|
}
|
|
|
|
void *calloc(size_t count, size_t size) {
|
|
char *res;
|
|
size_t need, used;
|
|
if (ckd_mul(&need, count, size))
|
|
need = -1;
|
|
used = heap.used;
|
|
if ((res = (char *)malloc(need)))
|
|
if (res - heap.memory < used)
|
|
bzero(res, need);
|
|
return res;
|
|
}
|
|
|
|
void *realloc(void *ptr, size_t need) {
|
|
char *res, *mem;
|
|
size_t base, have, toto;
|
|
if (!ptr) {
|
|
res = (char *)malloc(need);
|
|
} else {
|
|
mem = (char *)ptr;
|
|
unassert(isheap(mem));
|
|
have = ((size_t *)mem)[-1];
|
|
base = mem - heap.memory;
|
|
if (need < have) {
|
|
res = mem;
|
|
} else if (base == heap.last) {
|
|
if (need < sizeof(size_t))
|
|
need = sizeof(size_t);
|
|
if (ckd_add(&need, need, sizeof(size_t) - 1))
|
|
goto OutOfMemory;
|
|
need &= -sizeof(size_t);
|
|
if (ckd_add(&toto, base, need))
|
|
goto OutOfMemory;
|
|
if (toto > heap.size)
|
|
goto OutOfMemory;
|
|
((size_t *)mem)[-1] = need;
|
|
heap.used = toto;
|
|
res = mem;
|
|
} else if ((res = (char *)malloc(need))) {
|
|
if (have > need)
|
|
have = need;
|
|
memcpy(res, mem, have);
|
|
free(mem);
|
|
}
|
|
}
|
|
return res;
|
|
OutOfMemory:
|
|
errno = ENOMEM;
|
|
return 0;
|
|
}
|
|
|
|
#pragma GCC pop_options
|