Vstring is a simple string building API for the C programming language. The API does not make any thread-safety guarantees; sharing vstrings between threads requires some form of synchronization. (At some point, I may make a concurrent string API, but that's neither here nor there.) Vstring supports static and dynamic buffers (static buffers are upgraded to dynamic buffers as needed), and can efficiently parse signed and unsigned integers.
The API is intended to promote safety in string manipulation as well as to
provide a means to avoid hugely expensive printf(3)-family calls for
common operations.
A vstring type is defined by the API; this type contains all necessary
information / metadata for modifying the underlying buffer. The type is not
opaque (because opaque types suck), but it is not recommended to play with
the type outside of the API. (If you find the need to do this, fix / extend
the API and send a pull request!)
Simply #include <vstring.h>. If you use vs_pushdouble, you may need to
link in the system's math library if that is not part of libc.
The vstring type is defined as follows:
typedef struct vstring {
	char		*contents;
	uint32_t	type;
	uint32_t	flags;
	uint64_t	pointer;
	uint64_t	size;
} vstring;The contents pointer represents the underlying buffer.
The type member is a bitmap containing information about the type of the
vstring instance.
The flags member is a bitmap containing metadata about the vstring
instance. These flags are intended for API internal use and should not be
relied upon by consumers of the API.
The pointer member is used as an offset into the contents buffer. It
points to the end of the string + 1 byte.
The size member contains the total capacity of the contents buffer.
Three sorts of vstrings exist:
- 
Static strings ( VS_TYPE_STATIC): these strings are backed by a static buffer and cannot grow.
- 
Growable static strings ( VS_TYPE_GROWABLE): these strings are backed by a static buffer, but may be upgraded to dynamic strings if an append operation would cause an overflow.
- 
Dynamic strings ( VS_TYPE_DYNAMIC): these strings are backed by a dynamically allocated buffer and may grow if an append option would cause an overflow.
typedef struct vstring_malloc {
	void		*(*vs_malloc)(size_t);
	void		*(*vs_realloc)(void *, size_t);
	void		(*vs_free)(void *);
} vstring_malloc;The vstring_malloc type provides a means for using a custom memory
allocator that may not be accessible through the malloc(3) API linked into
the program.
static inline vstring *
vs_init(vstring *vs, vstring_malloc *vm, enum vstring_type type, char *buf,
    size_t size)A vstring is initialized by calling vs_init with the proper arguments.
If the first argument is NULL, the vstring itself will be dynamically
allocated. It is legal to pass a pointer to a statically allocated
vstring of type VS_TYPE_DYNAMIC.
The vstring_malloc argument, if non-NULL, provides pointers to a
set of malloc(3), realloc(3), and free(3)-compatible functions that are
used to allocate the vstring (if needed) and its underlying buffer. If this
argument is NULL, the library uses calloc(3), realloc(3), and free(3)
directly.
The buf and size arguments are useful when vs_init is called to
initialize a VS_TYPE_STATIC or VS_TYPE_GROWABLE vstring, but also
allow passing in an externally allocated buffer with a known size into a
vstring of type VS_TYPE_DYNAMIC. Note that such a buffer must have
been allocated with the same malloc(3) available to vstring (and must
therefore be the same allocator passed through vstring_malloc, if any).
static inline void
vs_deinit(vstring *vs)The vs_deinit function destroys the vstring passed into it. If the
vstring is of type VS_TYPE_DYNAMIC, its contents will be freed. If the
vstring passed to vs_deinit was dynamically allocated, it will be freed
as well. In all cases, sizeof (*vs) bytes will be zeroed at the address
pointed to by the argument to vs_deinit.
static inline void
vs_rewind(vstring *vs)To avoid constant allocation of vstrings and their contents, it may be
useful to reuse vstring objects. Calling vs_rewind resets the pointer
of the string. Future calls to operations modifying the underlying buffer
will then start at the beginning of the buffer.
static inline void *
vs_resize(vstring *vs, size_t hint)Don't worry about it. This is done for you.
static inline bool
vs_push(vstring *vs, char c)The vs_push function appends an individual character c into the buffer
managed by *vs. Returns true if successful, false otherwise.
This function isn't really intended to be used outside the API. If you are using this function in a loop, you are almost certainly doing it wrong.
This function may fail if:
- The buffer is VS_TYPE_STATICand the append would overflow the buffer.
- The buffer is not large enough to hold lenbytes and resizing failed.
static inline bool
vs_pushstr(vstring *vs, const char *s, uint64_t len)The vs_pushstr function appends len characters pointed to by s into
the buffer managed by *vs. Returns true if successful, false
otherwise.
This function may fail if:
- The value of lenis 0.
- The value of sis NULL.
- The buffer is VS_TYPE_STATICand the append would overflow the buffer.
- The buffer is not large enough to hold lenbytes and resizing failed.
static inline bool
vs_pushuint(vstring *vs, uint64_t n)
static inline bool
vs_pushint(vstring *vs, int64_t n)The vs_pushuint and vs_pushint functions append a string representation
of the integer n to the buffer pointed to by *vs. Returns true if
successful, false otherwise.
This can function fail for the same reasons as vs_push and vs_pushstr.
static inline bool
vs_pushdouble(vstring *vs, double n)Stringifies the representation of n and appends that to the buffer pointed
to by *vs. It works by using modf(3) to retrieve the whole integer part
and the fractional part. The fractional part is padded to 9 spaces for no good
reason. NAN, positive and negative infinity, and negative zero is handled.
Returns true if successful, false otherwise.
This function fails when any concatenation fails, or if the floating point number passed in is a subnormal FP number.
static inline bool
vs_finalize(vstring *vs)To make the buffer managed by *vs a valid C string, vs_finalize must be
called. This function is a convenience wrapper around vs_push(vs, '\0')
and can fail for the same reasons.
To get the length of the resulting C string without calling strlen(3), you
may use vs_len(vs) - 1.
static inline char *
vs_contents(vstring *vs)The buffer returned by vs_contents is not safe to use as a C string until
vs_finalize was successfully called.
static inline uint64_t
vs_len(vstring *vs)The length of the vstring-managed buffer can always be determined by a
call to vs_len.
vs_pushdouble probably isn't correct. We probably want to change the API to
allow expression of the number of significant figures wanted. We may also want
to specify minimum or maximum padding values (with no padding being an option
for maximum value).
If you have additional suggestions, file an issue or send a pull request!