gems-kernel/source/THIRDPARTY/xnu/bsd/kern/subr_sbuf.c
2024-06-03 11:29:39 -05:00

1797 lines
38 KiB
C

/*
* Copyright (c) 2020 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#include <kern/kalloc.h>
#include <libkern/libkern.h>
#include <os/base.h>
#include <os/overflow.h>
#include <sys/param.h>
#include <sys/sbuf.h>
#include <sys/uio.h>
#if DEBUG || DEVELOPMENT
#include <kern/macro_help.h>
#include <sys/errno.h>
#include <sys/sysctl.h>
#endif /* DEBUG || DEVELOPMENT */
#define SBUF_ISSET(s, f) ((s)->s_flags & (f))
#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0)
#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
#define SBUF_CANEXTEND(s) SBUF_ISSET(s, SBUF_AUTOEXTEND)
#define SBUF_HASOVERFLOWED(s) SBUF_ISSET(s, SBUF_OVERFLOWED)
#define SBUF_ISDYNAMIC(s) SBUF_ISSET(s, SBUF_DYNAMIC)
#define SBUF_ISDYNSTRUCT(s) SBUF_ISSET(s, SBUF_DYNSTRUCT)
#define SBUF_ISFINISHED(s) SBUF_ISSET(s, SBUF_FINISHED)
#define SBUF_MINEXTENDSIZE 16
#define SBUF_MAXEXTENDSIZE PAGE_SIZE
#define SBUF_MAXEXTENDINCR PAGE_SIZE
/*!
* @function sbuf_delete
*
* @brief
* Destroys an sbuf. Frees the underlying buffer if it's allocated on the heap
* (indicated by SBUF_ISDYNAMIC) and frees the sbuf if it itself is allocated
* on the heap (SBUF_ISDYNSTRUCT).
*
* @param s
* The sbuf to destroy.
*/
void
sbuf_delete(struct sbuf *s)
{
if (SBUF_ISDYNAMIC(s) && s->s_buf) {
kfree_data(s->s_buf, s->s_size);
s->s_buf = NULL;
}
if (SBUF_ISDYNSTRUCT(s)) {
kfree_type(struct sbuf, s);
}
}
/*!
* @function sbuf_extendsize
*
* @brief
* Attempts to extend the size of an sbuf to the value pointed to by size.
*
* @param size
* Points to a size_t containing the desired size for input and receives the
* actual new size on success (which will be greater than or equal to the
* requested size).
*
* @returns
* 0 on success, -1 on failure.
*/
static int
sbuf_extendsize(size_t *size)
{
size_t target_size = *size;
size_t new_size;
if (target_size > INT_MAX) {
return -1;
}
if (target_size < SBUF_MAXEXTENDSIZE) {
new_size = SBUF_MINEXTENDSIZE;
while (new_size < target_size) {
new_size *= 2;
}
} else {
/* round up to nearest page: */
new_size = (target_size + PAGE_SIZE - 1) & ~PAGE_MASK;
}
if (new_size > INT_MAX) {
return -1;
}
*size = new_size;
return 0;
}
/*!
* @function sbuf_new
*
* @brief
* Allocates and/or initializes an sbuf.
*
* @param s
* An optional existing sbuf to initialize. If NULL, a new one is allocated on
* the heap.
*
* @param buf
* An optional existing backing buffer to assign to the sbuf. If NULL, a new
* one is allocated on the heap.
*
* @param length_
* The initial size of the sbuf. The actual size may be greater than this
* value.
*
* @param flags
* The flags to set on the sbuf. Accepted values are:
*
* - SBUF_FIXEDLEN: Do not allow the backing buffer to dynamically expand
* to accommodate appended data.
* - SBUF_AUTOEXPAND: Automatically reallocate the backing buffer using the
* heap if required.
*
* @returns
* The new and/or initialized sbuf on success, or NULL on failure.
*/
struct sbuf *
sbuf_new(struct sbuf *s, char *buf, int length_, int flags)
{
size_t length = (size_t)length_;
if (length > INT_MAX || flags & ~SBUF_USRFLAGMSK) {
return NULL;
}
if (s == NULL) {
s = (struct sbuf *)kalloc_type(struct sbuf, Z_WAITOK | Z_ZERO);
if (NULL == s) {
return NULL;
}
s->s_flags = flags;
SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
} else {
bzero(s, sizeof(*s));
s->s_flags = flags;
}
if (buf) {
s->s_size = (int)length;
s->s_buf = buf;
return s;
}
if (SBUF_CANEXTEND(s) && (-1 == sbuf_extendsize(&length))) {
goto fail;
}
/*
* we always need at least 1 byte for \0, so s_size of 0 will cause an
* underflow in sbuf_capacity.
*/
if (length == 0) {
goto fail;
}
s->s_buf = kalloc_data(length, Z_WAITOK | Z_ZERO);
if (NULL == s->s_buf) {
goto fail;
}
s->s_size = (int)length;
SBUF_SETFLAG(s, SBUF_DYNAMIC);
return s;
fail:
sbuf_delete(s);
return NULL;
}
/*!
* @function sbuf_setpos
*
* @brief
* Set the current position of the sbuf.
*
* @param s
* The sbuf to modify.
*
* @param pos
* The new position to set. Must be less than or equal to the current position.
*
* @returns
* 0 on success, -1 on failure.
*/
int
sbuf_setpos(struct sbuf *s, int pos)
{
if (pos < 0 || pos > s->s_len) {
return -1;
}
s->s_len = pos;
return 0;
}
/*!
* @function sbuf_clear
*
* @brief
* Resets the position/length of the sbuf data to zero and clears the finished
* and overflow flags.
*
* @param s
* The sbuf to clear.
*/
void
sbuf_clear(struct sbuf *s)
{
SBUF_CLEARFLAG(s, SBUF_FINISHED);
SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
sbuf_setpos(s, 0);
}
/*!
* @function sbuf_extend
*
* @brief
* Attempt to extend the size of an sbuf's backing buffer by @a addlen bytes.
*
* @param s
* The sbuf to extend.
*
* @param addlen
* How many bytes to increase the size by.
*
* @returns
* 0 on success, -1 on failure.
*/
static int OS_WARN_RESULT
sbuf_extend(struct sbuf *s, size_t addlen)
{
char *new_buf;
size_t new_size;
if (addlen == 0) {
return 0;
}
if (!SBUF_CANEXTEND(s)) {
return -1;
}
if (os_add_overflow((size_t)s->s_size, addlen, &new_size)) {
return -1;
}
if (-1 == sbuf_extendsize(&new_size)) {
return -1;
}
new_buf = (char *) kalloc_data(new_size, Z_WAITOK);
if (NULL == new_buf) {
return -1;
}
bcopy(s->s_buf, new_buf, (size_t)s->s_size);
if (SBUF_ISDYNAMIC(s)) {
kfree_data(s->s_buf, (size_t)s->s_size);
} else {
SBUF_SETFLAG(s, SBUF_DYNAMIC);
}
s->s_buf = new_buf;
s->s_size = (int)new_size;
return 0;
}
/*!
* @function sbuf_capacity
*
* @brief
* Get the current capacity of an sbuf: how many more bytes we can append given
* the current size and position.
*
* @param s
* The sbuf to get the capacity of.
*
* @returns
* The current sbuf capacity.
*/
static size_t
sbuf_capacity(const struct sbuf *s)
{
/* 1 byte reserved for \0: */
return (size_t)(s->s_size - s->s_len - 1);
}
/*!
* @function sbuf_ensure_capacity
*
* @brief
* Ensure that an sbuf can accommodate @a add_len bytes, reallocating the
* backing buffer if necessary.
*
* @param s
* The sbuf.
*
* @param wanted
* The minimum capacity to ensure @a s has.
*
* @returns
* 0 if the minimum capacity is met by @a s, or -1 on error.
*/
static int
sbuf_ensure_capacity(struct sbuf *s, size_t wanted)
{
size_t size;
size = sbuf_capacity(s);
if (size >= wanted) {
return 0;
}
return sbuf_extend(s, wanted - size);
}
/*!
* @function sbuf_bcat
*
* @brief
* Append data to an sbuf.
*
* @param s
* The sbuf.
*
* @param data
* The data to append.
*
* @param len
* The length of the data.
*
* @returns
* 0 on success, -1 on failure. Will always fail if the sbuf is marked as
* overflowed.
*/
int
sbuf_bcat(struct sbuf *s, const void *data, size_t len)
{
if (SBUF_HASOVERFLOWED(s)) {
return -1;
}
if (-1 == sbuf_ensure_capacity(s, len)) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return -1;
}
bcopy(data, s->s_buf + s->s_len, len);
s->s_len += (int)len; /* safe */
return 0;
}
/*!
* @function sbuf_bcpy
*
* @brief
* Set the entire sbuf data, possibly reallocating the backing buffer to
* accommodate.
*
* @param s
* The sbuf.
*
* @param data
* The data to set.
*
* @param len
* The length of the data to set.
*
* @returns
* 0 on success or -1 on failure. Will clear the finished/overflowed flags.
*/
int
sbuf_bcpy(struct sbuf *s, const void *data, size_t len)
{
sbuf_clear(s);
return sbuf_bcat(s, data, len);
}
/*!
* @function sbuf_cat
*
* @brief
* Append a string to an sbuf, possibly expanding the backing buffer to
* accommodate.
*
* @param s
* The sbuf.
*
* @param str
* The string to append.
*
* @returns
* 0 on success, -1 on failure. Always fails if the sbuf is marked as
* overflowed.
*/
int
sbuf_cat(struct sbuf *s, const char *str)
{
return sbuf_bcat(s, str, strlen(str));
}
/*!
* @function sbuf_cpy
*
* @brief
* Set the entire sbuf data to the given nul-terminated string, possibly
* expanding the backing buffer to accommodate it if necessary.
*
* @param s
* The sbuf.
*
* @param str
* The string to set the sbuf data to.
*
* @returns
* 0 on success, -1 on failure. Clears and resets the sbuf first.
*/
int
sbuf_cpy(struct sbuf *s, const char *str)
{
sbuf_clear(s);
return sbuf_cat(s, str);
}
/*!
* @function sbuf_vprintf
*
* @brief
* Formatted-print into an sbuf using a va_list.
*
* @param s
* The sbuf.
*
* @param fmt
* The format string.
*
* @param ap
* The format string argument data.
*
* @returns
* 0 on success, -1 on failure. Always fails if the sbuf is marked as
* overflowed.
*/
int
sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
{
va_list ap_copy;
int result;
size_t capacity;
size_t len;
if (SBUF_HASOVERFLOWED(s)) {
return -1;
}
do {
capacity = sbuf_capacity(s);
va_copy(ap_copy, ap);
/* +1 for \0. safe because we already accommodate this. */
result = vsnprintf(&s->s_buf[s->s_len], capacity + 1, fmt, ap_copy);
va_end(ap_copy);
if (result < 0) {
return -1;
}
len = (size_t)result;
if (len <= capacity) {
s->s_len += (int)len;
return 0;
}
} while (-1 != sbuf_ensure_capacity(s, len));
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return -1;
}
/*!
* @function sbuf_printf
*
* @brief
* Formatted-print into an sbuf using variadic arguments.
*
* @param s
* The sbuf.
*
* @param fmt
* The format string.
*
* @returns
* 0 on success, -1 on failure. Always fails if the sbuf is marked as
* overflowed.
*/
int
sbuf_printf(struct sbuf *s, const char *fmt, ...)
{
va_list ap;
int result;
va_start(ap, fmt);
result = sbuf_vprintf(s, fmt, ap);
va_end(ap);
return result;
}
/*!
* @function sbuf_putc
*
* @brief
* Append a single character to an sbuf. Ignores '\0'.
*
* @param s
* The sbuf.
*
* @param c_
* The character to append.
*
* @returns
* 0 on success, -1 on failure. This function will always fail if the sbuf is
* marked as overflowed.
*/
int
sbuf_putc(struct sbuf *s, int c_)
{
char c = (char)c_;
if (SBUF_HASOVERFLOWED(s)) {
return -1;
}
if (-1 == sbuf_ensure_capacity(s, 1)) {
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
return -1;
}
if (c != '\0') {
s->s_buf[s->s_len++] = c;
}
return 0;
}
static inline int
isspace(char ch)
{
return ch == ' ' || ch == '\n' || ch == '\t';
}
/*!
* @function sbuf_trim
*
* @brief
* Removes whitespace from the end of an sbuf.
*
* @param s
* The sbuf.
*
* @returns
* 0 on success or -1 if the sbuf is marked as overflowed.
*/
int
sbuf_trim(struct sbuf *s)
{
if (SBUF_HASOVERFLOWED(s)) {
return -1;
}
while (s->s_len > 0 && isspace(s->s_buf[s->s_len - 1])) {
--s->s_len;
}
return 0;
}
/*!
* @function sbuf_overflowed
*
* @brief
* Indicates whether the sbuf is marked as overflowed.
*
* @param s
* The sbuf.
*
* @returns
* 1 if the sbuf has overflowed or 0 otherwise.
*/
int
sbuf_overflowed(struct sbuf *s)
{
return !!SBUF_HASOVERFLOWED(s);
}
/*!
* @function sbuf_finish
*
* @brief
* Puts a trailing nul byte onto the sbuf data.
*
* @param s
* The sbuf.
*/
void
sbuf_finish(struct sbuf *s)
{
/* safe because we always reserve a byte at the end for \0: */
s->s_buf[s->s_len] = '\0';
SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
SBUF_SETFLAG(s, SBUF_FINISHED);
}
/*!
* @function sbuf_data
*
* @brief
* Gets a pointer to the sbuf backing data.
*
* @param s
* The sbuf.
*
* @returns
* A pointer to the sbuf data.
*/
char *
sbuf_data(struct sbuf *s)
{
return s->s_buf;
}
/*!
* @function sbuf_len
*
* @brief
* Retrieves the current length of the sbuf data.
*
* @param s
* The sbuf
*
* @returns
* The length of the sbuf data or -1 if the sbuf is marked as overflowed.
*/
int
sbuf_len(struct sbuf *s)
{
if (SBUF_HASOVERFLOWED(s)) {
return -1;
}
return s->s_len;
}
/*!
* @function sbuf_done
*
* @brief
* Tests if the sbuf is marked as finished.
*
* @param s
* The sbuf.
*
* @returns
* 1 if the sbuf is marked as finished or 0 if not.
*/
int
sbuf_done(struct sbuf *s)
{
return !!SBUF_ISFINISHED(s);
}
#if DEBUG || DEVELOPMENT
/*
* a = assertion string
*/
#define SBUF_FAIL(a) \
MACRO_BEGIN \
printf("sbuf_tests: failed assertion: %s\n", a); \
if (what != NULL && should != NULL) { \
printf("sbuf_tests: while testing: %s should %s\n", what, should); \
} \
goto fail; \
MACRO_END
#define SBUF_PASS \
++passed
/*
* x = expression
*/
#define SBUF_ASSERT(x) \
MACRO_BEGIN \
if (x) { \
SBUF_PASS; \
} else { \
SBUF_FAIL(#x); \
} \
MACRO_END
#define SBUF_ASSERT_NOT(x) \
SBUF_ASSERT(!(x))
/*
* e = expected
* a = actual
* c = comparator
*/
#define SBUF_ASSERT_CMP(e, a, c) \
MACRO_BEGIN \
if ((a) c (e)) { \
SBUF_PASS; \
} else { \
SBUF_FAIL(#a " " #c " " #e); \
} \
MACRO_END
#define SBUF_ASSERT_EQ(e, a) SBUF_ASSERT_CMP(e, a, ==)
#define SBUF_ASSERT_NE(e, a) SBUF_ASSERT_CMP(e, a, !=)
#define SBUF_ASSERT_GT(e, a) SBUF_ASSERT_CMP(e, a, >)
#define SBUF_ASSERT_GTE(e, a) SBUF_ASSERT_CMP(e, a, >=)
#define SBUF_ASSERT_LT(e, a) SBUF_ASSERT_CMP(e, a, <)
#define SBUF_ASSERT_LTE(e, a) SBUF_ASSERT_CMP(e, a, <=)
#define SBUF_TEST_BEGIN \
size_t passed = 0; \
const char *what = NULL; \
const char *should = NULL;
/*
* include the trailing semi-colons here intentionally to allow for block-like
* appearance:
*/
#define SBUF_TESTING(f) \
MACRO_BEGIN \
what = (f); \
MACRO_END;
#define SBUF_SHOULD(s) \
MACRO_BEGIN \
should = (s); \
MACRO_END;
#define SBUF_TEST_END \
printf("sbuf_tests: %zu assertions passed\n", passed); \
return 0; \
fail: \
return ENOTRECOVERABLE;
static int
sysctl_sbuf_tests SYSCTL_HANDLER_ARGS
{
#pragma unused(arg1, arg2)
int rval = 0;
char str[32] = { 'o', 'k', 0 };
rval = sysctl_handle_string(oidp, str, sizeof(str), req);
if (rval != 0 || req->newptr == 0 || req->newlen < 1) {
return rval;
}
SBUF_TEST_BEGIN;
SBUF_TESTING("sbuf_new")
{
SBUF_SHOULD("fail to allocate >INT_MAX")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, INT_MIN, 0);
SBUF_ASSERT_EQ(NULL, s);
}
SBUF_SHOULD("fail when claiming a backing buffer >INT_MAX")
{
struct sbuf *s = NULL;
char buf[4] = { 0 };
s = sbuf_new(NULL, buf, INT_MIN, 0);
SBUF_ASSERT_EQ(NULL, s);
}
SBUF_SHOULD("fail to allocate a zero-length sbuf")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 0, 0);
SBUF_ASSERT_EQ(NULL, s);
}
SBUF_SHOULD("not accept invalid flags")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0x10000);
SBUF_ASSERT_EQ(NULL, s);
}
SBUF_SHOULD("succeed when passed an existing sbuf")
{
struct sbuf *s = NULL;
struct sbuf existing;
memset(&existing, 0x41, sizeof(existing));
s = sbuf_new(&existing, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(&existing, s);
SBUF_ASSERT(SBUF_ISSET(s, SBUF_AUTOEXTEND));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_DYNAMIC));
SBUF_ASSERT_NE(NULL, s->s_buf);
SBUF_ASSERT_NE(0, s->s_size);
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("succeed when passed an existing sbuf and buffer")
{
struct sbuf *s = NULL;
struct sbuf existing;
char buf[4] = { 0 };
memset(&existing, 0x41, sizeof(existing));
s = sbuf_new(&existing, buf, sizeof(buf), 0);
SBUF_ASSERT_EQ(&existing, s);
SBUF_ASSERT_EQ(buf, s->s_buf);
SBUF_ASSERT_EQ(4, s->s_size);
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("succeed without an existing sbuf or buffer")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_NE(NULL, s);
SBUF_ASSERT(SBUF_ISSET(s, SBUF_DYNAMIC));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_DYNSTRUCT));
SBUF_ASSERT_NE(NULL, s->s_buf);
SBUF_ASSERT_NE(0, s->s_size);
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("succeed without an existing sbuf, but with a buffer")
{
struct sbuf *s = NULL;
char buf[4] = { 0 };
s = sbuf_new(NULL, buf, sizeof(buf), 0);
SBUF_ASSERT_NE(NULL, s);
SBUF_ASSERT(SBUF_ISSET(s, SBUF_DYNSTRUCT));
SBUF_ASSERT_EQ(buf, s->s_buf);
SBUF_ASSERT_EQ(4, s->s_size);
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("round up the requested size if SBUF_AUTOEXTEND")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 1, SBUF_AUTOEXTEND);
SBUF_ASSERT_GT(1, s->s_size);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_clear")
{
SBUF_SHOULD("clear the overflowed and finished flags")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
SBUF_SETFLAG(s, SBUF_FINISHED);
SBUF_ASSERT(SBUF_ISSET(s, SBUF_FINISHED));
sbuf_clear(s);
SBUF_ASSERT_NOT(SBUF_ISSET(s, SBUF_OVERFLOWED));
SBUF_ASSERT_NOT(SBUF_ISSET(s, SBUF_FINISHED));
sbuf_delete(s);
}
SBUF_SHOULD("reset the position to zero")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
s->s_len = 1;
sbuf_clear(s);
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_extend")
{
SBUF_SHOULD("allow zero")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_extend(s, 0));
SBUF_ASSERT_EQ(size_before, s->s_size);
sbuf_delete(s);
}
SBUF_SHOULD("fail for sbuf not marked as SBUF_AUTOEXTEND")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(-1, sbuf_extend(s, 10));
sbuf_delete(s);
}
SBUF_SHOULD("accommodate reasonable requests")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_extend(s, 10));
SBUF_ASSERT_GTE(10, s->s_size - size_before);
sbuf_delete(s);
}
SBUF_SHOULD("reject requests that cause overflows")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(-1, sbuf_extend(s, SIZE_MAX));
SBUF_ASSERT_EQ(-1, sbuf_extend(s, INT_MAX));
sbuf_delete(s);
}
SBUF_SHOULD("transform the sbuf into an SBUF_DYNAMIC one")
{
struct sbuf *s = NULL;
char buf[4] = { 0 };
s = sbuf_new(NULL, buf, sizeof(buf), SBUF_AUTOEXTEND);
SBUF_ASSERT_NOT(SBUF_ISSET(s, SBUF_DYNAMIC));
SBUF_ASSERT_EQ(0, sbuf_extend(s, 10));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_DYNAMIC));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_capacity")
{
SBUF_SHOULD("account for the trailing nul byte")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(s->s_size - s->s_len - 1, sbuf_capacity(s));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_ensure_capacity")
{
SBUF_SHOULD("return 0 if the sbuf already has enough capacity")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_ensure_capacity(s, 5));
SBUF_ASSERT_EQ(size_before, s->s_size);
sbuf_delete(s);
}
SBUF_SHOULD("extend the buffer as needed")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_ensure_capacity(s, 30));
SBUF_ASSERT_GT(size_before, s->s_size);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_bcat")
{
SBUF_SHOULD("fail if the sbuf is marked as overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(-1, sbuf_bcat(s, "A", 1));
sbuf_delete(s);
}
SBUF_SHOULD("fail if len is too big")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(-1, sbuf_bcat(s, "A", INT_MAX));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf within limits")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_bcat(s, "ABC", 3));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed for binary data, even with nul bytes")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_bcat(s, "A\0C", 3));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('\0', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
sbuf_delete(s);
}
SBUF_SHOULD("append to existing data")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_bcat(s, "ABC", 3));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
SBUF_ASSERT_EQ(0, sbuf_bcat(s, "DEF", 3));
SBUF_ASSERT_EQ(6, s->s_len);
SBUF_ASSERT_EQ('D', s->s_buf[3]);
SBUF_ASSERT_EQ('E', s->s_buf[4]);
SBUF_ASSERT_EQ('F', s->s_buf[5]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf right up to the limit")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_bcat(s, "0123456789abcde", 15));
SBUF_ASSERT_EQ(15, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("fail for a fixed buf if too big")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(-1, sbuf_bcat(s, "0123456789abcdef", 16));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("expand the backing buffer as needed")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_bcat(s, "0123456789abcdef", 16));
SBUF_ASSERT_GT(size_before, s->s_size);
SBUF_ASSERT_EQ(16, s->s_len);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_bcpy")
{
SBUF_SHOULD("overwrite any existing data")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "ABC", 3));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "XYZ123", 6));
SBUF_ASSERT_EQ(6, s->s_len);
SBUF_ASSERT_EQ('X', s->s_buf[0]);
SBUF_ASSERT_EQ('Y', s->s_buf[1]);
SBUF_ASSERT_EQ('Z', s->s_buf[2]);
SBUF_ASSERT_EQ('1', s->s_buf[3]);
SBUF_ASSERT_EQ('2', s->s_buf[4]);
SBUF_ASSERT_EQ('3', s->s_buf[5]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed if the sbuf is marked as overflowed, but there is space")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "A", 1));
sbuf_delete(s);
}
SBUF_SHOULD("fail if len is too big")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(-1, sbuf_bcpy(s, "A", INT_MAX));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf within limits")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "ABC", 3));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf right up to the limit")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "0123456789abcde", 15));
SBUF_ASSERT_EQ(15, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("fail for a fixed buf if too big")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(-1, sbuf_bcpy(s, "0123456789abcdef", 16));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("expand the backing buffer as needed")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "0123456789abcdef", 16));
SBUF_ASSERT_GT(size_before, s->s_size);
SBUF_ASSERT_EQ(16, s->s_len);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_cat")
{
SBUF_SHOULD("fail if the sbuf is marked as overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(-1, sbuf_cat(s, "A"));
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf within limits")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cat(s, "ABC"));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
sbuf_delete(s);
}
SBUF_SHOULD("only copy up to a nul byte")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cat(s, "A\0C"));
SBUF_ASSERT_EQ(1, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
sbuf_delete(s);
}
SBUF_SHOULD("append to existing data")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cat(s, "ABC"));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
SBUF_ASSERT_EQ(0, sbuf_cat(s, "DEF"));
SBUF_ASSERT_EQ(6, s->s_len);
SBUF_ASSERT_EQ('D', s->s_buf[3]);
SBUF_ASSERT_EQ('E', s->s_buf[4]);
SBUF_ASSERT_EQ('F', s->s_buf[5]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf right up to the limit")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cat(s, "0123456789abcde"));
SBUF_ASSERT_EQ(15, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("fail for a fixed buf if too big")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(-1, sbuf_cat(s, "0123456789abcdef"));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("expand the backing buffer as needed")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_cat(s, "0123456789abcdef"));
SBUF_ASSERT_GT(size_before, s->s_size);
SBUF_ASSERT_EQ(16, s->s_len);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_cpy")
{
SBUF_SHOULD("overwrite any existing data")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "ABC"));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "XYZ123"));
SBUF_ASSERT_EQ(6, s->s_len);
SBUF_ASSERT_EQ('X', s->s_buf[0]);
SBUF_ASSERT_EQ('Y', s->s_buf[1]);
SBUF_ASSERT_EQ('Z', s->s_buf[2]);
SBUF_ASSERT_EQ('1', s->s_buf[3]);
SBUF_ASSERT_EQ('2', s->s_buf[4]);
SBUF_ASSERT_EQ('3', s->s_buf[5]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed if the sbuf is marked as overflowed, but there is space")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(0, sbuf_bcpy(s, "A", 1));
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf within limits")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "ABC"));
SBUF_ASSERT_EQ(3, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ('B', s->s_buf[1]);
SBUF_ASSERT_EQ('C', s->s_buf[2]);
sbuf_delete(s);
}
SBUF_SHOULD("succeed for a fixed buf right up to the limit")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "0123456789abcde"));
SBUF_ASSERT_EQ(15, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("fail for a fixed buf if too big")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(-1, sbuf_cpy(s, "0123456789abcdef"));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("expand the backing buffer as needed")
{
struct sbuf *s = NULL;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "0123456789abcdef"));
SBUF_ASSERT_GT(size_before, s->s_size);
SBUF_ASSERT_EQ(16, s->s_len);
sbuf_delete(s);
}
}
/* also tests sbuf_vprintf: */
SBUF_TESTING("sbuf_printf")
{
SBUF_SHOULD("support simple printing")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(0, sbuf_printf(s, "hello"));
SBUF_ASSERT_EQ(5, s->s_len);
SBUF_ASSERT_EQ('h', s->s_buf[0]);
SBUF_ASSERT_EQ('e', s->s_buf[1]);
SBUF_ASSERT_EQ('l', s->s_buf[2]);
SBUF_ASSERT_EQ('l', s->s_buf[3]);
SBUF_ASSERT_EQ('o', s->s_buf[4]);
sbuf_delete(s);
}
SBUF_SHOULD("support format strings")
{
struct sbuf *s = NULL;
char data1 = 'A';
int data2 = 123;
const char *data3 = "foo";
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(0, sbuf_printf(s, "%c %d %s", data1, data2, data3));
SBUF_ASSERT_EQ(9, s->s_len);
SBUF_ASSERT_EQ('A', s->s_buf[0]);
SBUF_ASSERT_EQ(' ', s->s_buf[1]);
SBUF_ASSERT_EQ('1', s->s_buf[2]);
SBUF_ASSERT_EQ('2', s->s_buf[3]);
SBUF_ASSERT_EQ('3', s->s_buf[4]);
SBUF_ASSERT_EQ(' ', s->s_buf[5]);
SBUF_ASSERT_EQ('f', s->s_buf[6]);
SBUF_ASSERT_EQ('o', s->s_buf[7]);
SBUF_ASSERT_EQ('o', s->s_buf[8]);
sbuf_delete(s);
}
SBUF_SHOULD("work with the fact we reserve a nul byte at the end")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_printf(s, "0123456789abcde"));
SBUF_ASSERT_NOT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("mark the sbuf as overflowed if we try to write too much")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(-1, sbuf_printf(s, "0123456789abcdef"));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("auto-extend as necessary")
{
struct sbuf *s = NULL;
const char *data = "0123456789abcdef";
int size_before;
size_t n;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_printf(s, "%s", data));
SBUF_ASSERT_GT(size_before, s->s_size);
for (n = 0; n < strlen(data); ++n) {
SBUF_ASSERT_EQ(data[n], s->s_buf[n]);
}
sbuf_delete(s);
}
SBUF_SHOULD("fail if the sbuf is marked as overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(-1, sbuf_printf(s, "A"));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_putc")
{
SBUF_SHOULD("work where we have capacity")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_putc(s, 'a'));
SBUF_ASSERT_EQ(1, s->s_len);
SBUF_ASSERT_EQ('a', s->s_buf[0]);
sbuf_delete(s);
}
SBUF_SHOULD("fail if we have a full, fixedlen sbuf")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_FIXEDLEN);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "0123456789abcd"));
SBUF_ASSERT_EQ(0, sbuf_putc(s, 'e'));
SBUF_ASSERT_EQ(-1, sbuf_putc(s, 'f'));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_OVERFLOWED));
sbuf_delete(s);
}
SBUF_SHOULD("ignore nul")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(0, sbuf_putc(s, '\0'));
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("auto-extend if necessary")
{
struct sbuf *s = NULL;
int len_before;
int size_before;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "0123456789abcde"));
len_before = s->s_len;
size_before = s->s_size;
SBUF_ASSERT_EQ(0, sbuf_putc(s, 'f'));
SBUF_ASSERT_EQ(len_before + 1, s->s_len);
SBUF_ASSERT_GT(size_before, s->s_size);
SBUF_ASSERT_EQ('f', s->s_buf[s->s_len - 1]);
sbuf_delete(s);
}
SBUF_SHOULD("fail if the sbuf is overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, SBUF_AUTOEXTEND);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(-1, sbuf_putc(s, 'a'));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_trim")
{
SBUF_SHOULD("remove trailing spaces, tabs and newlines")
{
struct sbuf *s = NULL;
const char *test = "foo \t\t\n\t";
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, test));
SBUF_ASSERT_EQ(strlen(test), s->s_len);
SBUF_ASSERT_EQ(0, sbuf_trim(s));
SBUF_ASSERT_EQ(3, s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("do nothing if there is no trailing whitespace")
{
struct sbuf *s = NULL;
const char *test = "foo";
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, test));
SBUF_ASSERT_EQ(strlen(test), s->s_len);
SBUF_ASSERT_EQ(0, sbuf_trim(s));
SBUF_ASSERT_EQ(strlen(test), s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("fail if the sbuf is overflowed")
{
struct sbuf *s = NULL;
const char *test = "foo ";
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, test));
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(-1, sbuf_trim(s));
SBUF_ASSERT_EQ(strlen(test), s->s_len);
sbuf_delete(s);
}
SBUF_SHOULD("work on empty strings")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_trim(s));
SBUF_ASSERT_EQ(0, s->s_len);
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_overflowed")
{
SBUF_SHOULD("return false if it hasn't overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_NOT(sbuf_overflowed(s));
sbuf_delete(s);
}
SBUF_SHOULD("return true if it has overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT(sbuf_overflowed(s));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_finish")
{
SBUF_SHOULD("insert a nul byte, clear the overflowed flag and set the finished flag")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_putc(s, 'A'));
s->s_buf[s->s_len] = 'x';
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_NOT(SBUF_ISSET(s, SBUF_FINISHED));
sbuf_finish(s);
SBUF_ASSERT_EQ(0, s->s_buf[s->s_len]);
SBUF_ASSERT_NOT(SBUF_ISSET(s, SBUF_OVERFLOWED));
SBUF_ASSERT(SBUF_ISSET(s, SBUF_FINISHED));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_data")
{
SBUF_SHOULD("return the s_buf pointer")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(s->s_buf, sbuf_data(s));
sbuf_delete(s);
}
SBUF_SHOULD("return the buffer we gave it")
{
struct sbuf *s = NULL;
char buf[4] = { 0 };
s = sbuf_new(NULL, buf, sizeof(buf), 0);
SBUF_ASSERT_EQ(buf, sbuf_data(s));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_len")
{
SBUF_SHOULD("return the length of the sbuf data")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "hello"));
SBUF_ASSERT_EQ(5, sbuf_len(s));
sbuf_delete(s);
}
SBUF_SHOULD("return -1 if the sbuf is overflowed")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_EQ(0, sbuf_cpy(s, "hello"));
SBUF_ASSERT_EQ(5, sbuf_len(s));
SBUF_SETFLAG(s, SBUF_OVERFLOWED);
SBUF_ASSERT_EQ(-1, sbuf_len(s));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_done")
{
SBUF_SHOULD("return false if the sbuf isn't finished")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_NOT(sbuf_done(s));
sbuf_delete(s);
}
SBUF_SHOULD("return true if the sbuf has finished")
{
struct sbuf *s = NULL;
s = sbuf_new(NULL, NULL, 16, 0);
SBUF_ASSERT_NOT(sbuf_done(s));
SBUF_SETFLAG(s, SBUF_FINISHED);
SBUF_ASSERT(sbuf_done(s));
sbuf_delete(s);
}
}
SBUF_TESTING("sbuf_delete")
{
SBUF_SHOULD("just free the backing buffer if we supplied an sbuf")
{
struct sbuf *s = NULL;
struct sbuf existing = {};
s = sbuf_new(&existing, NULL, 16, 0);
SBUF_ASSERT_NE(NULL, s->s_buf);
sbuf_delete(s);
SBUF_ASSERT_EQ(NULL, s->s_buf);
}
}
SBUF_TEST_END;
}
SYSCTL_PROC(_kern, OID_AUTO, sbuf_test, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_KERN | CTLFLAG_MASKED, 0, 0, sysctl_sbuf_tests, "A", "sbuf tests");
#endif /* DEBUG || DEVELOPMENT */