summaryrefslogtreecommitdiffstats
path: root/lib/temp.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/temp.c')
-rw-r--r--lib/temp.c1349
1 files changed, 1349 insertions, 0 deletions
diff --git a/lib/temp.c b/lib/temp.c
new file mode 100644
index 00000000..759c2bb3
--- /dev/null
+++ b/lib/temp.c
@@ -0,0 +1,1349 @@
+/* Generic vector interface routine -- functions
+ * Copyright (C) 1997 Kunihiro Ishiguro
+ *
+ * 24-Nov-2009 -- extended to add a number of new operations on vectors.
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "vector.h"
+#include "memory.h"
+
+/* Vectors are implemented as a structure which points to an array of pointers
+ * to vector items. That array -- the body of the vector -- can change size,
+ * and therefore may move around in memory.
+ *
+ * The vector structure may be statically allocated, embedded in another
+ * structure, or allocated dynamically. In any case the vector operations
+ * require the address of the vector structure -- see typedef for vector.
+ *
+ * Vector items are accessed by index, fetching and storing pointers to
+ * those items. Can also push and pop items.
+ *
+ * A vector has a known (logical) end position. Everything beyond the end is
+ * defined to be NULL. When a vector is initialised it is set empty.
+ *
+ * At any given moment the vector body has a limit on the number of items it
+ * can accommodate (the physical end).
+ *
+ * A vector will grow to accommodate what is put into it. Adding items beyond
+ * the (logical) end moves it. Adding items beyond the (physical) limit causes
+ * the body to be extended to suit, and to leave some spare space for future
+ * expansion.
+ *
+ * While the vector is small (see VECTOR_LIMIT_DOUBLE_MAX) the body will grow by
+ * doubling in size. When it is larger, it grows to be multiples of
+ * VECTOR_LIMIT_DOUBLE_MAX.
+ *
+ * Deleting items reduces the (logical) end position, but does NOT release
+ * memory -- the (physical) limit is not changed.
+ *
+ * To release memory: vector_chop will release everything beyond the current
+ * end; vector_decant will create a new body of exactly the current size,
+ * releasing the old body; vector_discard will release everything beyond a
+ * given position.
+ *
+ * NB: you can set a vector item to be NULL. If you set a vector item beyond
+ * the current end, NULL items are inserted in the vector.
+ *
+ * NB: when setting a vector item it is the caller's responsibility to
+ * deallocate any pre-existing value of the item.
+ *
+ * NB: when deleting items it is also the caller's responsibility to deallocate
+ * any values that require it.
+ *
+ * Implementation Notes
+ *
+ * Everything beyond the (logical) end is implicitly NULL.
+ *
+ * Actual memory between (logical) end and (physical) limit is UNDEFINED. So
+ * when advancing the end some care has to be taken to ensure that any new
+ * items in the vector are either set to something or cleared to NULL.
+ *
+ * It would have been possible to ensure that everything between end and limit
+ * is cleared to NULL, but that is more work -- in particular it creates work
+ * when it is not always required.
+ */
+
+#define P_ITEMS_SIZE(n) SIZE(p_vector_item, n)
+
+/*==============================================================================
+ * Initialisation, allocation, reset etc.
+ */
+
+/*------------------------------------------------------------------------------
+ * Initialise a brand new vector, setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, otherwise body of exactly
+ * the required size is allocated.
+ *
+ * NB: discards any existing vector body -- so it is the caller's responsibility
+ * to release any existing body, and any items in that body.
+ */
+extern vector
+vector_init_new(vector v, vector_length_t limit)
+{
+ if (v == NULL)
+ v = XCALLOC(MTYPE_VECTOR, sizeof(struct vector)) ;
+ else
+ memset(v, 0, sizeof(struct vector)) ;
+
+ if (limit != 0)
+ {
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
+ } ;
+
+ return v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Initialize vector : allocate memory and return vector.
+ * allocates body with at least 1 entry.
+ *
+ * This is a "legacy" function.
+ */
+extern vector
+vector_init (vector_length_t limit)
+{
+ return vector_init_new(NULL, limit ? limit : 1) ; /* at least 1 entry */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Basic: free the vector body and the vector structure.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+void
+vector_free (vector v)
+{
+ XFREE (MTYPE_VECTOR_BODY, v->p_items);
+ XFREE (MTYPE_VECTOR, v);
+} ;
+
+/*------------------------------------------------------------------------------
+ * Re-initialise vector (or create new one), setting it empty.
+ *
+ * Allocates vector structure if none given -- that is, if v == NULL.
+ *
+ * If size is given as zero, no body is allocated, but any existing body is
+ * retained. (vector_reset() will discard body.)
+ *
+ * Otherwise ensures existing body is at least the required size, or a body
+ * of exactly the required size is allocated.
+ *
+ * NB: when re-initialising an existing vector it is the caller's responsibility
+ * to release any vector item values *before* doing this.
+ * */
+extern vector
+vector_re_init(vector v, vector_length_t)
+{
+ if ((v == NULL) || (v->p_items == NULL))
+ return vector_init_new(v, limit) ;
+
+ v->end = 0 ;
+
+ if (v->limit < size)
+ {
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(limit)) ;
+ v->limit = limit ;
+ } ;
+
+ return v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Free the vector body, and (if required) free the vector structure.
+ *
+ * Return NULL if releases vector, otherwise the address of the vector.
+ *
+ * NB: it is the caller's responsibility to release any vector item values
+ * *before* doing this.
+ */
+vector
+vector_reset(vector v, free_keep_b free_structure)
+{
+ if (v == NULL)
+ return NULL ; /* allow for already freed vector */
+
+ if (v->p_items != NULL)
+ XFREE(MTYPE_VECTOR_BODY, v->p_items) ;
+
+ if (free_structure)
+ {
+ confirm(free_it == true) ;
+ XFREE(MTYPE_VECTOR, v) ;
+ return NULL ;
+ }
+ else
+ return vector_init_new(v, 0) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Set vector length to be (at least) the given fixed length.
+ *
+ * There must be a vector.
+ *
+ * Does nothing if the vector is already as long or longer than the given
+ * length.
+ *
+ * If the body is not big enough for the new length, allocates or extends to
+ * exactly the new length. Otherwise, leaves body as it is.
+ *
+ * Appends NULLs as required to extend to the required length.
+ *
+ * Note that the existing contents of the vector are preserved in all cases.
+ */
+Private void
+vector_set_new_min_length(vector v, vector_length_t len)
+{
+ assert (v != NULL) ;
+
+ if (len > v->limit)
+ {
+ if (v->p_items == NULL)
+ v->p_items = XMALLOC(MTYPE_VECTOR_BODY, P_ITEMS_SIZE(len)) ;
+ else
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items,
+ P_ITEMS_SIZE(len)) ;
+ v->limit = len ;
+ } ;
+
+ if (v->end < len)
+ vector_extend(v, len) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Pop item from vector, stepping past any NULLs.
+ * If vector is empty, free the body and, if required, the vector structure.
+ *
+ * Useful for emptying out and discarding a vector:
+ *
+ * while ((p_v = vector_ream_out(v, 1)))
+ * ... do what's required to release the item p_v
+ *
+ * Returns NULL if vector was empty and has now been freed as required.
+ */
+p_vector_item
+vector_ream(vector v, free_keep_b free_structure)
+{
+ p_vector_item p_v ;
+
+ if (v == NULL)
+ return NULL ;
+
+ while (v->end != 0)
+ {
+ p_v = v->p_items[--v->end] ;
+ if (p_v != NULL)
+ return p_v ; /* return non-NULL item */
+ } ;
+
+ /* vector is empty: free the body, and (if required) the vector structure. */
+ vector_reset(v, free_structure) ;
+
+ return NULL ; /* signals end */
+} ;
+
+/*==============================================================================
+ * Unset item, condensing and trimming vector.
+ *
+ * These are legacy operations.
+ */
+
+/*------------------------------------------------------------------------------
+ * Unset item at given index (ie set it NULL).
+ *
+ * Return the old value of the item.
+ *
+ * If the item at the current (logical) end of the vector is NULL, move the
+ * end backwards until finds a non-NULL item, or the vector becomes empty.
+ */
+extern p_vector_item
+vector_unset_item(vector v, vector_index_t i)
+{
+ p_vector_item was ;
+
+ if (i < v->end)
+ {
+ was = v->p_items[i] ;
+ v->p_items[i] = NULL ;
+ }
+ else if (v->end == 0)
+ return NULL ; /* avoid test for last entry NULL if is empty */
+ else
+ was = NULL ;
+
+ if (v->p_items[v->end - 1] == NULL)
+ vector_trim(v) ;
+
+ return was ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Trim any NULL entries at the current (logical) end of the vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_trim(vector v)
+{
+ vector_length_t e = v->end ;
+ while ((e > 0) && (v->p_items[e - 1] == NULL))
+ --e ;
+ v->end = e ;
+ return e ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Removes any NULL entries from the given vector.
+ *
+ * Returns the (new) length (end) of the vector.
+ */
+extern vector_length_t
+vector_condense(vector v)
+{
+ vector_length_t e = 0 ;
+ vector_index_t j ;
+
+ /* Find first NULL, if any */
+ while ((e < v->end) && (v->p_items[e] != NULL))
+ ++e ;
+
+ /* Quit if no NULLs (or vector is empty) */
+ if (e == v->end)
+ return e ;
+
+ /* Shuffle any remaining non-NULL down */
+ for (j = e + 1 ; j < v->end ; ++j)
+ if (v->p_items[j] != NULL)
+ v->p_items[e++] = v->p_items[j] ;
+
+ v->end = e ;
+
+ return e ;
+} ;
+
+/*==============================================================================
+ * Inserting and deleting items.
+ */
+
+/*------------------------------------------------------------------------------
+ * Insert item in vector, before item at given position
+ * Move items and extend vector as required.
+ */
+extern void
+vector_insert_item(vector v, vector_index_t i, void* p_v)
+{
+ if ((i == v->end) && (i < v->limit))
+ ++v->end ;
+ else
+ vector_insert(v, i, 1) ;
+
+ v->p_items[i] = (p_vector_item)p_v ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert item in vector at given position with rider:
+ *
+ * rider < 0 -- insert before the item at the given position
+ * rider == 0 -- insert at the given position -- REPLACING any existing value
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: when an item is replaced, it is the caller's responsibility to release
+ * any memory used by the item, if required.
+ *
+ * Move items and extend vector as required.
+ */
+extern void
+vector_insert_item_here(vector v, vector_index_t i, int rider,
+ p_vector_item p_v)
+{
+ if (rider == 0)
+ return vector_set_item(v, i, p_v) ;
+
+ if (rider > 0)
+ ++i ; /* insert before next item */
+ vector_insert_item(v, i, p_v) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete item from vector.
+ *
+ * Move items as required. Reduces number of items in the vector (unless
+ * the item in question is beyond the end of the vector !)
+ *
+ * Returns: the item that has just been deleted.
+ *
+ * NB: it is the caller's responsibility to release memory used by any
+ * current value of the item, if required.
+ *
+ * NB: does NOT change the size of the vector body.
+ */
+extern p_vector_item
+vector_delete_item(vector v, vector_index_t i)
+{
+ p_vector_item p_e ;
+ if (i < v->end)
+ {
+ p_e = v->p_items[i] ; /* pick up the current value */
+ if (i != (v->end - 1))
+ vector_delete(v, i, 1) ;
+ else
+ v->end = i ;
+ return p_e ;
+ }
+ else
+ return NULL ;
+} ;
+
+/*==============================================================================
+ * Moving items within vector.
+ */
+
+/*------------------------------------------------------------------------------
+ * Move item in vector from source position to destination position.
+ *
+ * Moves intervening items up or down as required.
+ *
+ * Extends vector to include the destination, if required.
+ *
+ * A source item beyond the end of the vector is implicitly NULL.
+ */
+extern void
+vector_move_item(vector v, vector_index_t i_dst, vector_index_t i_src)
+{
+ p_vector_item* pp_s ;
+ p_vector_item* pp_d ;
+ p_vector_item p_e ;
+
+ vector_length_t old_end = v->end ;
+
+ /* Worry about whether both source and destination exist. */
+ if (i_dst >= old_end)
+ {
+ vector_insert(v, i_dst, 1) ; /* ensure destination exists */
+ if (i_src >= old_end)
+ return ; /* both were beyond the end */
+ }
+ else if (i_src >= old_end)
+ {
+ i_src = old_end ; /* clamp to just beyond last */
+ vector_insert(v, i_src, 1) ; /* create empty entry */
+ } ;
+
+ if (i_dst == i_src) /* avoid work and edge case */
+ return ;
+
+ /* Both src and dst are within the vector and src != dst */
+ pp_s = &v->p_items[i_src] ; /* address of src entry */
+ pp_d = &v->p_items[i_dst] ; /* address of dst entry */
+ p_e = *pp_s ; /* pick up item to move */
+ if (i_src < i_dst)
+ memmove(pp_s, pp_s+1, P_ITEMS_SIZE(i_dst - i_src)) ;
+ else
+ memmove(pp_d+1, pp_d, P_ITEMS_SIZE(i_src - i_dst)) ;
+ *pp_d = p_e ; /* put down the item to move */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Move item in vector to given position with rider:
+ *
+ * rider < 0 -- move to before the item at the given position
+ * rider == 0 -- move to replace item at the given position
+ * rider > 0 -- insert after the item at the given position
+ *
+ * NB: it is the caller's responsibility to release the any existing value
+ * that will be replaced.
+ *
+ * Move items and extend vector as required.
+ */
+extern void
+vector_move_item_here(vector v, vector_index_t i_dst, int rider,
+ vector_index_t i_src)
+{
+ if (rider != 0)
+ {
+ if (rider > 0)
+ ++i_dst ;
+ vector_move_item(v, i_dst, i_src) ;
+ }
+ else
+ {
+ /* to replace: copy and then delete. */
+ vector_set_item(v, i_dst, vector_get_item(v, i_src)) ;
+ vector_delete_item(v, i_src) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reverse vector: reverse the order of items in the vector.
+ */
+extern void
+vector_reverse(vector v)
+{
+ if (v != NULL)
+ vector_part_reverse(v, 0, v->end) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Reverse portion of vector.
+ */
+extern void
+vector_part_reverse(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_index_t j ;
+
+ if (v == NULL)
+ return ;
+
+ if ((i + n) > v->limit)
+ vector_extend(v, i + n) ; /* ensure portion exists */
+
+ if (n <= 1)
+ return ;
+
+ j = i + n - 1 ; /* j > i, because n > 1 */
+ do
+ {
+ p_vector_item p_i = v->p_items[i] ;
+ v->p_items[i++] = v->p_items[j] ;
+ v->p_items[j--] = p_i ;
+ } while (j > i) ;
+} ;
+
+/*==============================================================================
+ * Copying, moving and appending entire vectors.
+ */
+
+static void vector_new_limit(vector v, vector_length_t new_end) ;
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ *
+ * Creates a new vector.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ */
+vector
+vector_copy (vector v)
+{
+ vector new = vector_init_new(NULL, v->limit) ;
+
+ new->end = v->end;
+
+ if (v->limit > 0)
+ memcpy(new->p_items, v->p_items, P_ITEMS_SIZE(v->end)) ;
+
+ return new;
+}
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy -- copies pointers to item values, not the values.
+ * Creates a new vector or re-initialises an existing one.
+ *
+ * NB: creates new vector with same limit as existing one, but copies only
+ * the known items (ie up to end, not up to limit).
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try copying a vector to itself !!
+ */
+vector
+vector_copy_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ dst = vector_re_init(dst, src->limit) ;
+
+ dst->end = src->end;
+
+ if (src->end > 0)
+ memcpy(dst->p_items, src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector move -- moves body of vector.
+ *
+ * Creates a new vector or re-initialises an existing one.
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to release any existing items if that is required.
+ *
+ * NB: if re-initialising existing vector, it is the caller's responsibility
+ * to ensure the vector structure is currently valid.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+extern vector
+vector_move_here(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if (dst != NULL)
+ dst = vector_reset(dst, 0) ; /* Reset to deallocate any existing body */
+ else
+ dst = vector_init_new(dst, 0) ; /* Create new structure sans body. */
+
+ *dst = *src ; /* Copy the vector structure */
+
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+}
+
+/*------------------------------------------------------------------------------
+ * Shallow vector copy append -- copies pointers to item values, not the values.
+ *
+ * Appends copied pointers to the destination vector.
+ *
+ * Creates a new destination vector if required.
+ *
+ * NB: Can append to self.
+ */
+extern vector
+vector_copy_append(vector dst, vector src)
+{
+ vector_index_t new_end ;
+
+ assert(src != NULL) ;
+
+ if (dst != NULL)
+ {
+ new_end = dst->end + src->end ;
+ if (new_end > dst->limit)
+ vector_new_limit(dst, new_end) ;
+ }
+ else
+ {
+ new_end = src->end ;
+ vector_init_new(dst, new_end) ;
+ } ;
+
+ if (src->end)
+ memcpy(&dst->p_items[dst->end], src->p_items, P_ITEMS_SIZE(src->end)) ;
+
+ dst->end = new_end ; /* Done last, allows for append to self ! */
+ return dst ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Vector move append -- moves pointers to item values.
+ *
+ * Appends moved pointers to the destination vector.
+ *
+ * Creates a new destination vector if required (dst == NULL).
+ *
+ * Leaves the source vector empty -- does not release the structure.
+ *
+ * NB: do NOT try moving a vector to itself !!
+ */
+extern vector
+vector_move_append(vector dst, vector src)
+{
+ assert((src != NULL) && (src != dst)) ;
+
+ if ((dst == NULL) || (dst->end == 0))
+ return vector_move_here(dst, src) ; /* Easy way to do it if dst empty */
+
+ vector_copy_append(dst, src) ; /* Extend dst and copy src */
+ vector_init_new(src, 0) ; /* Set empty, forgetting body */
+
+ return dst ;
+} ;
+
+/*==============================================================================
+ * Portmanteau splice/extract/replace function.
+ *
+ * All take a portion of 'src' vector and:
+ *
+ * splice:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * copying the replaced portion of the 'dst' vector to the 'to' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to', 'dst' and 'src' must be distinct vectors.
+ *
+ * extract:
+ *
+ * a) copy a portion of the 'src' vector to the 'to' vector
+ * c) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- true
+ * to -- vector, or NULL => allocate new vector
+ * dst -- NULL
+ * i_dst -- ignored
+ * n_dst -- ignored
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: the (possibly new) 'to' vector
+ *
+ * NB: 'to' and 'src' must be distinct vectors.
+ *
+ * replace:
+ *
+ * a) replace a portion of the 'dst' vector by a portion of the 'src' vector
+ * b) either: leave 'src' unchanged -- copy
+ * or: remove the stuff copied from 'src' -- move
+ *
+ * Arguments: to_copy -- false
+ * to -- ignored
+ * dst -- vector
+ * i_dst -- start of portion to replace
+ * n_dst -- length of portion to replace. 0 => insertion.
+ * src -- vector, or NULL => nothing to copy/move
+ * i_src -- start of portion to copy/move
+ * n_src -- length of portion to copy/move. 0 => nothing.
+ * src_move -- true => move, otherwise copy.
+ *
+ * Returns: original 'to' argument
+ *
+ * NB: 'dst' and 'src' must be distinct vectors.
+ *
+ * All copies are shallow -- pointers to item values are copied, not the values.
+ *
+ * NB: any existing contents of the 'to' vector are discarded.
+ *
+ * NB: it is the caller's responsibility to release memory allocated to any
+ * items which are discarded in these operations.
+ *
+ * NB: for splice and replace, the resulting destination vector will be at
+ * least i_dst + n_src long. (Even if is copying actual or implied NULLs
+ * from the source.)
+ *
+ * NB: where new vectors are created, they will be of exactly the required size.
+ *
+ * NB: where an existing vector is reused, it is the caller's responsibility
+ * to ensure the vector structure is currently valid (by vector_init_new()
+ * or by ensuring it is zeroized).
+ */
+extern vector
+vector_sak(int to_copy, vector to,
+ vector dst, vector_index_t i_dst, vector_length_t n_dst,
+ vector src, vector_index_t i_src, vector_length_t n_src, int src_move)
+{
+ int dst_replace ; /* true => replace portion of 'dst' */
+
+ vector_index_t new_dst_end = 0 ; /* new end of dst */
+
+ vector_length_t n_dst_nulls ; /* number of implicit NULLs to add */
+ vector_length_t n_dst_move ; /* number of items to move up or down */
+ vector_length_t n_src_real ; /* number of items to really copy */
+ vector_length_t n_src_nulls ; /* number of implicit NULLs to "copy" */
+
+ assert((to == NULL) || (dst == NULL) || (to != dst)) ;
+ assert((src == NULL) || (dst == NULL) || (src != dst)) ;
+
+ /* Worry about how much we really have in the source vector. */
+
+ n_src_real = n_src ; /* assume all real */
+ n_src_nulls = 0 ; /* so no NULLs to "copy" */
+
+ if (n_src != 0)
+ {
+ if ((src == NULL) || (i_src >= src->end))
+ n_src_real = 0 ;
+ else if ((i_src + n_src) > src->end)
+ n_src_real = src->end - i_src ;
+ n_src_nulls = n_src - n_src_real ;
+ } ;
+
+ /* If no 'dst' vector, then this is an extract. */
+
+ n_dst_move = 0 ; /* assume nothing to move */
+ n_dst_nulls = 0 ; /* assume no NULLs to add */
+
+ if (dst == NULL)
+ /* For extract: set up dst, i_dst and n_dst so that can copy to the */
+ /* 'to' vector as if from 'dst'. */
+ {
+ dst_replace = 0 ; /* no replacement operation */
+ dst = src ; /* copy from here */
+ i_dst = i_src ;
+ n_dst = n_src_real ;
+ }
+ else
+ /* Reduce n_dst to the number of actual items to be replaced. */
+ /* */
+ /* Calculate the new end of 'dst'. */
+ {
+ dst_replace = 1 ; /* have replacement to do */
+ if (i_dst >= dst->end)
+ /* If i_dst is beyond the end of 'dst', then there is nothing */
+ /* to replace (so set n_dst == 0). Will be adding n_src items */
+ /* at i_dst -- so new end must be i_dst + n_src. */
+ {
+ n_dst_nulls = i_dst - dst->end ; /* fill from end to i_dst */
+ n_dst = 0 ; /* nothing to replace */
+ new_dst_end = i_dst + n_src ; /* all beyond current end */
+ }
+ else
+ /* If i_dst + n_dst is beyond the end of 'dst', reduce n_dst to */
+ /* number of items up to the end. */
+ /* Will remove n_dst items and insert n_src, so end will move */
+ /* by n_src - n_dst. */
+ {
+ if ((i_dst + n_dst) > dst->end)
+ n_dst = dst->end - i_dst ; /* what we actually replace */
+ else if (n_dst != n_src)
+ n_dst_move = dst->end - (i_dst + n_dst) ;
+ /* what we move up or down */
+
+ new_dst_end = dst->end + n_src - n_dst ;
+ /* end depends on amount added */
+ /* & amount actually replaced */
+ } ;
+ } ;
+
+ /* Copy portion of 'dst' (or of 'src') to 'to', if required. */
+ /* */
+ /* Have arranged: n_dst -- number of items to copy, all existent */
+ /* dst -- vector to copy from -- if n_dst > 0 */
+ /* i_dst -- first item to copy -- if n_dst > 0 */
+
+ if (to_copy)
+ {
+ to = vector_re_init(to, n_dst) ; /* reinitialise or create */
+ to->end = n_dst ;
+ if (n_dst > 0)
+ memcpy(to->p_items, &dst->p_items[i_dst], P_ITEMS_SIZE(n_dst)) ;
+ } ;
+
+ /* Replace portion of 'dst' by portion of 'src', if required. */
+ /* */
+ /* Have arranged: */
+ /* */
+ /* new_dst_end -- end of dst once dust settles */
+ /* n_dst_nulls -- number of NULLs to insert at dst->end to fill up */
+ /* to i_dst (when i_dst is beyond old end.) */
+ /* n_dst_move -- number of items in dst to move up or down to */
+ /* leave n_src item hole at i_dst to fill in. */
+ /* n_src_real -- number of real src items at i_src to copy to dst */
+ /* at i_dst. */
+ /* n_src_nulls -- number of nulls to add to fill to i_dst + n_src. */
+
+ if (dst_replace)
+ {
+ if (new_dst_end > dst->limit) /* extend body if required */
+ vector_new_limit(dst, new_dst_end) ;
+
+ if (n_dst_nulls > 0)
+ memset(&dst->p_items[dst->end], 0, P_ITEMS_SIZE(n_dst_nulls)) ;
+ if (n_dst_move > 0)
+ memmove(&dst->p_items[i_dst + n_dst], &dst->p_items[i_dst + n_src],
+ P_ITEMS_SIZE(n_dst_move)) ;
+ if (n_src_real > 0)
+ memcpy(&dst->p_items[i_dst], &src->p_items[i_src],
+ P_ITEMS_SIZE(n_src_real)) ;
+ if (n_src_nulls > 0)
+ memset(&dst->p_items[i_dst + n_src_real], 0,
+ P_ITEMS_SIZE(n_src_nulls)) ;
+ dst->end = new_dst_end ;
+ } ;
+
+ /* Delete portion of 'src', if required (and have 'src' !) */
+
+ if (src_move && (n_src_real != 0))
+ vector_delete(src, i_src, n_src_real) ;
+
+ /* Done -- return 'to' vector as promised. */
+
+ return to ;
+} ;
+
+/*==============================================================================
+ * Legacy Vector Operations
+ */
+
+/*------------------------------------------------------------------------------
+ * Set value to the smallest empty slot.
+ *
+ * Returns: index of slot used.
+ */
+extern int
+vector_set (vector v, void *val)
+{
+ vector_index_t i;
+
+ i = 0 ;
+ while (1)
+ {
+ if (i == v->end)
+ {
+ i = vector_extend_by_1(v) ;
+ break ;
+ }
+
+ if (v->p_items[i] == NULL)
+ break ;
+
+ ++i ;
+ } ;
+
+ v->p_items[i] = val;
+
+ return i ;
+}
+
+/*------------------------------------------------------------------------------
+ * Set value to specified index slot.
+ *
+ * Returns: index of slot (as given)
+ */
+extern int
+vector_set_index (vector v, vector_index_t i, void *val)
+{
+ vector_ensure (v, i);
+ v->p_items[i] = val;
+ return i;
+}
+
+/*------------------------------------------------------------------------------
+ * Look up vector -- get the i'th item.
+ *
+ * Returns: the i'th item -- NULL if item is null, or i >= logical len of vector
+ */
+extern p_vector_item
+vector_lookup (vector v, vector_index_t i)
+{
+ if (i >= v->end)
+ return NULL;
+ return v->p_items[i];
+}
+
+/*------------------------------------------------------------------------------
+ * Lookup vector, ensure it -- get i'th item and ensure logical len > i.
+ *
+ * Returns: the i'th item -- NULL if item is null
+ */
+extern p_vector_item
+vector_lookup_ensure (vector v, vector_index_t i)
+{
+ vector_ensure (v, i);
+ return v->p_items[i];
+}
+
+/*------------------------------------------------------------------------------
+ * Count the number of not empty slots.
+ */
+extern vector_length_t
+vector_count (vector v)
+{
+ vector_index_t i;
+ vector_length_t count = 0;
+
+ for (i = 0; i < v->end; i++)
+ if (v->p_items[i] != NULL)
+ count++;
+
+ return count;
+}
+
+/*==============================================================================
+ * Sorting and Searching vector.
+ */
+
+/*------------------------------------------------------------------------------
+ * Sort the given vector.
+ *
+ * NB: the comparison function receives a pointer to the pointer to the
+ * vector item's value.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+extern void
+vector_sort(vector v, vector_sort_cmp* cmp)
+{
+ if (v->end <= 1)
+ return ; /* Stop dead if 0 or 1 items */
+
+ typedef int qsort_cmp(const void*, const void*) ;
+
+ qsort(v->p_items, v->end, sizeof(p_vector_item), (qsort_cmp*)cmp) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Perform binary search on the given vector.
+ *
+ * The vector MUST be sorted in the order implied by the comparison function
+ * given. The vector need not contain unique values, but this search makes
+ * no effort to select any particular instance of a sequence of equals.
+ *
+ * Returns:
+ *
+ * result == 0: found an equal value.
+ * index returned is of first entry found which is equal to
+ * the value sought. There may be other equal values, before
+ * and/or after this one in the vector.
+ *
+ * result == +1: value not found and vector not empty.
+ * index returned is of the largest entry whose value is less
+ * than the value sought.
+ * (The value sought belongs after this point.)
+ *
+ * result == -1: value is less than everything in the vector, or the
+ * vector is empty.
+ * index returned is 0
+ *
+ * NB: The comparison function takes arguments which are:
+ *
+ * const void** pointer to pointer to value being searched for.
+ * const void** pointer to pointer to vector item to compare with
+ *
+ * The value being searched for need not be in the same form as a vector
+ * item. However, if it is then the same comparison function can be used
+ * to sort and search.
+ *
+ * NB: if there are NULL items in the vector, the comparison function MUST
+ * be ready for them.
+ */
+extern vector_index_t
+vector_bsearch(vector v, vector_bsearch_cmp* cmp, const void* p_val,
+ int* result)
+{
+ vector_index_t il, iv, ih ;
+ int c ;
+
+ if (v->end <= 1)
+ {
+ *result = (v->end == 0) ? -1 : cmp(&p_val, (const void**)&v->p_items[0]) ;
+ return 0 ; /* Stop dead if 0 or 1 items */
+ } ;
+
+ /* We have at least two items. */
+ il = 0 ;
+ ih = v->end - 1 ;
+
+ /* Pick off the edge cases: >= last and <= first. */
+ if ((c = cmp(&p_val, (const void**)&v->p_items[ih])) >= 0)
+ {
+ *result = c ; /* 0 => found. +1 => val > last */
+ return ih ; /* return high index. */
+ } ;
+ if ((c = cmp(&p_val, (const void**)&v->p_items[il])) <= 0)
+ {
+ *result = c ; /* 0 => found. -1 => val < first */
+ return il ; /* return low index. */
+ }
+
+ /* Now binary chop. We know that item[il] < val < item[ih] */
+ /* We also know that il < ih */
+ while (1)
+ {
+ iv = (il + ih) / 2 ;
+ if (iv == il) /* true if (ih == il+1) */
+ {
+ *result = +1 ;
+ return il ; /* return il: item[il] < val < item[il+1] */
+ } ;
+ /* We now know that il < iv < ih */
+ c = cmp(&p_val, (const void**)&v->p_items[iv]) ;
+ if (c == 0)
+ {
+ *result = 0 ;
+ return iv ; /* found !! */
+ }
+ if (c < 0)
+ ih = iv ; /* step down iv > il, so new ih > il */
+ else
+ il = iv ; /* step up iv < ih, so new il < ih */
+ } ;
+} ;
+
+/*==============================================================================
+ * Mechanics for adding/deleting items and managing the vector (logical) end
+ * and (physical) limit.
+ */
+
+/* Extract the LS bit of unsigned integer 'x'. */
+#define lsbit(x) ((x) & ((~(x)) + 1))
+/* Round 'x' up to a multiple of 'm' */
+#define multiple(x, m) ((((x) + (m) - 1) / (m)) * (m))
+
+/*------------------------------------------------------------------------------
+ * Set new limit to suit new end for given vector.
+ *
+ * The new limit will be at least: VECTOR_LIMIT_MIN.
+ *
+ * While the vector is relatively small, the limit is doubled until there
+ * is at least 1/8 of the new vector free.
+ *
+ * Beyond VECTOR_LIMIT_DOUBLE_MAX, however, the limit is set to the
+ * smallest multiple of VECTOR_LIMIT_DOUBLE_MAX which gives at least
+ * VECTOR_LIMIT_SLACK_MIN free entries beyond the new end.
+ *
+ * This is an attempt to balance the cost of repeated reallocations of
+ * memory against the cost of possible wasted space at the end of the
+ * vector.
+ *
+ * NB: the new_limit depends entirely on the new end position. (Current
+ * end position is ignored.)
+ *
+ * NB: the new limit may be less than the current limit, in which case the
+ * vector body is reduced in size.
+ *
+ * Except for any size set when the vector is initialised, the vector body
+ * size will be a power of 2 or a multiple of VECTOR_LIMIT_DOUBLE_MAX.
+ * (Vectors are regular in their habits, which may help the memory allocator).
+ *
+ * TODO: what to do if calculation of new_limit overflows, or calculation
+ * of P_ITEMS_SIZE will ?
+ */
+static void
+vector_new_limit(vector v, vector_index_t new_end)
+{
+ vector_length_t old_limit = v->limit ;
+ vector_length_t new_limit ;
+
+ if (new_end > ((VECTOR_LIMIT_DOUBLE_MAX * 7) / 8))
+ {
+ new_limit = multiple(new_end + VECTOR_LIMIT_SLACK_MIN,
+ VECTOR_LIMIT_DOUBLE_MAX) ;
+ }
+ else
+ {
+ /* Want the new_limit to be a power of 2. */
+ /* If the old_limit was a power of 2, start from there. */
+ /* Otherwise start from a power of 2 less than new_end: either the */
+ /* minimum value or a value mid way to VECTOR_LIMIT_DOUBLE_MAX. */
+ if ( (old_limit != 0) && (old_limit == lsbit(old_limit))
+ && (new_end >= old_limit) )
+ new_limit = old_limit ;
+ else
+ new_limit = (new_end < VECTOR_LIMIT_MID) ? VECTOR_LIMIT_MIN
+ : VECTOR_LIMIT_MID ;
+ while (new_end > ((new_limit * 7) / 8))
+ new_limit *= 2 ;
+ } ;
+
+ v->p_items =
+ XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+
+ v->limit = new_limit ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Extend vector and set new (logical) end.
+ *
+ * Extends body if required.
+ * Ensures everything between old and new end is set NULL.
+ *
+ * NB: expects new end > old end, but copes with new end <= old end.
+ */
+extern void
+vector_extend(vector v, vector_length_t new_end)
+{
+ vector_length_t old_end = v->end ;
+
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (new_end > old_end)
+ memset(&v->p_items[old_end], 0, P_ITEMS_SIZE(new_end - old_end)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Insert entries into vector: insert 'n' NULL entries at location 'i'.
+ *
+ * Updates end (and limit) to be at least 'i' + 'n'.
+ * (So if 'i' < end then end becomes end + 'n', else end becomes 'i' + 'n'.)
+ */
+extern void
+vector_insert(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_length_t old_end, new_end ;
+ vector_length_t n_above ;
+
+ /* If i < old end, then we are inserting n NULLs, and need
+ * to shuffle at least one item up.
+ * else we are setting new end to i + n and need to NULL
+ * fill from old end to the new end.
+ */
+ old_end = v->end ;
+ if (i < old_end)
+ {
+ if (n == 0)
+ return ; /* give up now if not inserting anything */
+ n_above = old_end - i ; /* number of items to shuffle up.. >= 1 */
+ new_end = old_end + n ;
+ }
+ else
+ {
+ n_above = 0 ; /* nothing to shuffle up. */
+ new_end = i + n ;
+ i = old_end ; /* where to zeroize from */
+ n = new_end - old_end ; /* how much to zeroize */
+ } ;
+
+ /* Now we extend the body if we need to. */
+ if (new_end > v->limit)
+ vector_new_limit(v, new_end) ;
+ v->end = new_end ;
+
+ if (n_above > 0)
+ memmove(&v->p_items[i + n], &v->p_items[i], P_ITEMS_SIZE(n_above)) ;
+
+ memset(&v->p_items[i], 0, P_ITEMS_SIZE(n)) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Delete items from vector: delete 'n' items at location 'i'.
+ *
+ * Does nothing if 'i' is beyond current end of vector or if 'n' == 0.
+ *
+ * Deletes from 'i' to end if less than 'n' items to the end.
+ *
+ * NB: does NOT change the size of the body.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being deleted.
+*/
+extern void
+vector_delete(vector v, vector_index_t i, vector_length_t n)
+{
+ vector_length_t old_end, new_end ;
+
+ old_end = v->end ;
+
+ if ((i >= old_end) || (n == 0))
+ return ;
+
+ /* If i + n < old_end, we have 1 or more items to keep and move down */
+ if ((i + n) < old_end)
+ {
+ memmove(&v->p_items[i], &v->p_items[i + n],
+ P_ITEMS_SIZE(old_end - (i + n))) ;
+ new_end = old_end - n ;
+ }
+ else
+ {
+ new_end = i ; /* We are keeping nothing above 'i' */
+ /* NB: new_end < old_end */
+ } ;
+
+ v->end = new_end ; /* account for stuff dropped */
+} ;
+
+/*------------------------------------------------------------------------------
+ * Discard entries from vector: discard entries from location 'i' onwards.
+ *
+ * Releases memory from 'i' onwards.
+ * Releases the body altogether if this sets the vector empty ('i' == 0).
+ * Sets new end of vector iff 'i' < current end.
+ *
+ * Does nothing if 'i' is beyond current limit (physical end) of vector.
+ *
+ * NB: it is the caller's responsibility to have released any memory allocated
+ * for the items that are being discarded.
+*/
+extern void
+vector_discard(vector v, vector_index_t i)
+{
+ if (i >= v->limit)
+ return ;
+
+ if (i == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ v->limit = i ;
+ if (i < v->end)
+ v->end = i ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(i)) ;
+ } ;
+} ;
+
+/* Chop vector at the current (logical) end.
+ *
+ * Releases the body altogether if the vector is currently empty.
+ */
+extern void
+vector_chop(vector v)
+{
+ vector_length_t new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else if (new_limit != v->limit)
+ {
+ v->limit = new_limit ;
+ v->p_items = XREALLOC(MTYPE_VECTOR_BODY, v->p_items, P_ITEMS_SIZE(new_limit)) ;
+ } ;
+} ;
+
+/* Decant vector into a new body allocated to current logical size, and
+ * release the old body.
+ *
+ * Releases the body altogether if the vector is currently empty.
+*/
+void
+vector_decant(vector v)
+{
+ p_vector_item* p_old_body ;
+ vector_length_t new_limit = v->end ;
+
+ if (new_limit == 0)
+ vector_reset(v, 0) ; /* reset, without releasing the structure */
+ else
+ {
+ p_old_body = v->p_items ;
+
+ vector_init_new(v, new_limit) ; /* initialise with new body */
+
+ memcpy(v->p_items, p_old_body, P_ITEMS_SIZE(new_limit)) ;
+ /* copy the old body across */
+ v->end = new_limit ;
+
+ XFREE(MTYPE_VECTOR_BODY, p_old_body) ;
+ } ;
+} ;