Browse Source

Redesign sdd.h/.c to use opaque ptr type

master
cancel 5 years ago
parent
commit
9576ebc28d
  1. 269
      thirdparty/sdd.c
  2. 93
      thirdparty/sdd.h

269
thirdparty/sdd.c

@ -1,5 +1,6 @@
#include "sdd.h" #include "sdd.h"
#include <assert.h> #include <assert.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -43,7 +44,7 @@ int main(int argc, char **argv) {
} }
#endif #endif
typedef struct sdd_header { typedef struct sdd {
size_t len; size_t len;
size_t cap; size_t cap;
} sdd_header; } sdd_header;
@ -58,10 +59,36 @@ typedef struct sdd_header {
#define SDD_NOINLINE #define SDD_NOINLINE
#endif #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, SDD_NOINLINE static sdd *sdd_impl_new(char const *init, size_t len,
va_list ap) { 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; size_t old_len;
int required; int required;
va_list cpy; va_list cpy;
@ -75,197 +102,141 @@ static SDD_NOINLINE sdd sdd_impl_catvprintf(sdd s, char const *fmt,
old_len = 0; old_len = 0;
s = sdd_newcap((size_t)required); s = sdd_newcap((size_t)required);
} }
if (s == NULL) if (!s)
return NULL; 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; SDD_HDR(s)->len = old_len + (size_t)required;
return s; return s;
} }
sdd sdd_newcap(size_t cap) { sdd *sdd_new(char const *str) {
sdd_header *header; size_t len = strlen(str);
char *str; return sdd_impl_new(str, len, len);
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_newlen(char const *init, size_t len) {
sdd sdd_newlen(void const *init_str, size_t len) { return sdd_impl_new(init, len, 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_newcap(size_t cap) { return sdd_impl_new(NULL, 0, cap); }
sdd sdd_new(char const *str) { return sdd_newlen(str, strlen(str)); } sdd *sdd_dup(sdd const *str) {
size_t len = SDD_HDR(str)->len;
sdd sdd_newvprintf(char const *fmt, va_list ap) { 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); return sdd_impl_catvprintf(NULL, fmt, ap);
} }
sdd sdd_newprintf(char const *fmt, ...) { sdd *sdd_newprintf(char const *fmt, ...) {
sdd s; sdd *s;
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
s = sdd_impl_catvprintf(NULL, fmt, ap); s = sdd_impl_catvprintf(NULL, fmt, ap);
va_end(ap); va_end(ap);
return s; return s;
} }
void sdd_free(sdd str) {
if (str == NULL)
return;
free((sdd_header *)str - 1);
}
sdd sdd_dup(sdd const str) { void sdd_free(sdd *s) {
assert(str); if (!s)
return sdd_newlen(str, SDD_HDR(str)->len); return;
free(s - 1);
} }
size_t sdd_len(sdd const str) { sdd *sdd_cpy(sdd *s, char const *restrict cstr) {
assert(str); return sdd_cpylen(s, cstr, strlen(cstr));
return SDD_HDR(str)->len;
} }
size_t sdd_cap(sdd const str) { sdd *sdd_cpylen(sdd *s, char const *restrict cstr, size_t len) {
assert(str); s = sdd_ensurecap(s, len);
return SDD_HDR(str)->cap; if (!s)
} return NULL;
SDD_HDR(s)->len = len;
size_t sdd_avail(sdd const str) { memcpy(s, cstr, len);
assert(str); ((char *)s)[len] = '\0';
sdd_header *h = SDD_HDR(str); return s;
return h->cap - h->len;
} }
void sdd_clear(sdd str) { SDD_NOINLINE
assert(str); sdd *sdd_ensurecap(sdd *s, size_t new_cap) {
SDD_HDR(str)->len = 0; sdd_header *hdr = SDD_HDR(s);
str[0] = '\0'; 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) { SDD_NOINLINE
assert(str); sdd *sdd_makeroomfor(sdd *s, size_t add_len) {
size_t curr_len = SDD_HDR(str)->len; sdd_header *hdr = SDD_HDR(s);
str = sdd_makeroomfor(str, other_len); size_t len = hdr->len, cap = hdr->cap;
if (str == NULL) if (len > SDD_CAP_MAX - add_len) { // overflow, goodnight
free(hdr);
return NULL; return NULL;
memcpy(str + curr_len, other, other_len); }
str[curr_len + other_len] = '\0'; size_t new_cap = len + add_len;
SDD_HDR(str)->len = curr_len + other_len; if (cap >= new_cap) /* Return if there is enough space left */
return str; return s;
return sdd_impl_realloc_hdr(hdr, new_cap);
} }
sdd sdd_catsdd(sdd str, sdd const other) { size_t sdd_len(sdd const *s) { return SDD_HDR(s)->len; }
return sdd_catlen(str, other, SDD_HDR(other)->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) { sdd *sdd_cat(sdd *s, char const *restrict other) {
return sdd_catlen(str, other, strlen(other)); return sdd_catlen(s, other, strlen(other));
} }
sdd *sdd_catlen(sdd *s, char const *restrict other, size_t other_len) {
sdd sdd_cpylen(sdd str, char const *cstr, size_t len) { size_t curr_len = SDD_HDR(s)->len;
if (sdd_cap(str) < len) { s = sdd_makeroomfor(s, other_len);
str = sdd_makeroomfor(str, len - SDD_HDR(str)->len); if (!s)
if (str == NULL) return NULL;
return NULL; memcpy((char *)s + curr_len, other, other_len);
} ((char *)s)[curr_len + other_len] = '\0';
SDD_HDR(str)->len = len; SDD_HDR(s)->len = curr_len + other_len;
memcpy(str, cstr, len); return s;
str[len] = '\0';
return str;
} }
sdd sdd_cpy(sdd str, char const *cstr) { sdd *sdd_catsdd(sdd *s, sdd const *restrict other) {
return sdd_cpylen(str, cstr, strlen(cstr)); return sdd_catlen(s, (char const *)other, SDD_HDR(other)->len);
} }
sdd *sdd_catvprintf(sdd *s, char const *fmt, va_list ap) {
sdd sdd_makeroomfor(sdd str, size_t add_len) { return sdd_impl_catvprintf(s, fmt, ap);
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_catprintf(sdd *s, char const *fmt, ...) {
void sdd_pokelen(sdd str, size_t len) { SDD_HDR(str)->len = len; } va_list ap;
va_start(ap, fmt);
size_t sdd_totalmemused(sdd const s) { s = sdd_impl_catvprintf(s, fmt, ap);
size_t cap = sdd_cap(s); va_end(ap);
return sizeof(sdd_header) + cap; return s;
} }
bool sdd_equal(sdd const lhs, sdd const rhs) { void sdd_clear(sdd *s) {
size_t lhs_len = SDD_HDR(lhs)->len; SDD_HDR(s)->len = 0;
size_t rhs_len = SDD_HDR(rhs)->len; ((char *)s)[0] = '\0';
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;
} }
sdd sdd_trim(sdd str, char const *cut_set) { void sdd_pokelen(sdd *s, size_t len) { SDD_HDR(s)->len = len; }
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_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)) while (start_pos <= end && strchr(cut_set, *start_pos))
start_pos++; start_pos++;
while (end_pos > start_pos && strchr(cut_set, *end_pos)) while (end_pos > start_pos && strchr(cut_set, *end_pos))
end_pos--; end_pos--;
size_t len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1);
len = (start_pos > end_pos) ? 0 : ((size_t)(end_pos - start_pos) + 1); SDD_HDR(s)->len = len;
SDD_HDR(str)->len = len;
if (str != start_pos) if (str != start_pos)
memmove(str, start_pos, len); memmove(str, start_pos, len);
str[len] = '\0'; 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, ...) { #if 0
assert(s != NULL); #endif
va_list ap;
va_start(ap, fmt);
s = sdd_impl_catvprintf(s, fmt, ap);
va_end(ap);
return s;
}
#undef SDD_HDR #undef SDD_HDR
#undef SDD_NOINLINE #undef SDD_NOINLINE
#undef SDD_CAP_MAX

93
thirdparty/sdd.h

@ -6,61 +6,80 @@
#ifdef __GNUC__ #ifdef __GNUC__
#define SDD_PRINTF(n1, n2) __attribute__((format(printf, n1, n2))) #define SDD_PRINTF(n1, n2) __attribute__((format(printf, n1, n2)))
#define SDD_NONNULL(...) __attribute__((nonnull __VA_ARGS__)) #define SDD_NONNULL(...) __attribute__((nonnull __VA_ARGS__))
#define SDD_ALLOCS __attribute__((malloc, warn_unused_result)) #define SDD_ALLOC __attribute__((malloc, warn_unused_result))
#define SDD_RESULT __attribute__((warn_unused_result)) #define SDD_USED __attribute__((warn_unused_result))
#else #else
#define SDD_PRINTF(n1, n2) #define SDD_PRINTF(n1, n2)
#define SDD_NONNULL #define SDD_NONNULL
#define SDD_ALLOCS #define SDD_ALLOC
#define SDD_RESULT #define SDD_USED
#endif #endif
typedef char *sdd; typedef struct sdd sdd;
sdd sdd_new(char const *str) SDD_NONNULL() SDD_ALLOCS; #define sddc(s) ((char *)s)
// ^- Create new with copy of null-terminated cstring. #define sddcc(s) ((char const *)s)
sdd sdd_newlen(void const *str, size_t len) SDD_NONNULL() SDD_ALLOCS;
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(). // ^- Same, but without calling strlen().
// Resulting new string will be null terminated. // Resulting new string will be '\0'-terminated.
sdd sdd_newcap(size_t cap) SDD_ALLOCS; sdd *sdd_newcap(size_t cap) SDD_ALLOC;
// ^- 'Raw' new with a specific capacity. // ^- 'Raw' new with a specific capacity.
// Length will be set to 0, and '\0' written at position 0. // 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)) // ^- Same as sdd_newlen(str, sdd_len(str))
sdd sdd_newvprintf(char const *fmt, va_list ap) SDD_ALLOCS; sdd *sdd_newvprintf(char const *fmt, va_list ap) SDD_ALLOC;
sdd sdd_newprintf(char const *fmt, ...) SDD_PRINTF(1, 2) SDD_ALLOCS; sdd *sdd_newprintf(char const *fmt, ...) SDD_PRINTF(1, 2) SDD_ALLOC;
void sdd_free(sdd str); // ^- 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; sdd *sdd_cpy(sdd *restrict s, char const *restrict cstr) SDD_NONNULL() SDD_USED;
// ^- Set `str` to contain the contents of `cstr` // ^- Set `s` to contain the contents of `cstr`
sdd sdd_cpylen(sdd str, char const *cstr, size_t len) SDD_RESULT; 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) // ^- 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) // ^- Bytes allocated on heap (excl. null term)
size_t sdd_avail(sdd const str) SDD_NONNULL(); size_t sdd_avail(sdd const *s) SDD_NONNULL();
// ^- cap - len // ^- sdd_cap(s) - sdd_len(s)
sdd sdd_cat(sdd str, char const *restrict other) SDD_NONNULL() SDD_RESULT; sdd *sdd_cat(sdd *restrict s, char const *restrict other)
sdd sdd_catlen(sdd str, char const *restrict other, size_t len) SDD_RESULT; SDD_NONNULL() SDD_USED;
sdd sdd_catsdd(sdd str, sdd restrict const other) SDD_RESULT; // ^- Appends contents. The two strings must not overlap.
sdd sdd_catvprintf(sdd str, char const *fmt, va_list ap) SDD_RESULT; sdd *sdd_catlen(sdd *restrict s, char const *restrict other, size_t len)
sdd sdd_catprintf(sdd str, char const *fmt, ...) SDD_PRINTF(2, 3) SDD_RESULT; 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 void sdd_clear(sdd *s) SDD_NONNULL();
sdd sdd_makeroomfor(sdd str, size_t add_len) SDD_NONNULL() SDD_RESULT; // ^- Set len to 0, write '\0' at pos 0. Leaves allocated memory in place.
// ^- Makes sure void sdd_pokelen(sdd *s, size_t len) SDD_NONNULL();
void sdd_pokelen(sdd str, size_t len) SDD_NONNULL();
// ^- Manually update length field. Doesn't do anything else for you. // ^- Manually update length field. Doesn't do anything else for you.
bool sdd_equal(sdd const lhs, sdd const rhs) SDD_NONNULL(); 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_trim(sdd str, char const *cut_set) SDD_RESULT SDD_NONNULL(); 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
size_t sdd_totalmemused(sdd const str); // 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_PRINTF
#undef SDD_NONNULL #undef SDD_NONNULL
#undef SDD_ALLOCS #undef SDD_ALLOC
#undef SDD_RESULT #undef SDD_USED

Loading…
Cancel
Save