Code

proto: Renamed sdb_proto_get_int to sdb_proto_unmarshal_int.
[sysdb.git] / src / utils / strbuf.c
1 /*
2  * SysDB - src/utils/strbuf.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #include "sysdb.h"
29 #include "utils/strbuf.h"
31 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <string.h>
38 #include <unistd.h>
40 /* free memory if most of the buffer is unused */
41 #define CHECK_SHRINK(buf) \
42         do { \
43                 if (((buf)->pos < (buf)->size / 3) \
44                                 && (2 * (buf)->pos > (buf)->min_size)) \
45                         /* don't free all memory to avoid churn */ \
46                         strbuf_resize((buf), 2 * (buf)->pos); \
47         } while (0)
49 /*
50  * private data structures
51  */
53 struct sdb_strbuf {
54         char  *string;
55         size_t size;
56         size_t pos;
58         /* min size to shrink the buffer to */
59         size_t min_size;
60 };
62 /*
63  * private helper functions
64  */
66 static int
67 strbuf_resize(sdb_strbuf_t *buf, size_t new_size)
68 {
69         size_t tmp_size;
70         char *tmp;
72         if (new_size <= buf->pos)
73                 return -1;
75         tmp_size = SDB_MAX(buf->size, buf->min_size);
76         if (! tmp_size)
77                 tmp_size = 64;
78         while (tmp_size < new_size)
79                 tmp_size *= 2;
81         tmp = realloc(buf->string, new_size);
82         if (! tmp)
83                 return -1;
85         if (new_size)
86                 buf->string = tmp;
87         else
88                 buf->string = NULL;
89         buf->size = new_size;
90         return 0;
91 } /* strbuf_resize */
93 /*
94  * public API
95  */
97 sdb_strbuf_t *
98 sdb_strbuf_create(size_t size)
99 {
100         sdb_strbuf_t *buf;
102         buf = calloc(1, sizeof(*buf));
103         if (! buf)
104                 return NULL;
106         buf->string = NULL;
107         if (size) {
108                 buf->string = malloc(size);
109                 if (! buf->string) {
110                         free(buf);
111                         return NULL;
112                 }
114                 buf->string[0] = '\0';
115                 buf->min_size = size;
116         }
117         else
118                 buf->min_size = 64;
120         buf->size = size;
121         buf->pos  = 0;
123         return buf;
124 } /* sdb_strbuf_create */
126 void
127 sdb_strbuf_destroy(sdb_strbuf_t *buf)
129         if (! buf)
130                 return;
132         if (buf->string)
133                 free(buf->string);
134         free(buf);
135 } /* sdb_strbuf_destroy */
137 ssize_t
138 sdb_strbuf_vappend(sdb_strbuf_t *buf, const char *fmt, va_list ap)
140         va_list aq;
141         int status;
143         if ((! buf) || (! fmt))
144                 return -1;
146         assert((buf->size == 0) || (buf->string[buf->pos] == '\0'));
148         if (! buf->size) {
149                 /* use some arbitrary but somewhat reasonable default */
150                 if (strbuf_resize(buf, 64))
151                         return -1;
152         }
153         /* make sure to reserve space for the nul-byte */
154         else if (buf->pos >= buf->size - 1)
155                 if (strbuf_resize(buf, 2 * buf->size))
156                         return -1;
158         assert(buf->size && buf->string);
159         assert(buf->pos < buf->size);
161         /* 'ap' is invalid after calling vsnprintf; thus copy before using it */
162         va_copy(aq, ap);
163         status = vsnprintf(buf->string + buf->pos,
164                         buf->size - buf->pos, fmt, ap);
166         if (status < 0) {
167                 va_end(aq);
168                 return status;
169         }
171         /* 'status' does not include nul-byte */
172         if ((size_t)status >= buf->size - buf->pos) {
173                 if (strbuf_resize(buf, buf->pos + (size_t)status + 1)) {
174                         va_end(aq);
175                         return -1;
176                 }
178                 /* reset string and try again */
179                 buf->string[buf->pos] = '\0';
180                 status = (int)sdb_strbuf_vappend(buf, fmt, aq);
181         }
182         else
183                 buf->pos += (size_t)status;
185         va_end(aq);
187         /* even though this function always appends to the existing buffer, the
188          * size might have previously been reset */
189         CHECK_SHRINK(buf);
191         return (ssize_t)status;
192 } /* sdb_strbuf_vappend */
194 ssize_t
195 sdb_strbuf_append(sdb_strbuf_t *buf, const char *fmt, ...)
197         va_list ap;
198         ssize_t status;
200         va_start(ap, fmt);
201         status = sdb_strbuf_vappend(buf, fmt, ap);
202         va_end(ap);
204         return status;
205 } /* sdb_strbuf_append */
207 ssize_t
208 sdb_strbuf_vsprintf(sdb_strbuf_t *buf, const char *fmt, va_list ap)
210         if (! buf)
211                 return -1;
213         if (buf->size) {
214                 buf->string[0] = '\0';
215                 buf->pos = 0;
216         }
218         return sdb_strbuf_vappend(buf, fmt, ap);
219 } /* sdb_strbuf_vsprintf */
221 ssize_t
222 sdb_strbuf_sprintf(sdb_strbuf_t *buf, const char *fmt, ...)
224         va_list ap;
225         ssize_t status;
227         va_start(ap, fmt);
228         status = sdb_strbuf_vsprintf(buf, fmt, ap);
229         va_end(ap);
231         return status;
232 } /* sdb_strbuf_sprintf */
234 ssize_t
235 sdb_strbuf_memappend(sdb_strbuf_t *buf, const void *data, size_t n)
237         if ((! buf) || (! data))
238                 return -1;
240         assert((buf->size == 0) || (buf->string[buf->pos] == '\0'));
242         if (buf->pos + n + 1 > buf->size) {
243                 if (strbuf_resize(buf, buf->pos + n + 1))
244                         return -1;
245         }
247         assert(buf->size && buf->string);
248         assert(buf->pos < buf->size);
250         memcpy((void *)(buf->string + buf->pos), data, n);
251         buf->pos += n;
252         buf->string[buf->pos] = '\0';
254         /* even though this function always appends to the existing buffer, the
255          * size might have previously been reset */
256         CHECK_SHRINK(buf);
258         return (ssize_t)n;
259 } /* sdb_strbuf_memappend */
261 ssize_t
262 sdb_strbuf_memcpy(sdb_strbuf_t *buf, const void *data, size_t n)
264         if ((! buf) || (! data))
265                 return -1;
267         if (buf->size) {
268                 buf->string[0] = '\0';
269                 buf->pos = 0;
270         }
272         return sdb_strbuf_memappend(buf, data, n);
273 } /* sdb_strbuf_memcpy */
275 ssize_t
276 sdb_strbuf_read(sdb_strbuf_t *buf, int fd, size_t n)
278         ssize_t ret;
280         if (! buf)
281                 return -1;
283         if (buf->pos + n + 1 >= buf->size)
284                 if (strbuf_resize(buf, buf->pos + n + 1))
285                         return -1;
287         ret = read(fd, buf->string + buf->pos, n);
288         if (ret > 0)
289                 buf->pos += (size_t)ret;
290         return ret;
291 } /* sdb_strbuf_read */
293 ssize_t
294 sdb_strbuf_chomp(sdb_strbuf_t *buf)
296         ssize_t ret = 0;
298         if (! buf)
299                 return -1;
301         assert((!buf->size) || (buf->pos < buf->size));
302         assert(buf->pos <= buf->size);
304         while ((buf->pos > 0)
305                         && (buf->string[buf->pos - 1] == '\n')) {
306                 --buf->pos;
307                 buf->string[buf->pos] = '\0';
308                 ++ret;
309         }
311         return ret;
312 } /* sdb_strbuf_chomp */
314 void
315 sdb_strbuf_skip(sdb_strbuf_t *buf, size_t offset, size_t n)
317         char *start;
318         size_t len;
320         if ((! buf) || (! n))
321                 return;
323         if (offset >= buf->pos)
324                 return;
326         len = buf->pos - offset;
328         if (n >= len) {
329                 buf->string[offset] = '\0';
330                 buf->pos = offset;
331                 return;
332         }
334         assert(offset + n < buf->pos);
335         assert(offset < buf->pos);
337         start = buf->string + offset;
338         memmove(start, start + n, len - n);
339         buf->pos -= n;
340         buf->string[buf->pos] = '\0';
342         /* don't resize now but wait for the next write to avoid churn */
343 } /* sdb_strbuf_skip */
345 void
346 sdb_strbuf_clear(sdb_strbuf_t *buf)
348         if ((! buf) || (! buf->size))
349                 return;
351         buf->string[0] = '\0';
352         buf->pos = 0;
354         /* don't resize now but wait for the next write to avoid churn */
355 } /* sdb_strbuf_clear */
357 const char *
358 sdb_strbuf_string(sdb_strbuf_t *buf)
360         if (! buf)
361                 return NULL;
362         if (! buf->size)
363                 return "";
364         return buf->string;
365 } /* sdb_strbuf_string */
367 size_t
368 sdb_strbuf_len(sdb_strbuf_t *buf)
370         if (! buf)
371                 return 0;
372         return buf->pos;
373 } /* sdb_strbuf_string */
375 size_t
376 sdb_strbuf_cap(sdb_strbuf_t *buf)
378         if (! buf)
379                 return 0;
380         return buf->size;
381 } /* sdb_strbuf_cap */
383 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */