From 7a3fedfea90b212b98f36150a9296d7a198b2b93 Mon Sep 17 00:00:00 2001 From: tocariimaa Date: Sat, 5 Apr 2025 19:31:25 -0300 Subject: [PATCH] add nstr --- misc/nstr/README.md | 25 +++++ misc/nstr/nstr.h | 211 ++++++++++++++++++++++++++++++++++++++++++ misc/nstr/nstr_test | Bin 0 -> 61088 bytes misc/nstr/nstr_test.c | 81 ++++++++++++++++ 4 files changed, 317 insertions(+) create mode 100644 misc/nstr/README.md create mode 100644 misc/nstr/nstr.h create mode 100755 misc/nstr/nstr_test create mode 100644 misc/nstr/nstr_test.c diff --git a/misc/nstr/README.md b/misc/nstr/README.md new file mode 100644 index 0000000..04b7d84 --- /dev/null +++ b/misc/nstr/README.md @@ -0,0 +1,25 @@ +# nstr +Of course yet another single-header string library for C. + +This library provides a simple `Str` type, supporting dynamic extending or fixed +static buffers. + +## Usage +In a single C file in your project do: +```c +#define NSTR_IMPLEMENTATION /* this is to enable the implementations */ +#include "nstr.h" +``` + +Then `nstr.h` can be freely included in any other file, which will only include the +signatures, macros and types. + +Functions and macros of this library are prefixed with `nstr_` (because `str*` is reserved in C). + +## Documentation +See the header file, the documentation are the comments. + +TODO some examples, for now the `nstr_test.c` serves as an example. + +## License +This library is under the Unlicense. diff --git a/misc/nstr/nstr.h b/misc/nstr/nstr.h new file mode 100644 index 0000000..95c1abd --- /dev/null +++ b/misc/nstr/nstr.h @@ -0,0 +1,211 @@ +#ifndef _nstr_h_ +#define _nstr_h_ + +#ifndef NSTR_BUF_TYPE +# define NSTR_BUF_TYPE char +#endif +#ifndef NSTR_LEN_TYPE +# include + 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 + +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 diff --git a/misc/nstr/nstr_test b/misc/nstr/nstr_test new file mode 100755 index 0000000000000000000000000000000000000000..dbc938d8fcb5f1781d2c4bde5438180a6d4753e9 GIT binary patch literal 61088 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 literal 0 HcmV?d00001 diff --git a/misc/nstr/nstr_test.c b/misc/nstr/nstr_test.c new file mode 100644 index 0000000..23fc3b0 --- /dev/null +++ b/misc/nstr/nstr_test.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include + +#define NSTR_IMPLEMENTATION +#define NSTR_LEN_TYPE size_t +#include "nstr.h" + +#define tmsg(s) fputs(s "... ", stderr) +#define tpass() fputs("passed\n", stderr) + +int +main(void) +{ + { + tmsg("String heap allocation with copy"); + + const char *cs1 = "hello woooooooooooooooorld aaaaaa!!!!aaaaaaaaaaaaaaaaaa"; + size_t cs1len = strlen(cs1); + Str s1 = nstr_new(cs1, cs1len); + s1.len = cs1len; + + assert(!nstr_is_null(s1)); /* ensure allocation success */ + assert(s1.s[s1.len] == '\0'); + assert(s1.len == cs1len); + assert(memcmp(s1.s, cs1, s1.len) == 0); + nstr_free(s1); + + tpass(); + } + { + tmsg("nstr_afmt"); + + Str s1 = nstr_afmt("%s/file/path/to/%s_%d", "/var", "log", 3129); + Str s2 = nstr_from_c("/var/file/path/to/log_3129"); + assert(nstr_equal(s1, s2)); + nstr_free(s1); + + tpass(); + } + { + tmsg("nstr_fmt"); + + Str buf = { .s = (char [128]){0}, .cap = 128 }; + Str s1 = nstr_fmt(buf, "%s/file/path/to/%s_%d", "/var", "log", 3129); + Str s2 = nstr_from_c("/var/file/path/to/log_3129"); + assert(nstr_equal(s1, s2)); + + tpass(); + } + { + tmsg("nstr_append with truncation"); + + Str s1 = { .s = (char [12]){0}, .cap = 12 }; + Str s2 = nstr_from_c("abcde"); + + s1 = nstr_append(s1, s2, NSTR_TRUNCATE); + assert(nstr_equal(s1, s2)); + s1 = nstr_append(s1, s2, NSTR_TRUNCATE); + assert(nstr_equal(s1, nstr_from_c("abcdeabcde"))); + s1 = nstr_append(s1, s2, NSTR_TRUNCATE); + assert(nstr_equal(s1, nstr_from_c("abcdeabcdea"))); + + tpass(); + } + { + tmsg("nstr_append with resizing"); + + Str s1 = nstr_new(NULL, 8); + s1 = nstr_append(s1, nstr_from_c("ABCDEFGHIJ"), NSTR_RESIZE); + s1 = nstr_append(s1, nstr_from_c("KLMNOP"), NSTR_RESIZE); + s1 = nstr_append(s1, nstr_from_c("QRSTUVWXYZ"), NSTR_RESIZE); + assert(nstr_equal(s1, nstr_from_c("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))); + nstr_free(s1); + + tpass(); + } + + return 0; +}