Merge remote-tracking branch 'origin/master'

This commit is contained in:
2025-09-01 21:45:18 +01:00
11 changed files with 249 additions and 11 deletions

64
impl/constructor.c Normal file
View File

@@ -0,0 +1,64 @@
/* 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-20
* Description: Lisp constructors/destructors
*/
#include <malloc.h>
#include <alisp.h>
lisp_t *make_int(i64 i)
{
return tag_int(i);
}
lisp_t *cons(sys_t *sys, lisp_t *car, lisp_t *cdr)
{
cons_t *cons = calloc(1, sizeof(*cons));
cons->car = car;
cons->cdr = cdr;
lisp_t *lcons = tag_cons(cons);
sys_register(sys, lcons);
return lcons;
}
lisp_t *make_vec(sys_t *sys, u64 capacity)
{
vec_t *vec = calloc(1, sizeof(*vec));
vec_init(vec, capacity);
lisp_t *ptr = tag_vec(vec);
sys_register(sys, ptr);
return ptr;
}
lisp_t *intern(sys_t *sys, sv_t sv)
{
char *str = sym_table_find(&sys->symtable, sv);
return tag_sym(str);
}
lisp_t *car(lisp_t *lsp)
{
if (!IS_TAG(lsp, CONS))
return NIL;
else
return CAR(lsp);
}
lisp_t *cdr(lisp_t *lsp)
{
if (!IS_TAG(lsp, CONS))
return NIL;
else
return CDR(lsp);
}

303
impl/stream.c Normal file
View File

