Freeciv-3.3: /fast/freeciv/nightly/main/utility/astring.c Source File

Freeciv-3.3

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 = nullptr;
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 == nullptr) {
96 astr_buffer_alloc = 4096;
97 astr_buffer = fc_malloc(astr_buffer_alloc);
98 atexit(astr_buffer_free);
99 }
100
101 *alloc = astr_buffer_alloc;
102
103 return astr_buffer;
104}
105
106/************************************************************************/
109 static inline char *astr_buffer_grow(size_t request, size_t *alloc)
110{
111 if (request > astr_buffer_alloc) {
112 /* We simply set buffer size to the requested one here.
113 * Old implementation went on by doubling the buffer size,
114 * presumably to match what low level memory handling does
115 * anyway. But need to increase the buffer size is so rare
116 * that we can as well call this function again, even when
117 * the increase would fall within limits of what doubling
118 * would have given us. */
119 astr_buffer_alloc = request;
120 }
121 astr_buffer = fc_realloc(astr_buffer, astr_buffer_alloc);
122
123 *alloc = astr_buffer_alloc;
124
125 return astr_buffer;
126}
127
128/************************************************************************/
131 static void astr_buffer_free(void)
132{
133 free(astr_buffer);
134}
135
136/************************************************************************/
139 void astr_init(struct astring *astr)
140{
141 *astr = zero_astr;
142}
143
144/************************************************************************/
148 void astr_free(struct astring *astr)
149{
150 if (astr->n_alloc > 0) {
151 fc_assert_ret(astr->str != nullptr);
152 free(astr->str);
153 }
154
155 *astr = zero_astr;
156}
157
158/************************************************************************/
163 char *astr_to_str(struct astring *astr)
164{
165 char *str = astr->str;
166
167 *astr = zero_astr;
168
169 return str;
170}
171
172/************************************************************************/
179 void astr_reserve(struct astring *astr, size_t n)
180{
181 unsigned int n1;
182 bool was_null = (astr->n == 0);
183
184 astr->n = n;
185 if (n <= astr-> n_alloc) {
186 return;
187 }
188
189 /* Allocated more if this is only a small increase on before: */
190 n1 = (3 * (astr->n_alloc + 10)) / 2;
191 astr->n_alloc = (n > n1) ? n : n1;
192 astr->str = (char *) fc_realloc(astr->str, astr->n_alloc);
193 if (was_null) {
194 astr_clear(astr);
195 }
196}
197
198/************************************************************************/
201 void astr_clear(struct astring *astr)
202{
203 if (astr->n == 0) {
204 /* astr_reserve() is really astr_size(), so we don't want to reduce
205 * the size. */
206 astr_reserve(astr, 1);
207 }
208 astr->str[0] = '\0';
209}
210
211/************************************************************************/
214 static inline void astr_vadd_at(struct astring *astr, size_t at,
215 const char *format, va_list ap)
216{
217 char *buffer;
218 size_t buffer_size;
219 size_t req_len;
220 va_list copy;
221
222 fc_mutex_allocate(&astr_mutex);
223
224 buffer = astr_buffer_get(&buffer_size);
225
226 va_copy(copy, ap);
227
228 req_len = fc_vsnprintf(buffer, buffer_size, format, ap);
229 if (req_len + 1 > buffer_size) {
230 buffer = astr_buffer_grow(req_len + 1, &buffer_size);
231 /* Even if buffer is *still* too small, we fill what we can */
232 req_len = fc_vsnprintf(buffer, buffer_size, format, copy);
233 if (req_len > buffer_size) {
234 /* What we actually got */
235 req_len = buffer_size;
236 }
237 }
238 va_end(copy);
239
240 req_len += at + 1;
241
242 astr_reserve(astr, req_len);
243 fc_strlcpy(astr->str + at, buffer, astr->n_alloc - at);
244
245 fc_mutex_release(&astr_mutex);
246}
247
248/************************************************************************/
251 void astr_set(struct astring *astr, const char *format, ...)
252{
253 va_list args;
254
255 va_start(args, format);
256 astr_vadd_at(astr, 0, format, args);
257 va_end(args);
258}
259
260/************************************************************************/
263 void astr_vadd(struct astring *astr, const char *format, va_list ap)
264{
265 astr_vadd_at(astr, astr_len(astr), format, ap);
266}
267
268/************************************************************************/
271 void astr_add(struct astring *astr, const char *format, ...)
272{
273 va_list args;
274
275 va_start(args, format);
276 astr_vadd_at(astr, astr_len(astr), format, args);
277 va_end(args);
278}
279
280/************************************************************************/
283 void astr_add_line(struct astring *astr, const char *format, ...)
284{
285 size_t len = astr_len(astr);
286 va_list args;
287
288 va_start(args, format);
289 if (0 < len) {
290 astr_vadd_at(astr, len + 1, format, args);
291 astr->str[len] = '\n';
292 } else {
293 astr_vadd_at(astr, len, format, args);
294 }
295 va_end(args);
296}
297
298/************************************************************************/
302 void astr_break_lines(struct astring *astr, size_t desired_len)
303{
304 fc_break_lines(astr->str, desired_len);
305}
306
307/************************************************************************/
313 const char *astr_build_or_list(struct astring *astr,
314 const char *const *items, size_t number)
315{
316 fc_assert_ret_val(astr != nullptr, nullptr);
317 fc_assert_ret_val(0 < number, nullptr);
318 fc_assert_ret_val(items != nullptr, nullptr);
319
320 if (1 == number) {
321 /* TRANS: "or"-separated string list with one single item. */
322 astr_set(astr, Q_("?or-list-single:%s"), *items);
323 } else if (2 == number) {
324 /* TRANS: "or"-separated string list with 2 items. */
325 astr_set(astr, Q_("?or-list:%s or %s"), items[0], items[1]);
326 } else {
327 /* Estimate the space we need. */
328 astr_reserve(astr, number * 64);
329 /* TRANS: start of an "or"-separated string list with more than
330 * two items. */
331 astr_set(astr, Q_("?or-list:%s"), *items++);
332 while (1 < --number) {
333 /* TRANS: next elements of an "or"-separated string list with more
334 * than two items. */
335 astr_add(astr, Q_("?or-list:, %s"), *items++);
336 }
337 /* TRANS: end of an "or"-separated string list with more than
338 * two items. */
339 astr_add(astr, Q_("?or-list:, or %s"), *items);
340 }
341
342 return astr->str;
343}
344
345/************************************************************************/
351 const char *astr_build_and_list(struct astring *astr,
352 const char *const *items, size_t number)
353{
354 fc_assert_ret_val(astr != nullptr, nullptr);
355 fc_assert_ret_val(0 < number, nullptr);
356 fc_assert_ret_val(items != nullptr, nullptr);
357
358 if (1 == number) {
359 /* TRANS: "and"-separated string list with one single item. */
360 astr_set(astr, Q_("?and-list-single:%s"), *items);
361 } else if (2 == number) {
362 /* TRANS: "and"-separated string list with 2 items. */
363 astr_set(astr, Q_("?and-list:%s and %s"), items[0], items[1]);
364 } else {
365 /* Estimate the space we need. */
366 astr_reserve(astr, number * 64);
367 /* TRANS: start of an "and"-separated string list with more than
368 * two items. */
369 astr_set(astr, Q_("?and-list:%s"), *items++);
370 while (1 < --number) {
371 /* TRANS: next elements of an "and"-separated string list with more
372 * than two items. */
373 astr_add(astr, Q_("?and-list:, %s"), *items++);
374 }
375 /* TRANS: end of an "and"-separated string list with more than
376 * two items. */
377 astr_add(astr, Q_("?and-list:, and %s"), *items);
378 }
379
380 return astr->str;
381}
382
383/************************************************************************/
386 void astr_copy(struct astring *dest, const struct astring *src)
387{
388 if (astr_empty(src)) {
389 astr_clear(dest);
390 } else {
391 astr_set(dest, "%s", src->str);
392 }
393}
394
395/************************************************************************/
398 void fc_astr_init(void)
399{
400 fc_mutex_init(&astr_mutex);
401}
402
403/************************************************************************/
406 void fc_astr_free(void)
407{
408 fc_mutex_destroy(&astr_mutex);
409}
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:148
astr_build_or_list
const char * astr_build_or_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:313
astr_buffer_free
static void astr_buffer_free(void)
Definition astring.c:131
astr_vadd
void astr_vadd(struct astring *astr, const char *format, va_list ap)
Definition astring.c:263
astr_set
void astr_set(struct astring *astr, const char *format,...)
Definition astring.c:251
astr_build_and_list
const char * astr_build_and_list(struct astring *astr, const char *const *items, size_t number)
Definition astring.c:351
astr_add_line
void astr_add_line(struct astring *astr, const char *format,...)
Definition astring.c:283
astr_reserve
void astr_reserve(struct astring *astr, size_t n)
Definition astring.c:179
astr_clear
void astr_clear(struct astring *astr)
Definition astring.c:201
astr_vadd_at
static void astr_vadd_at(struct astring *astr, size_t at, const char *format, va_list ap)
Definition astring.c:214
astr_init
void astr_init(struct astring *astr)
Definition astring.c:139
fc_astr_init
void fc_astr_init(void)
Definition astring.c:398
astr_to_str
char * astr_to_str(struct astring *astr)
Definition astring.c:163
astr_add
void astr_add(struct astring *astr, const char *format,...)
Definition astring.c:271
str
#define str
Definition astring.c:76
astr_buffer_grow
static char * astr_buffer_grow(size_t request, size_t *alloc)
Definition astring.c:109
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:406
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:302
astr_copy
void astr_copy(struct astring *dest, const struct astring *src)
Definition astring.c:386
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:777
fc_vsnprintf
int fc_vsnprintf(char *str, size_t n, const char *format, va_list ap)
Definition support.c:886
fc_break_lines
int fc_break_lines(char *str, size_t desired_len)
Definition support.c:1135
support.h