#include "collections.h" #include #include #include #include #include #include #define MIN(A, B) ((A) <= (B) ? (A) : (B)) #define MAX(A, B) ((A) >= (B) ? (A) : (B)) #define ALIGN(VAL, ALIGN) \ ((VAL) % (ALIGN) != 0 ? (VAL) + ((ALIGN) - (VAL) % (ALIGN)) : (VAL)) void *array_push(void **data, size_t *capacity, size_t *count, void const *elem, size_t elem_size) { if (!*data) { *capacity = 8; *data = malloc(*capacity * elem_size); *count = 0; } else if (*count + 1 > *capacity) { *capacity *= 2; *data = realloc(*data, *capacity * elem_size); } void *ptr = &((unsigned char *)*data)[*count * elem_size]; if (elem) { memcpy(ptr, elem, elem_size); } *count += 1; return ptr; } void *array_insert_at(void **data, size_t *capacity, size_t *count, size_t idx, void const *elem, size_t elem_size) { if (idx >= *count) { return array_push(data, capacity, count, elem, elem_size); } if (*count + 1 > *capacity) { array_push(data, capacity, count, NULL, elem_size); } void *src_ptr = &((unsigned char *)*data)[idx * elem_size]; void *dest_ptr = &((unsigned char *)*data)[(idx + 1) * elem_size]; memmove(dest_ptr, src_ptr, (*count - idx) * elem_size); *count += 1; if (elem) { memcpy(src_ptr, elem, elem_size); } return src_ptr; } #define small_count_max 3 void smallarray_construct(struct smallarray *a) { *a = (struct smallarray) { 0 }; } void smallarray_destroy(struct smallarray *a) { if ((a->smalldata[0] & 1) == 0 && a->data) { free(a->data); } } static bool sa_is_big(struct smallarray const *a) { return a->data && (a->smalldata[0] & 1) == 0; } static void sa_push_big(struct smallarray *a, void *value) { if (!sa_is_big(a)) { size_t capacity = 8; void **ptr = malloc(capacity * sizeof(void *)); size_t count = 0; for (size_t i = 0; i < small_count_max; ++i) { if ((a->smalldata[i] & 1) == 0) break; ptr[i] = (void *)(a->smalldata[i] & ~1ull); count += 1; } a->capacity = capacity; a->data = ptr; a->count = count; } array_push( (void **)&a->data, &a->capacity, &a->count, &value, sizeof(void *)); } static void sa_push_small(struct smallarray *a, void *value) { for (size_t i = 0; i < small_count_max; ++i) { if (a->smalldata[i] & 1) continue; a->smalldata[i] = (size_t)value | 1; return; } } void smallarray_push(struct smallarray *a, void *value) { assert((size_t)value % 2 == 0 && "pointer must be 2 bytes aligned"); if (sa_is_big(a) || smallarray_count(a) >= small_count_max) { sa_push_big(a, value); } else { sa_push_small(a, value); } } size_t smallarray_count(struct smallarray const *a) { if (sa_is_big(a)) { return a->count; } else { size_t count = 0; for (size_t i = 0; i < small_count_max; ++i) { if ((a->smalldata[i] & 1) == 0) break; count += 1; } return count; } } void *smallarray_get(struct smallarray *a, size_t idx) { if (sa_is_big(a)) { return a->data[idx]; } else { if (idx >= small_count_max || (a->smalldata[idx] & 1) == 0) return NULL; return (void *)(a->smalldata[idx] & ~1ull); } } void const *smallarray_get_const(struct smallarray const *a, size_t idx) { return smallarray_get((struct smallarray *)a, idx); } static uint64_t hash_key(char const *data) { // djb2 uint64_t hash = 5381; unsigned char c; while ((c = (unsigned char)*data++)) { hash = ((hash << 5) + hash) + c; // hash * 33 + c } return hash; } static uint64_t hash_key_sized(char const *data, size_t size) { // djb2 uint64_t hash = 5381; unsigned char c; while (size-- > 0 && (c = (unsigned char)*data++)) { hash = ((hash << 5) + hash) + c; // hash * 33 + c } return hash; } void hashmap_construct(struct hashmap *t) { *t = (struct hashmap) { NULL, 0, 0, NULL, 0, 0 }; } void hashmap_destroy(struct hashmap *t) { if (t->buckets) free(t->buckets); if (t->keys) free(t->keys); } static struct hash_entry *find_entry(struct hashmap *m, uint64_t hash) { for (size_t i = 0; i < m->buckets_count; ++i) { struct hash_bucket *bucket = &m->buckets[i]; if (hash < bucket->first_hash) break; if (hash > bucket->last_hash) continue; for (size_t j = 0; j < bucket->count; ++j) { struct hash_entry *entry = &bucket->entries[j]; if (entry->hash == hash) return entry; } } return NULL; } static struct hash_bucket *insert_bucket_at(struct hashmap *m, size_t idx) { struct hash_bucket bucket = { .entries = { { 0 } }, .count = 0, .first_hash = 0, .last_hash = 0, }; return array_insert_at((void **)&m->buckets, &m->buckets_capacity, &m->buckets_count, idx, &bucket, sizeof(struct hash_bucket)); } static struct hash_entry *add_entry_to_bucket( struct hash_bucket *b, uint64_t hash) { b->count += 1; b->first_hash = MIN(b->first_hash, hash); b->last_hash = MAX(b->last_hash, hash); return &b->entries[b->count - 1]; } static struct hash_entry *make_entry(struct hashmap *m, uint64_t hash) { assert(m->buckets_count >= 1 && m->buckets[0].count != 0); if (hash < m->buckets[0].first_hash && m->buckets[0].count < bucket_capacity) { return add_entry_to_bucket(&m->buckets[0], hash); } for (size_t i = 0; i < m->buckets_count; ++i) { struct hash_bucket *curr = &m->buckets[i]; struct hash_bucket *next = i + 1 < m->buckets_count ? &m->buckets[i + 1] : NULL; if (next && hash >= next->first_hash) continue; if (curr->count < bucket_capacity) { return add_entry_to_bucket(curr, hash); } else if (next && next->count < bucket_capacity) { return add_entry_to_bucket(next, hash); } else { struct hash_bucket *b = insert_bucket_at(m, i + 1); b->first_hash = UINT64_MAX; return add_entry_to_bucket(b, hash); } } assert(false); } static void insert_key( struct hashmap *m, char const *key, size_t key_size, uint64_t hash) { key_size = key_size != 0 ? key_size : strlen(key); char *key_value = malloc(key_size + 1); strncpy(key_value, key, key_size); key_value[key_size] = '\0'; struct hash_key_entry key_entry = { .hash = hash, .value = key_value, }; array_push((void **)&m->keys, &m->keys_capacity, &m->keys_count, &key_entry, sizeof(key_entry)); } static void hashmap_set_internal(struct hashmap *m, char const *key, size_t key_size, uint64_t hash, void *value) { if (m->buckets_count == 0) { struct hash_bucket *bucket = insert_bucket_at(m, 0); bucket->entries[0] = (struct hash_entry) { hash, value }; bucket->count += 1; bucket->first_hash = hash; bucket->last_hash = hash; insert_key(m, key, key_size, hash); return; } struct hash_entry *entry = find_entry(m, hash); if (!entry) { entry = make_entry(m, hash); entry->hash = hash; insert_key(m, key, key_size, hash); } entry->value = value; return; } void hashmap_set(struct hashmap *m, char const *key, void *value) { uint64_t hash = hash_key(key); hashmap_set_internal(m, key, 0, hash, value); } void hashmap_set_sized( struct hashmap *m, char const *key, size_t key_size, void *value) { uint64_t hash = hash_key_sized(key, key_size); hashmap_set_internal(m, key, key_size, hash, value); } bool hashmap_has(struct hashmap *m, char const *key) { uint64_t hash = hash_key(key); return find_entry(m, hash) != NULL; } static void *hashmap_get_internal(struct hashmap *m, uint64_t hash) { struct hash_entry *entry = find_entry(m, hash); return entry ? entry->value : NULL; } void *hashmap_get(struct hashmap *m, char const *key) { uint64_t hash = hash_key(key); return hashmap_get_internal(m, hash); } void *hashmap_get_sized(struct hashmap *m, char const *key, size_t key_size) { uint64_t hash = hash_key_sized(key, key_size); return hashmap_get_internal(m, hash); } void *hashmap_get_hash(struct hashmap *m, size_t hash) { return hashmap_get_internal(m, hash); } void const *hashmap_get_hash_const(struct hashmap const *m, size_t hash) { return hashmap_get_internal((struct hashmap *)m, hash); } void hashmap_keys( struct hashmap const *m, struct hash_key_entry const **keys, size_t *count) { *keys = m->keys; *count = m->keys_count; } struct blockalloc_block { unsigned char *data; size_t size; }; void blockalloc_construct(struct blockalloc *a) { *a = (struct blockalloc) { NULL, 0, 0, 0 }; } void blockalloc_destroy(struct blockalloc *a) { if (a->blocks) free(a->blocks); } void *blockalloc_alloc(struct blockalloc *a, size_t size, size_t align) { size_t p = ALIGN(a->p, align); if (!a->blocks || p + size > a->blocks[a->count - 1].size) { size_t block_size = MAX(blockalloc_default_block, size); struct blockalloc_block block = { .data = malloc(block_size), .size = block_size, }; array_push((void **)&a->blocks, &a->capacity, &a->count, &block, sizeof(struct blockalloc_block)); a->p = 0; p = 0; } void *ptr = &a->blocks[a->count - 1].data[p]; a->p = p + size; return ptr; }