ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/dynbuf.C
Revision: 1.40
Committed: Tue May 10 11:02:56 2022 UTC (2 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.39: +3 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 *
7 * Deliantra is free software: you can redistribute it and/or modify it under
8 * the terms of the Affero GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the Affero GNU General Public License
18 * and the GNU General Public License along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <support@deliantra.net>
22 */
23
24 #include "global.h"
25
26 #include <cstdio>
27
28 void
29 dynbuf::init (int initial)
30 {
31 cextend = extend;
32 _size = 0;
33
34 first = last = (chunk *)salloc<char> (sizeof (chunk) + initial);
35 first->alloc = sizeof (chunk) + initial;
36 first->next = 0;
37
38 ptr = first->data;
39 end = ptr + initial;
40 }
41
42 // frees a full chain and sets the pointer to zero
43 void
44 dynbuf::free (chunk *&chain)
45 {
46 while (chain)
47 {
48 chunk *next = chain->next;
49
50 sfree<char> ((char *)chain, chain->alloc);
51 chain = next;
52 }
53 }
54
55 void
56 dynbuf::clear ()
57 {
58 cextend = extend;
59 free (first->next);
60
61 _size = 0;
62 ptr = first->data;
63 end = ptr + first->alloc - sizeof (chunk);
64 last = first;
65 }
66
67 void
68 dynbuf::finalise ()
69 {
70 // finalise current chunk
71 _size += last->size = ptr - last->data;
72 }
73
74 void
75 dynbuf::reserve (int size)
76 {
77 finalise ();
78
79 do
80 {
81 cextend += cextend >> 1;
82 cextend = (cextend + 15) & ~15;
83 }
84 while (cextend < size);
85
86 chunk *add = (chunk *) salloc<char> (sizeof (chunk) + cextend);
87 add->alloc = sizeof (chunk) + cextend;
88 add->next = 0;
89
90 last->next = add;
91 last = add;
92
93 ptr = last->data;
94 end = ptr + cextend;
95 }
96
97 void
98 dynbuf::linearise (void *data)
99 {
100 last->size = ptr - last->data;
101
102 for (chunk *c = first; c; c = c->next)
103 {
104 memcpy (data, c->data, c->size);
105 data = (void *)(((char *)data) + c->size);
106 }
107 }
108
109 char *
110 dynbuf::_linearise (int extra)
111 {
112 finalise ();
113
114 chunk *add = (chunk *) salloc<char> (sizeof (chunk) + _size + extra);
115 add->alloc = sizeof (chunk) + _size;
116 add->next = 0;
117
118 linearise ((void *)add->data);
119 free (first);
120
121 first = last = add;
122 ptr = last->data + _size;
123 end = ptr + extra;
124 _size = 0;
125
126 return first->data;
127 }
128
129 dynbuf::operator std::string ()
130 {
131 // could optimise
132 return std::string (linearise (), size ());
133 }
134
135 void
136 dynbuf::splice (int offset, int olen, const char *s, int slen)
137 {
138 // how much bytes to extend (negative if shrinking)
139 int adjust = slen - olen;
140
141 // linearise, unless everything fits in the last chunk
142 if (offset < _size || room () < adjust)
143 _linearise (max (adjust, 0));
144
145 offset -= _size; // offset into chunk
146
147 // now move tail to final position
148 char *pos = last->data + offset;
149 char *src = pos + olen;
150 char *dst = pos + slen;
151 memmove (dst, src, ptr - src);
152
153 // now copy new content
154 memcpy (pos, s, slen);
155
156 // finally adjust length
157 ptr += adjust;
158 }
159
160 void
161 dynbuf_text::vprintf (const char *format, va_list ap)
162 {
163 int len;
164
165 {
166 force (128);
167
168 va_list apc;
169 va_copy (apc, ap);
170 len = vsnprintf (ptr, end - ptr, format, apc);
171 va_end (apc);
172
173 assert (len >= 0); // shield against broken vsnprintf's
174
175 // was enough room available
176 if (ptr + len < end)
177 {
178 ptr += len;
179 return;
180 }
181 }
182
183 // longer, try harder
184 vsnprintf (force (len + 1), len + 1, format, ap);
185
186 ptr += len;
187 }
188
189 void
190 dynbuf_text::printf (const char *format, ...)
191 {
192 va_list ap;
193 va_start (ap, format);
194 vprintf (format, ap);
195 va_end (ap);
196 }
197
198 // simply return a mask with "bits" bits set
199 static inline uint64
200 m (int b)
201 {
202 return (uint64 (1) << b) - 1;
203 }
204
205 // convert 9 digits to ascii, using only a single multiplication
206 // (depending on cpu and compiler).
207 // will generate a single 0 as output when v=lz=0
208 static inline char *
209 i2a_9 (char *ptr, uint32 v, bool lz)
210 {
211 // convert to 4.56 fixed-point representation
212 // this should be optimal on 64 bit cpus, and rather
213 // slow on 32 bit cpus. go figure :)
214 // 56 bit is good up to 1160869954, 60 is good up to 2932500666 (> 2**31)
215 // that emans we can do signed 32 in one go, but we are too lazy
216 const int bits = 7*8;
217
218 uint64 u = v * ((m (bits) + 100000000) / 100000000); // 10**8
219
220 if (lz)
221 {
222 // output leading zeros
223 // good compilers will compile this into only shifts, masks and adds
224 *ptr++ = char (u >> (bits - 0)) + '0'; u = (u & m (bits - 0)) * 5;
225 *ptr++ = char (u >> (bits - 1)) + '0'; u = (u & m (bits - 1)) * 5;
226 *ptr++ = char (u >> (bits - 2)) + '0'; u = (u & m (bits - 2)) * 5;
227 *ptr++ = char (u >> (bits - 3)) + '0'; u = (u & m (bits - 3)) * 5;
228 *ptr++ = char (u >> (bits - 4)) + '0'; u = (u & m (bits - 4)) * 5;
229 *ptr++ = char (u >> (bits - 5)) + '0'; u = (u & m (bits - 5)) * 5;
230 *ptr++ = char (u >> (bits - 6)) + '0'; u = (u & m (bits - 6)) * 5;
231 *ptr++ = char (u >> (bits - 7)) + '0'; u = (u & m (bits - 7)) * 5;
232 *ptr++ = char (u >> (bits - 8)) + '0';
233 }
234 else
235 {
236 // do not output leading zeroes (except if v == 0)
237 // good compilers will compile this into completely branchless code
238 char digit, nz = 0;
239
240 digit = (u >> (bits - 0)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 0)) * 5;
241 digit = (u >> (bits - 1)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 1)) * 5;
242 digit = (u >> (bits - 2)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 2)) * 5;
243 digit = (u >> (bits - 3)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 3)) * 5;
244 digit = (u >> (bits - 4)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 4)) * 5;
245 digit = (u >> (bits - 5)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 5)) * 5;
246 digit = (u >> (bits - 6)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 6)) * 5;
247 digit = (u >> (bits - 7)); *ptr = digit + '0'; nz |= digit; ptr += nz ? 1 : 0; u = (u & m (bits - 7)) * 5;
248 digit = (u >> (bits - 8)); *ptr = digit + '0'; nz |= digit; ptr += 1;
249 }
250
251 return ptr;
252 }
253
254 void
255 dynbuf_text::add (sint32 i)
256 {
257 force (sint32_digits);
258
259 *ptr = '-'; ptr += i < 0 ? 1 : 0;
260 uint32 u = i < 0 ? -i : i;
261
262 if (ecb_expect_true (u < 10)) // we have a lot of single-digit numbers, so optimise
263 *ptr++ = u + '0';
264 else if (ecb_expect_true (u < 100)) // we have a lot of double-digit numbers, too :)
265 {
266 // let the compiler figure out sth. efficient here
267 *ptr++ = u / 10 + '0';
268 *ptr++ = u % 10 + '0';
269 }
270 else if (ecb_expect_true (u < 1000000000)) // 9 0's
271 ptr = i2a_9 (ptr, u, false);
272 else
273 {
274 uint32 div = u / 1000000000;
275 uint32 rem = u % 1000000000;
276
277 ptr = i2a_9 (ptr, div, false);
278 ptr = i2a_9 (ptr, rem, true);
279 }
280 }
281
282 void
283 dynbuf_text::add (sint64 i)
284 {
285 force (sint64_digits);
286
287 *ptr = '-'; ptr += i < 0 ? 1 : 0;
288 uint64 u = i < 0 ? -i : i;
289
290 // split the number into a 1-digit part
291 // (#19) and two 9 digit parts (9..18 and 0..8)
292
293 // good compilers will only use multiplications here
294
295 if (u < 10) // we have a lot of single-digit numbers, so optimise
296 *ptr++ = u + '0';
297 else if (ecb_expect_true (u < 1000000000)) // 9 0's
298 ptr = i2a_9 (ptr, u, false);
299 else if (ecb_expect_true (u < UINT64_C (1000000000000000000))) // 18 0's
300 {
301 uint32 div = u / 1000000000;
302 uint32 rem = u % 1000000000;
303
304 ptr = i2a_9 (ptr, div, false);
305 ptr = i2a_9 (ptr, rem, true);
306 }
307 else
308 {
309 // a biggy, split off the topmost digit
310 uint32 div = u / UINT64_C (1000000000000000000);
311 uint64 rem = u % UINT64_C (1000000000000000000);
312
313 *ptr++ = div + '0';
314
315 u = rem;
316
317 {
318 uint32 div = u / 1000000000;
319 uint32 rem = u % 1000000000;
320
321 ptr = i2a_9 (ptr, div, true);
322 ptr = i2a_9 (ptr, rem, true);
323 }
324 }
325 }
326
327 dynbuf_text::operator char *()
328 {
329 *this << '\0';
330 linearise ();
331 --ptr;
332 return first->data;
333 }
334
335 void
336 dynbuf_text::add_abilities (const char *name, uint32 abilities)
337 {
338 if (!abilities)
339 return;
340
341 *this << '(' << name;
342
343 const char *sep = ": ";
344 for_all_bits_sparse_32 (abilities, i)
345 {
346 *this << sep; sep = ", ";
347 *this << attacks [i];
348 }
349
350 *this << ')';
351 }
352
353 void
354 dynbuf_text::add_paths (const char *name, uint32 paths)
355 {
356 if (!paths)
357 return;
358
359 *this << '(' << name;
360
361 const char *sep = ": ";
362 for (int i = 0; i < NRSPELLPATHS; ++i)
363 if (paths & (1 << i))
364 {
365 *this << sep; sep = ", ";
366 *this << spellpathnames [i];
367 }
368
369 *this << ')';
370 }
371
372 #if 0
373 struct dynbuf_test_class {
374 dynbuf_test_class ()
375 {
376 sint64 s = 0;
377 for (int i = 0; i < 10000000; ++i)
378 {
379 char b1[256], b2[256];
380
381 dynbuf_text db;
382 db.add (s);
383 db.add (char (0));
384
385 db.linearise (b1);
386 sprintf (b2, "%ld", s);
387
388 if (strcmp (b1, b2))
389 printf ("<%s,%s>\n", b1, b2);
390
391 if (i < 20)
392 s = (sint64) pow (10., i);
393 else
394 s = (sint64) exp (random () * (43.6682723752766 / RAND_MAX));
395 }
396
397 exit (0);
398 }
399 } dynbuf_test;
400 #endif
401