/* $Id: vector.c 6699 2004-04-07 06:47:44Z rra $
**
** Vector handling (counted lists of char *'s).
**
** Written by Russ Allbery <rra@stanford.edu>
** 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 <ctype.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1