mirror of
https://github.com/OPM/ResInsight.git
synced 2025-02-25 18:55:39 -06:00
Use commit 0e1e780fd6f18ce93119061e36a4fca9711bc020 Excluded multibuild folder, as this caused git issues
665 lines
20 KiB
C++
665 lines
20 KiB
C++
/*
|
|
Copyright (C) 2011 Equinor ASA, Norway.
|
|
|
|
The file 'vector.c' is part of ERT - Ensemble based Reservoir Tool.
|
|
|
|
ERT 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ERT 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 at <http://www.gnu.org/licenses/gpl.html>
|
|
for more details.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <ert/util/util.h>
|
|
#include <ert/util/node_data.hpp>
|
|
#include <ert/util/type_macros.hpp>
|
|
#include <ert/util/vector.hpp>
|
|
|
|
|
|
#define VECTOR_TYPE_ID 551087
|
|
#define VECTOR_DEFAULT_SIZE 10
|
|
|
|
|
|
struct vector_struct {
|
|
UTIL_TYPE_ID_DECLARATION;
|
|
int alloc_size; /* The number of elements allocated in the data vector - in general > size. */
|
|
int size; /* THe number of elements the user has added to the vector. */
|
|
node_data_type **data; /* node_data instances - which again contain user data. */
|
|
};
|
|
|
|
|
|
|
|
/* Small datastructure PURELY used for sorting the vector. */
|
|
|
|
typedef struct {
|
|
vector_cmp_ftype * user_cmp;
|
|
node_data_type * data;
|
|
int index;
|
|
} vector_sort_node_type;
|
|
|
|
|
|
UTIL_SAFE_CAST_FUNCTION(vector , VECTOR_TYPE_ID)
|
|
UTIL_IS_INSTANCE_FUNCTION(vector , VECTOR_TYPE_ID)
|
|
|
|
|
|
static void vector_resize__(vector_type * vector, int new_alloc_size) {
|
|
int i;
|
|
if (new_alloc_size < vector->alloc_size) {
|
|
/* The vector is shrinking. */
|
|
for (i=new_alloc_size; i < vector->alloc_size; i++)
|
|
node_data_free( vector->data[i] );
|
|
}
|
|
|
|
vector->data = (node_data_type**)util_realloc( vector->data , new_alloc_size * sizeof * vector->data );
|
|
for (i = vector->alloc_size; i < new_alloc_size; i++)
|
|
vector->data[i] = NULL; /* Initialising new nodes to NULL */
|
|
|
|
vector->alloc_size = new_alloc_size;
|
|
}
|
|
|
|
|
|
|
|
vector_type * vector_alloc_new() {
|
|
vector_type * vector = (vector_type*)util_malloc( sizeof * vector );
|
|
UTIL_TYPE_ID_INIT(vector , VECTOR_TYPE_ID);
|
|
vector->size = 0;
|
|
vector->alloc_size = 0;
|
|
vector->data = NULL;
|
|
vector_resize__(vector , VECTOR_DEFAULT_SIZE);
|
|
return vector;
|
|
}
|
|
|
|
|
|
void vector_grow_NULL( vector_type * vector , int new_size ) {
|
|
int i;
|
|
for (i = vector->size; i < new_size; i++)
|
|
vector_append_ref( vector , NULL );
|
|
}
|
|
|
|
/**
|
|
This functon will allocate a vector 'size' elements. Each of these
|
|
elements is initialized with NULL pointers.
|
|
*/
|
|
|
|
vector_type * vector_alloc_NULL_initialized( int size ) {
|
|
vector_type * vector = vector_alloc_new();
|
|
vector_grow_NULL( vector , size );
|
|
return vector;
|
|
}
|
|
|
|
|
|
static int vector_append_node(vector_type * vector , node_data_type * node);
|
|
|
|
|
|
/**
|
|
If the index is beyond the length of the vector the hole in the
|
|
vector will be filled with NULL nodes.
|
|
*/
|
|
|
|
static void vector_iset__(vector_type * vector , int index , node_data_type * node) {
|
|
if (index > vector->size)
|
|
vector_grow_NULL( vector , index );
|
|
|
|
if (index == vector->size)
|
|
vector_append_node( vector , node );
|
|
else {
|
|
if (vector->data[index] != NULL)
|
|
node_data_free( vector->data[index] );
|
|
|
|
vector->data[index] = node;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
This is the low level function opposite to the vector_idel()
|
|
function. A new value (node) is inserted at index, and the rest of
|
|
the vector is shifted to the right.
|
|
*/
|
|
|
|
static void vector_insert__(vector_type * vector , int index , node_data_type * node) {
|
|
if (vector->size == vector->alloc_size)
|
|
vector_resize__(vector , 2*(vector->alloc_size + 1));
|
|
{
|
|
int bytes_to_move = (vector->size - index) * sizeof * vector->data;
|
|
memmove(&vector->data[index + 1] , &vector->data[index] , bytes_to_move);
|
|
vector->data[index] = NULL; /* Otherwise the destructor might try to pick up on it in the vector_iset__() call below */
|
|
}
|
|
vector->size++;
|
|
vector_iset__( vector , index , node );
|
|
}
|
|
|
|
|
|
/**
|
|
This is the low-level append node function which actually "does
|
|
it", the node has been allocated in one of the front-end
|
|
functions. The return value is the index of the node (which can
|
|
subsequently be used in a vector_iget() call)).
|
|
*/
|
|
static int vector_append_node(vector_type * vector , node_data_type * node) {
|
|
if (vector->size == vector->alloc_size)
|
|
vector_resize__(vector , 2*(vector->alloc_size + 1));
|
|
|
|
vector->size++;
|
|
vector_iset__(vector , vector->size - 1 , node);
|
|
return vector->size - 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
This is like the vector_append_node() function, but the node is
|
|
pushed in at the front.
|
|
*/
|
|
|
|
static void vector_push_node(vector_type * vector , node_data_type * node) {
|
|
if (vector->size == vector->alloc_size)
|
|
vector_resize__(vector , 2*(vector->alloc_size + 1));
|
|
{
|
|
int bytes = vector->size * sizeof * vector->data;
|
|
if (bytes > 0) {
|
|
memmove(&vector->data[1] , vector->data , bytes);
|
|
vector->data[0] = NULL; /* Otherwise the destructor might try to pick up on it in the vector_iset__() call below */
|
|
}
|
|
}
|
|
vector->size++;
|
|
vector_iset__(vector , 0 , node);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Will append NULL pointers until the vectors length is equal to
|
|
@min_size.
|
|
*/
|
|
|
|
static void vector_assert_size( vector_type * vector , int min_size) {
|
|
while (vector->size < min_size)
|
|
vector_append_ref( vector , NULL );
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Append a user-pointer which comes without either copy constructor
|
|
or destructor, this implies that the calling scope has FULL
|
|
responsabilty for the storage of the data added to the vector.
|
|
*/
|
|
|
|
|
|
int vector_append_ref(vector_type * vector , const void * data) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , NULL);
|
|
return vector_append_node(vector , node);
|
|
}
|
|
|
|
void vector_push_front_ref(vector_type * vector , const void * data) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , NULL);
|
|
vector_push_node(vector , node);
|
|
}
|
|
|
|
void vector_iset_ref(vector_type * vector , int index , const void * data) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , NULL);
|
|
vector_iset__(vector , index , node);
|
|
}
|
|
|
|
void vector_safe_iset_ref(vector_type * vector , int index , const void * data) {
|
|
vector_assert_size( vector , index + 1);
|
|
vector_iset_ref( vector , index , data );
|
|
}
|
|
|
|
|
|
void vector_insert_ref(vector_type * vector , int index , const void * data) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , NULL);
|
|
vector_insert__(vector , index , node);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Append a user-pointer which the vector instance takes ownership
|
|
of. This means that when the vector is destroyed it calls the
|
|
destructor on the data which has been supplied. The calling scope
|
|
should basically let this object be - the vector has taken control.
|
|
*/
|
|
|
|
|
|
int vector_append_owned_ref(vector_type * vector , const void * data , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , del);
|
|
return vector_append_node(vector , node);
|
|
}
|
|
|
|
void vector_push_front_owned_ref(vector_type * vector , const void * data , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , del);
|
|
vector_push_node(vector , node);
|
|
}
|
|
|
|
|
|
void vector_iset_owned_ref(vector_type * vector , int index , const void * data , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , del);
|
|
vector_iset__(vector , index , node);
|
|
}
|
|
|
|
|
|
void vector_safe_iset_owned_ref(vector_type * vector , int index , const void * data, free_ftype * del) {
|
|
vector_assert_size( vector , index + 1);
|
|
vector_iset_owned_ref( vector , index , data , del);
|
|
}
|
|
|
|
|
|
void vector_insert_owned_ref(vector_type * vector , int index , const void * data , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, NULL , del);
|
|
vector_insert__(vector , index , node);
|
|
}
|
|
|
|
|
|
/**
|
|
This function appends a COPY of user object. This implies that the
|
|
calling scope is still responsible for the instance declared and
|
|
used in that scope, whereas the vector takes responsability of
|
|
freeing it's own copy.
|
|
*/
|
|
|
|
|
|
int vector_append_copy(vector_type * vector , const void * data , copyc_ftype * copyc , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, copyc , del);
|
|
return vector_append_node(vector , node);
|
|
}
|
|
|
|
void vector_push_copy(vector_type * vector , const void * data , copyc_ftype * copyc , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, copyc , del);
|
|
vector_push_node(vector , node);
|
|
}
|
|
|
|
|
|
void vector_iset_copy(vector_type * vector , int index , const void * data , copyc_ftype * copyc , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, copyc , del);
|
|
vector_iset__(vector , index , node);
|
|
}
|
|
|
|
|
|
void vector_safe_iset_copy(vector_type * vector , int index , const void * data, copyc_ftype * copyc , free_ftype * del) {
|
|
vector_assert_size( vector , index + 1);
|
|
vector_iset_copy( vector , index , data , copyc , del);
|
|
}
|
|
|
|
|
|
void vector_insert_copy(vector_type * vector , int index , const void * data , copyc_ftype * copyc , free_ftype * del) {
|
|
node_data_type * node = node_data_alloc_ptr( data, copyc , del);
|
|
vector_insert__(vector , index , node);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
A buffer is unstructured storage (i.e. a void *) which is destroyed
|
|
with free, and copied with malloc + memcpy. The vector takes a copy
|
|
of the buffer which is inserted (and freed on vector destruction).
|
|
*/
|
|
|
|
|
|
void vector_append_buffer(vector_type * vector , const void * buffer, int buffer_size) {
|
|
node_data_type * node = node_data_alloc_buffer( buffer , buffer_size );
|
|
vector_append_node(vector , node);
|
|
}
|
|
|
|
|
|
void vector_iset_buffer(vector_type * vector , int index , const void * buffer, int buffer_size) {
|
|
node_data_type * node = node_data_alloc_buffer( buffer , buffer_size );
|
|
vector_iset__(vector , index , node);
|
|
}
|
|
|
|
|
|
void vector_insert_buffer(vector_type * vector , int index , const void * buffer, int buffer_size) {
|
|
node_data_type * node = node_data_alloc_buffer( buffer , buffer_size );
|
|
vector_insert__(vector , index , node);
|
|
}
|
|
|
|
|
|
void vector_push_buffer(vector_type * vector , const void * buffer, int buffer_size) {
|
|
node_data_type * node = node_data_alloc_buffer( buffer , buffer_size );
|
|
vector_push_node(vector , node);
|
|
}
|
|
|
|
|
|
|
|
|
|
const void * vector_iget_const(const vector_type * vector, int index) {
|
|
if ((index >= 0) && (index < vector->size)) {
|
|
const node_data_type * node = vector->data[index];
|
|
return node_data_get_ptr( node );
|
|
} else {
|
|
util_abort("%s: Invalid index:%d Valid range: [0,%d> \n",__func__ , index , vector->size);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void * vector_iget(const vector_type * vector, int index) {
|
|
if ((index >= 0) && (index < vector->size)) {
|
|
const node_data_type * node = vector->data[index];
|
|
return node_data_get_ptr( node );
|
|
} else {
|
|
util_abort("%s: Invalid index:%d Valid range: [0,%d> \n",__func__ , index , vector->size);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
The safe_iget() functions will return NULL if index is greater than
|
|
the length of the vector.
|
|
*/
|
|
|
|
const void * vector_safe_iget_const(const vector_type * vector, int index) {
|
|
if ((index >= 0) && (index < vector->size)) {
|
|
const node_data_type * node = vector->data[index];
|
|
return node_data_get_ptr( node );
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void * vector_safe_iget(const vector_type * vector, int index) {
|
|
if ((index >= 0) && (index < vector->size)) {
|
|
const node_data_type * node = vector->data[index];
|
|
return node_data_get_ptr( node );
|
|
} else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Removes element nr index from the vector, if a destructor is
|
|
associated with element 'index' it is called, and the memory
|
|
freed. Afterwards all elements at positions (index +1) and onwards
|
|
are shifted one element to the left.
|
|
*/
|
|
|
|
void vector_idel(vector_type * vector , int index) {
|
|
if ((index >= 0) && (index < vector->size)) {
|
|
node_data_type * node = vector->data[index];
|
|
node_data_free( node ); /* Discard the element */
|
|
{
|
|
int bytes_to_move = (vector->size - 1 - index) * sizeof * vector->data;
|
|
memmove(&vector->data[index] , &vector->data[index + 1] , bytes_to_move);
|
|
vector->data[vector->size - 1] = NULL; /* Clear the last element - which is no longer valid. */
|
|
vector->size--;
|
|
}
|
|
} else
|
|
util_abort("%s: Invalid index:%d Valid range: [0,%d> \n",__func__ , index , vector->size);
|
|
}
|
|
|
|
|
|
/**
|
|
This function will remove the last elements of the vector, so that
|
|
the new size becomes @new_size. If the @new_size is greater than
|
|
the current length of the vector the function will fail HARD.
|
|
*/
|
|
|
|
|
|
void vector_shrink( vector_type * vector , int new_size ) {
|
|
if (new_size > vector->size)
|
|
util_abort("%s: Tried to \'shrink\' vector to %d elements - current size:%d\n",__func__ , new_size , vector->size);
|
|
{
|
|
int index;
|
|
for (index = (vector->size - 1); index >= new_size; index--)
|
|
vector_idel( vector , index );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Will abort if the vector is empty.
|
|
*/
|
|
void * vector_get_last(const vector_type * vector) {
|
|
if (vector->size == 0)
|
|
util_abort("%s: asking to get the last element in an empty vector - impossible ... \n",__func__);
|
|
{
|
|
const node_data_type * node = vector->data[vector->size - 1];
|
|
return node_data_get_ptr( node );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Will abort if the vector is empty.
|
|
*/
|
|
const void * vector_get_last_const(const vector_type * vector) {
|
|
if (vector->size == 0)
|
|
util_abort("%s: asking to get the last element in an empty vector - impossible ... \n",__func__);
|
|
{
|
|
const node_data_type * node = vector->data[vector->size - 1];
|
|
return node_data_get_ptr( node );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
This function removes the last element from the vector and returns
|
|
it to the calling scope. Irrespective of whether the element _was_
|
|
inserted with a destructor: when calling vector_pop() the calling
|
|
scope takes responsability for freeing data; i.e. vector_pop will
|
|
NEVER call a destructor.
|
|
*/
|
|
|
|
|
|
void * vector_pop_back(vector_type * vector) {
|
|
if (vector->size == 0)
|
|
util_abort("%s: asking to get the last element in an empty vector - impossible ... \n",__func__);
|
|
{
|
|
node_data_type * node = vector->data[vector->size - 1];
|
|
void * data = node_data_get_ptr( node );
|
|
|
|
node_data_free_container( node ); /* Free the container holding data. */
|
|
vector->data[ vector->size -1 ] = NULL;
|
|
vector->size--; /* Shrink the vector */
|
|
return data;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Removes the first element from the vector and returns it - similar
|
|
to vector_pop():
|
|
*/
|
|
|
|
void * vector_pop_front(vector_type * vector ) {
|
|
if (vector->size == 0)
|
|
util_abort("%s: asking to get the last element in an empty vector - impossible ... \n",__func__);
|
|
{
|
|
node_data_type * node = vector->data[0];
|
|
void * data = node_data_get_ptr( node );
|
|
|
|
node_data_free_container( node ); /* Free the container holding data. */
|
|
{
|
|
int bytes = (vector->size - 1) * sizeof * vector->data; /* Move the storage one element to the left (could als be implemented with an offset??). */
|
|
memmove( vector->data , &vector->data[1] , bytes);
|
|
}
|
|
vector->size--; /* Shrink the vector */
|
|
return data;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int vector_get_size( const vector_type * vector) {
|
|
return vector->size;
|
|
}
|
|
|
|
|
|
/**
|
|
This vector frees all the storage of the vector, including all the
|
|
nodes which have been installed with a destructor.
|
|
*/
|
|
|
|
void vector_clear(vector_type * vector) {
|
|
int i;
|
|
for (i = 0; i < vector->size; i++) {
|
|
node_data_free(vector->data[i]); /* User specific destructors are called here. */
|
|
vector->data[i] = NULL; /* This is essential to protect against unwaranted calls to destructors when data is reused. */
|
|
}
|
|
vector->size = 0;
|
|
}
|
|
|
|
|
|
void vector_free(vector_type * vector) {
|
|
vector_clear( vector );
|
|
free( vector->data );
|
|
free( vector );
|
|
}
|
|
|
|
|
|
void vector_free__( void * arg ) {
|
|
vector_free( vector_safe_cast( arg ));
|
|
}
|
|
|
|
|
|
static int vector_cmp(const void * s1 , const void * s2) {
|
|
const vector_sort_node_type * node1 = (const vector_sort_node_type *) s1;
|
|
const vector_sort_node_type * node2 = (const vector_sort_node_type *) s2;
|
|
|
|
return node1->user_cmp(node_data_get_ptr(node1->data) , node_data_get_ptr(node2->data));
|
|
}
|
|
|
|
|
|
/**
|
|
This function will sort the vector content in place. The sorting is
|
|
based on a user-supplied cmp function which should return -1,0,1
|
|
when comparing two elements. The prototype of this function is
|
|
|
|
int (* user_cmp) (const void *, const void *)
|
|
|
|
i.e. the same as for qsort. The vector implementation considers
|
|
(fully) untyped data, it is therefore the users responsability to
|
|
ensure that the comparison makes sense. For example:
|
|
|
|
|
|
double * p = util_malloc(10 * sizeof * p );
|
|
vector_append_buffer(vector , "This is a string ..." , strlen());
|
|
vector_append_buffer(vector , p , 10 * sizeof * p);
|
|
|
|
Here we have inserted one (char *) and one (double *). When these
|
|
elements arrive in the sort function they will just be (void *),
|
|
and the comparison will be quite meaningless(??).
|
|
*/
|
|
|
|
|
|
static vector_sort_node_type * vector_alloc_sort_data( const vector_type * vector , vector_cmp_ftype * cmp) {
|
|
vector_sort_node_type * sort_data = (vector_sort_node_type*)util_calloc( vector->size , sizeof * sort_data );
|
|
int i;
|
|
|
|
/* Fill up the temporary storage used for sorting */
|
|
for (i = 0; i < vector->size; i++) {
|
|
sort_data[i].data = vector->data[i];
|
|
sort_data[i].user_cmp = cmp;
|
|
sort_data[i].index = i;
|
|
}
|
|
|
|
/* Sort the temporary vector */
|
|
qsort(sort_data , vector->size , sizeof * sort_data , vector_cmp);
|
|
|
|
return sort_data;
|
|
}
|
|
|
|
|
|
void vector_sort(vector_type * vector , vector_cmp_ftype * cmp) {
|
|
vector_sort_node_type * sort_data = vector_alloc_sort_data( vector , cmp );
|
|
int i;
|
|
/* Recover the sorted vector */
|
|
for (i = 0; i < vector->size; i++)
|
|
vector->data[i] = sort_data[i].data;
|
|
|
|
free( sort_data );
|
|
}
|
|
|
|
|
|
int_vector_type * vector_alloc_sort_perm(const vector_type * vector , vector_cmp_ftype * cmp) {
|
|
vector_sort_node_type * sort_data = vector_alloc_sort_data( vector , cmp );
|
|
int_vector_type * sort_perm = int_vector_alloc(0,0);
|
|
int i;
|
|
int_vector_resize( sort_perm, vector->size, 0 );
|
|
for (i = 0; i < vector->size; i++)
|
|
int_vector_iset( sort_perm , i , sort_data[i].index);
|
|
|
|
free( sort_data );
|
|
return sort_perm;
|
|
}
|
|
|
|
|
|
void vector_permute(vector_type * vector , const int_vector_type * perm_vector) {
|
|
node_data_type ** new_data = (node_data_type**)util_calloc( vector->size , sizeof * new_data );
|
|
for (int index = 0; index < vector->size; index++) {
|
|
int perm_index = int_vector_iget( perm_vector , index );
|
|
new_data[index] = vector->data[ perm_index ];
|
|
}
|
|
free(vector->data);
|
|
vector->data = new_data;
|
|
}
|
|
|
|
|
|
|
|
void vector_inplace_reverse(vector_type * vector) {
|
|
if (vector->size > 0) {
|
|
node_data_type ** new_data = (node_data_type**)util_calloc( vector->size , sizeof * new_data );
|
|
int index;
|
|
for (index = 0; index < vector->size; index++) {
|
|
int rev_index = vector->size - 1 - index;
|
|
new_data[index] = vector->data[ rev_index ];
|
|
}
|
|
free(vector->data);
|
|
vector->data = new_data;
|
|
}
|
|
}
|
|
|
|
|
|
int vector_find( const vector_type * vector , const void * ptr) {
|
|
int location_index = -1;
|
|
int index = 0;
|
|
|
|
while (true) {
|
|
if (index < vector_get_size( vector )) {
|
|
const void * element = vector_iget( vector , index );
|
|
if (element == ptr) {
|
|
location_index = index;
|
|
break;
|
|
} else
|
|
index++;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
return location_index;
|
|
}
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
|
|
/*
|
|
If deep_copy == true - all elements in the vector MUST have
|
|
constructor, otherwise the node_data_alloc_copy() function will fail.
|
|
*/
|
|
|
|
vector_type * vector_alloc_copy(const vector_type * src , bool deep_copy) {
|
|
vector_type * copy = vector_alloc_new();
|
|
int i;
|
|
for (i=0; i < src->size; i++) {
|
|
node_data_type * node_copy = node_data_alloc_copy( src->data[i] , deep_copy);
|
|
vector_append_node( copy , node_copy );
|
|
}
|
|
return copy;
|
|
}
|
|
|