Þ   briarpig  » code  » row


row

     This page is a stub placeholder for cy::Row appearing soon: a clone of cy::Deck for scatter/gather.

namespace cy { // ----- cy ----- // ----- forwards ----- class Row; class RowCut; // ydz: row slice (d=row z=slice) class RowArray; // yda: row as array (d=row, a=array) class RowIterByte; // RowIterByte template<class T> class RowIterT; // RowIterT iter over T in row

// ----- row cut ----- struct RowCut : public Cut { // ydz: slice of Row Row& z_d; RowCut(zp32 p, zn32 n, Row const& d) : Cut(p, n), z_d(*(Row*)&d) { } RowCut(Cut const& z, Row const& d) : Cut(z), z_d(*(Row*)&d) { } Row z2d() const; // same as { return Row(*this); } operator Row() const; // same as { return Row(*this); } u32 zout(Buf& b) const; // copy content to b, unchanged u32 zout(IovOut& o) const; // copy content to o, unchanged u32 zout(cy_sink& o) const; // copy content to o, unchanged Hash32& zcrc(Hash32& dest) const;

u32 zhash() const { Hash32 x; zcrc(x); return x.hcrc(); } bool zempty() const; u32 zsize() const; RowCut& zassign(u32 sz); // resize, any new bytes are zero RowCut& zassign(u32 sz, u8 val); // resize, all bytes val RowCut& zassign(const char* str); // init to one fragment RowCut& zassign(Iov const& run); // init to single fragment RowCut& zassign(Iovec const& v); // init to single fragment RowCut& zassign(IovVec const& v); // array of fragments RowCut& zassign(RowCut const& z); RowCut& zassign(Row const& dk); void zerase() const; void ztouch() const; // force copy-on-write of shared parts RowCut& operator=(u32 newLen) { return this->zassign(newLen); } inline RowCut& operator=(Nil1& nada); // erase w/ dzap() private: // internal Iovec looping/visitation methods typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); // Though passed mutable instead of const, pt must not be // modified more than necessary. Some callers may want to // write iterators -- for example, to effect copy-on-write // -- s pt argument must be modifiable in principle. But // no non-iterator methods should be changed since this // will likely disrupt behavior of _zwhile() below. friend class Row; // while (*fn)()=>true (iterate while fn returns true) bool _zwhile(Zwhile_fn fn, void* closure) const; // _zwhile() creates an instance of RowIter and populates it // w/ info to describe each Iov in the row corresponding to // entire byte range described by [m_offset:m_length] in this // Cut instance; each Iov is passed to the fn arg like this: // bool moreWhile = (*fn)(pt, closure); // and as long as the boolean moreWhile return from fn is // true, iteration over Iovecs continues until the range end. // _zwhile() returns true unless & until fn ever returns false; // _zwhile() returns false IFF (if & only if) fn returns false. // (In degenerate case, for an empty range, fn is never called // and so never returns false.) Presumably the only reason fn // ever returns false is to end while loop after finding a bit // of information sought, when searching more is not useful. // // In practice, a caller creates parameter block object for // a task to be performed, passing address of this as the // void* closure parameter, which gets passed through to fn // without inspection. Presumably the fn method casts the // void& closure to the right object type for reading and // writing whatever state is maintained by the task. // // In contrast to yd::_dseek(), which also takes a RowIter // arg, _zwhile() uses Point::m_first and Point:m_second a // bit differently, because there is never a need to have // bytes "before" Point::m_pos in m_first. Instead, the // current Iov passed to fn is always passed in m_second, // whose position corresponds to Point::m_pos. The first Iov // passed to (*fn)() might have some bytes in m_first, but // all following Points passed have (zero,zero) in m_first. // // fn is called once for each discontiguous block of memory // in the [m_offset:m_length] range of bytes, with the // state of the iteration passed in the RowIter argument. public: // RowCut versions of similar Row::dread() and dreadv() methods u32 zread(Buf& outBuf) const; // writes on outBuf.v_p // request read of outBuf.b_x bytes at offset off to outBuf.v_p; // actual bytes read are returned in outBuf.v_n and the method. u32 zread(Iovec& dest) const; // writes on dest.iov_base // requests a read of min(z_n,dest.iov_len) bytes at z_p; u32 zreadv(IovVec& dest) const; // writes to each iov_base // requests a read of min(z_n,dest.capacity()) bytes at z_p public: // printing inline void operator=(Row const& x); // z_d.dput(*this, x) inline void operator=(RowCut const& x); // z_d.dput(*this, x) inline void operator=(IovVec& x); // z_d.dput(*this, x) inline void operator=(Iov const& x); // z_d.dput(*this, x) inline void operator=(Iovec const& x); // z_d.dput(*this, x) inline void operator=(const char* x); // z_d.dput(*this, x)

struct Zq { RowCut const& q_z; Zq(RowCut const& z): q_z(z) { } }; Zq quote() const { return Zq(*this); } // to request dump void zdump(cy_sink& o) const; void zcite(cy_sink& o) const; }; // struct RowCut

inline cy_sink& operator<<(cy_sink& o, RowCut::Zq const& x) { x.q_z.zdump(o); return o; } inline Hash32& operator<<(Hash32& x, RowCut const& z) { z.zcrc(x); return x; } inline Buf& operator<<(Buf& x, RowCut const& z) { z.zout(x); return x; } inline IovOut& operator<<(IovOut& x, RowCut const& z) { z.zout(x); return x; } inline cy_sink& operator<<(cy_sink& x, RowCut const& z) { z.zout(x); return x; }

class Row { // scatter/gather vector of discontiguous bytes protected: friend class RowCut; friend class RowArray; friend class RowIterByte; cy_vat* d_vat; u32 d_size; // sum of used vecs u32 d_len; // current vector length void _dgoodsize(); // check: d_len matches vector lengths u32 d_changes; // count of changes for iter sync enum { e_ddead = 0xdeaddecc, e_dlive = 0x4465436b /*'yd'*/ }; u32 d_tag; // e_dlive mutable Vt<Iovec> d_vv; // vector vector (vector of Iov) mutable Vt< GumT<cy_fix> > d_fgv; // node handle vector public: // private by convention; for helper methods & classes void _dappend(Iovec const& v, GumT<cy_fix> const& fg); private: // _dseek() algorithm looks a lot like _dbreak() [done 1st] bool _dseek(u32 offset, RowIter& pt) const; // returns pt void _dbreak(u32 offset); // make Iovec boundary at offset bool _dreserve(u32 vlen); // ensure vectors are this long void _dtail(u32 head); // remove head bytes from start void _dtruncate(u32 newSize); // reduce size to newSize u32 _dscansize() const; // calculate size by iteration void _delide(u32 start, u32 end); // delete start to end // NOTE: end is pos KEPT in row; cut only end - start bytes void _dholepunch(RowIter& from, RowIter& to); // elide hole // from and to are the same Iovec containing start and end Iov _dview(u32 pos, Iov& x); // x is buf of sizeof(T) bytes // _dview() allows view objects of type T in Row at pos, where // x has sizeof(T) bytes, like this: T t; Iov x(&t, sizeof(T)); // _dview() returns contig block of bytes of length x.v_n at // pos in Row; if pos + x.v_n > dsize(), Iov(0,0) is failure. // // If x.v_n bytes in Row at pos are ALREADY contig, returned Iov // points directly at space in Row, without copying at all. Else... // If x.v_n bytes at pos are NOT already contiguous, the bytes are // copied into x.v_n and the Iov returned is a copy of x itself, // into which x.v_n bytes were copied. public: // testing methods void dfracture(p32 offset) { _dbreak(offset); } // force break u32 dunshare(); // COW shared space; return new bytes added // (So calling dunshare() twice should return zero 2nd time.) public: static void dshow(cy_sink& fo, Iovec const& v, u32 index, Buf& arcBuf, u32 sum); void dpr() const; // cite(Out::sout()); u32 dout(Buf& b) const; // copy content to b, unchanged u32 dout(IovOut& o) const; // copy content to o, unchanged u32 dout(cy_sink& o) const; // copy content to o, unchanged Hash32& dcrc(Hash32& dest) const; u32 dhash() const; u32 dchanges() const { return d_changes; } cy_vat* dvat() const { return d_vat; } bool dgood() const { return e_dlive == d_tag; } void dbad() const; // complain when dgood() is false Row(); // empty uses cy_heapvat_global(); explicit Row(cy_vat* v); // uses cy_heapvat_global(); ~Row(); // NOT virtual (no subclasses expected) Row(u32 sz, u8 c, cy_vat* v=0); // alloc sz; set all to c Row(Row const& src, cy_vat* v=0); // COW: like deep copy; Row(RowCut const& src, cy_vat* v=0); // Row(v) & assign Row(IovVec const & src, cy_vat* v=0); // Row(v) & assign Row(Iov const& src, cy_vat* v=0); // Row(v) & assign Row(Iovec const& src, cy_vat* v=0); // Row(v) & assign Row(const char* str, cy_vat* v=0); // copy ::strlen(str)

RowCut dz(zp32 p, zn32 n) const { return RowCut(p, n, *this); } RowCut operator()(zp32 p, zn32 n) const { return RowCut(p, n, *this); } RowCut dz(Cut const& z) const { return RowCut(z, *this); } RowCut operator()(Cut const& z) const { return RowCut(z, *this); } IovVec d2v() const; // Iovec array for reading IovVec d2cowv(); // (copy-on-write) Iovec array for writing

class Dx { public: bool d_x; Dx() : d_x(true) { } }; Dx& end() const { return *((Dx*) 0); } // unique type RowIterByte begin() const; // { return RowIterByte(*this); } RowIterByte rbegin() const; // { RowIterByte it(*this, size()); --it; return it; } template <typename T> RowIterT<T> tbegin() const; template <typename T> RowIterT<T> trbegin() const; u32 dsize() const { return d_size; } void dresize(u32 size, u8 octet); // dclear(): remove all; after: dsize() == 0; bool dclear(); // only false if !dgood() void dswap(Row& from); // exchange all content void derase(u32 pos, u32 len); // dzap() copes with neg z_p & z_n both relative to dsize() void dzap(Cut const& z); // resembles derase(z.z_p, z.z_n); void dinsert(p32 offset, u32 sz, u8 val); // insert copy void dinsert(p32 offset, Row const& x); // insert COW void dinsert(p32 offset, RowCut const& x); // insert cow of slice void dinsert(p32 offset, IovVec const& x); // insert copy void dinsert(p32 offset, Iov const& x); // insert copy void dinsert(p32 offset, Iovec const& x); // insert copy void dinsert(p32 offset, const char* x); // insert copy Row& dappend(u32 sz, u8 val); // dinsert(dsize(), x); Row& dappend(Row const& x); // dinsert(dsize(), x); Row& dappend(RowCut const& x); // dinsert(dsize(), x); Row& dappend(IovVec const& x); // dinsert(dsize(), x); Row& dappend(Iov const& x); // dinsert(dsize(), x); Row& dappend(Iovec const& x); // dinsert(dsize(), x); Row& dappend(const char* x); // dinsert(dsize(), x); Row& operator<<(Row const& x) { return this->dappend(x); } Row& operator<<(RowCut const& x) { return this->dappend(x); } Row& operator<<(IovVec const& x) { return this->dappend(x); } Row& operator<<(Iov const& x) { return this->dappend(x); } Row& operator<<(Iovec const& x) { return this->dappend(x); } Row& operator<<(const char* x) { return this->dappend(x); } void dput(Cut const& z, Row const& x); // replace z w/ copy of x void dput(Cut const& z, RowCut const& x); // replace z w/ copy of x void dput(Cut const& z, IovVec const& x); // replace z w/ copy of x void dput(Cut const& z, Iov const& x); // replace z with w/ of x void dput(Cut const& z, Iovec const& x); // replace z w/ copy of x void dput(Cut const& z, const char* x); // replace z w/ copy of x Row& dassign(Row const& x); // replace all w/ copy of x Row& dassign(RowCut const& x); // replace all w/ copy of x Row& dassign(IovVec const& x); // replace all w/ copy of x Row& dassign(Iov const& x); // replace all w/ copy of x Row& dassign(Iovec const& x); // replace all w/ copy of x Row& dassign(const char* x); // replace all w/ copy of x Row& operator=(Row const& x) { return this->dassign(x); } Row& operator=(RowCut const& x) { return this->dassign(x); } Row& operator=(IovVec const& x) { return this->dassign(x); } Row& operator=(Iov const& x) { return this->dassign(x); } Row& operator=(Iovec const& x) { return this->dassign(x); } Row& operator=(const char* x) { return this->dassign(x); } u32 dread(p32 pos, Buf& x) const; // pread x.b_x bytes from pos to v_p u32 dread(p32 pos, void* dest, size_t len) const; // pread pos to dest u32 dread(p32 pos, Iovec& x) const; // pread pos to dest.iov_base // request pread of x.iov_len bytes at pos to x.iov_base // dreadv(): pread dest.capacity() bytes from pos u32 dreadv(p32 pos, IovVec& dest) const; u32 dreadv(p32 pos, const Iovec* destArray, size_t count) const; // { IovVec v(vector, count); return this->readv(pos, v); } u32 dwrite(p32 pos, Iov const& x); // pwrite x to pos u32 dwrite(p32 pos, const char* x); // ::strlen(x) to pos u32 dwrite(p32 pos, const void* x, size_t len); // x to pos u32 dwrite(p32 pos, Iovec const& x); // pwrite x to pos // request pwrite of x.iov_len bytes from x.iov_base to pos // dwritev(): pwrite x.capacity() bytes to pos u32 dwritev(p32 pos, IovVec const& x); u32 dwritev(p32 pos, const Iovec* vector, size_t count); // { IovVec v(vector, count); return this->writev(pos, v); }

struct Dq { Row const& q_d; Dq(Row const& d): q_d(d) { } }; Dq quote() const { return Dq(*this); } // for dump() void ddump(cy_sink& o) const; void dcite(cy_sink& o) const; }; // class Row

inline cy_sink& operator<<(cy_sink& o, Cite<Row> const& x) { x.c_t.dcite(o); return o; } inline cy_sink& operator<<(cy_sink& o, Row::Dq const& x) { x.q_d.ddump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Row const& x) { x.dout(o); return o; } inline Hash32& operator<<(Hash32& x, Row const& d) { d.dcrc(x); return x; }

inline Buf& operator<<(Buf& x, Row const& d) { d.dout(x); return x; } inline IovOut& operator<<(IovOut& x, Row const& d) { d.dout(x); return x; }

//inline std::ostream& // operator<<(std::ostream& x, Row const& d) { // d.dout(x); return x; }

inline RowCut& RowCut::operator=(Nil1&) { z_d.dzap(*this); return *this; } inline void RowCut::operator=(Row const& x) { z_d.dput(*this, x); } inline void RowCut::operator=(RowCut const& x) { z_d.dput(*this, x); } inline void RowCut::operator=(IovVec& x) { z_d.dput(*this, x); } inline void RowCut::operator=(Iov const& x) { z_d.dput(*this, x); } inline void RowCut::operator=(Iovec const& x) { z_d.dput(*this, x); } inline void RowCut::operator=(const char* x) { z_d.dput(*this, x); }

///// ----- RowCut ----- ///// Row RowCut::z2d() const { // same as { return Row(*this); } return Row(*this); }

RowCut::operator Row() const { // like { return Row(*this); } return Row(*this); }

bool RowCut::zempty() const { // true if row(z_p,z_n) is empty return (z_n == 0 || (u32) z_p >= z_d.dsize()); }

u32 RowCut::zsize() const { // number of bytes in row(z_p,z_n) u32 dsz = z_d.dsize(); if (z_p < (zp32) dsz && z_n) { // any bytes at all inside? u32 end = z_p + z_n; if (end > dsz) // goes past eof? end = dsz; // only count bytes up to eof return end - z_p; } return 0; }

void RowCut::zerase() const { // same as z_d.erase(z_p,z_n); Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable Cut absolute(*this, d.dsize()); d.derase(absolute.z_p, absolute.z_n); }

RowCut& RowCut::zassign(u32 sz) { // resize, new bytes are zero Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable if (sz) { u32 eod = z_d.dsize(); // end of row if (z_p < (zp32) eod) { // bytes affected before eod? u32 oldSize = this->z_n; if (sz < oldSize) { // getting smaller? u32 less = oldSize - sz; d.derase(z_p + sz, less); } else if (oldSize < sz) { // getting larger? u32 more = sz - oldSize; d.dinsert(z_p + oldSize, more, 0); } else { // same size } } else { d.dappend(sz, 0); } } else { // sz == 0 means erase: d.derase(z_p, z_n); } return *this; }

RowCut& RowCut::zassign(u32 sz, u8 val) { // resize, all bytes val Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable if (sz) { u32 eod = z_d.dsize(); // end of row if (z_p < (zp32) eod) { // any bytes affected before end-of-row? d.derase(z_p, z_n); d.dinsert(z_p, sz, val); } } else { // sz == 0 means erase: d.derase(z_p, z_n); } return *this; }

RowCut& RowCut::zassign(const char* str) { // single fragment Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable d.derase(z_p, z_n); d.dinsert(z_p, str); return *this; }

RowCut& RowCut::zassign(Iov const& run) { // single fragment Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable d.derase(z_p, z_n); d.dinsert(z_p, run); return *this; } RowCut& RowCut::zassign(Iovec const& v) { // single fragment Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable d.derase(z_p, z_n); d.dinsert(z_p, v); return *this; }

RowCut& RowCut::zassign(IovVec const& v) { // fragment array Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable d.derase(z_p, z_n); d.dinsert(z_p, v); return *this; }

RowCut& RowCut::zassign(RowCut const& z) { Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable if ( &z.z_d == &z_d ) { // self modification? Row tempCopy(z, d.d_vat); // make a new row first d.derase(z_p, z_n); d.dinsert(z_p, tempCopy); } else { d.derase(z_p, z_n); d.dinsert(z_p, z); } return *this; }

RowCut& RowCut::zassign(Row const& dk) { Row& d = *const_cast<Row*>(&z_d); // so vectors are mutable if ( &dk == &z_d ) { // self modification? Row tempCopy(dk, d.d_vat); // make a new row first d.derase(z_p, z_n); d.dinsert(z_p, tempCopy); } else { d.derase(z_p, z_n); d.dinsert(z_p, dk); } return *this; }

void RowCut::zcite(cy_sink& o) const { u32 deof = z_d.dsize(); // end-of-file in row cy_sink_ft(&o, "<RowCut me=%lx row=%lx deof=%lu off=%lu len=%lu>", (long) this, (long) &z_d, (long) deof, (long) z_p, (long) z_n); if (deof) { double rdeof = deof; double offper = ((double) z_p / rdeof) * 100.0; double lenper = ((double) z_n / rdeof) * 100.0; double endper = ((double) (z_p+z_n) / rdeof) * 100.0; cy_sink_nf(&o, "<percent-of-Row-size start=%3.1f " "n=%3.1f end=%3.1f/>", offper, lenper, endper); } o << cy_endl; z_d.dcite(o); cy_sink_unend(&o, "RowCut"); }

RowCut_hub_dump { // closure for RowCut_while_dump public: cy_sink& h_out; u32 h_i; u32 h_sum; RowCut_hub_dump(cy_sink& x): h_out(x), h_i(0), h_sum(0) { } };

static bool RowCut_while_dump(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_dump& hub = *(RowCut_hub_dump*)closure; hub.h_out << pt << cy_addi << cy_endl; char arc[128]; Buf arcBuf(arc, 0, 128); Row::dshow(hub.h_out, pt.p_second, hub.h_i, arcBuf, hub.h_sum); hub.h_out << cy_subi; ++hub.h_i; hub.h_sum += pt.p_second.iov_len; return true; // keep going }

void RowCut::zdump(cy_sink& o) const { u32 deof = z_d.dsize(); // end-of-file in Row Hash32 myCrc; myCrc << *this; cy_sink_ft(&o, "<RowCut me=%lx Row=%lx deof=%lu off=%lu " "n=%lu crc=#%lx:%lu>", (long) this, (long) &z_d, (long) deof, (long) z_p, (long) z_n, (long) myCrc.hcrc(), (long) myCrc.hlen()); if (deof) { double rdeof = deof; double offper = ((double) z_p / rdeof) * 100.0; double lenper = ((double) z_n / rdeof) * 100.0; double endper = ((double) (z_p+z_n) / rdeof) * 100.0; cy_sink_nf(&o, "<percent-of-Row-size start=%3.1f " "len=%3.1f end=%3.1f/>", offper, lenper, endper); } o << cy_endl; z_d.dcite(o); RowCut_hub_dump hub(o); this->_zwhile(RowCut_while_dump, &hub); o << cy_endl; cy_sink_unend(&o, "RowCut"); }

class RowCut_hub_cy_sink { // closure for RowCut_while_cy_sink public: cy_sink& h_o; u32 h_actual; RowCut_hub_cy_sink(cy_sink& o): h_o(o), h_actual(0) { } };

static bool RowCut_while_cy_sink(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_cy_sink& hub = *(RowCut_hub_cy_sink*)closure; // size_t n = pt.p_second.iov_len; // pt.p_second.iov_base Iov v2(pt.p_second); ssize_t actual = cy_sink_v(&hub.h_o, v2); if (actual >= 0) { hub.h_actual += (u32) actual; } else { cy_yellerrno(__LINE__,__FILE__); // report errno return false; } return true; // keep going }

u32 RowCut::zout(cy_sink& o) const { // copy to o, unchanged RowCut_hub_cy_sink hub(o); this->_zwhile(RowCut_while_cy_sink, &hub); return hub.h_actual; }

struct RowCut_hub_vout { // closure for RowCut_while_vout IovOut& h_vout; u32 h_actual; RowCut_hub_vout(IovOut& x): h_vout(x), h_actual(0) { } };

static bool RowCut_while_vout(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_vout& hub = *(RowCut_hub_vout*)closure; size_t n = pt.p_second.iov_len; hub.h_actual += hub.h_vout.owrite(pt.p_second.iov_base, n); return true; // keep going }

u32 RowCut::zout(IovOut& v) const { // copy to v, unchanged RowCut_hub_vout hub(v); this->_zwhile(RowCut_while_vout, &hub); if (hub.h_actual != v.oactual()) { cy_yellf(__LINE__, __FILE__, "hub.h_actual=%lu DIFFERS: v.actual()=%lu", (long) hub.h_actual, (long) v.oactual()); } return hub.h_actual; }

struct RowCut_hub_outbuf { // closure for RowCut_while_outbuf Buf& h_buf; RowCut_hub_outbuf(Buf& x): h_buf(x) { } };

static bool RowCut_while_outbuf(RowIter& pt, void* closure) { RowCut_hub_outbuf& hub = *(RowCut_hub_outbuf*)closure; Iov run(pt.p_second.iov_base, pt.p_second.iov_len); hub.h_buf.bappend(run); return hub.h_buf.v_n < hub.h_buf.b_x; // keep going }

u32 RowCut::zout(Buf& b) const { // copy to b, unchanged u32 before = b.v_n; RowCut_hub_outbuf hub(b); this->_zwhile(RowCut_while_outbuf, &hub); return b.v_n - before; }

u32 RowIter::ptouch() { // force cow of shared Iovec // NOTE: assumes 1 ref for Iovec, 1 ref for iter, for max=2 GumT<cy_fix> handle(*p_j); // NOTE: increases refcount by 1 // A refcount equal to TWO means only this Row has an Iov. // (it would be ONE, but we just added another ref in gum.) // if no gum exists (global space living forever) or if gum // is for a fix with more than two refcounts (shared), then // copy old Iov (but not entire original refcounted space) if (handle.gnice(2)) { // no refs or has more than 2 refs? Iovec old = *p_i; cy_iovec cold = old; GumT<cy_fix> newGum; cy_vat* vat = p_d->dvat(); cy_iovec newIov = vat_copy_do(vat, cold, &newGum); if (newIov.iov_base) { Iovec copy(newIov); *p_i = copy; // replace old Iovec with new copy *p_j = newGum; // replace old handle with new return copy.iov_len; // number of copied bytes } } return 0; }

static bool RowCut_while_touch(RowIter& p, void* ) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); p.ptouch(); return true; // keep going }

void RowCut::ztouch() const { // force cow of any shared parts // void* hub = 0; this->_zwhile(RowCut_while_touch, (void*) 0); }

struct RowCut_hub_read { // closure format for RowCut_while_read u8* h_o; // start of Iovec to write u8* h_p; // where to write next byte u8* h_x; // one beyond last writable byte in Iovec RowCut_hub_read(Iovec& v): h_o(0), h_p(0), h_x(0) { if (v.iov_base && v.iov_len) { h_o = h_p = (u8*)v.iov_base; // start of writable buffer h_x = h_o + v.iov_len; // one after end of writable buffer } } u32 actual() const { return h_p - h_o; } // number of bytes };

static bool RowCut_while_read(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_read& hub = *(RowCut_hub_read*)closure; if (pt.p_second.iov_base && pt.p_second.iov_len) { u32 quantum = hub.h_x - hub.h_p; // space remaining if (quantum > pt.p_second.iov_len) quantum = pt.p_second.iov_len; ::memcpy(hub.h_p, pt.p_second.iov_base, quantum); hub.h_p += quantum; if (hub.h_p >= hub.h_x) // destination is full? return false; // don't need any more cut Iovecs } return true; // keep going }

u32 Row::dread(u32 off, Iovec& x) const { // to x.iov_base // read of dest.iov_len bytes at offset off to x.iov_base RowCut slice = (*this)(off, x.iov_len); return slice.zread(x); }

u32 RowCut::zread(Iovec& dest) const { // to dest.iov_base // requests a read of min(z_n,dest.iov_len) bytes at z_p; RowCut_hub_read hub(dest); this->_zwhile(RowCut_while_read, &hub); return hub.actual(); }

u32 RowCut::zread(Buf& outBuf) const { // to outBuf.v_p // actual bytes read returned in outBuf.v_n and the method. outBuf.v_n = 0; if (outBuf.b_x && outBuf.v_p) { Iovec dest; dest.iov_base = (void*) outBuf.v_p; dest.iov_len = outBuf.b_x; RowCut_hub_read hub(dest); this->_zwhile(RowCut_while_read, &hub); outBuf.v_n = hub.actual(); return outBuf.v_n; } return 0; }

u32 Row::dread(u32 off, Buf& outBuf) const { // to outBuf.v_p // read of outBuf.b_x bytes at offset off to outBuf.v_p; // actual bytes read are returned in outBuf.v_n & method. RowCut slice = (*this)(off, outBuf.b_x); return slice.zread(outBuf); }

u32 Row::dread(u32 off, void* outBuf, size_t len) const { // request read of len bytes at offset off to outBuf Iovec dest; dest.iov_base = outBuf; dest.iov_len = len; RowCut slice = (*this)(off, dest.iov_len); return slice.zread(dest); }

struct RowCut_hub_read_vec { // RowCut_while_read_vec closure u8* h_o; // start of Iovec to write u8* h_p; // where to write next byte u8* h_x; // one beyond last writable byte in Iovec u32 h_actual; RowCut_hub_read_vec(IovVec& vec) : h_o(0), h_p(0), h_x(0), h_actual(0) { Iovec& v = vec.v_base[0]; if (vec.v_len && v.iov_base && v.iov_len) { h_o = h_p = (u8*)v.iov_base; // start writable buf h_x = h_o + v.iov_len; // 1 after writable buf end } } u32 actual() const { return h_actual; } // number of bytes };

static bool RowCut_while_read_vec(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_read_vec& hub = *(RowCut_hub_read_vec*)closure; if (pt.p_second.iov_base && pt.p_second.iov_len) { u32 quantum = hub.h_x - hub.h_p; // space remaining if (quantum > pt.p_second.iov_len) quantum = pt.p_second.iov_len; ::memcpy(hub.h_p, pt.p_second.iov_base, quantum); hub.h_p += quantum; hub.h_actual += quantum; if (hub.h_p >= hub.h_x) // destination is full? return false; // don't need any more cut Iovecs } return true; // keep going }

u32 RowCut::zreadv(IovVec& dest) const { // to each iov_base // requests a read of min(z_n,dest.capacity()) bytes at z_p RowCut_hub_read_vec hub(dest); this->_zwhile(RowCut_while_read_vec, &hub); return hub.actual(); }

u32 Row::dreadv(u32 off, IovVec& x) const { // to each iov_base // requests a read of min(z_n,x.capacity()) bytes at z_p RowCut slice = (*this)(off, x.vcapacity()); return slice.zreadv(x); }

u32 Row::dreadv(u32 off, const Iovec* vector, size_t n) const { IovVec v(vector, n); return this->dreadv(off, v); }

struct RowCut_hub_crc32 { // closure for RowCut_while_crc32 Hash32& h_hash; RowCut_hub_crc32(Hash32& x): h_hash(x) { } };

static bool RowCut_while_crc32(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_crc32& hub = *(RowCut_hub_crc32*)closure; hub.h_hash.hadd(pt.p_second.iov_base, pt.p_second.iov_len); return true; // keep going }

Hash32& RowCut::zcrc(Hash32& hash) const { RowCut_hub_crc32 hub(hash); this->_zwhile(RowCut_while_crc32, &hub); return hash; }

///// ----- Row ----- ///// u32 Row::dunshare() { // cow every single shared Iovec in Row // unshare() returns number of bytes that had to be cow'ed. // (check: calling unshare() again right after returns 0.) // d2cowv() uses unshare() to actually prep Row for IovVec. u32 touched = 0; // total of copied bytes ++d_changes; // make iterators see changes Vt<Iovec>::iterator i = d_vv.vbegin(); Vt< GumT<cy_fix> >::iterator j = d_fgv.vbegin(); u32 maxRefs = 2; // 1 for d_fgv, 1 for FixGum handle = *j for ( ; i != d_vv.vend() && j != d_fgv.vend(); ++i, ++j) { Iovec old = *i; GumT<cy_fix> handle(*j); // NOTE: incr refcount by one. // Refcount equal to TWO means only this Row has an Iov // (it would be ONE, but we added another ref in gum). if (handle.gnice(maxRefs)) { // has more than maxRefs? cy_iovec cold = old; GumT<cy_fix> newGum; cy_iovec ciov = vat_copy_do(d_vat, cold, &newGum); if (ciov.iov_base) { Iovec copy(ciov); *i = copy; // replace old Iovec with new copy *j = newGum; // replace old handle with new touched += copy.iov_len; // sum bytes copied } } } return touched; }

IovVec Row::d2v() const { // Row as Iov array for read -- readv() if (d_size == 0) { // no content in this Row? return IovVec(0, 0); } Iovec* first = &d_vv.vfront(); return IovVec(first, d_vv.vsize()); }

IovVec Row::d2cowv() { // Row as Iovec array for writing -- writev() // if Row is sharing and has readonly cards, then COW them if (d_size == 0) { // no content in this Row? return IovVec(0, 0); } // make all Iovecs unique to this Row: u32 copied = this->dunshare(); if (copied > d_size) { cy_yellf(__LINE__,__FILE__, "copied='%lu' exceeds d_size='%lu'", (long) copied, (long) d_size); } Iovec* first = &d_vv.vfront(); return IovVec(first, d_vv.vsize()); }

Hash32& Row::dcrc(Hash32& hash) const { // add size() bytes to this crc Vt<Iovec>::const_iterator i; for (i = d_vv.vbegin(); i != d_vv.vend(); ++i) { Iovec v = *i; hash.hadd(v.iov_base, v.iov_len); } return hash; }

u32 Row::dhash() const { Hash32 x; this->dcrc(x); return x.hcrc(); }

void Row::_dappend(Iovec const& v, GumT<cy_fix> const& n) { if (e_dlive == d_tag) { // dgood()? if (v.iov_base && v.iov_len) { // any content to add? ++d_changes; // make iterators see changes ++d_len; u32 sz = v.iov_len; // number of bytes being added d_size += sz; // Row sequence has more bytes d_vv.vpush_back(v); // append Iov to end of vector d_fgv.vpush_back(n); // append metainfo (gum) } } else this->dbad(); }

u32 Row::_dscansize() const { // calculate size by iteration u32 sum = 0; // sum of bytes in every Iovec Vt<Iovec>::const_iterator i; for (i = d_vv.vbegin(); i != d_vv.vend(); ++i) { Iovec v = *i; sum += v.iov_len; } return sum; }

bool RowCut::_zwhile(Zwhile_fn fun, void* closure) const { // while (*fun)()=>true // please read long comment in header file. Here's last: // fun is called once for each discontiguous block of memory // in the [z_p:z_n] range of bytes, with the state of the // iteration passed in the RowIter argument. Row const* constRow = &z_d; // need to cast away const RowIter p(constRow); Row& d = *const_cast<Row*>(constRow); // mutable vectors p.p_pos = z_p; // MEMBER VAR: RowCut::z_p p.p_sum = 0; // sum of all bytes in earlier Iovecs p.p_count = 0; // count earlier Iovecs (index of this one) p.p_first.iov_base = p.p_second.iov_base = 0; p.p_first.iov_len = p.p_second.iov_len = 0; p.p_i = d.d_vv.vbegin(); p.p_j = d.d_fgv.vbegin(); u32 more = z_n; // number of bytes more we need to see u32 calls = 0; // number of times (*fun)() has been called for ( ; !calls && p.p_i != d.d_vv.vend() && p.p_j != d.d_fgv.vend() ; ++p.p_i, ++p.p_j) { Iovec v = *p.p_i; u32 quantum = v.iov_len; u32 zp = z_p; if (p.p_sum <= zp && p.p_sum+quantum > zp) { // start? // number to keep in first half: u32 split = z_p - p.p_sum; u8* base = (u8*) v.iov_base; p.p_first.iov_base = base; p.p_first.iov_len = split; p.p_second.iov_base = base + split; u32 part = quantum - split; if (part > more) part = more; more -= part; p.p_second.iov_len = part; ++calls; // end loop, but still update stats below if (!(*fun)(p, closure)) // stop while loop? return false; // return false only if fun does p.p_pos += part; } p.p_sum += quantum; ++p.p_count; } p.p_first.iov_base = 0; // p_first won't be used more below p.p_first.iov_len = 0; for ( ; more && p.p_i != d.d_vv.vend() && p.p_j != d.d_fgv.vend() ; ++p.p_i, ++p.p_j) { Iovec v = *p.p_i; u32 quantum = v.iov_len; // keep quantum for sum below u32 part = quantum; if (part > more) part = more; more -= part; p.p_second.iov_base = (u8*) v.iov_base; p.p_second.iov_len = part; ++calls; // end loop, but still update stats below if (!(*fun)(p, closure)) // stop while loop? return false; // return false only if fun does p.p_pos += quantum; p.p_sum += quantum; ++p.p_count; } return true; }

bool Row::_dseek(u32 offset, RowIter& p) const { // returns pt // _dseek() algorithm looks a lot _dbreak() [done 1st] // src/test/utils/flags/flags.debug -ez -tz Eye Row Cover p.p_pos = offset; p.p_sum = 0; // sum of all bytes in earlier Iovecs p.p_count = 0; // count earlier Iovs (index of this one) p.p_first.iov_base = p.p_second.iov_base = 0; p.p_first.iov_len = p.p_second.iov_len = 0; p.p_i = d_vv.vbegin(); p.p_j = d_fgv.vbegin(); for ( ; p.p_i != d_vv.vend() && p.p_j != d_fgv.vend() ; ++p.p_i, ++p.p_j) { Iovec v = *p.p_i; u32 quantum = v.iov_len; // should we break here? if (p.p_sum <= offset && p.p_sum+quantum > offset) { // number to keep in first half: u32 split = offset - p.p_sum; u8* base = (u8*) v.iov_base; p.p_first.iov_base = base; p.p_first.iov_len = split; p.p_second.iov_base = base + split; p.p_second.iov_len = quantum - split; return true; // we're finished } p.p_sum += quantum; ++p.p_count; } cy_yellf(__LINE__,__FILE__, "could not find position=%lu", offset); return false; }

void Row::_dbreak(u32 offset) { // internal: make Iovec boundary at offset // if offset is in an existing Iov, break Iov in two here. if (offset) { // not starting pos which is already a break? ++d_changes; // make iterators see changes u32 sum = 0; Vt<Iovec>::iterator i = d_vv.vbegin(); Vt< GumT<cy_fix> >::iterator j = d_fgv.vbegin(); for ( ; i != d_vv.vend() && j != d_fgv.vend() ; ++i, ++j) { Iovec v = *i; u32 quantum = v.iov_len; // should we break here? if (sum < offset && sum+quantum > offset) { // number to keep in first half: u32 split = offset - sum; u8* base = (u8*) v.iov_base; Iovec first; // first half of divide first.iov_base = (void*) base; first.iov_len = split; Iovec second; // second half of divide second.iov_base = (void*) (base + split); second.iov_len = quantum - split; ++d_len; // vector will have another piece // copy of same gum in new piece: GumT<cy_fix> gum(*j); *i = second; // make second piece smaller d_vv.vinsert(i, first); d_fgv.vinsert(j, gum); // same ref return; // we're finished } sum += quantum; if (sum == offset) // already broken at Iov edge? return; // we're finished } } }

void Row::dswap(Row& from) { // exchange contents with another Row temp(from, from.d_vat); from.dassign(*this); this->dassign(temp); }

void Row::dzap(Cut const& z) { // same as z_d.erase(z_p,z_n); Cut absolute(z, d_size); this->derase(absolute.z_p, absolute.z_n); } void Row::derase(u32 pos, u32 len) { // remove bytes from Row // cut len bytes from Row at position pos; afterward, all // downstream bytes now appear at offsets that are len less. if (e_dlive == d_tag) { // Row looks non-corrupt? if (pos < d_size && len) { // anything to erase? u32 end = pos + len; // end of deleted range if (end >= d_size) // everything from pos onward? this->_dtruncate(pos); // cut at and after pos else { if (pos == 0) { // removal from very 1st byte? this->_dtail(len); } else { // both start & end have bytes both sides this->_delide(pos, end); } } } else { // either pos > d_size or len == 0, so no-op } //_dgoodsize(); // debug only vlen consistency check } else this->dbad(); }

void Row::_delide(u32 start, u32 end) { // delete start to end // NOTE: end is a pos KEPT in Row; remove end - start bytes if (end <= start) // empty range? return; RowIter from(this); RowIter to(this); if (this->_dseek(start, from) && this->_dseek(end, to)) { ++d_changes; // make iterators see changes d_size -= (end - start); if (from.p_count == to.p_count) { // change only 1 Iov? this->_dholepunch(from, to); } else { // start and end are in different Iovecs u32 eraseCount = 0; // number of Iovecs to erase if ( to.p_count > from.p_count ) { eraseCount = to.p_count - from.p_count; } else { // should not be able to reach this branch cy_yellf(__LINE__,__FILE__, "to.p_count=%lu ?? from.p_count=%lu", (long) to.p_count, (long) from.p_count); } if (from.p_first.iov_len) { // keep part of start Iov? // keep only leading part of Iovec: *from.p_i = from.p_first; ++from.p_i; // do NOT erase this vector member ++from.p_j; // do NOT erase this vector member if (eraseCount) --eraseCount; // needn't to erase 1st one } if (to.p_first.iov_len) { // remove part of end Iov? // keep only leading part of Iovec *to.p_i = to.p_second; } if (eraseCount) { // any Iovs need outright erase? d_len -= eraseCount; d_vv.verase(from.p_i, to.p_i); d_fgv.verase(from.p_j, to.p_j); } } } }

void Row::_dholepunch(RowIter& from, RowIter& to) { // elide special case // from and to are the same Iovec containing start and end // keep both ends? if (from.p_first.iov_len && to.p_second.iov_len) { // code like _dbreak(), but with a nonzero sized gap GumT<cy_fix> gum(*to.p_j); // copy same ref, new piece *to.p_i = to.p_second; // make second piece smaller ++d_len; d_vv.vinsert(to.p_i, from.p_first); d_fgv.vinsert(to.p_j, gum); // same ref } else if (from.p_first.iov_len) { // keep some of start only? *from.p_i = from.p_first; // only leading part of Iov } else if (to.p_second.iov_len) { // keep some of end only? *to.p_i = to.p_second; // only leading part of Iov } }

void Row::_dtail(u32 head) { // remove head bytes from start ++d_changes; // make iterators see changes d_size -= head; u32 sum = 0; // total bytes in earlier Iovecs u32 count = 0; // count of earlier Iovecs Vt<Iovec>::iterator i = d_vv.vbegin(); Vt< GumT<cy_fix> >::iterator j = d_fgv.vbegin(); for ( ; i != d_vv.vend() && j != d_fgv.vend(); ++i, ++j) { Iovec v = *i; u32 quantum = v.iov_len; if (sum == head) { // erase all earlier Iovecs & nodes d_len -= count; d_vv.verase(d_vv.vbegin(), i); d_fgv.verase(d_fgv.vbegin(), j); return; // we're finished } else if (sum+quantum > head) { // need to break here? u32 split = head - sum; // number kept in 1st half u8* base = (u8*) v.iov_base; Iovec first; // first half of divide first.iov_base = base; first.iov_len = split; Iovec second; // second half of divide second.iov_base = base + split; second.iov_len = quantum - split; *i = second; // make second piece smaller if ( count ) { // any earlier Iovecs to remove? d_len -= count; d_vv.verase(d_vv.vbegin(), i); // earlier Iovs d_fgv.verase(d_fgv.vbegin(), j); } return; // we're finished } sum += quantum; ++count; // count all earlier Iovecs } }

void Row::_dtruncate(u32 newSize) { // internal // 1) iterate Iovec vector until newSize bytes are seen. // 2) shorten the last partial Iovec if it's too long. // 3) resize both vectors to remove any content afterward. ++d_changes; // make iterators see changes if (newSize) { // keeping any old content? u32 sum = 0; // sum of bytes in Iovecs seen u32 count = 0; // number of Iovecs to keep Vt<Iovec>::iterator i; for (i = d_vv.vbegin(); i != d_vv.vend(); ++i) { Iovec v = *i; sum += v.iov_len; ++count; if (sum == newSize) { // PERFECT fit? break; // all done: remove extra vector members } else if (sum > newSize) { // too much: update Iov? // NOTE: new Iov size DOES NOT alter content, so // copy-on-write is not needed to truncate Iovs. u32 here = v.iov_len; // bytes in this Iovec u32 over = sum - newSize; // excess bytes if (over <= here) v.iov_len -= over; else { cy_logf(0, "iov_len=%lu less than over=%lu", (long) here, (long) over); v.iov_len = 0; // make it empty for safety } *i = v; // update this vector member break; } } d_len = count; d_size = newSize; d_vv.vresize(count); d_fgv.vresize(count); } else this->dclear(); // remove everything }

void Row::dresize(u32 sz, u8 val) { // change size, use val as fill; if new space is added, // every new byte contains val as the value. if (sz > d_size) // become larger? this->dappend(sz - d_size, val); else if (sz < d_size) { // become smaller? this->_dtruncate(sz); } //_dgoodsize(); // debug only vlen consistency check }

void Row::dbad() const { // report take is wrong if (e_dlive == d_tag) { cy_logf(0, "d_tag=0x%lx as expected (why dbad()?)", (long) d_tag); } else if (e_ddead == d_tag) { cy_logf(0, "d_tag=e_dead=0x%lx DEAD! (e_dlive=0x%lx)", (long) d_tag, (long) e_dlive); } else { cy_logf(0, "d_tag=0x%lx expected: e_dlive=0x%lx", (long) d_tag, (long) e_dlive); } }

Row::Row() // empty: containing zero bytes & one empty Iovec : d_vat(cy_global_heapvat()) , d_size(0) // sum of USED Iovecs: return value for size() , d_len(0) // current used length of vector , d_changes(0) // count of changes so Iter knows to sync , d_tag(e_dlive) { }

Row::~Row() { if (this->dclear()) d_tag = e_ddead; }

bool Row::dclear() { // remove content; after: size() == 0 if (this->dgood()) { ++d_changes; // make iterators see changes d_vv.vclear(); // empty the Iovec vector d_fgv.vclear(); // empty the metainfo vector d_len = 0; d_size = 0; // no content return true; } else this->dbad(); return false; } Row::Row(const char* cstring, cy_vat* v) // global null-terminated c string; causes byte sequence to // contain strlen(cstring) bytes whose content is identical to // content inside cstring. This behaves just like a copy of // cstring. But cstring might be used by pointer instead of // being copied (assume cstring has permanent static storage). : d_vat(v) , d_size(0) // sum of USED Iovecs: return value for size() , d_len(0) // current used length of vector , d_changes(0) // count of changes so Iter knows to sync , d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(cstring); #ifdef APPEND_BY_ALIAS_INSTEAD_OF_COPY if (cstring) { ++d_changes; // make iterators see changes u32 slen = (u32) ::strlen(cstring); d_size = slen; Iovec first; // alias pointer (NOT a copy): first.iov_base = (void*) cstring; first.iov_len = (size_t) slen; GumT<cy_fix> handle; ++d_len; d_vv.vpush_back(first); d_fgv.vpush_back(handle); } #endif /*APPEND_BY_ALIAS_INSTEAD_OF_COPY*/ }

Row::Row(u32 sz, u8 val, cy_vat* v) : d_vat(v), d_size(0), d_len(0), d_changes(0), d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(sz, val); } Row::Row(Row const& other, cy_vat* v) // deep copy other : d_vat(v), d_size(0), d_len(0), d_changes(0), d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(other); } Row::Row(RowCut const& cut, cy_vat* v) : d_vat(v), d_size(0), d_len(0), d_changes(0), d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(cut); } Row::Row(Iov const& frag, cy_vat* v) : d_vat(v), d_size(0), d_len(0), d_changes(0), d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(frag); } Row::Row(Iovec const& frag, cy_vat* v) : d_vat(v), d_size(0), d_len(0), d_changes(0), d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(frag); } Row::Row(IovVec const& array, cy_vat* v) : d_vat(v), d_size(0), d_len(0), d_changes(0), d_tag(e_dlive) { if (!v) // need to use default vat? d_vat = cy_global_heapvat(); this->dappend(array); }

bool Row::_dreserve(u32 vlen) { // (ensure min capacity) if (e_dlive == d_tag) { // dgood()? // good idea when we grow by several new members at once: this->d_vv.vreserve(vlen); this->d_fgv.vreserve(vlen); return true; } else this->dbad(); return false; }

Row& Row::dappend(u32 sz, u8 val) { GumT<cy_fix> newGum; cy_iovec newIov = vat_alloc_do(d_vat, sz, &newGum); if (newIov.iov_base) { ::memset(newIov.iov_base, val, sz); Iovec last(newIov); this->_dappend(last, newGum); } //_dgoodsize(); // debug only vlen consistency check return *this; } Row& Row::dappend(const char* copy) { // same as insert(size(), copy); if (copy && *copy) { // non-nil and non-empty? Iov r(copy); this->dappend(r); } return *this; } Row& Row::dappend(Iov const& copy) { // same as insert(size(), copy); GumT<cy_fix> newGum; cy_iovec src = copy; cy_iovec ciov = vat_copy_do(d_vat, src, &newGum); if (ciov.iov_base) { Iovec last(ciov); this->_dappend(last, newGum); } //_dgoodsize(); // debug only vlen consistency check return *this; } Row& Row::dappend(Iovec const& v) { // same as insert(size(), copy); Iov r(v.iov_base, v.iov_len); this->dappend(r); //_dgoodsize(); // debug only vlen consistency check return *this; }

struct RowCut_hub_append { // closure for RowCut_while_append Row& h_Row; RowCut_hub_append(Row& x): h_Row(x) { } };

static bool RowCut_while_append(RowIter& pt, void* closure) { // typedef bool (*Zwhile_fn)(RowIter& pt, void* closure); RowCut_hub_append& hub = *(RowCut_hub_append*)closure; GumT<cy_fix> handle(*pt.p_j); hub.h_Row._dappend(pt.p_second, handle); return true; // keep going } Row& Row::dappend(RowCut const& cut) { // same as insert(size(), cut); if (&cut.z_d == this) { // self modification? Row copy(cut, d_vat); // make a new Row first this->dappend(copy); } else { RowCut_hub_append hub(*this); cut._zwhile(RowCut_while_append, &hub); } //_dgoodsize(); // debug only vlen consistency check return *this; } Row& Row::dappend(Row const& dk) { // same as insert(size(), Row); //u32 dsz = d->_dscansize(); // perfect accounting of size if (this == &dk) { // self modification? Row copy(dk, d_vat); // make a new Row first this->dappend(copy); // NOTE: recursion } else { bool anything = (dk.d_len || dk.d_size); // any input? if (anything && this->_dreserve(d_len + dk.d_len) ) { Vt<Iovec>::const_iterator i = dk.d_vv.vbegin(); Vt< GumT<cy_fix> >::const_iterator j = dk.d_fgv.vbegin(); for ( ; i != dk.d_vv.vend() && j != dk.d_fgv.vend() ; ++i, ++j) { this->_dappend(*i, *j); } } } //_dgoodsize(); // debug only vlen consistency check return *this; }

void Row::dinsert(u32 offset, u32 sz, u8 val) { // insert sz bytes of val if (sz) { // anything to insert? if (offset >= d_size) { // at or after end of content? this->dappend(sz, val); } else { // anything to insert? Row inbound(sz, val, d_vat); // temp uniform source form this->dinsert(offset, inbound); // insert as Row } } } void Row::dinsert(u32 offset, const char* copy) { // insert copy at offset if (copy && *copy) { // non-nil and non-empty? Iov r(copy); this->dinsert(offset, r); } } void Row::dinsert(u32 offset, Iov const& copy) { // insert copy at offset if (offset >= d_size) { // at or after end of all content? this->dappend(copy); } else if (copy.v_p && copy.v_n) { // anything to insert? Row inbound(copy, d_vat); // temp uniform source form this->dinsert(offset, inbound); // always insert as Row } } void Row::dinsert(u32 offset, Iovec const& copy) { // insert copy at offset if (offset >= d_size) { // at or after end of all content? this->dappend(copy); } else if (copy.iov_base && copy.iov_len) { // anything? Row inbound(copy, d_vat); // temp uniform source form this->dinsert(offset, inbound); // always insert as Row } //_dgoodsize(); // debug only vlen consistency check } void Row::dinsert(u32 offset, RowCut const& cut) { // copy Row subset if (offset >= d_size) { // at or after end of all content? this->dappend(cut); } else if (!cut.zempty()) { // anything to insert? Row inbound(cut, d_vat); // temp uniform source form this->dinsert(offset, inbound); // always insert as Row } //_dgoodsize(); // debug only vlen consistency check }

void Row::dinsert(u32 offset, Row const& row) { // copy row if (offset >= d_size) { // at or after end of all content? this->dappend(row); } else if (row.d_size) { // anything to insert? ++d_changes; // make iterators see changes this->_dbreak(offset); // force edge between Iovs here u32 sum = 0; // used to count Vt<Iovec>::iterator i = d_vv.vbegin(); Vt< GumT<cy_fix> >::iterator j = d_fgv.vbegin(); for ( ; sum < offset && i != d_vv.vend() && j != d_fgv.vend() ; ++i, ++j) { Iovec v = *i; sum += v.iov_len; // skip Iovecs til offset bytes passed } d_vv.vinsert(i, row.d_vv.vbegin(), row.d_vv.vend()); d_fgv.vinsert(j, row.d_fgv.vbegin(), row.d_fgv.vend()); d_len += row.d_len; d_size += row._dscansize(); // row is bigger by size of other } //_dgoodsize(); // debug only vlen consistency check } void Row::dinsert(u32 offset, IovVec const& array) { // copy array content if (offset >= d_size) { // at or after end of all content? this->dappend(array); } else if (array.vcapacity()) { // anything to insert? Row inbound(array, d_vat); // temp uniform source form this->dinsert(offset, inbound); // always insert as row } //_dgoodsize(); // debug only vlen consistency check }

u32 Row::dwrite(u32 off, const char* literal) { // reads from literal // request ::strlen(literal) bytes from literal to offset if (literal && *literal) { Iovec src; src.iov_base = (void*) literal; src.iov_len = ::strlen(literal); return this->dwrite(off, src); } return 0; } u32 Row::dwrite(u32 off, Iov const& v) { // reads from inRun // request write of v.v_n bytes from v.v_p to offset off; if (v.v_p && v.v_n) { Iovec src; src.iov_base = (void*) v.v_p; src.iov_len = v.v_n; return this->dwrite(off, src); } return 0; } u32 Row::dwrite(u32 off, const void* inBuf, size_t len) { // request write of len bytes from inBuf to offset off Iovec src; src.iov_base = (void*) inBuf; src.iov_len = len; return this->dwrite(off, src); } u32 Row::dwrite(u32 off, Iovec const& src) { // reads from dest.iov_base; request write of src.iov_len // bytes from src.iov_base to offset off // return value is actual bytes written if (d_size < off && off < (256*1024*1024)) this->dresize(off, 0); u32 cap = src.iov_len; this->derase(off, cap); this->dinsert(off, src); return cap; }

u32 Row::dwritev(u32 off, IovVec const& src) { // each iov_base // requests a write of src.capacity() bytes at offset off if (d_size < off && off < (256*1024*1024)) this->dresize(off, 0); u32 cap = src.vcapacity(); this->derase(off, cap); this->dinsert(off, src); return cap; } u32 Row::dwritev(u32 off, const Iovec* vector, size_t count) { IovVec v(vector, count); return this->dwritev(off, v); }

//define ROW_USE_TMP_STAGE 1 Row& Row::dappend(IovVec const& v) { // same as insert(size(), array); #ifdef ROW_USE_TMP_STAGE Iovec vtmp; char tmp[512]; // temp staging for copies of small pieces vtemp.iov_base = tmp; vtemp.iov_len = 0; u32 room = 512; // byte remaining in tmp staging buffer #endif /*ROW_USE_TMP_STAGE*/ if (v.v_len && this->_dreserve(d_len + v.v_len) ) { const Iovec* array = v.v_base; for (size_t i = 0; i < v.v_len; i++) { // another Iov? this->dappend(array[i]); } } //_dgoodsize(); // debug only vlen consistency check return *this; }

Row& Row::dassign(Row const& row) { // same as copy constructor this->dclear(); this->dappend(row); return *this; } Row& Row::dassign(RowCut const& cut) { // copy or share subset this->dclear(); this->dappend(cut); return *this; } Row& Row::dassign(const char* fragment) { // copy fragment this->dclear(); if (fragment && *fragment) { // non-nil and non-empty? Iov iov(fragment); this->dappend(iov); } return *this; } Row& Row::dassign(Iov const& fragment) { // copy fragment this->dclear(); this->dappend(fragment); return *this; } Row& Row::dassign(Iovec const& fragment) { // copy fragmen this->dclear(); this->dappend(fragment); return *this; } Row& Row::dassign(IovVec const& array) { // copy Iov array // like (but faster than): d.erase(0,~0); d.write(array); this->dclear(); this->dappend(array); return *this; }

void Row::dshow(cy_sink& fo, Iovec const& v, u32 index, Buf& arcBuf, u32 sum) { arcBuf("iov[%lu] sum=%lu", (long) index, (long) sum); Iov iov(v.iov_base, v.iov_len); iov.vshow(fo, (const char*) arcBuf.v_p, /*maxbytes*/ 8192); }

void Row::_dgoodsize() { // check : d_len is size of d_vv & d_fgv long vsize = (long) d_vv.vsize(); long nsize = (long) d_fgv.vsize(); if ( vsize != nsize || vsize != (long) d_len) { cy_logf(0, "mismatched d_len=%ld vv-sz=%ld nhv-sz=%ld", (long) d_len, vsize, nsize); } }

void Row::dcite(cy_sink& o) const { // show summary row info //char arc[128]; Buf arcBuf(arc, 0, 128); if (cy_unlikely(!this->dgood())) this->dbad(); Hash32 myCrc; myCrc << *this; cy_sink_ft(&o, "<Row me=%lx sz=%lu len=%lu chg=%lu crc=%08lx:%lu>", (long) this, (long) d_size, (long) d_len, (long) d_changes, (long) myCrc.hcrc(), (long) myCrc.hlen()); bool mid = false; long vsize = (long) d_vv.vsize(); long nsize = (long) d_fgv.vsize(); if (cy_unlikely(vsize != nsize || vsize != (long) d_len)) { o << cy_endl; cy_sink_fn(&o, "<mismatch len=%ld d_vv-size=%ld d_fgv-size=%ld/>", (long) d_len, vsize, nsize); o << cy_endl; mid = true; } u32 expectedSize = this->_dscansize(); // add all iov_len if (cy_unlikely(expectedSize != d_size)) { o << cy_endl; cy_sink_fn(&o, "<size-DRIFT d_size=%ld expected=%ld/>", (long) d_size, (long) expectedSize); o << cy_endl; mid = true; } if (mid) { // stuff in middle? need a newline? cy_sink_unend(&o, "Row"); } else { o << cy_subi << "</Row>"; } }

u32 Row::dout(IovOut& v) const { // copy to v, unchanged u32 actual = 0; Vt<Iovec>::const_iterator i; for (i = d_vv.vbegin(); i != d_vv.vend(); ++i) { Iovec x = *i; actual += v.owrite(x.iov_base, x.iov_len); } if (actual != v.oactual()) { cy_logf(0, "actual=%lu DIFFERS: v.actual()=%lu", (long) actual, (long) v.oactual()); } return actual; } /*u32 Row::dout(std::ostream& s) const { // copy to s, unchanged u32 actual = 0; Vt<Iovec>::const_iterator i; for (i = d_vv.vbegin(); i != d_vv.vend(); ++i) { Iovec v = *i; actual += v.iov_len; s.write((const char*) v.iov_base, v.iov_len); } // s.flush(); return actual; }*/ u32 Row::dout(Buf& b) const { // copy to b, unchanged u32 before = b.v_n; Vt<Iovec>::const_iterator i; for (i = d_vv.vbegin(); i != d_vv.vend() && b.v_n < b.b_x; ++i) { Iovec v = *i; Iov iov(v.iov_base, v.iov_len); b.bappend(iov); } return b.v_n - before; } u32 Row::dout(cy_sink& o) const { // copy to o, unchanged // read every byte & write them efficiently to out stream o u32 actual = 0; Vt<Iovec>::const_iterator i; for (i = d_vv.vbegin(); i != d_vv.vend(); ++i) { Iovec v = *i; actual += sink_write_do(&o, v.iov_base, v.iov_len); } o << cy_now; return actual; }

void Row::ddump(cy_sink& o) const { // show EVERYTHING char arc[128]; Buf arcBuf(arc, 0, 128); if (!this->dgood()) dbad(); Hash32 myCrc; myCrc << *this; // = this->dhash(); RowIterByte j8(*this); Hash32 itCrc; itCrc << j8; RowArray jspy(*this); Hash32 spCrc; spCrc << jspy; cy_sink_ft(&o, "<Row me=%lx sz=%lu len=%lu chg=%lu", (long) this, (long) d_size, (long) d_len, (long) d_changes); o << cy_addi << cy_endl; cy_sink_f(&o, "crc=%08lx:%lu it=%08lx:%lu spy=%08lx:%lu>", (long) myCrc.hcrc(), (long) myCrc.hlen(), (long) itCrc.hcrc(), (long) itCrc.hlen(), (long) spCrc.hcrc(), (long) spCrc.hlen()); o << cy_subi << cy_endl; long vsize = (long) d_vv.vsize(); long nsize = (long) d_fgv.vsize(); if ( vsize != nsize || vsize != (long) d_len) { o << cy_endl; cy_sink_fn(&o, "<mismatch len=%ld d_vv-size=%ld d_fgv-size=%ld/>", (long) d_len, vsize, nsize); o << cy_endl; } u32 expectedSize = this->_dscansize(); // add all iov_len if (expectedSize != d_size) { o << cy_endl; cy_sink_fn(&o, "<size-DRIFT d_size=%ld expected=%ld/>", (long) d_size, (long) expectedSize); o << cy_endl; } u32 a = 0; // array index Vt<Iovec>::const_iterator i; Vt< GumT<cy_fix> >::const_iterator j; u32 sizeSum = 0; cy_sink_ft(&o, "<Iovecs name=d_vv size=%ld cap=%ld>", (long) vsize, (long) d_fgv.vcapacity()); for (a = 0, i = d_vv.vbegin(); i != d_vv.vend() ; ++i, ++a) { Iovec v = *i; o << cy_endl; sizeSum += v.iov_len; Row::dshow(o, v, a, arcBuf, sizeSum); } cy_sink_unend(&o, "Iovecs"); o << cy_endl; cy_sink_ft(&o, "<nodes name=d_fgv size=%ld cap=%ld>", (long) nsize, (long) d_fgv.vcapacity()); for (a = 0, j = d_fgv.vbegin(); j != d_fgv.vend() ; ++j, ++a) { GumT<cy_fix> handle(*j); arcBuf("node[%lu]", (long) a); o << cy_endl << handle.quote(arc); } cy_sink_unend(&o, "nodes"); cy_sink_unend(&o, "Row"); if (myCrc != itCrc) { o << cy_now; cy_logf(0, "crc=/#%lx:%lu/ != itCrc=/#%lx:%lu/", (long) myCrc.hcrc(), (long) myCrc.hlen(), (long) itCrc.hcrc(), (long) itCrc.hlen()); } if (myCrc != spCrc) { o << cy_now; cy_logf(0, "crc=/#%lx:%lu/ != spCrc=/#%lx:%lu/", (long) myCrc.hcrc(), (long) myCrc.hlen(), (long) spCrc.hcrc(), (long) spCrc.hlen()); } }

RowIterByte Row::begin() const { return RowIterByte(*this); } RowIterByte Row::rbegin() const { RowIterByte it(*this, this->dsize()); --it; return it; }

class RowIterByte { // iterate one u8 byte at a time protected: const Row* p_d; long p_pos; // pos of current byte u8* p_0; // origin (start of Iovec) u8* p_p; // point (current position) u8* p_x; // max (one past end of Iovec) u16 p_end[2]; void _rend() { p_pos = -1; p_0 = p_p = p_x = (u8*) p_end; } void _fend() { p_pos = (long) p_d->dsize(); p_0 = p_p = p_x = (u8*) p_end; } public: RowIterByte(const Row& d); // pseek(0); // at 1st byte, u32 pos RowIterByte(const Row& d, p32 pos); // pseek(pos); // at 1st byte const Row& pd() const { return *p_d; } u32 psize() const { return p_d->dsize(); } long ppos() const { return p_pos; } // current position const u8* pseek(long pos); // note: -1 is before first byte const u8* pinc() { ++p_pos; return (++p_p < p_x)? p_p : pseek(p_pos); } const u8* pdec() { --p_pos; return (--p_p > p_0)? p_p : pseek(p_pos); } const u8* operator++() { ++p_pos; return (++p_p < p_x)? p_p : pseek(p_pos); } const u8* operator--() { --p_pos; return (--p_p > p_0)? p_p : pseek(p_pos); } u8 operator*() const { return *p_p; } const u8* begin() { return this->pseek(0); } void* end() { return p_end; } protected: template <class T> T* pfocus(const u8* p, T* copy) { const u8* after = p + sizeof(T); // after this T in Iov if (after <= p_x) { // whole contig copy of T in Iov? *copy = *(T*) p; // Intel works at any aligment return copy; } else { // try to copy entire next T into local p_t buf u32 actual = p_d->dread((u32) p_pos, copy, sizeof(T)); if (actual == sizeof(T)) { return copy; } } return 0; } private: // postfix is bad idea because it's not efficient const u8* operator++(int) { return 0; } const u8* operator--(int) { return 0; } public: // for comparison with Row::u8_end() bool operator==(Row::Dx const&) const { return p_p==(u8*)p_end; } bool operator!=(Row::Dx const&) const { return p_p!=(u8*)p_end; } bool pmore() const { return p_p!=(u8*)p_end; } //operator bool() const { return p_p!=(u8*)p_end; } public: // printing struct Pq { RowIterByte const& q_p; Pq(RowIterByte const& p): q_p(p) { } }; Pq quote() const { return Pq(*this); } void pdump(cy_sink& o) const; Hash32& pcrc(Hash32& hash) const; u32 phash() const; // { Hash32 x; this->pcrc(x); return x.hcrc(); } }; // class RowIterByte

inline cy_sink& operator<<(cy_sink& o, RowIterByte::Pq const& x) { x.q_p.pdump(o); return o; } inline Hash32& operator<<(Hash32& h, RowIterByte const& x) { x.pcrc(h); return h; }

///// ----- RowIterByte ----- ///// RowIterByte::RowIterByte(const Row& d) : p_d(&d), p_pos(-1), p_0(0), p_p(0), p_x(0) { p_end[0] = 0xeeee; p_end[1] = 0xeded; this->pseek(0); } RowIterByte::RowIterByte(const Row& d, p32 pos) : p_d(&d), p_pos(-1), p_0(0), p_p(0), p_x(0) { p_end[0] = 0xeeee; p_end[1] = 0xeded; this->pseek(pos); }

#if defined(ROW_EYE_ECHO) && defined(ROW_MAX_DEBUG) static const char* Row_true = "true"; static const char* Row_false = "false"; #endif /*ROW_EYE_ECHO && ROW_MAX_DEBUG*/ Hash32& RowIterByte::pcrc(Hash32& hash) const { // add size() more bytes to this crc u8 temp[13]; // add 13 bytes at a time to the crc u8* after = temp + 13; // one after last byte in temp const Row& d = *p_d; RowIterByte j = d.begin(); // seek(0); while (j != d.end()) { u8* t; // first byte in temp for ( t = temp; t < after && j != d.end(); ++j) { *t++ = *j; } if (t > temp) { // any new content inside temp? hash.hadd(temp, t - temp); } } return hash; }

u32 RowIterByte::phash() const { Hash32 x; this->pcrc(x); return x.hcrc(); }

const u8* RowIterByte::pseek(long pos) { // note: -1 is before 1st byte //p_changes = p_d->changes(); // refresh cached row changes u32 dsz = p_d->dsize(); // 1 beyond last addressable byte // note: if pos < 0, then ((u32) pos) is a very big value. if (pos >= 0 && (u32) pos < dsz) { // pos is in sequence? RowIter pt(p_d); if (p_d->_dseek((u32) pos, pt)) { p_pos = (long) pt.p_pos; p_0 = (u8*) pt.p_first.iov_base; p_p = (u8*) pt.p_second.iov_base; p_x = p_p + pt.p_second.iov_len; } else { p_pos = -1; p_0 = p_p = p_x = (u8*) p_end; return 0; // nil seek() means *this == row.end() } } else { // position is outside sequence if (pos < 0) { // "before" first byte in the sequence? // inline version of endBackward(): p_pos = -1; p_0 = p_p = p_x = (u8*) p_end; } else { // beyond end of sequence? // inline version of endForward(): p_pos = (long) dsz; // one beyond end of sequence p_0 = p_p = p_x = (u8*) p_end; } return 0; // nil seek() means *this == row.end() } return p_p; }

void RowIterByte::pdump(cy_sink& o) const { cy_sink_ft(&o, "<RowIterByte row=%lx pos=%ld ", (long) p_d, (long) p_pos); if ( p_p == (u8*) p_end ) { o << "at=END>" << cy_endl; } else if ( p_0 < p_x ) { cy_sink_fn(&o, "at='+%lu inside ^%lx:%lu'>", (long) (p_p - p_0), (long) p_0, (long) (p_x - p_0)); } else { o << "at=garbled>" << cy_endl; } p_d->dcite(o); o << cy_endl; bool showBefore = (p_0 < p_p); if (showBefore) { Iov before(p_0, p_p - p_0); before.vshow(o, "before", /*maxbytes*/ 8192); } if (p_p < p_x) { if (showBefore) o << cy_endl; Iov after(p_p, p_x - p_p); after.vshow(o, "after", /*maxbytes*/ 8192); } o << cy_subi << cy_endl << "</RowIterByte>"; }

template <class T> class RowIterT : public RowIterByte { // one T at a time protected: T t_t; // single instance of T public: T const& operator*() const { return t_t; } public: RowIterT<T>(const Row& d) : RowIterByte(d) { if(pseek(0)) pfocus(p_p, &t_t); } RowIterT<T>(const Row& d, u32 pos) : RowIter(d) { if(pseek((long) pos)) pfocus(p_p, &t_t); } // NOTE: byte offset pos indexes BYTES, not T instances. const T* operator++() { // ++ prefix operator p_pos += sizeof(T); // position of next T p_p += sizeof(T); // advance Iovec pointer to next T const u8* t = (p_p < p_x)? p_p : this->pseek(p_pos); // next T if (t) { T* copy = this->pfocus(t, &t_t); if (!copy) { // ensure *this==row.end() is true this->endForward(); } return copy; } return 0; } const T* operator--() { // -- prefix operator p_pos -= sizeof(T); // position of next T p_p -= sizeof(T); // advance Iovec pointer to next T const u8* t = (p_p >= p_0)? p_p : this->pseek(p_pos); // next T if (t) { T* copy = this->pfocus(t, &t_t); if (!copy) { // ensure *this==row.end() is true this->endBackward(); } return copy; } return 0; } }; // class RowIterT<T>

template <class T> RowIterT<T> Row::tbegin() const { return RowIterT<T>(*this); }

template <class T> RowIterT<T> Row::trbegin() const { u32 count = d_size / sizeof(T); // number of T's inside if (count) { // at least one T inside this row? return RowIterT<T>(*this, sizeof(T)*(count-1)); // last } return RowIterT<T>(*this, d_size+1); // beyond last T in row }

struct RowIter { // a pointer inside Row const Row* p_d; p32 p_pos; // position targeted by seek u32 p_sum; // sum of bytes in earlier Iovecs u32 p_count; // count of earlier Iovecs (index of this one) Iovec p_first; // bytes BEFORE p_pos in target Iovec Iovec p_second; // bytes AT and AFTER p_pos in target Iovec mutable Vt<Iovec>::iterator p_i; // holds p_pos in d_vv mutable Vt< GumT<cy_fix> >::iterator p_j; // holds p_pos in d_fgv explicit RowIter(const Row* d) : p_d(d), p_pos(0), p_sum(0), p_count(0) { } RowIter(const Row* d, p32 at) : p_d(d), p_pos(at), p_sum(0), p_count(0) { } u32 ptouch(); // force COW of shared Iovec during iteration // NOTE: assumes 1 ref for Iovec & 1 ref for iter for max=2 struct Pq { RowIter const& q_p; Pq(RowIter const& p): q_p(p) { } }; Pq quote() const { return Pq(*this); } // to request dump void pdump(cy_sink& o) const; void pcite(cy_sink& o) const; }; // struct RowIter

inline cy_sink& operator<<(cy_sink& o, RowIter::Pq const& x) { x.q_p.pdump(o); return o; }

///// ----- RowIter ----- ///// void RowIter::pdump(cy_sink& o) const { // show EVERYTHING cy_sink_nf(&o, "<RowIter pos=%lu sum=%lu cnt=%lu 1st=%lx:%lu 2nd=%lx:%lu/>", (long) p_pos, (long) p_sum, (long) p_count, (long) p_first.iov_base, (long) p_first.iov_len, (long) p_second.iov_base, (long) p_second.iov_len); }

class RowArray { // view of Row as byte array protected: const Row* a_d; u32 a_max; // cached copy of a_d->dsize() u32 a_lo; // pos of lowest byte inside Iov u32 a_hi; // just above high byte: a_lo+a_v.len Iov a_v; // current run of contiguous bytes u8 a_miss[4]; // used for out of bounds indexes bool _afocus(u32 pos); // make a_v describe containing Iovec public: // construction and values directly from construction: RowArray(const Row& d); // _afocus(0); // at first byte const Row& ad() const { return *a_d; } u32 asize() const { return a_d->dsize(); } u32 amax() const { return a_max; } // cached a_d->dsize() u8 operator[](u32 i); // read single byte public: // printing Hash32& acrc(Hash32& dest) const; u32 ahash() const; struct Aq { RowArray const& q_a; Aq(RowArray const& a): q_a(a) { } }; Aq quote() const { return Aq(*this); } // for printing void adump(cy_sink& o) const; void acite(cy_sink& o) const; }; // class IovOut

inline cy_sink& operator<<(cy_sink& o, RowArray::Aq const& x) { x.q_a.adump(o); return o; } inline Hash32& operator<<(Hash32& h, RowArray const& x) { x.acrc(h); return h; }

///// ----- RowArray ----- ///// RowArray::RowArray(const Row& d) : a_d(&d), a_max(d.dsize()), a_lo(0) , a_hi(a_max), a_v(a_miss,4) { if (a_max) this->_afocus(0); } u8 RowArray::operator[](u32 i) { // valid index i: 0 <= i < a_max if (i < a_lo || i >= a_hi) // not inside a_v? this->_afocus(i); u32 index = i - a_lo; if (index < a_v.v_n) return a_v.v_p[index]; else { cy_logf(0, "run[i=%lu] spy[i=%lu] lo=%lu hi=%lu", (long) index, (long) i, (long) a_lo, (long) a_hi); } return 0; }

bool RowArray::_afocus(u32 pos) { if (pos < a_max) { // position denotes spot in sequence? RowIter pt(a_d); if (a_d->_dseek(pos, pt)) { u32 hitPos = (long) pt.p_pos; if (hitPos != pos) { // should not happen cy_logf(0, "pos=%lu hitPos=%lu", (long) pos, (long) hitPos); } u8* origin = (u8*) pt.p_first.iov_base; u8* point = (u8*) pt.p_second.iov_base; u8* after = point + pt.p_second.iov_len; u32 both = after - origin; // 1st and 2nd added a_lo = hitPos - pt.p_first.iov_len; // origin pos a_hi = both + a_lo; a_v = Iov(origin, both); return true; } else { cy_logf(0, "RowArray::seek(%lu) fail", (long) pos); a_v = Iov(a_miss,4); a_lo = pos; a_hi = a_lo + pos; } } else { // position is outside sequence if (a_max) { cy_logf(0, "pos=%lu max=%lu size=%lu", (long) pos, (long) a_max, (long) a_d->dsize()); } a_v = Iov(a_miss,4); a_lo = pos; a_hi = a_lo + pos; } return false; }

Hash32& RowArray::acrc(Hash32& hash) const { // add size() more bytes to this crc u8 temp[13]; // add 13 bytes at a time to the crc u8* after = temp + 13; // one after last byte in temp RowArray j(*a_d); u32 i = 0; while ( i < a_max ) { u8* t = temp; while (t < after && i < a_max) { *t++ = j[i++]; } if (t > temp) { // any new content inside temp? hash.hadd(temp, t - temp); } } return hash; }

u32 RowArray::ahash() const { Hash32 x; this->acrc(x); return x.hcrc(); }

void RowArray::adump(cy_sink& o) const { cy_sink_ft(&o, "<RowArray row=%lx max=%lu lo=%lu hi=%lu miss=%lx>", (long) a_d, (long) a_max, (long) a_lo, (long) a_hi, (long) a_miss); a_d->dcite(o); o << cy_endl; a_v.vshow(o, "a_v", /*maxbytes*/ 8192); o << cy_subi << cy_endl << "</RowArray>"; } }; // namespace cy

using namespace cy; void cy_row_try(uint32_t seed, cy_sink& o); void cy_row_test(uint32_t seed) { char buf[ 2048 + 4 ]; cy_fdsink sout; cy_iovec_t bufiov; cy_sink& o = sout.f_sink; bufiov.iov_base = buf; bufiov.iov_len = 2048; cy_fdsink_ctor(&sout, bufiov, STDOUT_FILENO); // init global heapvat cy_heapvat* ghv = (cy_heapvat*) cy_global_heapvat(); cy_heapvat_cite(ghv, &o); o << cy_endl; cy_row_try(seed, o); cy_heapvat_cite(ghv, &o); o << cy_endl << cy_now; } void cy_row_try(uint32_t seed, cy_sink& o) { cy::Row d; d << "foo"; d << "another fine mess"; d.dinsert(10, "HOLLY"); d(2, 11) = cy_nil; o << d.quote() << cy_endl; // o << cy_cite(d) << cy_endl; }

menu

     Here's a menu of pages on cy code.

  • vector - std::vector clone
  • bheap - binary min heap
  • label - ascync descriptors
  • misc - bunch of basic utils
  • pool - C-based vats and pools
  • deck - scatter/gather suite
  • sink - C-based out stream api
  • row - a new deck rewrite

license

     See license and copyright for code here. For more context, see the cy page.

comments

     Compared to a thorn demo, I explain cy code less: I care little whether folks use or grasp cy source. But since I aim to get ideas across, I reveal a point to code constructs so you see intentions.

     If you ask: What was this for? That's the only question I address: why a thing was done. If you what to know how code works or what loose ends remain, figure it out.

color coding

     Library source code appears appears in amber (orange/brown):

amber is_source(code* c);

     Source .cpp code appears in red:

void cy_logf(int, const char* f, ...) { char temp[ 2048 + 4 ]; va_list args; va_start(args,f); vsnprintf(temp, 2048, f, args); va_end(args); temp[2048] = 0; printf("%s\n", temp); }

     Sample test code is purple:

o << "# purple=test green=stdout" << cy_newl;

     Printed output on stdout is green:

# purple=test green=stdout

     I know these aren't the best color cues. (Amber and green might appear the same hue to color blind folks. I have excellent color discrimination myself.)