From 9576ebc28dfe843a32a5bed8cebc0707974fbc6c Mon Sep 17 00:00:00 2001 From: cancel Date: Wed, 8 Jan 2020 05:48:05 +0900 Subject: [PATCH] Redesign sdd.h/.c to use opaque ptr type --- thirdparty/sdd.c | 269 +++++++++++++++++++++-------------------------- thirdparty/sdd.h | 93 +++++++++------- 2 files changed, 176 insertions(+), 186 deletions(-) diff --git a/thirdparty/sdd.c b/thirdparty/sdd.c index a36ae2c..d49e268 100644 --- a/thirdparty/sdd.c +++ b/thirdparty/sdd.c @@ -1,5 +1,6 @@ #include "sdd.h" #include +#include #include #include #include @@ -43,7 +44,7 @@ int main(int argc, char **argv) { } #endif -typedef struct sdd_header { +typedef struct sdd { size_t len; size_t cap; } sdd_header; @@ -58,10 +59,36 @@ typedef struct sdd_header { #define SDD_NOINLINE #endif -static void sdd_setcap(sdd str, size_t cap) { SDD_HDR(str)->cap = cap; } +#define SDD_CAP_MAX (SIZE_MAX - (sizeof(sdd_header) + 1)) -static SDD_NOINLINE sdd sdd_impl_catvprintf(sdd s, char const *fmt, - va_list ap) { +SDD_NOINLINE static 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_NOINLINE static sdd *sdd_impl_realloc_hdr(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; +} + +static SDD_NOINLINE sdd *sdd_impl_catvprintf(sdd *s, char const *fmt, + va_list ap) { size_t old_len; int required; va_list cpy; @@ -75,197 +102,141 @@ static SDD_NOINLINE sdd sdd_impl_catvprintf(sdd s, char const *fmt, old_len = 0; s = sdd_newcap((size_t)required); } - if (s == NULL) + if (!s) return NULL; - vsnprintf(s + old_len, (size_t)required + 1, fmt, ap); + vsnprintf((char *)s + old_len, (size_t)required + 1, fmt, ap); SDD_HDR(s)->len = old_len + (size_t)required; return s; } -sdd sdd_newcap(size_t cap) { - sdd_header *header; - char *str; - header = (sdd_header *)malloc(sizeof(sdd_header) + cap + 1); - if (!header) - return NULL; - header->len = 0; - header->cap = cap; - str = (char *)(header + 1); - *str = '\0'; - return str; +sdd *sdd_new(char const *str) { + size_t len = strlen(str); + return sdd_impl_new(str, len, len); } - -sdd sdd_newlen(void const *init_str, size_t len) { - sdd_header *header; - char *str; - header = (sdd_header *)malloc(sizeof(sdd_header) + len + 1); - if (!header) - return NULL; - header->len = len; - header->cap = len; - str = (char *)(header + 1); - memcpy(str, init_str, len); - str[len] = '\0'; - return str; +sdd *sdd_newlen(char const *init, size_t len) { + return sdd_impl_new(init, len, len); } - -sdd sdd_new(char const *str) { return sdd_newlen(str, strlen(str)); } - -sdd sdd_newvprintf(char const *fmt, va_list ap) { +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; +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 str) { - if (str == NULL) - return; - free((sdd_header *)str - 1); -} -sdd sdd_dup(sdd const str) { - assert(str); - return sdd_newlen(str, SDD_HDR(str)->len); +void sdd_free(sdd *s) { + if (!s) + return; + free(s - 1); } -size_t sdd_len(sdd const str) { - assert(str); - return SDD_HDR(str)->len; +sdd *sdd_cpy(sdd *s, char const *restrict cstr) { + return sdd_cpylen(s, cstr, strlen(cstr)); } -size_t sdd_cap(sdd const str) { - assert(str); - return SDD_HDR(str)->cap; -} - -size_t sdd_avail(sdd const str) { - assert(str); - sdd_header *h = SDD_HDR(str); - return h->cap - h->len; +sdd *sdd_cpylen(sdd *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; } -void sdd_clear(sdd str) { - assert(str); - SDD_HDR(str)->len = 0; - str[0] = '\0'; +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_realloc_hdr(hdr, new_cap); } -sdd sdd_catlen(sdd str, char const *other, size_t other_len) { - assert(str); - size_t curr_len = SDD_HDR(str)->len; - str = sdd_makeroomfor(str, other_len); - if (str == NULL) +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; - memcpy(str + curr_len, other, other_len); - str[curr_len + other_len] = '\0'; - SDD_HDR(str)->len = curr_len + other_len; - return str; + } + size_t new_cap = len + add_len; + if (cap >= new_cap) /* Return if there is enough space left */ + return s; + return sdd_impl_realloc_hdr(hdr, new_cap); } -sdd sdd_catsdd(sdd str, sdd const other) { - return sdd_catlen(str, other, SDD_HDR(other)->len); +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; } -sdd sdd_cat(sdd str, char const *other) { - return sdd_catlen(str, other, strlen(other)); +sdd *sdd_cat(sdd *s, char const *restrict other) { + return sdd_catlen(s, other, strlen(other)); } - -sdd sdd_cpylen(sdd str, char const *cstr, size_t len) { - if (sdd_cap(str) < len) { - str = sdd_makeroomfor(str, len - SDD_HDR(str)->len); - if (str == NULL) - return NULL; - } - SDD_HDR(str)->len = len; - memcpy(str, cstr, len); - str[len] = '\0'; - return str; +sdd *sdd_catlen(sdd *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_cpy(sdd str, char const *cstr) { - return sdd_cpylen(str, cstr, strlen(cstr)); +sdd *sdd_catsdd(sdd *s, sdd const *restrict other) { + return sdd_catlen(s, (char const *)other, SDD_HDR(other)->len); } - -sdd sdd_makeroomfor(sdd str, size_t add_len) { - size_t len = SDD_HDR(str)->len; - size_t new_len = len + add_len; // TODO overflow check - void *ptr, *new_ptr; - size_t available, new_size; - - available = sdd_avail(str); - if (available >= add_len) /* Return if there is enough space left */ - return str; - ptr = (char *)str - sizeof(sdd_header); - new_size = sizeof(sdd_header) + new_len + 1; - new_ptr = realloc(ptr, new_size); - if (new_ptr == NULL) { - free(ptr); - return NULL; - } - str = (char *)new_ptr + sizeof(sdd_header); - sdd_setcap(str, new_len); - return str; +sdd *sdd_catvprintf(sdd *s, char const *fmt, va_list ap) { + return sdd_impl_catvprintf(s, fmt, ap); } - -void sdd_pokelen(sdd str, size_t len) { SDD_HDR(str)->len = len; } - -size_t sdd_totalmemused(sdd const s) { - size_t cap = sdd_cap(s); - return sizeof(sdd_header) + cap; +sdd *sdd_catprintf(sdd *s, char const *fmt, ...) { + va_list ap; + va_start(ap, fmt); + s = sdd_impl_catvprintf(s, fmt, ap); + va_end(ap); + return s; } -bool sdd_equal(sdd const lhs, sdd const rhs) { - size_t lhs_len = SDD_HDR(lhs)->len; - size_t rhs_len = SDD_HDR(rhs)->len; - if (lhs_len != rhs_len) - return false; - for (size_t i = 0; i < lhs_len; i++) { - if (lhs[i] != rhs[i]) - return false; - } - return true; +void sdd_clear(sdd *s) { + SDD_HDR(s)->len = 0; + ((char *)s)[0] = '\0'; } -sdd sdd_trim(sdd str, char const *cut_set) { - char *start, *end, *start_pos, *end_pos; - size_t len; - - start_pos = start = str; - end_pos = end = str + SDD_HDR(str)->len - 1; +void sdd_pokelen(sdd *s, size_t len) { SDD_HDR(s)->len = len; } +void sdd_trim(sdd *s, char const *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--; - - len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1); - - SDD_HDR(str)->len = len; + 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'; - return str; -} - -sdd sdd_catvprintf(sdd s, char const *fmt, va_list ap) { - // not sure if we should make exception for cat_* functions to allow cat'ing - // to null pointer. we should see if it ends up being useful in code, or if - // we should just match the existing behavior of sds/gb_string. - assert(s != NULL); - return sdd_impl_catvprintf(s, fmt, ap); } -sdd sdd_catprintf(sdd s, char const *fmt, ...) { - assert(s != NULL); - va_list ap; - va_start(ap, fmt); - s = sdd_impl_catvprintf(s, fmt, ap); - va_end(ap); - return s; -} +#if 0 +#endif #undef SDD_HDR #undef SDD_NOINLINE +#undef SDD_CAP_MAX diff --git a/thirdparty/sdd.h b/thirdparty/sdd.h index e94f95c..cd33450 100644 --- a/thirdparty/sdd.h +++ b/thirdparty/sdd.h @@ -6,61 +6,80 @@ #ifdef __GNUC__ #define SDD_PRINTF(n1, n2) __attribute__((format(printf, n1, n2))) #define SDD_NONNULL(...) __attribute__((nonnull __VA_ARGS__)) -#define SDD_ALLOCS __attribute__((malloc, warn_unused_result)) -#define SDD_RESULT __attribute__((warn_unused_result)) +#define SDD_ALLOC __attribute__((malloc, warn_unused_result)) +#define SDD_USED __attribute__((warn_unused_result)) #else #define SDD_PRINTF(n1, n2) #define SDD_NONNULL -#define SDD_ALLOCS -#define SDD_RESULT +#define SDD_ALLOC +#define SDD_USED #endif -typedef char *sdd; +typedef struct sdd sdd; -sdd sdd_new(char const *str) SDD_NONNULL() SDD_ALLOCS; -// ^- Create new with copy of null-terminated cstring. -sdd sdd_newlen(void const *str, size_t len) SDD_NONNULL() SDD_ALLOCS; +#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 null terminated. -sdd sdd_newcap(size_t cap) SDD_ALLOCS; +// 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 str) SDD_ALLOCS; +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_ALLOCS; -sdd sdd_newprintf(char const *fmt, ...) SDD_PRINTF(1, 2) SDD_ALLOCS; -void sdd_free(sdd 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 str, char const *cstr) SDD_RESULT; -// ^- Set `str` to contain the contents of `cstr` -sdd sdd_cpylen(sdd str, char const *cstr, size_t len) SDD_RESULT; +sdd *sdd_cpy(sdd *restrict s, char const *restrict cstr) SDD_NONNULL() SDD_USED; +// ^- Set `s` to contain the contents of `cstr` +sdd *sdd_cpylen(sdd *restrict s, char const *restrict cstr, size_t len) + SDD_NONNULL() SDD_USED; -size_t sdd_len(sdd const str) SDD_NONNULL(); +size_t sdd_len(sdd const *s) SDD_NONNULL(); // ^- Bytes used by string (excl. null term) -size_t sdd_cap(sdd const str) SDD_NONNULL(); +size_t sdd_cap(sdd const *s) SDD_NONNULL(); // ^- Bytes allocated on heap (excl. null term) -size_t sdd_avail(sdd const str) SDD_NONNULL(); -// ^- cap - len +size_t sdd_avail(sdd const *s) SDD_NONNULL(); +// ^- sdd_cap(s) - sdd_len(s) -sdd sdd_cat(sdd str, char const *restrict other) SDD_NONNULL() SDD_RESULT; -sdd sdd_catlen(sdd str, char const *restrict other, size_t len) SDD_RESULT; -sdd sdd_catsdd(sdd str, sdd restrict const other) SDD_RESULT; -sdd sdd_catvprintf(sdd str, char const *fmt, va_list ap) SDD_RESULT; -sdd sdd_catprintf(sdd str, char const *fmt, ...) SDD_PRINTF(2, 3) SDD_RESULT; +sdd *sdd_cat(sdd *restrict s, char const *restrict other) + SDD_NONNULL() SDD_USED; +// ^- Appends contents. The two strings must not overlap. +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. -void sdd_clear(sdd str) SDD_NONNULL(); // Set len to 0, write '\0' at pos 0 -sdd sdd_makeroomfor(sdd str, size_t add_len) SDD_NONNULL() SDD_RESULT; -// ^- Makes sure -void sdd_pokelen(sdd str, size_t len) SDD_NONNULL(); +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. -bool sdd_equal(sdd const lhs, sdd const rhs) SDD_NONNULL(); - -sdd sdd_trim(sdd str, char const *cut_set) SDD_RESULT SDD_NONNULL(); - -size_t sdd_totalmemused(sdd const str); +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 need to call sdd_pokelen(), +// otherwise you're probably using it incorrectly. #undef SDD_PRINTF #undef SDD_NONNULL -#undef SDD_ALLOCS -#undef SDD_RESULT +#undef SDD_ALLOC +#undef SDD_USED