/* Growable String Buffers * (c) Mark A. Sheldon, Spring 2007 */ #include #include #include /* #include "string_buffer.h" ommitted because of conflicting typedef, and I don't want to cast everything. */ #define INITIAL_BUFFSIZE 1 #define MAX(x, y) (x) > (y) ? (x) : (y) typedef struct strbuff { char *contents; int size, capacity; } *strbuff_t; strbuff_t mk_strbuff(void) { strbuff_t sb = (strbuff_t) malloc(sizeof(struct strbuff)); if (sb == NULL) return NULL; sb->contents = malloc(INITIAL_BUFFSIZE); if (sb->contents == NULL) { free(sb); return NULL; } sb->size = 0; sb->capacity = INITIAL_BUFFSIZE; return sb; } inline int strbuff_size(strbuff_t sb) { return (sb == NULL) ? -1 : sb->size; } inline int strbuff_capacity(strbuff_t sb) { return (sb == NULL) ? -1 : sb->capacity; } static inline char *strbuff_contents(strbuff_t sb) { return (sb == NULL) ? NULL : sb->contents; } char *strbuff_to_string(strbuff_t sb) { int size; char *s; if (sb == NULL) return NULL; size = strbuff_size(sb); s = malloc(size + 1); if (s == NULL) return NULL; memcpy(s, sb->contents, size); s[size] = '\0'; return s; } /* Follows the Java StringBuffer model: if new capacity is greater than current capacity, then allocate space equal to larger of new capacity or 2 + twice the current capacity. */ int strbuff_ensure_capacity(strbuff_t sb, int new_cap) { int curr_cap, req_cap; char *new_contents; if (sb == NULL) return EINVAL; curr_cap = strbuff_capacity(sb); if (curr_cap >= new_cap) /* quietly succeeds if new_cap < 0 */ return 0; req_cap = MAX(new_cap, (2 * curr_cap) + 2); new_contents = (char *) malloc(req_cap); if (new_contents == NULL) return ENOMEM; memcpy(new_contents, strbuff_contents(sb), strbuff_size(sb)); free(strbuff_contents(sb)); sb->contents = new_contents; sb->capacity = req_cap; return 0; } int strbuff_append(strbuff_t sb, char c) { int ret; if (sb == NULL) return EINVAL; if ((ret = strbuff_ensure_capacity(sb, strbuff_size(sb) + 1)) != 0) return ret; sb->contents[sb->size] = c; sb->size++; return 0; } int strbuff_append_str(strbuff_t sb, char *s) { int len, size, ret; if ( (sb == NULL) || (s == NULL) ) return EINVAL; len = strlen(s); size = strbuff_size(sb); ret = strbuff_ensure_capacity(sb, size + len); if (ret != 0) return ret; memcpy(&strbuff_contents(sb)[size], s, len); sb->size += len; return 0; } int strbuff_getchar(strbuff_t sb, int index) { if ((sb == NULL) || (index < 0) || (index >= strbuff_size(sb))) return -1; return strbuff_contents(sb)[index]; } int strbuff_setchar(strbuff_t sb, char c, int index) { if ((sb == NULL) || (index < 0) || (index >= strbuff_size(sb))) return -1; return strbuff_contents(sb)[index] = c; } /* Note: Does not actually free space, just resets size so current contents array is reused. */ int strbuff_truncate(strbuff_t sb, int newsize) { if (sb == NULL) return EINVAL; if ((newsize >= 0) && (newsize <= strbuff_size(sb))) { sb->size = newsize; return 0; } else return EINVAL; } void strbuff_destroy(strbuff_t sb) { if (sb == NULL) return; free(strbuff_contents(sb)); /* Invariant: contents never NULL */ free(sb); }