#pragma once #include "calls.h" /* Usage: Start with MAKE_MUT(name, itemAmount) or MAKE_MUT_INIT(name, itemAmount, elType). Those allocate the Mut instance on the stack, so it must be finished before the scope ends. MAKE_MUT doesn't allocate any backing array, and will do so during mut_(set|fill|copy) (one of which must be the next operation on the Mut). MAKE_MUT_INIT makes a backing array with the specified element type; mut_(set|fill|copy) can still widen it, but the primary use-case is for the G-postfixed functions. End with mut_f(v|c|cd|p);. There must be no allocations while a mut object is being built so GC doesn't read the partially-initialized object. mut_pfree must be used to free a partially filled `mut` instance safely (e.g. before throwing an error). */ typedef struct Mut Mut; typedef struct MutFns MutFns; typedef void (*M_SetF)(void*, usz, B); typedef B (*M_GetF)(void*, usz); struct MutFns { u8 elType; u8 valType; M_CopyF m_copy, m_copyG; M_FillF m_fill, m_fillG; M_SetF m_set, m_setG; M_GetF m_getU; }; extern MutFns mutFns[el_MAX+1]; struct Mut { MutFns* fns; usz ia; Arr* val; void* a; }; void mut_to(Mut* m, u8 n); #if __clang__ && __has_attribute(noescape) // workaround for clang not realizing that stack-returned structs aren't captured void make_mut_init(__attribute__((noescape)) Mut* rp, ux ia, u8 el); #define MAKE_MUT_INIT(N, IA, EL) Mut N##_val; Mut* N = &N##_val; make_mut_init(N, IA, EL); #else Mut make_mut_init(ux ia, u8 el); #define MAKE_MUT_INIT(N, IA, EL) Mut N##_val = make_mut_init(IA, EL); Mut* N = &N##_val; #endif #define MAKE_MUT(N, IA) Mut N##_val; N##_val.fns = &mutFns[el_MAX]; N##_val.ia = (IA); Mut* N = &N##_val; static B mut_fv(Mut* m) { assert(m->fns->elType!=el_MAX); NOGC_E; assert(m->ia == m->val->ia); Arr* a = m->val; a->sh = &a->ia; SPRNK(a, 1); return taga(a); } static B mut_fc(Mut* m, B x) { assert(m->fns->elType!=el_MAX); // doesn't consume x NOGC_E; assert(m->ia == m->val->ia); Arr* a = m->val; arr_shCopy(a, x); return taga(a); } static B mut_fcd(Mut* m, B x) { assert(m->fns->elType!=el_MAX); // consumes x NOGC_E; assert(m->ia == m->val->ia); Arr* a = m->val; arr_shCopy(a, x); decG(x); return taga(a); } static Arr* mut_fp(Mut* m) { assert(m->fns->elType!=el_MAX); NOGC_E; assert(m->ia == m->val->ia); return m->val; } extern u8 el_orArr[]; static u8 el_or(u8 a, u8 b) { return el_orArr[a*16 + b]; } void mut_pfree(Mut* m, usz n); // consumes x; sets m[ms] to x static void mut_set(Mut* m, usz ms, B x) { m->fns->m_set(m, ms, x); } // clears the object (decrements its refcount) at position ms static void mut_rm(Mut* m, usz ms) { if (m->fns->elType == el_B) dec(((B*)m->a)[ms]); } // gets object at position ms, without increasing refcount static B mut_getU(Mut* m, usz ms) { return m->fns->m_getU(m->a, ms); } // doesn't consume; fills m[ms…ms+l] with x static void mut_fill(Mut* m, usz ms, B x, usz l) { m->fns->m_fill(m, ms, x, l); } // doesn't consume; expects x to be an array, each position must be written to precisely once static void mut_copy(Mut* m, usz ms, B x, usz xs, usz l) { assert(isArr(x)); m->fns->m_copy(m, ms, x, xs, l); } // MUT_APPEND_INIT must be called immediately after MAKE_MUT or MAKE_MUT_INIT // after that, the only valid operation on the Mut will be MUT_APPEND // using this append system will no longer prevent allocations from being done during the lifetime of the Mut #define MUT_APPEND_INIT(N) ux N##_ci = 0; NOGC_E; #define MUT_APPEND(N, X, XS, L) ({ ux l_ = (L); NOGC_CHECK; \ mut_copy(N, N##_ci, X, XS, l_); \ N##_ci+= l_; \ if (PTY(N->val) == t_harr) { NOGC_E; N->val->ia = N##_ci; } \ }) #define MUTG_INIT(N) MutFns N##_mutfns = *N->fns; void* N##_mutarr = N->a // these methods function as the non-G-postfixed ones, except that // the MAKE_MUT_INIT must have been used, MUTG_INIT called, and x must fit into the array type initialized to #define mut_setG(N, ms, x) N##_mutfns.m_setG(N##_mutarr, ms, x) #define mut_fillG(N, ms, x, l) N##_mutfns.m_fillG(N##_mutarr, ms, x, l) #define mut_copyG(N, ms, x, xs, l) N##_mutfns.m_copyG(N##_mutarr, ms, x, xs, l) // Companion to bit_cpy when uniform syntax is wanted #define MEM_CPY(R,RI,X,XI,L) memcpy((u8*)(R)+(RI), (u8*)(X)+(XI), (L)) static void bit_cpy(u64* r, usz rs, u64* x, usz xs, usz l) { u64 re = rs+(u64)l; i64 d = (i64)xs-(i64)rs; u64 ti = rs>>6; u64 ei = re>>6; u64 dp = (u64)(d>>6); u64 df = ((u64)d)&63u; #define RDFo(N) *(x + (i64)(ti+dp+N)) #define RDFp ((RDFo(0) >> df) | (RDFo(1) << (64-df))) #define READ (df==0? RDFo(0) : RDFp) if (ti!=ei) { if (rs&63) { u64 m = (1ULL << (rs&63))-1; r[ti] = (READ & ~m) | (r[ti] & m); ti++; } if (df==0) for (; ti=el_c8) goto yes; break; case t_c32arr: if (TYARR_SZ(C32,ria)=el_c8) goto yes; break; case t_harr: if (fsizeof(HArr,a,B,ria)ia = ria; return FL_KEEP(w,fl_squoze); // keeping fl_squoze as appending items can't make the largest item smaller } static inline bool inplace_add(B w, B x) { // consumes x if returns true; fails if fills wouldn't be correct usz wia = IA(w); usz ria = wia+1; if (reusable(w)) { u64 wsz = mm_sizeUsable(v(w)); u8 wt = TY(w); switch (wt) { case t_bitarr: if (BITARR_SZ( ria)ia = ria; return true; } B vec_addF(B w, B x); static B vec_add(B w, B x) { // consumes both; fills may be wrong if (inplace_add(w, x)) return w; return vec_addF(w, x); }