From 14c8610315e980655e7e9db9e5907ca1a7a0c4e9 Mon Sep 17 00:00:00 2001 From: cancel Date: Sat, 11 Jan 2020 08:18:46 +0900 Subject: [PATCH] Change to use altnerate heap string implementation. `sdd` was the first attempt at making one of these individually-allocated string management/utility things. I didn't end up liking the design, so I tried again from scratch, and called the new one `oso`. Let's see how that one turns out. The name might change again in the future, though I feel better about the design of this one. --- term_util.c | 11 ++- term_util.h | 4 +- thirdparty/oso.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++ thirdparty/oso.h | 182 +++++++++++++++++++++++++++++++++++++ thirdparty/sdd.c | 198 ---------------------------------------- thirdparty/sdd.h | 104 --------------------- tool | 2 +- tui_main.c | 79 ++++++++-------- 8 files changed, 462 insertions(+), 350 deletions(-) create mode 100644 thirdparty/oso.c create mode 100644 thirdparty/oso.h delete mode 100644 thirdparty/sdd.c delete mode 100644 thirdparty/sdd.h diff --git a/term_util.c b/term_util.c index f05d435..b3ebb09 100644 --- a/term_util.c +++ b/term_util.c @@ -1,5 +1,5 @@ #include "term_util.h" -#include "sdd.h" +#include "oso.h" #include #include #include @@ -564,7 +564,8 @@ int qform_id(Qform const* qf) { return qf->id; } void qform_add_text_line(Qform* qf, int id, char const* initial) { FIELD* f = new_field(1, 30, 0, 0, 0, 0); - set_field_buffer(f, 0, initial); + if (initial) + set_field_buffer(f, 0, initial); set_field_userptr(f, (void*)(intptr_t)(id)); field_opts_off(f, O_WRAP | O_BLANK | O_STATIC); qf->ncurses_fields[qf->fields_count] = f; @@ -665,7 +666,7 @@ FIELD* qform_find_field(Qform const* qf, int id) { return NULL; } -bool qform_get_text_line(Qform const* qf, int id, sdd** out) { +bool qform_get_text_line(Qform const* qf, int id, oso** out) { FIELD* f = qform_find_field(qf, id); if (!f) return false; @@ -674,6 +675,6 @@ bool qform_get_text_line(Qform const* qf, int id, sdd** out) { if (!buf) return false; Usz trimmed = size_without_trailing_spaces(buf); - *out = *out ? sdd_cpylen(*out, buf, trimmed) : sdd_newlen(buf, trimmed); - return (bool)*out; + osoputlen(out, buf, trimmed); + return osolen(*out) > 0; } diff --git a/term_util.h b/term_util.h index b02b940..12e98cc 100644 --- a/term_util.h +++ b/term_util.h @@ -4,7 +4,7 @@ #define CTRL_PLUS(c) ((c)&037) -struct sdd; +struct oso; typedef enum { C_natural, @@ -143,6 +143,6 @@ void qform_add_text_line(Qform* qf, int id, char const* initial); void qform_push_to_nav(Qform* qf); void qform_set_title(Qform* qf, char const* title); bool qform_drive(Qform* qf, int key, Qform_action* out_action); -bool qform_get_text_line(Qform const* qf, int id, struct sdd** out); +bool qform_get_text_line(Qform const* qf, int id, struct oso** out); extern Qnav_stack qnav_stack; diff --git a/thirdparty/oso.c b/thirdparty/oso.c new file mode 100644 index 0000000..6f7aa52 --- /dev/null +++ b/thirdparty/oso.c @@ -0,0 +1,232 @@ +#include "oso.h" +#include +#include +#include +#include + +#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) +#if __has_attribute(noinline) && __has_attribute(noclone) +#define OSO_NOINLINE __attribute__((noinline, noclone)) +#elif __has_attribute(noinline) +#define OSO_NOINLINE __attribute__((noinline)) +#endif +#elif defined(_MSC_VER) +#define OSO_NOINLINE __declspec(noinline) +#endif +#ifndef OSO_NOINLINE +#define OSO_NOINLINE +#endif + +#define OSO_INTERNAL OSO_NOINLINE static +#define OSO_HDR(s) ((oso_header *)s - 1) +#define OSO_CAP_MAX (SIZE_MAX - (sizeof(oso_header) + 1)) + +typedef struct oso { + size_t len; + size_t cap; +} oso_header; + +OSO_INTERNAL oso *oso_impl_reallochdr(oso_header *hdr, size_t new_cap) { + if (hdr) { + oso_header *new_hdr = realloc(hdr, sizeof(oso_header) + new_cap + 1); + if (!new_hdr) { + free(hdr); + return NULL; + } + new_hdr->cap = new_cap; + return new_hdr + 1; + } + hdr = malloc(sizeof(oso_header) + new_cap + 1); + if (!hdr) + return NULL; + hdr->len = 0; + hdr->cap = new_cap; + ((char *)(hdr + 1))[0] = '\0'; + return hdr + 1; +} +OSO_INTERNAL oso *oso_impl_catvprintf(oso *s, char const *fmt, va_list ap) { + size_t old_len; + int required; + va_list cpy; + va_copy(cpy, ap); + required = vsnprintf(NULL, 0, fmt, cpy); + va_end(cpy); + osomakeroomfor(&s, (size_t)required); + if (!s) + return NULL; + old_len = OSO_HDR(s)->len; + vsnprintf((char *)s + old_len, (size_t)required + 1, fmt, ap); + OSO_HDR(s)->len = old_len + (size_t)required; + return s; +} + +OSO_NOINLINE +void osoensurecap(oso **p, size_t new_cap) { + oso *s = *p; + if (new_cap > OSO_CAP_MAX) { + if (s) { + free(OSO_HDR(s)); + *p = NULL; + } + return; + } + oso_header *hdr = NULL; + if (s) { + hdr = OSO_HDR(s); + if (hdr->cap >= new_cap) + return; + } + *p = oso_impl_reallochdr(hdr, new_cap); +} + +OSO_NOINLINE +void osomakeroomfor(oso **p, size_t add_len) { + oso *s = *p; + oso_header *hdr = NULL; + size_t new_cap; + if (s) { + hdr = OSO_HDR(s); + size_t len = hdr->len, cap = hdr->cap; + if (len > OSO_CAP_MAX - add_len) { // overflow, goodnight + free(hdr); + *p = NULL; + return; + } + new_cap = len + add_len; + if (cap >= new_cap) + return; + } else { + if (add_len > OSO_CAP_MAX) + return; + new_cap = add_len; + } + *p = oso_impl_reallochdr(hdr, new_cap); +} + +void osoput(oso **p, char const *restrict cstr) { + osoputlen(p, cstr, strlen(cstr)); +} + +OSO_NOINLINE +void osoputlen(oso **p, char const *restrict cstr, size_t len) { + oso *s = *p; + osoensurecap(&s, len); + if (s) { + OSO_HDR(s)->len = len; + memcpy((char *)s, cstr, len); + ((char *)s)[len] = '\0'; + } + *p = s; +} +void osoputoso(oso **p, oso const *other) { + if (!other) + return; + osoputlen(p, (char const *)other, OSO_HDR(other)->len); +} +void osoputvprintf(oso **p, char const *fmt, va_list ap) { + *p = oso_impl_catvprintf(*p, fmt, ap); +} +void osoputprintf(oso **p, char const *fmt, ...) { + va_list ap; + va_start(ap, fmt); + *p = oso_impl_catvprintf(*p, fmt, ap); + va_end(ap); +} +void osocat(oso **p, char const *cstr) { osocatlen(p, cstr, strlen(cstr)); } +OSO_NOINLINE +void osocatlen(oso **p, char const *cstr, size_t len) { + oso *s = *p; + osomakeroomfor(&s, len); + if (s) { + oso_header *hdr = OSO_HDR(s); + size_t curr_len = hdr->len; + memcpy((char *)s + curr_len, cstr, len); + ((char *)s)[curr_len + len] = '\0'; + hdr->len = curr_len + len; + } + *p = s; +} +void osocatoso(oso **p, oso const *other) { + if (!other) + return; + osocatlen(p, (char const *)other, OSO_HDR(other)->len); +} +void osocatvprintf(oso **p, char const *fmt, va_list ap) { + oso *s = *p; + if (s) { + OSO_HDR(s)->len = 0; + ((char *)s)[0] = '\0'; + } + *p = oso_impl_catvprintf(s, fmt, ap); +} +void osocatprintf(oso **p, char const *fmt, ...) { + oso *s = *p; + if (s) { + OSO_HDR(s)->len = 0; + ((char *)s)[0] = '\0'; + } + va_list ap; + va_start(ap, fmt); + *p = oso_impl_catvprintf(s, fmt, ap); + va_end(ap); +} +void osoclear(oso **p) { + oso *s = *p; + if (!s) + return; + OSO_HDR(s)->len = 0; + ((char *)s)[0] = '\0'; +} +void osofree(oso *s) { + if (s) + free(OSO_HDR(s)); +} +void osowipe(oso **p) { + osofree(*p); + *p = NULL; +} +void ososwap(oso const **a, oso const **b) { + oso const *tmp = *a; + *a = *b; + *b = tmp; +} +size_t osolen(oso const *s) { return s ? OSO_HDR(s)->len : 0; } +size_t osocap(oso const *s) { return s ? OSO_HDR(s)->cap : 0; } +void osolencap(oso const *s, size_t *out_len, size_t *out_cap) { + if (!s) { + *out_len = 0; + *out_cap = 0; + return; + } + oso_header *hdr = OSO_HDR(s); + *out_len = hdr->len; + *out_cap = hdr->cap; +} +size_t osoavail(oso const *s) { + if (!s) + return 0; + oso_header *h = OSO_HDR(s); + return h->cap - h->len; +} + +void osotrim(oso *restrict s, char const *restrict cut_set) { + if (!s) + return; + char *str, *start, *end, *start_pos, *end_pos; + start_pos = start = str = (char *)s; + end_pos = end = str + OSO_HDR(s)->len - 1; + while (start_pos <= end && strchr(cut_set, *start_pos)) + start_pos++; + while (end_pos > start_pos && strchr(cut_set, *end_pos)) + end_pos--; + size_t len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1); + OSO_HDR(s)->len = len; + if (str != start_pos) + memmove(str, start_pos, len); + str[len] = '\0'; +} + +#undef OSO_HDR +#undef OSO_NOINLINE +#undef OSO_CAP_MAX +#undef OSO_INTERNAL diff --git a/thirdparty/oso.h b/thirdparty/oso.h new file mode 100644 index 0000000..41f1984 --- /dev/null +++ b/thirdparty/oso.h @@ -0,0 +1,182 @@ +#pragma once +// Heap-allocated string handling. +// Inspired by antirez's sds and gingerBill's gb_string.h. +// +// "I need null-terminated strings to interact with libc and/or POSIX, and my +// software isn't serious enough to warrant using arena or page allocation +// strategies. Manually fussing with null-terminated strings with libc sucks, +// even though we're allocating everything individually on the heap! Why can't +// we at least get a nicer interface for the trade-off we've already made?" +// +// EXAMPLE +// --------- +// oso *mystring = NULL; +// osoput(&mystring, "Hello World"); +// printf((char *)mystring); +// osoput(&mystring, "How about some pancakes?"); +// printf((char *)mystring); +// osocat(&mstring, " Sure!"); +// printf((char *)mystring); +// osofree(mystring); +// +// > Hello World! +// > How about some pancakes? +// > How about some pancakes? Sure! +// +// RULES +// ------- +// 1. `oso *` can always be cast to `char *`, but it's your job to check if +// it's null before passing it to on something that doesn't tolerate null +// pointers. +// +// 2. The functions defined in this header tolerate null pointers like this: +// +// `oso *` -> OK to be null. +// `char const *` -> Must not be null. +// `oso **` -> Must not be null, but the `oso *` pointed to +// can be null. The pointed-to `oso *` may be +// modified during the call. +// +// 3. `oso *` and `char const *` as arguments to the functions here must not +// overlap in memory. During the call, the buffer pointed to by a `oso *` +// might need to be reallocated in memory to make room for the `char const +// *` contents, and if the `char const *` contents end up being moved by +// that operation, the pointer will no longer be pointing at valid memory. +// (This also applies to functions which accept two `oso *` as inputs.) +// +// Parameters with the type `oso *` are safe to pass as a null pointer. But +// `oso **` parameters shouldn't be null. The `oso *` pointed to by the `oso +// **` can be null, though. In other words, you need to do `&mystring` to pass +// a `oso **` argument. +// +// During the function call, if the `oso *` pointed to by the `oso **` needs to +// become non-null, or if the existing buffer needs to be moved to grow larger, +// the `oso *` will be set to a new value. +// +// oso *mystring = NULL; +// osolen(mystring); // Gives 0 +// osocat(&mystring, "waffles"); +// osolen(mystring); // Gives 7 +// osofree(mystring); +// +// NAMES +// ------- +// osoput______ -> Copy a string into an oso string. +// osocat______ -> Append a string onto the end of an oso string. +// ______len -> Do it with an explicit length argument, so the C-string +// doesn't have to be '\0'-terminated. +// ______oso -> Do it with a second oso string. +// ______printf -> Do it by using printf. +// +// ALLOC FAILURE +// --------------- +// If an allocation fails (including failing to reallocate) the `oso *` will be +// set to null. If you decide to handle memory allocation failures, you'll need +// to check for that. +// +// In the oso code, if a reallocation of an existing buffer fails (`realloc()` +// returns null) then the old, still-valid buffer is immediately freed. +// Therefore, in an out-of-memory situation, it's possible that you will *lose* +// your strings without getting a chance to do something with the old buffers. +// +// Variations of the oso functions may be added in the future, with a _c suffix +// or something, which preserve the old buffer and return an error code in the +// event of a reallocation failure. I'm not sure how important this is, since +// most existing libc-based software doesn't handle out-of-memory conditions +// for small strings without imploding. +// +// (sds, for example, will lose your string *and* leak the old buffer if a +// reallocation fails.) + +#include +#include + +#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) +#if __has_attribute(format) +#define OSO_PRINTF(...) __attribute__((format(printf, __VA_ARGS__))) +#endif +#if __has_attribute(nonnull) +#define OSO_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) +#endif +#endif +#ifndef OSO_PRINTF +#define OSO_PRINTF(...) +#endif +#ifndef OSO_NONNULL +#define OSO_NONNULL(...) +#endif + +typedef struct oso oso; + +#define osoc(s) ((char const *)s) + +void osoput(oso **p, char const *cstr) OSO_NONNULL(); +// ^- Copies the '\0'-terminated string into the `oso *` string located at +// `*p`. If `*p` is null or there isn't enough capacity to hold `cstr`, it +// will be reallocated. The pointer value at `*p` will be updated if +// necessary. `*p` and `cstr` must not point to overlapping memory. +void osoputlen(oso **p, char const *cstr, size_t len) OSO_NONNULL(); +// ^- Same as above, but uses an additional parameter that specifies the length +// of `cstr, and `cstr` does not have to be '\0'-terminated. +// `*p` and `cstr` must not point to overlapping memory. +void osoputoso(oso **p, oso const *other) OSO_NONNULL(1); +// ^- Same as above, but using another `oso`. `*p` and `other` must not point +// to overlapping memory. +void osoputvprintf(oso **p, char const *fmt, va_list ap) OSO_NONNULL(1, 2) + OSO_PRINTF(2, 0); +void osoputprintf(oso **p, char const *fmt, ...) OSO_NONNULL(1, 2) + OSO_PRINTF(2, 3); +// ^- Same as above, but do it by using printf. + +void osocat(oso **p, char const *cstr) OSO_NONNULL(); +void osocatlen(oso **p, char const *cstr, size_t len) OSO_NONNULL(); +void osocatoso(oso **p, oso const *other) OSO_NONNULL(1); +void osocatvprintf(oso **p, char const *fmt, va_list ap) OSO_NONNULL(1, 2) + OSO_PRINTF(2, 0); +void osocatprintf(oso **p, char const *fmt, ...) OSO_NONNULL(1, 2) + OSO_PRINTF(2, 3); +// ^- Append string to oso string. Same rules as `osoput` family. + +void osoensurecap(oso **p, size_t cap) OSO_NONNULL(); +// ^- Ensure that s has at least `cap` memory allocated for it. This does not +// care about the strlen of the characters or the prefixed length count -- +// only the backing memory allocation. +void osomakeroomfor(oso **p, size_t len) OSO_NONNULL(); +// ^- Ensure that s has enough allocated space after the '\0'-terminnation +// character to hold an additional add_len characters. It does not adjust +// the `length` number value, only the capacity, if necessary. +// +// Both `osoensurecap()` and `osomakeroomfor()` can be used to avoid making +// multiple smaller reallactions as the string grows in the future. You can +// also use them if you're going to modify the string buffer manually in +// your own code, and need to create some space in the buffer. + +void osoclear(oso **p) OSO_NONNULL(); +// ^- Set len to 0, write '\0' at pos 0. Leaves allocated memory in place. +void osofree(oso *s); +// ^- You know. And calling with null is allowed. +void osowipe(oso **p) OSO_NONNULL(); +// ^- It's like `osofree()`, except you give it a ptr-to-ptr, and it also sets +// `*p` to null for you when it's done freeing the memory. +void ososwap(oso const **a, oso const **b) OSO_NONNULL(); +// ^- Swaps the two pointers. Yeah, that's all it does. Why? Because it's +// common when dealing memory management for individually allocated strings +// and changing between old and new string values. +void osopokelen(oso *s, size_t len) OSO_NONNULL(); +// ^- Manually updates length field. Doesn't do anything else for you. + +size_t osolen(oso const *s); +// ^- Bytes in use by the string (not including the '\0' character.) +size_t osocap(oso const *s); +// ^- Bytes allocated on heap (not including the '\0' terminator.) +void osolencap(oso const *s, size_t *out_len, size_t *out_cap) + OSO_NONNULL(2, 3); +// ^- Get both the len and the cap in one call. +size_t osoavail(oso const *s); +// ^- osocap(s) - osolen(s) + +void osotrim(oso *restrict s, char const *restrict cut_set) OSO_NONNULL(2); +// ^- Remove the characters in `cut_set` from the beginning and ending of `s`. + +#undef OSO_PRINTF +#undef OSO_NONNULL diff --git a/thirdparty/sdd.c b/thirdparty/sdd.c deleted file mode 100644 index da731b0..0000000 --- a/thirdparty/sdd.c +++ /dev/null @@ -1,198 +0,0 @@ -// Derived from gingerBill's public domain gb_string.h file. -#include "sdd.h" -#include -#include -#include -#include - -#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) -#if __has_attribute(noinline) && __has_attribute(noclone) -#define SDD_NOINLINE __attribute__((noinline, noclone)) -#elif __has_attribute(noinline) -#define SDD_NOINLINE __attribute__((noinline)) -#endif -#elif defined(_MSC_VER) -#define SDD_NOINLINE __declspec(noinline) -#endif -#ifndef SDD_NOINLINE -#define SDD_NOINLINE -#endif - -#define SDD_INTERNAL SDD_NOINLINE static -#define SDD_HDR(s) ((sdd_header *)s - 1) -#define SDD_CAP_MAX (SIZE_MAX - (sizeof(sdd_header) + 1)) - -typedef struct sdd { - size_t len; - size_t cap; -} sdd_header; - -SDD_INTERNAL sdd *sdd_impl_new(char const *init, size_t len, size_t cap) { - if (cap > SDD_CAP_MAX) - return NULL; - sdd_header *header = (sdd *)malloc(sizeof(sdd) + cap + 1); - if (!header) - return NULL; - header->len = len; - header->cap = cap; - char *str = (char *)(header + 1); - if (init) - memcpy(str, init, len); - str[len] = '\0'; - return (sdd *)str; -} -SDD_INTERNAL sdd *sdd_impl_reallochdr(sdd_header *hdr, size_t new_cap) { - sdd_header *new_hdr = realloc(hdr, sizeof(sdd_header) + new_cap + 1); - if (!new_hdr) { - free(hdr); - return NULL; - } - new_hdr->cap = new_cap; - return new_hdr + 1; -} -SDD_INTERNAL sdd *sdd_impl_catvprintf(sdd *s, char const *fmt, va_list ap) { - size_t old_len; - int required; - va_list cpy; - va_copy(cpy, ap); - required = vsnprintf(NULL, 0, fmt, cpy); - va_end(cpy); - if (s) { - old_len = SDD_HDR(s)->len; - s = sdd_makeroomfor(s, (size_t)required); - } else { - old_len = 0; - s = sdd_newcap((size_t)required); - } - if (!s) - return NULL; - vsnprintf((char *)s + old_len, (size_t)required + 1, fmt, ap); - SDD_HDR(s)->len = old_len + (size_t)required; - return s; -} - -sdd *sdd_new(char const *str) { - size_t len = strlen(str); - return sdd_impl_new(str, len, len); -} -sdd *sdd_newlen(char const *init, size_t len) { - return sdd_impl_new(init, len, len); -} -sdd *sdd_newcap(size_t cap) { return sdd_impl_new(NULL, 0, cap); } -sdd *sdd_dup(sdd const *str) { - size_t len = SDD_HDR(str)->len; - return sdd_impl_new((char const *)str, len, len); -} -sdd *sdd_newvprintf(char const *fmt, va_list ap) { - return sdd_impl_catvprintf(NULL, fmt, ap); -} -sdd *sdd_newprintf(char const *fmt, ...) { - sdd *s; - va_list ap; - va_start(ap, fmt); - s = sdd_impl_catvprintf(NULL, fmt, ap); - va_end(ap); - return s; -} -void sdd_free(sdd *s) { - if (!s) - return; - free(s - 1); -} -sdd *sdd_cpy(sdd *restrict s, char const *restrict cstr) { - return sdd_cpylen(s, cstr, strlen(cstr)); -} -SDD_NOINLINE -sdd *sdd_cpylen(sdd *restrict s, char const *restrict cstr, size_t len) { - s = sdd_ensurecap(s, len); - if (!s) - return NULL; - SDD_HDR(s)->len = len; - memcpy(s, cstr, len); - ((char *)s)[len] = '\0'; - return s; -} -sdd *sdd_cpysdd(sdd *restrict s, sdd const *restrict other) { - return sdd_cpylen(s, (char const *)other, SDD_HDR(other)->len); -} -sdd *sdd_cat(sdd *restrict s, char const *restrict other) { - return sdd_catlen(s, other, strlen(other)); -} -SDD_NOINLINE -sdd *sdd_catlen(sdd *restrict s, char const *restrict other, size_t other_len) { - size_t curr_len = SDD_HDR(s)->len; - s = sdd_makeroomfor(s, other_len); - if (!s) - return NULL; - memcpy((char *)s + curr_len, other, other_len); - ((char *)s)[curr_len + other_len] = '\0'; - SDD_HDR(s)->len = curr_len + other_len; - return s; -} -sdd *sdd_catsdd(sdd *restrict s, sdd const *restrict other) { - return sdd_catlen(s, (char const *)other, SDD_HDR(other)->len); -} -sdd *sdd_catvprintf(sdd *restrict s, char const *fmt, va_list ap) { - return sdd_impl_catvprintf(s, fmt, ap); -} -sdd *sdd_catprintf(sdd *restrict s, char const *fmt, ...) { - va_list ap; - va_start(ap, fmt); - s = sdd_impl_catvprintf(s, fmt, ap); - va_end(ap); - return s; -} -SDD_NOINLINE -sdd *sdd_ensurecap(sdd *s, size_t new_cap) { - sdd_header *hdr = SDD_HDR(s); - if (new_cap > SDD_CAP_MAX) { - free(hdr); - return NULL; - } - if (hdr->cap >= new_cap) - return s; - return sdd_impl_reallochdr(hdr, new_cap); -} -SDD_NOINLINE -sdd *sdd_makeroomfor(sdd *s, size_t add_len) { - sdd_header *hdr = SDD_HDR(s); - size_t len = hdr->len, cap = hdr->cap; - if (len > SDD_CAP_MAX - add_len) { // overflow, goodnight - free(hdr); - return NULL; - } - size_t new_cap = len + add_len; - if (cap >= new_cap) - return s; - return sdd_impl_reallochdr(hdr, new_cap); -} -size_t sdd_len(sdd const *s) { return SDD_HDR(s)->len; } -size_t sdd_cap(sdd const *s) { return SDD_HDR(s)->cap; } -size_t sdd_avail(sdd const *s) { - sdd_header *h = SDD_HDR(s); - return h->cap - h->len; -} -void sdd_clear(sdd *s) { - SDD_HDR(s)->len = 0; - ((char *)s)[0] = '\0'; -} -void sdd_pokelen(sdd *s, size_t len) { SDD_HDR(s)->len = len; } -void sdd_trim(sdd *restrict s, char const *restrict cut_set) { - char *str, *start, *end, *start_pos, *end_pos; - start_pos = start = str = (char *)s; - end_pos = end = str + SDD_HDR(s)->len - 1; - while (start_pos <= end && strchr(cut_set, *start_pos)) - start_pos++; - while (end_pos > start_pos && strchr(cut_set, *end_pos)) - end_pos--; - size_t len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1); - SDD_HDR(s)->len = len; - if (str != start_pos) - memmove(str, start_pos, len); - str[len] = '\0'; -} - -#undef SDD_HDR -#undef SDD_NOINLINE -#undef SDD_CAP_MAX -#undef SDD_INTERNAL diff --git a/thirdparty/sdd.h b/thirdparty/sdd.h deleted file mode 100644 index 428bb22..0000000 --- a/thirdparty/sdd.h +++ /dev/null @@ -1,104 +0,0 @@ -// Strings, Dynamic, Dumb -#pragma once -#include -#include - -#if (defined(__GNUC__) || defined(__clang__)) && defined(__has_attribute) -#if __has_attribute(format) -#define SDD_PRINTF(n1, n2) __attribute__((format(printf, n1, n2))) -#endif -#if __has_attribute(nonnull) -#define SDD_NONNULL(...) __attribute__((nonnull __VA_ARGS__)) -#endif -#if __has_attribute(malloc) && __has_attribute(warn_unused_result) -#define SDD_ALLOC __attribute__((malloc, warn_unused_result)) -#elif __has_attribute(warn_unused_result) -#define SDD_ALLOC __attribute__((warn_unused_result)) -#endif -#if __has_attribute(warn_unused_result) -#define SDD_USED __attribute__((warn_unused_result)) -#endif -#endif -#ifndef SDD_PRINTF -#define SDD_PRINTF(n1, n2) -#endif -#ifndef SDD_NONNULL -#define SDD_NONNULL(...) -#endif -#ifndef SDD_ALLOC -#define SDD_ALLOC -#endif -#ifndef SDD_USED -#define SDD_USED -#endif - -typedef struct sdd sdd; - -#define sddc(s) ((char *)s) -#define sddcc(s) ((char const *)s) - -sdd *sdd_new(char const *s) SDD_NONNULL() SDD_ALLOC; -// ^- Create new with copy of '\0'-terminated cstring. -sdd *sdd_newlen(char const *s, size_t len) SDD_NONNULL() SDD_ALLOC; -// ^- Same, but without calling strlen(). -// Resulting new string will be '\0'-terminated. -sdd *sdd_newcap(size_t cap) SDD_ALLOC; -// ^- 'Raw' new with a specific capacity. -// Length will be set to 0, and '\0' written at position 0. -sdd *sdd_dup(sdd const *s) SDD_ALLOC; -// ^- Same as sdd_newlen(str, sdd_len(str)) -sdd *sdd_newvprintf(char const *fmt, va_list ap) SDD_ALLOC; -sdd *sdd_newprintf(char const *fmt, ...) SDD_PRINTF(1, 2) SDD_ALLOC; -// ^- Create new by using printf -void sdd_free(sdd *s); -// ^- Calling with null is allowed. - -sdd *sdd_cpy(sdd *restrict s, char const *restrict cstr) SDD_NONNULL() SDD_USED; -// ^- Set `s` to contain the contents of `cstr`. This is really more like -// "change into" rather than "copy". -sdd *sdd_cpylen(sdd *restrict s, char const *restrict cstr, size_t len) - SDD_NONNULL() SDD_USED; -sdd *sdd_cpysdd(sdd *restrict s, sdd const *restrict other); - -sdd *sdd_cat(sdd *restrict s, char const *restrict other) - SDD_NONNULL() SDD_USED; -// ^- Appends contents. The two strings must not overlap in memory. -sdd *sdd_catlen(sdd *restrict s, char const *restrict other, size_t len) - SDD_NONNULL() SDD_USED; -sdd *sdd_catsdd(sdd *restrict s, sdd const *restrict other) - SDD_NONNULL() SDD_USED; -sdd *sdd_catvprintf(sdd *restrict s, char const *fmt, va_list ap) - SDD_NONNULL((1, 2)) SDD_USED; -sdd *sdd_catprintf(sdd *restrict s, char const *fmt, ...) SDD_NONNULL((1, 2)) - SDD_PRINTF(2, 3) SDD_USED; -// ^- Appends by using printf. - -size_t sdd_len(sdd const *s) SDD_NONNULL(); -// ^- Bytes used by string (excluding '\0' terminator) -size_t sdd_cap(sdd const *s) SDD_NONNULL(); -// ^- Bytes allocated on heap (excluding '\0' terminator) -size_t sdd_avail(sdd const *s) SDD_NONNULL(); -// ^- sdd_cap(s) - sdd_len(s) - -void sdd_clear(sdd *s) SDD_NONNULL(); -// ^- Set len to 0, write '\0' at pos 0. Leaves allocated memory in place. -void sdd_pokelen(sdd *s, size_t len) SDD_NONNULL(); -// ^- Manually update length field. Doesn't do anything else for you. - -void sdd_trim(sdd *restrict s, char const *cut_set) SDD_NONNULL(); -// ^- Remove the characters in cut_set from the beginning and ending of s. -sdd *sdd_ensurecap(sdd *s, size_t cap) SDD_NONNULL() SDD_USED; -// ^- Ensure that s has at least cap memory allocated for it. This does not -// care about the strlen of the characters or the prefixed length count -- -// only the backing memory allocation. -sdd *sdd_makeroomfor(sdd *s, size_t add_len) SDD_NONNULL() SDD_USED; -// ^- Ensure that s has enough allocated space after the valid, -// null-terminated characters to hold an additional add_len characters. It -// does not adjust the length, only the capacity, if necessary. Soon after -// you call sdd_makeroomfor(), you probably will want to call sdd_pokelen(), -// otherwise you're probably using it incorrectly. - -#undef SDD_PRINTF -#undef SDD_NONNULL -#undef SDD_ALLOC -#undef SDD_USED diff --git a/tool b/tool index 32b1189..1b65b7d 100755 --- a/tool +++ b/tool @@ -323,7 +323,7 @@ build_target() { out_exe=cli ;; orca|tui) - add source_files osc_out.c term_util.c sysmisc.c thirdparty/sdd.c tui_main.c + add source_files osc_out.c term_util.c sysmisc.c thirdparty/oso.c tui_main.c add cc_flags -D_XOPEN_SOURCE_EXTENDED=1 # thirdparty headers (like sokol_time.h) should get -isystem for their # include dir so that any warnings they generate with our warning flags diff --git a/tui_main.c b/tui_main.c index 0a6c6ca..0cd39e0 100644 --- a/tui_main.c +++ b/tui_main.c @@ -3,7 +3,7 @@ #include "field.h" #include "gbuffer.h" #include "osc_out.h" -#include "sdd.h" +#include "oso.h" #include "sim.h" #include "sysmisc.h" #include "term_util.h" @@ -2134,15 +2134,15 @@ void push_open_form(char const* initial) { qform_push_to_nav(qf); } -bool try_save_with_msg(Field* field, sdd const* str) { - if (!sdd_len(str)) +bool try_save_with_msg(Field* field, oso const* str) { + if (!osolen(str)) return false; - bool ok = hacky_try_save(field, sddc(str)); + bool ok = hacky_try_save(field, osoc(str)); if (ok) { - qmsg_printf_push(NULL, "Saved to:\n%s", sddc(str)); + qmsg_printf_push(NULL, "Saved to:\n%s", osoc(str)); } else { qmsg_printf_push("Error Saving File", "Unable to save file to:\n%s", - sddc(str)); + osoc(str)); } return ok; } @@ -2380,7 +2380,7 @@ int main(int argc, char** argv) { Argopt_portmidi_output_device}, #endif {NULL, 0, NULL, 0}}; - sdd* file_name = NULL; + oso* file_name = NULL; int undo_history_limit = 100; char const* osc_hostname = NULL; char const* osc_port = NULL; @@ -2538,7 +2538,7 @@ int main(int argc, char** argv) { if (optind == argc - 1) { should_autosize_grid = false; - file_name = sdd_new(argv[optind]); + osoput(&file_name, argv[optind]); } else if (optind < argc - 1) { fprintf(stderr, "Expected only 1 file argument.\n"); exit(1); @@ -2572,18 +2572,17 @@ int main(int argc, char** argv) { } } - if (file_name) { - Field_load_error fle = field_load_file(sddc(file_name), &ged_state.field); + if (osolen(file_name)) { + Field_load_error fle = field_load_file(osoc(file_name), &ged_state.field); if (fle != Field_load_error_ok) { char const* errstr = field_load_error_string(fle); fprintf(stderr, "File load error: %s.\n", errstr); ged_deinit(&ged_state); qnav_deinit(); - sdd_free(file_name); + osofree(file_name); exit(1); } } else { - file_name = sdd_newcap(0); // Temp hacky stuff: we've crammed two code paths into the KEY_RESIZE event // case. One of them is for the initial setup for an automatic grid size. // The other is for actual resize events. We will factor this out into @@ -2598,7 +2597,7 @@ int main(int argc, char** argv) { (Usz)init_grid_dim_x, '.'); } } - ged_state.filename = sdd_len(file_name) ? sddc(file_name) : "unnamed"; + ged_state.filename = osolen(file_name) ? osoc(file_name) : "unnamed"; ged_set_midi_mode(&ged_state, &midi_mode); // Set up timer lib @@ -2902,17 +2901,17 @@ int main(int argc, char** argv) { push_confirm_new_file_menu(); break; case Main_menu_open: - push_open_form(sddc(file_name)); + push_open_form(osoc(file_name)); break; case Main_menu_save: - if (sdd_len(file_name) > 0) { + if (osolen(file_name) > 0) { try_save_with_msg(&ged_state.field, file_name); } else { push_save_as_form(""); } break; case Main_menu_save_as: - push_save_as_form(sddc(file_name)); + push_save_as_form(osoc(file_name)); break; case Main_menu_set_tempo: push_set_tempo_form(ged_state.bpm); @@ -2986,7 +2985,7 @@ int main(int argc, char** argv) { ged_make_cursor_visible(&ged_state); ged_state.needs_remarking = true; ged_state.is_draw_dirty = true; - sdd_clear(file_name); + osoclear(&file_name); ged_state.filename = "unnamed"; // slightly redundant qnav_stack_pop(); pop_qnav_if_main_menu(); @@ -3022,17 +3021,17 @@ int main(int argc, char** argv) { case Qform_action_type_submitted: { switch (qform_id(qf)) { case Open_form_id: { - sdd* temp_name = NULL; + oso* temp_name = NULL; if (qform_get_text_line(qf, Open_name_text_line_id, &temp_name) && - sdd_len(temp_name) > 0) { + osolen(temp_name) > 0) { undo_history_push(&ged_state.undo_hist, &ged_state.field, ged_state.tick_num); Field_load_error fle = - field_load_file(sddc(temp_name), &ged_state.field); + field_load_file(osoc(temp_name), &ged_state.field); if (fle == Field_load_error_ok) { qnav_stack_pop(); - file_name = sdd_cpysdd(file_name, temp_name); - ged_state.filename = sddc(file_name); + osoputoso(&file_name, temp_name); + ged_state.filename = osoc(file_name); mbuf_reusable_ensure_size(&ged_state.mbuf_r, ged_state.field.height, ged_state.field.width); @@ -3048,43 +3047,43 @@ int main(int argc, char** argv) { undo_history_pop(&ged_state.undo_hist, &ged_state.field, &ged_state.tick_num); qmsg_printf_push("Error Loading File", "%s:\n%s", - sddc(temp_name), + osoc(temp_name), field_load_error_string(fle)); } } - sdd_free(temp_name); + osofree(temp_name); } break; case Save_as_form_id: { - sdd* temp_name = NULL; + oso* temp_name = NULL; if (qform_get_text_line(qf, Save_as_name_id, &temp_name) && - sdd_len(temp_name) > 0) { + osolen(temp_name) > 0) { qnav_stack_pop(); bool saved_ok = try_save_with_msg(&ged_state.field, temp_name); if (saved_ok) { - file_name = sdd_cpysdd(file_name, temp_name); - ged_state.filename = sddc(file_name); + osoputoso(&file_name, temp_name); + ged_state.filename = osoc(file_name); } } - sdd_free(temp_name); + osofree(temp_name); } break; case Set_tempo_form_id: { - sdd* tmpstr = NULL; + oso* tmpstr = NULL; if (qform_get_text_line(qf, Tempo_text_line_id, &tmpstr) && - sdd_len(tmpstr) > 0) { - int newbpm = atoi(sddc(tmpstr)); + osolen(tmpstr) > 0) { + int newbpm = atoi(osoc(tmpstr)); if (newbpm > 0) { ged_state.bpm = (Usz)newbpm; qnav_stack_pop(); } } - sdd_free(tmpstr); + osofree(tmpstr); } break; case Set_grid_dims_form_id: { - sdd* tmpstr = NULL; + oso* tmpstr = NULL; if (qform_get_text_line(qf, Tempo_text_line_id, &tmpstr) && - sdd_len(tmpstr) > 0) { + osolen(tmpstr) > 0) { int newheight, newwidth; - if (sscanf(sddc(tmpstr), "%dx%d", &newwidth, &newheight) == 2 && + if (sscanf(osoc(tmpstr), "%dx%d", &newwidth, &newheight) == 2 && newheight > 0 && newwidth > 0 && newheight < ORCA_Y_MAX && newwidth < ORCA_X_MAX) { if (ged_state.field.height != (Usz)newheight || @@ -3102,7 +3101,7 @@ int main(int argc, char** argv) { qnav_stack_pop(); } } - sdd_free(tmpstr); + osofree(tmpstr); } break; } } break; @@ -3170,7 +3169,7 @@ int main(int argc, char** argv) { case CTRL_PLUS('q'): goto quit; case CTRL_PLUS('o'): - push_open_form(sddc(file_name)); + push_open_form(osoc(file_name)); break; case KEY_UP: case CTRL_PLUS('k'): @@ -3394,7 +3393,7 @@ int main(int argc, char** argv) { break; case CTRL_PLUS('s'): // TODO duplicated with menu item code - if (sdd_len(file_name) > 0) { + if (osolen(file_name) > 0) { try_save_with_msg(&ged_state.field, file_name); } else { push_save_as_form(""); @@ -3428,7 +3427,7 @@ quit: printf("\033[?2004h\n"); // Tell terminal to not use bracketed paste endwin(); ged_deinit(&ged_state); - sdd_free(file_name); + osofree(file_name); midi_mode_deinit(&midi_mode); #ifdef FEAT_PORTMIDI if (portmidi_is_initialized)