@@ -0,0 +1,303 @@
/* 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-26
* Description: Stream implementation
*/
#include <string.h>
#include <alisp.h>
stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents)
{
if (!stream)
return STREAM_ERR_INVALID_PTR;
name = name ? name : "<stream>";
memset(stream, 0, sizeof(*stream));
stream->type = STREAM_TYPE_STRING;
stream->name = name;
stream->string = contents;
return STREAM_ERR_OK;
}
stream_err_t stream_init_file(stream_t *stream, char *name, FILE *pipe)
{
if (!stream || !pipe)
return STREAM_ERR_INVALID_PTR;
name = name ? name : "<stream>";
memset(stream, 0, sizeof(*stream));
stream->type = STREAM_TYPE_FILE;
stream->name = name;
stream->pipe.file = pipe;
vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
// try to read an initial chunk
stream_chunk(stream);
return STREAM_ERR_OK;
}
void stream_stop(stream_t *stream)
{
if (!stream)
return;
switch (stream->type)
{
case STREAM_TYPE_STRING:
// Nothing to do, all dealt with outside of stream
break;
case STREAM_TYPE_FILE:
// Must cleanup vector
vec_free(&stream->pipe.cache);
break;
}
memset(stream, 0, sizeof(*stream));
}
u64 stream_size(stream_t *stream)
{
assert(stream);
switch (stream->type)
{
case STREAM_TYPE_STRING:
return stream->string.size;
case STREAM_TYPE_FILE:
return stream->pipe.cache.size;
default:
FAIL("Unreachable");
return 0;
}
}
bool stream_eos(stream_t *stream)
{
assert(stream);
switch (stream->type)
{
case STREAM_TYPE_STRING:
return stream->position >= stream->string.size;
case STREAM_TYPE_FILE:
return feof(stream->pipe.file);
default:
FAIL("Unreachable");
return 0;
}
}
bool stream_eoc(stream_t *stream)
{
assert(stream);
switch (stream->type)
{
case STREAM_TYPE_STRING:
return stream->position >= stream->string.size;
case STREAM_TYPE_FILE:
return feof(stream->pipe.file) &&
stream->position >= stream->pipe.cache.size;
default:
FAIL("Unreachable");
return 0;
}
}
bool stream_chunk(stream_t *stream)
{
assert(stream);
switch (stream->type)
{
case STREAM_TYPE_STRING:
// vacuously true
return true;
case STREAM_TYPE_FILE:
{
if (feof(stream->pipe.file))
return false;
vec_ensure_free(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
int read = fread(vec_data(&stream->pipe.cache) + stream->pipe.cache.size, 1,
STREAM_DEFAULT_CHUNK, stream->pipe.file);
stream->pipe.cache.size += read;
return true;
}
default:
FAIL("Unreachable");
return 0;
}
}
char stream_next(stream_t *stream)
{
char c = stream_peek(stream);
if (c != '\0')
++stream->position;
return c;
}
char stream_peek(stream_t *stream)
{
// If we've reached end of stream, and end of content, there's really nothing
// to check here.
if (stream_eoc(stream))
return '\0';
switch (stream->type)
{
case STREAM_TYPE_STRING:
return stream->string.data[stream->position];
case STREAM_TYPE_FILE:
{
// Cached already? We are done.
if (stream->position < stream->pipe.cache.size)
return ((char *)vec_data(&stream->pipe.cache))[stream->position];
// Try to read chunks in till we've reached it or we're at the end of the
// file.
for (bool read_chunk = stream_chunk(stream);
read_chunk && stream->position >= stream->pipe.cache.size;
read_chunk = stream_chunk(stream))
continue;
// Same principle as the stream_eos(stream) check.
if (stream->position >= stream->pipe.cache.size)
return '\0';
return ((char *)vec_data(&stream->pipe.cache))[stream->position];
}
default:
FAIL("Unreachable");
return 0;
}
}
bool stream_seek(stream_t *stream, i64 offset)
{
if (offset < 0)
return stream_seek_backward(stream, offset * -1);
else if (offset > 0)
return stream_seek_forward(stream, offset);
else
// vacuously successful
return true;
}
bool stream_seek_forward(stream_t *stream, u64 offset)
{
if (stream_eoc(stream))
return false;
switch (stream->type)
{
case STREAM_TYPE_STRING:
{
if (stream->position + offset >= stream->string.size)
return false;
stream->position += offset;
return true;
}
case STREAM_TYPE_FILE:
{
// Similar principle as stream_peek really...
// Cached already? We are done.
if (stream->position + offset < stream->pipe.cache.size)
{
stream->position += offset;
return true;
}
// Try to read chunks in till we've reached it or we're at the end of the
// file.
for (bool read_chunk = stream_chunk(stream);
read_chunk && stream->position + offset >= stream->pipe.cache.size;
read_chunk = stream_chunk(stream))
continue;
// Same principle as the stream_eoc(stream) check.
if (stream->position + offset >= stream->pipe.cache.size)
return false;
stream->position += offset;
return true;
}
default:
FAIL("Unreachable");
return 0;
}
}
bool stream_seek_backward(stream_t *stream, u64 offset)
{
assert(stream);
if (stream->position < offset)
return false;
stream->position -= offset;
return true;
}
sv_t stream_substr(stream_t *stream, u64 size)
{
if (stream_eoc(stream))
return SV(NULL, 0);
// TODO: this is kinda disgusting, any better way of doing this
u64 current_position = stream->position;
bool successful = stream_seek_forward(stream, size);
// Reset the position in either situation
stream->position = current_position;
if (!successful)
return SV(NULL, 0);
char *ptr = NULL;
switch (stream->type)
{
case STREAM_TYPE_STRING:
ptr = stream->string.data;
break;
case STREAM_TYPE_FILE:
ptr = vec_data(&stream->pipe.cache);
break;
default:
FAIL("Unreachable");
return SV(NULL, 0);
}
return SV(ptr + stream->position, size);
}
sv_t stream_substr_abs(stream_t *stream, u64 index, u64 size)
{
switch (stream->type)
{
case STREAM_TYPE_STRING:
if (index + size <= stream_size(stream))
return SV(stream->string.data + index, size);
return SV(NULL, 0);
case STREAM_TYPE_FILE:
{
if (index + size <= stream_size(stream))
return SV(vec_data(&stream->pipe.cache) + index, size);
// stream_size(stream) <= index + size => try reading chunks
for (bool read_chunk = stream_chunk(stream);
read_chunk && index + size >= stream->pipe.cache.size;
read_chunk = stream_chunk(stream))
continue;
if (index + size > stream_size(stream))
return SV(NULL, 0);
return SV(vec_data(&stream->pipe.cache) + index, size);
}
default:
assert("Unreachable");
return SV(NULL, 0);
}
}

27
impl/sv.c Normal file
View File

@@ -0,0 +1,27 @@
/* 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-21
* Description: String views
*/
#include <malloc.h>
#include <string.h>
#include <alisp.h>
sv_t sv_copy(sv_t old)
{
char *newstr = calloc(1, (old.size + 1) * sizeof(*newstr));
memcpy(newstr, old.data, old.size);
newstr[old.size] = '\0';
return SV(newstr, old.size);
}

75
impl/symtable.c Normal file
View File

@@ -0,0 +1,75 @@
/* 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: Symbol Table implementation
*/
#include <alisp.h>
#include <malloc.h>
#include <string.h>
u64 djb2(sv_t string)
{
u64 hash = 5381;
for (u64 i = 0; i < string.size; ++i)
hash = string.data[i] + (hash + (hash << 5));
return hash;
}
void sym_table_init(sym_table_t *table)
{
table->capacity = MAX(table->capacity, SYM_TABLE_INIT_SIZE);
table->count = 0;
vec_init(&table->entries, table->capacity * sizeof(sv_t));
}
char *sym_table_find(sym_table_t *table, sv_t sv)
{
// Initialise the table if it's not done already
if (table->entries.capacity == 0)
sym_table_init(table);
// TODO: Deal with resizing this when table->count > table->size / 2
u64 index = djb2(sv) & (table->capacity - 1);
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 (!VEC_GET(&table->entries, sv_t)[index].data)
{
sv_t newsv = sv_copy(sv);
VEC_GET(&table->entries, sv_t)[index] = newsv;
++table->count;
}
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)
{
current = VEC_GET(&table->entries, sv_t)[i];
if (current.data)
free(current.data);
}
// kill the container
vec_free(&table->entries);
memset(table, 0, sizeof(*table));
}

