/* $Id: vector.c 6699 2004-04-07 06:47:44Z rra $ ** ** Vector handling (counted lists of char *'s). ** ** Written by Russ Allbery ** This work is hereby placed in the public domain by its author. ** ** A vector is a table for handling a list of strings with less overhead than ** linked list. The intention is for vectors, once allocated, to be reused; ** this saves on memory allocations once the array of char *'s reaches a ** stable size. ** ** There are two types of vectors. Standard vectors copy strings when ** they're inserted into the vector, whereas cvectors just accept pointers ** to external strings to store. There are therefore two entry points for ** every vector function, one for vectors and one for cvectors. ** ** There's a whole bunch of code duplication here. This would be a lot ** cleaner with C++ features (either inheritance or templates would ** probably help). One could probably in some places just cast a cvector ** to a vector and perform the same operations, but I'm leery of doing that ** as I'm not sure if it's a violation of the C type aliasing rules. */ #include "config.h" #include "clibrary.h" #include #include "inn/vector.h" #include "libinn.h" /* ** Allocate a new, empty vector. */ struct vector * vector_new(void) { struct vector *vector; vector = xmalloc(sizeof(struct vector)); vector->count = 0; vector->allocated = 0; vector->strings = NULL; return vector; } struct cvector * cvector_new(void) { struct cvector *vector; vector = xmalloc(sizeof(struct cvector)); vector->count = 0; vector->allocated = 0; vector->strings = NULL; return vector; } /* ** Resize a vector (using realloc to resize the table). */ void vector_resize(struct vector *vector, size_t size) { size_t i; if (vector->count > size) { for (i = size; i < vector->count; i++) free(vector->strings[i]); vector->count = size; } if (size == 0) { free(vector->strings); vector->strings = NULL; } else { vector->strings = xrealloc(vector->strings, size * sizeof(char *)); } vector->allocated = size; } void cvector_resize(struct cvector *vector, size_t size) { if (vector->count > size) vector->count = size; if (size == 0) { free(vector->strings); vector->strings = NULL; } else { vector->strings = xrealloc(vector->strings, size * sizeof(const char *)); } vector->allocated = size; } /* ** Add a new string to the vector, resizing the vector as necessary. The ** vector is resized an element at a time; if a lot of resizes are expected, ** vector_resize should be called explicitly with a more suitable size. */ void vector_add(struct vector *vector, const char *string) { size_t next = vector->count; if (vector->count == vector->allocated) vector_resize(vector, vector->allocated + 1); vector->strings[next] = xstrdup(string); vector->count++; } void cvector_add(struct cvector *vector, const char *string) { size_t next = vector->count; if (vector->count == vector->allocated) cvector_resize(vector, vector->allocated + 1); vector->strings[next] = string; vector->count++; } /* ** Empty a vector but keep the allocated memory for the pointer table. */ void vector_clear(struct vector *vector) { size_t i; for (i = 0; i < vector->count; i++) free(vector->strings[i]); vector->count = 0; } void cvector_clear(struct cvector *vector) { vector->count = 0; } /* ** Free a vector completely. */ void vector_free(struct vector *vector) { vector_clear(vector); free(vector->strings); free(vector); } void cvector_free(struct cvector *vector) { cvector_clear(vector); free(vector->strings); free(vector); } /* ** Given a vector that we may be reusing, clear it out. If the first ** argument is NULL, allocate a new vector. Used by vector_split*. */ static struct vector * vector_reuse(struct vector *vector) { if (vector == NULL) return vector_new(); else { vector_clear(vector); return vector; } } static struct cvector * cvector_reuse(struct cvector *vector) { if (vector == NULL) return cvector_new(); else { cvector_clear(vector); return vector; } } /* ** Given a string and a separator character, count the number of strings ** that it will split into. */ static size_t split_count(const char *string, char separator) { const char *p; size_t count; if (*string == '\0') return 1; for (count = 1, p = string; *p; p++) if (*p == separator) count++; return count; } /* ** Given a string and a separator character, form a vector by splitting the ** string at those separators. Do a first pass to size the vector, and if ** the third argument isn't NULL, reuse it. Otherwise, allocate a new one. */ struct vector * vector_split(const char *string, char separator, struct vector *vector) { const char *p, *start; size_t i, count; vector = vector_reuse(vector); count = split_count(string, separator); if (vector->allocated < count) vector_resize(vector, count); for (start = string, p = string, i = 0; *p; p++) if (*p == separator) { vector->strings[i++] = xstrndup(start, p - start); start = p + 1; } vector->strings[i++] = xstrndup(start, p - start); vector->count = i; return vector; } /* ** Given a modifiable string and a separator character, form a cvector by ** modifying the string in-place to add nuls at the separators and then ** building a vector of pointers into the string. Do a first pass to size ** the vector, and if the third argument isn't NULL, reuse it. Otherwise, ** allocate a new one. */ struct cvector * cvector_split(char *string, char separator, struct cvector *vector) { char *p, *start; size_t i, count; vector = cvector_reuse(vector); count = split_count(string, separator); if (vector->allocated < count) cvector_resize(vector, count); for (start = string, p = string, i = 0; *p; p++) if (*p == separator) { *p = '\0'; vector->strings[i++] = start; start = p + 1; } vector->strings[i++] = start; vector->count = i; return vector; } /* ** Given a string, count the number of strings that it will split into when ** splitting on whitespace. */ static size_t split_space_count(const char *string) { const char *p; size_t count; if (*string == '\0') return 0; for (count = 1, p = string + 1; *p != '\0'; p++) if ((*p == ' ' || *p == '\t') && !(p[-1] == ' ' || p[-1] == '\t')) count++; /* If the string ends in whitespace, we've overestimated the number of strings by one. */ if (p[-1] == ' ' || p[-1] == '\t') count--; return count; } /* ** Given a string, split it at whitespace to form a vector, copying each ** string segment. If the fourth argument isn't NULL, reuse that vector; ** otherwise, allocate a new one. Any number of consecutive whitespace ** characters is considered a single separator. */ struct vector * vector_split_space(const char *string, struct vector *vector) { const char *p, *start; size_t i, count; vector = vector_reuse(vector); count = split_space_count(string); if (vector->allocated < count) vector_resize(vector, count); for (start = string, p = string, i = 0; *p; p++) if (*p == ' ' || *p == '\t') { if (start != p) vector->strings[i++] = xstrndup(start, p - start); start = p + 1; } if (start != p) vector->strings[i++] = xstrndup(start, p - start); vector->count = i; return vector; } /* ** Given a string, split it at whitespace to form a vector, destructively ** modifying the string to nul-terminate each segment. If the fourth ** argument isn't NULL, reuse that vector; otherwise, allocate a new one. ** Any number of consecutive whitespace characters is considered a single ** separator. */ struct cvector * cvector_split_space(char *string, struct cvector *vector) { char *p, *start; size_t i, count; vector = cvector_reuse(vector); count = split_space_count(string); if (vector->allocated < count) cvector_resize(vector, count); for (start = string, p = string, i = 0; *p; p++) if (*p == ' ' || *p == '\t') { if (start != p) { *p = '\0'; vector->strings[i++] = start; } start = p + 1; } if (start != p) vector->strings[i++] = start; vector->count = i; return vector; } /* ** Given a vector and a separator string, allocate and build a new string ** composed of all the strings in the vector separated from each other by the ** seperator string. Caller is responsible for freeing. */ char * vector_join(const struct vector *vector, const char *seperator) { char *string; size_t i, size, seplen; seplen = strlen(seperator); for (size = 0, i = 0; i < vector->count; i++) size += strlen(vector->strings[i]); size += (vector->count - 1) * seplen + 1; string = xmalloc(size); strlcpy(string, vector->strings[0], size); for (i = 1; i < vector->count; i++) { strlcat(string, seperator, size); strlcat(string, vector->strings[i], size); } return string; } char * cvector_join(const struct cvector *vector, const char *seperator) { char *string; size_t i, size, seplen; seplen = strlen(seperator); for (size = 0, i = 0; i < vector->count; i++) size += strlen(vector->strings[i]); size += (vector->count - 1) * seplen + 1; string = xmalloc(size); strlcpy(string, vector->strings[0], size); for (i = 1; i < vector->count; i++) { strlcat(string, seperator, size); strlcat(string, vector->strings[i], size); } return string; }