#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
MODULE = Array::Splice PACKAGE = Array::Splice
void
_splice(array,offset,length,...)
SV* array;
I32 offset;
I32 length;
INIT:
AV* ary;
I32 i;
I32 newlen;
I32 after;
I32 diff;
SV **tmparyval = 0;
MAGIC *mg;
register SV **src;
register SV **dst;
PPCODE:
if ( !SvROK(array) || SvTYPE(SvRV(array)) != SVt_PVAV ) {
croak("first argument to Array::Splice::_splice() not an array");
}
ary = (AV*) SvRV(array);
if ((mg = SvTIED_mg((SV*)ary, 'P'))) {
croak("Array::Splice::_splice() not implemented for tied arrays");
}
i = offset;
if (offset < 0)
offset += AvFILLp(ary) + 1;
if (offset < 0)
croak(PL_no_aelem, i);
if (length < 0) {
length += AvFILLp(ary) - offset + 1;
if (length < 0)
length = 0;
}
if (offset > AvFILLp(ary) + 1) {
if (ckWARN(WARN_MISC))
warn("Array::Splice::_splice() offset past end of array" );
offset = AvFILLp(ary) + 1;
}
after = AvFILLp(ary) + 1 - (offset + length);
if (after < 0) { /* not that much array */
length += after; /* offset+length now in array */
after = 0;
if (!AvALLOC(ary))
av_extend(ary, 0);
}
newlen = items - 3;
diff = newlen - length;
/* inc refcounts now: avoid problems if they're from the array */
for (src = &ST(3), i = newlen; i; i--) {
SvREFCNT_inc(*src++);
}
if (diff < 0) { /* shrinking the area */
if (newlen) {
New(451, tmparyval, newlen, SV*); /* so remember insertion */
Copy(&ST(3), tmparyval, newlen, SV*);
}
if (GIMME == G_ARRAY) { /* copy return vals to stack */
MEXTEND(SP, length);
src = AvARRAY(ary)+offset;
i = length;
if (AvREAL(ary)) {
for ( ; i; i--) {
PUSHs(sv_2mortal(*src++)); /* free them eventualy */
}
} else {
for ( ; i; i--) {
PUSHs(*src++); /* don't free them eventualy */
}
}
}
else {
PUSHs(sv_2mortal(AvARRAY(ary)[offset+length-1]));
if (AvREAL(ary)) {
for (i = length - 1, dst = &AvARRAY(ary)[offset]; i > 0; i--)
SvREFCNT_dec(*dst++); /* free them now */
}
}
AvFILLp(ary) += diff;
/* pull up or down? */
if (offset < after) { /* easier to pull up */
if (offset) { /* esp. if nothing to pull */
src = &AvARRAY(ary)[offset-1];
dst = src - diff; /* diff is negative */
for (i = offset; i > 0; i--) /* can't trust Copy */
*dst-- = *src--;
}
dst = AvARRAY(ary);
SvPVX(ary) = (char*)(AvARRAY(ary) - diff); /* diff is negative */
AvMAX(ary) += diff;
}
else {
if (after) { /* anything to pull down? */
src = AvARRAY(ary) + offset + length;
dst = src + diff; /* diff is negative */
Move(src, dst, after, SV*);
}
dst = &AvARRAY(ary)[AvFILLp(ary)+1];
/* avoid later double free */
}
i = -diff;
while (i)
dst[--i] = &PL_sv_undef;
if (newlen) {
for (src = tmparyval, dst = AvARRAY(ary) + offset;
newlen; newlen--) {
*dst++ = *src++;
}
Safefree(tmparyval);
}
}
else { /* no, expanding (or same) */
if (length) {
New(452, tmparyval, length, SV*); /* so remember deletion */
Copy(AvARRAY(ary)+offset, tmparyval, length, SV*);
}
if (diff > 0) { /* expanding */
/* push up or down? */
if (offset < after && diff <= AvARRAY(ary) - AvALLOC(ary)) {
if (offset) {
src = AvARRAY(ary);
dst = src - diff;
Move(src, dst, offset, SV*);
}
SvPVX(ary) = (char*)(AvARRAY(ary) - diff);/* diff is positive */
AvMAX(ary) += diff;
AvFILLp(ary) += diff;
}
else {
if (AvFILLp(ary) + diff >= AvMAX(ary)) /* oh, well */
av_extend(ary, AvFILLp(ary) + diff);
AvFILLp(ary) += diff;
if (after) {
dst = AvARRAY(ary) + AvFILLp(ary);
src = dst - diff;
for (i = after; i; i--) {
*dst-- = *src--;
}
}
}
}
for (src = &ST(3), dst = AvARRAY(ary) + offset; newlen; newlen--) {
*dst++ = *src++;
}
if (GIMME == G_ARRAY) { /* copy return vals to stack */
if (length) {
src = tmparyval;
if (AvREAL(ary)) {
for (i = length; i; i--) {
PUSHs(sv_2mortal(*src++)); /* free them eventualy */
}
} else {
for (i = length; i; i--) {
PUSHs(*src++); /* don't free them eventualy */
}
}
Safefree(tmparyval);
}
}
else if (length--) {
PUSHs(sv_2mortal(tmparyval[length]));
if (AvREAL(ary)) {
while (length-- > 0)
SvREFCNT_dec(tmparyval[length]);
}
Safefree(tmparyval);
}
}