|
|
@ -5,62 +5,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
#include <str.h>
|
|
|
|
#include <str.h>
|
|
|
|
|
|
|
|
|
|
|
|
static str_t _str_alloc_new(usize reserve)
|
|
|
|
struct string {
|
|
|
|
|
|
|
|
usize len, cap;
|
|
|
|
|
|
|
|
struct str_meta {
|
|
|
|
|
|
|
|
enum str_ownership owned;
|
|
|
|
|
|
|
|
union _str_meta_inner {
|
|
|
|
|
|
|
|
// `STRF_OWNED`
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
|
|
|
_Atomic usize refs; // Number of derrivations from this
|
|
|
|
|
|
|
|
void (*m_free)(void*); // How to free (when `_MALLOC`).
|
|
|
|
|
|
|
|
} owned;
|
|
|
|
|
|
|
|
// `STRF_DERRIVED`
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
|
|
|
// String this is derrived from, can be either owned or derrived.
|
|
|
|
|
|
|
|
struct string* _shared parent;
|
|
|
|
|
|
|
|
// `str`'s offset from `parent->str`.
|
|
|
|
|
|
|
|
usize offset;
|
|
|
|
|
|
|
|
} derrived;
|
|
|
|
|
|
|
|
} _inner;
|
|
|
|
|
|
|
|
} meta;
|
|
|
|
|
|
|
|
// The start of this string slice. Regardless of slicing
|
|
|
|
|
|
|
|
char* str;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//TODO: We can use offsetof() to reverse the result of `str_new()` from char* (str field) to str_t*.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline static bool _str_meta_is_owned(enum str_ownership owned)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return aligned_alloc(_Alignof(struct string), sizeof(struct string) + reserve + 1);
|
|
|
|
return AS(owned, u32) & AS(STRF_OWNED, u32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline static str_t _str_unbox(str_t* restrict boxed)
|
|
|
|
inline static bool _str_is_owned(const str_t*pIN str)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
str_t unboxed = *boxed;
|
|
|
|
return _str_meta_is_owned(str->meta.owned);
|
|
|
|
free(boxed);
|
|
|
|
|
|
|
|
return unboxed;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_cconv(alloc) str_t* str_alloc(usize cap)
|
|
|
|
static union _str_meta_inner _str_default_meta(enum str_ownership oship)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
str_t unboxed = _str_alloc_new(cap);
|
|
|
|
bool owned = _str_meta_is_owned(oship);
|
|
|
|
unboxed->cap = cap;
|
|
|
|
switch(oship)
|
|
|
|
unboxed->len = 0;
|
|
|
|
{
|
|
|
|
unboxed->meta = (struct str_meta){
|
|
|
|
|
|
|
|
.derrived = NULL,
|
|
|
|
}
|
|
|
|
.refs = 0,
|
|
|
|
TODO("default str metadata");
|
|
|
|
.m_free = &free,
|
|
|
|
|
|
|
|
.owned = STR_OWNED_MALLOC,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
memset(unboxed->str, 0, cap+1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return box_value(unboxed);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void str_free(str_t* restrict str)
|
|
|
|
static str_t _str_alloc_bare(usize cap, enum str_ownership owned)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
str_t ub = _str_unbox(str);
|
|
|
|
bool is_owned = _str_meta_is_owned(owned);
|
|
|
|
|
|
|
|
struct str_meta meta = {
|
|
|
|
void* to_free = (void*)ub;
|
|
|
|
.owned = owned,
|
|
|
|
let _cont[] = { &&_done, &&_finish_free_derrived };
|
|
|
|
._inner = _str_default_meta(owned),
|
|
|
|
usize cont = 0;
|
|
|
|
};
|
|
|
|
switch(ub->meta.owned) {
|
|
|
|
str_t str = {
|
|
|
|
case STR_DERRIVED_MALLOC:
|
|
|
|
.len = 0,
|
|
|
|
ub->meta.derrived->meta.refs -=1;
|
|
|
|
.cap = cap,
|
|
|
|
to_free = NULL;
|
|
|
|
.meta = meta,
|
|
|
|
cont = 1;
|
|
|
|
};
|
|
|
|
case STR_OWNED_MALLOC:
|
|
|
|
str.str = is_owned ? calloc(cap+1, 1) : NULL;
|
|
|
|
//TODO: How to handle this refcounting?
|
|
|
|
return str;
|
|
|
|
if(ub->meta.refs > 0) WARN("Freeing referenced string");
|
|
|
|
|
|
|
|
//if(ub->meta.refs == 0)
|
|
|
|
|
|
|
|
if(to_free)
|
|
|
|
|
|
|
|
(ub->meta.m_free ?: &free)(to_free);
|
|
|
|
|
|
|
|
/*else {
|
|
|
|
|
|
|
|
ub->meta.refs -=1;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
goto _cont[cont];
|
|
|
|
|
|
|
|
_finish_free_derrived:
|
|
|
|
|
|
|
|
free(ub);
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
_done:
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|