211 lines
4.6 KiB
C
211 lines
4.6 KiB
C
#ifndef _nstr_h_
|
|
#define _nstr_h_
|
|
|
|
#ifndef NSTR_BUF_TYPE
|
|
# define NSTR_BUF_TYPE char
|
|
#endif
|
|
#ifndef NSTR_LEN_TYPE
|
|
# include <stddef.h>
|
|
typedef ptrdiff_t isize;
|
|
# define NSTR_LEN_TYPE isize
|
|
#endif
|
|
#ifndef NSTR_ALLOC
|
|
# define NSTR_ALLOC calloc
|
|
#endif
|
|
#ifndef NSTR_FREE
|
|
# define NSTR_FREE free
|
|
#endif
|
|
#ifndef NSTR_ASSERT
|
|
# define NSTR_ASSERT assert
|
|
#endif
|
|
#ifndef NSTR_WITH_CAP
|
|
# define NSTR_WITH_CAP 1
|
|
#endif
|
|
|
|
typedef struct Str Str;
|
|
typedef enum StrAppendMode StrAppendMode;
|
|
|
|
struct Str {
|
|
NSTR_BUF_TYPE *s; /* string data */
|
|
NSTR_LEN_TYPE len; /* length */
|
|
NSTR_LEN_TYPE cap; /* capacity for appending */
|
|
};
|
|
|
|
enum StrAppendMode {
|
|
NSTR_TRUNCATE, /* Append as much bytes as it can fit without resizing. */
|
|
NSTR_RESIZE, /* Assumes that the `Str` is heap allocated. */
|
|
};
|
|
|
|
/* Creates a `Str` from a buffer of size `len`. Asserts that the buffer is null-terminated */
|
|
Str
|
|
nstr_from_buf(NSTR_BUF_TYPE *buf, NSTR_LEN_TYPE len);
|
|
|
|
/* "Converts" a `Str` into a C string. Since `Str` are meant to be
|
|
* null-terminated already, no conversion is made, but ensures that the
|
|
* null terminator is present. */
|
|
char *
|
|
nstr_to_c(Str s);
|
|
|
|
/* Returns `true` if both strings are equal. */
|
|
int
|
|
nstr_equal(Str s1, Str s2);
|
|
|
|
/* Heaps allocates a new `Str` of size `len` + 1 (extra for the null terminator), copying
|
|
* the contents from `data` if it isn't null to the new string. */
|
|
Str
|
|
nstr_new(const NSTR_BUF_TYPE *data, NSTR_LEN_TYPE cap);
|
|
|
|
/* Deallocates a heap-allocated `Str`. This function exists only for simmetry,
|
|
* plain `free` can be used as well. */
|
|
void
|
|
nstr_free(Str s);
|
|
|
|
/* Appends contents from the string `s2` to `s1` according to the append `mode`.
|
|
* Both `s1` and `s2` backing buffers must not overlap (because memcmp is used
|
|
* instead of memmove). */
|
|
Str
|
|
nstr_append(Str s1, Str s2, StrAppendMode mode);
|
|
|
|
/* Formats a `Str`, storing the formatted string in `buf`. Formatting
|
|
* arguments are passed as a va_list in `args`. Assumes that there is enough
|
|
* space for a null terminator. */
|
|
Str
|
|
nstr_vfmt(Str buf, const char *fmt, va_list args);
|
|
|
|
/* Formats a `Str`, storing the formatted string in `buf`. Assumes that there is enough
|
|
* space for a null terminator. */
|
|
Str
|
|
nstr_fmt(Str buf, const char *fmt, ...);
|
|
|
|
/* Returns a formatted string (heap allocated) of the exact required size.
|
|
* The caller takes ownership of the string data and is responsible of freeing
|
|
* the allocated memory. Requires `vsnprintf`. */
|
|
Str
|
|
nstr_afmt(const char *fmt, ...);
|
|
|
|
#define nstr_from_c(s) nstr_from_buf(s, strlen(s))
|
|
#define nstr_is_empty(str) ((str).len == 0)
|
|
#define nstr_is_null(str) ((str).s == NULL)
|
|
|
|
#ifdef NSTR_IMPLEMENTATION
|
|
#include <stdarg.h>
|
|
|
|
int
|
|
vsnprintf(char *, unsigned long, const char *, va_list);
|
|
|
|
Str
|
|
nstr_from_buf(NSTR_BUF_TYPE *buf, NSTR_LEN_TYPE len)
|
|
{
|
|
NSTR_ASSERT(buf[len] == '\0');
|
|
return (Str){ buf, len, len };
|
|
}
|
|
|
|
char *
|
|
nstr_to_c(Str s)
|
|
{
|
|
if (s.len == 0 || s.s == NULL)
|
|
return NULL;
|
|
|
|
NSTR_ASSERT(s.s[s.len] == '\0');
|
|
return (char *)s.s;
|
|
}
|
|
|
|
int
|
|
nstr_equal(Str s1, Str s2)
|
|
{
|
|
/* because passing nil to mem* is UB even if size == 0... */
|
|
return (s1.len == s2.len) && (s1.len == 0 || memcmp(s1.s, s2.s, s1.len) == 0);
|
|
}
|
|
|
|
Str
|
|
nstr_new(const NSTR_BUF_TYPE *data, NSTR_LEN_TYPE cap)
|
|
{
|
|
NSTR_ASSERT(cap >= 0);
|
|
Str s;
|
|
if ((s.s = NSTR_ALLOC(cap + 1, sizeof(*s.s))) == NULL)
|
|
return (Str){0};
|
|
|
|
s.cap = cap;
|
|
if (data != NULL) {
|
|
memcpy(s.s, data, cap);
|
|
s.s[cap + 1] = '\0'; /* ensure */
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void
|
|
nstr_free(Str s)
|
|
{
|
|
NSTR_FREE(s.s);
|
|
}
|
|
|
|
Str
|
|
nstr_append(Str s1, Str s2, StrAppendMode mode)
|
|
{
|
|
switch (mode) {
|
|
case NSTR_TRUNCATE:
|
|
if (s1.len + s2.len > s1.cap)
|
|
s2.len = s1.cap - 1 - s1.len;
|
|
break;
|
|
case NSTR_RESIZE:
|
|
if (s1.cap + s2.len > s1.cap) {
|
|
Str stmp = nstr_new(s1.s, s1.cap + s2.len);
|
|
if (nstr_is_null(stmp))
|
|
return (Str){0};
|
|
s1.s = stmp.s;
|
|
s1.cap = stmp.cap;
|
|
}
|
|
break;
|
|
}
|
|
|
|
memcpy(s1.s + s1.len, s2.s, s2.len);
|
|
s1.len += s2.len;
|
|
s1.s[s1.len] = '\0';
|
|
|
|
return s1;
|
|
}
|
|
|
|
Str
|
|
nstr_vfmt(Str buf, const char *fmt, va_list args)
|
|
{
|
|
buf.len = (NSTR_LEN_TYPE)vsnprintf((char *)buf.s, buf.cap + 1, fmt, args);
|
|
return buf;
|
|
}
|
|
|
|
Str
|
|
nstr_fmt(Str buf, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
buf = nstr_vfmt(buf, fmt, args);
|
|
va_end(args);
|
|
return buf;
|
|
}
|
|
|
|
Str
|
|
nstr_afmt(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
Str buf;
|
|
|
|
va_start(args, fmt);
|
|
/* Calculate buffer size required to hold the formatted string */
|
|
int reqs = vsnprintf(NULL, 0, fmt, args);
|
|
if (reqs < 0)
|
|
return (Str){0};
|
|
|
|
va_start(args, fmt);
|
|
buf = nstr_new(NULL, (NSTR_LEN_TYPE)reqs);
|
|
va_end(args);
|
|
|
|
buf = nstr_vfmt(buf, fmt, args);
|
|
if (nstr_is_null(buf)) /* allocation failed */
|
|
return (Str){0};
|
|
|
|
return buf;
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|