Something went wrong. Try again.
Reference-counted, deduplicated string store.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120/* text-store - A store for storing reference-counted, de-duplicated strings. * Copyright (C) 2025 Jeremia Dominguez <me@jeremia.dev> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */
#ifndef TEXTSTORE_H#define TEXTSTORE_H
#include <assert.h>#include <stdbool.h>#include <stddef.h>#include <stdint.h>
/** * A handle used to reference a string within the text store. */typedef uint64_t textstore_handle;#define TEXTSTORE_HANDLE_ERROR UINT64_MAX
typedef struct { uint32_t offset; uint32_t len;} textstore_slice;
#define TEXTSTORE_SOA \ X(textstore_slice, key) \ X(uint32_t, ref) \ X(uint32_t, generation) \ X(bool, active) \
/** * A store containing all string data. */typedef struct textstore_t { char *buffer; /* Buffer of all allocated text. Pointers to this buffer are considered volatile. */ uint32_t length; /* Length of currently used buffer. */ uint32_t buffer_cap; /* The current capacity of the buffer. */ uint32_t available; /* Count of available free handles. */
uint64_t *map; /* The hashmap's indexes into the tables below. */
#define X(type, member) type *member; TEXTSTORE_SOA #undef X
uint32_t count; /* The number of string entries. */ uint32_t hashmap_cap; /* The current capacity of the hashmap. */} textstore_t;
int textstore_init(textstore_t *store, uint32_t initial_buffer);void textstore_free(textstore_t *store);int textstore_gc(textstore_t *store);textstore_handle textstore_intern(textstore_t *store, char *str);textstore_handle textstore_intern_len(textstore_t *store, char *str, uint32_t len);
static inline uint32_t textstore_as_index(textstore_handle handle) { return (uint32_t) (handle & 0xFFFFFFFF);}static inline uint32_t textstore_as_generation(textstore_handle handle) { return (uint32_t) (handle >> 32);}
static inline char *textstore_string(textstore_t *store, textstore_handle handle) { textstore_t sstore = *store; uint32_t index = textstore_as_index(handle); if (index > sstore.count || !sstore.active[index]) return NULL;
uint32_t generation = textstore_as_generation(handle); if (generation != sstore.generation[index]) return NULL;
return sstore.buffer + sstore.key[index].offset;}
static inline uint32_t textstore_length(textstore_t *store, textstore_handle handle) { textstore_t sstore = *store; uint32_t index = textstore_as_index(handle); if (index == 0 || index > sstore.count || !sstore.active[index]) return 0;
uint32_t generation = textstore_as_generation(handle); if (generation != sstore.generation[index]) return 0;
return sstore.key[index].len;}
static inline void textstore_retain(textstore_t *store, textstore_handle handle) { uint32_t index = textstore_as_index(handle); assert(index < store->count); assert(store->active[index]);
store->ref[index] += 1;}
static inline void textstore_release(textstore_t *store, textstore_handle handle) { uint32_t index = textstore_as_index(handle); assert(index < store->count); assert(store->active[index]);
// Avoid underflow. uint32_t ref = store->ref[index]; if (ref) { store->ref[index] = ref - 1; }}
#endif // TEXTSTORE_H