Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347#ifndef ANT_GC_STRINGS_H#define ANT_GC_STRINGS_H
#include "pool.h"#include "value.h"#include <stdbool.h>#include <string.h>
static constexpr int STR_SHORT_CONS_THRESHOLD = 32;static constexpr size_t STR_BUILDER_TAIL_CAP = 256;static constexpr double ANT_STRING_MAX_LENGTH = 536870888.0;
enum: uint64_t { STR_HEAP_TAG_MASK = 0x3, STR_HEAP_TAG_FLAT = 0x0, STR_HEAP_TAG_ROPE = 0x1, STR_HEAP_TAG_BUILDER = 0x2,};
enum { STR_ASCII_UNKNOWN = 0, STR_ASCII_YES = 1, STR_ASCII_NO = 2,};
enum { STR_UTF_UNKNOWN = 0, STR_UTF_VALID = 1, STR_UTF_INVALID = 2, STR_UTF_INVALID_SAME_LENGTH = 3,};
static constexpr uint64_t STR_META_ASCII_SHIFT = 56;static constexpr uint64_t STR_META_VALID_SHIFT = 58;static constexpr uint64_t STR_META_FIELD_MASK = 0x3;
static constexpr uint64_t STR_META_PERMANENT = UINT64_C(1) << 60;static constexpr uint64_t STR_META_UTF16_MASK = (UINT64_C(1) << STR_META_ASCII_SHIFT) - 1;
static constexpr uint64_t STR_META_ASCII_MASK = STR_META_FIELD_MASK << STR_META_ASCII_SHIFT;static constexpr uint64_t STR_META_VALID_MASK = STR_META_FIELD_MASK << STR_META_VALID_SHIFT;static constexpr uint64_t STR_META_STATE_MASK = ~STR_META_UTF16_MASK;static constexpr uint64_t STR_UTF16_LEN_UNKNOWN = STR_META_UTF16_MASK;
typedef struct { ant_offset_t len; uint64_t meta; char bytes[];} ant_flat_string_t;
static_assert( sizeof(ant_flat_string_t) == 16, "flat string header must remain 16 bytes");
static_assert( offsetof(ant_flat_string_t, bytes) == sizeof(ant_flat_string_t), "flat string bytes must follow packed metadata");
static_assert( offsetof(ant_large_string_alloc_t, meta) - offsetof(ant_large_string_alloc_t, len) == offsetof(ant_flat_string_t, meta), "large and pooled string metadata layouts must match");
static_assert( offsetof(ant_large_string_alloc_t, bytes) - offsetof(ant_large_string_alloc_t, len) == offsetof(ant_flat_string_t, bytes), "large and pooled string byte layouts must match");
typedef struct ant_builder_chunk { struct ant_builder_chunk *next; ant_value_t value;} ant_builder_chunk_t;
struct ant_string_builder { ant_offset_t len; ant_value_t snapshot; ant_builder_chunk_t *head; ant_builder_chunk_t *chunk_tail; ant_value_t cached; uint16_t tail_len; uint8_t ascii_state; uint8_t in_remember_set; char tail[STR_BUILDER_TAIL_CAP];};
typedef struct { const char *ptr; size_t len; bool needs_free;} js_cstr_t;
typedef struct { size_t count; size_t bytes;} js_intern_stats_t;
bool utf8_validate_bytes(const char *str, size_t byte_len);size_t utf8_export_length_slow(const char *str, size_t str_len);size_t wtf8_join_surrogate_pairs(char *bytes, size_t len);
static inline bool str_is_heap_rope(ant_value_t value) { return vtype(value) == kTypeString && ((vdata(value) & STR_HEAP_TAG_MASK) == STR_HEAP_TAG_ROPE);}
static inline bool str_is_heap_builder(ant_value_t value) { return vtype(value) == kTypeString && ((vdata(value) & STR_HEAP_TAG_MASK) == STR_HEAP_TAG_BUILDER);}
static inline ant_rope_heap_t *ant_str_rope_ptr(ant_value_t value) { return (ant_rope_heap_t *)vptr_masked(value, STR_HEAP_TAG_MASK);}
static inline ant_string_builder_t *ant_str_builder_ptr(ant_value_t value) { return (ant_string_builder_t *)vptr_masked(value, STR_HEAP_TAG_MASK);}
static inline ant_value_t ant_mkrope_value(ant_rope_heap_t *rope) { return mkref_tagged(kTypeString, rope, STR_HEAP_TAG_ROPE);}
static inline ant_value_t ant_mkbuilder_value(ant_string_builder_t *builder) { return mkref_tagged(kTypeString, builder, STR_HEAP_TAG_BUILDER);}
static inline ant_flat_string_t *str_flat_from_bytes(const char *str) { return (ant_flat_string_t *)((char *)str - offsetof(ant_flat_string_t, bytes));}
static inline ant_flat_string_t *ant_str_flat_ptr(ant_value_t value) { if (vtype(value) != kTypeString) return NULL; if ((vdata(value) & STR_HEAP_TAG_MASK) != STR_HEAP_TAG_FLAT) return NULL; return (ant_flat_string_t *)vptr(value);}
static inline ant_flat_string_t *large_string_flat_ptr(ant_large_string_alloc_t *alloc) { return alloc ? (ant_flat_string_t *)&alloc->len : NULL;}
static inline uint8_t str_concat_ascii_state(uint8_t left, uint8_t right) { if (left == STR_ASCII_NO || right == STR_ASCII_NO) return STR_ASCII_NO; return (left == STR_ASCII_YES && right == STR_ASCII_YES) ? STR_ASCII_YES : STR_ASCII_UNKNOWN;}
static inline uint8_t str_detect_ascii_bytes(const char *str, size_t len) { const unsigned char *s = (const unsigned char *)str; while (len >= 4 * sizeof(uint64_t)) { uint64_t words[4]; memcpy(words, s, sizeof(words)); if ((words[0] | words[1] | words[2] | words[3]) & UINT64_C(0x8080808080808080)) return STR_ASCII_NO; s += sizeof(words); len -= sizeof(words); } while (len >= sizeof(uint64_t)) { uint64_t word; memcpy(&word, s, sizeof(word)); if (word & UINT64_C(0x8080808080808080)) return STR_ASCII_NO; s += sizeof(word); len -= sizeof(word); } for (size_t i = 0; i < len; i++) if (s[i] >= 0x80) return STR_ASCII_NO; return STR_ASCII_YES;}
static inline uint8_t str_flat_ascii_state(const ant_flat_string_t *flat) { if (!flat) return STR_ASCII_UNKNOWN; return (uint8_t)((flat->meta & STR_META_ASCII_MASK) >> STR_META_ASCII_SHIFT);}
static inline uint8_t str_flat_utf_valid_state(const ant_flat_string_t *flat) { if (!flat) return STR_UTF_UNKNOWN; return (uint8_t)((flat->meta & STR_META_VALID_MASK) >> STR_META_VALID_SHIFT);}
static inline void str_flat_set_utf_valid_state(ant_flat_string_t *flat, uint8_t state) { if (!flat) return; flat->meta = (flat->meta & ~STR_META_VALID_MASK) | ( ((uint64_t)state << STR_META_VALID_SHIFT) & STR_META_VALID_MASK);}
static inline ant_offset_t str_flat_cached_utf16_len(const ant_flat_string_t *flat) { return flat ? (ant_offset_t)(flat->meta & STR_META_UTF16_MASK) : STR_UTF16_LEN_UNKNOWN;}
static inline void str_flat_set_ascii_state(ant_flat_string_t *flat, uint8_t state) { if (!flat) return; flat->meta = (flat->meta & ~STR_META_ASCII_MASK) | (((uint64_t)state << STR_META_ASCII_SHIFT) & STR_META_ASCII_MASK);}
static inline void str_flat_init_meta(ant_flat_string_t *flat, uint8_t ascii_state) { if (!flat) return; flat->meta = ((uint64_t)ascii_state << STR_META_ASCII_SHIFT) | STR_UTF16_LEN_UNKNOWN;}
static inline void str_flat_finish(ant_flat_string_t *flat, uint8_t ascii_state) { if (ascii_state != STR_ASCII_YES) { flat->len = (ant_offset_t)wtf8_join_surrogate_pairs(flat->bytes, (size_t)flat->len); flat->bytes[flat->len] = '\0'; } str_flat_init_meta(flat, ascii_state);}
static inline void str_copy_small(char *dst, const char *src, size_t n) { if (n > 32) { memcpy(dst, src, n); return; } if (n >= 16) { uint64_t head[2], tail[2]; memcpy(head, src, 16); memcpy(tail, src + n - 16, 16); memcpy(dst, head, 16); memcpy(dst + n - 16, tail, 16); return; } if (n >= 8) { uint64_t head, tail; memcpy(&head, src, 8); memcpy(&tail, src + n - 8, 8); memcpy(dst, &head, 8); memcpy(dst + n - 8, &tail, 8); return; } if (n >= 4) { uint32_t head, tail; memcpy(&head, src, 4); memcpy(&tail, src + n - 4, 4); memcpy(dst, &head, 4); memcpy(dst + n - 4, &tail, 4); return; } if (n >= 2) { uint16_t head, tail; memcpy(&head, src, 2); memcpy(&tail, src + n - 2, 2); memcpy(dst, &head, 2); memcpy(dst + n - 2, &tail, 2); return; } if (n) dst[0] = src[0];}
static inline void str_flat_fill_concat( ant_flat_string_t *out, const char *a, size_t na, uint8_t a_ascii, const char *b, size_t nb, uint8_t b_ascii) { str_copy_small(out->bytes, a, na); str_copy_small(out->bytes + na, b, nb); out->bytes[na + nb] = '\0'; uint8_t ascii = str_concat_ascii_state(a_ascii, b_ascii); if (ascii == STR_ASCII_UNKNOWN) ascii = str_detect_ascii_bytes(out->bytes, na + nb); str_flat_finish(out, ascii);}
static inline void str_flat_set_utf16_len(ant_flat_string_t *flat, ant_offset_t len) { if (!flat) return; flat->meta = (flat->meta & STR_META_STATE_MASK) | (len & STR_META_UTF16_MASK);}
static inline void str_set_ascii_state(const char *str, uint8_t state) { str_flat_set_ascii_state(str_flat_from_bytes(str), state);}
static inline bool str_flat_is_permanent(const ant_flat_string_t *flat) { return (flat->meta & STR_META_PERMANENT) != 0;}
static inline bool str_is_ascii(const char *str) { ant_flat_string_t *flat = str_flat_from_bytes(str); uint8_t state = str_flat_ascii_state(flat); if (state == STR_ASCII_UNKNOWN) { state = str_detect_ascii_bytes(flat->bytes, (size_t)flat->len); str_flat_set_ascii_state(flat, state); } return state == STR_ASCII_YES;}
static inline bool str_is_valid_utf8(const char *str) { if (str_is_ascii(str)) return true;
ant_flat_string_t *flat = str_flat_from_bytes(str); uint8_t state = str_flat_utf_valid_state(flat); if (state == STR_UTF_UNKNOWN) { state = utf8_validate_bytes(flat->bytes, (size_t)flat->len) ? STR_UTF_VALID : STR_UTF_INVALID; str_flat_set_utf_valid_state(flat, state); } return state == STR_UTF_VALID;}
static inline size_t utf8_export_length(const char *str, size_t str_len) { if (str_len == 0) return 0; uint8_t state = str_flat_utf_valid_state(str_flat_from_bytes(str)); if (state == STR_UTF_VALID || state == STR_UTF_INVALID_SAME_LENGTH) return str_len; if (state == STR_UTF_UNKNOWN && str_is_ascii(str)) return str_len; return utf8_export_length_slow(str, str_len);}
const char *intern_string(ant_t *js, const char *str, size_t len);const char *intern_find(ant_t *js, const char *str, size_t len);size_t intern_length(const char *interned);
js_cstr_t js_to_cstr(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size);js_cstr_t js_inspect_cstr(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size);js_cstr_t js_inspect_cstr_depth(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size, int max_depth);js_cstr_t js_inspect_cstr_opts(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size, int max_depth, uint8_t flags);
ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len);ant_offset_t vstrlen(ant_t *js, ant_value_t value);ant_offset_t str_len_fast(ant_t *js, ant_value_t str);ant_offset_t str_utf16_len(ant_t *js, ant_value_t str);
ant_value_t rope_flatten(ant_t *js, ant_value_t rope);ant_value_t str_materialize(ant_t *js, ant_value_t value);ant_value_t js_mkstr_byte_range(ant_t *js, const char *parent, size_t start, size_t len);ant_value_t js_mkstr_ascii(ant_t *js, const void *ptr, size_t len);
size_t utf8_export_into( const char *str, size_t str_len, uint8_t *dst, size_t dst_len, size_t *out_read_units);
uint64_t gc_strings_sweep_epoch(void);js_intern_stats_t js_intern_stats(ant_t *js);
void gc_strings_begin(ant_t *js);void gc_strings_sweep(ant_t *js);void gc_strings_epoch_bump(void);void gc_strings_mark(ant_t *js, const void *ptr);void gc_strings_destroy(ant_t *js);
#endif