Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449#include "internal.h"#include "gc.h"
#include <stddef.h>#include <stdlib.h>#include <string.h>
static const size_t g_size_classes[ANT_POOL_SIZE_CLASS_COUNT] = { 16u, 24u, 32u, 40u, 48u, 56u, 64u, 80u, 96u, 112u, 128u, 160u, 192u, 224u, 256u, 320u, 384u, 448u, 512u, 640u, 768u, 896u, 1024u, 1280u, 1536u, 1792u, 2048u, 2560u, 3072u, 4096u, 8192u, 16384u};
static inline size_t align_up_size(size_t value, size_t align) { if (align == 0) return value; size_t mask = align - 1; return (value + mask) & ~mask;}
static inline int pool_size_class_index(size_t need) { for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { if (need <= g_size_classes[i]) return i; } return -1;}
static inline size_t pool_class_block_items(size_t class_size) { if (class_size <= 256u) return 256u; if (class_size <= 1024u) return 128u; if (class_size <= 4096u) return 64u; return 32u;}
static inline size_t pool_chain_block_cap(size_t block_size, size_t need) { if (block_size == 0 || block_size >= need) return block_size ? block_size : need; size_t blocks = (need + block_size - 1) / block_size; return blocks * block_size;}
static inline size_t pool_default_block_size(ant_alloc_kind_t kind) {switch (kind) { case ANT_ALLOC_SYMBOL: return ANT_POOL_SYMBOL_BLOCK_SIZE; case ANT_ALLOC_BIGINT: return ANT_POOL_BIGINT_BLOCK_SIZE; case ANT_ALLOC_STRING: return ANT_POOL_STRING_BLOCK_SIZE; case ANT_ALLOC_ROPE: default: return ANT_POOL_ROPE_BLOCK_SIZE;}}
static inline ant_pool_t *pool_for_kind(ant_t *js, ant_alloc_kind_t kind) {switch (kind) { case ANT_ALLOC_SYMBOL: return &js->pool.symbol; case ANT_ALLOC_BIGINT: return &js->pool.bigint.base; case ANT_ALLOC_ROPE: default: return &js->pool.rope;}}
static inline ant_class_pool_t *class_pool_for_kind(ant_t *js, ant_alloc_kind_t kind) {switch (kind) { case ANT_ALLOC_BIGINT: return &js->pool.bigint; default: return NULL;}}
static void pool_block_list_destroy(ant_pool_block_t *head) {while (head) { ant_pool_block_t *next = head->next; pool_block_free(head); head = next;}}
static void pool_free_block_list_destroy(ant_pool_block_t *head) {while (head) { ant_pool_block_t *next = pool_free_next(head); pool_block_free(head); head = next;}}
static ant_pool_stats_t pool_block_list_stats(ant_pool_block_t *head) { ant_pool_stats_t s = {0}; for (ant_pool_block_t *b = head; b; b = b->next) { s.used += b->used; s.capacity += b->cap; s.blocks++; } return s;}
static inline void pool_stats_add(ant_pool_stats_t *dst, ant_pool_stats_t src) { dst->used += src.used; dst->capacity += src.capacity; dst->blocks += src.blocks;}
static inline size_t pool_page_size(void) { static size_t cached = 0; if (cached) return cached;#ifdef _WIN32 SYSTEM_INFO info; GetSystemInfo(&info); cached = info.dwPageSize ? (size_t)info.dwPageSize : 4096u;#else long page = sysconf(_SC_PAGESIZE); cached = page > 0 ? (size_t)page : 4096u;#endif return cached;}
static inline void large_string_unlink(ant_large_string_alloc_t **head, ant_large_string_alloc_t *alloc) { if (!head || !alloc) return; if (alloc->prev) alloc->prev->next = alloc->next; else *head = alloc->next; if (alloc->next) alloc->next->prev = alloc->prev; alloc->next = NULL; alloc->prev = NULL;}
static inline void large_string_push_front(ant_large_string_alloc_t **head, ant_large_string_alloc_t *alloc) { if (!head || !alloc) return; alloc->prev = NULL; alloc->next = *head; if (*head) (*head)->prev = alloc; *head = alloc;}
static inline size_t flat_payload_capacity_from_size(size_t flat_size) { if (flat_size <= offsetof(ant_flat_string_t, bytes)) return 0; return flat_size - offsetof(ant_flat_string_t, bytes) - 1u;}
static ant_large_string_alloc_t *large_string_alloc_fresh(size_t flat_size) { size_t payload_capacity = flat_payload_capacity_from_size(flat_size); size_t needed = offsetof(ant_large_string_alloc_t, bytes) + payload_capacity + 1u; size_t alloc_size = align_up_size(needed, pool_page_size()); ant_large_string_alloc_t *alloc = (ant_large_string_alloc_t *)ant_os_alloc(alloc_size); if (!alloc) return NULL;
alloc->next = NULL; alloc->prev = NULL; alloc->capacity = alloc_size - offsetof(ant_large_string_alloc_t, bytes) - 1u; alloc->alloc_size = alloc_size; alloc->quarantine_epoch = 0; alloc->marked = 0; alloc->len = 0; alloc->is_ascii = STR_ASCII_UNKNOWN; alloc->bytes[0] = '\0'; return alloc;}
static ant_large_string_alloc_t *large_string_reuse_candidate(ant_large_string_space_t *space, size_t flat_size) { if (!space) return NULL;
size_t needed_capacity = flat_payload_capacity_from_size(flat_size); size_t max_capacity = needed_capacity > (SIZE_MAX / 2u) ? SIZE_MAX : needed_capacity * 2u;
for (ant_large_string_alloc_t *cur = space->reusable; cur; cur = cur->next) if (cur->capacity >= needed_capacity && cur->capacity <= max_capacity) return cur; return NULL;}
static ant_flat_string_t *string_large_space_alloc(ant_t *js, size_t flat_size) { ant_large_string_space_t *space = &js->pool.string.large; ant_large_string_alloc_t *alloc = large_string_reuse_candidate(space, flat_size);
if (alloc) large_string_unlink(&space->reusable, alloc); else { alloc = large_string_alloc_fresh(flat_size); if (!alloc) return NULL; }
alloc->quarantine_epoch = 0; alloc->marked = 0; alloc->len = 0; alloc->is_ascii = STR_ASCII_UNKNOWN; alloc->bytes[0] = '\0'; large_string_push_front(&space->live, alloc);
return large_string_flat_ptr(alloc);}
static void *pool_bucket_alloc_fast(ant_pool_bucket_t *bucket, size_t class_size, size_t align) { if (!bucket) return NULL; const size_t cache_align = _Alignof(max_align_t); if (align > cache_align) return NULL; if (bucket->slot_stride == 0) bucket->slot_stride = align_up_size(class_size, cache_align);
if (bucket->slot_free) { void *ptr = bucket->slot_free; void *next; memcpy(&next, ptr, sizeof(void *)); bucket->slot_free = next; return ptr; }
if (!bucket->current || !bucket->cursor || bucket->cursor + bucket->slot_stride > bucket->end) { if (bucket->free_head) { ant_pool_block_t *reuse = bucket->free_head; bucket->free_head = pool_free_next(reuse); reuse->used = 0; reuse->prev = NULL; reuse->next = bucket->head; if (bucket->head) bucket->head->prev = reuse; bucket->head = reuse; bucket->current = reuse; bucket->cursor = reuse->data; bucket->end = reuse->data + reuse->cap; } else { size_t cap = bucket->block_size; if (cap < bucket->slot_stride) cap = bucket->slot_stride; ant_pool_block_t *block = pool_block_alloc(cap); if (!block) return NULL; block->next = bucket->head; if (bucket->head) bucket->head->prev = block; bucket->head = block; bucket->current = block; bucket->cursor = block->data; bucket->end = block->data + cap; }}
uint8_t *ptr = bucket->cursor; bucket->cursor += bucket->slot_stride; if (bucket->current) bucket->current->used = (size_t)(bucket->cursor - bucket->current->data); return ptr;}
static void *string_pool_alloc(ant_t *js, size_t size, size_t align) { ant_string_pool_t *pool = &js->pool.string; if (pool->block_size == 0) pool->block_size = pool_default_block_size(ANT_ALLOC_STRING);
size_t needed = size + (align - 1u); int class_idx = pool_size_class_index(needed); if (class_idx >= 0) { size_t class_size = g_size_classes[class_idx]; ant_pool_bucket_t *bucket = &pool->classes[class_idx]; if (bucket->block_size == 0) { size_t bucket_block_size = pool->block_size; size_t min_block = class_size * pool_class_block_items(class_size); if (bucket_block_size < min_block) bucket_block_size = min_block; bucket->block_size = bucket_block_size; } void *fast = pool_bucket_alloc_fast(bucket, class_size, align); if (fast) return fast; return pool_alloc_chain(&bucket->head, &bucket->free_head, bucket->block_size, size, align); }
return string_large_space_alloc(js, size);}
void *pool_alloc_chain( ant_pool_block_t **head, ant_pool_block_t **free_head, size_t block_size, size_t size, size_t align) { if (!head || size == 0) return NULL; if (align == 0) align = sizeof(void *);
ant_pool_block_t *block = *head; if (!block) { size_t cap = pool_chain_block_cap(block_size, size + align); block = pool_block_alloc(cap); if (!block) return NULL; *head = block; }
uintptr_t base = (uintptr_t)block->data; size_t start = align_up_size(block->used, align); if (start + size > block->cap) { if (free_head && *free_head) { block = *free_head; *free_head = pool_free_next(block); block->used = 0; block->prev = NULL; block->next = *head; if (*head) (*head)->prev = block; *head = block; } else { size_t cap = pool_chain_block_cap(block_size, size + align); ant_pool_block_t *next = pool_block_alloc(cap); if (!next) return NULL; next->next = block; if (block) block->prev = next; *head = next; block = next; } base = (uintptr_t)block->data; start = align_up_size(block->used, align); }
void *ptr = (void *)(base + start); block->used = start + size; return ptr;}
void *js_type_alloc(ant_t *js, ant_alloc_kind_t kind, size_t size, size_t align) { if (!js || size == 0) return NULL; if (align == 0) align = sizeof(void *);
js->gc_pool_alloc += size; size_t pool_threshold = gc_pool_major_threshold(js); if (js->gc_pool_alloc >= pool_threshold) gc_run(js); if (kind == ANT_ALLOC_STRING) return string_pool_alloc(js, size, align);
ant_pool_t *pool = pool_for_kind(js, kind); if (pool->block_size == 0) pool->block_size = pool_default_block_size(kind); ant_class_pool_t *class_pool = class_pool_for_kind(js, kind); if (class_pool) { size_t needed = size + (align - 1u); int class_idx = pool_size_class_index(needed); if (class_idx >= 0) { size_t class_size = g_size_classes[class_idx]; ant_pool_bucket_t *bucket = &class_pool->classes[class_idx]; if (bucket->block_size == 0) { size_t bucket_block_size = pool->block_size; size_t min_block = class_size * pool_class_block_items(class_size); if (bucket_block_size < min_block) bucket_block_size = min_block; bucket->block_size = bucket_block_size; } void *fast = pool_bucket_alloc_fast(bucket, class_size, align); if (fast) return fast; return pool_alloc_chain(&bucket->head, &bucket->free_head, bucket->block_size, size, align); }}
return pool_alloc_chain(&pool->head, &pool->free_head, pool->block_size, size, align);}
void js_pool_destroy(ant_pool_t *pool) { if (!pool) return; pool_block_list_destroy(pool->head); pool_free_block_list_destroy(pool->free_head); pool->head = NULL; pool->free_head = NULL; pool->block_size = 0;}
ant_pool_stats_t js_pool_stats(ant_pool_t *pool) { if (!pool) return (ant_pool_stats_t){0}; return pool_block_list_stats(pool->head);}
ant_pool_stats_t js_class_pool_stats(ant_class_pool_t *pool) { ant_pool_stats_t s = {0}; if (!pool) return s;
for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) pool_stats_add(&s, pool_block_list_stats(pool->classes[i].head)); pool_stats_add(&s, js_pool_stats(&pool->base)); return s;}
static ant_pool_stats_t string_large_stats(ant_large_string_alloc_t *head, bool include_used) { ant_pool_stats_t s = {0}; for (ant_large_string_alloc_t *cur = head; cur; cur = cur->next) { if (include_used) s.used += offsetof(ant_flat_string_t, bytes) + (size_t)cur->len + 1u; s.capacity += cur->alloc_size; s.blocks++; } return s;}
ant_string_pool_stats_t js_string_pool_stats(ant_string_pool_t *pool) { ant_string_pool_stats_t s = {0}; if (!pool) return s;
for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) pool_stats_add(&s.pooled, pool_block_list_stats(pool->classes[i].head));
s.large_live = string_large_stats(pool->large.live, true); s.large_reusable = string_large_stats(pool->large.reusable, false); s.large_quarantine = string_large_stats(pool->large.quarantine, false);
s.total = s.pooled; pool_stats_add(&s.total, s.large_live); s.total.capacity += s.large_reusable.capacity + s.large_quarantine.capacity; s.total.blocks += s.large_reusable.blocks + s.large_quarantine.blocks;
return s;}
void js_class_pool_destroy(ant_class_pool_t *pool) { if (!pool) return;
for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { pool_block_list_destroy(pool->classes[i].head); pool_free_block_list_destroy(pool->classes[i].free_head); pool->classes[i].head = NULL; pool->classes[i].current = NULL; pool->classes[i].free_head = NULL; pool->classes[i].slot_free = NULL; pool->classes[i].block_size = 0; pool->classes[i].cursor = NULL; pool->classes[i].end = NULL; pool->classes[i].slot_stride = 0; }
js_pool_destroy(&pool->base);}
void js_string_pool_destroy(ant_string_pool_t *pool) { if (!pool) return;
for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { pool_block_list_destroy(pool->classes[i].head); pool_free_block_list_destroy(pool->classes[i].free_head); pool->classes[i].head = NULL; pool->classes[i].current = NULL; pool->classes[i].free_head = NULL; pool->classes[i].slot_free = NULL; pool->classes[i].block_size = 0; pool->classes[i].cursor = NULL; pool->classes[i].end = NULL; pool->classes[i].slot_stride = 0; }
ant_large_string_alloc_t *lists[] = { pool->large.live, pool->large.reusable, pool->large.quarantine, };
for (size_t i = 0; i < sizeof(lists) / sizeof(lists[0]); i++) { ant_large_string_alloc_t *cur = lists[i]; while (cur) { ant_large_string_alloc_t *next = cur->next; ant_arena_free(cur, cur->alloc_size); cur = next; }}
pool->large.live = NULL; pool->large.reusable = NULL; pool->large.quarantine = NULL; pool->large.gc_epoch = 0; pool->block_size = 0;}