Intern
Interned byte strings. Equal bytes share one stored copy and one stable pointer.
Usage#
The caller owns the LfIntern handle. The table owns each stored copy. Do not free an interned p. destroy frees the copies and the index, not the handle. Concurrent use is not supported.
Include lfintern.h. A successful intern's p stays valid until destroy, including across later interns. Each distinct string is its own block, so a later intern does not move earlier bytes. n is the byte length. A trailing NUL is written after those bytes and is not counted in n, so p is a C string when the content has no interior NUL. An interior NUL is raw bytes: lf_intern can store it, and lf_intern_cstr cannot, because a C string ends at the first NUL.
The index is an LfHashMap from each stored view to itself, using lfstr_match and lfstr_hash_cb, with no destroy callbacks. Equal bytes return the same pointer and length. A duplicate does not allocate a second copy. Empty input (n == 0, including a NULL p) stores one shared non-NULL pointer with n == 0 and a trailing NUL.
LfIntern table;
if (lf_intern_init(&table) == 0) {
LfStr a, b;
if (lf_intern_cstr(&table, "namespace:kind/slug", &a) == 0 &&
lf_intern(&table, a, &b) == 0) {
/* a.p == b.p and a.n == b.n; a.p[a.n] == '\0' */
}
lf_intern_destroy(&table);
}
On failure, *out is left unchanged and previously interned views stay valid.
Structs#
LfIntern#
typedef struct LfSlab LfSlab;
typedef struct LfIntern {
LfHashMap map;
LfSlab *slabs;
} LfIntern;
Declare LfIntern by value. map and slabs are private and not a supported API. Do not read or write them. LfSlab is incomplete in this header; the block layout is private to the implementation.
Functions#
lf_intern_init#
Builds an empty table. Returns 0 on success, -1 if t is NULL or the index cannot be allocated. Failed initialization leaves a non-NULL handle destroyable, and destroy on that handle frees nothing. Do not initialize a live table.
int lf_intern_init(LfIntern *t);lf_intern / lf_intern_cstr#
Copy bytes into the table. Returns 0 on success, -1 on error. There is no separate duplicate code: a repeat is success and returns the stored view. On success *out views the stored bytes. On failure *out is unchanged, the table is unchanged, and previously interned views stay valid.
A NULL table or a NULL out returns -1. s.n > 0 with s.p == NULL returns -1 and changes nothing. A length that does not fit in one block, or an allocation failure, does the same. lf_intern_cstr returns -1 on a NULL c-string and does not write *out. "" is an empty string.
int lf_intern(LfIntern *t, LfStr s, LfStr *out);
int lf_intern_cstr(LfIntern *t, const char *s, LfStr *out);lf_intern_destroy#
Frees every stored block and the index, and invalidates every interned pointer. The handle is not freed. destroy(NULL) does nothing. Destroying a failed init frees nothing.
void lf_intern_destroy(LfIntern *t);