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

# User Rev Content
1 root 1.15 /*
2 root 1.20 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.36 *
4 root 1.38 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 root 1.37 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 root 1.36 *
7 root 1.24 * 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 root 1.36 *
12 root 1.16 * 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 root 1.36 *
17 root 1.24 * 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 root 1.36 *
21 root 1.20 * The authors can be reached via e-mail to <support@deliantra.net>
22 root 1.15 */
23    
24 root 1.1 #include "global.h"
25    
26     #include <cstdio>
27    
28 root 1.17 void
29     dynbuf::init (int initial)
30 root 1.1 {
31 root 1.23 cextend = extend;
32 root 1.1 _size = 0;
33 root 1.5
34 root 1.8 first = last = (chunk *)salloc<char> (sizeof (chunk) + initial);
35     first->alloc = sizeof (chunk) + initial;
36 root 1.1 first->next = 0;
37 root 1.8
38 root 1.5 ptr = first->data;
39 root 1.9 end = ptr + initial;
40 root 1.1 }
41    
42 root 1.22 // frees a full chain and sets the pointer to zero
43 root 1.5 void
44 root 1.17 dynbuf::free (chunk *&chain)
45 root 1.1 {
46 root 1.17 while (chain)
47 root 1.1 {
48 root 1.17 chunk *next = chain->next;
49 root 1.5
50 root 1.17 sfree<char> ((char *)chain, chain->alloc);
51     chain = next;
52 root 1.1 }
53 root 1.5 }
54 root 1.1
55 root 1.5 void
56 root 1.10 dynbuf::clear ()
57     {
58 root 1.23 cextend = extend;
59 root 1.17 free (first->next);
60    
61 root 1.10 _size = 0;
62     ptr = first->data;
63 root 1.17 end = ptr + first->alloc - sizeof (chunk);
64 root 1.22 last = first;
65 root 1.10 }
66    
67     void
68 root 1.17 dynbuf::finalise ()
69 root 1.1 {
70     // finalise current chunk
71     _size += last->size = ptr - last->data;
72     }
73    
74 root 1.5 void
75 root 1.17 dynbuf::reserve (int size)
76 root 1.1 {
77 root 1.17 finalise ();
78 root 1.1
79     do
80     {
81 root 1.23 cextend += cextend >> 1;
82     cextend = (cextend + 15) & ~15;
83 root 1.1 }
84 root 1.23 while (cextend < size);
85 root 1.1
86 root 1.23 chunk *add = (chunk *) salloc<char> (sizeof (chunk) + cextend);
87     add->alloc = sizeof (chunk) + cextend;
88 root 1.1 add->next = 0;
89    
90     last->next = add;
91     last = add;
92    
93 root 1.5 ptr = last->data;
94 root 1.23 end = ptr + cextend;
95 root 1.1 }
96    
97 root 1.5 void
98     dynbuf::linearise (void *data)
99 root 1.1 {
100     last->size = ptr - last->data;
101    
102 root 1.10 for (chunk *c = first; c; c = c->next)
103 root 1.1 {
104 root 1.10 memcpy (data, c->data, c->size);
105     data = (void *)(((char *)data) + c->size);
106 root 1.1 }
107     }
108    
109 root 1.5 char *
110 root 1.26 dynbuf::_linearise (int extra)
111 root 1.1 {
112 root 1.17 finalise ();
113    
114 root 1.26 chunk *add = (chunk *) salloc<char> (sizeof (chunk) + _size + extra);
115 root 1.17 add->alloc = sizeof (chunk) + _size;
116     add->next = 0;
117    
118     linearise ((void *)add->data);
119     free (first);
120 root 1.1
121 root 1.17 first = last = add;
122     ptr = last->data + _size;
123 root 1.26 end = ptr + extra;
124 root 1.17 _size = 0;
125 root 1.1
126     return first->data;
127     }
128    
129 root 1.7 dynbuf::operator std::string ()
130     {
131     // could optimise
132     return std::string (linearise (), size ());
133     }
134    
135     void
136 root 1.26 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 root 1.18 dynbuf_text::vprintf (const char *format, va_list ap)
162 root 1.7 {
163     int len;
164    
165     {
166     force (128);
167    
168 root 1.18 va_list apc;
169     va_copy (apc, ap);
170 root 1.19 len = vsnprintf (ptr, end - ptr, format, apc);
171 root 1.18 va_end (apc);
172 root 1.7
173     assert (len >= 0); // shield against broken vsnprintf's
174    
175     // was enough room available
176 root 1.9 if (ptr + len < end)
177 root 1.7 {
178 root 1.13 ptr += len;
179 root 1.7 return;
180     }
181     }
182    
183     // longer, try harder
184 root 1.18 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 root 1.7 va_list ap;
193     va_start (ap, format);
194 root 1.18 vprintf (format, ap);
195 root 1.7 va_end (ap);
196     }
197    
198 root 1.11 // simply return a mask with "bits" bits set
199 root 1.28 static inline uint64
200 root 1.11 m (int b)
201 root 1.1 {
202 root 1.11 return (uint64 (1) << b) - 1;
203     }
204 root 1.1
205 root 1.11 // 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 root 1.28 static inline char *
209 root 1.11 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 root 1.40 // 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 root 1.11
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 root 1.33 *ptr++ = char (u >> (bits - 0)) + '0'; u = (u & m (bits - 0)) * 5;
225 root 1.11 *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 root 1.1 }
234     else
235 root 1.4 {
236 root 1.11 // 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 root 1.4 }
250 root 1.1
251 root 1.11 return ptr;
252     }
253    
254     void
255     dynbuf_text::add (sint32 i)
256     {
257 root 1.14 force (sint32_digits);
258 root 1.11
259     *ptr = '-'; ptr += i < 0 ? 1 : 0;
260     uint32 u = i < 0 ? -i : i;
261    
262 root 1.39 if (ecb_expect_true (u < 10)) // we have a lot of single-digit numbers, so optimise
263 root 1.30 *ptr++ = u + '0';
264 root 1.39 else if (ecb_expect_true (u < 100)) // we have a lot of double-digit numbers, too :)
265 root 1.30 {
266     // let the compiler figure out sth. efficient here
267     *ptr++ = u / 10 + '0';
268     *ptr++ = u % 10 + '0';
269     }
270 root 1.39 else if (ecb_expect_true (u < 1000000000)) // 9 0's
271 root 1.11 ptr = i2a_9 (ptr, u, false);
272     else
273 root 1.1 {
274 root 1.30 uint32 div = u / 1000000000;
275 root 1.11 uint32 rem = u % 1000000000;
276 root 1.5
277 root 1.11 ptr = i2a_9 (ptr, div, false);
278     ptr = i2a_9 (ptr, rem, true);
279 root 1.1 }
280     }
281    
282 root 1.5 void
283 root 1.7 dynbuf_text::add (sint64 i)
284 root 1.1 {
285 root 1.14 force (sint64_digits);
286 root 1.11
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 root 1.4
293 root 1.11 // good compilers will only use multiplications here
294 root 1.2
295 root 1.11 if (u < 10) // we have a lot of single-digit numbers, so optimise
296 root 1.30 *ptr++ = u + '0';
297 root 1.39 else if (ecb_expect_true (u < 1000000000)) // 9 0's
298 root 1.11 ptr = i2a_9 (ptr, u, false);
299 root 1.39 else if (ecb_expect_true (u < UINT64_C (1000000000000000000))) // 18 0's
300 root 1.1 {
301 root 1.30 uint32 div = u / 1000000000;
302 root 1.11 uint32 rem = u % 1000000000;
303    
304     ptr = i2a_9 (ptr, div, false);
305     ptr = i2a_9 (ptr, rem, true);
306 root 1.1 }
307 root 1.2 else
308 root 1.1 {
309 root 1.30 // a biggy, split off the topmost digit
310     uint32 div = u / UINT64_C (1000000000000000000);
311 root 1.11 uint64 rem = u % UINT64_C (1000000000000000000);
312    
313 root 1.30 *ptr++ = div + '0';
314    
315 root 1.11 u = rem;
316    
317     {
318 root 1.30 uint32 div = u / 1000000000;
319 root 1.11 uint32 rem = u % 1000000000;
320    
321     ptr = i2a_9 (ptr, div, true);
322     ptr = i2a_9 (ptr, rem, true);
323     }
324 root 1.1 }
325 root 1.11 }
326 root 1.1
327 root 1.25 dynbuf_text::operator char *()
328 root 1.17 {
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 root 1.29 for_all_bits_sparse_32 (abilities, i)
345     {
346     *this << sep; sep = ", ";
347     *this << attacks [i];
348     }
349 root 1.17
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 root 1.11 #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 root 1.17