Memory management#

Functions declared in mem.h provide interface for usage of dynamically allocated data. All other modules interact with dynamic data only through these functions.

Memory layout#

State of memory management is represented with structure lsp_mem_t. It contains continuous memory block of platform specific word count. Addresses used for referencing each data correspond to this memory block starting word index. Because addresses are represented with 14bit unsigned integer values, usable memory for allocation of dynamic data is limited to 16384 words (32768 bytes). In case of memory constrained systems, this size is even smaller.

Data allocation#

Each word (event those which are not single data starting words) have single bit dedicated for memory management usage. This most significant bit represent words current usage state where 0 represents used word (word is used for representing data content) and 1 represent unused word (word is available for allocation of new data instance).

During allocation of new data, all available words are searched for continuous block of unused words which could be used to represent newly allocated data content. If such word block could not be found, garbage collection procedure is triggered, after which search for free block is repeated. If search is not successful for the second time, allocation of new data fails. If search is successful, address of newly allocated data is remembered and used as starting address for future allocation searches.

All lsp_mem_create_* functions, used for data allocation, add allocated data to list of accessible root data. Management of accessible data is possible with lsp_mem_inc_ref and lsp_mem_dec_ref which increase and decrease references to root data. Once data is not part of root list or is not referenced by other data which are part of root list, it is considered not accessible and can be reclaimed by garbage collector.

During memory initialization, often used data instances are preallocated and available as part of lsp_mem_t structure (nil, zero, one, quote, quasiquote, unquote and unquote_splicing).

Garbage collector#

Garbage collector is based on variant of simple mark and sweep design. Initially, all memory words are marked as free. Once words are used for data representation, used words are immediately marked as non free. After repeated allocation of new data instances, pool of available free words is depleted and garbage procedure is started.

As first step of garbage collection, all memory words are marked as free. Then, words used for representation of root list are marked as non free by usage of recursive function which, together with immediately used words, marks all other referenced data words. Data types which reference other data are pair, function and syntax. After all data directly and indirectly referenced by root list is marked as used, garbage collection finishes (all other words remain marked as unused).

Data usage#

Together with data allocation function, memory management functions include interface for manipulation and usage of allocated data. These include lsp_mem_is_* function for data type assertion and lsp_mem_get_* functions for data content retrieval. This functions provide thin wrapper for cell.h functions with mapping of data addresses to data words.

All data types, except for string and pair are considered immutable - data content is initialized during allocation and is not mutated afterwards. In case of string and pair data, lsp_mem_set_* functions enable in place modification of data instance content. In case of string data, only content of preallocated size can be modified (size of string can not change after allocation).

All other parts of interpreter use only lsp_mem_* wrappers instead of direct lsp_cell_* function usage.

Symbol reusability#

Because symbols are immutable, memory usage optimization can be done during symbol allocation. Each time new symbol should be allocated, content of all memory words is searched for already allocated symbol with same content. If such instance is found, reference to already existing symbol is returned together with incrementing this reference in root list. This optimization comes with cost of additional memory search during each symbol allocation.

Ownership conventions#

Since data availability is controlled with usage create function and reference incrementation/decrementation, convention for memory ownership is required. In case of this project, if not explicitly specified otherwise, caller of function is owner of all input arguments and remains their owner after function execution finished (function temporary borrows ownership of input arguments). If function returns data, ownership of returned data is passed to function caller. It is responsibility of function caller to release data returned as result of function execution.

Source code#

mem.h#

#ifndef LISP16_MEM_H
#define LISP16_MEM_H

#include "cell.h"
#include "status.h"


typedef struct {
    lsp_addr_t nil;
    lsp_addr_t zero;
    lsp_addr_t one;
    lsp_addr_t quote;
    lsp_addr_t quasiquote;
    lsp_addr_t unquote;
    lsp_addr_t unquote_splicing;

    // internal
    lsp_uint16_t size;
    lsp_addr_t last_addr;
    lsp_addr_t root;
    lsp_cell_t cells[];
} lsp_mem_t;


