/*****
* array.cc
* Andy Hammerlindl 2008/01/26
*
* Array type used by virtual machine.
*****/
#include "array.h"
#include "mod.h"
namespace vm {
const char *dereferenceNullArray="dereference of null array";
inline void checkBackSlice(Int left, Int right)
{
if (right < left)
// There isn't a clear behaviour for slices of the form A[5:2], so we don't
// allow them. (Atleast, not until we can figure out what the behaviour
// should be.)
vm::error("slice ends before it begins");
}
inline size_t sliceIndex(Int in, size_t len) {
if (in < 0)
// The Python behaviour here would simply be
// in += len;
// but this is inconsistent with Asymptote issuing an error for A[-1] if A
// is a non-cyclic array, so we also issue an error here.
vm::error("invalid negative index in slice of non-cyclic array");
if (in < 0)
return 0;
size_t index = (size_t)in;
return index < len ? index : len;
}
array *array::slice(Int left, Int right)
{
checkBackSlice(left, right);
if (left == right)
return new array();
size_t length=size();
if (length == 0)
return new array();
if (cycle) {
size_t resultLength = (size_t)(right - left);
array *result = new array(resultLength);
size_t i = (size_t)imod(left, length), ri = 0;
while (ri < resultLength) {
(*result)[ri] = (*this)[i];
++ri;
++i;
if (i >= length)
i -= length;
}
return result;
}
else { // Non-cyclic
size_t l = sliceIndex(left, length);
size_t r = sliceIndex(right, length);
size_t resultLength = r - l;
array *result = new array(resultLength);
size_t asize=a->size();
if (asize == sliceLength) {
// In place
std::copy(a->begin(), a->end(), this->begin()+l);
}
else if (asize < sliceLength) {
// Shrinking
std::copy(a->begin(), a->end(), this->begin()+l);
this->erase(this->begin()+(l+a->size()), this->begin()+r);
}
else {
// Expanding
// NOTE: As a speed optimization, we could check capacity() to see if the
// array can fit the new entries, and build the new array from scratch
// (using swap()) if a new allocation is necessary.
std::copy(a->begin(), a->begin()+sliceLength, this->begin()+l);
this->insert(this->begin()+r, a->begin()+sliceLength, a->end());
}
}
void array::setSlice(Int left, Int right, array *a)
{
checkBackSlice(left, right);
// If we are slicing an array into itself, slice in a copy instead, to ensure
// the proper result.
mem::vector<item> *v = (a == this) ? new mem::vector<item>(*a) : a;
size_t length=size();
if (cycle) {
if (right == left) {
// Notice that assigning to the slice A[A.length:A.length] is the same as
// assigning to the slice A[0:0] for a cyclic array.
size_t l = (size_t)imod(left, length);
setNonBridgingSlice(l, l, v);
}
else {
if (left + (Int) length < right)
vm::error("assigning to cyclic slice with repeated entries");
size_t l = (size_t)imod(left, length);
// Set r to length instead of zero, so that slices that go to the end of
// the array are properly treated as non-bridging.
size_t r = (size_t)imod(right, length);
if (r == 0)
r = length;
if (l < r)
setNonBridgingSlice(l, r, v);
else {
if (r + length - l == v->size())
setBridgingSlice(l, r, v);
else
vm::error("assignment to cyclic slice is not well defined");
}
}
}
else {
size_t l=sliceIndex(left, length);
size_t r=sliceIndex(right, length);