/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 | | |