/* text-store - A store for storing reference-counted, de-duplicated strings. * Copyright (C) 2025 Jeremia Dominguez * * 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 . */ #ifndef TEXTSTORE_H #define TEXTSTORE_H #include #include #include #include /** * 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