/** * @file vector.h * @brief Functions for creating and manipulating vectors. * * A vector is an array that can be resized to meet changing storage * requirements. Usage is typically as follows: \include vector_test.c * * @author Jon Schutz * @date 05-Feb-2001 **/ #ifndef __VECTOR_H #define __VECTOR_H /* To stop multiple inclusions. */ #include #include /** Vector */ typedef struct _vector { /** The array to hold the data. */ void *array; /** The size of the array. */ size_t array_size; /** The used size, assuming vector_append is the only operation which adds elements to the vector. */ size_t index; } vector; /** Add a new item to the end of a vector. @param v The vector to which to add an item. @param item The item to add. @param increment The capacity to add when the array is resized. Should be >0, or at least =0 if factor>1. @param factor The factor by which to increase storage when the array is resized. Should be >1, or at least =1 if increment>0. @return 0 on success, -1 if vector_resize() fails. */ #define vector_append(v, item, increment, factor) \ ({ \ int rv=0; \ \ if ((v)->index >= (v)->array_size) \ rv = vector_resize((v), \ (v)->array_size * factor + increment, sizeof(item)); \ if (0 == rv) \ ((typeof(item) *)(v)->array)[(v)->index++] = item; \ rv; \ }) /** Insert space for several items into a vector at the given position. No items are actually inserted. If the given position exceeds the current end index of the array, the new end index, then item is inserted before the specified position and the end index points to the next position after the inserted item. If necessary, the vector is resized to accommodate the request. @param v The vector to which to insert space. @param item An instance or type of the items in the vector. @param pos The position at which to insert space. @param n The number of items for which to insert space. @param increment The capacity to add when the array is resized. Should be >0, or at least =0 if factor>1. @param factor The factor by which to increase storage when the array is resized. Should be >1, or at least =1 if increment>0. @return 0 on success, -1 if vector_resize() fails. */ #define vector_insert_space(v, item, pos, n, increment, factor) \ ({ \ int _rv=0; \ int new_end = ((pos)>(v)->index)?((pos)+(n)):((v)->index+(n)); \ if (new_end > (v)->array_size) { \ size_t new_size = (v)->array_size * factor + increment; \ if (new_end > new_size) \ new_size = new_end; \ _rv = vector_resize((v), new_size, sizeof(item)); \ } \ if (0 == _rv) { \ if ((v)->index > pos) { \ memmove(((char *)(v)->array) + sizeof(item)*((pos)+(n)), \ ((char *)(v)->array) + sizeof(item)*(pos), \ sizeof(item)*((v)->index - pos)); \ (v)->index += (n); \ } \ else \ (v)->index = new_end; \ } \ _rv; \ }) /** Add a new item to the vector at a given position. The item is inserted before the specified position. If the given position exceeds the current end index of the array, the new end index, the item is inserted before the specified position and the end index points to the next position after the inserted item. If necessary, the vector is resized to accommodate the request. @param v The vector to which to insert an item. @param item The item to insert. @param pos The position at which to insert the item. @param increment The capacity to add when the array is resized. Should be >0, or at least =0 if factor>1. @param factor The factor by which to increase storage when the array is resized. Should be >1, or at least =1 if increment>0. @return 0 on success, -1 if vector_resize() fails. */ #define vector_insert(v, item, pos, increment, factor) \ ({ \ const int c_pos = pos; \ int rv = vector_insert_space(v, item, pos, 1, increment, factor); \ if (0 == rv) { \ ((typeof(item) *)(v)->array)[c_pos] = item; \ } \ rv; \ }) \ /** Delete several item from a vector. @param v The vector in which to delete the item. @param item An instance or type of the item to delete. @param pos The first position at which to delete. @param n The number of items to delete. */ #define vector_delete_n(v, item, pos, n) \ if ((v)->index > ((pos) + (n))) { \ memmove((v)->array + sizeof(item)*(pos), \ (v)->array + sizeof(item)*((pos)+(n)), \ sizeof(item)*((v)->index - (pos) - (n))); \ (v)->index -= (n); \ } \ else \ (v)->index = pos; /** Delete an item from a vector. @param v The vector in which to delete the item. @param item An instance or type of the item to delete. @param pos The position at which to delete. */ #define vector_delete(v, item, pos) \ vector_delete_n(v, item, pos, 1) /** Access a member of a vector. @param v The vector to access @param index The index to read. @param element_type The type name or an instance of the type of which the vector is comprised. @return v[index] */ #define element_at(v, index, element_type) \ (((typeof(element_type) *)(v)->array)[index]) /** Reverse items in a vector. @param v The vector in which to reverse items. @param item The type name or an instance of the type of which the vector is comprised. @param start The starting index of the sequence to be reversed. @param end The index following the end of the sequence to be reversed. */ #define vector_reverse(v, item, start, end) { \ int _i; \ int _half = (end-start)/2; \ typeof(item) _temp; \ for (_i=0; _i<_half; _i++) { \ _temp = element_at(v, (start)+_i, item); \ element_at(v, (start)+_i, item) = element_at(v, (end)-_i-1, item); \ element_at(v, (end)-_i-1, item) = _temp; \ } \ } int vector_resize(vector *v, size_t size, size_t element_size); int vector_create(vector *v, size_t size, size_t element_size); void vector_destroy(vector *v); /** Finds the next unused slot in a vector, by comparing the contents * of a given \a field with \a thing that indicates a free slot. * * @param v The vector to search through. * @param item The type of data held by the vector. * @param field A field of a struct which is held in the vector's array. * @param thing If the contents of the above field match this thing, we * have found an unused slot. * * @return the index of the first unused slot. **/ #define vector_find_next_free_slot(v, item, field, thing) \ ({ \ int i = 0; \ if ((NULL != (v)->array) && (0 < (v)->index)) \ for (i = 0; i < (v)->index; i++) \ if (thing == ((typeof(item) *)(v)->array)[i]field) \ break; \ i; \ }) /** * Stores a \a token of type \a item into \a v, in the first location in * \a v whose \a field matches \a thing. If all \a v->index slots in \a * v are in use, a new element is inserted at the end of the vector and * \a v->index is incremented, otherwise, \a token is inserted at the * first available slot, and \a v->index is left unchanged. An * extension of the "vector" class, which can be used when * "vector_delete" is not suitable (e.g. if the index into the vector * is being remembered by external software, then vector_delete will not * work, because it deletes from the middle of a vector and moves all * the top entries down to fill the vacated slot(s)). * * @param v Vector into which we will insert the token. * @param token The object to store in the vector. * @param item The data-type of the token that we're inserting. * @param size The size of the token that we're inserting. * @param field A field of a struct which is held in the vector's array. * (May be empty if not using a struct). * @param thing If the contents of the above field match this thing, we * have found an unused slot. * * @return The location of the slot in the vector where the data was * stored. **/ #define vector_store(v, token, item, size,field, thing) \ ({ \ int pos = vector_find_next_free_slot(v, item,field, thing); \ if (pos == (v)->index) \ /* Increment (v)->index */ \ vector_append((v), token, size, 1); \ else \ /* Ignore (v)->index */ \ element_at((v), pos, item) = token; \ pos; \ }) #endif /* __VECTOR_H */