diff options
| author | Aryadev Chavali <aryadev@aryadevchavali.com> | 2025-08-20 23:27:04 +0100 | 
|---|---|---|
| committer | Aryadev Chavali <aryadev@aryadevchavali.com> | 2025-08-20 23:37:08 +0100 | 
| commit | 847eb1a69b54da3a5d686922f0a2fcd8ab37f1e6 (patch) | |
| tree | 057d4c1ca6f478a2909d0ee271d2bb8ff0f25c2f | |
| parent | 13142dc7f38e6b148efadc97edffca8664b9cde7 (diff) | |
| download | alisp-847eb1a69b54da3a5d686922f0a2fcd8ab37f1e6.tar.gz alisp-847eb1a69b54da3a5d686922f0a2fcd8ab37f1e6.tar.bz2 alisp-847eb1a69b54da3a5d686922f0a2fcd8ab37f1e6.zip  | |
Refactor vectors to SBO, removing inlined entirely.
Avoid 2 levels of indirection, and having to allocate twice for small
payloads, by having an inlined array on the vector directly!
Beautiful and simple.
Required a bit of refactoring around the board, but overall the result
makes me feel happier.
| -rw-r--r-- | alisp.h | 56 | ||||
| -rw-r--r-- | build.sh | 2 | ||||
| -rw-r--r-- | constructor.c | 4 | ||||
| -rw-r--r-- | ivec.c | 76 | ||||
| -rw-r--r-- | symtable.c | 28 | ||||
| -rw-r--r-- | tag.c | 6 | ||||
| -rw-r--r-- | vec.c | 51 | 
7 files changed, 92 insertions, 131 deletions
@@ -15,6 +15,9 @@  #ifndef ALISP_H  #define ALISP_H +#include <assert.h> +#include <stdalign.h> +#include <stddef.h>  #include <stdint.h>  /// The bare fucking minimum @@ -46,53 +49,46 @@ typedef struct  sv_t sv_copy(sv_t); -/// Good ol' Dynamic Arrays +/// Dynamic arrays + +#define VEC_INLINE_CAPACITY 32 +#define VEC_MULT            2 +  typedef struct Vector  {    u64 size, capacity; -  void *data; +  u8 is_inlined; +  union +  { +    void *ptr; +    alignas(max_align_t) u8 inlined[VEC_INLINE_CAPACITY]; +  };  } vec_t; -#define VEC_MULT             2 -#define VEC_DEFAULT_CAPACITY 8 +static_assert(sizeof(vec_t) == 64, "vec_t has to be 64 bytes"); +#define VEC_GET(V, T) ((T *)vec_data(V)) + +void vec_init(vec_t *, u64);  void vec_free(vec_t *); +void *vec_data(vec_t *);  void vec_ensure_free(vec_t *, u64);  void vec_append(vec_t *, void *, u64);  void vec_clone(vec_t *, vec_t *); -/// Inlined Dynamic arrays -typedef struct InlineVector -{ -  u64 size, capacity; -  u8 bytes[]; -} ivec_t; - -#define IVEC_GET(P)  (((ivec_t *)(P)) - 1) -#define IVEC_SIZE(P) (IVEC_GET(P)->size) -#define IVEC_CAP(P)  (IVEC_GET(P)->capacity) -#define IVEC_MULT    2 - -void ivec_make(void **, u64); -void ivec_free(void **); -void ivec_ensure_free(void **, u64); -void ivec_append_byte(void **, u8); -void ivec_append(void **, void *, u64); -void ivec_clone(void **, void **); -  /// Symbol table  typedef struct  { -  u64 count;     // How many strings? -  u64 capacity;  // How many entry buckets? -  sv_t *entries; // this is actually a vector on the inside lol +  u64 count;    // How many strings? +  u64 capacity; // How many entry buckets? +  vec_t entries;  } sym_table_t; -#define SYM_TABLE_INIT_SIZE 1024 +#define SYM_TABLE_INIT_SIZE (1 << 10)  u64 djb2(sv_t string);  void sym_table_init(sym_table_t *); -sv_t *sym_table_find(sym_table_t *, sv_t); +char *sym_table_find(sym_table_t *, sv_t);  void sym_table_cleanup(sym_table_t *);  /// Basic defintions for a Lisp @@ -123,7 +119,7 @@ lisp_t *intern(sys_t *, sv_t);  lisp_t *cons(sys_t *, lisp_t *, lisp_t *);  i64 as_int(lisp_t *); -sv_t *as_sym(lisp_t *); +char *as_sym(lisp_t *);  cons_t *as_cons(lisp_t *);  vec_t *as_vec(lisp_t *); @@ -168,7 +164,7 @@ enum Mask  tag_t get_tag(lisp_t *);  lisp_t *tag_int(i64); -lisp_t *tag_sym(sv_t *); +lisp_t *tag_sym(char *);  lisp_t *tag_cons(cons_t *);  lisp_t *tag_vec(vec_t *); @@ -1,7 +1,7 @@  #!/usr/bin/env sh  CFLAGS="-Wall -Wextra -std=c11 -ggdb -fsanitize=address -fsanitize=undefined" -SRC="vec.c ivec.c symtable.c tag.c constructor.c sys.c main.c" +SRC="vec.c symtable.c tag.c constructor.c sys.c main.c"  OUT="alisp.out"  set -xe diff --git a/constructor.c b/constructor.c index 910ed2a..9db0330 100644 --- a/constructor.c +++ b/constructor.c @@ -35,7 +35,7 @@ lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)  lisp_t *make_vec(sys_t *sys, u64 capacity)  {    vec_t *vec = calloc(1, sizeof(*vec)); -  vec_ensure_free(vec, MAX(capacity, VEC_DEFAULT_CAPACITY)); +  vec_init(vec, capacity);    lisp_t *ptr = tag_vec(vec);    sys_register(sys, ptr);    return ptr; @@ -43,6 +43,6 @@ lisp_t *make_vec(sys_t *sys, u64 capacity)  lisp_t *intern(sys_t *sys, sv_t sv)  { -  sv_t *str = sym_table_find(&sys->symtable, sv); +  char *str = sym_table_find(&sys->symtable, sv);    return tag_sym(str);  } @@ -1,76 +0,0 @@ -/* Copyright (C) 2025 Aryadev Chavali - - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE.  See the Unlicense for details. - - * You may distribute and modify this code under the terms of the Unlicense, - * which you should have received a copy of along with this program.  If not, - * please go to <https://unlicense.org/>. - - * Created: 2025-08-19 - * Description: Inline Vector implementation - */ - -#include <malloc.h> -#include <string.h> - -#include "./alisp.h" - -void ivec_make(void **ptr, u64 size) -{ -  if (!ptr) -    return; -  ivec_t *ivector   = calloc(1, sizeof(*ivector) + size); -  ivector->size     = 0; -  ivector->capacity = size; -  *ptr              = (ivector + 1); -} - -void ivec_free(void **data) -{ -  if (!data || !*data) -    return; -  free(IVEC_GET(*data)); -  *data = NULL; -} - -void ivec_ensure_free(void **ptr, u64 space) -{ -  if (!ptr || !*ptr) -    return; -  ivec_t *ivec = IVEC_GET(*ptr); -  if (ivec->capacity - ivec->size < space) -  { -    void *new_ivec = NULL; -    ivec_make(&new_ivec, MAX(ivec->capacity * IVEC_MULT, ivec->size + space)); -    IVEC_SIZE(new_ivec) = ivec->size; -    memcpy(new_ivec, *ptr, ivec->size); -    ivec_free(ptr); -    *ptr = new_ivec; -  } -} - -void ivec_append_byte(void **ptr, u8 byte) -{ -  ivec_ensure_free(ptr, 1); -  ivec_t *ivec              = IVEC_GET(*ptr); -  ivec->bytes[ivec->size++] = byte; -} - -void ivec_append(void **ptr, void *data, u64 size) -{ -  ivec_ensure_free(ptr, size); -  ivec_t *ivec = IVEC_GET(*ptr); -  memcpy(*ptr + ivec->size, data, size); -  ivec->size += size; -} - -void ivec_clone(void **dest, void **src) -{ -  if (!dest || !src || !*src) -    return; -  ivec_make(dest, IVEC_SIZE(*src)); -  memcpy(*dest, *src, IVEC_SIZE(*src)); -  IVEC_SIZE(*dest) = IVEC_SIZE(*src); -} @@ -29,39 +29,43 @@ void sym_table_init(sym_table_t *table)  {    table->capacity = MAX(table->capacity, SYM_TABLE_INIT_SIZE);    table->count    = 0; -  ivec_make((void **)&table->entries, -            table->capacity * sizeof(*table->entries)); +  vec_init(&table->entries, table->capacity * sizeof(sv_t));  } -sv_t *sym_table_find(sym_table_t *table, sv_t sv) +char *sym_table_find(sym_table_t *table, sv_t sv)  {    // WIP: Deal with resizing this when table->count > table->size / 2    u64 index = djb2(sv) & (table->capacity - 1); -  for (sv_t comp  = table->entries[index]; comp.data; index += 1, -            index = index & (table->capacity - 1), comp = table->entries[index]) +  for (sv_t comp = VEC_GET(&table->entries, sv_t)[index]; comp.data; index += 1, +            index = index & (table->capacity - 1), +            comp  = VEC_GET(&table->entries, sv_t)[index])      // Is it present in the table?      if (sv.size == comp.size && strncmp(sv.data, comp.data, sv.size) == 0)        break;    // we couldn't find it in our linear search (worst case scenario) -  if (!table->entries[index].data) +  if (!VEC_GET(&table->entries, sv_t)[index].data)    { -    sv_t newsv            = sv_copy(sv); -    table->entries[index] = newsv; +    sv_t newsv                            = sv_copy(sv); +    VEC_GET(&table->entries, sv_t)[index] = newsv;      ++table->count;    } -  return table->entries + index; +  return VEC_GET(&table->entries, sv_t)[index].data;  }  void sym_table_cleanup(sym_table_t *table)  {    // kill the data +  sv_t current = {0};    for (u64 i = 0; i < table->capacity; ++i) -    if (table->entries[i].data) -      free(table->entries[i].data); +  { +    current = VEC_GET(&table->entries, sv_t)[i]; +    if (current.data) +      free(current.data); +  }    // kill the container -  ivec_free((void **)&table->entries); +  vec_free(&table->entries);    memset(table, 0, sizeof(*table));  } @@ -22,7 +22,7 @@ lisp_t *tag_int(i64 i)    return TAG((u64)i, INT);  } -lisp_t *tag_sym(sv_t *str) +lisp_t *tag_sym(char *str)  {    return TAG((u64)str, SYM);  } @@ -57,10 +57,10 @@ i64 as_int(lisp_t *obj)        ;  } -sv_t *as_sym(lisp_t *obj) +char *as_sym(lisp_t *obj)  {    assert(IS_TAG(obj, SYM)); -  return (sv_t *)UNTAG(obj, SYM); +  return (char *)UNTAG(obj, SYM);  }  cons_t *as_cons(lisp_t *obj) @@ -18,15 +18,39 @@  #include "./alisp.h" +void vec_init(vec_t *vec, u64 size) +{ +  memset(vec, 0, sizeof(*vec)); +  if (!vec) +    return; +  else if (size <= VEC_INLINE_CAPACITY) +  { +    vec->is_inlined = 1; +    vec->capacity   = VEC_INLINE_CAPACITY; +    vec->ptr        = NULL; +  } +  else +  { +    vec->is_inlined = 0; +    vec->capacity   = size; +    vec->ptr        = calloc(1, vec->capacity); +  } +} +  void vec_free(vec_t *vec)  {    if (!vec)      return; -  if (vec->data) -    free(vec->data); +  if (!vec->is_inlined && vec->ptr) +    free(vec->ptr);    memset(vec, 0, sizeof(*vec));  } +void *vec_data(vec_t *vec) +{ +  return vec->is_inlined ? vec->inlined : vec->ptr; +} +  void vec_ensure_free(vec_t *vec, u64 size)  {    if (!vec) @@ -34,7 +58,21 @@ void vec_ensure_free(vec_t *vec, u64 size)    if (vec->capacity - vec->size < size)    {      vec->capacity = MAX(vec->capacity * VEC_MULT, vec->size + size); -    vec->data     = realloc(vec->data, vec->capacity); +    if (vec->is_inlined) +    { +      // If we're inlined, we need to allocate on the heap now.  So let's copy +      // vec->inlined over to vec->ptr, then turn off inlining. + +      // We need to do a two-way swap since vec->ptr and vec->inlined are taking +      // up the same space. +      u8 buffer[VEC_INLINE_CAPACITY]; +      memcpy(buffer, vec->inlined, vec->size); +      vec->ptr = calloc(1, vec->capacity); +      memcpy(vec->ptr, buffer, vec->size); +      vec->is_inlined = 0; +    } +    else +      vec->ptr = realloc(vec->ptr, vec->capacity);    }  } @@ -43,7 +81,7 @@ void vec_append(vec_t *vec, void *ptr, u64 size)    if (!vec)      return;    vec_ensure_free(vec, size); -  memcpy(vec->data + vec->size, ptr, size); +  memcpy(vec_data(vec) + vec->size, ptr, size);    vec->size += size;  } @@ -51,7 +89,6 @@ void vec_clone(vec_t *dest, vec_t *src)  {    if (!src || !dest)      return; -  dest       = src; -  dest->data = calloc(1, dest->capacity); -  memcpy(dest->data, src->data, src->size); +  vec_init(dest, src->capacity); +  memcpy(vec_data(dest), vec_data(src), src->size);  }  | 
