/*
* 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