Download
145 lines 5.2 KiBCModified 2022-09-24 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
#include <stdint.h>
#include <err.h>
#include "gc.h"
#include "types.h"

static const int64_t INT_NIL = 0x7FFFFFFFFFFFFFFF;

void list_insert_all(list_t *list, size_t item_size, int64_t index, list_t *other, const char *err_fmt) {
    if (index == INT_NIL) index = list->len + 1;
    else if (__builtin_expect((index < 1) | (index > list->len + 1), 0))
        errx(1, err_fmt, index);

    if (index == list->len + 1 && list->slack >= other->len) {
        memcpy(list->items.i8 + (index-1)*item_size, other->items.i8, other->len*item_size);
        list->slack -= other->len;
    } else {
        char *old_items = list->items.i8;
        list->items.i8 = gc_alloc(item_size * (list->len + other->len));
        list->slack = 0;
        memcpy(list->items.i8, old_items, item_size*(index-1));
        memcpy(list->items.i8 + (index-1)*item_size, other->items.i8, other->len*item_size);
        memcpy(list->items.i8 + (index-1 + other->len)*item_size, old_items+item_size*(index-1), item_size*(list->len - (index-1)));
    }
    list->len += other->len;
}

void list_insert(list_t *list, size_t item_size, int64_t index, int64_t item, const char *err_fmt) {
    if (index == INT_NIL) index = list->len + 1;
    else if (__builtin_expect((index < 1) | (index > list->len + 1), 0))
        errx(1, err_fmt, index);

    if (index == list->len + 1 && list->slack >= 1) {
        list->slack -= 1;
    } else {
        char *old_items = list->items.i8;
        list->slack = 8;
        list->items.i8 = gc_alloc(item_size * (list->len + 1 + list->slack));
        memcpy(list->items.i8, old_items, item_size*(index-1));
        memcpy(list->items.i8+item_size*index, old_items+item_size*(index-1), item_size*(list->len - (index-1)));
    }

    switch (item_size) {
        case 1: list->items.i8[index-1]  = *((int8_t*)&item); break;
        case 2: list->items.i16[index-1] = *((int16_t*)&item); break;
        case 4: list->items.i32[index-1] = *((int32_t*)&item); break;
        case 8: list->items.i64[index-1] = *((int64_t*)&item); break;
        default: break;
    }
    list->len += 1;
}

void list_remove(list_t *list, size_t item_size, int64_t first, int64_t last, const char *err_fmt) {
    if (first == INT_NIL) first = list->len;
    if (last == INT_NIL) last = first;

    if (__builtin_expect((first < 1) | (last < first) | (last > list->len), 0))
        errx(1, err_fmt, first, last);

    int64_t to_remove = last - first + 1;
    if (to_remove <= 0) to_remove = 0;

    // Even if there is free memory, writing there would break invariants (like 
    // preserving slice/iteration content), so it's no longer slack
    list->slack = 0;

    if (last == list->len) {
        list->len -= to_remove;
        if (list->len == 0)
            list->items.i8 = NULL;
        return;
    }

    char *old_items = list->items.i8;
    list->items.i8 = gc_alloc(item_size * (list->len - to_remove));
    if (first > 1)
        memcpy(list->items.i8, old_items, item_size*(first-1));
    memcpy(list->items.i8 + (first-1)*item_size, old_items+item_size*last, item_size*(list->len - last));
    list->len -= to_remove;
}

bool list_equal(list_t *a, list_t *b, size_t item_size) {
    return a == b || ((a->len | b->len) == 0) || (a->len == b->len && memcmp(a->items.i8, b->items.i8, a->len*item_size) == 0);
}

list_t *list_copy(list_t *l, size_t item_size) {
    list_t *copy = gc_alloc(sizeof(list_t));
    copy->len = l->len;
    if (l->len > 0) {
        copy->items.i8 = gc_alloc(item_size*l->len);
        memcpy(copy->items.i8, l->items.i8, item_size*l->len);
    }
    return copy;
}

void list_clear(list_t *l) {
    l->len = 0;
    l->items.i8 = NULL;
}

list_t *list_slice(list_t *list, range_t *r, size_t list_item_size, bool allow_aliasing) {
    list_t *slice = gc_alloc(sizeof(list_t));
    int64_t first = r->first;
    int64_t step = r->next - r->first;
    if (step == 0) return slice;
    int64_t last = r->last;
    if (step > 0) {
        if (first > list->len) return slice;
        if (first < 1) {
            first = first % step;
            if (first < 1) first += step;
        }
        if (last > list->len) last = list->len;
        if (last < first) return slice;
    } else {
        if (first < 1) return slice;
        if (first > list->len) {
            first = list->len - (list->len % -step) + (first % -step);
            if (first > list->len) first += step;
        }
        if (last < 1) last = 1;
        if (last > first) return slice;
    }
    int64_t len = ((last+step) - first) / step;
    if (len <= 0) len = 0;
    slice->len = len;

    if (step == 1 && allow_aliasing) {
        slice->items.i8 = list->items.i8 + (first-1)*list_item_size;
    } else if (len > 0) {
        void *p = gc_alloc(len * list_item_size);
        slice->items.i8 = p;
        void *src_items = list->items.i8;
        if (step == 1) {
            memcpy(slice->items.i8, &list->items.i8[(first-1)*list_item_size], len * list_item_size);
        } else {
            int64_t actual_len = 0;
            for (int64_t i = first; step > 0 ? (i <= last) : (i >= last); i += step) {
                actual_len += 1;
                p = mempcpy(p, src_items + (i - 1)*list_item_size, list_item_size);
            }
            slice->len = actual_len;
        }
    }
    return slice;
}