str_t impl: added alloc bare

str_t impl: started `str_free()`

TODO: Walk derived string slice"s parent list, and free orphaned top-parent. (`str_free()` for derived string slices is more complex and is currently unimplemented, requiring list walking functions and full-offset computation during the walk.)

Fortune for naka's current commit: Future blessing − 末吉
strings
Avril 3 years ago
parent 152e0aa8e7
commit 267a4dae66

@ -55,7 +55,13 @@
#define _cconv__hot _hot
#define _cconv__entry __attribute__((constructor))
#define _cconv__exit __attribute__((destructor))
// Allocates
#define _cconv__alloc __attribute__((malloc))
// Infallible alloc
#define _cconv__ialloc __attribute__((malloc)) __attribute__((returns_nonnull))
#define _cconv__nonnull __attribute__((returns_nonnull))
#define _callcv(name) _cconv__ ## name
#define _callconv(name) _callcv(name)
@ -130,7 +136,7 @@ _mixin void _drain_val(void* x, ...) { IGNORE(x); } // This compiles to no-op on
#define box(t) aligned_alloc(_Alignof(t), sizeof(t))
#define box_value(v) ({ let _box = box(var(v)); *_box = (v); _box; })
#define unbox_value(v) ({ let _v = (v); let _res = *_v; free(_v); _res; }
#define unbox_value(v) ({ let _v = (v); let _res = *_v; free(_v); _res; })
#define stackalloc(t) __builtin_alloca_with_align(sizeof(t), _Alignof(t))
// Function macros
@ -257,4 +263,9 @@ static_assert_eq(bswap(bswap(128lu)), 128, "bswap128 (lu) failed (3)");
#define VERSION_REV(ver) _AS_u32(_AS_u32(ver) & 0xffu)
_Static_assert( (VERSION_COMP(VERSION(1,2,3,4), VER_COMP_MIN) >> VER_COMP_MIN) == 2u, "invalid native version spec");
// Misc.
#define PTR_ASSIGN(ptr, val) ( (ptr) ? (*(ptr) = (val), (ptr)) : (ptr) )
#define PTR_ASSIGNx(ptr, val) ({ let _ptr = (ptr); let _val = (val); PTR_ASSIGN(_ptr, _val); })
#endif /* _MACROS_H */

