Þ   briarpig  » code  » misc


misc

     This page clones a lot of simple thorn data types for use in this cy release under a BSD license.

purpose

     This page is a stub placeholder for many cloned thorn classes used by various cy sample code.

purpose

     The purpose of all these classes is to support the other pages showing cy code. The idea is to include everything needed to compile and run test cases.

     However, far more is included than necessary for minimal code running in tests. When possible, I simply included all the code in the class I cloned, except when cutting a few methods could remove another type entirely, providing only fringe benefit.

basics

     This appears near the top of a header file. You'll note simple pseudo random number (linear congruential) generators are inline methods.

#include <math.h> #include <assert.h> #include <stdlib.h> //define CY_STD_STRING 1 /* if you want std::string*/ #ifdef CY_STD_STRING #include <string> #endif /*CY_STD_STRING*/ /* ----- C linkage ----- */ #ifdef __cplusplus extern "C" { /*C linkage if included in C++ code*/ #endif static inline unsigned long cy_u32rand(unsigned long long n) { /* note: n is 64-bit ONLY so n * 48271 cannot */ /* overflow 32 bits; note: largest prime in */ /* 32-bits: 4294967291 == 0xfffffffb. */ return (unsigned long) ((n * 48271) % 4294967291ULL); }; static inline double /* uniform in (0, 1.0) */ cy_r64rand(uint32_t* seed) { /* largest 32-bit prime: 4294967291 == 0xfffffffb. */ unsigned long long big = *seed; // 64-bits unsigned long x = (big)? /* ensure x cannot be zero */ ((big * 48271) % 4294967291ULL) : 1; *seed = x; /* return u such that 0.0 < u < 1.0 */ return ((double) x) / 4294967291.0; };