lsp_status_t lsp_mem_init(lsp_mem_t *m, lsp_uint16_t size);
lsp_status_t lsp_mem_inc_ref(lsp_mem_t *m, lsp_addr_t addr);
void lsp_mem_dec_ref(lsp_mem_t *m, lsp_addr_t addr);

lsp_status_t lsp_mem_create_number(lsp_mem_t *m, lsp_int32_t value,
                                   lsp_addr_t *addr);
lsp_status_t lsp_mem_create_pair(lsp_mem_t *m, lsp_addr_t first,
                                 lsp_addr_t second, lsp_addr_t *addr);
lsp_status_t lsp_mem_create_string(lsp_mem_t *m, lsp_uint16_t data_len,
                                   lsp_addr_t *addr);
lsp_status_t lsp_mem_create_symbol_from_string(lsp_mem_t *m, lsp_addr_t str,
                                               lsp_addr_t *addr);
lsp_status_t lsp_mem_create_symbol_from_char(lsp_mem_t *m, char *name,
                                             lsp_addr_t *addr);
lsp_status_t lsp_mem_create_builtin_function(lsp_mem_t *m, lsp_uint16_t index,
                                             lsp_addr_t *addr);
lsp_status_t lsp_mem_create_builtin_syntax(lsp_mem_t *m, lsp_uint16_t index,
                                           lsp_addr_t *addr);
lsp_status_t lsp_mem_create_function(lsp_mem_t *m, lsp_addr_t parent_ctx,
                                     lsp_addr_t args, lsp_addr_t body,
                                     lsp_addr_t *addr);
lsp_status_t lsp_mem_create_syntax(lsp_mem_t *m, lsp_addr_t parent_ctx,
                                   lsp_addr_t args, lsp_addr_t body,
                                   lsp_addr_t *addr);

lsp_bool_t lsp_mem_eq(lsp_mem_t *m, lsp_addr_t a1, lsp_addr_t a2);
lsp_bool_t lsp_mem_equal(lsp_mem_t *m, lsp_addr_t a1, lsp_addr_t a2);


