Freeciv-3.2: /fast/freeciv/nightly/S3_2/utility/astring.c Source File

Freeciv-3.2

Loading...
Searching...
No Matches
astring.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14/***********************************************************************
15 Allocated/allocatable strings
16 original author: David Pfitzner <dwp@mso.anu.edu.au>
17
18 A common technique is to have some memory dynamically allocated
19 (using malloc etc), to avoid compiled-in limits, but only allocate
20 enough space as initially needed, and then realloc later if/when
21 require more space. Typically, the realloc is made a bit more than
22 immediately necessary, to avoid frequent reallocs if the object
23 grows incrementally. Also, don't usually realloc at all if the
24 object shrinks. This is straightforward, but just requires a bit
25 of book-keeping to keep track of how much has been allocated etc.
26 This module provides some tools to make this a bit easier.
27
28 This is deliberately simple and light-weight. The user is allowed
29 full access to the struct elements rather than use accessor
30 functions etc.
31
32 Note one potential hazard: when the size is increased (astr_reserve()),
33 realloc (really fc_realloc()) is used, which retains any data which
34 was there previously, _but_: any external pointers into the allocated
35 memory may then become wild. So you cannot safely use such external
36 pointers into the astring data, except strictly between times when
37 the astring size may be changed.
38
39 There are two ways of getting the resulting string as a char *:
40
41 - astr_str() returns a const char *. This should not be modified
42 or freed by the caller; the storage remains owned by the
43 struct astring, which should be freed with astr_free().
44
45 - astr_to_str() returns a char * and destroys the struct astring.
46 Responsibility for freeing the storage becomes the caller's.
47
48 One pattern for using astr_str() is to replace static buffers in
49 functions that return a pointer to static storage. Where previously
50 you would have had e.g. "static struct buf[128]" with an arbitrary
51 size limit, you can have "static struct astring buf", and reuse
52 the same astring on subsequent calls; the caller should behave
53 the same (only reading the string and not freeing it).
54
55***********************************************************************/
56
57#ifdef HAVE_CONFIG_H
58#include <fc_config.h>
59#endif
60
61#include "fc_prehdrs.h"
62
63#include <stdarg.h>
64#include <stdlib.h>
65#include <string.h>
66
67/* utility */
68#include "fcintl.h"
69#include "fcthread.h"
70#include "log.h" /* fc_assert */
71#include "mem.h"
72#include "support.h" /* fc_vsnprintf, fc_strlcat */
73
74#include "astring.h"
75
76#define str _private_str_
77#define n _private_n_
78#define n_alloc _private_n_alloc_
79
80 static const struct astring zero_astr = ASTRING_INIT;
81 static char *astr_buffer = NULL;
82 static size_t astr_buffer_alloc = 0;
83
84 static fc_mutex astr_mutex;
85
86static inline char *astr_buffer_get(size_t *alloc);
87static inline char *astr_buffer_grow(size_t request, size_t *alloc);
88static void astr_buffer_free(void);
89
90/************************************************************************/
93 static inline char *astr_buffer_get(size_t *alloc)
94{
95 if (!astr_buffer) {
96#ifndef HAVE_VA_COPY
97 /* This buffer will never be grown, so it should be big enough
98 * from the beginning. */
99 astr_buffer_alloc = 65536;
100#else
101 astr_buffer_alloc = 4096;
102#endif
103 astr_buffer = fc_malloc(astr_buffer_alloc);
104 atexit(astr_buffer_free);
105 }
106
107 *alloc = astr_buffer_alloc;
108 return astr_buffer;
109}
110
111/************************************************************************/
114 static inline char *astr_buffer_grow(size_t request, size_t *alloc)
115{
116 if (request > astr_buffer_alloc) {
117 /* We simply set buffer size to the requested one here.
118 * Old implementation went on by doubling the buffer size,
119 * presumably to match what low level memory handling does
120 * anyway. But need to increase the buffer size is so rare
121 * that we can as well call this function again, even when
122 * the increase would fall within limits of what doubling
123 * would have given us. */
124 astr_buffer_alloc = request;
125 }
126 astr_buffer = fc_realloc(astr_buffer, astr_buffer_alloc);
127
128 *alloc = astr_buffer_alloc;
129
130 return astr_buffer;
131}
132
133/************************************************************************/
136 static void astr_buffer_free(void)
137{
138 free(astr_buffer);
139}
140
141/************************************************************************/
144 void astr_init(struct astring *astr)
145{
146 *astr = zero_astr;
147}
148
149/************************************************************************/
153 void astr_free(struct astring *astr)
154{
155 if (astr->n_alloc > 0) {
156 fc_assert_ret(NULL != astr->str);
157 free(astr->str);
158 }
159 *astr = zero_astr;
160}
161
162/************************************************************************/
167 char *astr_to_str(struct astring *astr)
168{
169 char *str = astr->str;
170
171 *astr = zero_astr;
172
173 return str;
174}
175
176/************************************************************************/
183 void astr_reserve(struct astring *astr, size_t n)
184{
185 unsigned int n1;
186 bool was_null = (astr->n == 0);
187
188 astr->n = n;
189 if (n <= astr-> n_alloc) {
190 return;
191 }
192
193 /* Allocated more if this is only a small increase on before: */
194 n1 = (3 * (astr->n_alloc + 10)) / 2;
195 astr->n_alloc = (n > n1) ? n : n1;
196 astr->str = (char *) fc_realloc(astr->str, astr->n_alloc);
197 if (was_null) {
198 astr_clear(astr);
199 }
200}
201
202/************************************************************************/
205 void astr_clear(struct astring *astr)
206{
207 if (astr->n == 0) {
208 /* astr_reserve is really astr_size, so we don't want to reduce the
209 * size. */
210 astr_reserve(astr, 1);
211 }
212 astr->str[0] = '\0';
213}
214
215/************************************************************************/
218 static inline void astr_vadd_at(struct astring *astr, size_t at,
219 const char *format, va_list ap)
220{
221 char *buffer;
222 size_t buffer_size;
223 size_t req_len;
224
225 fc_mutex_allocate(&astr_mutex);
226
227#ifdef HAVE_VA_COPY
228 va_list copy;
229
230 buffer = astr_buffer_get(&buffer_size);
231
232 va_copy(copy, ap);
233
234 req_len = fc_vsnprintf(buffer, buffer_size, format, ap);
235 if (req_len + 1 > buffer_size) {
236 buffer = astr_buffer_grow(req_len + 1, &buffer_size);
237 /* Even if buffer is *still* too small, we fill what we can */
238 req_len = fc_vsnprintf(buffer, buffer_size, format, copy);
239 if (req_len > buffer_size) {
240 /* What we actually got */
241 req_len = buffer_size;
242 }
243 }
244 va_end(copy);
245#else /* HAVE_VA_COPY */
246 buffer = astr_buffer_get(&buffer_size);
247
248 req_len = fc_vsnprintf(buffer, buffer_size, format, ap);
249
250 if (req_len > buffer_size) {
251 /* What we actually got */
252 req_len = buffer_size;
253 }
254#endif /* HAVE_VA_COPY */
255
256 req_len += at + 1;
257
258 astr_reserve(astr, req_len);
259 fc_strlcpy(astr->str + at, buffer, astr->n_alloc - at);
260
261 fc_mutex_release(&astr_mutex);
262}
263
264/************************************************************************/
267 void astr_set(struct astring *astr, const char *format, ...)
268{
269 va_list args;
270
271 va_start(args, format);
272 astr_vadd_at(astr, 0, format, args);
273 va_end(args);
274}
275
276/************************************************************************/
279 void astr_vadd(struct astring *astr, const char *format, va_list ap)
280{
281 astr_vadd_at(astr, astr_len(astr), format, ap);
282}
283
284/************************************************************************/
287 void astr_add(struct astring *astr, const char *format, ...)
288{
289 va_list args;
290
291 va_start(args, format);
292 astr_vadd_at(astr, astr_len(astr), format, args);
293 va_end(args);
294}
295
296/************************************************************************/
299 void astr_add_line(struct astring *astr, const char *format, ...)
300{
301 size_t len = astr_len(astr);
302 va_list args;
303
304 va_start(args, format);
305 if (0 < len) {
306 astr_vadd_at(astr, len + 1, format, args);
307 astr->str[len] = '\n';
308 } else {
309 astr_vadd_at(astr, len, format, args);
310 }
311 va_end(args);
312}
313
314/************************************************************************/
318 void astr_break_lines(struct astring *astr, size_t desired_len)
319{
320 fc_break_lines(astr->str, desired_len);
321}
322
323/************************************************************************/
329 const char *astr_build_or_list(struct astring *astr,
330 const char *const *items, size_t number)
331{
332 fc_assert_ret_val(NULL != astr, NULL);
333 fc_assert_ret_val(0 < number, NULL);
334 fc_assert_ret_val(NULL != items, NULL);
335
336 if (1 == number) {
337 /* TRANS: "or"-separated string list with one single item. */
338 astr_set(astr, Q_("?or-list-single:%s"), *items);
339 } else if (2 == number) {
340 /* TRANS: "or"-separated string list with 2 items. */
341 astr_set(astr, Q_("?or-list:%s or %s"), items[0], items[1]);
342 } else {
343 /* Estimate the space we need. */
344 astr_reserve(astr, number * 64);
345 /* TRANS: start of an "or"-separated string list with more than two
346 * items. */
347 astr_set(astr, Q_("?or-list:%s"), *items++);
348 while (1 < --number) {
349 /* TRANS: next elements of an "or"-separated string list with more
350 * than two items. */
351 astr_add(astr, Q_("?or-list:, %s"), *items++);
352 }
353 /* TRANS: end of an "or"-separated string list with more than two
354 * items. */
355 astr_add(astr, Q_("?or-list:, or %s"), *items);
356 }
357
358 return astr->str;
359}
360
361/************************************************************************/
367 const char *astr_build_and_list(struct astring *astr,
368 const char *const *items, size_t number)
369{
370 fc_assert_ret_val(NULL != astr, NULL);
371 fc_assert_ret_val(0 < number, NULL);
372 fc_assert_ret_val(NULL != items, NULL);
373
374 if (1 == number) {
375 /* TRANS: "and"-separated string list with one single item. */
376 astr_set(astr, Q_("?and-list-single:%s"), *items);
377 } else if (2 == number) {
378 /* TRANS: "and"-separated string list with 2 items. */
379 astr_set(astr, Q_("?and-list:%s and %s"), items[0], items[1]);
380 } else {
381 /* Estimate the space we need. */
382 astr_reserve(astr, number * 64);
383 /* TRANS: start of an "and"-separated string list with more than two
384 * items. */
385 astr_set(astr, Q_("?and-list:%s"), *items++);
386 while (1 < --number) {
387 /* TRANS: next elements of an "and"-separated string list with more
388 * than two items. */
389 astr_add(astr, Q_("?and-list:, %s"), *items++);
390 }
391 /* TRANS: end of an "and"-separated string list with more than two
392 * items. */
393 astr_add(astr, Q_("?and-list:, and %s"), *items);
394 }
395
396 return astr->str;
397}
398
399/************************************************************************/
402 void astr_copy(struct astring *dest, const struct astring *src)
403{
404 if (astr_empty(src)) {
405 astr_clear(dest);
406 } else {
407 astr_set(dest, "%s", src->str);
408 }
409}
410
411/************************************************************************/
414 void fc_astr_init(void)
415{
416 fc_mutex_init(&astr_mutex);
417}
418
419/************************************************************************/
422 void fc_astr_free(void)
423{
424 fc_mutex_destroy(&astr_mutex);
425}
astr_buffer_get
static char * astr_buffer_get(size_t *alloc)
Definition astring.c:93
n_alloc
#define n_alloc
Definition astring.c:78
astr_free
void astr_free(struct astring *astr)
Definition astring.c:153
astr_build_or_list
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:329
astr_buffer_free
static void astr_buffer_free(void)
Definition astring.c:136
astr_vadd
void astr_vadd(struct astring *astr, const char *format, va_list ap)
Definition astring.c:279
astr_set
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:267
astr_build_and_list
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:367
astr_add_line
void astr_add_line(struct astring *astr, const char *format,...)
Definition astring.c:299
astr_reserve
void astr_reserve(struct astring *astr, size_t n)
Definition astring.c:183
astr_clear
void astr_clear(struct astring *astr)
Definition astring.c:205
astr_vadd_at
static void astr_vadd_at(struct astring *astr, size_t at, const char *format, va_list ap)
Definition astring.c:218
astr_init
void astr_init(struct astring *astr)
Definition astring.c:144
fc_astr_init
void fc_astr_init(void)
Definition astring.c:414
astr_to_str
char * astr_to_str(struct astring *astr)
Definition astring.c:167
astr_add
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:287
str
#define str
Definition astring.c:76
astr_buffer_grow
static char * astr_buffer_grow(size_t request, size_t *alloc)
Definition astring.c:114
zero_astr
static const struct astring zero_astr
Definition astring.c:80
astr_buffer
static char * astr_buffer
Definition astring.c:81
fc_astr_free
void fc_astr_free(void)
Definition astring.c:422
n
#define n
Definition astring.c:77
astr_mutex
static fc_mutex astr_mutex
Definition astring.c:84
astr_buffer_alloc
static size_t astr_buffer_alloc
Definition astring.c:82
astr_break_lines
void astr_break_lines(struct astring *astr, size_t desired_len)
Definition astring.c:318
astr_copy
void astr_copy(struct astring *dest, const struct astring *src)
Definition astring.c:402
astring.h
astr_len
static size_t astr_len(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:101
ASTRING_INIT
#define ASTRING_INIT
Definition astring.h:44
astr_empty
static bool astr_empty(const struct astring *astr) fc__attribute((nonnull(1)))
Definition astring.h:125
incite_cost
char * incite_cost
Definition comments.c:74
fc_prehdrs.h
fcintl.h
Q_
#define Q_(String)
Definition fcintl.h:70
fcthread.h
fc_mutex_allocate
void fc_mutex_allocate(fc_mutex *mutex)
fc_mutex_init
void fc_mutex_init(fc_mutex *mutex)
fc_mutex_release
void fc_mutex_release(fc_mutex *mutex)
fc_mutex_destroy
void fc_mutex_destroy(fc_mutex *mutex)
log.h
fc_assert_ret
#define fc_assert_ret(condition)
Definition log.h:191
fc_assert_ret_val
#define fc_assert_ret_val(condition, val)
Definition log.h:194
mem.h
fc_realloc
#define fc_realloc(ptr, sz)
Definition mem.h:36
fc_malloc
#define fc_malloc(sz)
Definition mem.h:34
len
int len
Definition packhand.c:127
astring
Definition astring.h:36
astring::str
char * str
Definition astring.h:37
astring::n
size_t n
Definition astring.h:38
fc_strlcpy
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:791
fc_vsnprintf
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:900
fc_break_lines
int fc_break_lines(char *str, size_t desired_len)
Definition support.c:1149
support.h