/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** 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 mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1999
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of 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 ***** */

#include "msgCore.h"
#include "nsUint8Array.h"

nsUint8Array::nsUint8Array()
{
	m_pData = nsnull;
	m_nSize = m_nMaxSize = m_nGrowBy = 0;
}

nsUint8Array::~nsUint8Array()
{
	PR_Free(m_pData);
}

void nsUint8Array::SetSize(PRInt32 nNewSize, PRInt32 nGrowBy)
{
//	AssertValid(this);
	NS_ASSERTION(nNewSize >= 0, "can't set size to negative value");

	if (nGrowBy != -1)
		m_nGrowBy = nGrowBy;  // set new size

	if (nNewSize == 0)
	{
		// shrink to nothing
		PR_Free(m_pData);
		m_pData = nsnull;
		m_nSize = m_nMaxSize = 0;
	}
	else if (m_pData == nsnull)
	{
		// create one with exact size
		m_pData = (PRUint8*)PR_Malloc(nNewSize * sizeof(PRUint8));

		memset(m_pData, 0, nNewSize * sizeof(PRUint8));  // zero fill

		m_nSize = m_nMaxSize = nNewSize;
	}
	else if (nNewSize <= m_nMaxSize)
	{
    // it fits
    if (nNewSize > m_nSize)
    {
	    // initialize the new elements

      memset(&m_pData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(PRUint8));

    }

		m_nSize = nNewSize;
	}
	else
	{
		// otherwise, grow array
		PRInt32 nGrowBy = m_nGrowBy;
		if (nGrowBy == 0)
		{
			nGrowBy = PR_MIN(1024, PR_MAX(4, m_nSize / 8));
		}
		PRInt32 nNewMax;
		if (nNewSize < m_nMaxSize + nGrowBy)
			nNewMax = m_nMaxSize + nGrowBy;  // granularity
		else
			nNewMax = nNewSize;  // no slush

		NS_ASSERTION(nNewMax >= m_nMaxSize, "no wraparound");  // no wrap around
		PRUint8* pNewData = (PRUint8*)PR_Malloc(nNewMax * sizeof(PRUint8));

		// copy new data from old
        memcpy(pNewData, m_pData, m_nSize * sizeof(PRUint8));

		NS_ASSERTION(nNewSize > m_nSize, "did't grow size");

		memset(&pNewData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(PRUint8));

		PR_Free(m_pData);
		m_pData = pNewData;
		m_nSize = nNewSize;
		m_nMaxSize = nNewMax;
	}
}

void nsUint8Array::FreeExtra()
{

	if (m_nSize != m_nMaxSize)
	{
		// shrink to desired size
		PRUint8* pNewData = nsnull;
		if (m_nSize != 0)
		{
			pNewData = (PRUint8*)PR_Malloc(m_nSize * sizeof(PRUint8));
			// copy new data from old
			memcpy(pNewData, m_pData, m_nSize * sizeof(PRUint8));
		}

		// get rid of old stuff (note: no destructors called)
		PR_Free(m_pData);
		m_pData = pNewData;
		m_nMaxSize = m_nSize;
	}
}

void nsUint8Array::CopyArray(nsUint8Array &aSrcArray)
{
  PR_FREEIF(m_pData);
  m_nSize = aSrcArray.m_nSize;
  m_nMaxSize = aSrcArray.m_nSize;
  m_pData = (PRUint8*)PR_Malloc(m_nSize * sizeof(PRUint8));
  if (m_pData)
   memcpy(m_pData, aSrcArray.m_pData, m_nSize * sizeof(PRUint8));
}

/////////////////////////////////////////////////////////////////////////////

void nsUint8Array::SetAtGrow(PRInt32 nIndex, PRUint8 newElement)
{
	NS_ASSERTION(nIndex >= 0, "can't insert at negative index");

	if (nIndex >= m_nSize)
		SetSize(nIndex+1);
	m_pData[nIndex] = newElement;
}

