shithub: orca

ref: fdc0e798d589272e3bea4a267935f0b94b9a27fb
dir: /thirdparty/gbstring.c/

View raw version
#include "gbstring.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 "gbstring.h"
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
	gbs str = gbs_new("Hello");
	gbs other_str = gbs_newlen(", ", 2);
	str = gbs_catgbs(str, other_str);
	str = gbs_cat(str, "world!");

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

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

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

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

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

	gbs_free(str);
	gbs_free(other_str);

}
#endif

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

#define GB_STRING_HEADER(s) ((gbStringHeader *)s - 1)

static void gbs_setlen(gbs str, size_t len) {
  GB_STRING_HEADER(str)->len = len;
}

static void gbs_setcap(gbs str, size_t cap) {
  GB_STRING_HEADER(str)->cap = cap;
}
gbs gbs_empty(void) { return gbs_newlen(NULL, 0); }

gbs gbs_newlen(void const *init_str, size_t len) {
  gbs str;
  gbStringHeader *header;
  size_t header_size = sizeof(gbStringHeader);
  void *ptr = malloc(header_size + len + 1);
  if (ptr == NULL)
    return NULL;
  if (!init_str)
    memset(ptr, 0, header_size + len + 1);

  str = (char *)ptr + header_size;
  header = GB_STRING_HEADER(str);
  header->len = len;
  header->cap = len;
  if (len && init_str)
    memcpy(str, init_str, len);
  str[len] = '\0';

  return str;
}

gbs gbs_new(char const *str) {
  size_t len = str ? strlen(str) : 0;
  return gbs_newlen(str, len);
}

void gbs_free(gbs str) {
  if (str == NULL)
    return;

  free((gbStringHeader *)str - 1);
}

gbs gbs_dup(gbs const str) { return gbs_newlen(str, gbs_len(str)); }

size_t gbs_len(gbs const str) { return GB_STRING_HEADER(str)->len; }

size_t gbs_cap(gbs const str) { return GB_STRING_HEADER(str)->cap; }

size_t gbs_avail(gbs const str) {
  gbStringHeader *h = GB_STRING_HEADER(str);
  if (h->cap > h->len)
    return h->cap - h->len;
  return 0;
}

void gbs_clear(gbs str) {
  gbs_setlen(str, 0);
  str[0] = '\0';
}

gbs gbs_catlen(gbs str, void const *other, size_t other_len) {
  size_t curr_len = gbs_len(str);

  str = gbs_makeroomfor(str, other_len);
  if (str == NULL)
    return NULL;

  memcpy(str + curr_len, other, other_len);
  str[curr_len + other_len] = '\0';
  gbs_setlen(str, curr_len + other_len);

  return str;
}

gbs gbs_catgbs(gbs str, gbs const other) {
  return gbs_catlen(str, other, gbs_len(other));
}

gbs gbs_cat(gbs str, char const *other) {
  return gbs_catlen(str, other, strlen(other));
}

gbs gbs_cpylen(gbs str, char const *cstr, size_t len) {
  if (gbs_cap(str) < len) {
    str = gbs_makeroomfor(str, len - gbs_len(str));
    if (str == NULL)
      return NULL;
  }

  memcpy(str, cstr, len);
  str[len] = '\0';
  gbs_setlen(str, len);

  return str;
}
gbs gbs_cpy(gbs str, char const *cstr) {
  return gbs_cpylen(str, cstr, strlen(cstr));
}

gbs gbs_makeroomfor(gbs str, size_t add_len) {
  size_t len = gbs_len(str);
  size_t new_len = len + add_len;
  void *ptr, *new_ptr;
  size_t available, new_size;

  available = gbs_avail(str);
  if (available >= add_len) /* Return if there is enough space left */
    return str;
  ptr = (char *)str - sizeof(gbStringHeader);
  new_size = sizeof(gbStringHeader) + new_len + 1;
  new_ptr = realloc(ptr, new_size);
  if (new_ptr == NULL) {
    free(ptr);
    return NULL;
  }
  str = (char *)new_ptr + sizeof(gbStringHeader);
  gbs_setcap(str, new_len);
  return str;
}

size_t gbs_totalmemused(gbs const s) {
  size_t cap = gbs_cap(s);
  return sizeof(gbStringHeader) + cap;
}

bool gbs_streq(gbs const lhs, gbs const rhs) {
  size_t lhs_len, rhs_len, i;
  lhs_len = gbs_len(lhs);
  rhs_len = gbs_len(rhs);
  if (lhs_len != rhs_len)
    return false;

  for (i = 0; i < lhs_len; i++) {
    if (lhs[i] != rhs[i])
      return false;
  }

  return true;
}

gbs gbs_trim(gbs str, char const *cut_set) {
  char *start, *end, *start_pos, *end_pos;
  size_t len;

  start_pos = start = str;
  end_pos = end = str + gbs_len(str) - 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);

  if (str != start_pos)
    memmove(str, start_pos, len);
  str[len] = '\0';

  gbs_setlen(str, len);

  return str;
}

gbs gbs_catvprintf(gbs s, const char *fmt, va_list ap) {
  va_list cpy;
  va_copy(cpy, ap);
  int required = vsnprintf(NULL, 0, fmt, cpy);
  va_end(cpy);
  s = gbs_makeroomfor(s, (size_t)required);
  if (s == NULL)
    return NULL;
  va_copy(cpy, ap);
  vsnprintf(s, (size_t)required + 1, fmt, cpy);
  va_end(cpy);
  return s;
}

gbs gbs_catprintf(gbs s, char const *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  s = gbs_catvprintf(s, fmt, ap);
  va_end(ap);
  return s;
}

#undef GB_STRING_HEADER