77
impl/sys.c Normal file
View File

@@ -0,0 +1,77 @@
/* 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-20
* Description: System management
*/
#include <alisp.h>
#include <assert.h>
#include <malloc.h>
#include <string.h>
void sys_init(sys_t *sys)
{
memset(sys, 0, sizeof(*sys));
}
void sys_register(sys_t *sys, lisp_t *ptr)
{
// Generate an unmanaged cons
cons_t *cons = calloc(1, sizeof(*cons));
cons->car = ptr;
cons->cdr = sys->memory;
sys->memory = tag_cons(cons);
}
void sys_cleanup(sys_t *sys)
{
static_assert(NUM_TAGS == 5);
sym_table_cleanup(&sys->symtable);
if (!sys->memory)
return;
// Iterate through each element of memory
for (lisp_t *cell = sys->memory, *next = cdr(cell); cell;
cell = next, next = cdr(next))
{
// Only reason allocated exists is because we had to allocate memory on the
// heap for it. It's therefore enough to deal with only the allocated
// types.
lisp_t *allocated = car(cell);
switch (get_tag(allocated))
{
case TAG_CONS:
// Delete the cons
free(as_cons(allocated));
break;
case TAG_VEC:
{
vec_t *vec = as_vec(allocated);
vec_free(vec);
free(vec);
break;
}
case TAG_NIL:
case TAG_INT:
case TAG_SYM:
case NUM_TAGS:
// shouldn't be dealt with (either constant or dealt with elsewhere)
break;
}
// Then free the current cell
free(as_cons(cell));
}
memset(sys, 0, sizeof(*sys));
}

76
impl/tag.c Normal file
View File

@@ -0,0 +1,76 @@
/* 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: Pointer tagging
*/
#include <assert.h>
#include <stdlib.h>
#include <alisp.h>
lisp_t *tag_int(i64 i)
{
return TAG((u64)i, INT);
}
lisp_t *tag_sym(char *str)
{
return TAG((u64)str, SYM);
}
lisp_t *tag_vec(vec_t *vec)
{
return TAG((u64)vec, VEC);
}
lisp_t *tag_cons(cons_t *cons)
{
return TAG((u64)cons, CONS);
}
tag_t get_tag(lisp_t *lisp)
{
static_assert(NUM_TAGS == 5);
if (!lisp)
return TAG_NIL;
else if (IS_TAG(lisp, INT))
return TAG_INT;
return (u64)lisp & 0xFF;
}
i64 as_int(lisp_t *obj)
{
assert(IS_TAG(obj, INT));
u64 p_obj = (u64)obj;
return UNTAG(p_obj, INT) | // Delete the tag
(NTH_BYTE(p_obj, 7) & 0x80) << 56 // duplicate the MSB (preserve sign)
;
}
char *as_sym(lisp_t *obj)
{
assert(IS_TAG(obj, SYM));
return (char *)UNTAG(obj, SYM);
}
cons_t *as_cons(lisp_t *obj)
{
assert(IS_TAG(obj, CONS));
return (cons_t *)UNTAG(obj, CONS);
}
vec_t *as_vec(lisp_t *obj)
{
assert(IS_TAG(obj, VEC));
return (vec_t *)UNTAG(obj, VEC);
}