static inline lsp_bool_t lsp_mem_is_number(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_number(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_pair(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_pair(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_string(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_string(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_symbol(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_symbol(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_builtin_function(lsp_mem_t *m,
                                                     lsp_addr_t addr) {
    return lsp_cell_is_builtin_function(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_builtin_syntax(lsp_mem_t *m,
                                                   lsp_addr_t addr) {
    return lsp_cell_is_builtin_syntax(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_function(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_function(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_syntax(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_syntax(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_string_or_symbol(lsp_mem_t *m,
                                                     lsp_addr_t addr) {
    return lsp_cell_is_string_or_symbol(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_builtin(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_is_builtin(m->cells + addr);
}

static inline lsp_bool_t lsp_mem_is_function_or_syntax(lsp_mem_t *m,
                                                       lsp_addr_t addr) {
    return lsp_cell_is_function_or_syntax(m->cells + addr);
}


static inline lsp_int32_t lsp_mem_get_number(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_get_number(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_pair_first(lsp_mem_t *m, lsp_addr_t addr) {
    return lsp_cell_get_pair_first(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_pair_second(lsp_mem_t *m,
                                                 lsp_addr_t addr) {
    return lsp_cell_get_pair_second(m->cells + addr);
}

static inline lsp_uint16_t lsp_mem_get_string_len(lsp_mem_t *m,
                                                  lsp_addr_t addr) {
    return lsp_cell_get_string_len(m->cells + addr);
}

static inline lsp_uint8_t lsp_mem_get_string_data(lsp_mem_t *m, lsp_addr_t addr,
                                                  lsp_uint16_t i) {
    return lsp_cell_get_string_data(m->cells + addr, i);
}

static inline lsp_uint16_t lsp_mem_get_symbol_len(lsp_mem_t *m,
                                                  lsp_addr_t addr) {
    return lsp_cell_get_symbol_len(m->cells + addr);
}

static inline lsp_uint8_t lsp_mem_get_symbol_name(lsp_mem_t *m, lsp_addr_t addr,
                                                  lsp_uint16_t i) {
    return lsp_cell_get_symbol_name(m->cells + addr, i);
}

static inline lsp_uint16_t lsp_mem_get_builtin_index(lsp_mem_t *m,
                                                     lsp_addr_t addr) {
    return lsp_cell_get_builtin_index(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_function_parent_ctx(lsp_mem_t *m,
                                                         lsp_addr_t addr) {
    return lsp_cell_get_function_parent_ctx(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_function_args(lsp_mem_t *m,
                                                   lsp_addr_t addr) {
    return lsp_cell_get_function_args(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_function_body(lsp_mem_t *m,
                                                   lsp_addr_t addr) {
    return lsp_cell_get_function_body(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_syntax_parent_ctx(lsp_mem_t *m,
                                                       lsp_addr_t addr) {
    return lsp_cell_get_syntax_parent_ctx(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_syntax_args(lsp_mem_t *m,
                                                 lsp_addr_t addr) {
    return lsp_cell_get_syntax_args(m->cells + addr);
}

static inline lsp_addr_t lsp_mem_get_syntax_body(lsp_mem_t *m,
                                                 lsp_addr_t addr) {
    return lsp_cell_get_syntax_body(m->cells + addr);
}


static inline void lsp_mem_set_pair_first(lsp_mem_t *m, lsp_addr_t addr,
                                          lsp_addr_t first) {
    lsp_cell_set_pair(m->cells + addr, first,
                      lsp_cell_get_pair_second(m->cells + addr));
}

static inline void lsp_mem_set_pair_second(lsp_mem_t *m, lsp_addr_t addr,
                                           lsp_addr_t second) {
    lsp_cell_set_pair(m->cells + addr, lsp_cell_get_pair_first(m->cells + addr),
                      second);
}

static inline void lsp_mem_set_string_data(lsp_mem_t *m, lsp_addr_t addr,
                                           lsp_uint16_t i, lsp_uint8_t data_i) {
    lsp_cell_set_string_data(m->cells + addr, i, data_i);
}

#endif

mem.c#

#include "mem.h"


static inline lsp_bool_t get_mark(lsp_cell_t *c) {
    return ((*c & 0x8000) ? true : false);
}


static inline void set_mark(lsp_cell_t *c, lsp_bool_t mark) {
    if (mark) {
        *c |= 0x8000;
    } else {
        *c &= 0x7fff;
    }
}


static void restore(lsp_mem_t *m, lsp_addr_t addr) {
    while (true) {
        lsp_cell_t *c = m->cells + addr;
        if (!get_mark(c))
            break;

        lsp_uint16_t c_size = lsp_cell_get_size(c);
        for (lsp_uint16_t i = 0; i < c_size; ++i)
            set_mark(c + i, false);

        if (lsp_cell_is_pair(c)) {
            restore(m, lsp_cell_get_pair_first(c));
            addr = lsp_cell_get_pair_second(c);

        } else if (lsp_cell_is_function(c)) {
            restore(m, lsp_cell_get_function_parent_ctx(c));
            restore(m, lsp_cell_get_function_args(c));
            addr = lsp_cell_get_function_body(c);

        } else if (lsp_cell_is_syntax(c)) {
            restore(m, lsp_cell_get_syntax_parent_ctx(c));
            restore(m, lsp_cell_get_syntax_args(c));
            addr = lsp_cell_get_syntax_body(c);
        }
    }
}


static void mark_and_restore(lsp_mem_t *m) {
    for (lsp_addr_t addr = 0; addr < m->size; ++addr)
        set_mark(m->cells + addr, true);

    restore(m, m->nil);
    restore(m, m->zero);
    restore(m, m->one);
    restore(m, m->quote);
    restore(m, m->quasiquote);
    restore(m, m->unquote);
    restore(m, m->unquote_splicing);
    restore(m, m->root);
}


static lsp_bool_t is_free_cell(lsp_mem_t *m, lsp_addr_t addr, lsp_uint16_t size,
                               lsp_addr_t *used_addr) {
    for (lsp_addr_t i = addr; i < addr + size; ++i) {
        if (!get_mark(m->cells + i)) {
            *used_addr = i;
            return false;
        }
    }

    return true;
}


static lsp_status_t find_free_cell(lsp_mem_t *m, lsp_uint16_t size,
                                   lsp_addr_t *addr) {
    if (!size)
        return LSP_ERR_MEM;

    for (lsp_addr_t i = m->last_addr; i < m->size - size; ++i) {
        if (is_free_cell(m, i, size, &i)) {
            *addr = i;
            m->last_addr = i;
            return LSP_SUCCESS;
        }
    }

    for (lsp_addr_t i = 0; i < m->last_addr && i < m->size - size; ++i) {
        if (is_free_cell(m, i, size, &i)) {
            *addr = i;
            m->last_addr = i;
            return LSP_SUCCESS;
        }
    }

    return LSP_ERR_MEM;
}


static lsp_status_t find_free_cell_with_gc(lsp_mem_t *m, lsp_uint16_t size,
                                           lsp_addr_t *addr) {
    lsp_status_t status = find_free_cell(m, size, addr);
    if (status == LSP_SUCCESS)
        return LSP_SUCCESS;

    mark_and_restore(m);

    return find_free_cell(m, size, addr);
}


static lsp_status_t alloc_cell(lsp_mem_t *m, lsp_uint16_t size,
                               lsp_addr_t *addr) {
    lsp_addr_t root;
    lsp_status_t status = find_free_cell_with_gc(m, 2, &root);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_pair(m->cells + root, m->nil, m->root);
    m->root = root;

    status = find_free_cell_with_gc(m, size, addr);
    if (status != LSP_SUCCESS) {
        m->root = lsp_cell_get_pair_second(m->cells + root);
        return status;
    }

    lsp_mem_set_pair_first(m, root, *addr);
    return LSP_SUCCESS;
}


static lsp_bool_t is_symbol_from_string(lsp_mem_t *m, lsp_addr_t symbol,
                                        lsp_addr_t str) {
    lsp_uint16_t str_len = lsp_cell_get_string_len(m->cells + str);
    if (lsp_mem_get_symbol_len(m, symbol) != str_len)
        return false;

    for (lsp_uint16_t i = 0; i < str_len; ++i)
        if (lsp_mem_get_symbol_name(m, symbol, i) !=
            lsp_mem_get_string_data(m, str, i))
            return false;

    return true;
}


static lsp_bool_t is_symbol_from_char(lsp_mem_t *m, lsp_addr_t symbol,
                                      char *name, lsp_uint16_t name_len) {
    if (lsp_mem_get_symbol_len(m, symbol) != name_len)
        return false;

    for (lsp_uint16_t i = 0; i < name_len; ++i)
        if (lsp_mem_get_symbol_name(m, symbol, i) != name[i])
            return false;

    return true;
}


static lsp_status_t find_symbol_from_string(lsp_mem_t *m, lsp_addr_t str,
                                            lsp_addr_t *addr) {
    for (lsp_addr_t i = 0; i < m->size; ++i) {
        if (get_mark(m->cells + i))
            continue;

        if (lsp_cell_is_symbol(m->cells + i) &&
            is_symbol_from_string(m, i, str)) {
            *addr = i;
            return LSP_SUCCESS;
        }

        i += lsp_cell_get_size(m->cells + i) - 1;
    }

    return LSP_ERR;
}


static lsp_status_t find_symbol_from_char(lsp_mem_t *m, char *name,
                                          lsp_uint16_t name_len,
                                          lsp_addr_t *addr) {
    for (lsp_addr_t i = 0; i < m->size; ++i) {
        if (get_mark(m->cells + i))
            continue;

        if (lsp_cell_is_symbol(m->cells + i) &&
            is_symbol_from_char(m, i, name, name_len)) {
            *addr = i;
            return LSP_SUCCESS;
        }

        i += lsp_cell_get_size(m->cells + i) - 1;
    }

    return LSP_ERR;
}


lsp_status_t lsp_mem_init(lsp_mem_t *m, lsp_uint16_t size) {
    m->size = size;
    m->last_addr = 0;

    for (lsp_addr_t addr = 0; addr < size; ++addr)
        set_mark(m->cells + addr, true);

    lsp_status_t status = find_free_cell(m, 2, &(m->nil));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_pair(m->cells + m->nil, m->nil, m->nil);

    status = find_free_cell(m, lsp_cell_get_number_size(0), &(m->zero));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_number(m->cells + m->zero, 0);

    status = find_free_cell(m, lsp_cell_get_number_size(1), &(m->one));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_number(m->cells + m->one, 1);

    status = find_free_cell(m, lsp_cell_get_string_symbol_size(5), &(m->quote));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_symbol(m->cells + m->quote, 5);
    for (lsp_uint16_t i = 0; i < 5; ++i)
        lsp_cell_set_symbol_name(m->cells + m->quote, i, ("quote")[i]);

    status = find_free_cell(m, lsp_cell_get_string_symbol_size(10),
                            &(m->quasiquote));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_symbol(m->cells + m->quasiquote, 10);
    for (lsp_uint16_t i = 0; i < 10; ++i)
        lsp_cell_set_symbol_name(m->cells + m->quasiquote, i,
                                 ("quasiquote")[i]);

    status =
        find_free_cell(m, lsp_cell_get_string_symbol_size(7), &(m->unquote));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_symbol(m->cells + m->unquote, 7);
    for (lsp_uint16_t i = 0; i < 7; ++i)
        lsp_cell_set_symbol_name(m->cells + m->unquote, i, ("unquote")[i]);

    status = find_free_cell(m, lsp_cell_get_string_symbol_size(16),
                            &(m->unquote_splicing));
    if (status != LSP_SUCCESS)
        return status;
    lsp_cell_set_symbol(m->cells + m->unquote_splicing, 16);
    for (lsp_uint16_t i = 0; i < 16; ++i)
        lsp_cell_set_symbol_name(m->cells + m->unquote_splicing, i,
                                 ("unquote-splicing")[i]);

    m->root = m->nil;
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_inc_ref(lsp_mem_t *m, lsp_addr_t addr) {
    if (addr == m->nil || addr == m->zero || addr == m->one || addr == m->quote)
        return LSP_SUCCESS;

    lsp_addr_t root;
    lsp_status_t status = find_free_cell_with_gc(m, 2, &root);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_pair(m->cells + root, addr, m->root);
    m->root = root;
    return LSP_SUCCESS;
}


void lsp_mem_dec_ref(lsp_mem_t *m, lsp_addr_t addr) {
    if (addr == m->nil || addr == m->zero || addr == m->one || addr == m->quote)
        return;

    lsp_addr_t curr_addr = m->root;
    lsp_addr_t prev_addr = m->nil;
    while (curr_addr != m->nil) {
        lsp_addr_t first = lsp_cell_get_pair_first(m->cells + curr_addr);
        lsp_addr_t second = lsp_cell_get_pair_second(m->cells + curr_addr);

        if (first == addr) {
            if (prev_addr == m->nil) {
                m->root = second;

            } else {
                lsp_mem_set_pair_second(m, prev_addr, second);
            }

            return;
        }

        prev_addr = curr_addr;
        curr_addr = second;
    }
}


lsp_status_t lsp_mem_create_number(lsp_mem_t *m, lsp_int32_t value,
                                   lsp_addr_t *addr) {
    lsp_uint16_t size = lsp_cell_get_number_size(value);
    lsp_status_t status = alloc_cell(m, size, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_number(m->cells + *addr, value);
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_pair(lsp_mem_t *m, lsp_addr_t first,
                                 lsp_addr_t second, lsp_addr_t *addr) {
    lsp_status_t status = alloc_cell(m, 2, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_pair(m->cells + *addr, first, second);
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_string(lsp_mem_t *m, lsp_uint16_t data_len,
                                   lsp_addr_t *addr) {
    lsp_uint16_t size = lsp_cell_get_string_symbol_size(data_len);
    lsp_status_t status = alloc_cell(m, size, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_string(m->cells + *addr, data_len);
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_symbol_from_string(lsp_mem_t *m, lsp_addr_t str,
                                               lsp_addr_t *addr) {
    if (find_symbol_from_string(m, str, addr) == LSP_SUCCESS)
        return lsp_mem_inc_ref(m, *addr);

    lsp_uint16_t name_len = lsp_mem_get_string_len(m, str);
    lsp_uint16_t size = lsp_cell_get_string_symbol_size(name_len);
    lsp_status_t status = alloc_cell(m, size, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_symbol(m->cells + *addr, name_len);
    for (lsp_uint16_t i = 0; i < name_len; ++i)
        lsp_cell_set_symbol_name(m->cells + *addr, i,
                                 lsp_mem_get_string_data(m, str, i));

    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_symbol_from_char(lsp_mem_t *m, char *name,
                                             lsp_addr_t *addr) {
    lsp_uint16_t name_len = 0;
    while (name[name_len])
        name_len++;

    if (find_symbol_from_char(m, name, name_len, addr) == LSP_SUCCESS)
        return lsp_mem_inc_ref(m, *addr);

    lsp_uint16_t size = lsp_cell_get_string_symbol_size(name_len);
    lsp_status_t status = alloc_cell(m, size, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_symbol(m->cells + *addr, name_len);
    for (lsp_uint16_t i = 0; i < name_len; ++i)
        lsp_cell_set_symbol_name(m->cells + *addr, i, name[i]);

    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_builtin_function(lsp_mem_t *m, lsp_uint16_t index,
                                             lsp_addr_t *addr) {
    lsp_status_t status = alloc_cell(m, 1, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_builtin_function(m->cells + *addr, index);
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_builtin_syntax(lsp_mem_t *m, lsp_uint16_t index,
                                           lsp_addr_t *addr) {
    lsp_status_t status = alloc_cell(m, 1, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_builtin_syntax(m->cells + *addr, index);
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_function(lsp_mem_t *m, lsp_addr_t parent_ctx,
                                     lsp_addr_t args, lsp_addr_t body,
                                     lsp_addr_t *addr) {
    lsp_status_t status = alloc_cell(m, 4, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_function(m->cells + *addr, parent_ctx, args, body);
    return LSP_SUCCESS;
}


lsp_status_t lsp_mem_create_syntax(lsp_mem_t *m, lsp_addr_t parent_ctx,
                                   lsp_addr_t args, lsp_addr_t body,
                                   lsp_addr_t *addr) {
    lsp_status_t status = alloc_cell(m, 4, addr);
    if (status != LSP_SUCCESS)
        return status;

    lsp_cell_set_syntax(m->cells + *addr, parent_ctx, args, body);
    return LSP_SUCCESS;
}


lsp_bool_t lsp_mem_eq(lsp_mem_t *m, lsp_addr_t a1, lsp_addr_t a2) {
    if (a1 == a2)
        return true;

    if (lsp_mem_is_number(m, a1)) {
        if (!lsp_mem_is_number(m, a2))
            return false;

        return lsp_mem_get_number(m, a1) == lsp_mem_get_number(m, a2);
    }

    return false;
}


lsp_bool_t lsp_mem_equal(lsp_mem_t *m, lsp_addr_t a1, lsp_addr_t a2) {
    if (lsp_mem_eq(m, a1, a2))
        return true;

    if (lsp_mem_is_pair(m, a1)) {
        if (!lsp_mem_is_pair(m, a2))
            return false;

        while (a1 != m->nil && a2 != m->nil) {
            if (!lsp_mem_equal(m, lsp_mem_get_pair_first(m, a1),
                               lsp_mem_get_pair_first(m, a2)))
                return false;

            a1 = lsp_mem_get_pair_second(m, a1);
            a2 = lsp_mem_get_pair_second(m, a2);
        }

        return a1 == a2;
    }

    if (lsp_mem_is_string(m, a1)) {
        if (!lsp_mem_is_string(m, a2))
            return false;

        lsp_uint16_t a1_len = lsp_mem_get_string_len(m, a1);
        lsp_uint16_t a2_len = lsp_mem_get_string_len(m, a2);
        if (a1_len != a2_len)
            return false;

        for (lsp_uint16_t i = 0; i < a1_len; ++i) {
            if (lsp_mem_get_string_data(m, a1, i) !=
                lsp_mem_get_string_data(m, a2, i))
                return false;
        }

        return true;
    }

    return false;
}