/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- pop
 - push
 - reverse
 - shift
 - splice
 - unshift
 - checkCapacity
 - push
 - push
 - removeAt
 - insert
 - setAt
 - getAt
 - clear
 
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
/////////////////////////////////////////////////////////
// AtomArray object
/////////////////////////////////////////////////////////
#include "avmplus.h"
namespace avmplus
{
        using namespace MMgc;
        AtomArray::AtomArray (int initialCapacity)
        {
                 m_length = 0; 
                 if (!initialCapacity)
                 {
                        m_atoms = 0; 
                 }
                 else
                 {
                        if (initialCapacity < kMinCapacity) 
                                initialCapacity = kMinCapacity;
                        GC *gc = GC::GetGC(this);
                        setAtoms(gc, (Atom*) gc->Calloc(initialCapacity, sizeof(Atom), GC::kContainsPointers|GC::kZero));
                 }
        }
        AtomArray::~AtomArray()
        {
                clear();  
        }
        /////////////////////////////////////////////////////
        // Array AS API
        /////////////////////////////////////////////////////
        // Pop last element off the end of the array and shrink length
        Atom AtomArray::pop()
        {
                if (!m_length)
                        return undefinedAtom;
                Atom retAtom = m_atoms[m_length - 1];
                setAtInternal(m_length - 1, 0); // so GC collects this item
                m_length--;
                return retAtom;
        }
        using namespace MMgc;
        // n arguments are pushed on the array
        uint32 AtomArray::push(Atom *args, int argc)
        {
                checkCapacity (m_length + argc);
                // slow path to trigger write barrier
                for(int i=0; i < argc; i++) {
                        push(args[i]);
                }
                return argc;
        }
        // Reverse array elements
        void AtomArray::reverse()
        {
                if (m_length > 1)
                {
                        for (uint32 k = 0; k < (m_length >> 1); k++)
                        {
                                Atom temp = m_atoms[k];
                                m_atoms[k] = m_atoms[m_length - k - 1];
                                m_atoms[m_length - k - 1] = temp;
                        }
                }
        }
        // return 0th element, shift rest down
        Atom AtomArray::shift()
        {
                if (!m_length)
                        return undefinedAtom;
                Atom *arr = m_atoms;
                Atom retAtom = arr[0];
                setAtInternal(0, 0);
                VMPI_memmove (arr, arr + 1, (m_length - 1) * sizeof(Atom));
                arr[m_length - 1] = 0; // clear item so GC can collect it.
                m_length--;
                return retAtom;
        }
        // insertPoint arg - place to insert
        // insertCount arg - number to insert
        // deleteCount - number to delete
        // args - #insertCount args to insert
        void AtomArray::splice(uint32 insertPoint, uint32 insertCount, uint32 deleteCount, AtomArray *args, int offset)
        {
                long l_shiftAmount = (long)insertCount - (long) deleteCount; // long because result could be negative
                // Must be BEFORE arr = m_atoms since m_atoms might change
                checkCapacity (m_length + l_shiftAmount);
                Atom *arr = m_atoms;
                Atom *argsArr = args ? args->m_atoms : 0;
                if (l_shiftAmount < 0) 
                {
                        int numberBeingDeleted = -l_shiftAmount;
                        // whack deleted items so they're ref count goes down
                        AvmCore::decrementAtomRegion(arr + insertPoint + insertCount, numberBeingDeleted);
                        // shift elements down
                        int toMove = m_length - insertPoint - deleteCount;
                        VMPI_memmove (arr + insertPoint + insertCount, arr + insertPoint + deleteCount, toMove * sizeof(Atom));
                        // clear top part for RC purposes
                        VMPI_memset (arr + m_length - numberBeingDeleted, 0, numberBeingDeleted * sizeof(Atom));
                }
                else if (l_shiftAmount > 0)
                {
                        VMPI_memmove (arr + insertPoint + l_shiftAmount, arr + insertPoint, (m_length - insertPoint) * sizeof(Atom));
                        // clear for RC purposes
                        VMPI_memset (arr + insertPoint, 0, l_shiftAmount * sizeof(Atom));
                }
                // Add the items to insert
                if (insertCount)
                {
                        AvmAssert(args != 0);
                        for (uint32 i=0; i<insertCount; i++)
                        {
                                setAtInternal(insertPoint+i, argsArr[i+offset]);
                        }
                }
                m_length += l_shiftAmount;
                return;
        }
        // insert array of arguments at front of array
        Atom AtomArray::unshift(Atom *args, int argc)
        {
                // shift elements up by argc
                // inserts args into initial spots
                checkCapacity (m_length + argc);
                Atom *arr = m_atoms;
                VMPI_memmove (arr + argc, arr, m_length * sizeof(Atom));
                // clear moved element for RC purposes
                VMPI_memset (arr, 0, argc * sizeof(Atom));
                for(int i=0; i<argc; i++) {
                        setAtInternal(i, args[i]);
                }
                m_length += argc;
                return nullObjectAtom;
        }
        /////////////////////////////////////////////////////
        /////////////////////////////////////////////////////
        /////////////////////////////////////////////////////
        void AtomArray::checkCapacity (int newLength)
        {
                AvmAssert(newLength >= 0);
                // !!@ handle case where capacity shrinks by 50% (resize buffer smaller)
                if (!m_atoms || newLength > int(capacity()))
                {
                        // We oversize our buffer by 50% (is that the best algorithm?)
                        int capacity = newLength + (newLength >> 2);
                        if (capacity < kMinCapacity) capacity = kMinCapacity;
                        GC* gc = GC::GetGC(this);
                        Atom* newAtoms = (Atom*) gc->Calloc(capacity, sizeof(Atom), GC::kContainsPointers|GC::kZero);
                        Atom* oldAtoms = m_atoms;
                        setAtoms(gc, newAtoms);
                        if(oldAtoms) {
                                // use a memcpy to skip ref counting
                                VMPI_memcpy(m_atoms, oldAtoms, m_length*sizeof(Atom));
                                VMPI_memset(oldAtoms, 0, m_length*sizeof(Atom));
                                gc->Free(oldAtoms);
                        }
                }
        }
        void AtomArray::push (Atom a)
        {
                checkCapacity (m_length + 1);
                setAtInternal(m_length++, a);
        }
        void AtomArray::push (const AtomArray *a)
        {
                if (!a)
                        return;
                push (a->m_atoms, a->getLength());
        }
        void AtomArray::removeAt (uint32 index)
        {
                AvmAssert (m_length > 0);
                AvmAssert (index < uint32(m_length));
                if (!m_length)
                        return;
                checkCapacity (m_length - 1);
                m_length--;
                // use setAt instead of direct access for proper ref count maintenance
                setAtInternal(index, 0);
                Atom *arr = m_atoms;
                if (m_length)
                {
                        // shift down entries
                        VMPI_memmove (arr + index, arr + index + 1, (this->m_length - index) * sizeof(Atom));
                }
                arr[m_length] = 0; // clear our entry so GC can collect it
        }
        void AtomArray::insert (uint32 index, Atom a)
        {
                AvmAssert(index <= m_length);
                checkCapacity (m_length + 1);
                m_length++;
                Atom *arr = m_atoms;
                // shift entries up by one to make room
                VMPI_memmove (arr + index + 1, arr + index, (m_length - index - 1) * sizeof(Atom));
                // this element is still in the array so don't let setAtInternal decrement its count
                m_atoms[index] = 0;
                setAtInternal(index, a);
        }
        void AtomArray::setAt (uint32 index, Atom a)
        {
                if (index > m_length)
                {
                        AvmAssert(0);
                        return;
                }
                setAtInternal(index, a);
        }
        Atom AtomArray::getAt (uint32 index) const
        {
                if (index > m_length)
                {
                        AvmAssert(0);
                        return nullObjectAtom;
                }
                return m_atoms[index];
        }
        void AtomArray::clear()
        {
                if(m_atoms) {
                        AvmCore::decrementAtomRegion(m_atoms, m_length);
                        GC::GetGC(m_atoms)->Free(m_atoms);
                        m_atoms = 0;
                }
                m_length = 0;
        }
}