|
@ -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 |
|
|