mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 10:02:47 +03:00
Merge pull request #3490 from urbit/jb/moar-pile
u3: use new road-stack api in noun traversals
This commit is contained in:
commit
4415c1c099
@ -81,9 +81,9 @@
|
||||
u3_noun c,
|
||||
u3_noun d);
|
||||
|
||||
/* u3r_mug(): MurmurHash3 on a noun.
|
||||
/* u3r_mug(): statefully mug a noun with 31-bit murmur3.
|
||||
*/
|
||||
c3_w
|
||||
c3_l
|
||||
u3r_mug(u3_noun veb);
|
||||
|
||||
/* u3r_fing():
|
||||
|
@ -1048,170 +1048,27 @@ _me_gain_use(u3_noun dog)
|
||||
}
|
||||
}
|
||||
|
||||
#define TAKE_ROOT 0
|
||||
#define TAKE_HEAD 1
|
||||
#define TAKE_TAIL 2
|
||||
|
||||
#undef VERBOSE_TAKE
|
||||
|
||||
// stack frame for recording head vs tail iteration
|
||||
//
|
||||
// In Hoon, this structure would be as follows:
|
||||
//
|
||||
// $% [%root ~]
|
||||
// [%head old=* new=*]
|
||||
// [%tail old=* new=*]
|
||||
// ==
|
||||
//
|
||||
typedef struct takeframe
|
||||
{
|
||||
c3_y tag_y;
|
||||
u3a_cell* old_u;
|
||||
u3a_cell* new_u;
|
||||
} takeframe;
|
||||
|
||||
static inline void
|
||||
_ca_take_push(c3_ys mov,
|
||||
c3_ys off,
|
||||
c3_y tag_y,
|
||||
u3a_cell* old_u,
|
||||
u3a_cell* new_u)
|
||||
{
|
||||
u3R->cap_p += mov;
|
||||
|
||||
// ensure we haven't overflowed the stack
|
||||
// (off==0 means we're on a north road)
|
||||
//
|
||||
if ( 0 == off ) {
|
||||
if( !(u3R->cap_p > u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( !(u3R->cap_p < u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
|
||||
takeframe* fam_u = u3to(takeframe, u3R->cap_p + off);
|
||||
fam_u->tag_y = tag_y;
|
||||
fam_u->old_u = old_u;
|
||||
fam_u->new_u = new_u;
|
||||
}
|
||||
|
||||
static inline takeframe
|
||||
_ca_take_pop(c3_ys mov, c3_ys off)
|
||||
{
|
||||
takeframe* fam_u = u3to(takeframe, u3R->cap_p + off);
|
||||
u3R->cap_p -= mov;
|
||||
|
||||
return *fam_u;
|
||||
}
|
||||
|
||||
/* u3a_take(): gain, copying juniors.
|
||||
/* _ca_take_atom(): reallocate an indirect atom off the stack.
|
||||
*/
|
||||
u3_noun
|
||||
u3a_take(u3_noun veb)
|
||||
static inline u3_atom
|
||||
_ca_take_atom(u3a_atom* old_u)
|
||||
{
|
||||
c3_assert(u3_none != veb);
|
||||
|
||||
u3t_on(coy_o);
|
||||
|
||||
// initialize signed stack offsets (relative to north/south road)
|
||||
//
|
||||
c3_o nor_o = u3a_is_north(u3R);
|
||||
c3_ys mov, off;
|
||||
{
|
||||
c3_y wis_y = c3_wiseof(takeframe);
|
||||
mov = ( c3y == nor_o ? -wis_y : wis_y );
|
||||
off = ( c3y == nor_o ? 0 : -wis_y );
|
||||
}
|
||||
|
||||
// stash the current stack post
|
||||
//
|
||||
u3p(takeframe) cap_p = u3R->cap_p;
|
||||
|
||||
// push the (only) ROOT stack frame (our termination condition)
|
||||
//
|
||||
_ca_take_push(mov, off, TAKE_ROOT, 0, 0);
|
||||
|
||||
// the finished copy of our current noun .veb
|
||||
//
|
||||
u3_noun pro;
|
||||
|
||||
// read from the current noun .veb
|
||||
//
|
||||
advance: {
|
||||
if ( c3y == u3a_is_cat(veb) ) {
|
||||
pro = veb;
|
||||
goto retreat;
|
||||
}
|
||||
// senior pointers are not refcounted
|
||||
//
|
||||
else if ( c3y == (( c3y == nor_o )
|
||||
? u3a_north_is_senior(u3R, veb)
|
||||
: u3a_south_is_senior(u3R, veb)) )
|
||||
{
|
||||
pro = veb;
|
||||
goto retreat;
|
||||
}
|
||||
// not junior; a normal pointer in our road -- refcounted
|
||||
//
|
||||
else if ( c3n == (( c3y == nor_o )
|
||||
? u3a_north_is_junior(u3R, veb)
|
||||
: u3a_south_is_junior(u3R, veb)) )
|
||||
{
|
||||
// bypass normal road checks in u3k
|
||||
//
|
||||
_me_gain_use(veb);
|
||||
pro = veb;
|
||||
goto retreat;
|
||||
|
||||
|
||||
}
|
||||
// junior pointers are copied
|
||||
//
|
||||
else {
|
||||
u3a_noun* veb_u = u3a_to_ptr(veb);
|
||||
|
||||
// 32-bit mug_w: already copied .veb and .mug_w is the pointer
|
||||
//
|
||||
if ( veb_u->mug_w >> 31 ) {
|
||||
u3_noun nov = (u3_noun)veb_u->mug_w;
|
||||
|
||||
c3_assert( c3y == (( c3y == nor_o)
|
||||
? u3a_north_is_normal(u3R, nov)
|
||||
: u3a_south_is_normal(u3R, nov)) );
|
||||
|
||||
#ifdef VERBOSE_TAKE
|
||||
u3l_log("%s: %p is already %p\r\n", ( c3y == nor_o )
|
||||
? "north"
|
||||
: "south",
|
||||
veb_u,
|
||||
u3a_to_ptr(nov));
|
||||
#endif
|
||||
|
||||
// bypass normal road checks in u3k
|
||||
//
|
||||
_me_gain_use(nov);
|
||||
pro = nov;
|
||||
goto retreat;
|
||||
}
|
||||
else {
|
||||
if ( c3y == u3a_is_atom(veb) ) {
|
||||
u3a_atom* old_u = u3a_to_ptr(veb);
|
||||
c3_w* new_w = u3a_walloc(old_u->len_w + c3_wiseof(u3a_atom));
|
||||
u3a_atom* new_u = (u3a_atom*)(void *)new_w;
|
||||
u3_noun new = u3a_to_pug(u3a_outa(new_u));
|
||||
|
||||
#ifdef VERBOSE_TAKE
|
||||
u3l_log("%s: atom %p to %p\r\n", ( c3y == nor_o )
|
||||
u3l_log("%s: atom %p to %p\r\n", ( c3y == u3a_is_north(u3R) )
|
||||
? "north"
|
||||
: "south",
|
||||
old_u,
|
||||
new_u);
|
||||
#endif
|
||||
|
||||
// XX use memcpy?
|
||||
//
|
||||
new_u->mug_w = old_u->mug_w;
|
||||
new_u->len_w = old_u->len_w;
|
||||
{
|
||||
@ -1222,22 +1079,26 @@ u3a_take(u3_noun veb)
|
||||
}
|
||||
}
|
||||
|
||||
// Borrow mug slot to record new destination in .old_u.
|
||||
// borrow mug slot to record new destination in [old_u]
|
||||
//
|
||||
old_u->mug_w = new;
|
||||
|
||||
pro = new;
|
||||
goto retreat;
|
||||
}
|
||||
else {
|
||||
u3a_cell* old_u = u3a_to_ptr(veb);
|
||||
return new;
|
||||
}
|
||||
|
||||
/* _ca_take_cell(): reallocate a cell off the stack.
|
||||
*/
|
||||
static inline u3_cell
|
||||
_ca_take_cell(u3a_cell* old_u, u3_noun hed, u3_noun tel)
|
||||
{
|
||||
// XX use u3a_celloc?
|
||||
//
|
||||
c3_w* new_w = u3a_walloc(c3_wiseof(u3a_cell));
|
||||
u3a_cell* new_u = (u3a_cell*)(void *)new_w;
|
||||
u3_cell new = u3a_to_pom(u3a_outa(new_u));
|
||||
|
||||
#ifdef VERBOSE_TAKE
|
||||
u3l_log("%s: cell %p to %p\r\n", ( c3y == nor_o )
|
||||
u3l_log("%s: cell %p to %p\r\n", ( c3y == u3a_is_north(u3R) )
|
||||
? "north"
|
||||
: "south",
|
||||
old_u,
|
||||
@ -1245,64 +1106,233 @@ u3a_take(u3_noun veb)
|
||||
#endif
|
||||
|
||||
new_u->mug_w = old_u->mug_w;
|
||||
new_u->hed = hed;
|
||||
new_u->tel = tel;
|
||||
|
||||
// borrow mug slot to record new destination in [old_u]
|
||||
//
|
||||
old_u->mug_w = new;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/* _ca_take: stack frame for recording cell travesal
|
||||
** (u3_none == hed) == head-frame
|
||||
*/
|
||||
typedef struct _ca_take
|
||||
{
|
||||
u3_weak hed; // taken head
|
||||
u3_cell old; // old cell
|
||||
} _ca_take;
|
||||
|
||||
/* _ca_take_next_south: take next noun, pushing cells on stack.
|
||||
*/
|
||||
static inline u3_noun
|
||||
_ca_take_next_north(u3a_pile* pil_u, u3_noun veb)
|
||||
{
|
||||
while ( 1 ) {
|
||||
// direct atoms and senior refs are not counted.
|
||||
//
|
||||
if ( (c3y == u3a_is_cat(veb))
|
||||
|| (c3y == u3a_north_is_senior(u3R, veb)) )
|
||||
{
|
||||
return veb;
|
||||
}
|
||||
// not junior; normal (heap) refs on our road are counted.
|
||||
//
|
||||
else if ( c3n == u3a_north_is_junior(u3R, veb) ) {
|
||||
_me_gain_use(veb); // bypass branches in u3k()
|
||||
return veb;
|
||||
}
|
||||
// junior (stack) refs are copied.
|
||||
//
|
||||
else {
|
||||
u3a_noun* veb_u = u3a_to_ptr(veb);
|
||||
|
||||
// 32-bit mug_w: already copied [veb] and [mug_w] is the new ref.
|
||||
//
|
||||
if ( veb_u->mug_w >> 31 ) {
|
||||
u3_noun nov = (u3_noun)veb_u->mug_w;
|
||||
|
||||
c3_assert( c3y == u3a_north_is_normal(u3R, nov) );
|
||||
|
||||
#ifdef VERBOSE_TAKE
|
||||
u3l_log("north: %p is already %p\r\n", veb_u, u3a_to_ptr(nov));
|
||||
#endif
|
||||
|
||||
_me_gain_use(nov); // bypass branches in u3k()
|
||||
return nov;
|
||||
}
|
||||
else if ( c3y == u3a_is_atom(veb) ) {
|
||||
return _ca_take_atom((u3a_atom*)veb_u);
|
||||
}
|
||||
else {
|
||||
u3a_cell* old_u = (u3a_cell*)veb_u;
|
||||
_ca_take* fam_u = u3a_push(pil_u);
|
||||
u3a_pile_sane(pil_u);
|
||||
|
||||
fam_u->hed = u3_none;
|
||||
fam_u->old = veb;
|
||||
|
||||
veb = old_u->hed;
|
||||
_ca_take_push(mov, off, TAKE_HEAD, old_u, new_u);
|
||||
goto advance;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// consume: popped stack frame, and .pro from above
|
||||
/* _ca_take_next_south: take next noun, pushing cells on stack.
|
||||
*/
|
||||
static inline u3_noun
|
||||
_ca_take_next_south(u3a_pile* pil_u, u3_noun veb)
|
||||
{
|
||||
while ( 1 ) {
|
||||
// direct atoms and senior refs are not counted.
|
||||
//
|
||||
retreat: {
|
||||
takeframe fam_u = _ca_take_pop(mov, off);
|
||||
|
||||
switch ( fam_u.tag_y ) {
|
||||
default: {
|
||||
c3_assert(0);
|
||||
if ( (c3y == u3a_is_cat(veb))
|
||||
|| (c3y == u3a_south_is_senior(u3R, veb)) )
|
||||
{
|
||||
return veb;
|
||||
}
|
||||
|
||||
// .fam_u is our stack root, we're done.
|
||||
// not junior; a normal pointer in our road -- refcounted
|
||||
//
|
||||
case TAKE_ROOT: {
|
||||
break;
|
||||
else if ( c3n == u3a_south_is_junior(u3R, veb) ) {
|
||||
_me_gain_use(veb); // bypass branches in u3k()
|
||||
return veb;
|
||||
}
|
||||
|
||||
// .pro is the copied head of a cell; save a pointer to it in .new_u
|
||||
// and advance to copy the tail
|
||||
// junior (stack) refs are copied.
|
||||
//
|
||||
case TAKE_HEAD: {
|
||||
fam_u.new_u->hed = pro;
|
||||
else {
|
||||
u3a_noun* veb_u = u3a_to_ptr(veb);
|
||||
|
||||
veb = fam_u.old_u->tel;
|
||||
_ca_take_push(mov, off, TAKE_TAIL, fam_u.old_u, fam_u.new_u);
|
||||
goto advance;
|
||||
}
|
||||
|
||||
// .pro is the copied tail of a cell; save a pointer to it in .new_u,
|
||||
// and produce the whole copied cell (as if it were a read from above).
|
||||
// 32-bit mug_w: already copied [veb] and [mug_w] is the new ref.
|
||||
//
|
||||
case TAKE_TAIL: {
|
||||
fam_u.new_u->tel = pro;
|
||||
pro = u3a_to_pom(u3a_outa(fam_u.new_u));
|
||||
if ( veb_u->mug_w >> 31 ) {
|
||||
u3_noun nov = (u3_noun)veb_u->mug_w;
|
||||
|
||||
// Borrow mug slot to record new destination in old_u.
|
||||
c3_assert( c3y == u3a_south_is_normal(u3R, nov) );
|
||||
|
||||
#ifdef VERBOSE_TAKE
|
||||
u3l_log("south: %p is already %p\r\n", veb_u, u3a_to_ptr(nov));
|
||||
#endif
|
||||
|
||||
_me_gain_use(nov); // bypass branches in u3k()
|
||||
return nov;
|
||||
}
|
||||
else if ( c3y == u3a_is_atom(veb) ) {
|
||||
return _ca_take_atom((u3a_atom*)veb_u);
|
||||
}
|
||||
else {
|
||||
u3a_cell* old_u = (u3a_cell*)veb_u;
|
||||
_ca_take* fam_u = u3a_push(pil_u);
|
||||
u3a_pile_sane(pil_u);
|
||||
|
||||
fam_u->hed = u3_none;
|
||||
fam_u->old = veb;
|
||||
|
||||
veb = old_u->hed;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* _ca_take_north(): in a north road, gain, copying juniors (from stack).
|
||||
*/
|
||||
static u3_noun
|
||||
_ca_take_north(u3_noun veb)
|
||||
{
|
||||
u3_noun pro;
|
||||
_ca_take* fam_u;
|
||||
u3a_pile pil_u;
|
||||
u3a_pile_prep(&pil_u, sizeof(*fam_u));
|
||||
|
||||
// commence taking
|
||||
//
|
||||
fam_u.old_u->mug_w = pro;
|
||||
pro = _ca_take_next_north(&pil_u, veb);
|
||||
|
||||
goto retreat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
// process cell results
|
||||
//
|
||||
c3_assert( u3R->cap_p == cap_p );
|
||||
if ( c3n == u3a_pile_done(&pil_u) ) {
|
||||
fam_u = u3a_peek(&pil_u);
|
||||
|
||||
do {
|
||||
// head-frame: stash copy and continue into the tail
|
||||
//
|
||||
if ( u3_none == fam_u->hed ) {
|
||||
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
|
||||
fam_u->hed = pro;
|
||||
pro = _ca_take_next_north(&pil_u, old_u->tel);
|
||||
fam_u = u3a_peek(&pil_u);
|
||||
}
|
||||
// tail-frame: copy cell and pop the stack
|
||||
//
|
||||
else {
|
||||
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
|
||||
pro = _ca_take_cell(old_u, fam_u->hed, pro);
|
||||
fam_u = u3a_pop(&pil_u);
|
||||
}
|
||||
} while ( c3n == u3a_pile_done(&pil_u) );
|
||||
}
|
||||
|
||||
return pro;
|
||||
}
|
||||
/* _ca_take_south(): in a south road, gain, copying juniors (from stack).
|
||||
*/
|
||||
static u3_noun
|
||||
_ca_take_south(u3_noun veb)
|
||||
{
|
||||
u3_noun pro;
|
||||
_ca_take* fam_u;
|
||||
u3a_pile pil_u;
|
||||
u3a_pile_prep(&pil_u, sizeof(*fam_u));
|
||||
|
||||
// commence taking
|
||||
//
|
||||
pro = _ca_take_next_south(&pil_u, veb);
|
||||
|
||||
// process cell results
|
||||
//
|
||||
if ( c3n == u3a_pile_done(&pil_u) ) {
|
||||
fam_u = u3a_peek(&pil_u);
|
||||
|
||||
do {
|
||||
// head-frame: stash copy and continue into the tail
|
||||
//
|
||||
if ( u3_none == fam_u->hed ) {
|
||||
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
|
||||
fam_u->hed = pro;
|
||||
pro = _ca_take_next_south(&pil_u, old_u->tel);
|
||||
fam_u = u3a_peek(&pil_u);
|
||||
}
|
||||
// tail-frame: copy cell and pop the stack
|
||||
//
|
||||
else {
|
||||
u3a_cell* old_u = u3a_to_ptr(fam_u->old);
|
||||
pro = _ca_take_cell(old_u, fam_u->hed, pro);
|
||||
fam_u = u3a_pop(&pil_u);
|
||||
}
|
||||
} while ( c3n == u3a_pile_done(&pil_u) );
|
||||
}
|
||||
|
||||
return pro;
|
||||
}
|
||||
|
||||
/* u3a_take(): gain, copying juniors.
|
||||
*/
|
||||
u3_noun
|
||||
u3a_take(u3_noun veb)
|
||||
{
|
||||
u3_noun pro;
|
||||
u3t_on(coy_o);
|
||||
|
||||
c3_assert(u3_none != veb);
|
||||
|
||||
pro = ( c3y == u3a_is_north(u3R) )
|
||||
? _ca_take_north(veb)
|
||||
: _ca_take_south(veb);
|
||||
|
||||
u3t_off(coy_o);
|
||||
|
||||
return pro;
|
||||
}
|
||||
|
||||
@ -2572,59 +2602,33 @@ u3a_walk_fore(u3_noun a,
|
||||
void (*pat_f)(u3_atom, void*),
|
||||
c3_o (*cel_f)(u3_noun, void*))
|
||||
{
|
||||
// initialize signed stack offsets (relative to N or S road)
|
||||
//
|
||||
c3_o nor_o = u3a_is_north(u3R);
|
||||
c3_ys mov_ys, off_ys;
|
||||
{
|
||||
c3_y wis_y = c3_wiseof(u3_noun);
|
||||
mov_ys = ( c3y == nor_o ? -wis_y : wis_y );
|
||||
off_ys = ( c3y == nor_o ? 0 : -wis_y );
|
||||
}
|
||||
u3_noun* top;
|
||||
u3a_pile pil_u;
|
||||
|
||||
// set stack root, push argument
|
||||
// initialize stack control; push argument
|
||||
//
|
||||
u3_noun *top, *don;
|
||||
{
|
||||
don = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
u3R->cap_p += mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
u3a_pile_prep(&pil_u, sizeof(u3_noun));
|
||||
top = u3a_push(&pil_u);
|
||||
*top = a;
|
||||
}
|
||||
|
||||
while ( top != don ) {
|
||||
while ( c3n == u3a_pile_done(&pil_u) ) {
|
||||
// visit an atom, then pop the stack
|
||||
//
|
||||
if ( c3y == u3a_is_atom(a) ) {
|
||||
pat_f(a, ptr_v);
|
||||
u3R->cap_p -= mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
top = u3a_pop(&pil_u);
|
||||
}
|
||||
// vist a cell, if c3n, pop the stack
|
||||
//
|
||||
else if ( c3n == cel_f(a, ptr_v) ) {
|
||||
u3R->cap_p -= mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
|
||||
top = u3a_pop(&pil_u);
|
||||
}
|
||||
// otherwise, push the tail and continue into the head
|
||||
//
|
||||
else {
|
||||
*top = u3t(a);
|
||||
u3R->cap_p += mov_ys;
|
||||
|
||||
if ( c3y == nor_o ) {
|
||||
if( !(u3R->cap_p > u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( !(u3R->cap_p < u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
top = u3a_push(&pil_u);
|
||||
u3a_pile_sane(&pil_u);
|
||||
*top = u3h(a);
|
||||
}
|
||||
|
||||
@ -2640,47 +2644,34 @@ u3a_walk_fore_unsafe(u3_noun a,
|
||||
void (*pat_f)(u3_atom, void*),
|
||||
c3_o (*cel_f)(u3_noun, void*))
|
||||
{
|
||||
// initialize signed stack offsets (relative to N or S road)
|
||||
//
|
||||
c3_ys mov_ys, off_ys;
|
||||
{
|
||||
c3_y wis_y = c3_wiseof(u3_noun);
|
||||
c3_o nor_o = u3a_is_north(u3R);
|
||||
mov_ys = ( c3y == nor_o ? -wis_y : wis_y );
|
||||
off_ys = ( c3y == nor_o ? 0 : -wis_y );
|
||||
}
|
||||
u3_noun* top;
|
||||
u3a_pile pil_u;
|
||||
|
||||
// set stack root, push argument
|
||||
// initialize stack control; push argument
|
||||
//
|
||||
u3_noun *top, *don;
|
||||
{
|
||||
don = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
u3R->cap_p += mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
u3a_pile_prep(&pil_u, sizeof(u3_noun));
|
||||
top = u3a_push(&pil_u);
|
||||
*top = a;
|
||||
}
|
||||
|
||||
while ( top != don ) {
|
||||
while ( c3n == u3a_pile_done(&pil_u) ) {
|
||||
// visit an atom, then pop the stack
|
||||
//
|
||||
if ( c3y == u3a_is_atom(a) ) {
|
||||
pat_f(a, ptr_v);
|
||||
u3R->cap_p -= mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
top = u3a_pop(&pil_u);
|
||||
}
|
||||
// vist a cell, if c3n, pop the stack
|
||||
//
|
||||
else if ( c3n == cel_f(a, ptr_v) ) {
|
||||
u3R->cap_p -= mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
|
||||
top = u3a_pop(&pil_u);
|
||||
}
|
||||
// otherwise, push the tail and continue into the head
|
||||
//
|
||||
else {
|
||||
*top = u3t(a);
|
||||
u3R->cap_p += mov_ys;
|
||||
top = u3to(u3_noun, u3R->cap_p + off_ys);
|
||||
// NB: overflow check elided here
|
||||
//
|
||||
top = u3a_push(&pil_u);
|
||||
*top = u3h(a);
|
||||
}
|
||||
|
||||
|
@ -219,9 +219,25 @@ u3r_mean(u3_noun som, ...)
|
||||
return ret_o;
|
||||
}
|
||||
|
||||
#define SONG_NONE 0
|
||||
#define SONG_HEAD 1
|
||||
#define SONG_TAIL 2
|
||||
// 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;
|
||||
@ -229,34 +245,43 @@ typedef struct {
|
||||
u3_noun b;
|
||||
} eqframe;
|
||||
|
||||
/* _cr_sing_push(): push a new stack frame, initialized as SING_NONE.
|
||||
*/
|
||||
static inline eqframe*
|
||||
_eq_push(c3_ys mov, c3_ys off, u3_noun a, u3_noun b)
|
||||
_cr_sing_push(u3a_pile* pil_u, u3_noun a, u3_noun b)
|
||||
{
|
||||
u3R->cap_p += mov;
|
||||
eqframe* cur = u3to(eqframe, u3R->cap_p + off);
|
||||
cur->sat_y = SONG_NONE;
|
||||
cur->a = a;
|
||||
cur->b = b;
|
||||
return cur;
|
||||
eqframe* fam_u = u3a_push(pil_u);
|
||||
fam_u->sat_y = SING_NONE;
|
||||
fam_u->a = a;
|
||||
fam_u->b = b;
|
||||
return fam_u;
|
||||
}
|
||||
|
||||
static inline eqframe*
|
||||
_eq_pop(c3_ys mov, c3_ys off)
|
||||
{
|
||||
u3R->cap_p -= mov;
|
||||
return u3to(eqframe, u3R->cap_p + off);
|
||||
}
|
||||
|
||||
/* _song_atom(): check if atom [a] is indirect and equal to noun [b]
|
||||
/* _cr_sing_mug(): short-circuit comparison if mugs are present and not equal.
|
||||
*/
|
||||
static inline c3_o
|
||||
_song_atom(u3_atom a, u3_noun b)
|
||||
_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)) )
|
||||
if ( (c3n == u3a_is_pug(a))
|
||||
|| (c3n == u3a_is_pug(b)) )
|
||||
{
|
||||
return c3n;
|
||||
}
|
||||
@ -264,23 +289,26 @@ _song_atom(u3_atom a, u3_noun b)
|
||||
u3a_atom* a_u = u3a_to_ptr(a);
|
||||
u3a_atom* b_u = u3a_to_ptr(b);
|
||||
|
||||
if ( (0 != a_u->mug_w) &&
|
||||
(0 != b_u->mug_w) &&
|
||||
(a_u->mug_w != b_u->mug_w) )
|
||||
{
|
||||
// [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 w_rez = a_u->len_w;
|
||||
c3_w w_mox = b_u->len_w;
|
||||
c3_w a_w = a_u->len_w;
|
||||
c3_w b_w = b_u->len_w;
|
||||
|
||||
if ( w_rez != w_mox ) {
|
||||
// [a] and [b] are not equal if their lengths are not equal
|
||||
//
|
||||
if ( a_w != b_w ) {
|
||||
return c3n;
|
||||
}
|
||||
else {
|
||||
c3_w i_w;
|
||||
|
||||
for ( i_w = 0; i_w < w_rez; 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;
|
||||
}
|
||||
@ -292,129 +320,68 @@ _song_atom(u3_atom a, u3_noun b)
|
||||
return c3y;
|
||||
}
|
||||
|
||||
/* _song_x_cape(): unifying equality with comparison deduplication
|
||||
* (tightly coupled to _song_x)
|
||||
*/
|
||||
static c3_o
|
||||
_song_x_cape(c3_ys mov, c3_ys off,
|
||||
eqframe* fam, eqframe* don,
|
||||
u3p(u3h_root) har_p)
|
||||
/* _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 a, b, key;
|
||||
u3_weak got;
|
||||
u3a_cell* a_u;
|
||||
u3a_cell* b_u;
|
||||
u3_noun key = u3nc(u3a_to_off(a), u3a_to_off(b));
|
||||
u3_noun val;
|
||||
|
||||
while ( don != fam ) {
|
||||
a = fam->a;
|
||||
b = fam->b;
|
||||
switch ( fam->sat_y ) {
|
||||
case SONG_NONE:
|
||||
if ( a == b ) {
|
||||
break;
|
||||
}
|
||||
else if ( c3y == u3a_is_atom(a) ) {
|
||||
if ( c3n == _song_atom(a, b) ) {
|
||||
return c3n;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ( c3y == u3a_is_atom(b) ) {
|
||||
return c3n;
|
||||
}
|
||||
else {
|
||||
a_u = u3a_to_ptr(a);
|
||||
b_u = u3a_to_ptr(b);
|
||||
|
||||
if ( (0 != a_u->mug_w) &&
|
||||
(0 != b_u->mug_w) &&
|
||||
(a_u->mug_w != b_u->mug_w) )
|
||||
{
|
||||
return c3n;
|
||||
}
|
||||
else {
|
||||
key = u3nc(u3a_to_off(a), u3a_to_off(b));
|
||||
u3t_off(euq_o);
|
||||
got = u3h_get(har_p, key);
|
||||
val = u3h_git(har_p, key);
|
||||
u3t_on(euq_o);
|
||||
|
||||
u3z(key);
|
||||
if ( u3_none != got ) {
|
||||
fam = _eq_pop(mov, off);
|
||||
continue;
|
||||
}
|
||||
fam->sat_y = SONG_HEAD;
|
||||
fam = _eq_push(mov, off, a_u->hed, b_u->hed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return ( u3_none == val ) ? c3y : c3n;
|
||||
}
|
||||
|
||||
case SONG_HEAD:
|
||||
a_u = u3a_to_ptr(a);
|
||||
b_u = u3a_to_ptr(b);
|
||||
u3a_wed(&(a_u->hed), &(b_u->hed));
|
||||
fam->sat_y = SONG_TAIL;
|
||||
fam = _eq_push(mov, off, a_u->tel, b_u->tel);
|
||||
continue;
|
||||
|
||||
case SONG_TAIL:
|
||||
a_u = u3a_to_ptr(a);
|
||||
b_u = u3a_to_ptr(b);
|
||||
u3a_wed(&(a_u->tel), &(b_u->tel));
|
||||
break;
|
||||
|
||||
default:
|
||||
c3_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
// [har_p] is effectively a set of equal pairs.
|
||||
// we cons [a] and [b] as posts so that we don't
|
||||
// touch their reference counts.
|
||||
/* _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 ) {
|
||||
key = u3nc(u3a_to_off(a), u3a_to_off(b));
|
||||
u3_noun key = u3nc(u3a_to_off(a), u3a_to_off(b));
|
||||
u3t_off(euq_o);
|
||||
u3h_put(har_p, key, c3y);
|
||||
u3t_on(euq_o);
|
||||
u3z(key);
|
||||
}
|
||||
fam = _eq_pop(mov, off);
|
||||
}
|
||||
|
||||
return c3y;
|
||||
}
|
||||
|
||||
/* _song_x(): yes if a and b are the same noun, use uni to unify
|
||||
*/
|
||||
/* _cr_sing_cape(): unifying equality with comparison deduplication
|
||||
* (tightly coupled to _cr_sing)
|
||||
*/
|
||||
static c3_o
|
||||
_song_x(u3_noun a, u3_noun b)
|
||||
_cr_sing_cape(u3a_pile* pil_u, u3p(u3h_root) har_p)
|
||||
{
|
||||
u3p(eqframe) empty = u3R->cap_p;
|
||||
|
||||
c3_y wis_y = c3_wiseof(eqframe);
|
||||
c3_o nor_o = u3a_is_north(u3R);
|
||||
c3_ys mov = ( c3y == nor_o ? -wis_y : wis_y );
|
||||
c3_ys off = ( c3y == nor_o ? 0 : -wis_y );
|
||||
c3_s ovr_s = 0;
|
||||
eqframe* fam = _eq_push(mov, off, a, b);
|
||||
eqframe* don = u3to(eqframe, empty + off);
|
||||
|
||||
eqframe* fam_u = u3a_peek(pil_u);
|
||||
u3_noun a, b, key;
|
||||
u3_weak got;
|
||||
u3a_cell* a_u;
|
||||
u3a_cell* b_u;
|
||||
|
||||
while ( don != fam ) {
|
||||
a = fam->a;
|
||||
b = fam->b;
|
||||
switch ( fam->sat_y ) {
|
||||
case SONG_NONE:
|
||||
// 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 == _song_atom(a, b) ) {
|
||||
u3R->cap_p = empty;
|
||||
if ( c3n == _cr_sing_atom(a, b) ) {
|
||||
return c3n;
|
||||
}
|
||||
else {
|
||||
@ -422,57 +389,178 @@ _song_x(u3_noun a, u3_noun b)
|
||||
}
|
||||
}
|
||||
else if ( c3y == u3a_is_atom(b) ) {
|
||||
u3R->cap_p = empty;
|
||||
return c3n;
|
||||
}
|
||||
// [a] and [b] are cells
|
||||
//
|
||||
else {
|
||||
a_u = u3a_to_ptr(a);
|
||||
b_u = u3a_to_ptr(b);
|
||||
|
||||
if ( (0 != a_u->mug_w) &&
|
||||
(0 != b_u->mug_w) &&
|
||||
(a_u->mug_w != b_u->mug_w) )
|
||||
{
|
||||
u3R->cap_p = empty;
|
||||
// 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->sat_y = SONG_HEAD;
|
||||
fam = _eq_push(mov, off, a_u->hed, b_u->hed);
|
||||
fam_u->sat_y = SING_HEAD;
|
||||
fam_u = _cr_sing_push(pil_u, a_u->hed, b_u->hed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case SONG_HEAD:
|
||||
// cells [a] and [b] have equal heads
|
||||
//
|
||||
case SING_HEAD: {
|
||||
a_u = u3a_to_ptr(a);
|
||||
b_u = u3a_to_ptr(b);
|
||||
u3a_wed(&(a_u->hed), &(b_u->hed));
|
||||
fam->sat_y = SONG_TAIL;
|
||||
fam = _eq_push(mov, off, a_u->tel, b_u->tel);
|
||||
continue;
|
||||
|
||||
case SONG_TAIL:
|
||||
// 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);
|
||||
u3a_wed(&(a_u->tel), &(b_u->tel));
|
||||
break;
|
||||
} break;
|
||||
|
||||
default:
|
||||
default: {
|
||||
c3_assert(0);
|
||||
break;
|
||||
} break;
|
||||
}
|
||||
|
||||
// [ovr_s] counts iterations. when it overflows, we know we've hit a
|
||||
// pathological case and MUST start de-duplicating comparisons.
|
||||
// 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);
|
||||
u3a_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);
|
||||
u3a_wed(&(a_u->tel), &(b_u->tel));
|
||||
} break;
|
||||
|
||||
default: {
|
||||
c3_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 = _song_x_cape(mov, off, fam, don, har_p);
|
||||
c3_o ret_o = _cr_sing_cape(&pil_u, har_p);
|
||||
u3h_free(har_p);
|
||||
u3R->cap_p = empty;
|
||||
u3R->cap_p = pil_u.top_p;
|
||||
return ret_o;
|
||||
}
|
||||
fam = _eq_pop(mov, off);
|
||||
|
||||
fam_u = u3a_pop(&pil_u);
|
||||
}
|
||||
|
||||
return c3y;
|
||||
@ -485,7 +573,7 @@ u3r_sing(u3_noun a, u3_noun b)
|
||||
{
|
||||
c3_o ret_o;
|
||||
u3t_on(euq_o);
|
||||
ret_o = _song_x(a, b);
|
||||
ret_o = _cr_sing(a, b);
|
||||
u3t_off(euq_o);
|
||||
return ret_o;
|
||||
}
|
||||
@ -1455,101 +1543,24 @@ u3r_mug_cell(u3_noun hed,
|
||||
return u3r_mug_both(lus_w, biq_w);
|
||||
}
|
||||
|
||||
#define MUG_ROOT 0
|
||||
#define MUG_HEAD 1
|
||||
#define MUG_TAIL 2
|
||||
/* _cr_mug: stack frame for recording cell traversal
|
||||
** !mug == head-frame
|
||||
*/
|
||||
typedef struct {
|
||||
c3_l mug_l;
|
||||
u3_cell cel;
|
||||
} _cr_mugf;
|
||||
|
||||
// stack frame for recording head vs tail iteration
|
||||
//
|
||||
// In Hoon, this structure would be as follows:
|
||||
//
|
||||
// $% [%root ~]
|
||||
// [%head cell=^]
|
||||
// [%tail cell=^ hed-mug=@]
|
||||
// ==
|
||||
//
|
||||
typedef struct mugframe
|
||||
/* _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)
|
||||
{
|
||||
c3_y tag_y;
|
||||
u3a_cell* cel_u;
|
||||
c3_w mug_w;
|
||||
} mugframe;
|
||||
|
||||
static inline void
|
||||
_mug_push(c3_ys mov,
|
||||
c3_ys off,
|
||||
c3_y tag_y,
|
||||
u3a_cell* cel_u,
|
||||
c3_w mug_w)
|
||||
{
|
||||
u3R->cap_p += mov;
|
||||
|
||||
// ensure we haven't overflowed the stack
|
||||
// (off==0 means we're on a north road)
|
||||
//
|
||||
if ( 0 == off ) {
|
||||
if( !(u3R->cap_p > u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( !(u3R->cap_p < u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
|
||||
mugframe* fam_u = u3to(mugframe, u3R->cap_p + off);
|
||||
fam_u->tag_y = tag_y;
|
||||
fam_u->cel_u = cel_u;
|
||||
fam_u->mug_w = mug_w;
|
||||
}
|
||||
|
||||
static inline mugframe
|
||||
_mug_pop(c3_ys mov, c3_ys off)
|
||||
{
|
||||
mugframe* fam_u = u3to(mugframe, u3R->cap_p + off);
|
||||
u3R->cap_p -= mov;
|
||||
|
||||
return *fam_u;
|
||||
}
|
||||
|
||||
// u3r_mug(): statefully mug a noun using a 31-bit MurmurHash3
|
||||
//
|
||||
c3_w
|
||||
u3r_mug(u3_noun veb)
|
||||
{
|
||||
// sanity check (makes a clear error message)
|
||||
//
|
||||
c3_assert( u3_none != veb );
|
||||
|
||||
// initialize signed stack offsets (relative to north/south road)
|
||||
//
|
||||
c3_ys mov, off;
|
||||
{
|
||||
c3_y wis_y = c3_wiseof(mugframe);
|
||||
c3_o nor_o = u3a_is_north(u3R);
|
||||
mov = ( c3y == nor_o ? -wis_y : wis_y );
|
||||
off = ( c3y == nor_o ? 0 : -wis_y );
|
||||
}
|
||||
|
||||
// stash the current stack post
|
||||
//
|
||||
u3p(mugframe) cap_p = u3R->cap_p;
|
||||
|
||||
// push the (only) ROOT stack frame (our termination condition)
|
||||
//
|
||||
_mug_push(mov, off, MUG_ROOT, 0, 0);
|
||||
|
||||
c3_w mug_w;
|
||||
|
||||
// read from the current noun .veb
|
||||
//
|
||||
advance: {
|
||||
while ( 1 ) {
|
||||
// veb is a direct atom, mug is not memoized
|
||||
//
|
||||
if ( _(u3a_is_cat(veb)) ) {
|
||||
mug_w = u3r_mug_words(&veb, 1);
|
||||
goto retreat;
|
||||
if ( c3y == u3a_is_cat(veb) ) {
|
||||
return (c3_l)u3r_mug_words(&veb, 1);
|
||||
}
|
||||
// veb is indirect, a pointer into the loom
|
||||
//
|
||||
@ -1558,72 +1569,86 @@ u3r_mug(u3_noun veb)
|
||||
|
||||
// veb has already been mugged, return memoized value
|
||||
//
|
||||
if ( 0 != veb_u->mug_w ) {
|
||||
mug_w = veb_u->mug_w;
|
||||
goto retreat;
|
||||
// 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 ( _(u3a_is_atom(veb)) ) {
|
||||
else if ( c3y == u3a_is_atom(veb) ) {
|
||||
u3a_atom* vat_u = (u3a_atom*)veb_u;
|
||||
mug_w = u3r_mug_words(vat_u->buf_w, vat_u->len_w);
|
||||
vat_u->mug_w = mug_w;
|
||||
goto retreat;
|
||||
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;
|
||||
_mug_push(mov, off, MUG_HEAD, cel_u, 0);
|
||||
_cr_mugf* fam_u = u3a_push(pil_u);
|
||||
|
||||
// check for overflow
|
||||
//
|
||||
u3a_pile_sane(pil_u);
|
||||
|
||||
fam_u->mug_l = 0;
|
||||
fam_u->cel = veb;
|
||||
|
||||
veb = cel_u->hed;
|
||||
goto advance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// consume the popped stack frame and mug from above
|
||||
//
|
||||
retreat: {
|
||||
mugframe fam_u = _mug_pop(mov, off);
|
||||
|
||||
switch ( fam_u.tag_y ) {
|
||||
default: {
|
||||
c3_assert(0);
|
||||
}
|
||||
|
||||
// we done
|
||||
//
|
||||
case MUG_ROOT: {
|
||||
break;
|
||||
}
|
||||
|
||||
// mug_w is the mug of the head of cel_u
|
||||
// push a stack frame to mark tail recursion,
|
||||
// record the mug of the head, and read the tail
|
||||
//
|
||||
case MUG_HEAD: {
|
||||
_mug_push(mov, off, MUG_TAIL, fam_u.cel_u, mug_w);
|
||||
|
||||
veb = fam_u.cel_u->tel;
|
||||
goto advance;
|
||||
}
|
||||
|
||||
// mug_w is the mug of the tail of cel_u
|
||||
// combine the mugs, memoize the value, and recur
|
||||
//
|
||||
case MUG_TAIL: {
|
||||
u3a_cell* cel_u = fam_u.cel_u;
|
||||
mug_w = u3r_mug_both(fam_u.mug_w, mug_w);
|
||||
cel_u->mug_w = mug_w;
|
||||
goto retreat;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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
|
||||
//
|
||||
c3_assert( u3R->cap_p == cap_p );
|
||||
c3_assert( u3_none != veb );
|
||||
|
||||
return mug_w;
|
||||
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;
|
||||
}
|
||||
|
@ -286,111 +286,35 @@ u3s_jam_xeno(u3_noun a, c3_d* len_d, c3_y** byt_y)
|
||||
return ur_bsw_done(&jam_u.rit_u, len_d, byt_y);
|
||||
}
|
||||
|
||||
#define CUE_ROOT 0
|
||||
#define CUE_HEAD 1
|
||||
#define CUE_TAIL 2
|
||||
|
||||
// stack frame for recording head vs tail iteration
|
||||
//
|
||||
// In Hoon, this structure would be as follows:
|
||||
//
|
||||
// $% [%root ~]
|
||||
// [%head cell-cursor=@]
|
||||
// [%tail cell-cursor=@ hed-width=@ hed-value=*]
|
||||
// ==
|
||||
//
|
||||
typedef struct _cs_cue_frame
|
||||
{
|
||||
c3_y tag_y;
|
||||
u3_atom cur;
|
||||
u3_atom wid;
|
||||
u3_noun hed;
|
||||
} cueframe;
|
||||
|
||||
/* _cs_cue_push(): construct a cueframe and push it onto the road stack.
|
||||
/* _cs_cue: stack frame for tracking intermediate cell results
|
||||
*/
|
||||
static inline void
|
||||
_cs_cue_push(c3_ys mov,
|
||||
c3_ys off,
|
||||
c3_y tag_y,
|
||||
typedef struct _cs_cue {
|
||||
u3_weak hed; // head of a cell or u3_none
|
||||
u3_atom wid; // bitwidth of [hed] or 0
|
||||
u3_atom cur; // bit-cursor position
|
||||
} _cs_cue;
|
||||
|
||||
/* _cs_rub: rub, TRANSFER [cur], RETAIN [a]
|
||||
*/
|
||||
static inline u3_noun
|
||||
_cs_rub(u3_atom cur, u3_atom a)
|
||||
{
|
||||
u3_noun pro = u3qe_rub(cur, a);
|
||||
u3z(cur);
|
||||
return pro;
|
||||
}
|
||||
|
||||
/* _cs_cue_next(): advance into [a], reading next value
|
||||
** TRANSFER [cur], RETAIN [a]
|
||||
*/
|
||||
static inline u3_noun
|
||||
_cs_cue_next(u3a_pile* pil_u,
|
||||
u3p(u3h_root) har_p,
|
||||
u3_atom cur,
|
||||
u3_atom wid,
|
||||
u3_noun hed)
|
||||
u3_atom a,
|
||||
u3_atom* wid)
|
||||
{
|
||||
u3R->cap_p += mov;
|
||||
|
||||
// ensure we haven't overflowed the stack
|
||||
// (off==0 means we're on a north road)
|
||||
//
|
||||
if ( 0 == off ) {
|
||||
if( !(u3R->cap_p > u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( !(u3R->cap_p < u3R->hat_p) ) {
|
||||
u3m_bail(c3__meme);
|
||||
}
|
||||
}
|
||||
|
||||
cueframe* fam_u = u3to(cueframe, u3R->cap_p + off);
|
||||
fam_u->tag_y = tag_y;
|
||||
fam_u->cur = cur;
|
||||
fam_u->wid = wid;
|
||||
fam_u->hed = hed;
|
||||
}
|
||||
|
||||
/* _cs_cue_pop(): pop a cueframe off the road stack and return it.
|
||||
*/
|
||||
static inline cueframe
|
||||
_cs_cue_pop(c3_ys mov, c3_ys off)
|
||||
{
|
||||
cueframe* fam_u = u3to(cueframe, u3R->cap_p + off);
|
||||
u3R->cap_p -= mov;
|
||||
|
||||
return *fam_u;
|
||||
}
|
||||
|
||||
/* u3s_cue(): cue [a]
|
||||
*/
|
||||
u3_noun
|
||||
u3s_cue(u3_atom a)
|
||||
{
|
||||
// initialize signed stack offsets (relative to north/south road)
|
||||
//
|
||||
c3_ys mov, off;
|
||||
{
|
||||
c3_y wis_y = c3_wiseof(cueframe);
|
||||
c3_o nor_o = u3a_is_north(u3R);
|
||||
mov = ( c3y == nor_o ? -wis_y : wis_y );
|
||||
off = ( c3y == nor_o ? 0 : -wis_y );
|
||||
}
|
||||
|
||||
// initialize a hash table for dereferencing backrefs
|
||||
//
|
||||
u3p(u3h_root) har_p = u3h_new();
|
||||
|
||||
// stash the current stack post
|
||||
//
|
||||
u3p(cueframe) cap_p = u3R->cap_p;
|
||||
|
||||
// push the (only) ROOT stack frame (our termination condition)
|
||||
//
|
||||
_cs_cue_push(mov, off, CUE_ROOT, 0, 0, 0);
|
||||
|
||||
// initialize cursor to bit-position 0
|
||||
//
|
||||
u3_atom cur = 0;
|
||||
|
||||
// the bitwidth and product from reading at cursor
|
||||
//
|
||||
u3_atom wid, pro;
|
||||
|
||||
// read from atom at cursor
|
||||
//
|
||||
// TRANSFER .cur
|
||||
//
|
||||
advance: {
|
||||
while ( 1 ) {
|
||||
// read tag bit at cur
|
||||
//
|
||||
c3_y tag_y = u3qc_cut(0, cur, 1, a);
|
||||
@ -400,19 +324,14 @@ u3s_cue(u3_atom a)
|
||||
// produce atom and the width we read
|
||||
//
|
||||
if ( 0 == tag_y ) {
|
||||
u3_noun bur;
|
||||
{
|
||||
u3_noun x = u3qa_inc(cur);
|
||||
bur = u3qe_rub(x, a);
|
||||
u3z(x);
|
||||
}
|
||||
u3_noun bur = _cs_rub(u3i_vint(cur), a);
|
||||
u3_noun pro = u3k(u3t(bur));
|
||||
|
||||
pro = u3k(u3t(bur));
|
||||
u3h_put(har_p, cur, u3k(pro));
|
||||
wid = u3qa_inc(u3h(bur));
|
||||
*wid = u3qa_inc(u3h(bur));
|
||||
|
||||
u3z(bur);
|
||||
goto retreat;
|
||||
return pro;
|
||||
}
|
||||
else {
|
||||
// read tag bit at (1 + cur)
|
||||
@ -428,87 +347,94 @@ u3s_cue(u3_atom a)
|
||||
// produce referenced value and the width we read
|
||||
//
|
||||
if ( 1 == tag_y ) {
|
||||
u3_noun bur;
|
||||
{
|
||||
u3_noun x = u3ka_add(2, cur);
|
||||
bur = u3qe_rub(x, a);
|
||||
u3z(x);
|
||||
}
|
||||
u3_noun bur = _cs_rub(u3ka_add(2, cur), a);
|
||||
u3_noun pro = u3x_good(u3h_get(har_p, u3t(bur)));
|
||||
|
||||
pro = u3h_get(har_p, u3k(u3t(bur)));
|
||||
|
||||
if ( u3_none == pro ) {
|
||||
return u3m_bail(c3__exit);
|
||||
}
|
||||
|
||||
wid = u3qa_add(2, u3h(bur));
|
||||
*wid = u3qa_add(2, u3h(bur));
|
||||
|
||||
u3z(bur);
|
||||
goto retreat;
|
||||
return pro;
|
||||
}
|
||||
// next bit unset, (2 + cur) points to the head of a cell
|
||||
//
|
||||
// push a frame to mark HEAD recursion and read the head
|
||||
// push a head-frame onto the road stack and read the head
|
||||
//
|
||||
else {
|
||||
_cs_cue_push(mov, off, CUE_HEAD, cur, 0, 0);
|
||||
_cs_cue* fam_u = u3a_push(pil_u);
|
||||
u3a_pile_sane(pil_u);
|
||||
|
||||
// NB: fam_u->wid unused in head-frame
|
||||
//
|
||||
fam_u->hed = u3_none;
|
||||
fam_u->cur = cur;
|
||||
|
||||
cur = u3qa_add(2, cur);
|
||||
goto advance;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// consume: popped stack frame, .wid and .pro from above.
|
||||
u3_noun
|
||||
u3s_cue(u3_atom a)
|
||||
{
|
||||
// pro: cue'd noun product
|
||||
// wid: bitwidth read to produce [pro]
|
||||
// fam_u: stack frame
|
||||
// har_p: backreference table
|
||||
// pil_u: stack control structure
|
||||
//
|
||||
// TRANSFER .wid, .pro, and contents of .fam_u
|
||||
// (.cur is in scope, but we have already lost our reference to it)
|
||||
u3_noun pro;
|
||||
u3_atom wid, cur = 0;
|
||||
_cs_cue* fam_u;
|
||||
u3p(u3h_root) har_p = u3h_new();
|
||||
u3a_pile pil_u;
|
||||
|
||||
// initialize stack control
|
||||
//
|
||||
retreat: {
|
||||
cueframe fam_u = _cs_cue_pop(mov, off);
|
||||
u3a_pile_prep(&pil_u, sizeof(*fam_u));
|
||||
|
||||
switch ( fam_u.tag_y ) {
|
||||
default: {
|
||||
c3_assert(0);
|
||||
}
|
||||
|
||||
// fam_u is our stack root, we're done.
|
||||
// commence cueing at bit-position 0
|
||||
//
|
||||
case CUE_ROOT: {
|
||||
break;
|
||||
}
|
||||
pro = _cs_cue_next(&pil_u, har_p, 0, a, &wid);
|
||||
|
||||
// .wid and .pro are the head of the cell at fam_u.cur.
|
||||
// save them (and the cell cursor) in a TAIL frame,
|
||||
// set the cursor to the tail and read there.
|
||||
// process cell results
|
||||
//
|
||||
case CUE_HEAD: {
|
||||
_cs_cue_push(mov, off, CUE_TAIL, fam_u.cur, wid, pro);
|
||||
if ( c3n == u3a_pile_done(&pil_u) ) {
|
||||
fam_u = u3a_peek(&pil_u);
|
||||
|
||||
cur = u3ka_add(2, u3qa_add(wid, fam_u.cur));
|
||||
goto advance;
|
||||
}
|
||||
|
||||
// .wid and .pro are the tail of the cell at fam_u.cur,
|
||||
// construct the cell, memoize it, and produce it along with
|
||||
// its total width (as if it were a read from above).
|
||||
do {
|
||||
// head-frame: stash [pro] and [wid]; continue into the tail
|
||||
//
|
||||
case CUE_TAIL: {
|
||||
pro = u3nc(fam_u.hed, pro);
|
||||
u3h_put(har_p, fam_u.cur, u3k(pro));
|
||||
wid = u3ka_add(2, u3ka_add(wid, fam_u.wid));
|
||||
goto retreat;
|
||||
if ( u3_none == fam_u->hed ) {
|
||||
// NB: fam_u->wid unused in head-frame
|
||||
//
|
||||
fam_u->hed = pro;
|
||||
fam_u->wid = wid;
|
||||
|
||||
// continue reading at the bit-position after [pro]
|
||||
{
|
||||
u3_noun cur = u3ka_add(2, u3qa_add(wid, fam_u->cur));
|
||||
pro = _cs_cue_next(&pil_u, har_p, cur, a, &wid);
|
||||
}
|
||||
|
||||
fam_u = u3a_peek(&pil_u);
|
||||
}
|
||||
// tail-frame: cons cell, recalculate [wid], and pop the stack
|
||||
//
|
||||
else {
|
||||
pro = u3nc(fam_u->hed, pro);
|
||||
u3h_put(har_p, fam_u->cur, u3k(pro));
|
||||
u3z(fam_u->cur);
|
||||
wid = u3ka_add(2, u3ka_add(wid, fam_u->wid));
|
||||
fam_u = u3a_pop(&pil_u);
|
||||
}
|
||||
} while ( c3n == u3a_pile_done(&pil_u) );
|
||||
}
|
||||
|
||||
u3z(wid);
|
||||
u3h_free(har_p);
|
||||
|
||||
// sanity check
|
||||
//
|
||||
c3_assert( u3R->cap_p == cap_p );
|
||||
|
||||
return pro;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user