nsresult nsUint8Array::InsertAt(PRInt32 nIndex, PRUint8 newElement, PRInt32 nCount)
{
	NS_ASSERTION(nIndex >= 0, "can't insert at negative index");
	NS_ASSERTION(nCount > 0, "have to insert something");     // zero or negative size not allowed

	if (nIndex >= m_nSize)
	{
		// adding after the end of the array
		SetSize(nIndex + nCount);  // grow so nIndex is valid
	}
	else
	{
		// inserting in the middle of the array
		PRInt32 nOldSize = m_nSize;
		SetSize(m_nSize + nCount);  // grow it to new size
		// shift old data up to fill gap
        memmove(&m_pData[nIndex+nCount], &m_pData[nIndex],
                (nOldSize-nIndex) * sizeof(PRUint8));

		// re-init slots we copied from

		memset(&m_pData[nIndex], 0, nCount * sizeof(PRUint8));

	}

	// insert new value in the gap
	NS_ASSERTION(nIndex + nCount <= m_nSize, "overflow");
	while (nCount--)
		m_pData[nIndex++] = newElement;
  return NS_OK;
}

void nsUint8Array::RemoveAt(PRInt32 nIndex, PRInt32 nCount)
{
  if (nIndex < 0 || nCount < 0 || nIndex + nCount > m_nSize)
  {
    NS_ASSERTION(PR_FALSE, "bad remove index or count");
    return;
  }

	// just remove a range
	PRInt32 nMoveCount = m_nSize - (nIndex + nCount);

	NS_ASSERTION(nMoveCount >= 0, "can't remove nothing");
	if (nMoveCount >= 0)
      memmove(&m_pData[nIndex], &m_pData[nIndex + nCount],
              nMoveCount * sizeof(PRUint8));
	m_nSize -= nCount;
}

nsresult nsUint8Array::InsertAt(PRInt32 nStartIndex, nsUint8Array* pNewArray)
{
  NS_ENSURE_ARG(pNewArray);
	NS_ASSERTION(nStartIndex >= 0, "start index must be positive");

	if (pNewArray->GetSize() > 0)
	{
		InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize());
		for (PRInt32 i = 0; i < pNewArray->GetSize(); i++)
			SetAt(nStartIndex + i, pNewArray->GetAt(i));
	}
  return NS_OK;
}


 PRInt32 nsUint8Array::GetSize() const
	{ return m_nSize; }
 PRInt32 nsUint8Array::GetUpperBound() const
	{ return m_nSize-1; }
 void nsUint8Array::RemoveAll()
	{ SetSize(0); }
 PRUint8 nsUint8Array::GetAt(PRInt32 nIndex) const
	{ NS_ASSERTION(nIndex >= 0 && nIndex < m_nSize, "out of bounds");
		return m_pData[nIndex]; }
 void nsUint8Array::SetAt(PRInt32 nIndex, PRUint8 newElement)
	{ NS_ASSERTION(nIndex >= 0 && nIndex < m_nSize, "out of bounds");
		m_pData[nIndex] = newElement; }
 PRUint8& nsUint8Array::ElementAt(PRInt32 nIndex)
	{ NS_ASSERTION(nIndex >= 0 && nIndex < m_nSize, "out of bounds");
		return m_pData[nIndex]; }
 PRInt32 nsUint8Array::Add(PRUint8 newElement)
	{ PRInt32 nIndex = m_nSize;
		SetAtGrow(nIndex, newElement);
		return nIndex; }
 PRUint8 nsUint8Array::operator[](PRInt32 nIndex) const
	{ return GetAt(nIndex); }
 PRUint8& nsUint8Array::operator[](PRInt32 nIndex)
	{ return ElementAt(nIndex); }


/////////////////////////////////////////////////////////////////////////////
// Diagnostics

#ifdef _DEBUG

void nsUint8Array::AssertValid() const
{
}
#endif //_DEBUG


/////////////////////////////////////////////////////////////////////////////
