Coverage Report

Created: 2026-04-16 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgis/liblwgeom/stringbuffer.c
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * PostGIS - Spatial Types for PostgreSQL
4
 * http://postgis.net
5
 *
6
 * PostGIS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 2 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * PostGIS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
18
 *
19
 **********************************************************************
20
 *
21
 * Copyright 2002 Thamer Alharbash
22
 * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
23
 *
24
 **********************************************************************/
25
26
#include "liblwgeom_internal.h"
27
#include "stringbuffer.h"
28
29
/**
30
* Allocate a new stringbuffer_t. Use stringbuffer_destroy to free.
31
*/
32
stringbuffer_t*
33
stringbuffer_create(void)
34
0
{
35
0
  return stringbuffer_create_with_size(STRINGBUFFER_STARTSIZE);
36
0
}
37
38
static void
39
stringbuffer_init_with_size(stringbuffer_t *s, size_t size)
40
0
{
41
0
  s->str_start = lwalloc(size);
42
0
  s->str_end = s->str_start;
43
0
  s->capacity = size;
44
0
  memset(s->str_start, 0, size);
45
0
}
46
47
void
48
stringbuffer_release(stringbuffer_t *s)
49
0
{
50
0
  if ( s->str_start ) lwfree(s->str_start);
51
0
}
52
53
void
54
stringbuffer_init(stringbuffer_t *s)
55
0
{
56
0
  stringbuffer_init_with_size(s, STRINGBUFFER_STARTSIZE);
57
0
}
58
59
void
60
stringbuffer_init_varlena(stringbuffer_t *s)
61
0
{
62
0
  stringbuffer_init_with_size(s, STRINGBUFFER_STARTSIZE + LWVARHDRSZ);
63
  /* Zero out LWVARHDRSZ bytes at the front of the buffer */
64
0
  stringbuffer_append_len(s, "\0\0\0\0\0\0\0\0", LWVARHDRSZ);
65
0
}
66
67
68
/**
69
* Allocate a new stringbuffer_t. Use stringbuffer_destroy to free.
70
*/
71
stringbuffer_t*
72
stringbuffer_create_with_size(size_t size)
73
0
{
74
0
  stringbuffer_t *s;
75
76
0
  s = lwalloc(sizeof(stringbuffer_t));
77
0
  stringbuffer_init_with_size(s, size);
78
0
  return s;
79
0
}
80
81
/**
82
* Free the stringbuffer_t and all memory managed within it.
83
*/
84
void
85
stringbuffer_destroy(stringbuffer_t *s)
86
0
{
87
0
  if ( s )
88
0
  {
89
0
    stringbuffer_release(s);
90
0
    lwfree(s);
91
0
  }
92
0
}
93
94
/**
95
* Reset the stringbuffer_t. Useful for starting a fresh string
96
* without the expense of freeing and re-allocating a new
97
* stringbuffer_t.
98
*/
99
void
100
stringbuffer_clear(stringbuffer_t *s)
101
0
{
102
0
  s->str_start[0] = '\0';
103
0
  s->str_end = s->str_start;
104
0
}
105
106
/**
107
* Return the last character in the buffer.
108
*/
109
char
110
stringbuffer_lastchar(stringbuffer_t *s)
111
0
{
112
0
  if( s->str_end == s->str_start )
113
0
    return 0;
114
115
0
  return *(s->str_end - 1);
116
0
}
117
118
119
/**
120
* Returns a reference to the internal string being managed by
121
* the stringbuffer. The current string will be null-terminated
122
* within the internal string.
123
*/
124
const char*
125
stringbuffer_getstring(stringbuffer_t *s)
126
0
{
127
0
  return s->str_start;
128
0
}
129
130
/**
131
* Returns a newly allocated string large enough to contain the
132
* current state of the string. Caller is responsible for
133
* freeing the return value.
134
*/
135
char*
136
stringbuffer_getstringcopy(stringbuffer_t *s)
137
0
{
138
0
  size_t size = (s->str_end - s->str_start) + 1;
139
0
  char *str = lwalloc(size);
140
0
  memcpy(str, s->str_start, size);
141
0
  str[size - 1] = '\0';
142
0
  return str;
143
0
}
144
145
lwvarlena_t *
146
stringbuffer_getvarlena(stringbuffer_t *s)
147
0
{
148
0
  lwvarlena_t *output = (lwvarlena_t *)(s->str_start);
149
0
  LWSIZE_SET(output->size, (s->str_end - s->str_start));
150
0
  return output;
151
0
}
152
153
lwvarlena_t *
154
stringbuffer_getvarlenacopy(stringbuffer_t *s)
155
0
{
156
0
  size_t size = (s->str_end - s->str_start);
157
0
  lwvarlena_t *output = (lwvarlena_t *)lwalloc(size + LWVARHDRSZ);
158
0
  LWSIZE_SET(output->size, size + LWVARHDRSZ);
159
160
0
  memcpy(output->data, s->str_start, size);
161
0
  return output;
162
0
}
163
164
/**
165
* Returns the length of the current string, not including the
166
* null terminator (same behavior as strlen()).
167
*/
168
int
169
stringbuffer_getlength(stringbuffer_t *s)
170
0
{
171
0
  return (s->str_end - s->str_start);
172
0
}
173
174
/**
175
* Clear the stringbuffer_t and re-start it with the specified string.
176
*/
177
void
178
stringbuffer_set(stringbuffer_t *s, const char *str)
179
0
{
180
0
  stringbuffer_clear(s);
181
0
  stringbuffer_append(s, str);
182
0
}
183
184
/**
185
* Copy the contents of src into dst.
186
*/
187
void
188
stringbuffer_copy(stringbuffer_t *dst, stringbuffer_t *src)
189
0
{
190
0
  stringbuffer_set(dst, stringbuffer_getstring(src));
191
0
}
192
193
/**
194
* Appends a formatted string to the current string buffer,
195
* using the format and argument list provided. Returns -1 on error,
196
* check errno for reasons, documented in the printf man page.
197
*/
198
static int
199
stringbuffer_avprintf(stringbuffer_t *s, const char *fmt, va_list ap) __attribute__ ((format (printf, 2, 0)));
200
static int
201
stringbuffer_avprintf(stringbuffer_t *s, const char *fmt, va_list ap)
202
0
{
203
0
  int maxlen = (s->capacity - (s->str_end - s->str_start));
204
0
  int len = 0; /* Length of the output */
205
0
  va_list ap2;
206
207
  /* Make a copy of the variadic arguments, in case we need to print twice */
208
  /* Print to our buffer */
209
0
  va_copy(ap2, ap);
210
0
  len = vsnprintf(s->str_end, maxlen, fmt, ap2);
211
0
  va_end(ap2);
212
213
  /* Propagate errors up */
214
0
  if ( len < 0 )
215
    #if defined(__MINGW64_VERSION_MAJOR)
216
    {
217
    va_copy(ap2, ap);
218
    len = _vscprintf(fmt, ap2);/**Assume windows flaky vsnprintf that returns -1 if initial buffer to small and add more space **/
219
    va_end(ap2);
220
    }
221
    #else
222
0
    return len;
223
0
    #endif
224
225
  /* We didn't have enough space! */
226
  /* Either Unix vsnprint returned write length larger than our buffer */
227
  /*     or Windows vsnprintf returned an error code. */
228
0
  if ( len >= maxlen )
229
0
  {
230
0
    stringbuffer_makeroom(s, len + 1);
231
0
    maxlen = (s->capacity - (s->str_end - s->str_start));
232
233
    /* Try to print a second time */
234
0
    len = vsnprintf(s->str_end, maxlen, fmt, ap);
235
236
    /* Printing error? Error! */
237
0
    if ( len < 0 ) return len;
238
    /* Too long still? Error! */
239
0
    if ( len >= maxlen ) return -1;
240
0
  }
241
242
  /* Move end pointer forward and return. */
243
0
  s->str_end += len;
244
0
  return len;
245
0
}
246
247
/**
248
* Appends a formatted string to the current string buffer,
249
* using the format and argument list provided.
250
* Returns -1 on error, check errno for reasons,
251
* as documented in the printf man page.
252
*/
253
int
254
stringbuffer_aprintf(stringbuffer_t *s, const char *fmt, ...)
255
0
{
256
0
  int r;
257
0
  va_list ap;
258
0
  va_start(ap, fmt);
259
0
  r = stringbuffer_avprintf(s, fmt, ap);
260
0
  va_end(ap);
261
0
  return r;
262
0
}
263
264
/**
265
* Trims whitespace off the end of the stringbuffer. Returns
266
* the number of characters trimmed.
267
*/
268
int
269
stringbuffer_trim_trailing_white(stringbuffer_t *s)
270
0
{
271
0
  char *ptr = s->str_end;
272
0
  int dist = 0;
273
274
  /* Roll backwards until we hit a non-space. */
275
0
  while( ptr > s->str_start )
276
0
  {
277
0
    ptr--;
278
0
    if( (*ptr == ' ') || (*ptr == '\t') )
279
0
    {
280
0
      continue;
281
0
    }
282
0
    else
283
0
    {
284
0
      ptr++;
285
0
      dist = s->str_end - ptr;
286
0
      *ptr = '\0';
287
0
      s->str_end = ptr;
288
0
      return dist;
289
0
    }
290
0
  }
291
0
  return dist;
292
0
}
293
294
/**
295
* Trims zeroes off the end of the last number in the stringbuffer.
296
* The number has to be the very last thing in the buffer. Only the
297
* last number will be trimmed. Returns the number of characters
298
* trimmed.
299
*
300
* eg: 1.22000 -> 1.22
301
*     1.0 -> 1
302
*     0.0 -> 0
303
*/
304
int
305
stringbuffer_trim_trailing_zeroes(stringbuffer_t *s)
306
0
{
307
0
  char *ptr = s->str_end;
308
0
  char *decimal_ptr = NULL;
309
0
  int dist;
310
311
0
  if ( s->str_end - s->str_start < 2)
312
0
    return 0;
313
314
  /* Roll backwards to find the decimal for this number */
315
0
  while( ptr > s->str_start )
316
0
  {
317
0
    ptr--;
318
0
    if ( *ptr == '.' )
319
0
    {
320
0
      decimal_ptr = ptr;
321
0
      break;
322
0
    }
323
0
    if ( (*ptr >= '0') && (*ptr <= '9' ) )
324
0
      continue;
325
0
    else
326
0
      break;
327
0
  }
328
329
  /* No decimal? Nothing to trim! */
330
0
  if ( ! decimal_ptr )
331
0
    return 0;
332
333
0
  ptr = s->str_end;
334
335
  /* Roll backwards again, with the decimal as stop point, trimming contiguous zeroes */
336
0
  while( ptr >= decimal_ptr )
337
0
  {
338
0
    ptr--;
339
0
    if ( *ptr == '0' )
340
0
      continue;
341
0
    else
342
0
      break;
343
0
  }
344
345
  /* Huh, we get anywhere. Must not have trimmed anything. */
346
0
  if ( ptr == s->str_end )
347
0
    return 0;
348
349
  /* If we stopped at the decimal, we want to null that out.
350
     It we stopped on a numeral, we want to preserve that, so push the
351
     pointer forward one space. */
352
0
  if ( *ptr != '.' )
353
0
    ptr++;
354
355
  /* Add null terminator re-set the end of the stringbuffer. */
356
0
  *ptr = '\0';
357
0
  dist = s->str_end - ptr;
358
0
  s->str_end = ptr;
359
0
  return dist;
360
0
}
361