From 2f4d1eb7208841c7008c22ae6fd61f386c255ef1 Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Sat, 19 May 2012 14:02:03 +0930 Subject: [PATCH] "strbuf.h" API for assembling fixed-sized string buffers --- Android.mk | 1 + Makefile.in | 1 + strbuf.c | 81 +++++++++++++++++++ strbuf.h | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 304 insertions(+) create mode 100644 strbuf.c create mode 100644 strbuf.h diff --git a/Android.mk b/Android.mk index be63a61e..39da102b 100644 --- a/Android.mk +++ b/Android.mk @@ -46,6 +46,7 @@ LOCAL_SRC_FILES:= \ serval-dna/dna.c \ serval-dna/log.c \ serval-dna/mkdir.c \ + serval-dna/strbuf.c \ serval-dna/gateway.c \ serval-dna/overlay.c \ serval-dna/overlay_broadcast.c \ diff --git a/Makefile.in b/Makefile.in index 2746e8b4..bd1ec92b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -7,6 +7,7 @@ SRCS= main.c \ dna.c \ log.c \ mkdir.c \ + strbuf.c \ dna_identity.c \ encode.c \ fifo.c \ diff --git a/strbuf.c b/strbuf.c new file mode 100644 index 00000000..4f6da9fa --- /dev/null +++ b/strbuf.c @@ -0,0 +1,81 @@ +/* +Serval string buffer primitives +Copyright (C) 2012 The Serval Project + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "strbuf.h" + +void strbuf_init(strbuf *sb, char *buffer, size_t size) +{ + sb->start = sb->current = buffer; + sb->end = sb->start + size - 1; + if (sb->start && sb->end >= sb->start) { + *sb->start = '\0'; + *sb->end = '\0'; + } +} + +strbuf *strbuf_ncat(strbuf *sb, const char *text, size_t len) +{ + if (sb->start && sb->current < sb->end) { + size_t n = sb->end - sb->current; + strncpy(sb->current, text, len < n ? len : n); + } + sb->current += len; + return sb; +} + +int strbuf_sprintf(strbuf *sb, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int n = strbuf_vsprintf(sb, fmt, ap); + va_end(ap); + return n; +} + +int strbuf_vsprintf(strbuf *sb, const char *fmt, va_list ap) +{ + va_list ap2; + va_copy(ap2, ap); + int n; + if (sb->start && sb->current < sb->end) { + n = vsnprintf(sb->start, sb->end - sb->current, fmt, ap2); + } else { + char tmp[1]; + n = vsnprintf(tmp, sizeof tmp, fmt, ap2); + } + if (n != -1) + sb->current += n; + va_end(ap2); + return n; +} + +char *strbuf_substr(const strbuf *sb, int offset) +{ + char *s; + if (offset < 0) { + s = sb->end + offset; + if (s < sb->start) + s = sb->start; + } else { + s = sb->start + offset; + if (s > sb->end) + s = sb->end; + } + return s; +} diff --git a/strbuf.h b/strbuf.h new file mode 100644 index 00000000..165a7cb0 --- /dev/null +++ b/strbuf.h @@ -0,0 +1,221 @@ +/* +Serval string buffer primitives +Copyright (C) 2012 The Serval Project + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __STRBUF_H__ +#define __STRBUF_H__ + +/* + A strbuf provides a convenient set of primitives for assembling a + null-terminated string in a fixed-size, caller-provided backing buffer, + using a sequence of append operations. + + An append operation that would overflow the buffer is truncated, and the + result null-terminated. Once a truncation has occurred, the "overrun" + property of the strbuf is true until the next strbuf_init(), and all + subsequent appends will be fully truncated, ie, nothing more will be + appended to the buffer. + + The string in the buffer is guaranteed to always be nul terminated, which + means that the maximum strlen() of the assembled string is one less than + the buffer size. In other words, the following invariants always hold: + strbuf_len(sb) < strbuf_size(sb) + strbuf_str(sb)[strbuf_len(sb)] == '\0' + + char buf[100]; + strbuf b; + strbuf_init(&b, buf, sizeof buf); + strbuf_cat(&b, "text"); + strbuf_sprintf(&b, "fmt", val...); + if (strbuf_overflow(&b)) + // error... + else + // use buf + + A strbuf counts the total number of chars appended to it, even ones that + were truncated. This count is always available via strbuf_count(). + + A NULL buffer can be provided. This causes the strbuf operations to + perform all character counting and truncation calculations as usual, but + not assemble the string. This allows a strbuf to be used for calculating + the size needed for a buffer, which the caller may then allocate and replay + the same operations to fill. + +*/ + +#include +#include +#include +#include + +typedef struct strbuf { + char *start; + char *end; + char *current; +} strbuf; + +/** Initialise the strbuf backing buffer. The current backing buffer and its + * contents are forgotten, and all strbuf operations henceforward will operate + * on the new backing buffer. + * + * Immediately following strbuf_init(sb,b,n), the following properties hold: + * strbuf_str(sb) == b + * strbuf_size(sb) == n + * strbuf_len(sb) == 0 + * strbuf_count(sb) == 0 + * b == NULL || b[0] == '\0' + * + * If the 'buffer' argument is NULL, the strbuf operations will all act as + * usual with the sole exception that no chars will be copied into a backing + * buffer. This allows strbuf to be used for summing the lengths of strings. + * + * If the 'size' argument is zero, the result is undefined. A segmentation + * violation could occur, or the calling process may be aborted. Output may + * be written to fd 2 (standard error), or the process may hang in a tight + * loop. Cats may speak in tongues and the sky may fall. If the caller wishes + * to avoid these things, should always ensure that 'size' is nonzero. + * + * @author Andrew Bettison + */ +void strbuf_init(strbuf *sb, char *buffer, size_t size); + +/** Append a given number of characters to the strbuf, truncating if necessary to + * avoid buffer overrun. Return a pointer to the strbuf so that concatenations + * can be chained in a single line: strbuf_ncat(strbuf_ncat(sb, "abc", 1), "bcd", 2); + * + * After these operations: + * n = strbuf_len(sb); + * c = strbuf_count(sb); + * strbuf_ncat(text, len); + * the following invariants hold: + * strbuf_count(sb) == c + len + * strbuf_len(sb) >= n + * strbuf_len(sb) <= n + len + * strbuf_str(sb) == NULL || strbuf_len(sb) == n || strncmp(strbuf_str(sb) + n, text, strbuf_len(sb) - n) == 0 + * + * @author Andrew Bettison + */ +strbuf *strbuf_ncat(strbuf *sb, const char *text, size_t len); + + +/** Append a null-terminated string to the strbuf, truncating if necessary to + * avoid buffer overrun. Return a pointer to the strbuf so that concatenations + * can be chained in a single line: strbuf_cat(strbuf_cat(sb, "a"), "b"); + * + * After these operations: + * n = strbuf_len(sb); + * c = strbuf_count(sb); + * strbuf_cat(text); + * the following invariants hold: + * strbuf_count(sb) == c + strlen(text) + * strbuf_len(sb) >= n + * strbuf_len(sb) <= n + strlen(text) + * strbuf_str(sb) == NULL || strbuf_len(sb) == n || strncmp(strbuf_str(sb) + n, text, strbuf_len(sb) - n) == 0 + * + * @author Andrew Bettison + */ +inline strbuf *strbuf_cat(strbuf *sb, const char *text) { + return strbuf_ncat(sb, text, strlen(text)); +} + + +/** Append the results of sprintf(fmt,...) to the string buffer, truncating if + * necessary to avoid buffer overrun. Return sprintf()'s return value. + * + * This is equivalent to char tmp[...]; sprintf(tmp, fmt, ...); strbuf_cat(tmp); + * assuming that tmp[] is large enough to contain the entire string produced by + * the sprintf(). + * + * @author Andrew Bettison + */ +int strbuf_sprintf(strbuf *sb, const char *fmt, ...); +int strbuf_vsprintf(strbuf *sb, const char *fmt, va_list ap); + +/** Return a pointer to the current null-terminated string in the strbuf. + * + * This is the same as the 'buffer' argument passed to the most recent + * strbuf_init(). If the caller still has that pointer, then can safely use it + * instead of calling strbuf_str(). + * + * @author Andrew Bettison + */ +inline char *strbuf_str(const strbuf *sb) { + return sb->start; +} + + +/** Return a pointer to the substring starting at a given offset. If the + * offset is negative, then it is taken from the end of the string, ie, the + * length of the string is added to it. The returned pointer always points + * within the string. If offset >= strbuf_len(sb), it points to the + * terminating nul. If offset <= -strbuf_len(sb) then it points to + * strbuf_str(sb). + * + * @author Andrew Bettison + */ +char *strbuf_substr(const strbuf *sb, int offset); + + +/** Return the size of the backing buffer. + * + * This is the same as the 'size' argument passed to the most recent + * strbuf_init(). + * + * @author Andrew Bettison + */ +inline size_t strbuf_size(const strbuf *sb) { + return sb->end - sb->start + 1; +} + + +/** Return length of current string in the strbuf, not counting the terminating + * nul. + * + * Invariant: strbuf_len(sb) == strlen(strbuf_str(sb)) + * + * @author Andrew Bettison + */ +inline size_t strbuf_len(const strbuf *sb) { + return (sb->current < sb->end ? sb->current : sb->end) - sb->start; +} + + +/** Return the number of chars appended to the strbuf so far, not counting the + * terminating nul. + * + * Invariant: strbuf_len(sb) <= strbuf_count(sb) + * + * @author Andrew Bettison + */ +inline size_t strbuf_count(const strbuf *sb) { + return sb->current - sb->start; +} + + +/** Return true iff the strbuf has been overrun, ie, any appended string has + * been truncated since strbuf_init(). + * + * Invariant: strbuf_overrun(sb) == strbuf_count(sb) != strbuf_len(sb) + * + * @author Andrew Bettison + */ +inline int strbuf_is_overrun(const strbuf *sb) { + return sb->current > sb->end; +} + +#endif // __STRBUF_H__