shithub: orca

ref: a5bea69b725259e74e806ae8d3b464c23cb073bf
dir: /thirdparty/sdd.c/

View raw version
#include "sdd.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Derived from gingerBill's public domain gb_string.h file.

/* Examples: */
/* C example */
#if 0
#include "sdd.h"
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
	sdd str = sdd_new("Hello");
	sdd other_str = sdd_newlen(", ", 2);
	str = sdd_catsdd(str, other_str);
	str = sdd_cat(str, "world!");

	printf("%s\n", str); // Hello, world!

	printf("str length = %d\n", sdd_len(str));

	str = sdd_cpy(str, "Potato soup");
	printf("%s\n", str); // Potato soup

	str = sdd_cpy(str, "Hello");
	other_str = sdd_cpy(other_str, "Pizza");
	if (sdd_equal(str, other_str))
		printf("Not called\n");
	else
		printf("Called\n");

	str = sdd_cpy(str, "Ab.;!...AHello World       ??");
	str = sdd_trim(str, "Ab.;!. ?");
	printf("%s\n", str); // "Hello World"

	sdd_free(str);
	sdd_free(other_str);

}
#endif

typedef struct sdd_header {
  size_t len;
  size_t cap;
} sdd_header;

#define SDD_HDR(s) ((sdd_header *)s - 1)

#if defined(__GNUC__) || defined(__clang__)
#define SDD_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#define SDD_NOINLINE __declspec(noinline)
#else
#define SDD_NOINLINE
#endif

static void sdd_setcap(sdd str, size_t cap) { SDD_HDR(str)->cap = cap; }

static SDD_NOINLINE 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 == NULL)
    return NULL;
  vsnprintf(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_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);
  if (len)
    memcpy(str, init_str, len);
  str[len] = '\0';
  return str;
}

sdd sdd_new(char const *str) {
  size_t len = str ? strlen(str) : 0;
  return sdd_newlen(str, 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 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);
}

size_t sdd_len(sdd const str) {
  assert(str);
  return SDD_HDR(str)->len;
}
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;
}

void sdd_clear(sdd str) {
  assert(str);
  SDD_HDR(str)->len = 0;
  str[0] = '\0';
}

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)
    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;
}

sdd sdd_catsdd(sdd str, sdd const other) {
  return sdd_catlen(str, other, SDD_HDR(other)->len);
}

sdd sdd_cat(sdd str, char const *other) {
  return sdd_catlen(str, 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_cpy(sdd str, char const *cstr) {
  return sdd_cpylen(str, cstr, strlen(cstr));
}

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;
}

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;
}

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;
}

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;

  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;
  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;
}

#undef SDD_HDR
#undef SDD_NOINLINE