/*
* tarrayobj.cc
*
* This file is part of NEST.
*
* Copyright (C) 2004 The NEST Initiative
*
* NEST 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 of the License, or
* (at your option) any later version.
*
* NEST 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 NEST. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "tarrayobj.h"
#include "datum.h"
#include "token.h"
#ifdef assert
#undef assert
#define assert(a)
#endif
size_t TokenArrayObj::allocations=0;
TokenArrayObj::TokenArrayObj(size_t s, const Token &t, size_t alloc)
:p(NULL),begin_of_free_storage(NULL),
end_of_free_storage(NULL),alloc_block_size(ARRAY_ALLOC_SIZE),refs_(1)
{
size_t a = (alloc == 0)? s : alloc;
resize(s,a,t);
}
TokenArrayObj::TokenArrayObj(const TokenArrayObj &a)
:p(NULL),begin_of_free_storage(NULL),
end_of_free_storage(NULL),alloc_block_size(ARRAY_ALLOC_SIZE),refs_(1)
{
if(a.p != NULL)
{
resize(a.size(),a.alloc_block_size,Token());
Token *from = a.p;
Token *to = p;
while(to < begin_of_free_storage)
*to++ = *from++;
}
}
TokenArrayObj::~TokenArrayObj()
{
if(p)
delete [] p;
}
void TokenArrayObj::allocate(size_t new_s, size_t new_c, size_t new_a, const Token &t )
{
// This resize function is private and does an unconditional resize, using
// all supplied parameters.
alloc_block_size = new_a;
size_t min_l;
size_t old_s = size();
assert(new_c != 0);
assert(new_a != 0);
Token *h = new Token[new_c];
assert(h != NULL);
if(t != Token())
for(Token *hi=h; hi <h+new_c; ++hi)
(*hi)=t;
end_of_free_storage = h + new_c; // [,) convention
begin_of_free_storage= h + new_s;
if(p!=NULL)
{
if(old_s < new_s)
{
min_l = old_s;
}
else
{
min_l = new_s;
}
for(size_t i=0; i< min_l; ++i) // copy old parts
h[i].move(p[i]);
delete [] p;
}
p = h;
assert(p!=NULL);
++allocations;
}
void TokenArrayObj::resize(size_t s, size_t alloc, const Token &t )
{
alloc_block_size = (alloc == 0)? alloc_block_size : alloc;
if((s!=size() && (s!=0)) || (size()==0 && alloc_block_size != 0))
allocate(s, s+alloc_block_size, alloc_block_size, t);
}
void TokenArrayObj::resize(size_t s, const Token &t )
{
resize(s,alloc_block_size,t);
}
const TokenArrayObj & TokenArrayObj::operator=(const TokenArrayObj &a)
{
if(capacity() >= a.size())
// This branch also covers the case where a is the null-vector.
{
Token *to = begin();
Token *from = a.begin();
while(from < a.end())
*to++ = *from++;
while(to < end())
{
to->clear();
to++;
}
begin_of_free_storage = p+a.size();
assert(begin_of_free_storage <= end_of_free_storage);
} else
{
if(p !=NULL)
{
delete [] p;
p = NULL;
}
resize(a.size(),a.alloc_block_size);
Token *to = begin();
Token *from = a.begin();
while(from < a.end())
*to++ = *from++;
begin_of_free_storage = to;
assert(begin_of_free_storage <= end_of_free_storage);
}
return *this;
}
// re-allocate, if the actual buffer is larger
// than alloc_block_size
// bool TokenArrayObj::shrink(void)
// {
// static const size_t hyst=1;
// size_t old_size = size();
// size_t n = old_size/alloc_block_size + 1 + hyst;
// size_t new_capacity = n*alloc_block_size;
// if( new_capacity < capacity())
// {
// allocate(old_size, new_capacity, alloc_block_size);
// return true;
// }
// return false;
// }
bool TokenArrayObj::shrink(void)
{
size_t new_capacity = size();
if( new_capacity < capacity())
{
allocate(size(), new_capacity, alloc_block_size);
return true;
}
return false;
}
bool TokenArrayObj::reserve(size_t new_capacity)
{
if(new_capacity > capacity() )
{
allocate(size(), new_capacity, alloc_block_size);
return true;
}
return false;
}
void TokenArrayObj::rotate(Token *first, Token *middle, Token *last)
{
// This algorithm is taken from the HP STL implementation.
if((first < middle) && (middle < last))
for(Token *i = middle; ;)
{
first->swap(*i);
i++; first++;
if(first == middle)
{
if(i == last) return;
middle = i;
}
else if (i == last)
i = middle;
}
}
void TokenArrayObj::erase(Token* first, Token *last)
{
// this algorithm we also use in replace_move
// array is decreasing. we move elements after point of
// erasure from right to left
Token *from = last;
Token *to = first;
Token *end = begin_of_free_storage; // 1 ahead as conventional
while (from<end)
{
if(to->p)
to->p->removeReference(); // deleting NULL pointer is safe in ISO C++
to->p=from->p; // move
from->p=NULL; // might be overwritten or not
++from;
++to;
}
while (last>to) // if sequence we have to erase is
{ // longer than the sequence to the
--last; // right of it, we explicitly delete the
if(last->p)
last->p->removeReference(); // elements which are still intact
last->p=NULL; // after the move above.
}
begin_of_free_storage=to;
}
// as for strings erase tolerates i+n >= size()
//
void TokenArrayObj::erase(size_t i, size_t n)
{
if (i+n<size())
erase(p+i, p+i+n );
else
erase(p+(i), p+size());
}
void TokenArrayObj::clear(void)
{
if(p)
delete [] p;
p = begin_of_free_storage = end_of_free_storage = NULL;
alloc_block_size = 1;
}
// reduce() could be further optimized by testing wether the
// new size leads to a resize. In this case, one could simply
// re-construct the array with the sub-array.
void TokenArrayObj::reduce(Token* first, Token *last)
{
assert(last<=end());
assert(first>= p);
// First step: shift all elements to the begin of
// the array.
Token *i= p, *l= first;
if(first > begin())
{
while(l < last)
{
i->move(*l);
i++; l++;
}
assert( l == last);
}
else
i = last;
assert( i == p + (last - first));
while (i < end())
{
i->clear();
i++;
}
begin_of_free_storage = p + (size_t)(last-first);
//shrink();
}
// as assign for strings reduce tolerates i+n >= size()
//
void TokenArrayObj::reduce(size_t i, size_t n)
{
if (i+n<size())
reduce(p+i, p+i+n );
else
reduce(p+(i), p+size());
}
void TokenArrayObj::insert(size_t i, size_t n, const Token &t)
{
// pointer argument pos would not be efficient because we
// have to recompute pointer anyway after reallocation
reserve(size()+n); // reallocate if necessary
Token *pos = p + i; // pointer to element i (starting with 0)
Token *from = begin_of_free_storage-1; // first Token which has to be moved
Token *to = from + n; // new location of first Token
while(from >= pos)
{
to->p = from->p; // move
from->p = NULL; // knowing that to->p is
--from; --to; // NULL before
}
for(size_t i=0; i<n; ++i) // insert n copies of Token t;
*(pos++) = t;
begin_of_free_storage+=n; // new size is old + n
}
void TokenArrayObj::insert_move(size_t i, TokenArrayObj &a)
{
reserve(size()+a.size()); // reallocate if necessary
assert(begin_of_free_storage + a.size() <= end_of_free_storage); // check
Token *pos = p + i; // pointer to element i (starting with 0)
Token *from = begin_of_free_storage-1; // first Token which has to be moved
Token *to = from + a.size(); // new location of first Token
while(from >= pos)
{
to->p = from->p; // move
from->p = NULL; // knowing that to->p is
--from; --to; // NULL before
}
from = a.p;
to = p + i;
while( from < a.end())
{
to->p = from->p; // we cannot do this in the loop
from->p = NULL; // above because of overlapping
++from;
++to;
}
begin_of_free_storage+=a.size(); // new size is old + n
a.begin_of_free_storage = a.p; // a is empty.
}
void TokenArrayObj::assign_move(TokenArrayObj &a, size_t i, size_t n)
{
reserve(n);
Token *from = a.begin()+i;
Token *end = a.begin()+i+n;
Token *to = p;
while (from < end)
{
to->p = from->p;
from->p = NULL;
++from;
++to;
}
begin_of_free_storage = p+n;
}
void TokenArrayObj::assign(const TokenArrayObj &a, size_t i, size_t n)
{
reserve(n);
Token *from = a.begin()+i;
Token *end = a.begin()+i+n;
Token *to = p;
while (from < end)
{
*to = *from;
++from;
++to;
}
begin_of_free_storage = p+n;
}
void TokenArrayObj::insert_move(size_t i, Token &t)
{
reserve(size()+1); // reallocate if necessary
assert(begin_of_free_storage + 1 <= end_of_free_storage); // check
Token *pos = p + i; // pointer to element i (starting with 0)
Token *from = begin_of_free_storage-1; // first Token which has to be moved
Token *to = from + 1; // new location of first Token
while(from >= pos)
{
to->p = from->p; // move
from->p = NULL; // knowing that to->p is
--from; --to; // NULL before
}
(p+i)->p=t.p; // move contens of t
t.p=NULL;
begin_of_free_storage+=1; // new size is old + 1
}
void TokenArrayObj::replace_move(size_t i, size_t n, TokenArrayObj &a)
{
assert(i<size()); // assume index in range
n = (size()-i<n)?(size()-i):n; // n more than available is allowed
long d=a.size()-n; // difference size after the replacement,
// positive for increase
reserve(size()+d); // reallocate if necessary
if (d>0)
{
// array is increasing. we move elements after point of
// replacement from left to right
Token *from = begin_of_free_storage-1;
Token *to = begin_of_free_storage-1+d;
Token *end = p+i+n-1; // 1 ahead (before) as conventional
while (from>end)
{
to->p=from->p; // move
from->p=NULL; // might be overwritten or not
--from;
--to;
}
}
else if (d<0)
{
// array is decreasing. we move elements after point of
// replacement from right to left
Token *last = p+i+n;
Token *from = last;
Token *to = p+i+a.size();
Token *end = begin_of_free_storage; // 1 ahead as conventional
while (from<end)
{
if(to->p)
to->p->removeReference(); // deleting NULL pointer is safe in ISO C++
to->p=from->p; // move
from->p=NULL; // might be overwritten or not
++from;
++to;
}
while (last>to) // if sequence we have to erase is
{ // longer than a plus the sequence to the
--last; // right of it, we explicitly delete the
if(last->p)
last->p->removeReference(); // elements which are still intact
last->p=NULL; // after the move above.
}
}
begin_of_free_storage+=d; // set new size
// move contens of array a
Token *to = p+i;
Token *end = a.end(); // 1 ahead as conventional
Token *from = a.begin();
while(from<end)
{
if(to->p)
to->p->removeReference(); // delete target before
to->p=from->p; // movement, it is typically
from->p= NULL; // not the NULL pointer
++from;
++to;
}
}
void TokenArrayObj::append_move(TokenArrayObj &a )
{
reserve(size()+a.size()); // reallocate if necessary
assert(begin_of_free_storage + a.size() <= end_of_free_storage); // check
Token *from = a.p;
Token *to = begin_of_free_storage;
while( from < a.end()) // move
{ // knowing that to->p is
to->p = from->p; // NULL before
from->p = NULL;
++from;
++to;
}
begin_of_free_storage+=a.size(); // new size is old + n
a.begin_of_free_storage = a.p; // a is empty.
}
bool TokenArrayObj::operator==(const TokenArrayObj &a) const
{
//std::cout << "comparison of TokenArrayObj" << std::endl;
//std::cout << "p: " << p << std::endl;
//std::cout << "a.p: " << a.p << std::endl;
if( p == a.p )
return true;
// experimentally replaced by line below 090120, Diesmann
// because [] cvx has non NULL p
//
// if( p == NULL || a.p == NULL || size() != a.size())
// return false;
if( size() != a.size() )
return false;
Token *i= begin(),*j = a.begin();
while(i < end())
if(!(*i++ == *j++))
return false;
return true;
}
void TokenArrayObj::info(std::ostream& out) const
{
out << "TokenArrayObj::info\n";
out << "p = " << p << std::endl;
out << "bofs = " << begin_of_free_storage << std::endl;
out << "eofs = " << end_of_free_storage << std::endl;
out << "abs = " << alloc_block_size << std::endl;
}
bool TokenArrayObj::valid(void) const
{
if(p == NULL)
{
std::cerr << "TokenArrayObj::valid: Data pointer missing!" << std::endl;
return false;
}
if(begin_of_free_storage == NULL)
{
std::cerr << "TokenArrayObj::valid: begin of free storage pointer missing!"
<< std::endl;
return false;
}
if(end_of_free_storage == NULL)
{
std::cerr << "TokenArrayObj::valid: end of free storage pointer missing!"
<< std::endl;
return false;
}
if(begin_of_free_storage >end_of_free_storage)
{
std::cerr << "TokenArrayObj::valid: begin_of_free_storage > end_of_free_storage !"
<< std::endl;
return false;
}
return true;
}
std::ostream & operator<<(std::ostream& out, const TokenArrayObj& a)
{
for(Token *i = a.begin(); i< a.end() ; ++i)
out << *i << ' ';
return out;
}