238
impl/test.c Normal file
View File

@@ -0,0 +1,238 @@
/* 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-21
* Description: Tests
*/
#include <stdio.h>
#include <string.h>
#include <alisp.h>
#define TEST_PASSED() printf("[%s]: Pass\n", __func__)
#define TEST(NAME, COND) \
do \
{ \
if (!(COND)) \
{ \
printf("[%s]: `%s` failed!\n", __func__, (NAME)); \
assert(0); \
} \
} while (0)
// Sample data
const char *unique_words[] = {
"bibendum", "etiam", "gravida", "dui", "cursus",
"purus", "diam", "phasellus", "nam", "fermentum",
"leo", "enim", "ac", "semper", "non",
"mauris", "proin", "tellus", "vivamus", "lobortis",
"lacus", "neque", "in", "nullam", "felis",
"orci", "pede", "tempus", "nec", "at",
"tortor", "massa", "sed", "magna", "eget",
"tempor", "velit", "imperdiet", "praesent", "volutpat",
"tristique", "id", "commodo", "aliquet", "quis",
"pellentesque", "eleifend", "porta", "nunc", "euismod",
"aliquam", "a", "erat", "dignissim", "ut",
"vitae", "vel", "donec",
};
char *words[] = {
"aliquam", "erat", "volutpat", "nunc", "eleifend",
"leo", "vitae", "magna", "in", "id",
"erat", "non", "orci", "commodo", "lobortis",
"proin", "neque", "massa", "cursus", "ut",
"gravida", "ut", "lobortis", "eget", "lacus",
"sed", "diam", "praesent", "fermentum", "tempor",
"tellus", "nullam", "tempus", "mauris", "ac",
"felis", "vel", "velit", "tristique", "imperdiet",
"donec", "at", "pede", "etiam", "vel",
"neque", "nec", "dui", "dignissim", "bibendum",
"vivamus", "id", "enim", "phasellus", "neque",
"orci", "porta", "a", "aliquet", "quis",
"semper", "a", "massa", "phasellus", "purus",
"pellentesque", "tristique", "imperdiet", "tortor", "nam",
"euismod", "tellus", "id", "erat",
};
char words_text[] =
"aliquam erat volutpat nunc eleifend leo vitae magna in id erat non orci "
"commodo lobortis proin neque massa cursus ut gravida ut lobortis eget "
"lacus sed diam praesent fermentum tempor tellus nullam tempus mauris ac "
"felis vel velit tristique imperdiet donec at pede etiam vel neque nec dui "
"dignissim bibendum vivamus id enim phasellus neque orci porta a aliquet "
"quis semper a massa phasellus purus pellentesque tristique imperdiet "
"tortor nam euismod tellus id erat";
char text[] =
"Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. "
"Etiam vel tortor sodales tellus ultricies commodo. Suspendisse potenti. "
"Aenean in sem ac leo mollis blandit. Donec neque quam, dignissim in, "
"mollis nec, sagittis eu, wisi. Phasellus lacus. Etiam laoreet quam sed "
"arcu. Phasellus at dui in ligula mollis ultricies. Integer placerat "
"tristique nisl. Praesent augue. Fusce commodo. Vestibulum convallis, "
"lorem a tempus semper, dui dui euismod elit, vitae placerat urna tortor "
"vitae lacus. Nullam libero mauris, consequat quis, varius et, dictum id, "
"arcu. Mauris mollis tincidunt felis. Aliquam feugiat tellus ut neque. "
"Nulla facilisis, risus a rhoncus fermentum, tellus tellus lacinia purus, "
"et dictum nunc justo sit amet elit.";
void symtable_test(void)
{
sym_table_t table = {0};
sym_table_init(&table);
for (u64 i = 0; i < ARRSIZE(words); ++i)
sym_table_find(&table, SV(words[i], strlen(words[i])));
TEST("|table|=|set(words)|", table.count == ARRSIZE(unique_words));
TEST("|table| < |words|", table.count < ARRSIZE(words));
TEST_PASSED();
sym_table_cleanup(&table);
}
void int_test(void)
{
i64 ints[] = {
1, -1, (1 << 10) - 1, (-1) * ((1 << 10) - 1), INT_MIN, INT_MAX,
};
for (u64 i = 0; i < ARRSIZE(ints); ++i)
{
i64 in = ints[i];
lisp_t *lisp = make_int(in);
i64 out = as_int(lisp);
TEST("in == out", in == out);
}
TEST_PASSED();
}
void sym_test(void)
{
sys_t system = {0};
sys_init(&system);
for (u64 i = 0; i < ARRSIZE(words); ++i)
{
char *in = words[i];
lisp_t *lisp = intern(&system, SV(in, strlen(in)));
char *out = as_sym(lisp);
TEST("unique pointers when interning a symbol", in != out);
TEST("same size", strlen(in) == strlen(out));
TEST("same string", strncmp(in, out, strlen(in)) == 0);
}
TEST_PASSED();
sys_cleanup(&system);
}
void vec_test1(void)
{
sys_t system = {0};
sys_init(&system);
// Generating a vector word by word
lisp_t *lvec = make_vec(&system, 0);
for (u64 i = 0; i < ARRSIZE(words); ++i)
{
char *word = words[i];
vec_append(as_vec(lvec), word, strlen(word));
if (i != ARRSIZE(words) - 1)
vec_append(as_vec(lvec), " ", 1);
}
vec_append(as_vec(lvec), "\0", 1);
vec_t *vec = as_vec(lvec);
TEST("same size vector", vec->size == ARRSIZE(words_text));
TEST("same as actually concatenated string",
strncmp(vec_data(vec), words_text, vec->size) == 0);
TEST_PASSED();
sys_cleanup(&system);
}
void vec_test2(void)
{
sys_t system = {0};
sys_init(&system);
// Generating substrings
struct Test
{
u64 start, size;
} tests[] = {
{0, 16},
{0, 32},
{32, 64},
{0, ARRSIZE(text)},
};
for (u64 i = 0; i < ARRSIZE(tests); ++i)
{
struct Test test = tests[i];
const sv_t substr = SV(text + test.start, test.size);
const u64 size = test.size / 2;
lisp_t *lvec = make_vec(&system, size);
vec_append(as_vec(lvec), text + test.start, test.size);
TEST("Vector grew", as_vec(lvec)->size > size);
TEST("Vector's substring is equivalent to the actual substring",
strncmp(vec_data(as_vec(lvec)), substr.data, substr.size) == 0);
}
printf("[vec_test2]: Pass\n");
sys_cleanup(&system);
}
void cons_test(void)
{
sys_t system = {0};
sys_init(&system);
// Let's make a list of words using `cons`
lisp_t *lisp = NIL;
for (u64 i = 0; i < ARRSIZE(words); ++i)
{
char *word = words[i];
lisp_t *lword = intern(&system, SV(word, strlen(word)));
lisp = cons(&system, lword, lisp);
}
// Make sure we've essentially reversed the `words` array
u64 i = ARRSIZE(words);
for (lisp_t *iter = lisp; iter; iter = cdr(iter))
{
lisp_t *item = car(iter);
TEST("we've reversed the array",
strncmp(words[i - 1], as_sym(item), strlen(words[i - 1])) == 0);
i -= 1;
}
TEST_PASSED();
sys_cleanup(&system);
}
typedef void (*test_fn)(void);
const test_fn TESTS[] = {
int_test, sym_test, vec_test1, vec_test2, cons_test,
};
int main(void)
{
for (u64 i = 0; i < ARRSIZE(TESTS); ++i)
{
TESTS[i]();
}
return 0;
}

94
impl/vec.c Normal file
View File

@@ -0,0 +1,94 @@
/* 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-20
* Description: Stable Vector implementation
*/
#include <malloc.h>
#include <string.h>
#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->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)
return;
if (vec->capacity - vec->size < size)
{
vec->capacity = MAX(vec->capacity * VEC_MULT, vec->size + size);
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);
}
}
void vec_append(vec_t *vec, const void *const ptr, u64 size)
{
if (!vec)
return;
vec_ensure_free(vec, size);
memcpy(vec_data(vec) + vec->size, ptr, size);
vec->size += size;
}
void vec_clone(vec_t *dest, vec_t *src)
{
if (!src || !dest)
return;
vec_init(dest, src->capacity);
memcpy(vec_data(dest), vec_data(src), src->size);
}