@ -6,7 +6,7 @@
#include <ints.h>
#define STRF_OWNED AS(1ul << 5, int)
#define STRF_DERRIVED AS(1ul << 6, int)
#define STRF_DERIVED AS(1ul << 6, int)
enum str_ownership {
STR_NULL = 0,
@ -15,10 +15,10 @@ enum str_ownership {
STR_OWNED_MALLOC,
STR_OWNED_STACK,
STR_DERRIVED = STRF_DERRIVED,
STR_DERRIVED_STATIC,
STR_DERRIVED_MALLOC,
STR_DERRIVED_STACK,
STR_DERIVED = STRF_DERIVED,
STR_DERIVED_STATIC,
STR_DERIVED_MALLOC,
STR_DERIVED_STACK,
};
typedef struct string str_t;

@ -11,17 +11,18 @@ struct string {
enum str_ownership owned;
union _str_meta_inner {
// `STRF_OWNED`
struct {
struct str_meta_owned {
_Atomic usize refs; // Number of derrivations from this
void (*m_free)(void*); // How to free (when `_MALLOC`).
_Atomic bool orphan; // If a `derive` references this parent somehow, but this string has been `str_free()`d. Tell the derived string that sets the `refs` to 0 to free it.
} owned;
// `STRF_DERRIVED`
struct {
// String this is derrived from, can be either owned or derrived.
// `STRF_DERIVED`
struct str_meta_derived {
// String this is derived from, can be either owned or derived.
struct string* _shared parent;
// `str`'s offset from `parent->str`.
usize offset;
} derrived;
} derived;
} _inner;
} meta;
// The start of this string slice. Regardless of slicing
@ -30,32 +31,88 @@ struct string {
//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 AS(owned, u32) & AS(STRF_OWNED, u32);
}
// Returns true for owned, false for derived
inline static bool _str_meta_parts_from(enum str_ownership o, union _str_meta_inner *pIN inner, struct str_meta_owned* *pOUT own, struct str_meta_derived* *pOUT derive)
{
if(_str_meta_is_owned(o))
return (PTR_ASSIGN(own, &inner->owned), true);
else return (PTR_ASSIGN(derive, &inner->derived), false);
}
inline static bool _str_is_owned(const str_t*pIN str)
{
return _str_meta_is_owned(str->meta.owned);
}
static union _str_meta_inner _str_default_meta(enum str_ownership oship)
inline static bool _str_is_owned_parts(str_t*pIN str, struct str_meta_owned* *pOUT own, struct str_meta_derived* *pOUT derive)
{
bool owned = _str_meta_is_owned(oship);
return _str_meta_parts_from(str->meta.owned, &str->meta._inner, own, derive);
}
static union _str_meta_inner _str_default_meta(enum str_ownership oship, bool *pOUT owned)
{
union _str_meta_inner output;
struct str_meta_owned* o_own = NULL;
struct str_meta_derived* o_der = NULL;
// PTR_ASSIGNx always evaluates both args, so this is fine
PTR_ASSIGNx(owned,
_str_meta_parts_from(oship, &output,
&o_own,
&o_der)
);
switch(oship)
{
case STR_OWNED_STACK:
case STR_OWNED_STATIC:
debug_assert(o_own);
*o_own = (struct str_meta_owned){
.refs = 0,
.m_free = NULL,
.orphan = false,
};
break;
case STR_OWNED_MALLOC:
debug_assert(o_own);
*o_own = (struct str_meta_owned){
.refs = 0,
.m_free = &free,
.orphan = false,
};
break;
case STR_DERIVED_STATIC:
case STR_DERIVED_STACK:
case STR_DERIVED_MALLOC:
debug_assert(o_der);
*o_der = (struct str_meta_derived){
.parent = NULL,
.offset = 0,
};
break;
default:
ERROR("Cannot assign metadata from ownership type 0x%x, returning 0'd metadata", AS(oship, u32));
memset(&output, 0, sizeof(output));
}
TODO("default str metadata");
return output;
}
/// Allocates a bare new str_t. The metadata will be incomplete for `derived` string slices, and `str` will be `NULL`. For `owned` strings, `str` will be `calloc()`ed to `cap+1`.
static str_t _str_alloc_bare(usize cap, enum str_ownership owned)
{
bool is_owned = _str_meta_is_owned(owned);
bool is_owned;
let meta_inner = _str_default_meta(owned, &is_owned);
struct str_meta meta = {
.owned = owned,
._inner = _str_default_meta(owned),
._inner = meta_inner,
};
str_t str = {
.len = 0,
@ -65,3 +122,33 @@ static str_t _str_alloc_bare(usize cap, enum str_ownership owned)
str.str = is_owned ? calloc(cap+1, 1) : NULL;
return str;
}
static void _str_force_free(struct str_meta_owned*pIN o_own, void* ptr)
{
(o_own->m_free ?: &free)(ptr);
}
void str_free(str_t* restrict strp)
{
str_t str = unbox_value(strp);
struct str_meta_owned* o_own = NULL;
struct str_meta_derived* o_der = NULL;
if(_str_is_owned_parts(&str, &o_own, &o_der))
{
debug_assert(o_own);
if(o_own->refs > 0) o_own->orphan = true;
else if(str.meta.owned == STR_OWNED_MALLOC && str.str)
_str_force_free(o_own, str.str);
}
else {
debug_assert(o_der);
TODO(" \
//TODO: Find top-level parent (traverse list of potentially derived `o_der->parent`s. \
//TODO: While doing so, compute the full offset that this slice has from the top owned parent. \
//TODO: Then, decrement its `refs` \
//TODO: Then, if it is marked as `orphan`, and we have decremented its `refs` to 0, free its `ptr`. \
");
}
}

Loading…
Cancel
Save