unlikely «

     This macro calls a builtin gcc method to tell a compiler the branch is not expected to be taken. This makes it easier to check for error conditions at the start of a function without any worry this slows down code (because you're telling the compiler to emit code assuming the else case is taken).

#define cy_unlikely(x) __builtin_expect((x)!=0,0)

logging «

     It's often necessary to provide simple logging calls:

extern void cy_logf(int level, const char* fmt, ...); extern void cy_yellf(int line, const char* file, const char* fmt, ...); extern void cy_yellerrno(int line, const char* file);

/* ----- logf ----- */ void cy_logf(int level, const char* fmt, ...) { // printf char temp[ 2048 + 4 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); // save space for null va_end(args); temp[2048] = 0; // whether or not vsnprintf() writes nul printf("%s\n", temp); }

/* ----- yellf ----- */ void (int line, const char* file, const char* fmt, ...) { char temp[ 2048 + 4 ]; va_list args; va_start(args,fmt); vsnprintf(temp, 2048, fmt, args); // max-1 saves end u8 for nul va_end(args); temp[2048] = 0; // whether or not vsnprintf() also wrote nul printf("file %.128s line %d: %s\n", file, line, temp); } void (int line, const char* file) { int e = errno; const char* estr = strerror(e); printf("file %.128s line %d: errno=%d (%s)\n", file, line, e, estr); }

generation numbers «

     Refcounted objects on the pool page work best with pseudo random generation numbers.

uint16_t cy_rand_gen16(); /* std pseudo rand generation */ uint32_t cy_rand_gen32(); /* std pseudo rand generation */ void cy_deck_test(uint32_t seed); void cy_row_test(uint32_t seed);

uint32_t g_cy_rand_seed = 777; /* std pseudo rand gen seed */ uint16_t cy_rand_gen16() { /* std pseudo rand generation */ uint32_t n = g_cy_rand_seed; if (0 == n) { n = 1; // avoid zero seed } n = cy_u32rand((unsigned long long) n); g_cy_rand_seed = n; return (uint16_t) n; } uint32_t cy_rand_gen32() { /* std pseudo rand generation */ uint32_t n = g_cy_rand_seed; if (0 == n) { n = 1; // avoid zero seed } n = cy_u32rand((unsigned long long) n); g_cy_rand_seed = n; return n; }

C iovecs «

     It's hard to avoid cloning struct iovec over and over when an environment denies including those system headers.

/* ----- cy_iovec ----- */ typedef struct cy_iovec_ { /* C clone of struct iovec */ void* iov_base; unsigned iov_len; } cy_iovec; /* ----- cy_viov ----- */ typedef struct cy_viov_ { /* iovec vector as single value */ cy_iovec* v_iov; /* first of v_len iovecs */ unsigned v_len; } cy_viov; #ifdef __cplusplus } /*extern "C"*/ /*end C linkage if used in C++*/ #endif

singletons

type overloading «

     These are useful types for method overloading:

namespace cy { // ----- cy ----- class End; // END type (type returned by STL-style end()) struct Nil1 { int m1_nil; Nil1() : m1_nil(0) { } operator int() const { return 0; } }; // empty struct Endl1 { int m1_endl; Endl1() : m1_endl(0) { } }; struct Subi1 { int m1_subi; Subi1() : m1_subi(0) { } }; struct Addi1 { int m1_addi; Addi1() : m1_addi(0) { } }; struct Now1 { int m1_now; Now1() : m1_now(0) { } }; struct Reset1 { int m1_reset; Reset1() : m1_reset(0) { } }; struct Lf1 { int m1_lf; Lf1() : m1_lf(0) { } }; // lf struct Gloss1 { int m1_gloss; Gloss1() : m1_gloss(0) { } }; }; // namespace cy

     These are standard instances of those types:

extern cy::Nil1 cy_nil; // singleton (value irrelevant) extern cy::Endl1 cy_endl; // singleton (value irrelevant) n() extern cy::Subi1 cy_subi; // singleton (value irrelevant) --indent extern cy::Addi1 cy_addi; // singleton (value irrelevant) ++indent extern cy::Now1 cy_now; // singleton (value irrelevant) // flush extern cy::Reset1 cy_reset; // singleton (value irrelevant) extern cy::Lf1 cy_lf; // singleton (value irrelevant) extern cy::Gloss1 cy_gloss; // singleton (irrelevant)

     Those instances must be defined someplace:

///// ----- global ----- ///// cy::Nil1 cy_nil; cy::Endl1 cy_endl; cy::Subi1 cy_subi; cy::Addi1 cy_addi; cy::Now1 cy_now; cy::Reset1 cy_reset; cy::Lf1 cy_lf; cy::Gloss1 cy_gloss;

scalars «

     Shorter names for standard integer types:

namespace cy { // ----- cy ----- typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint32_t x32; typedef uint32_t p32; typedef int32_t i32; typedef int32_t zn32; typedef int32_t zp32; typedef int64_t i64; typedef uint64_t u64; typedef ptrdiff_t cy_pd_t; typedef double r64;

forwards «

     Many classes refer to one another:

// ----- forwards ----- struct Buf; struct BufCut; struct RowIter; // ydp (d=deck p=iter (ie pointer)) struct DeckIter; // ydp (d=deck p=iter (ie pointer)) class IovOut; // ydvo (d=deck v=vec o=out) class IovVec; // ydv: vector in Deck (d=deck v=vec) class IovPtr; // IovPtr: Iovec vector iterator class IovPtrCut; // ydvpz: Iovec vector iterator slice class Deck; class DeckCut; // ydz: deck slice (d=deck z=slice) class DeckArray; // yda: deck as array (d=deck, a=array) class DeckIterByte; // DeckIterByte template<class T> class DeckIterT; // DeckIterT iter over T in deck class Row; class RowCut; // ydz: deck slice (d=deck z=slice) class RowArray; // yda: deck as array (d=deck, a=array) class RowIterByte; // RowIterByte template<class T> class RowIterT; // RowIterT iter over T in row

value wrappers

citing «

     For any lvalue x, you can write cy_cite(x) which returns a struct instance with the size of a pointer, containing a reference to your value with a unique type you can use to overload methods. By convention, this type means: please print this object using at most one line, in abbreviated form.

// ----- Cite<T> ----- template <typename T> struct Cite { // "Cite" template T const& c_t; // wee wrapper for T to request cite not dump Cite(T const& t) : c_t(t) { } }; template <typename T> Cite<T> cy_cite(T const& t) { return Cite<T>(t); }

pool operators «

     Overloaded operators for citing pools:

inline cy_sink& operator<<(cy_sink& o, cy_pool const& x) { pool_cite_do(&x, &o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<cy_pool> const& x) { pool_cite_do(&x.c_t, &o); return o; }

sequence subsets

slices «

     A slice is a subset of some sequence, expressed as an offset and length. When either offset or length is negative, this is understood as relative to length N of the whole sequence.

// ----- Cut ----- struct Cut { // yz: slice (starting pos & length in sequence) typedef int32_t zp32; // 32-bit signed offset (pointer) typedef int32_t zn32; // 32-bit signed length zp32 z_p; // signed position (neg is relative to eof) zn32 z_n; // signed length (neg is relative to eof) Cut(zp32 p, zn32 n) : z_p(p), z_n(n) { } void zinit(zp32 p, zn32 n) { z_p = p; z_n = n; } // set void zassign(Cut const& z) { z_p = z.z_p; z_n = z.z_n; } Cut() : z_p(0), z_n(0) { } // union of this & z (assert: neither z_n can be negative) // expected useful for union of token slices in in-streams void zjoin(Cut const& z); // union of both and all between Cut& operator+=(Cut const& z) { this->zjoin(z); return *this; } Cut operator+(Cut const& z) { Cut x(*this); x.zjoin(z); return x; } // increase z_n so z_p+z_n == x if x follows z_p+z_n void zmax(zp32 x); // extend slice so x is 1st after slice Cut& operator+=(zp32 after) { this->zmax(after); return *this; } Cut operator+(zp32 after) { Cut x(*this); x.zmax(after); return x; } // zskip() uses Cut as fast sub-slice space consumer, but // Cut normalized to non-neg z_p and z_n will work here void zskip(unsigned n) { // skip n elements if (z_n > (zn32) n) { // enough elems to cut n of them? z_p += n; // advance beyond n z_n -= n; // cut n from length } else { // skip all remaining members of this slice z_p += z_n; // advance beyond remaining z_n = 0; // zero length } } // if either z_p or z_n is negative, become N+z_p or N+z_n // if sum N+z_p or N+c_m is negative, make zero instead; // if sequence len=N, don't let slice ref elems outside Cut(Cut const& z, zn32 N); // N-relative if z_p<0 or z_n<0 void zabsolute(zn32 N); // N-relative if z_p<0 or z_n<0 bool zrelative() const { // either is neg? return 0 > z_p || 0 > z_n; } };

///// ----- Cut ----- ///// Cut::Cut(Cut const& z, int32_t N) : z_p(z.z_p), z_n(z.z_n) { if (z_p < 0) { if ((z_p += (long) N) < 0) z_p = 0; } if (z_n < 0) { if ((z_n += (long) N) < 0) z_n = 0; } if (z_p > N) z_p = N; int32_t remaining = N - z_p; if (z_n > remaining) z_n = remaining; }

void Cut::zabsolute(int32_t N) { if (z_p < 0) { if ((z_p += (long) N) < 0) z_p = 0; } if (z_n < 0) { if ((z_n += (long) N) < 0) z_n = 0; } if (z_p > N) z_p = N; int32_t remaining = N - z_p; if (z_n > remaining) z_n = remaining; }

void Cut::zjoin(Cut const& z) { assert(z_n >= 0 && z.z_n >= 0); int32_t tx = z_p + z_n; int32_t zx = z.z_p + z.z_n; int32_t p = (z_p < z.z_p)? z_p : z.z_p; int32_t x = (tx > zx)? tx : zx; z_n = (x > p)? (x - p) : 0; z_p = p; }

void Cut::zmax(int32_t after) { int32_t p = z_p; int32_t x = p + z_n; if (after > x) z_n = after - p; }

misc iovecs

C++ iovec «

     This is a minimal C++ clone of C-based iovecs. It has several constructors and operators designed to smooth conversion to and from other iovec types.

// ----- Iovec ----- struct Iov; struct Iovec { void* iov_base; unsigned iov_len; Iovec() : iov_base(0), iov_len(0) { } Iovec(const void* p, unsigned n) : iov_base((void*) p), iov_len(n) { } explicit Iovec(const cy_iovec& x) : iov_base(x.iov_base), iov_len(x.iov_len) { } explicit Iovec(const cy_iov& x) : iov_base(x.iov_base), iov_len(x.iov_len) { } explicit Iovec(const char* s) : iov_base((void*) s) , iov_len((s)? strlen(s) : 0) { } inline operator Iov() const; operator cy_iovec() const { cy_iovec v; v.iov_base = iov_base; v.iov_len = iov_len; return v; } operator cy_iov() const { cy_iov v; v.iov_base = iov_base; v.iov_len = iov_len; return v; } }; inline cy_sink& operator<<(cy_sink& o, Iovec const& x) { if (x.iov_base && x.iov_len) { sink_write_do(&o, x.iov_base, x.iov_len); } return o; }

C++ iov «

     This code is a clone of yv in the run thorn demo (cf «) which gathers together many simple utility methods which work when all you know is a pointer and a length (in bytes). In other words, Iov is just an iovec, but it has a lot of methods you can call when all you know is the information in an iovec.

     For example, suppose you want to allocate memory from an iovec representing remaining unused space: vtake() does that.

// ----- Iov ----- #define Iov_PAGESZ 4096 struct Iov { u8* v_p; u32 v_n; inline operator Iovec() const { Iovec v; v.iov_base = v_p; v.iov_len = v_n; return v; } operator cy_iovec() const { cy_iovec v; v.iov_base = v_p; v.iov_len = v_n; return v; } operator cy_iov() const { cy_iov v; v.iov_base = v_p; v.iov_len = v_n; return v; } Iov() : v_p(0), v_n(0) { } Iov(const void* p, unsigned n) : v_p((u8*) p), v_n(n) { } explicit Iov(BufCut const& bufslice); explicit Iov(const Iovec& x) : v_p((u8*) x.iov_base), v_n(x.iov_len) { } explicit Iov(const cy_iovec& x) : v_p((u8*) x.iov_base), v_n(x.iov_len) { } explicit Iov(const cy_iov& x) : v_p((u8*) x.iov_base), v_n(x.iov_len) { } explicit Iov(const char* s) : v_p((u8*) s), v_n((s)? strlen(s) : 0) { } #ifdef CY_STD_STRING explicit Iov(const std::string& str) // from std string : v_p((u8*) str.c_str()), v_n((u32) str.size()) { } Iov& operator=(const std::string& s) { v_p = (u8*) s.c_str(); v_n = (u32) s.size(); return *this; } operator std::string() const { // convert to std string return std::string((const char*) v_p, v_n); } #endif /*CY_STD_STRING*/

void vclear() { v_p = 0; v_n = 0; } bool vempty() const { return 0==v_n || 0==v_p; } operator bool() const { return v_n && v_p; } void vinit(const void* p, u32 n) { v_p = (u8*)p; v_n = n; } void vreverse(); // invert order of bytes by updating in place i32 vatoi32() const; // decimal ascii to i32 (opt leading '-') i64 vatoi64() const; // decimal ascii to i64 (opt leading '-') void vhexdump(u32 max, int base=0) const; void vhex(const char* tag, u32 max, int base=0) const;

void vtail() { if (v_n) { ++v_p; --v_n; } } //!< skip first byte void vskip(unsigned n) { if (v_n>=n) { v_p+=n; v_n-=n; } }

//uses Iov as a very fast pointer-bumping heap allocator u8* vtake(unsigned n) { // like vskip(), but return 1st n bytes if (n <= v_n) { // enough bytes remain in this vector? u8* p = v_p; v_p = p + n; v_n -= n; return p; } else // unlikely case: return 0; // nil when the Iov space is exhausted }

// vcalloc() is like vtake() but zeroes the allocated space u8* vcalloc(unsigned n) { // like vskip(), but returns leading n if (n <= v_n) { // enough bytes remain in octet vector? u8* p = v_p; v_p = p + n; v_n -= n; memset(p, 0, n); return p; } else // unlikely case: return 0; // nil when the Iov space is exhausted }

/// \brief vpage() acts like valloc() but allocating from Iov u8* vpage(unsigned sz) { u8* p = v_p; u32 n = v_n; cy_pd_t a = (cy_pd_t) p; //address p as ptrdiff_t to page align cy_pd_t lo = (Iov_PAGESZ-1) & a; //low bits of a (inside a page) if (0 != lo) { //p not already aligned? (must add complement?) cy_pd_t rest = Iov_PAGESZ - lo; //complement: rest of page if (rest > (cy_pd_t) n) //skipping rest exhausts remainder? return 0; //allocation cannot succeed p += rest; n -= rest; //p is now aligned; n is new remainder } if (n > sz) { //able to alloc sz bytes? v_p = p + sz; // next alloc follows this one v_n = n - sz; return p; // sz bytes allocated at page aligned boundary } return 0; //not enough bytes left in iov }

Iov vprefix(u32 n)const { return Iov(v_p, (n<v_n)? n: v_n); } Iov vsuffix(u32 n) const { // a vec equal to the final n octets if (n > v_n) return Iov(*this); else { u8* x = v_p + v_n; // one after last byte in iov return Iov(x - n, n); } } // returns the start of 1st instance of pattern in this: u8* vifind(Iov const& v) const; // ci find sub v, else 0 u8* vfind(Iov const& v) const; // find sub v, else 0 // true when this starts with a prefix equal to head: bool vistarts(Iov const& head) const; // v is ci prefix subset? bool vstarts(Iov const& head) const; // v is prefix subset? // vends(tail) is true when this ends with a suffix equal to tail: bool viends(Iov const& tail) const; // v is a ci suffix subset? bool vends(Iov const& tail) const; // v is a suffix subset? u32 vcopy(const Iov& src); // new length is min(v_n, src.v_n) // vscanf() makes a temp stack copy to ensure nul after end: int vscanf(const char* fmt, ...); // ::vsscanf() for results // true iff vintersect() is not empty: bool voverlaps(const Iov& alt) const; Iov vintersect(const Iov& alt) const; Iov operator&(Iov const& v) const { return this->vintersect(v); }

bool vinside(const void* point) const { // is point inside? const u8* pt = (const u8*) point; return (v_p <= pt) && (v_p + v_n > pt); // in v_n bytes } // log failure of vinside() to be true for point void vlogmiss(const void* point, const char* where) const; u8* vsplit(int c, Iov& outSecondHalf); unsigned vcount(int c) const; // count c's in this iov u8* vindex(register int c) const; // 1st c (from left) or nil u8* vrindex(register int c) const; // 1st c from RIGHT or nil

bool veq(const Iov& v) const { // true iff identical content unsigned n = v.v_n; return n == v_n && (!n || 0 == ::memcmp(v_p, v.v_p, n)); } bool operator==(Iov const& v) const { return this->veq(v); } bool operator!=(Iov const& v) const { return !this->veq(v); } static int vcmp(const Iov& one, const Iov& two); static int vicmp(const Iov& one, const Iov& two); bool operator< (Iov const& v) const { return Iov::vcmp(*this, v)< 0; } bool operator<=(Iov const& v) const { return Iov::vcmp(*this, v)<=0; } bool operator> (Iov const& v) const { return Iov::vcmp(*this, v)> 0; } bool operator>=(Iov const& v) const { return Iov::vcmp(*this, v)>=0; } u32 vsame(Iov const& pattern) const; // prefix match len u32 vsame(IovPtr const& pattern) const; // prefix match len u8* vdiff(Iov const& v) const; // 1st diff byte or nil // cmp via vdiff() & print diff u32 vodiff(cy_sink& o, Iov const& v) const; static int vtest_diff(); // test methods vdiff(), vodiff() Iov iovCut(zp32 p, zn32 n) const; // slice this vector

Iov iovCut(Cut const& slice) { return this->iovCut(slice.z_p, slice.z_n); } Iov operator()(zp32 p, zn32 n) const { return this->iovCut(p, n); } u32 vhash() const; // inline after Hash32 (crc32() of this) u32 vihash() const; // case insensitive hash based on crc32() public: // quoting and printing

struct Ve { Iov const& e_v; Ve(Iov const& v): e_v(v) { } }; Ve escape() const { return Ve(*this); } // request Iovw::wescape() // cy_sink& vescape(cy_sink& o) const;

struct Vq { Iov const& q_v; Vq(Iov const& v): q_v(v) { } }; Vq quote() const { return Vq(*this); } // to request dump void vdump(cy_sink& o) const; void vcite(cy_sink& o) const; void vshow(cy_sink& o, const char* kind, u32 maxLen, u32 base=0) const; public: // print utils // show pos as hex prefix, return len: static u32 p32prefix(cy_sink& o, p32 pos); // hex dump line ascii "abc\x01" => "; abc.\n" void vascii(cy_sink& o) const; void vhexmax(cy_sink& o, u32 maxLen, u32 base=0) const; public: // iter api class Vp; inline Vp vbegin(int c) const; inline End* vend() { return (End*) 0; } }; // struct Iov

inline cy_sink& operator<<(cy_sink& o, Iov const& x) { if (x.v_p && x.v_n) { sink_write_do(&o, x.v_p, x.v_n); } return o; } inline cy_sink& operator<<(cy_sink& o, Iov::Vq const& x) { x.q_v.vdump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<Iov> const& x) { x.c_t.vcite(o); return o; } inline int cy_sink_v(cy_sink* o, Iov const& x) { if (x.v_p && x.v_n) { return sink_write_do(o, x.v_p, x.v_n); } return 0; }

inline Iovec::operator Iov() const { Iov v; v.v_p = (u8*) iov_base; v.v_n = iov_len; return v; }

///// ----- Iov ----- ///// Iov::Iov(BufCut const& bufslice) { Iov v = bufslice.cutIov(); v_p = v.v_p; v_n = v.v_n; }

void Iov::vreverse() { // invert order of all bytes register u8* p = v_p; if ( p ) { // not nil and not empty? register u8 c; // temp for swaps register u8* end = p + v_n; // one past last used byte for (/*preincr*/--p ; ++p < --end; ) { // 2 more bytes to swap? c = *p; // save earlier byte *p = *end; // move later byte forward *end = c; // finish swap at end } } }

void Iov::vlogmiss(const void* point, const char* where) const { const u8* pt = (const u8*) point; const u8* end = v_p + v_n; if (pt < v_p) { cy_logf(1, "!vinside(%s) pt=%lx (BEFORE) < p=%lx <= x=%lx ", where, (long) pt, (long) v_p, (long) end); } else if (pt >= end) { cy_logf(1, "!vinside(%s) p=%lx <= x=%lx <= (AFTER) pt=%lx", where, (long) v_p, (long) end, (long) pt); } else { // how did this happen? point is actuall inside: cy_logf(1, "!vinside(%s) p=%lx <= pt=%lx < x=%lx (huh?)", where, (long) v_p, (long) pt, (long) end); } }

Iov Iov::vintersect(const Iov& kv) const { // k: const Iov empty(0, 0); // empty: nothing in common if (!v_n || !kv.v_n) // either empty? return empty; const Iov* lo; // at low address const Iov* hi; // high address if (v_p > kv.v_p) { // high? hi = this; lo = &kv; } else { // this Iov is low hi = &kv; lo = this; } const u8* a = lo->v_p; // lowest address of either const u8* b = a + lo->v_n; // 1 byte past low end const u8* c = hi->v_p; // beginning of higher Iov const u8* d = c + hi->v_n; // 1 byte past high end if ( c >= b ) // hi starts after lo? no overlap? return empty; const u8* end = (b < d)? b : d; // lowest Iov end Iov overlap(c, end - c); // common to this and kv return overlap; }

bool Iov::voverlaps(const Iov& alt) const { // true iff vintersect() is not empty Iov both = this->vintersect(alt); return (both.v_p || both.v_n); // true only if both is not empty }

// iovCut: neg p relative to v_p+v_n; neg n relative to v_n; Iov Iov::iovCut(zp32 p, zn32 n) const { // slice this vector u8* pz = (p < 0)? ((v_p+v_n)+p) : (v_p + p); i32 nz = (n < 0)? ((i32) v_n + n) : n; // nz can still be neg if abs(n)>abs(v_n); if so, len is "neg" Iov slice(pz, (u32) nz); // assume nz is positive if (nz < 0) // neg length makes "end" of slice come before start? slice.vinit(pz + nz, (u32) -nz); // make earlier "end" the start return this->vintersect(slice); // return overlap of this & slice }

i32 Iov::vatoi32() const { // MAN ATOI(3) if (v_p && v_n) { char copy[ 512 + 1]; // so we can add an end nul u32 cpyLen = (v_n < 512)? v_n : 512; ::memcpy(copy, v_p, cpyLen); copy[cpyLen] = 0; return ::atoi(copy); } return -1; }

i64 Iov::vatoi64() const { // MAN ATOLL(3) if (v_p && v_n) { char copy[ 512 + 1]; // so we can add an end nul u32 cpyLen = (v_n < 512)? v_n : 512; ::memcpy(copy, v_p, cpyLen); copy[cpyLen] = 0; return ::atoll(copy); } return -1; }

u32 Iov::vsame(const Iov& pattern) const { const u8* t = pattern.v_p; const u8* s = (const u8*) v_p; u32 cmpLen = pattern.v_n; if (s && t) { if (cmpLen > v_n) // pattern is longer than this? cmpLen = v_n; // min(v_n, pattern.v_n) const u8* end = s + cmpLen; // one past last byte to see --s; // prepare for preincrement while ( ++s < end ) { // another byte to examine? if ( *s != *t++ ) { // found non-matching byte? return s - v_p; // distance from mismatch to start } } return cmpLen; // all the requested bytes matched } return 0; // empty }

u32 Iov::vsame(const IovPtr& pattern) const { IovPtr iter(pattern, 0); // locally modified copy u32 outLen = 0; // value returned by this method Iov copy(*this); // local copy of this run we can modify for ( ; iter; ++iter) { Iov next(*iter); // construct from next iovec in iter u32 nextLen = copy.vsame(next); outLen += nextLen; if (nextLen < next.v_n) // did not match all of next? return outLen; // stop matching now copy.vskip(nextLen); // advance beyond bytes just matched } return outLen; }

u32 Iov::vodiff(cy_sink& o, Iov const& v) const { // cmp via vdiff() & print diff Buf span(0, 0, 0); u32 n = span.bdiff(*this, v); if (n) { // this and v are not identical? if (v_n != v.v_n) { // varying length? cy_sink_fn(&o, "vodiff() v_n=%d v.v_n=%d lengths differ", (int) v_n, (int) v.v_n); } if (span.v_n) { // run content differs? p32 pos = span.v_p - v_p; cy_sink_fn(&o, "vodiff() D=%d in N=%d span after=%d of=%d", (int) n, (int) span.v_n, (int) pos, (int) v_n); span.vshow(o, "lhs-diff", 0xffff); o << cy_endl; Iov piece = v.iovCut(pos, span.v_n); // slice of v piece.vshow(o, "rhs-diff", 0xffff); o << cy_endl << cy_now; } o << cy_now; } return n; // no differences }

u8* Iov::vdiff(Iov const& two) const { // 1st diff byte, else nil const Iov& one = *this; u8* p = one.v_p; // first byte u32 n = one.v_n; // my length u8* bp = two.v_p; // first byte u32 bn = two.v_n; // second length if (p && n && bp && bn) { // neither is empty? ++n; ++bn; // prep predecrement while (--n) { // more in one? if (0 == --bn) // two exhausted first? return p; if (*p == *bp) { // still equal? ++p; ++bp; } else return p; // first difference } // if bn is exactly 1, both exhausted at the same time: return (1 == bn)? 0 : p; // equal or p is first diff byte } else return p; }

/*static*/ int Iov::vcmp(const Iov& one, const Iov& two) { u8* a = one.v_p; // first byte u8* aend = a + one.v_n; // one beyond last byte u8* b = two.v_p; // first byte u8* bend = b + two.v_n; // one beyond last byte while (a < aend && b < bend) { // neither has hit end? if (*a != *b) return ((int)*a) - ((int)*b); ++a; ++b; } // either a or b or both was exhausted: if (a >= aend) { //0=equal if both exhausted; 1:a>b if a exhausted return (b>=bend)? 0 : -1; } else return 1; //-1:a<b if b exhausted but not a too }

/*static*/ int Iov::vicmp(const Iov& one, const Iov& two) { u8* a = one.v_p; // first byte u8* aend = a + one.v_n; // one beyond last byte u8* b = two.v_p; // first byte u8* bend = b + two.v_n; // one beyond last byte while (a < aend && b < bend) { // neither has hit end? if (*a != *b) { // first mismatching octet? int la = tolower(*a); int lb = tolower(*b); if (la != lb) return ((int)la) - ((int)lb); } ++a; ++b; } // either a or b or both was exhausted: if (a >= aend) { //0=equal if both exhausted; 1:a>b if a exhausted return (b>=bend)? 0 : -1; } else return 1; //-1:a<b if b exhausted but not a too }

u32 Iov::vcopy(Iov const& src) { // v_n = min(v_n, src.v_n), then copy if (v_n > src.v_n) v_n = src.v_n; if (v_p && src.v_p && v_n) ::memcpy(v_p, src.v_p, v_n); return v_n; }

u8* Iov::vindex(register int c) const { // 1st c in this, from left register u8* p = v_p; u8* end = p + v_n; // one beyond last byte in the run --p; // prepare for preincrement (start before first byte) while ( ++p < end ) { // until we pass beyond last byte if ( *p == c ) return p; // return 1st c in this (from left), else nil } return (u8*) 0; }

u8* Iov::vrindex(register int c) const { // vindex() but from right u8* p = v_p; register u8* x = p + v_n; // one beyond last byte in the run while ( --x >= p ) { // until we pass before first byte if ( *x == c ) return x; // return 1st c in this from RIGHT, else nil } return (u8*) 0; } unsigned Iov::vcount(int c) const { // count number of c's in this run unsigned sum = 0; register const u8* p = v_p; const u8* end = p + v_n; // one beyond last byte in the run --p; // prepare for preincrement while ( ++p < end ) { if ( *p == c ) ++sum; } return sum; }

u8* Iov::vsplit(register int c, Iov& outSecondHalf) { // vsplit() returns the same value returned by vindex() above. // But memsplit also alters both this and outSecondHalf, so this // run ends just before the first c, and outSecondHalf starts with // the first c. Callers can use tail() to remove the leading c. register u8* p = v_p; u8* end = p + v_n; // one beyond last byte in the run --p; // prepare for preincrement while ( ++p < end ) { // another byte to examine? if ( *p == c ) { // found c? need to split the run in two parts? this->v_n = p - v_p; // bytes remaining in first half outSecondHalf.vinit(p, end - p); // bytes moving to 2nd half return p; } } // c was not found inside the run anywhere, so second half is empty: outSecondHalf.vinit(end, 0); // no match, but return end spot anyway return (u8*) 0; // nil }

// vscanf() makes temp stack copy to ensure nul termination after end: int Iov::vscanf(const char* fmt, ...) { // use vsscanf() to get results const char* p = (const char*) v_p; u32 n = v_n; if (!p || !n) { return -1; } char temp[ 2048 + 2 ]; // temp buf used if necessary if (p[n-1]) { // does not have nul byte in last byte in the vector? if (n > 2048) n = 2048; // do not copy more than tmp buf worth ::memcpy(temp, p, n); temp[n] = 0; // write nul after last byte copied p = temp; } va_list args; va_start(args,fmt); int outVal = ::vsscanf(p, fmt, args); va_end(args); return outVal; }

void Iov::vdump(cy_sink& o) const { this->vshow(o, "Iov", (16*1024), 0); // show up to 16K worth } void Iov::vcite(cy_sink& o) const { Hash32 h; h << *this; cy_sink_f(&o, "<Iov p=%#lx n=%ld crc=%#lx:%lu/>", (long) v_p, (long) v_n, (long) h.hcrc(), (long) h.hlen()); }

void Iov::vshow(cy_sink& o, const char* kind, u32 maxLen, u32 base) const { if (v_n) { Hash32 h; h << *this; cy_sink_ftn(&o, "<%s p=%#lx n=%ld crc=%#lx:%lu>", kind, (long) v_p, (long) v_n, (long) h.hcrc(), (long) h.hlen()); this->vhexmax(o, maxLen, base); cy_sink_uend(&o, kind); } else cy_sink_f(&o, "<%s p=%#lx n=%ld/>", kind, (long) v_p, (long) v_n); }

u32 Iov::p32prefix(cy_sink& o, p32 pos) { // show pos as hex prefix, return len cy_sink_f(&o, "%05lx: ", (long) pos); return 7; // plan: 7 byte line prefix }

void Iov::vascii(cy_sink& o) const { // hex dump line ascii "abc\x01" => "; abc.\n" u8* end = v_p + v_n; cy_sink_2c(&o, ';', ' '); for (u8* a = v_p-1; ++a < end; ) { int c = *a; if (!isprint(c)) c = '.'; cy_sink_c(&o, c); } cy_sink_n(&o); }

iov hex «

     Whenever cy hex dumps raw memory, this is the method which does it:

// const unsigned khexcap = 96; // 32 per line const unsigned khexcap = 48; // 16 per line // const unsigned khexcap = 36; // 12 per line static const char Iov_blanks[] = // 72 blanks in constant on the next line: " "; //123456789_123456789_123456789_123456789_123456789_123456789_123456789_12

void Iov::vhexmax(cy_sink& o, u32 maxLen, u32 base) const { static const char* hex = "0123456789abcdef"; // digits u32 line = Iov::p32prefix(o, base); // length of current line u32 olen = line; // prefix offset length u8* p = v_p; u8* asc = v_p; // location of last bytes not written in ascii yet u32 limit = (maxLen < v_n)? maxLen : v_n; u8* end = p + limit; int c; for (/*prep preincr*/ --p; ++p < end ; ) { if (line >= (khexcap+olen)) { // 16 octets in 'XX ' fmt so far? Iov va(asc, p-asc); va.vascii(o); line = Iov::p32prefix(o, (p - v_p) + base); asc = p; // for next time: last octet not written in ascii } c = *p; cy_sink_3c(&o, hex[(c>>4)&0xf], hex[c&0xf], ' '); line += 3; } if (asc < p) { // last trailing portion to write in ascii? if (line < (khexcap+olen)) { Iov white(Iov_blanks, (khexcap+olen)-line); cy_sink_v(&o, white); } Iov last(asc, p-asc); last.vascii(o); } if (limit < v_n) { cy_sink_fn(&o, "# tunc after %d bytes; %d bytes more ignored", (int) limit, (int)(v_n-limit)); } }

u32 Iov::vhash() const { Hash32 x; x << *this; return x.hcrc(); }

u32 Iov::vihash() const { // lowercase bytes only when needed before crc32 // Submit contig non-uppercase to crc32 as-is from where they sit now; // but copy uppercase bytes to a temp buf for lowercasing before crc32. Hash32 x; // where the crc hash gets accumulated char temp[ 64 + 1 ]; // to copy bytes that are lowercased on the fly const u8* p = v_p; const u8* end = p + v_n; while (p < end) { // at least one more byte to hash? const u8* p0 = p; // start of successive bytes this time in loop int c = *p++; // first byte in remaining bytes to process if (isupper(c)) { // need to find longest consec upcase sequence? char* s = temp; char* send = temp + 64; *s++ = tolower(c); // copy to temp buf as lowercase while (p < end && s < send && isupper(*p)) { // another fits? *s++ = tolower(*p); ++p; // copy lower, then advance past } // assert: p - p0 is number of lowercased bytes put into temp Iov lowerCopy(temp, p - p0); // contig lower copy in temp buf x << lowerCopy; // add to crc } else { // need longest NON-uppercase sequence in original vector while (p < end && !isupper(*p)) // another non-upper? ++p; // just advance beyond each non-upper byte // assert: p is end or p points at 1st upper byte before end Iov lower(p0, p - p0); // contiguous original non-upper bytes x << lower; // add to crc } } return x.hcrc(); }

u8* Iov::vifind(Iov const& pat) const { // ci find sub pat inside this, else 0 // plain vanilla, naive brute force search, and not BoyerMoore if (v_n >= pat.v_n) { // big enough to contain pat? u32 extra = v_n - pat.v_n; // extra bytes in excess of pat.v_n u8* p = v_p; u8* end = p + extra; // one past last octet that might start pat for (/*prep preincr*/--p; ++p < end; ) { Iov here(p, pat.v_n); // next u8 position with length of pat if (Iov::vicmp(here, pat)==0) // case insens match? return p; } } return 0; }

u8* Iov::vfind(Iov const& pat) const { // find sub pat in this, else 0 // this is a plain vanilla, naive brute force search, not BoyerMoore if (v_n >= pat.v_n) { // big enough to contain pat? u32 extra = v_n - pat.v_n; // extra bytes in excess of pat.v_n u8* p = v_p; u8* end = p + extra; // one past last octet that might start pat for (/*prep preincr*/--p; ++p < end; ) { if (::memcmp(p, pat.v_p, pat.v_n)==0) // match? return p; } } return 0; }

bool Iov::vistarts(Iov const& head) const { // starts with? head is ci prefix subset? if ( v_n >= head.v_n ) { // has at least as many octets as prefix head? Iov pre = vprefix(head.v_n); // prefix of ourselves: length of head return Iov::vicmp(pre,head)==0; // case insens compare is equal } return false; }

bool Iov::vstarts(const Iov& head) const { // head is a prefix subset? return v_n >= head.v_n && vprefix(head.v_n) == head; }

bool Iov::viends(Iov const& tail) const { // ends with? tail is ci suffix subset? if ( v_n >= tail.v_n ) { // at least as many octets as suffix? Iov suf = vsuffix(tail.v_n); // suffix of ourselves: length of tail return Iov::vicmp(suf,tail)==0; // case insens compare is equal } return false; }

bool Iov::vends(const Iov& tail) const { // tail is a suffix subset? return v_n >= tail.v_n && this->vsuffix(tail.v_n) == tail; }

gaussian random

pseudo random gaussians «

     Instances of Gaus64 generate pseudo random sequences of floating point numbers with a desired average value and standard deviaation, in a gaussian distribution. This code is a clone of yr64g in the rand thorn demo (cf «).

///// ----- Gaus64 ----- ///// class Gaus64 { // gaussian pseudo-random r64 generator protected: // http://www.taygeta.com/random/gaussian.html u32* g_u32; // seed used by h32rand() generator double g_sdv; // standard dev (sqrt(variance)) double g_avg; // mean for values; ie average double g_now; // last value returned by operator*() double g_r64; // precalculated next rand r64 bool g_use_r64; // true when g_r64 is good public: ~Gaus64() { } // noop: don't care Gaus64(u32* seed); // saved by g_u32 for future use Gaus64(u32* seed, double avg, double sdv); void ginit(r64 avg, r64 sdv) { g_avg = avg; g_sdv = sdv; } void ginit(u32* s, r64 avg, r64 sdv) { g_u32 = s; g_avg = avg; g_sdv = sdv; } u32* gu32() const { return g_u32; } // seed double gavg() const { return g_avg; } // average double gsdv() const { return g_sdv; } // std dev u32 gu32rand() { u32 u = cy_u32rand(*g_u32); *g_u32 = u; return u; }; u64 gu64rand(); r64 grandom(double avg, double sdv); // Box-Muller r64 gaussian() { return this->grandom(g_avg, g_sdv); } // guniform() returns rand r64 such that 0.0 < r < 1.0 r64 guniform(); // pseudo-rand in [0,1] unit interval double operator++() { return (g_now = gaussian()); } double operator*() { return g_now; } };

///// ----- Gaus64 ----- ///// r64 Gaus64::grandom(double avg, double sdv) { double r64val; if ( g_use_r64 ) { r64val = g_r64; g_use_r64 = false; } else { // Box-Muller transformation double x1, x2, w; do { x1 = 2.0 * this->guniform( ) - 1.0; x2 = 2.0 * this->guniform( ) - 1.0; w = x1 * x1 + x2 * x2; } while ( w >= 1.0 ); w = ::sqrt( (-2.0 * ::log( w )) / w); r64val = x1 * w; // first val to return g_r64 = x2 * w; // second value created g_use_r64 = true; // next time use g_r64 } return avg + (r64val * sdv); }

r64 Gaus64::guniform() { // pseudo-rand in [0,1] unit interval return cy_r64rand(g_u32); // cy_deck.h }

u64 Gaus64::gu64rand() { // two u32 rand's joined as one u64 u32 one = cy_u32rand(*g_u32); u32 two = cy_u32rand(one); *g_u32 = two; return ( ((u64) one) << 32 ) | ((u64) two); }

Gaus64::Gaus64(u32* seed) : g_u32( seed ), g_sdv( 1.0 ), g_avg(0.0), g_now(0.0), g_r64(0.0), g_use_r64(false) { } Gaus64::Gaus64(u32* seed, double avg, double sdv) : g_u32(seed), g_sdv(sdv), g_avg(avg), g_now(0.0), g_r64(0.0), g_use_r64(false) { }

variance-and-means

sum of squares «

     Instances of SquareSum allow you to track average and standard deviation for a population of values. This code is a clone of yrsum in the stat thorn demo (cf «).

///// ----- SquareSum ----- ///// // sum-of-squares to calculate variance (a real stat) struct SquareSum { // real sequence sum maps to standard dev long long m_len; // number of points observed double m_sum; // sum of all points double m_squares; // sum of squared points SquareSum() : m_len(0), m_sum(0.0), m_squares(0.0) { } void clear() { m_len = 0; m_sum = 0.0; m_squares = 0.0; } struct Sq { SquareSum const& q_s; Sq(SquareSum const& s):q_s(s) {} }; Sq quote() const { return Sq(*this); } // to request dump void add(double x) { ++m_len; m_sum += x; m_squares += x * x; } SquareSum& operator+=(double x) { this->add(x); return *this; } SquareSum& operator+=(long x) { this->add((double) x); return *this; } SquareSum& operator+=(long long x) { this->add(x); return *this; } SquareSum& operator+=(u32 x) { this->add((double) x); return *this; } SquareSum& operator+=(u64 x) { this->add((double) x); return *this; } SquareSum operator-(const SquareSum& x) const { SquareSum copy(*this); copy.m_len -= x.m_len; copy.m_sum -= x.m_sum; copy.m_squares -= x.m_squares; return copy; } SquareSum& operator-=(const SquareSum& x) { m_len -= x.m_len; m_sum -= x.m_sum; m_squares -= x.m_squares; return *this; } SquareSum& operator/=(unsigned divisor) { m_len /= divisor; m_sum /= divisor; m_squares /= divisor; return *this; }

long long size() const { return m_len; } operator long long() const { return m_len; } double mean() const { return (m_len>1)? m_sum/m_len : m_sum; } double variance() const { // square of standard deviation if (m_len > 1) { double mu = this->mean(); double n_mu_sq = m_len * (mu * mu); return (m_squares - n_mu_sq)/(m_len - 1); } return 0.0; } double avg() const { return this->mean(); } double sdv() const { // standard deviation double v = this->variance(); // std dev is sqrt(v) return (v < 0.0)? ::sqrt(-v): ::sqrt(v); } void sprint() const; void sdump(cy_sink& o, const char* who=0) const; void scite(cy_sink& o) const; }; // SquareSum: real sequence mapped to sums (std dev) inline cy_sink& operator<<(cy_sink& o, SquareSum::Sq const& x) { x.q_s.sdump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<SquareSum> const& x) { x.c_t.scite(o); return o; }

///// ----- SquareSum ----- ///// void SquareSum::sdump(cy_sink& o, const char* who) const { if (!who) who = "SquareSum"; cy_sink_f(&o, "<%.48s n=%lld avg=%3.1f sdv=%3.1f sum=%3.1f/>\n", who, (long long) this->size(), (double) this->avg(), (double) this->sdv(), (double) m_sum); } void SquareSum::scite(cy_sink& o) const { this->sdump(o); }

32-bit hash: crc32

crc32 hash «

     Instances of Hash32 allow you to hash a sequence of possibly discontiguous bytes using zlib's crc32(). This code is a clone of yh32 in the crc thorn demo (cf «).

// ----- Hash32 ----- class Hash32 { // crc32 based on zlib's ::crc32() private: u32 h_len; // number of bytes added to crc u32 h_crc; // crc from applying crc32() to h_len bytes void _hinit() { h_len = 0; h_crc = (u32)::crc32(0L, Z_NULL, 0); } public: u32 hlen() const { return h_len; } u32 hcrc() const { return h_crc; } operator unsigned long() const { return h_crc; } void hadd(const char* s) { u32 n = ::strlen(s); h_crc = ::crc32(h_crc, (Bytef*) s, n); h_len += n; } void hadd(Iovec const& v) { u32 n = v.iov_len; h_crc = ::crc32(h_crc, (Bytef*) v.iov_base, n); h_len += n; } void hadd(Iov const& src) { u32 n = src.v_n; h_crc = ::crc32(h_crc, (Bytef*) src.v_p, n); h_len += n; } void hadd(void* p, u32 n) { h_crc = ::crc32(h_crc, (Bytef*) p, n); h_len += n; } Hash32() { _hinit(); } Hash32(Iov const& src) { _hinit(); if (src) hadd(src); } Hash32(void* p, u32 n) { _hinit(); if (p && n) hadd(p, n); } Hash32& operator<<(const char* s) { if (s) hadd(s); return *this; } Hash32& operator<<(Iov const& v) { if (v) hadd(v); return *this; } Hash32& operator<<(Iovec const& v) { if (v.iov_base && v.iov_len) hadd(v); return *this; }

struct Hq { Hash32 const& q_h; Hq(Hash32 const& h): q_h(h) { } }; Hq quote() const { return Hq(*this); } // request dump //void hdump(cy_sink& o) const; //void hcite(cy_sink& o) const; }; // class Hash32

sized buffers

     Instances of Buf and BufCut allow you to manage content in a buffer with a maximum size. This code is a clone of yb in the buf thorn demo (cf «).

buf slice subset «

struct Buf; struct BufCut : public Cut { // ybz Buf& z_b; BufCut(zp32 p, zn32 n, Buf const& b) : Cut(p, n), z_b(*(Buf*)&b) { } BufCut(Cut const& z, Buf const& b) : Cut(z), z_b(*(Buf*)&b) { } struct Zq { BufCut const& q_z; Zq(BufCut 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; // cutIov() explicitly converts Buf slice into Iov of range in buf: inline Iov cutIov() const; // defined immediately after struct Buf };

inline cy_sink& operator<<(cy_sink& o, BufCut::Zq const& x) { x.q_z.zdump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<BufCut> const& x) { x.c_t.zcite(o); return o; }

buffer «

struct Buf : public Iov { // octet buffer: Iov plus max capacity x32 b_x; // max physical capacity (v_n is logical length) Buf() : Iov(0, 0), b_x(0) { } Buf(const void* p, u32 len, x32 x) : Iov(p, len), b_x(x) { } Buf(Iov const& v, x32 max) : Iov(v), b_x(max) { } Buf(Iovec const& v, x32 max) : Iov(v), b_x(max) { } Buf(Iov const& v) : Iov(v.v_p, 0), b_x(v.v_n) { } Buf(Iovec const& v) : Iov(v.iov_base, 0), b_x(v.iov_len) { } void binit(const void* p, u32 n, x32 x) { v_p = (u8*)p; v_n = n; b_x = x; } void bclear() { v_p = 0; v_n = 0; b_x = 0; } void bmemset(int c) { if (v_p && b_x) ::memset(v_p, c, b_x); } u32 bdiff(const Iov& one, const Iov& two); void bseek(p32 n) { v_n = (n < b_x)? n : b_x; } // set len Buf& operator=(p32 n) { v_n = (n < b_x)? n : b_x; return *this; } bool bappend(Iov const& v); bool bappend(const char* s); bool bappend(Iovec const& iov) { Iov v(iov); return bappend(v); } bool bappend(BufCut const& z) { return this->bappend(z.cutIov()); } bool bappend(IovPtr const& src); // true if fits bool bappend(IovPtr const& src, u32 limit); // true if fits bool bappend(IovPtrCut const& src); // true if fits Buf& bf(const char* fmt, ...); // eg printf() to this buffer // bf() with leading position p begins format at position p: Buf& operator()(const char* fmt, ...); // (ie bf()) printf to buf BufCut bz(zp32 p, zn32 n) const { return BufCut(p, n, *this); } BufCut operator()(zp32 p, zn32 n) const { return BufCut(p, n, *this); } struct Bq { Buf const& q_b; Bq(Buf const& b): q_b(b) { } }; Bq quote() const { return Bq(*this); } // request dump void bdump(cy_sink& o) const; void bcite(cy_sink& o) const; }; // struct Buf

inline Iov BufCut::cutIov() const { Iov v(z_b.v_p, z_b.b_x); return v.iovCut(*this); }

inline cy_sink& operator<<(cy_sink& o, Buf::Bq const& x) { x.q_b.bdump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<Buf> const& x) { x.c_t.bcite(o); return o; } inline Buf& operator<<(Buf& b, Iov const& x) { b.bappend(x); return b; } inline Buf& operator<<(Buf& b, Iovec const& x) { b.bappend(x); return b; } inline Buf& operator<<(Buf& b, const char* s) { b.bappend(s); return b; } inline Buf& operator<<(Buf& b, BufCut const& z) { b.bappend(z); return b; } inline Buf& operator<<(Buf& b, IovPtr const& x) { b.bappend(x); return b; } inline Buf& operator<<(Buf& b, IovPtrCut const& x) { b.bappend(x); return b; }

///// ----- Buf ----- ///// void Buf::bdump(cy_sink& o) const { if (v_n) { Hash32 h; h << *this; cy_sink_ftn( &o, "<Buf p=%#lx n=%ld x=%ld crc='#%lx:%lu'>", (long) v_p, (long) v_n, (long) b_x, (long) h.hcrc(), (long) h.hlen()); this->vhexmax(o, (16*1024)); cy_sink_uend(&o, "Buf"); } else this->bcite(o); } void Buf::bcite(cy_sink& o) const { Hash32 h; h << *this; cy_sink_f(&o, "<Buf p=%#lx n=%ld x=%ld crc='#%lx:%lu'/>", (long) v_p, (long) v_n, (long) b_x, (long) h.hcrc(), (long) h.hlen()); }

Buf& Buf::bf(const char* fmt, ...) { // like printf to this buffer v_n = 0; // default to empty result if (!fmt) { cy_logf(1, "bf(fmt=nil)"); return *this; } if (!v_p) { cy_logf(1, "Buf::v_p"); return *this; } u32 max = b_x; if (!max) { return *this; } // empty; no space for nul if (1 == max) { *v_p = 0; v_n = 0; return *this; } // empty; 1 for nul va_list args; va_start(args,fmt); vsnprintf((char*)v_p, max-1, fmt, args); // max-1 saves end u8 for nul va_end(args); v_p[max-1] = 0; // whether or not vsnprintf() also wrote a nul v_n = (u32) ::strlen((const char*) v_p); // bytes before end nul return *this; } Buf& Buf::operator()(const char* fmt, ...) { // printf to buf v_n = 0; // default to empty result if (!fmt) { cy_logf(1, "bf(fmt=nil)"); return *this; } if (!v_p) { cy_logf(1, "Buf::v_p"); return *this; } u32 max = b_x; if (!max) { return *this; } // empty; no space for nul if (1 == max) { // empty; 1 for nul *v_p = 0; v_n = 0; return *this; } va_list args; va_start(args,fmt); vsnprintf((char*) v_p, max-1, fmt, args); // save end u8 for nul va_end(args); v_p[max-1] = 0; // whether or not vsnprintf() also wrote a nul v_n = (u32) ::strlen((const char*)v_p); // bytes before end nul return *this; }

bool Buf::bappend(Iov const& r) { u32 more = (b_x > v_n)? (b_x - v_n): 0; u32 quantum = (more < r.v_n)? more: r.v_n; if (v_p && r.v_p && quantum) { ::memcpy(v_p+v_n, r.v_p, quantum); v_n += quantum; } return quantum == r.v_n; // added all of r } bool Buf::bappend(const char* s) { if (s) { // can convert to Iov? Iov v(s); return this->bappend(v); } return true; // all of nothing added } bool Buf::bappend(IovPtr const& src, u32 limit) { bool entire = true; if ( v_n > b_x ) { // length over capacity? cy_logf(1, "logical v_n=%d > physical b_x=%d", (int) v_n, (int) b_x); v_n = b_x; // force rational values } u8* bp = v_p + v_n; // buf write cursor u32 sz = b_x - v_n; // available space IovPtr ivp(src, 0); // mutable copy at index zero for ( ; limit && ivp; ++ivp) { // more to do? const Iovec& v = *ivp; // iter's next iovec u32 n = v.iov_len; if (n > limit) // iovec exceeds limit left? n = limit; // max is requested limit if (n > sz) { // exceeds space left? n = sz; // only what can fit entire = false; // insufficient room if (0 == sz) // no room left? all done? break; } if (n && v.iov_base) { // act on this Iovec? ::memcpy(bp, v.iov_base, n); bp += n; // skip written bytes sz -= n; // less buf space limit -= n; // closer to limit } } v_n = (u32) (bp - v_p); // used buf bytes return entire; // true if all of request fit } bool Buf::bappend(IovPtr const& src) { bool entire = true; if ( v_n > b_x ) { // length over capacity? cy_logf(1, "logical v_n=%d > physical b_x=%d", (int) v_n, (int) b_x); v_n = b_x; // force rational values } u8* bp = v_p + v_n; // buf write cursor u32 sz = b_x - v_n; // available space IovPtr ivp(src, 0); // mutable copy at index zero for ( ; ivp; ++ivp) { // space and more iter? const Iovec& v = *ivp; // iter's next Iovec u32 n = v.iov_len; if (n > sz) { // exceeds space left? n = sz; // only what can fit entire = false; // insufficient room if (0 == sz) // no room left? all done? break; } if (n && v.iov_base) { // act on this Iovec? ::memcpy(bp, v.iov_base, n); bp += n; // skip written bytes sz -= n; // less buf space } } v_n = (u32) (bp - v_p); // used buf bytes return entire; // true if all of request fit }

static bool b256_IovPtrCut(Buf& b, IovPtrCut const& pz) { IovPtr256 iter(pz); // does iter.pcut(); return b.bappend(iter); }

static bool b1K_IovPtrCut(Buf& b, IovPtrCut const& pz) { IovPtr1024 iter(pz); // does iter.pcut(); return b.bappend(iter); }

static bool b4K_IovPtrCut(Buf& b, IovPtrCut const& pz) { IovPtr4096 iter(pz); // does iter.pcut(); return b.bappend(iter); }

static bool b16K_IovPtrCut(Buf& b, IovPtrCut const& pz) { long psz = pz.z_dvp.psize(); if (psz > 16 * 1024) // over capcity? (maybe assert?) cy_logf(0, "b16K_IovPtrCut() psize=%lu over 16K max", psz); IovPtr16K iter(pz); // does iter.pcut(); return b.bappend(iter); } bool Buf::bappend(IovPtrCut const& pz) { // true if fits u32 n = pz.z_dvp.psize(); // max Iovecs needed for pcut() if (n <= 256) // at most 1K Iovecs needed? return b256_IovPtrCut(*this, pz); else if (n <= 1024) // at most 1K Iovecs needed? return b1K_IovPtrCut(*this, pz); else if (n <= 4096) // at most 4K Iovecs needed? return b4K_IovPtrCut(*this, pz); else return b16K_IovPtrCut(*this, pz); // 64K Iovecs is max }

u32 Buf::bdiff(const Iov& one, const Iov& two) { // Returns ndiff count of bytes actually different. // Buf span covers first to last actual differences. u32 ndiff = 0; // total count of different bytes u8* first = 0; // where first byte varied in one u8* last = 0; // where last byte varied in one u8* xp = 0; // end in one of shorter of one and two u8* p = (u8*) one.v_p; u32 n = one.v_n; u32 ntwo = two.v_n; u8* ptwo = (u8*) two.v_p; if ( n && ntwo && p && ptwo ) { // neither empty? register int a; register int b; // byte from either ++n; ++ntwo; // prepare both for predecrement while (--n) { // more remains in one? if (!--ntwo) { // two done first? one is greater? xp = p; // spot in one where two ran out first break; } b = (u8) *ptwo++; // one's next octet a = (u8) *p++; // two's next octet if ( a != b ) { // another difference? ++ndiff; // count each actual diff last = p - 1; // where last diff was seen if (!first) // first seen difference? first = last; } } if (ntwo != 1) { // one and two did NOT end together? xp = p; // location in one where one ran out first } // (else if ntwo == 1, two and one were same length) } else if (n || ntwo) { // either one or two is nonempty? xp = p; // different at start of one } if (first) { // at least one byte was different? v_p = first; // first byte in one that differed v_n = (last+1) - first; // length to cover last diff b_x = (xp)? (xp - v_p): v_n; } else if (xp) { // lengths differ? ndiff = 1; // only difference was in length v_p = xp; // zero length diff at end of shorter v_n = 0; b_x = 0; // b_x = xp - v_p; } else { v_p = 0; v_n = 0; b_x = 0; } return ndiff; // actual diff count (or 1 when v_n == 0) }

iovec vector utils

     Instances of several iovec classes allow you read, write, and slice iovec vectors effectively. This code is a clone of ydvp (etc) in the iovec thorn demo (cf «).

iov vector slice «

///// ----- IovPtrCut ----- ///// class IovPtr; class Iov; class IovOut; class Hash32; struct IovPtrCut : public Cut { // slice of IovPtr IovPtr& z_dvp; IovPtrCut(zp32 p, zn32 n, IovPtr const& x) : Cut(p, n), z_dvp(*(IovPtr*)&x) { } IovPtrCut(Cut const& z, IovPtr const& x) : Cut(z), z_dvp(*(IovPtr*)&x) { } struct Zq { IovPtrCut const& q_z; Zq(IovPtrCut 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; void zout(cy_sink& o) const; // write the content described to o // 256, 1K, 4K, 16K stack arrays of Iovec for IovPtr::prender(): void z256(cy_sink& o, bool quo) const; void z1K(cy_sink& o, bool quo) const; void z4K(cy_sink& o, bool quo) const; void z16K(cy_sink& o, bool quo) const; void z64K(cy_sink& o, bool quo) const; }; // IovPtrCut

inline cy_sink& operator<<(cy_sink& o, IovPtrCut const& x) { x.zout(o); return o; } inline cy_sink& operator<<(cy_sink& o, IovPtrCut::Zq const& x) { x.q_z.zdump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<IovPtrCut> const& x) { x.c_t.zcite(o); return o; }

///// ----- IovPtrCut ----- ///// void IovPtrCut::zdump(cy_sink& o) const { IovPtr& p = z_dvp; cy_sink_ftn(&o, "<IovPtrCut z=%d:%d v=%#lx n=%u i=%u x=%u>", (int) z_p, (int) z_n, (long) p.p_v, (int) p.p_n, (int) p.p_i, (int) p.p_x); u32 n = z_dvp.psize(); // max Iovecs needed for pcut() if (n <= 256) // at most 256 Iovecs needed? this->z256(o, /*quo*/ true); else if (n <= 1024) // at most 1K Iovecs needed? this->z1K(o, /*quo*/ true); else if (n <= 4096) // at most 4K Iovecs needed? this->z4K(o, /*quo*/ true); else if (n <= (16 * 1024)) // at most 16K Iovecs needed? this->z16K(o, /*quo*/ true); else this->z64K(o, /*quo*/ true); // 16K Iovecs is max cy_sink_uend(&o, "IovPtrCut"); } void IovPtrCut::zcite(cy_sink& o) const { //Hash32 h; h << *this; // cy_sink_f(&o, "crc='%#lx:%lu'", h.hcrc(), (long) h.hlen()); IovPtr& p = z_dvp; cy_sink_f(&o, "<IovPtrCut z=%d:%d v=%#lx n=%u i=%u x=%u/>", (int) z_p, (int) z_n, (long) p.p_v, (int) p.p_n, (int) p.p_i, (int) p.p_x); }

void IovPtrCut::zout(cy_sink& o) const { // write slice to o u32 n = z_dvp.psize(); // max Iovecs needed for pcut() if (n <= 256) // at most 256 Iovecs needed? this->z256(o, /*quo*/ false); else if (n <= 1024) // at most 1K Iovecs needed? this->z1K(o, /*quo*/ false); else if (n <= 4096) // at most 4K Iovecs needed? this->z4K(o, /*quo*/ false); else if (n <= (16 * 1024)) // at most 16K Iovecs needed? this->z16K(o, /*quo*/ false); else this->z64K(o, /*quo*/ false); // 64K Iovecs is max }

void IovPtrCut::z256(cy_sink& o, bool quo) const { IovPtr256 iter(o, *this, quo); // pdump() or pout() }

void IovPtrCut::z1K(cy_sink& o, bool quo) const { IovPtr1024 iter(o, *this, quo); // pdump() or pout() }

void IovPtrCut::z4K(cy_sink& o, bool quo) const { IovPtr4096 iter(o, *this, quo); // pdump() or pout() }

void IovPtrCut::z16K(cy_sink& o, bool quo) const { IovPtr16K iter(o, *this, quo); // pdump() or pout() }

void IovPtrCut::z64K(cy_sink& o, bool quo) const { long psz = z_dvp.psize(); if (psz > 64 * 1024) // over capcity? (maybe assert?) cy_logf(0, "IovPtrCut::z64K() psize=%lu over 64K max", psz); Iovec v64K[ 64 * 1024 ]; // space for 64K Iovecs IovPtr iter(v64K, 0, 64 * 1024); // max 64K Iovecs iter.prender(o, *this, quo); // pdump() or pout() }

iov vector (iter) «

///// ----- IovPtr ----- ///// class IovPtr { // iterator over Iov vector protected: friend class IovOut; friend class IovPtrCut; Iovec* p_v/a>; // first of p_n Iovec's unsigned p_n; // length of p_v vector unsigned p_i; // index into p_v unsigned p_x; // max value for p_n Iovec p_nil; // used with bad index public: // note: bitwise copy and assign are okay IovPtr(IovPtr const& src, unsigned i) // new p_i == i : p_v(src.p_v) , p_n(src.p_n), p_i(i), p_x(src.p_x) { p_nil.iov_base = 0; p_nil.iov_len = 0; } IovPtr(const Iovec* v, unsigned n) // n == max : p_v((Iovec*) v), p_n(n), p_i(0), p_x(n) { p_nil.iov_base = 0; p_nil.iov_len = 0; } IovPtr(const Iovec* v, unsigned n, unsigned x) // n != max : p_v((Iovec*) v), p_n(n), p_i(0), p_x(x) { p_nil.iov_base = 0; p_nil.iov_len = 0; } void pinit(IovPtr const& src, unsigned i) { // new p_i == i p_v = src.p_v; p_n = src.p_n; p_i = i; p_x = src.p_x; } void pinit(const Iovec* v, unsigned n) { // n == max p_v = (Iovec*) v; p_n = n; p_i = 0; p_x = n; } void pinit(const Iovec* v, unsigned n, unsigned x) { p_v = (Iovec*) v; p_n = n; p_i = 0; p_x = x; // n != max } ~IovPtr() { p_v = 0; p_n = 0; } // crash now on use Iovec* pvec() const { return p_v; } unsigned psize() const { return p_n; } unsigned pmax() const { return p_x; } // padd() only appends when p_x > p_n (has more room) unsigned padd(Iov const& v); // append 1 unsigned padd(Iovec const& iov); // append 1 unsigned padd(IovPtr const& iter); // append N size_t psum() const; // sum of all p_n Iovecs const Iovec& operator*() const { // current Iovec return (p_i < p_n)? p_v[p_i] : p_nil; } Iovec operator[](size_t i) { return (i < p_n)? p_v[i] : p_nil; } bool pseek(unsigned j) { p_i = j; return j < p_n;} bool operator+=(unsigned n) { return (p_i += n) < p_n; } bool operator++() { return ++p_i < p_n; } // PREFIX // prefix is better; we act just like prefix here (beware) bool operator++(int){ return ++p_i < p_n; } // POSTFIX bool operator--() { --p_i; return p_i < p_n; } // PREFIX // prefix is better; we act just like prefix here (beware) bool operator--(int){ --p_i; return p_i < p_n; } // POSTFIX operator bool() const { return p_i < p_n; } // more? // compare to nominal "end" value not actually used: bool operator!=(End*) const { return p_i < p_n; } bool operator==(End*) const { return p_i >= p_n; } public: // slicing

IovPtrCut pz(zp32 p, zn32 n) const { return IovPtrCut(p, n, *this); } IovPtrCut operator()(zp32 p, zn32 n) const { return IovPtrCut(p, n, *this); } // all pcut() methods below have these specs in common: // the Iovecs in p_v are updated as necesary for new Iovecs; // true is returned iff p_n was long enough for the new data; // p_n is long enough if at LEAST as long as an original p_n; // no data is copied other than pointers from source Iovecs; // referenced data (by pointer alias) must live while in use; // incoming Iovec vec must NOT intersect p_v of this iter; bool pcut(Iov const& one, p32 pos, u32 len); bool pcut(Iovec const& one, p32 pos, u32 len); bool pcut(Iov const& one, const Cut& z); bool pcut(Iovec const& one, const Cut& z); bool pcut(const IovPtrCut& pz); bool pcut(const IovPtr& iter, const Cut& z); // all the other pcut() methods eventually call this one: bool pcut(const IovPtr& iter, p32 pos, u32 len); // ptruncate() changes both p_n and p_v[i].iov_len as needed // if max > psum(), return psum() - max, else return zero u32 ptruncate(u32 max); // beware: this edits the Iovec vec // pjigsaw() inits this iter to cut src into a random puzzle; // returns the actual new psum() expected to equal src.v_n, // aiming for an iter whose content equals that inside src. // This is for pseudo random monte carlo testing to exercise // scatter-gather code. A Box-Muller guassian distribution of // Iovec sizes is generated with Gaus64, using the given seed, // with a target of using half the p_x available Iovecs in this // iter. But if this would make the average Iovec less than // eight bytes, the average size is eight bytes (the std dev // is always two thirds the average random Iovec). Min size // for an Iovec added is one byte, but max could easily get // three stardard deviations or more above the average. u32 pjigsaw(Iov const& src, u32* randseed); // pseudo random public: // printing, i/o, hashing, writing

u32 p2v(Iov& dest) const { // write to Iov, return len Buf buf(dest.v_p, /*len*/ 0, /*max*/ dest.v_n); buf.bappend(*this); return buf.v_n; // count of bytes written to dest } void pcrc(Hash32& crc) const; u32 pout(cy_sink& o) const; // write all bytes to o u32 pout(IovPtr& dest) const; // write bytes to dest's Iovec struct Pq { IovPtr const& q_p; Pq(IovPtr 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; // render: pcut(pz) and then if (quo) pout(); else pdump(); void prender(cy_sink& o, IovPtrCut const& pz, bool quo); void pzero() { p_n = 0; } // make none of iovs used void pzeroall(); // zero p_n and clear all iovs to zero }; // IovPtr

inline IovPtr& operator<<(IovPtr& p, Iov const& x) { p.padd(x); return p; } inline IovPtr& operator<<(IovPtr& p, Iovec const& x) { p.padd(x); return p; } inline IovPtr& operator<<(IovPtr& p, IovPtr const& x) { p.padd(x); return p; } inline IovPtr& operator<<(IovPtr& p, BufCut const& z) { p.padd(z.cutIov()); return p; }

inline Hash32& operator<<(Hash32& h, IovPtr const& x) { x.pcrc(h); return h; } inline cy_sink& operator<<(cy_sink& o, IovPtr const& x) { x.pout(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<IovPtr> const& x) { x.c_t.pcite(o); return o; } inline cy_sink& operator<<(cy_sink& o, IovPtr::Pq const& x) { x.q_p.pdump(o); return o; }

///// ----- IovPtr ----- ///// void IovPtr::pzeroall() { // zero p_n and clear all iovs to zero p_n = 0; unsigned x = p_x; for (unsigned i = 0; i < x; ++i) { p_v[i].iov_base = 0; p_v[i].iov_len = 0; } }

void IovPtr::prender(cy_sink& o, IovPtrCut const& pz, bool quo) { // this IovPtr is assumed on the stack as a temp this->pcut(pz); // generate slice result if (quo) // 'quote' content in dump format? this->pdump(o); // print description else // this is the same as calling pout(): cy_sink_writev(&o, (cy_iovec_t*) p_v, p_n); }

u32 IovPtr::pout(cy_sink& o) const { // write content described to o return cy_sink_writev(&o, (cy_iovec_t*) p_v, p_n); } u32 IovPtr::pout(IovPtr& dest) const { // write bytes to dest's Iovec IovOut o(dest); // writes to dest u32 actual = o.owrite(*this); // write this to dest o.oflush(); // makes oactual() up to date if (actual != o.oactual()) { // inconsistent?? // we should never hit this; so, just checking... cy_logf(1, "IovPtr::pout() actual=%d != oactual()=%d", (int) actual, (int) o.oactual()); } return actual; }

void IovPtr::pdump(cy_sink& o) const { if (p_v && p_n) { // nonempty? Hash32 h; h << *this; cy_sink_ft(&o, "<IovPtr v=%#lx n=%u i=%u x=%u sum=%u crc='%#lx:%lu'>", (long) p_v, (int) p_n, (int) p_i, (int) p_x, (int) this->psum(), (long) h.hcrc(), (long) h.hlen()); char vtag[ 32 ]; // for "v%u" format in sprintf() u32 base = 0; // cumulative base offset for (unsigned i = 0; i < p_n; i++) { sprintf(vtag, "v%u", i); cy_sink_n(&o); Iov fragment(p_v[i]); fragment.vshow(o, vtag, 0xffff, base); base += fragment.v_n; } cy_sink_uend(&o, "IovPtr"); } else this->pcite(o); }

void IovPtr::pcite(cy_sink& o) const { Hash32 h; h << *this; cy_sink_f(&o, "<IovPtr v=%#lx n=%u i=%u x=%u sum=%u crc='%#lx:%lu'/>", (long) p_v, (int) p_n, (int) p_i, (int) p_x, (int) this->psum(), (long) h.hcrc(), (long) h.hlen()); }

void IovPtr::pcrc(Hash32& crc) const { // crc of each Iovec for (unsigned i = 0; i < p_n; i++) crc << p_v[i]; }

size_t IovPtr::psum() const { // sum of all p_n Iovecs size_t sum = 0; for (unsigned i = 0; i < p_n; i++) sum += p_v[i].iov_len; return sum; }

bool IovPtr::pcut(Iov const& v, p32 pos, u32 len) { Iovec iov = v; // convert to Iovec and cut: IovPtr iter(&iov, 1); // single Iovec iter return this->pcut(iter, pos, len); }

bool IovPtr::pcut(Iovec const& src, p32 pos, u32 len) { Iovec iov = src; // mutable copy IovPtr iter(&iov, 1); // single Iovec iter return this->pcut(iter, pos, len); }

int g_IovPtrCut_rel2abs_count = 0; // rel to abs slices bool IovPtr::pcut(Iov const& src, const Cut& zrel) { Iovec iov = src; // convert to Iovec and cut: IovPtr iter(&iov, 1); // single Iovec iter if (zrel.zrelative()) { // z_p or z_n is negative? Cut zabs(zrel); // new copy we can make absolute ++g_IovPtrCut_rel2abs_count; // count these events zabs.zabsolute(src.v_n); // relative to N=v_n return this->pcut(iter, zabs.z_p, zabs.z_n); } return this->pcut(iter, zrel.z_p, zrel.z_n); } bool IovPtr::pcut(Iovec const& src, const Cut& zrel) { Iovec iov = src; // mutable copy IovPtr iter(&iov, 1); // single Iovec iter if (zrel.zrelative()) { // z_p or z_n is negative? Cut zabs(zrel); // new copy we can make absolute ++g_IovPtrCut_rel2abs_count; // count these events zabs.zabsolute(iov.iov_len); // relative to N return this->pcut(iter, zabs.z_p, zabs.z_n); } return this->pcut(iter, zrel.z_p, zrel.z_n); } bool IovPtr::pcut(const IovPtr& iter, const Cut& zrel) { if (zrel.zrelative()) { // z_p or z_n is negative? Cut zabs(zrel); // new copy we can make absolute ++g_IovPtrCut_rel2abs_count; // count these events zabs.zabsolute(iter.psum()); // relative to N return this->pcut(iter, zabs.z_p, zabs.z_n); } return this->pcut(iter, zrel.z_p, zrel.z_n); } bool IovPtr::pcut(const IovPtrCut& pz) { if (pz.zrelative()) { // z_p or z_n is negative? Cut zabs(pz); // new copy we can make absolute ++g_IovPtrCut_rel2abs_count; // count these events zabs.zabsolute(pz.z_dvp.psum()); // relative to N return this->pcut(pz.z_dvp, zabs.z_p, zabs.z_n); } return this->pcut(pz.z_dvp, pz.z_p, pz.z_n); } bool IovPtr::pcut(const IovPtr& iter, p32 pos, u32 len) { p_n = 0; // locially empty (p_x is physical size) if (0 == len) // zero sized input? all done? return true; // all of request was appended Iovec* v = iter.p_v; // incoming vectdor Iovec* x = v + iter.p_n; // one past end of v Iovec iov; // temp Iovec for each append u8* p = 0; // incoming data in Iovec in v u32 n = 0; // length of data at p in Iovec for ( ; v < x && pos; ++v) { // more offset to skip? n = v->iov_len; // length of next Iovec if (n > pos) { // need some of this Iovec? p = (u8*) v->iov_base; // u8 arithmetic iov.iov_base = p + pos; // skip pos in v n -= pos; // remainder after pos if (n > len) // more than request? iov.iov_len = len; // only the request else iov.iov_len = n; // all of first piece this->padd(iov); // 1st Iovec in new vector pos = 0; // orginal offset now all skipped len -= iov.iov_len; // part of request done } else // skip all of this iov (skip all of n) pos -= n; } if (pos) // original offset more than iter.psum() return true; // no data exists after iter's eof for ( ; len && v < x; ++v) { // need more? more vec? p = (u8*) v->iov_base; n = v->iov_len; // bytes of content at p if (!p) { // nil address in input Iovec vector? cy_logf(0, "IovPtr::pcut() nil == v[%d].iov_base", (v - iter.pvec())); // report bad index assert(0 != p); // die (rub in caller's nose) } iov.iov_base = p; // from start of Iovec v if (n > len) // more than request? iov.iov_len = len; // only the request else iov.iov_len = n; // all of next Iovec if (this->padd(iov)) // append succeeded? len -= iov.iov_len; // less request left else return false; // unable to append all } return (0 == len); // appended all described content }

unsigned IovPtr::padd(IovPtr const& iter) { unsigned n = p_n; // save for return value Iovec* v = iter.p_v; // 1st incoming Iovec Iovec* x = v + iter.p_n; // one past last in v for ( ; v < x; ++v) { // another iter Iovec? this->padd(*v); // append each separately } return p_n - n; // increase in length, if any } unsigned IovPtr::padd(Iovec const& iov) { unsigned n = p_n; // current count of used Iovecs if (p_x > n) { // more room in vector? p_v[n] = iov; // copy Iovec to new high p_n = n + 1; // one closer to p_x maximum return 1; // length increase } else { // no slots available: log message cy_logf(1, "IovPtr holds only x=%d Iovecs", (int) p_x); } return 0; // none added: no length delta } unsigned IovPtr::padd(Iov const& v) { Iovec iov = v; // convert to Iovec and add: return this->padd(iov); }

// ptruncate() changes both p_n & p_v[i].iov_len as needed u32 IovPtr::ptruncate(u32 max) { // beware: edits Iovec vec // if max > psum(), return psum() - max, else return zero if (p_n) { // not already empty? Iovec* v = p_v; // cursor Iovec* x = v + p_n; // one beyond last used p_n = 0; // now make empty until iov's get re-added for ( ; max && v < x; ++v) { ++p_n; // take at least one byte from *v if (max >= v->iov_len) // keep all of *v? max -= v->iov_len; // progress to zero else { // edit *v to be exactly max in len v->iov_len = max; // only amount needed max = 0; // we're done return 0; // no need for loop condition } } } return max; // extra bytes not seen in Iovec }

// pjigsaw() inits this iter to cut src into a random puzzle; // returns the actual new psum() expected to equal src.v_n, // aiming for an iter whose content equals that inside src. // This is for pseudo random monte carlo testing to exercise // scatter-gather code. A Box-Muller guassian distribution of // Iovec sizes is generated with Gaus64, using the given seed, // with a target of using half the p_x available Iovecs in this // iter. But if this would make the average Iovec less than // eight bytes, the average size is eight bytes (the std dev // is always two thirds the average random Iovec). Min size // for an Iovec added is one byte, but max could easily get // three stardard deviations or more above the average. u32 IovPtr::pjigsaw(Iov const& src, u32* randseed) { if (0 == p_x) // degenerate noop case? not even one Iovec? return 0; u32 seed = cy_u32rand(*randseed); // immune to seed changes if (src.v_n < 8 || p_x <= 4) { // undersized? single Iovec? p_n = 1; // just one (need more Iovecs or more src) p_v[0] = src; // Iov::operator Iovec() return src.v_n; // total length added } double all = src.v_n; // total target sum double avg = all/(p_x/2); // target average Iovec if (avg < 8.0) // under desired min? avg = 8.0; double std = (avg * 2)/3; // two thirds std dev Gaus64 boxm(&seed, avg, std); // Box-Muller generator u8* p = src.v_p; // cursor over source bytes u32 n = src.v_n; // total bytes to add (psum()) Iovec* v = p_v; // cursor in destination Iovecs Iovec* x = v + p_x; // one past last in vector --x; // reserve last Iovec for trailing over spill while (n && v < x) { // more src and Iovec slots? double d = boxm.gaussian(); // pseudo rand length // makes sure quantum is signed -- can be negative long quantum = (long) (d + 0.5); // round up by 0.5 if (quantum < 1) // oops? too small? quantum = 1; // at least one byte per Iovec else if (quantum > (long) n) // more than remains? quantum = n; // just what remains assert(n >= (u32) quantum); // quantum is okay Iov piece(p, quantum); // next puzzle piece *v++ = piece; // Iov::operator Iovec() n -= quantum; p += quantum; } if (n) { // final overspill needs adding? Iov last(p, n); // all of remainder *v++ = last; // Iov::operator Iovec() } p_n = v - p_v; // len is count of used Iovecs *randseed = seed; // export new seed value return src.v_n; // guaranteed all added }

builtin iovec space

iov vec length 32 «

///// ----- IovPtr32 ----- ///// class IovPtr32 : public IovPtr { // with built-in 32 Iovecs public: // sample IovPtr with in-built space for use in tests enum { pe_max = 32 }; // arbitrary built-in Iovecs Iovec p_iovec[ pe_max ]; // space for p_v in IovPtr IovPtr32() : IovPtr(p_iovec, /*len*/ 0, /*max*/ pe_max) { } IovPtr32(cy_sink& o, IovPtrCut const& pz, bool quo) : IovPtr(p_iovec, 0, pe_max) { this->prender(o, pz, quo); } IovPtr32(IovPtrCut const& pz) : IovPtr(p_iovec, 0, pe_max) { this->pcut(pz); } ~IovPtr32() { } IovPtr pbegin() const { return IovPtr(p_v, p_n); } // new iter End* pend() const { return (End*) 0; } }; // IovPtr32

iov vec length 64 «

///// ----- IovPtr64 ----- ///// class IovPtr64 : public IovPtr { // with built-in 64 iovecs public: // sample IovPtr with in-built space for use in tests enum { pe_max = 64 }; // arbitrary built-in iovecs Iovec p_iovec[ pe_max ]; // space for p_v in IovPtr IovPtr64() : IovPtr(p_iovec, /*len*/ 0, /*max*/ pe_max) { } IovPtr64(cy_sink& o, IovPtrCut const& pz, bool quo) : IovPtr(p_iovec, 0, pe_max) { this->prender(o, pz, quo); } IovPtr64(IovPtrCut const& pz) : IovPtr(p_iovec, 0, pe_max) { this->pcut(pz); } ~IovPtr64() { } IovPtr pbegin() const { return IovPtr(p_v, p_n); } // new iter End* pend() const { return (End*) 0; } }; // IovPtr64

iov vec length 256 «

///// ----- IovPtr256 ----- ///// class IovPtr256 : public IovPtr { // with built-in 256 iovecs public: // sample IovPtr with in-built space for use in tests enum { pe_max = 256 }; // arbitrary built-in iovecs Iovec p_iovec[ pe_max ]; // space for p_v in IovPtr IovPtr256() : IovPtr(p_iovec, /*len*/ 0, /*max*/ pe_max) { } IovPtr256(cy_sink& o, IovPtrCut const& pz, bool quo) : IovPtr(p_iovec, 0, pe_max) { this->prender(o, pz, quo); } IovPtr256(IovPtrCut const& pz) : IovPtr(p_iovec, 0, pe_max) { this->pcut(pz); } ~IovPtr256() { } IovPtr pbegin() const { return IovPtr(p_v, p_n); } // new iter End* pend() const { return (End*) 0; } }; // IovPtr256

iov vec length 1024 «

///// ----- IovPtr1024 ----- ///// class IovPtr1024 : public IovPtr { // with built-in 1024 iovecs public: // sample IovPtr with in-built space for use in tests enum { pe_max = 1024 }; // arbitrary built-in iovecs Iovec p_iovec[ pe_max ]; // space for p_v in IovPtr IovPtr1024() : IovPtr(p_iovec, /*len*/ 0, /*max*/ pe_max) { } IovPtr1024(cy_sink& o, IovPtrCut const& pz, bool quo) : IovPtr(p_iovec, 0, pe_max) { this->prender(o, pz, quo); } IovPtr1024(IovPtrCut const& pz) : IovPtr(p_iovec, 0, pe_max) { this->pcut(pz); } ~IovPtr1024() { } IovPtr pbegin() const { return IovPtr(p_v, p_n); } // new iter End* pend() const { return (End*) 0; } }; // IovPtr1024

iov vec length 4096 «

///// ----- IovPtr4096 ----- ///// class IovPtr4096 : public IovPtr { // with built-in 4096 iovecs public: // sample IovPtr with in-built space for use in tests enum { pe_max = 4096 }; // arbitrary built-in iovecs Iovec p_iovec[ pe_max ]; // space for p_v in IovPtr IovPtr4096() : IovPtr(p_iovec, /*len*/ 0, /*max*/ pe_max) { } IovPtr4096(cy_sink& o, IovPtrCut const& pz, bool quo) : IovPtr(p_iovec, 0, pe_max) { this->prender(o, pz, quo); } IovPtr4096(IovPtrCut const& pz) : IovPtr(p_iovec, 0, pe_max) { this->pcut(pz); } ~IovPtr4096() { } IovPtr pbegin() const { return IovPtr(p_v, p_n); } // new iter End* pend() const { return (End*) 0; } }; // IovPtr4096

iov vec length 16K «

///// ----- IovPtr16K ----- ///// class IovPtr16K : public IovPtr { // with built-in 16K iovecs public: // sample IovPtr with in-built space for use in tests enum { pe_max = 16 * 1024 }; // arbitrary built-in iovecs Iovec p_iovec[ pe_max ]; // space for p_v in IovPtr IovPtr16K() : IovPtr(p_iovec, /*len*/ 0, /*max*/ pe_max) { } IovPtr16K(cy_sink& o, IovPtrCut const& pz, bool quo) : IovPtr(p_iovec, 0, pe_max) { this->prender(o, pz, quo); } IovPtr16K(IovPtrCut const& pz) : IovPtr(p_iovec, 0, pe_max) { this->pcut(pz); } ~IovPtr16K() { } IovPtr pbegin() const { return IovPtr(p_v, p_n); } // new iter End* pend() const { return (End*) 0; } }; // IovPtr16K

writing-to-iovec-vectors

iov out stream «

///// ----- IovOut ----- ///// class IovOut { // iovec vector output sink (resembles cy_sink) protected: // o_base and o_len imitate two members in IovVec: Iovec* o_base; // first of o_len iovec's in an array size_t o_len; // length of contiguous o_base iovec vector // these members resemble those in cy_sink: Iovec* o_vec; // the iovec used for o_0, o_p, and o_x: u8* o_0; // origin: start of iovec u8* o_p; // pointer inside iovec u8* o_x; // one past last writable byte in Iovec u32 o_actual; // actual bytes written public: IovOut(Iov const& one); IovOut(Iovec const& one); IovOut(const Iovec* base, size_t sz); IovOut(IovPtr const& iter); int oindex() const { return o_vec - o_base; } // iov in vec u32 osize() const { return o_actual; } u32 oactual() const { return o_actual; } void _oc(int c); bool _onext(); // advance to next iov in vec if 1 remains public: void oflush(); // make o_actual is accurate u32 owrite(IovPtr const& src); // append u32 owrite(const void* base, size_t sz); // append u32 ov(Iov const& v) { return this->owrite(v.v_p, v.v_n); } void ov(Iovec const& v) { // append this->owrite(v.iov_base, v.iov_len); } void os(const char* s) { // append Iov v(s); owrite(v.v_p, v.v_n); } void oc(int c) { if (o_p < o_x) *o_p++ = (u8) c; else this->_oc(c); } IovOut& operator<<(Iovec const& v) { this->owrite(v.iov_base, v.iov_len); return *this; } IovOut& operator<<(Iov const& v) { this->owrite(v.v_p, v.v_n); return *this; } IovOut& operator<<(int c) { if (o_p < o_x) *o_p++ = (u8) c; else this->_oc(c); return *this; } struct Oq { IovOut const& q_o; Oq(IovOut const& o): q_o(o) { } }; Oq quote() const { return Oq(*this); } // to request dump void odump(cy_sink& o) const; void ocite(cy_sink& o) const; public: // testing // IovOut::otest() returns error count; zero is success static int otest(); // (int argc, const char** argv); }; // class IovOut

inline IovOut& operator<<(IovOut& o, Now1 const&) { o.oflush(); return o; } inline IovOut& operator<<(IovOut& o, IovPtr const& x) { o.owrite(x); return o; } inline IovOut& operator<<(IovOut& o, Iov const& v) { o.ov(v); return o; } inline IovOut& operator<<(IovOut& o, Iovec const& v) { o.ov(v); return o; } inline IovOut& operator<<(IovOut& o, const char* s) { o.os(s); return o; } inline IovOut& operator<<(IovOut& o, int c) { o.oc(c); return o; }

inline cy_sink& operator<<(cy_sink& o, IovOut::Oq const& x) { x.q_o.odump(o); return o; } inline cy_sink& operator<<(cy_sink& o, Cite<IovOut> const& x) { x.c_t.ocite(o); return o; }

///// ----- IovOut ----- ///// IovOut::IovOut(Iov const& single) : o_base(0), o_len(0), o_vec(0), o_0(0), o_p(0), o_x(0), o_actual(0) { o_0 = o_p = (u8*) single.v_p; o_x = o_0 + single.v_n; } IovOut::IovOut(Iovec const& single) : o_base(0), o_len(0), o_vec(0), o_0(0), o_p(0), o_x(0), o_actual(0) { o_0 = o_p = (u8*) single.iov_base; o_x = o_0 + single.iov_len; } IovOut::IovOut(const Iovec* base, size_t ln) : o_base((Iovec*) base), o_len(ln), o_vec((Iovec*) base), o_0(0), o_p(0), o_x(0), o_actual(0) { if (o_base && o_len) { Iovec v = o_base[0]; o_0 = o_p = (u8*) v.iov_base; o_x = o_0 + v.iov_len; } else o_len = 0; } IovOut::IovOut(IovPtr const& vec) : o_base(vec.p_v), o_len(vec.p_n) , o_vec(vec.p_v), o_0(0), o_p(0), o_x(0), o_actual(0) { if (o_base && o_len) { Iovec v = o_base[0]; o_0 = o_p = (u8*) v.iov_base; // 1st in v o_x = o_0 + v.iov_len; // one past last in v } else o_len = 0; }

void IovOut::oflush() { // make o_actual is accurate // compare this to flush at start of _onext() if (o_p > o_0) { o_actual += o_p - o_0; o_0 = o_p; // don't flush this part again } }

bool IovOut::_onext() { // go to next Iovec if one exists if (o_p > o_0) { // flush to actual? o_actual += o_p - o_0; } o_p = o_0 = o_x = 0; // mark buffer empty if (o_base) { // another Iovec? Iovec* vx = o_base + o_len; // end: one past last while (++o_vec < vx) { // another Iovec to see? Iovec v = *o_vec; if (v.iov_base && v.iov_len) { // non-empty? o_0 = o_p = (u8*) v.iov_base; // 1st in v o_x = o_0 + v.iov_len; // one past last in v return true; // success } } } return false; // exhausted storage capacity }

void IovOut::_oc(int byte) { if (o_p < o_x) *o_p++ = (u8) byte; else { if (this->_onext() && o_p < o_x) *o_p++ = (u8) byte; } }

u32 IovOut::owrite(const void* base, size_t len) { u32 outActual = 0; // bytes actually written const u8* src = (const u8*) base; const u8* end = src + len; // one beyond last byte to read if (src && len) { // anything to read? while (src < end) { // have not read everything yet? u32 quantum = end - src; // bytes remaining u32 room = o_x - o_p; // capacity in this Iovec if (!room) { // need to advance? if (!this->_onext()) // no more capacity? return outActual; else room = o_x - o_p; // capacity in new Iovec } if (quantum > room) // more than fits here? quantum = room; // max for this Iovec ::memcpy(o_p, src, quantum); // copy into Iovec o_p += quantum; // note: o_actual changes in _onext() src += quantum; // this many fewer bytes to read outActual += quantum; // sum bytes written this call } } return outActual; }

u32 IovOut::owrite(const IovPtr& src) { IovPtr p(src, 0); // new copy we can modify uint32_t outActual = 0; for ( ; p; ++p) { const Iovec& v = *p; outActual += this->owrite(v.iov_base, v.iov_len); } return outActual; }

iovec vector type

iov vector «

class IovVec { public: Iovec* v_base; // 1st of v_len Iovec instances in an array size_t v_len; // contig length of v_base in Iovec members IovVec() : v_base(0), v_len(0) { } IovVec(const Iovec* base, size_t ln) : v_base((Iovec*) base), v_len(ln) { } u32 vcapacity() const; // count of bytes described by v_base //{ // u32 sum = 0; // for (size_t i = 0; i < v_len; i++) // sum += v_base[i].iov_len; // bytes in this fragment // return sum; //} u32 vsize() const { return vcapacity(); } void vinit(const char** argv, size_t argc); //{ // for (size_t i = 0; i < v_len; i++) { // if (i < argc && argv[i]) { // v_base[i].iov_base = argv[i]; // v_base[i].iov_len = ::strlen(argv[i]); // } // else { // v_base[i].iov_base = 0; // v_base[i].iov_len = 0; // } // } //} IovVec(const Iovec* base, size_t ln, const char** argv, size_t argc) : v_base((Iovec*) base), v_len(ln) { this->vinit(argv, argc); } public: // Vec debug printing void vdump(cy_sink& o) const; u32 vout(Buf& b) const; // transfer content to b, unchanged u32 vout(IovOut& v) const; // transfer content to v, unchanged u32 vout(cy_sink& o) const; // transfer content to o, unchanged // read every byte and write them efficiently to out stream o Hash32& vcrc(Hash32& hash) const; u32 vhash() const; struct Vq { IovVec const& q_v; Vq(IovVec const& v): q_v(v) { } }; Vq quote() const { return Vq(*this); } void ddump(cy_sink& o) const; void dcite(cy_sink& o) const; }; // class IovVec

inline cy_sink& operator<<(cy_sink& o, IovVec::Vq const& x) { x.q_v.vdump(o); return o; } inline cy_sink& operator<<(cy_sink& o, IovVec const& x) { x.vout(o); return o; } inline Hash32& operator<<(Hash32& h, IovVec const& x) { x.vcrc(h); return h; } inline Buf& operator<<(Buf& b, IovVec const& x) { x.vout(b); return b; } inline IovOut& operator<<(IovOut& o, IovVec const& x) { x.vout(o); return o; }

///// ----- IovVec ----- ///// u32 IovVec::vcapacity() const { // count bytes described by v_base u32 sum = 0; for (size_t i = 0; i < v_len; i++) sum += v_base[i].iov_len; // bytes in this fragment return sum; }

void IovVec::vinit(const char** argv, size_t argc) { for (size_t i = 0; i < v_len; i++) { if (i < argc && argv[i]) { v_base[i].iov_base = (void*) argv[i]; v_base[i].iov_len = ::strlen(argv[i]); } else { v_base[i].iov_base = 0; v_base[i].iov_len = 0; } } }

void IovVec::vdump(cy_sink& o) const { char arc[128]; Buf arcBuf(arc, 0, 128); Hash32 myCrc; myCrc << *this; cy_sink_ftn(&o, "<IovVec base=%lx len=%lu capacity=%lu crc=#%08lx:%lu/>", (long) v_base, (long) v_len, (long) this->vcapacity(), (long) myCrc.hcrc(), (long) myCrc.hlen()); if ( v_base) { u32 sizeSum = 0; for (u32 i = 0; i < v_len; ++i) { Iovec v = v_base[i]; o << cy_endl; sizeSum += v.iov_len; Deck::dshow(o, v, i, arcBuf, sizeSum); } } cy_sink_unend(&o, "IovVec"); }

u32 IovVec::vout(IovOut& v) const { // copy to v, unchanged u32 outActual = 0; if ( v_base) { for (u32 i = 0; i < v_len; ++i) { Iovec x = v_base[i]; outActual += v.owrite(x.iov_base, x.iov_len); } } if (outActual != v.oactual()) { cy_yellf(__LINE__, __FILE__, "outActual='%lu' DIFFERS: v.actual()='%lu'", (long) outActual, (long) v.oactual()); } return outActual; } u32 IovVec::vout(Buf& b) const { u32 before = b.v_n; if ( v_base) { for (u32 i = 0; i < v_len && b.v_n < b.b_x; ++i) { Iovec v = v_base[i]; Iov r(v.iov_base, v.iov_len); b.bappend(r); } } return b.v_n - before; } /*u32 IovVec::vout(std::ostream& s) const { u32 outActual = 0; if ( v_base) { for (u32 i = 0; i < v_len; ++i) { Iovec v = v_base[i]; outActual += v.iov_len; s.write((const char*) v.iov_base, v.iov_len); } } return outActual; }*/ u32 IovVec::vout(cy_sink& o) const { // copy to o, unchanged u32 outActual = 0; if ( v_base) { for (u32 i = 0; i < v_len; ++i) { Iovec v = v_base[i]; outActual += sink_write_do(&o, v.iov_base, v.iov_len); } } return outActual; }

Hash32& IovVec::vcrc(Hash32& hash) const { if ( v_base) { for (u32 i = 0; i < v_len; ++i) { Iovec v = v_base[i]; hash.hadd(v.iov_base, v.iov_len); } } return hash; }

print format control operators

sink operators «

}; // namespace cy // ----- cy_sink inlines ----- inline cy_sink& operator<<(cy_sink& o, cy::Endl1 const& x) { cy_sink_n(&o); return o; } inline cy_sink& operator<<(cy_sink& o, cy::Now1 const& x) { sink_flush_do(&o); return o; } inline cy_sink& operator<<(cy_sink& o, cy::Addi1 const& x) { cy_sink_t(&o); return o; } inline cy_sink& operator<<(cy_sink& o, cy::Subi1 const& x) { cy_sink_u(&o); return o; } inline cy_sink& operator<<(cy_sink& o, const char* s) { if (s) { cy_sink_s(&o, s); } return o; }

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

     Header .h interface 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.)