|
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
///// ----- 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.
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.) |