/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2005,2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ #include "global.h" #include void dynbuf::init (int initial) { cextend = extend; _size = 0; first = last = (chunk *)salloc (sizeof (chunk) + initial); first->alloc = sizeof (chunk) + initial; first->next = 0; ptr = first->data; end = ptr + initial; } // frees a full chain and sets the pointer to zero void dynbuf::free (chunk *&chain) { while (chain) { chunk *next = chain->next; sfree ((char *)chain, chain->alloc); chain = next; } } void dynbuf::clear () { cextend = extend; free (first->next); _size = 0; ptr = first->data; end = ptr + first->alloc - sizeof (chunk); last = first; } void dynbuf::finalise () { // finalise current chunk _size += last->size = ptr - last->data; } void dynbuf::reserve (int size) { finalise (); do { cextend += cextend >> 1; cextend = (cextend + 15) & ~15; } while (cextend < size); chunk *add = (chunk *) salloc (sizeof (chunk) + cextend); add->alloc = sizeof (chunk) + cextend; add->next = 0; last->next = add; last = add; ptr = last->data; end = ptr + cextend; } 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 (int extra) { finalise (); chunk *add = (chunk *) salloc (sizeof (chunk) + _size + extra); add->alloc = sizeof (chunk) + _size; add->next = 0; linearise ((void *)add->data); free (first); first = last = add; ptr = last->data + _size; end = ptr + extra; _size = 0; return first->data; } dynbuf::operator std::string () { // could optimise return std::string (linearise (), size ()); } void dynbuf::splice (int offset, int olen, const char *s, int slen) { // how much bytes to extend (negative if shrinking) int adjust = slen - olen; // linearise, unless everything fits in the last chunk if (offset < _size || room () < adjust) _linearise (max (adjust, 0)); offset -= _size; // offset into chunk // now move tail to final position char *pos = last->data + offset; char *src = pos + olen; char *dst = pos + slen; memmove (dst, src, ptr - src); // now copy new content memcpy (pos, s, slen); // finally adjust length ptr += adjust; } void dynbuf_text::vprintf (const char *format, va_list ap) { int len; { force (128); va_list apc; va_copy (apc, ap); len = vsnprintf (ptr, end - ptr, format, apc); va_end (apc); assert (len >= 0); // shield against broken vsnprintf's // was enough room available if (ptr + len < end) { ptr += len; return; } } // longer, try harder vsnprintf (force (len + 1), len + 1, format, ap); ptr += len; } void dynbuf_text::printf (const char *format, ...) { va_list ap; va_start (ap, format); vprintf (format, ap); va_end (ap); } // simply return a mask with "bits" bits set static 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 static 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; // 7 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 (sint32_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 *ptr++ = u + '0'; else if (expect_true (u < 100)) // we have a lot of double-digit numbers, too :) { // let the compiler figure out sth. efficient here *ptr++ = u / 10 + '0'; *ptr++ = u % 10 + '0'; } else if (expect_true (u < 1000000000)) // 9 0's ptr = i2a_9 (ptr, u, false); else { uint32 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 (sint64_digits); *ptr = '-'; ptr += i < 0 ? 1 : 0; uint64 u = i < 0 ? -i : 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 *ptr++ = 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 { uint32 div = u / 1000000000; uint32 rem = u % 1000000000; ptr = i2a_9 (ptr, div, false); ptr = i2a_9 (ptr, rem, true); } else { // a biggy, split off the topmost digit uint32 div = u / UINT64_C (1000000000000000000); uint64 rem = u % UINT64_C (1000000000000000000); *ptr++ = div + '0'; u = rem; { uint32 div = u / 1000000000; uint32 rem = u % 1000000000; ptr = i2a_9 (ptr, div, true); ptr = i2a_9 (ptr, rem, true); } } } dynbuf_text::operator char *() { *this << '\0'; linearise (); --ptr; return first->data; } void dynbuf_text::add_abilities (const char *name, uint32 abilities) { if (!abilities) return; *this << '(' << name; const char *sep = ": "; for_all_bits_sparse_32 (abilities, i) { *this << sep; sep = ", "; *this << attacks [i]; } *this << ')'; } void dynbuf_text::add_paths (const char *name, uint32 paths) { if (!paths) return; *this << '(' << name; const char *sep = ": "; for (int i = 0; i < NRSPELLPATHS; ++i) if (paths & (1 << i)) { *this << sep; sep = ", "; *this << spellpathnames [i]; } *this << ')'; } #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