/// @file #include "retrieve.h" #include "allocate.h" #include "hashtable.h" #include "imprison.h" #include "murmur3.h" #include "trace.h" #include "xtract.h" // declarations of inline functions // c3_o u3r_cell(u3_noun a, u3_noun* b, u3_noun* c); c3_o u3r_trel(u3_noun a, u3_noun* b, u3_noun* c, u3_noun* d); c3_o u3r_qual(u3_noun a, u3_noun* b, u3_noun* c, u3_noun* d, u3_noun* e); c3_o u3r_quil(u3_noun a, u3_noun* b, u3_noun* c, u3_noun* d, u3_noun* e, u3_noun* f); c3_o u3r_hext(u3_noun a, u3_noun* b, u3_noun* c, u3_noun* d, u3_noun* e, u3_noun* f, u3_noun* g); /* _frag_word(): fast fragment/branch prediction for top word. */ static u3_weak _frag_word(c3_w a_w, u3_noun b) { u3_assert(0 != a_w); { c3_w dep_w = u3x_dep(a_w); while ( dep_w ) { if ( c3n == u3a_is_cell(b) ) { return u3_none; } else { u3a_cell* b_u = u3a_to_ptr(b); b = *(((u3_noun*)&(b_u->hed)) + (1 & (a_w >> (dep_w - 1)))); dep_w--; } } return b; } } /* _frag_deep(): fast fragment/branch for deep words. */ static u3_weak _frag_deep(c3_w a_w, u3_noun b) { c3_w dep_w = 32; while ( dep_w ) { if ( c3n == u3a_is_cell(b) ) { return u3_none; } else { u3a_cell* b_u = u3a_to_ptr(b); b = *(((u3_noun*)&(b_u->hed)) + (1 & (a_w >> (dep_w - 1)))); dep_w--; } } return b; } /* u3r_at(): ** ** Return fragment (a) of (b), or u3_none if not applicable. */ u3_weak u3r_at(u3_atom a, u3_noun b) { u3_assert(u3_none != a); u3_assert(u3_none != b); u3t_on(far_o); if ( 0 == a ) { u3t_off(far_o); return u3_none; } if ( _(u3a_is_cat(a)) ) { u3t_off(far_o); return _frag_word(a, b); } else { if ( !_(u3a_is_pug(a)) ) { u3t_off(far_o); return u3_none; } else { u3a_atom* a_u = u3a_to_ptr(a); c3_w len_w = a_u->len_w; b = _frag_word(a_u->buf_w[len_w - 1], b); len_w -= 1; if ( u3_none == b ) { u3t_off(far_o); return b; } while ( len_w ) { b = _frag_deep(a_u->buf_w[len_w - 1], b); if ( u3_none == b ) { u3t_off(far_o); return b; } else { len_w--; } } u3t_off(far_o); return b; } } } /* u3r_mean(): ** ** Attempt to deconstruct `a` by axis, noun pairs; 0 terminates. ** Axes must be sorted in tree order. */ struct _mean_pair { c3_w axe_w; u3_noun* som; }; static c3_w _mean_cut(c3_w len_w, struct _mean_pair* prs_m) { c3_w i_w, cut_t, cut_w; cut_t = 0; cut_w = 0; for ( i_w = 0; i_w < len_w; i_w++ ) { c3_w axe_w = prs_m[i_w].axe_w; if ( (cut_t == 0) && (3 == u3x_cap(axe_w)) ) { cut_t = 1; cut_w = i_w; } prs_m[i_w].axe_w = u3x_mas(axe_w); } return cut_t ? cut_w : i_w; } static c3_o _mean_extract(u3_noun som, c3_w len_w, struct _mean_pair* prs_m) { if ( len_w == 0 ) { return c3y; } else if ( (len_w == 1) && (1 == prs_m[0].axe_w) ) { *prs_m->som = som; return c3y; } else { if ( c3n == u3a_is_cell(som) ) { return c3n; } else { c3_w cut_w = _mean_cut(len_w, prs_m); return c3a (_mean_extract(u3a_h(som), cut_w, prs_m), _mean_extract(u3a_t(som), (len_w - cut_w), (prs_m + cut_w))); } } } c3_o u3r_vmean(u3_noun som, va_list ap) { va_list aq; c3_w len_w; struct _mean_pair* prs_m; u3_assert(u3_none != som); // traverse copy of va_list for alloca // va_copy(aq, ap); len_w = 0; while ( 1 ) { if ( 0 == va_arg(aq, c3_w) ) { break; } va_arg(aq, u3_noun*); len_w++; } va_end(aq); u3_assert( 0 != len_w ); prs_m = alloca(len_w * sizeof(struct _mean_pair)); // traverse va_list and extract args // { c3_w i_w; for ( i_w = 0; i_w < len_w; i_w++ ) { prs_m[i_w].axe_w = va_arg(ap, c3_w); prs_m[i_w].som = va_arg(ap, u3_noun*); } va_end(ap); } // extract axis from som // return _mean_extract(som, len_w, prs_m); } c3_o u3r_mean(u3_noun som, ...) { c3_o ret_o; va_list ap; va_start(ap, som); ret_o = u3r_vmean(som, ap); va_end(ap); return ret_o; } // stack frame for tracking noun comparison and unification // // we always compare arbitrary nouns in a none-frame. // when we compare two cells, we change the none-frame to a head-frame // and push a new none-frame for their heads. if the heads are equal, // we get the cells from the head-frame and unify their head pointers. // then, we convert the head-frame to a tail-frame and repeat with // the tails, mutatis mutandis. // // in Hoon, this structure would be: // // $% [%none a=* b=*] // [%head a=^ b=^] // [%tail a=^ b=^] // == // #define SING_NONE 0 #define SING_HEAD 1 #define SING_TAIL 2 typedef struct { c3_y sat_y; u3_noun a; u3_noun b; } eqframe; /* _cr_sing_push(): push a new stack frame, initialized as SING_NONE. */ static inline eqframe* _cr_sing_push(u3a_pile* pil_u, u3_noun a, u3_noun b) { eqframe* fam_u = u3a_push(pil_u); fam_u->sat_y = SING_NONE; fam_u->a = a; fam_u->b = b; return fam_u; } /* _cr_sing_mug(): short-circuit comparison if mugs are present and not equal. */ static inline c3_o _cr_sing_mug(u3a_noun* a_u, u3a_noun* b_u) { // XX add debug assertions that both mugs are 31-bit // (ie, not u3a_take() relocation references) // if ( a_u->mug_w && b_u->mug_w && (a_u->mug_w != b_u->mug_w) ) { return c3n; } return c3y; } /* _cr_sing_atom(): check if atom [a] is indirect and equal to noun [b] */ static inline c3_o _cr_sing_atom(u3_atom a, u3_noun b) { // [a] is an atom, not pointer-equal to noun [b]. // if they're not both indirect atoms, they can't be equal. // if ( (c3n == u3a_is_pug(a)) || (c3n == u3a_is_pug(b)) ) { return c3n; } else { u3a_atom* a_u = u3a_to_ptr(a); u3a_atom* b_u = u3a_to_ptr(b); // [a] and [b] are not equal if their mugs are present and not equal. // if ( c3n == _cr_sing_mug((u3a_noun*)a_u, (u3a_noun*)b_u) ) { return c3n; } else { c3_w a_w = a_u->len_w; c3_w b_w = b_u->len_w; // [a] and [b] are not equal if their lengths are not equal // if ( a_w != b_w ) { return c3n; } else { c3_w i_w; // XX memcmp // for ( i_w = 0; i_w < a_w; i_w++ ) { if ( a_u->buf_w[i_w] != b_u->buf_w[i_w] ) { return c3n; } } } } } return c3y; } /* _cr_sing_cape_test(): check for previous comparison of [a] and [b]. */ static inline c3_o _cr_sing_cape_test(u3p(u3h_root) har_p, u3_noun a, u3_noun b) { u3_noun key = u3nc(u3a_to_off(a) >> u3a_vits, u3a_to_off(b) >> u3a_vits); u3_noun val; u3t_off(euq_o); val = u3h_git(har_p, key); u3t_on(euq_o); u3z(key); return ( u3_none != val ) ? c3y : c3n; } /* _cr_sing_cape_keep(): store [a] and [b] to short-circuit subsequent tests. ** NB: [a] and [b] (which MUST be equal nouns) ** are cons'd as offsets (direct atoms) to avoid refcount churn. */ static inline void _cr_sing_cape_keep(u3p(u3h_root) har_p, u3_noun a, u3_noun b) { // only store if [a] and [b] are copies of each other // if ( a != b ) { c3_dessert( (c3n == u3a_is_cat(a)) && (c3n == u3a_is_cat(b)) ); u3_noun key = u3nc(u3a_to_off(a) >> u3a_vits, u3a_to_off(b) >> u3a_vits); u3t_off(euq_o); u3h_put(har_p, key, c3y); u3t_on(euq_o); u3z(key); } } static inline __attribute__((always_inline)) void _cr_sing_wed(u3_noun *restrict a, u3_noun *restrict b) { if ( *a != *b ) { u3a_wed(a, b); } } /* _cr_sing_cape(): unifying equality with comparison deduplication * (tightly coupled to _cr_sing) */ static c3_o _cr_sing_cape(u3a_pile* pil_u, u3p(u3h_root) har_p) { eqframe* fam_u = u3a_peek(pil_u); u3_noun a, b; u3a_cell* a_u; u3a_cell* b_u; // loop while arguments remain on the stack // do { a = fam_u->a; b = fam_u->b; switch ( fam_u->sat_y ) { // [a] and [b] are arbitrary nouns // case SING_NONE: { if ( a == b ) { break; } else if ( c3y == u3a_is_atom(a) ) { if ( c3n == _cr_sing_atom(a, b) ) { return c3n; } else { break; } } else if ( c3y == u3a_is_atom(b) ) { return c3n; } // [a] and [b] are cells // else { a_u = u3a_to_ptr(a); b_u = u3a_to_ptr(b); // short-circuiting mug check // if ( c3n == _cr_sing_mug((u3a_noun*)a_u, (u3a_noun*)b_u) ) { return c3n; } // short-circuiting re-comparison check // else if ( c3y == _cr_sing_cape_test(har_p, a, b) ) { fam_u = u3a_pop(pil_u); continue; } // upgrade none-frame to head-frame, check heads // else { fam_u->sat_y = SING_HEAD; fam_u = _cr_sing_push(pil_u, a_u->hed, b_u->hed); continue; } } } break; // cells [a] and [b] have equal heads // case SING_HEAD: { a_u = u3a_to_ptr(a); b_u = u3a_to_ptr(b); _cr_sing_wed(&(a_u->hed), &(b_u->hed)); // upgrade head-frame to tail-frame, check tails // fam_u->sat_y = SING_TAIL; fam_u = _cr_sing_push(pil_u, a_u->tel, b_u->tel); continue; } // cells [a] and [b] are equal // case SING_TAIL: { a_u = u3a_to_ptr(a); b_u = u3a_to_ptr(b); _cr_sing_wed(&(a_u->tel), &(b_u->tel)); } break; default: { u3_assert(0); } break; } // track equal pairs to short-circuit possible (re-)comparison // _cr_sing_cape_keep(har_p, a, b); fam_u = u3a_pop(pil_u); } while ( c3n == u3a_pile_done(pil_u) ); return c3y; } /* _cr_sing(): unifying equality. */ static c3_o _cr_sing(u3_noun a, u3_noun b) { c3_s ovr_s = 0; u3a_cell* a_u; u3a_cell* b_u; eqframe* fam_u; u3a_pile pil_u; // initialize stack control, push arguments onto the stack (none-frame) // u3a_pile_prep(&pil_u, sizeof(eqframe)); fam_u = _cr_sing_push(&pil_u, a, b); // loop while arguments are on the stack // while ( c3n == u3a_pile_done(&pil_u) ) { a = fam_u->a; b = fam_u->b; switch ( fam_u->sat_y ) { // [a] and [b] are arbitrary nouns // case SING_NONE: { if ( a == b ) { break; } else if ( c3y == u3a_is_atom(a) ) { if ( c3n == _cr_sing_atom(a, b) ) { u3R->cap_p = pil_u.top_p; return c3n; } else { break; } } else if ( c3y == u3a_is_atom(b) ) { u3R->cap_p = pil_u.top_p; return c3n; } // [a] and [b] are cells // else { a_u = u3a_to_ptr(a); b_u = u3a_to_ptr(b); // short-circuiting mug check // if ( c3n == _cr_sing_mug((u3a_noun*)a_u, (u3a_noun*)b_u) ) { u3R->cap_p = pil_u.top_p; return c3n; } // upgrade none-frame to head-frame, check heads // else { fam_u->sat_y = SING_HEAD; fam_u = _cr_sing_push(&pil_u, a_u->hed, b_u->hed); continue; } } } break; // cells [a] and [b] have equal heads // case SING_HEAD: { a_u = u3a_to_ptr(a); b_u = u3a_to_ptr(b); _cr_sing_wed(&(a_u->hed), &(b_u->hed)); // upgrade head-frame to tail-frame, check tails // fam_u->sat_y = SING_TAIL; fam_u = _cr_sing_push(&pil_u, a_u->tel, b_u->tel); continue; } // cells [a] and [b] are equal // case SING_TAIL: { a_u = u3a_to_ptr(a); b_u = u3a_to_ptr(b); _cr_sing_wed(&(a_u->tel), &(b_u->tel)); } break; default: { u3_assert(0); } break; } // [ovr_s] counts comparisons, if it overflows, we've likely hit // a pathological case (highly duplicated tree), so we de-duplicate // subsequent comparisons by maintaining a set of equal pairs. // if ( 0 == ++ovr_s ) { u3p(u3h_root) har_p = u3h_new(); c3_o ret_o = _cr_sing_cape(&pil_u, har_p); u3h_free(har_p); u3R->cap_p = pil_u.top_p; return ret_o; } fam_u = u3a_pop(&pil_u); } return c3y; } /* u3r_sing(): Yes iff [a] and [b] are the same noun. */ c3_o u3r_sing(u3_noun a, u3_noun b) { c3_o ret_o; u3t_on(euq_o); ret_o = _cr_sing(a, b); u3t_off(euq_o); return ret_o; } c3_o u3r_fing(u3_noun a, u3_noun b) { return (a == b) ? c3y : c3n; } /* u3r_sing_cell(): ** ** Yes iff `[p q]` and `b` are the same noun. */ c3_o u3r_sing_cell(u3_noun p, u3_noun q, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_sing(p, u3a_h(b)), u3r_sing(q, u3a_t(b)))); } c3_o u3r_fing_cell(u3_noun p, u3_noun q, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_fing(p, u3a_h(b)), u3r_fing(q, u3a_t(b)))); } /* u3r_sing_mixt(): ** ** Yes iff `[p q]` and `b` are the same noun. */ c3_o u3r_sing_mixt(const c3_c* p_c, u3_noun q, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_sing_c(p_c, u3a_h(b)), u3r_sing(q, u3a_t(b)))); } c3_o u3r_fing_mixt(const c3_c* p_c, u3_noun q, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_sing_c(p_c, u3a_h(b)), u3r_fing(q, u3a_t(b)))); } /* u3r_sing_trel(): ** ** Yes iff `[p q r]` and `b` are the same noun. */ c3_o u3r_sing_trel(u3_noun p, u3_noun q, u3_noun r, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_sing(p, u3a_h(b)), u3r_sing_cell(q, r, u3a_t(b)))); } c3_o u3r_fing_trel(u3_noun p, u3_noun q, u3_noun r, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_fing(p, u3a_h(b)), u3r_fing_cell(q, r, u3a_t(b)))); } /* u3r_sing_qual(): ** ** Yes iff `[p q r]` and `b` are the same noun. */ c3_o u3r_sing_qual(u3_noun p, u3_noun q, u3_noun r, u3_noun s, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_sing(p, u3a_h(b)), u3r_sing_trel(q, r, s, u3a_t(b)))); } c3_o u3r_fing_qual(u3_noun p, u3_noun q, u3_noun r, u3_noun s, u3_noun b) { return c3a(_(u3a_is_cell(b)), c3a(u3r_fing(p, u3a_h(b)), u3r_fing_trel(q, r, s, u3a_t(b)))); } /* u3r_nord(): ** ** Return 0, 1 or 2 if `a` is below, equal to, or above `b`. */ u3_atom u3r_nord(u3_noun a, u3_noun b) { u3_assert(u3_none != a); u3_assert(u3_none != b); if ( a == b ) { return 1; } else { if ( _(u3a_is_atom(a)) ) { if ( !_(u3a_is_atom(b)) ) { return 0; } else { if ( _(u3a_is_cat(a)) ) { if ( _(u3a_is_cat(b)) ) { return (a < b) ? 0 : 2; } else return 0; } else if ( _(u3a_is_cat(b)) ) { return 2; } else { u3a_atom* a_u = u3a_to_ptr(a); u3a_atom* b_u = u3a_to_ptr(b); c3_w w_rez = a_u->len_w; c3_w w_mox = b_u->len_w; if ( w_rez != w_mox ) { return (w_rez < w_mox) ? 0 : 2; } else { c3_w i_w; for ( i_w = 0; i_w < w_rez; i_w++ ) { c3_w ai_w = a_u->buf_w[i_w]; c3_w bi_w = b_u->buf_w[i_w]; if ( ai_w != bi_w ) { return (ai_w < bi_w) ? 0 : 2; } } return 1; } } } } else { if ( _(u3a_is_atom(b)) ) { return 2; } else { u3_atom c = u3r_nord(u3a_h(a), u3a_h(b)); if ( 1 == c ) { return u3r_nord(u3a_t(a), u3a_t(b)); } else { return c; } } } } } /* u3r_sing_c(): cord/C-string value equivalence. */ c3_o u3r_sing_c(const c3_c* a_c, u3_noun b) { u3_assert(u3_none != b); if ( !_(u3a_is_atom(b)) ) { return c3n; } else { c3_w w_sof = strlen(a_c); c3_w i_w; if ( w_sof != u3r_met(3, b) ) { return c3n; } for ( i_w = 0; i_w < w_sof; i_w++ ) { if ( u3r_byte(i_w, b) != a_c[i_w] ) { return c3n; } } return c3y; } } /* u3r_bush(): ** ** Factor [a] as a bush [b.[p q] c]. */ c3_o u3r_bush(u3_noun a, u3_noun* b, u3_noun* c) { u3_assert(u3_none != a); if ( _(u3a_is_atom(a)) ) { return c3n; } else { *b = u3a_h(a); if ( _(u3a_is_atom(*b)) ) { return c3n; } else { *c = u3a_t(a); return c3y; } } } /* u3r_bite(): retrieve/default $bloq and $step from $bite. */ c3_o u3r_bite(u3_noun bite, u3_atom* bloq, u3_atom *step) { u3_noun hed, tal; if ( c3n == u3r_cell(bite, &hed, &tal) ) { *bloq = bite; *step = 1; return c3y; } else if ( (c3n == u3a_is_atom(hed)) || (c3n == u3a_is_atom(tal)) ) { return c3n; } else { *bloq = hed; *step = tal; return c3y; } } /* u3r_p(): ** ** & [0] if [a] is of the form [b *c]. */ c3_o u3r_p(u3_noun a, u3_noun b, u3_noun* c) { u3_noun feg, nux; if ( (c3y == u3r_cell(a, &feg, &nux)) && (c3y == u3r_sing(feg, b)) ) { if ( c ) *c = nux; return c3y; } else return c3n; } /* u3r_pq(): ** ** & [0] if [a] is of the form [b *c d]. */ c3_o u3r_pq(u3_noun a, u3_noun b, u3_noun* c, u3_noun* d) { u3_noun nux; if ( (c3y == u3r_p(a, b, &nux)) && (c3y == u3r_cell(nux, c, d)) ) { return c3y; } else return c3n; } /* u3r_pqr(): ** ** & [0] if [a] is of the form [b *c *d *e]. */ c3_o u3r_pqr(u3_noun a, u3_noun b, u3_noun* c, u3_noun* d, u3_noun* e) { u3_noun nux; if ( (c3y == u3r_p(a, b, &nux)) && (c3y == u3r_trel(nux, c, d, e)) ) { return c3y; } else return c3n; } /* u3r_pqrs(): ** ** & [0] if [a] is of the form [b *c *d *e *f]. */ c3_o u3r_pqrs(u3_noun a, u3_noun b, u3_noun* c, u3_noun* d, u3_noun* e, u3_noun* f) { u3_noun nux; if ( (c3y == u3r_p(a, b, &nux)) && (c3y == u3r_qual(nux, c, d, e, f)) ) { return c3y; } else return c3n; } /* u3r_met(): ** ** Return the size of (b) in bits, rounded up to ** (1 << a_y). ** ** For example, (a_y == 3) returns the size in bytes. ** NB: (a_y) must be < 37. */ c3_w u3r_met(c3_y a_y, u3_atom b) { c3_dessert(u3_none != b); c3_dessert(_(u3a_is_atom(b))); if ( b == 0 ) { return 0; } /* gal_w: number of words besides (daz_w) in (b). ** daz_w: top word in (b). */ c3_w gal_w; c3_w daz_w; if ( _(u3a_is_cat(b)) ) { gal_w = 0; daz_w = b; } else { u3a_atom* b_u = u3a_to_ptr(b); gal_w = (b_u->len_w) - 1; daz_w = b_u->buf_w[gal_w]; } /* 5 because 1<<2 bytes in c3_w, 1<<3 bits in byte. aka log2(CHAR_BIT * sizeof gal_w) a_y < 5 informs whether we shift return left or right */ if (a_y < 5) { c3_y max_y = (1 << a_y) - 1; c3_y gow_y = 5 - a_y; if (gal_w > ((UINT32_MAX - (32 + max_y)) >> gow_y)) return u3m_bail(c3__fail); return (gal_w << gow_y) + ((c3_bits_word(daz_w) + max_y) >> a_y); } c3_y gow_y = (a_y - 5); return ((gal_w + 1) + ((1 << gow_y) - 1)) >> gow_y; } /* u3r_bit(): ** ** Return bit (a_w) of (b). */ c3_b u3r_bit(c3_w a_w, u3_atom b) { u3_assert(u3_none != b); u3_assert(_(u3a_is_atom(b))); if ( _(u3a_is_cat(b)) ) { if ( a_w >= 31 ) { return 0; } else return (1 & (b >> a_w)); } else { u3a_atom* b_u = u3a_to_ptr(b); c3_y vut_y = (a_w & 31); c3_w pix_w = (a_w >> 5); if ( pix_w >= b_u->len_w ) { return 0; } else { c3_w nys_w = b_u->buf_w[pix_w]; return (1 & (nys_w >> vut_y)); } } } /* u3r_byte(): ** ** Return byte (a_w) of (b). */ c3_y u3r_byte(c3_w a_w, u3_atom b) { u3_assert(u3_none != b); u3_assert(_(u3a_is_atom(b))); if ( _(u3a_is_cat(b)) ) { if ( a_w > 3 ) { return 0; } else return (255 & (b >> (a_w << 3))); } else { u3a_atom* b_u = u3a_to_ptr(b); c3_y vut_y = (a_w & 3); c3_w pix_w = (a_w >> 2); if ( pix_w >= b_u->len_w ) { return 0; } else { c3_w nys_w = b_u->buf_w[pix_w]; return (255 & (nys_w >> (vut_y << 3))); } } } /* u3r_bytes(): ** ** Copy bytes (a_w) through (a_w + b_w - 1) from (d) to (c). */ void u3r_bytes(c3_w a_w, c3_w b_w, c3_y* c_y, u3_atom d) { u3_assert(u3_none != d); u3_assert(_(u3a_is_atom(d))); if ( _(u3a_is_cat(d)) ) { c3_w e_w = d >> (c3_min(a_w, 4) << 3); c3_w m_w = c3_min(b_w, 4); memcpy(c_y, (c3_y*)&e_w, m_w); if ( b_w > 4 ) { memset(c_y + 4, 0, b_w - 4); } } else { u3a_atom* d_u = u3a_to_ptr(d); c3_w n_w = d_u->len_w << 2; c3_y* x_y = (c3_y*)d_u->buf_w + a_w; if ( a_w >= n_w ) { memset(c_y, 0, b_w); } else { c3_w z_w = c3_min(b_w, n_w - a_w); memcpy(c_y, x_y, z_w); if ( b_w > n_w - a_w ) { memset(c_y + z_w, 0, b_w + a_w - n_w); } } } } /* u3r_bytes_fit(): ** ** Copy (len_w) bytes of (a) into (buf_y) if it fits, returning overage */ c3_w u3r_bytes_fit(c3_w len_w, c3_y *buf_y, u3_atom a) { c3_w met_w = u3r_met(3, a); if ( met_w <= len_w ) { u3r_bytes(0, len_w, buf_y, a); return 0; } else { return len_w - met_w; } } /* u3r_bytes_alloc(): ** ** Copy (len_w) bytes starting at (a_w) from (b) into a fresh allocation. */ c3_y* u3r_bytes_alloc(c3_w a_w, c3_w len_w, u3_atom b) { c3_y* b_y = u3a_malloc(len_w); u3r_bytes(a_w, a_w + len_w, b_y, b); return b_y; } /* u3r_bytes_all(): ** ** Allocate and return a new byte array with all the bytes of (a), ** storing the length in (len_w). */ c3_y* u3r_bytes_all(c3_w* len_w, u3_atom a) { c3_w met_w = *len_w = u3r_met(3, a); return u3r_bytes_alloc(0, met_w, a); } /* u3r_mp(): ** ** Copy (b) into (a_mp). */ void u3r_mp(mpz_t a_mp, u3_atom b) { u3_assert(u3_none != b); u3_assert(_(u3a_is_atom(b))); if ( _(u3a_is_cat(b)) ) { mpz_init_set_ui(a_mp, b); } else { u3a_atom* b_u = u3a_to_ptr(b); c3_w len_w = b_u->len_w; c3_d bit_d = (c3_d)len_w << 5; // avoid reallocation on import, if possible // mpz_init2(a_mp, (c3_w)c3_min(bit_d, UINT32_MAX)); mpz_import(a_mp, len_w, -1, sizeof(c3_w), 0, 0, b_u->buf_w); } } /* u3r_short(): ** ** Return short (a_w) of (b). */ c3_s u3r_short(c3_w a_w, u3_atom b) { u3_assert( u3_none != b ); u3_assert( c3y == u3a_is_atom(b) ); if ( c3y == u3a_is_cat(b) ) { switch ( a_w ) { case 0: return b & 0xffff; case 1: return b >> 16; default: return 0; } } else { u3a_atom* b_u = u3a_to_ptr(b); c3_w nix_w = a_w >> 1; if ( nix_w >= b_u->len_w ) { return 0; } else { c3_w wor_w = b_u->buf_w[nix_w]; return ( a_w & 1 ) ? (wor_w >> 16) : (wor_w & 0xffff); } } } /* u3r_word(): ** ** Return word (a_w) of (b). */ c3_w u3r_word(c3_w a_w, u3_atom b) { u3_assert(u3_none != b); u3_assert(_(u3a_is_atom(b))); if ( _(u3a_is_cat(b)) ) { if ( a_w > 0 ) { return 0; } else return b; } else { u3a_atom* b_u = u3a_to_ptr(b); if ( a_w >= b_u->len_w ) { return 0; } else return b_u->buf_w[a_w]; } } /* u3r_word_fit(): ** ** Fill (out_w) with (a) if it fits, returning success. */ c3_t u3r_word_fit(c3_w *out_w, u3_atom a) { if ( u3r_met(5, a) > 1 ) { return 0; } else { *out_w = u3r_word(0, a); return 1; } } /* u3r_chub(): ** ** Return double-word (a_w) of (b). */ c3_d u3r_chub(c3_w a_w, u3_atom b) { c3_w wlo_w = u3r_word(a_w * 2, b); c3_w whi_w = u3r_word(1 + (a_w * 2), b); return (((uint64_t)whi_w) << 32ULL) | ((uint64_t)wlo_w); } /* u3r_words(): ** ** Copy words (a_w) through (a_w + b_w - 1) from (d) to (c). */ void u3r_words(c3_w a_w, c3_w b_w, c3_w* c_w, u3_atom d) { u3_assert(u3_none != d); u3_assert(_(u3a_is_atom(d))); if ( b_w == 0 ) { return; } if ( _(u3a_is_cat(d)) ) { if ( a_w == 0 ) { *c_w = d; memset((c3_y*)(c_w + 1), 0, (b_w - 1) << 2); } else { memset((c3_y*)c_w, 0, b_w << 2); } } else { u3a_atom* d_u = u3a_to_ptr(d); if ( a_w >= d_u->len_w ) { memset((c3_y*)c_w, 0, b_w << 2); } else { c3_w z_w = c3_min(b_w, d_u->len_w - a_w); c3_w* x_w = d_u->buf_w + a_w; memcpy((c3_y*)c_w, (c3_y*)x_w, z_w << 2); if ( b_w > d_u->len_w - a_w ) { memset((c3_y*)(c_w + z_w), 0, (b_w + a_w - d_u->len_w) << 2); } } } } /* u3r_chubs(): ** ** Copy double-words (a_w) through (a_w + b_w - 1) from (d) to (c). */ void u3r_chubs(c3_w a_w, c3_w b_w, c3_d* c_d, u3_atom d) { /* XX: assumes little-endian */ u3r_words(a_w * 2, b_w * 2, (c3_w *)c_d, d); } /* u3r_safe_byte(): validate and retrieve byte. */ c3_o u3r_safe_byte(u3_noun dat, c3_y* out_y) { if ( (c3n == u3a_is_atom(dat)) || (1 < u3r_met(3, dat)) ) { return c3n; } *out_y = u3r_byte(0, dat); return c3y; } /* u3r_safe_word(): validate and retrieve word. */ c3_o u3r_safe_word(u3_noun dat, c3_w* out_w) { if ( (c3n == u3a_is_atom(dat)) || (1 < u3r_met(5, dat)) ) { return c3n; } *out_w = u3r_word(0, dat); return c3y; } /* u3r_safe_chub(): validate and retrieve chub. */ c3_o u3r_safe_chub(u3_noun dat, c3_d* out_d) { if ( (c3n == u3a_is_atom(dat)) || (1 < u3r_met(6, dat)) ) { return c3n; } *out_d = u3r_chub(0, dat); return c3y; } /* u3r_chop_bits(): ** ** XOR `wid_d` bits from`src_w` at `bif_g` to `dst_w` at `bif_g` ** ** NB: [dst_w] must have space for [bit_g + wid_d] bits */ void u3r_chop_bits(c3_g bif_g, c3_d wid_d, c3_g bit_g, c3_w* dst_w, const c3_w* src_w) { c3_y fib_y = 32 - bif_g; c3_y tib_y = 32 - bit_g; // we need to chop words // if ( wid_d >= tib_y ) { // align *dst_w // if ( bit_g ) { c3_w low_w = src_w[0] >> bif_g; if ( bif_g > bit_g ) { low_w ^= src_w[1] << fib_y; } *dst_w++ ^= low_w << bit_g; wid_d -= tib_y; bif_g += tib_y; src_w += !!(bif_g >> 5); bif_g &= 31; fib_y = 32 - bif_g; } { size_t i_i, byt_i = wid_d >> 5; if ( !bif_g ) { for ( i_i = 0; i_i < byt_i; i_i++ ) { dst_w[i_i] ^= src_w[i_i]; } } else { for ( i_i = 0; i_i < byt_i; i_i++ ) { dst_w[i_i] ^= (src_w[i_i] >> bif_g) ^ (src_w[i_i + 1] << fib_y); } } src_w += byt_i; dst_w += byt_i; wid_d &= 31; bit_g = 0; } } // we need to chop (more) bits // if ( wid_d ) { c3_w hig_w = src_w[0] >> bif_g; if ( wid_d > fib_y ) { hig_w ^= src_w[1] << fib_y; } *dst_w ^= (hig_w & (((c3_d)1 << wid_d) - 1)) << bit_g; } } /* u3r_chop_words(): ** ** Into the bloq space of `met`, from position `fum` for a ** span of `wid`, to position `tou`, XOR from `src_w` ** into `dst_w`. ** ** NB: [dst_w] must have space for [tou_w + wid_w] bloqs */ void u3r_chop_words(c3_g met_g, c3_w fum_w, c3_w wid_w, c3_w tou_w, c3_w* dst_w, c3_w len_w, const c3_w* src_w) { // operate on words // if ( met_g >= 5 ) { size_t i_i, wid_i; { c3_g hut_g = met_g - 5; size_t fum_i = (size_t)fum_w << hut_g; size_t tou_i = (size_t)tou_w << hut_g; size_t tot_i; wid_i = (size_t)wid_w << hut_g; tot_i = fum_i + wid_i; // since [dst_w] must have space for (tou_w + wid_w) bloqs, // neither conversion can overflow // if ( (fum_i >> hut_g != fum_w) || (tot_i - wid_i != fum_i) ) { u3m_bail(c3__fail); return; } else if ( fum_i >= len_w ) { return; } if ( tot_i > len_w ) { wid_i -= tot_i - len_w; } src_w += fum_i; dst_w += tou_i; } for ( i_i = 0; i_i < wid_i; i_i++ ) { dst_w[i_i] ^= src_w[i_i]; } } // operate on bits // else { c3_d wid_d = (c3_d)wid_w << met_g; c3_g bif_g, bit_g; { c3_d len_d = (c3_d)len_w << 5; c3_d fum_d = (c3_d)fum_w << met_g; c3_d tou_d = (c3_d)tou_w << met_g; c3_d tot_d = fum_d + wid_d; // see above // if ( (fum_d >> met_g != fum_w) || (tot_d - wid_d != fum_d) ) { u3m_bail(c3__fail); return; } else if ( fum_d > len_d ) { return; } if ( tot_d > len_d ) { wid_d -= tot_d - len_d; } src_w += fum_d >> 5; dst_w += tou_d >> 5; bif_g = fum_d & 31; bit_g = tou_d & 31; } u3r_chop_bits(bif_g, wid_d, bit_g, dst_w, src_w); } } /* u3r_chop(): ** ** Into the bloq space of `met`, from position `fum` for a ** span of `wid`, to position `tou`, XOR from atom `src` ** into `dst_w`. ** ** NB: [dst_w] must have space for [tou_w + wid_w] bloqs */ void u3r_chop(c3_g met_g, c3_w fum_w, c3_w wid_w, c3_w tou_w, c3_w* dst_w, u3_atom src) { c3_w* src_w; c3_w len_w; if ( _(u3a_is_cat(src)) ) { len_w = src ? 1 : 0; src_w = &src; } else { u3a_atom* src_u = u3a_to_ptr(src); u3_assert(u3_none != src); u3_assert(_(u3a_is_atom(src))); len_w = src_u->len_w; src_w = src_u->buf_w; } u3r_chop_words(met_g, fum_w, wid_w, tou_w, dst_w, len_w, src_w); } /* u3r_string(): `a` as malloced C string. */ c3_c* u3r_string(u3_atom a) { c3_w met_w = u3r_met(3, a); c3_c* str_c = c3_malloc(met_w + 1); u3r_bytes(0, met_w, (c3_y*)str_c, a); str_c[met_w] = 0; return str_c; } /* u3r_tape(): `a`, a list of bytes, as malloced C string. */ c3_y* u3r_tape(u3_noun a) { u3_noun b; c3_w i_w; c3_y *a_y; for ( i_w = 0, b=a; c3y == u3a_is_cell(b); i_w++, b=u3a_t(b) ) ; a_y = c3_malloc(i_w + 1); for ( i_w = 0, b=a; c3y == u3a_is_cell(b); i_w++, b=u3a_t(b) ) { a_y[i_w] = u3a_h(b); } a_y[i_w] = 0; return a_y; } /* u3r_mug_both(): Join two mugs. */ c3_l u3r_mug_both(c3_l lef_l, c3_l rit_l) { c3_y len_y = 4 + ((c3_bits_word(rit_l) + 0x7) >> 3); c3_w syd_w = 0xdeadbeef; c3_w i_w = 0; c3_y buf_y[8]; buf_y[0] = lef_l & 0xff; buf_y[1] = (lef_l >> 8) & 0xff; buf_y[2] = (lef_l >> 16) & 0xff; buf_y[3] = (lef_l >> 24) & 0xff; buf_y[4] = rit_l & 0xff; buf_y[5] = (rit_l >> 8) & 0xff; buf_y[6] = (rit_l >> 16) & 0xff; buf_y[7] = (rit_l >> 24) & 0xff; while ( i_w < 8 ) { c3_w haz_w; c3_l ham_l; MurmurHash3_x86_32(buf_y, len_y, syd_w, &haz_w); ham_l = (haz_w >> 31) ^ (haz_w & 0x7fffffff); if ( 0 == ham_l ) { syd_w++; i_w++; } else { return ham_l; } } return 0xfffe; } /* u3r_mug_bytes(): Compute the mug of `buf`, `len`, LSW first. */ c3_l u3r_mug_bytes(const c3_y *buf_y, c3_w len_w) { c3_w syd_w = 0xcafebabe; c3_w i_w = 0; while ( i_w < 8 ) { c3_w haz_w; c3_l ham_l; MurmurHash3_x86_32(buf_y, len_w, syd_w, &haz_w); ham_l = (haz_w >> 31) ^ (haz_w & 0x7fffffff); if ( 0 == ham_l ) { syd_w++; i_w++; } else { return ham_l; } } return 0x7fff; } /* u3r_mug_c(): Compute the mug of `a`, LSB first. */ c3_l u3r_mug_c(const c3_c* a_c) { return u3r_mug_bytes((c3_y*)a_c, strlen(a_c)); } /* u3r_mug_cell(): Compute the mug of the cell `[hed tel]`. */ c3_l u3r_mug_cell(u3_noun hed, u3_noun tel) { c3_w lus_w = u3r_mug(hed); c3_w biq_w = u3r_mug(tel); return u3r_mug_both(lus_w, biq_w); } /* u3r_mug_chub(): Compute the mug of `num`, LSW first. */ c3_l u3r_mug_chub(c3_d num_d) { c3_w buf_w[2]; buf_w[0] = (c3_w)(num_d & 0xffffffffULL); buf_w[1] = (c3_w)(num_d >> 32); return u3r_mug_words(buf_w, 2); } /* u3r_mug_words(): 31-bit nonzero MurmurHash3 on raw words. */ c3_l u3r_mug_words(const c3_w* key_w, c3_w len_w) { c3_w byt_w; // ignore trailing zeros // while ( len_w && !key_w[len_w - 1] ) { len_w--; } // calculate byte-width a la u3r_met(3, ...) // if ( !len_w ) { byt_w = 0; } else { c3_w gal_w = len_w - 1; c3_w daz_w = key_w[gal_w]; byt_w = (gal_w << 2) + ((c3_bits_word(daz_w) + 7) >> 3); } // XX: assumes little-endian // return u3r_mug_bytes((c3_y*)key_w, byt_w); } /* _cr_mug: stack frame for recording cell traversal ** !mug == head-frame */ typedef struct { c3_l mug_l; u3_cell cel; } _cr_mugf; /* _cr_mug_next(): advance mug calculation, pushing cells onto the stack. */ static inline c3_l _cr_mug_next(u3a_pile* pil_u, u3_noun veb) { while ( 1 ) { // veb is a direct atom, mug is not memoized // if ( c3y == u3a_is_cat(veb) ) { return (c3_l)u3r_mug_words(&veb, 1); } // veb is indirect, a pointer into the loom // else { u3a_noun* veb_u = u3a_to_ptr(veb); // veb has already been mugged, return memoized value // // XX add debug assertion that mug is 31-bit? // if ( veb_u->mug_w ) { return (c3_l)veb_u->mug_w; } // veb is an indirect atom, mug its bytes and memoize // else if ( c3y == u3a_is_atom(veb) ) { u3a_atom* vat_u = (u3a_atom*)veb_u; c3_l mug_l = u3r_mug_words(vat_u->buf_w, vat_u->len_w); vat_u->mug_w = mug_l; return mug_l; } // veb is a cell, push a stack frame to mark head-recursion // and read the head // else { u3a_cell* cel_u = (u3a_cell*)veb_u; _cr_mugf* fam_u = u3a_push(pil_u); fam_u->mug_l = 0; fam_u->cel = veb; veb = cel_u->hed; continue; } } } } /* u3r_mug(): statefully mug a noun with 31-bit murmur3. */ c3_l u3r_mug(u3_noun veb) { u3a_pile pil_u; _cr_mugf* fam_u; c3_l mug_l; // sanity check // u3_assert( u3_none != veb ); u3a_pile_prep(&pil_u, sizeof(*fam_u)); // commence mugging // mug_l = _cr_mug_next(&pil_u, veb); // process cell results // if ( c3n == u3a_pile_done(&pil_u) ) { fam_u = u3a_peek(&pil_u); do { // head-frame: stash mug and continue into the tail // if ( !fam_u->mug_l ) { u3a_cell* cel_u = u3a_to_ptr(fam_u->cel); fam_u->mug_l = mug_l; mug_l = _cr_mug_next(&pil_u, cel_u->tel); fam_u = u3a_peek(&pil_u); } // tail-frame: calculate/memoize cell mug and pop the stack // else { u3a_cell* cel_u = u3a_to_ptr(fam_u->cel); mug_l = u3r_mug_both(fam_u->mug_l, mug_l); cel_u->mug_w = mug_l; fam_u = u3a_pop(&pil_u); } } while ( c3n == u3a_pile_done(&pil_u) ); } return mug_l; } /* u3r_skip(): ** ** Extract a constant from a formula, ignoring ** safe/static hints, doing no computation. */ u3_weak u3r_skip(u3_noun fol) { while ( c3y == u3du(fol) ) { switch ( u3h(fol) ) { default: return u3_none; case 1: return u3t(fol); case 11: { u3_noun arg = u3t(fol), hod = u3h(arg); if ( (c3y == u3du(hod)) && (u3_none == u3r_skip(u3t(hod))) ) { return u3_none; } fol = u3t(arg); } } } return u3_none; } /* u3r_safe(): ** ** Returns yes if the formula won't crash ** and has no hints, returning constant result ** if possible. *out is undefined if the return ** is c3n */ c3_o u3r_safe(u3_noun fol, u3_weak* out) { u3_noun h_fol, t_fol; c3_o saf_o; if ( c3n == u3r_cell(fol, &h_fol, &t_fol) ) { return c3n; } switch ( h_fol ) { default: return c3n; case 0: *out = u3_none; return __(1 == t_fol); case 1: *out = t_fol; return c3y; case 3: { u3_weak o; saf_o = u3r_safe(t_fol, &o); if ( _(saf_o) ) { *out = (u3_none == o) ? u3_none : u3du(o); } return saf_o; } case 5: { u3_noun p, q; u3_weak o1, o2; saf_o = c3a(u3r_cell(t_fol, &p, &q), c3a(u3r_safe(p, &o1), u3r_safe(q, &o2))); if ( _(saf_o) ) { *out = (u3_none == o1) ? u3_none : (u3_none == o2) ? u3_none : u3r_sing(o1, o2); } return saf_o; } case 6: { u3_noun p, q, r; u3_weak o; saf_o = c3a(u3r_trel(t_fol, &p, &q, &r), u3r_safe(p, &o)); if ( _(saf_o) ) { switch ( o ) { case c3y: return u3r_safe(q, out); case c3n: return u3r_safe(r, out); default: return c3n; } } else { return c3n; } } case 7: case 8: { u3_noun p, q; u3_weak o; return c3a(u3r_cell(t_fol, &p, &q), c3a(u3r_safe(p, &o), u3r_safe(q, out))); } } }