mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-18 02:39:44 +00:00
"strbuf.h" API for assembling fixed-sized string buffers
This commit is contained in:
parent
0f65028a0b
commit
2f4d1eb720
@ -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 \
|
||||
|
@ -7,6 +7,7 @@ SRCS= main.c \
|
||||
dna.c \
|
||||
log.c \
|
||||
mkdir.c \
|
||||
strbuf.c \
|
||||
dna_identity.c \
|
||||
encode.c \
|
||||
fifo.c \
|
||||
|
81
strbuf.c
Normal file
81
strbuf.c
Normal file
@ -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;
|
||||
}
|
221
strbuf.h
Normal file
221
strbuf.h
Normal file
@ -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 <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
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 <andrew@servalproject.com>
|
||||
*/
|
||||
inline int strbuf_is_overrun(const strbuf *sb) {
|
||||
return sb->current > sb->end;
|
||||
}
|
||||
|
||||
#endif // __STRBUF_H__
|
Loading…
Reference in New Issue
Block a user