You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
6.7 KiB
306 lines
6.7 KiB
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module ARRAY.C -- Generic Array Implementation |
|
|
*
|
|
* Original Author: <nl>
|
|
* Christian Fortini
|
|
*
|
|
* History: <nl>
|
|
* 6/25/95 alexgo Cleanup and Commented
|
|
*
|
|
* Copyright (c) 1995-1997, Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
|
|
#include "_common.h"
|
|
#include "_array.h"
|
|
|
|
ASSERTDATA
|
|
|
|
const int celGrow = 8;
|
|
|
|
//
|
|
// Invariant support
|
|
//
|
|
#define DEBUG_CLASSNAME CArrayBase
|
|
#include "_invar.h"
|
|
|
|
// =================================== CArrayBase ================================================
|
|
|
|
#ifdef DEBUG
|
|
/*
|
|
* CArrayBase::Invariant()
|
|
*
|
|
* @mfunc Tests the array state to make sure it is valid. DEBUG only
|
|
*
|
|
* @rdesc TRUE if the tests succeed, FALSE otherwise
|
|
*/
|
|
BOOL CArrayBase::Invariant() const
|
|
{
|
|
Assert(_cbElem > 0);
|
|
|
|
if(!_prgel)
|
|
{
|
|
Assert(_cel == 0);
|
|
Assert(_celMax == 0);
|
|
|
|
// We go ahead and return a value here so that
|
|
// this function can be executed in the "watch"
|
|
// window of various debuggers
|
|
if(_cel || _celMax)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
Assert(_celMax > 0 );
|
|
Assert(_cel <= _celMax);
|
|
|
|
if(_celMax == 0 || _cel > _celMax)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* CArrayBase::Elem(iel)
|
|
*
|
|
* @mfunc Returns a pointer to the element indexed by <p iel>
|
|
*
|
|
* @rdesc A pointer to the element indexed by <p iel>. This pointer may
|
|
* be cast to a pointer of the appropriate element type.
|
|
*/
|
|
void* CArrayBase::Elem(
|
|
LONG iel) const //@parm Index to use
|
|
{
|
|
_TEST_INVARIANT_
|
|
|
|
// This can arise with empty froms^3 controls.
|
|
// Review (keithcu) Why did this start happening in Richedit 3?
|
|
if (!_cel)
|
|
return NULL;
|
|
|
|
AssertSz(iel == 0 || (iel > 0 && iel < _cel),
|
|
"CArrayBase::Elem() - Index out of range");
|
|
|
|
return _prgel + iel * _cbElem;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* CArrayBase::CArrayBase
|
|
*
|
|
* @mfunc Constructor
|
|
*/
|
|
CArrayBase::CArrayBase(
|
|
LONG cbElem) //@parm Size of an individual array element
|
|
{
|
|
_prgel = NULL;
|
|
_cel = 0;
|
|
_celMax = 0;
|
|
_cbElem = cbElem;
|
|
}
|
|
|
|
/*
|
|
* CArrayBase::ArAdd
|
|
*
|
|
* @mfunc Adds <p celAdd> elements to the end of the array.
|
|
*
|
|
* @rdesc A pointer to the start of the new elements added. If non-NULL,
|
|
* <p pielIns> will be set to the index at which elements were added.
|
|
*
|
|
* We grow in steps of celGrow when small and exponentially when large.
|
|
*/
|
|
void* CArrayBase::ArAdd(
|
|
LONG celAdd, //@parm Count of elements to add
|
|
LONG *pielIns) //@parm Out parm for index of first element added
|
|
{
|
|
_TEST_INVARIANT_
|
|
char *pel;
|
|
|
|
if(_cel + celAdd > _celMax) // need to grow
|
|
{
|
|
LONG celNew = max(celAdd, celGrow) + _cel / 16;
|
|
pel = (char*)PvReAlloc(_prgel, (_celMax + celNew) * _cbElem);
|
|
if(!pel)
|
|
return NULL;
|
|
_prgel = pel;
|
|
_celMax += celNew;
|
|
}
|
|
pel = _prgel + _cel * _cbElem;
|
|
ZeroMemory(pel, celAdd * _cbElem);
|
|
|
|
if(pielIns)
|
|
*pielIns = _cel;
|
|
|
|
_cel += celAdd;
|
|
return pel;
|
|
}
|
|
|
|
/*
|
|
* CArrayBase::ArInsert (iel, celIns)
|
|
*
|
|
* @mfunc Inserts <p celIns> new elements at index <p iel>
|
|
*
|
|
* @rdesc A pointer to the newly inserted elements. Will be NULL on
|
|
* failure.
|
|
*/
|
|
void* CArrayBase::ArInsert(
|
|
LONG iel, //@parm Index at which to insert
|
|
LONG celIns) //@parm Count of elements to insert
|
|
{
|
|
char *pel;
|
|
|
|
_TEST_INVARIANT_
|
|
|
|
AssertSz(iel <= _cel, "CArrayBase::Insert() - Insert out of range");
|
|
|
|
if(iel >= _cel)
|
|
return ArAdd(celIns, NULL);
|
|
|
|
if(_cel + celIns > _celMax) // need to grow
|
|
{
|
|
AssertSz(_prgel, "CArrayBase::Insert() - Growing a non existent array !");
|
|
|
|
LONG celNew = max(celIns, celGrow) + _cel / 16;
|
|
pel = (char*)PvReAlloc(_prgel, (_celMax + celNew) * _cbElem);
|
|
if(!pel)
|
|
{
|
|
TRACEERRORSZ("CArrayBase::Insert() - Couldn't realloc line array");
|
|
return NULL;
|
|
}
|
|
_prgel = pel;
|
|
_celMax += celNew;
|
|
}
|
|
pel = _prgel + iel * _cbElem;
|
|
if(iel < _cel) // Nove Elems up to make room for new ones
|
|
MoveMemory(pel + celIns*_cbElem, pel, (_cel - iel)*_cbElem);
|
|
|
|
_cel += celIns;
|
|
return pel;
|
|
}
|
|
|
|
/*
|
|
* CArrayBase::Remove
|
|
*
|
|
* @mfunc Removes the <p celFree> elements from the array starting at index
|
|
* <p ielFirst>. If <p celFree> is negative, then all elements after
|
|
* <p ielFirst> are removed.
|
|
*
|
|
* @rdesc nothing
|
|
*/
|
|
void CArrayBase::Remove(
|
|
LONG ielFirst, //@parm Index at which elements should be removed
|
|
LONG celFree) //@parm Count of elements to remove.
|
|
{
|
|
char *pel;
|
|
|
|
_TEST_INVARIANT_
|
|
|
|
if(celFree < 0)
|
|
celFree = _cel - ielFirst;
|
|
|
|
AssertSz(ielFirst + celFree <= _cel, "CArrayBase::Free() - Freeing out of range");
|
|
|
|
if(_cel > ielFirst + celFree)
|
|
{
|
|
pel = _prgel + ielFirst * _cbElem;
|
|
MoveMemory(pel, pel + celFree * _cbElem,
|
|
(_cel - ielFirst - celFree) * _cbElem);
|
|
}
|
|
|
|
_cel -= celFree;
|
|
|
|
if(_cel < _celMax - celGrow - _cel / 16)
|
|
{
|
|
// Shrink array
|
|
_celMax = max(_cel, celGrow);
|
|
_prgel = (char*)PvReAlloc(_prgel, _celMax * _cbElem);
|
|
Assert(_prgel);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CArrayBase::Clear
|
|
*
|
|
* @mfunc Clears the entire array, potentially deleting all of the memory
|
|
* as well.
|
|
*
|
|
* @rdesc nothing
|
|
*/
|
|
void CArrayBase::Clear(
|
|
ArrayFlag flag) //@parm Indicates what should be done with the memory
|
|
//in the array. One of AF_DELETEMEM or AF_KEEPMEM
|
|
{
|
|
_TEST_INVARIANT_
|
|
|
|
if( flag == AF_DELETEMEM )
|
|
{
|
|
FreePv(_prgel);
|
|
_prgel = NULL;
|
|
_celMax = 0;
|
|
}
|
|
else if (_prgel)
|
|
{
|
|
_celMax = min(celGrow, _celMax);
|
|
_prgel = (char*) PvReAlloc(_prgel, celGrow * _cbElem);
|
|
}
|
|
_cel = 0;
|
|
}
|
|
|
|
/*
|
|
* CArrayBase::Replace
|
|
*
|
|
* @mfunc Replaces the <p celRepl> elements at index <p ielRepl> with the
|
|
* contents of the array specified by <p par>. If <p celRepl> is negative,
|
|
* then the entire contents of <p this> array starting at <p ielRepl> should
|
|
* be replaced.
|
|
*
|
|
* @rdesc Returns TRUE on success, FALSE otherwise.
|
|
*/
|
|
BOOL CArrayBase::Replace(
|
|
LONG ielRepl, //@parm index at which replacement should occur
|
|
LONG celRepl, //@parm number of elements to replace (may be
|
|
// negative, indicating that all
|
|
CArrayBase *par) //@parm array to use as the replacement source
|
|
{
|
|
_TEST_INVARIANT_
|
|
|
|
LONG celMove = 0;
|
|
LONG celIns = par->Count();
|
|
|
|
if (celRepl < 0)
|
|
celRepl = _cel - ielRepl;
|
|
|
|
AssertSz(ielRepl + celRepl <= _cel, "CArrayBase::ArReplace() - Replacing out of range");
|
|
|
|
celMove = min(celRepl, celIns);
|
|
|
|
if (celMove > 0)
|
|
{
|
|
MoveMemory(Elem(ielRepl), par->Elem(0), celMove * _cbElem);
|
|
celIns -= celMove;
|
|
celRepl -= celMove;
|
|
ielRepl += celMove;
|
|
}
|
|
|
|
Assert(celIns >= 0);
|
|
Assert(celRepl >= 0);
|
|
Assert(celIns + celMove == par->Count());
|
|
|
|
if(celIns > 0)
|
|
{
|
|
Assert(celRepl == 0);
|
|
void *pelIns = ArInsert (ielRepl, celIns);
|
|
if (!pelIns)
|
|
return FALSE;
|
|
MoveMemory(pelIns, par->Elem(celMove), celIns * _cbElem);
|
|
}
|
|
else if(celRepl > 0)
|
|
Remove (ielRepl, celRepl);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|