#include "global.h" #include dynbuf::dynbuf (int initial, int extend) { ext = extend; _size = 0; first = last = (chunk *)salloc (sizeof (chunk) + initial); first->alloc = sizeof (chunk) + initial; first->next = 0; ptr = first->data; end = ptr + initial; } dynbuf::~dynbuf () { _clear (); } void dynbuf::_clear () { while (first) { chunk *next = first->next; sfree ((char *)first, first->alloc); first = next; } } void dynbuf::clear () { _clear (); _size = 0; first = last = (chunk *)salloc (sizeof (chunk) + ext); first->alloc = sizeof (chunk) + ext; first->next = 0; ptr = first->data; end = ptr + ext; } void dynbuf::finish () { // finalise current chunk _size += last->size = ptr - last->data; } void dynbuf::_reserve (int size) { finish (); do { ext += ext >> 1; ext = (ext + 15) & ~15; } while (ext < size); chunk *add = (chunk *) salloc (sizeof (chunk) + ext); add->alloc = sizeof (chunk) + ext; add->next = 0; last->next = add; last = add; ptr = last->data; end = ptr + ext; } void dynbuf::linearise (void *data) { last->size = ptr - last->data; for (chunk *c = first; c; c = c->next) { memcpy (data, c->data, c->size); data = (void *)(((char *)data) + c->size); } } char * dynbuf::linearise () { if (first->next) { finish (); chunk *add = (chunk *) salloc (sizeof (chunk) + _size); add->alloc = sizeof (chunk) + _size; add->next = 0; linearise ((void *)add->data); _clear (); first = last = add; ptr = last->data + _size; end = ptr; _size = 0; } return first->data; } dynbuf::operator std::string () { // could optimise return std::string (linearise (), size ()); } void dynbuf_text::printf (const char *format, ...) { int len; { force (128); va_list ap; va_start (ap, format); len = vsnprintf (ptr, end - ptr, format, ap); va_end (ap); assert (len >= 0); // shield against broken vsnprintf's // was enough room available if (ptr + len < end) { alloc (len); return; } } // longer, try harder va_list ap; va_start (ap, format); vsnprintf (force (len + 1), len + 1, format, ap); va_end (ap); alloc (len); } // simply return a mask with "bits" bits set inline uint64 m (int b) { return (uint64 (1) << b) - 1; } // convert 9 digits to ascii, using only a single multiplication // (depending on cpu and compiler). // will generate a single 0 as output when v=lz=0 inline char * i2a_9 (char *ptr, uint32 v, bool lz) { // convert to 4.56 fixed-point representation // this should be optimal on 64 bit cpus, and rather // slow on 32 bit cpus. go figure :) const int bits = 7*8; // 8 bits per post-comma digit uint64 u = v * ((m (bits) + 100000000) / 100000000); // 10**8 if (lz) { // output leading zeros // good compilers will compile this into only shifts, masks and adds *ptr++ = char (u >> (bits - 0)) + '0'; u = (u & m (bits - 0)) * 5; *ptr++ = char (u >> (bits - 1)) + '0'; u = (u & m (bits - 1)) * 5; *ptr++ = char (u >> (bits - 2)) + '0'; u = (u & m (bits - 2)) * 5; *ptr++ = char (u >> (bits - 3)) + '0'; u = (u & m (bits - 3)) * 5; *ptr++ = char (u >> (bits - 4)) + '0'; u = (u & m (bits - 4)) * 5; *ptr++ = char (u >> (bits - 5)) + '0'; u = (u & m (bits - 5)) * 5; *ptr++ = char (u >> (bits - 6)) + '0'; u = (u & m (bits - 6)) * 5; *ptr++ = char (u >> (bits - 7)) + '0'; u = (u & m (bits - 7)) * 5; *ptr++ = char (u >> (bits - 8)) + '0'; } else { // do not output leading zeroes (except if v == 0) // good compilers will compile this into completely branchless code char digit, nz = 0; digit = (u >> (bits - 0)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 0)) * 5; digit = (u >> (bits - 1)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 1)) * 5; digit = (u >> (bits - 2)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 2)) * 5; digit = (u >> (bits - 3)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 3)) * 5; digit = (u >> (bits - 4)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 4)) * 5; digit = (u >> (bits - 5)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 5)) * 5; digit = (u >> (bits - 6)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 6)) * 5; digit = (u >> (bits - 7)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 7)) * 5; digit = (u >> (bits - 8)); *ptr = digit + '0'; nz |= digit; ptr += 1; } return ptr; } void dynbuf_text::add (sint32 i) { force (11); // 10 digits + '-' *ptr = '-'; ptr += i < 0 ? 1 : 0; uint32 u = i < 0 ? -i : i; if (expect_true (u < 10)) // we have a lot of single-digit numbers, so optimise fadd (char (u + '0')); else if (expect_true (u < 1000000000)) // 9 0's ptr = i2a_9 (ptr, u, false); else { sint32 div = u / 1000000000; uint32 rem = u % 1000000000; ptr = i2a_9 (ptr, div, false); ptr = i2a_9 (ptr, rem, true); } } void dynbuf_text::add (sint64 i) { force (20); // 19 digits + '-' *ptr = '-'; ptr += i < 0 ? 1 : 0; uint64 u = i < 0 ? -i : i; if (i < 0) { fadd ('-'); u = -i; } else u = i; // split the number into a 1-digit part // (#19) and two 9 digit parts (9..18 and 0..8) // good compilers will only use multiplications here if (u < 10) // we have a lot of single-digit numbers, so optimise fadd (char (u + '0')); else if (expect_true (u < 1000000000)) // 9 0's ptr = i2a_9 (ptr, u, false); else if (expect_true (u < UINT64_C (1000000000000000000))) // 18 0's { sint32 div = u / 1000000000; uint32 rem = u % 1000000000; ptr = i2a_9 (ptr, div, false); ptr = i2a_9 (ptr, rem, true); } else { // a biggy sint32 div = u / UINT64_C (1000000000000000000); uint64 rem = u % UINT64_C (1000000000000000000); fadd (char (div + '0')); u = rem; { sint32 div = u / 1000000000; uint32 rem = u % 1000000000; ptr = i2a_9 (ptr, div, true); ptr = i2a_9 (ptr, rem, true); } } } #if 0 struct dynbuf_test_class { dynbuf_test_class () { sint64 s = 0; for (int i = 0; i < 10000000; ++i) { char b1[256], b2[256]; dynbuf_text db; db.add (s); db.add (char (0)); db.linearise (b1); sprintf (b2, "%ld", s); if (strcmp (b1, b2)) printf ("<%s,%s>\n", b1, b2); if (i < 20) s = (sint64) pow (10., i); else s = (sint64) exp (random () * (43.6682723752766 / RAND_MAX)); } exit (0); } } dynbuf_test; #endif