windows-nt/Source/XPSP1/NT/admin/netui/common/h/array.hxx
2020-09-26 16:20:57 +08:00

569 lines
29 KiB
C++

/**********************************************************************/
/** Microsoft LAN Manager **/
/** Copyright(c) Microsoft Corp., 1987-1990 **/
/**********************************************************************/
/*
ARRAY.HXX
LM 3.0 Generic array class module
This module contains type generic implementations of static
and dynamic arrays that provide bounds checking.
FILE HISTORY:
Johnl 02-Aug-90 Created
Johnl 05-Dec-90 Changed Size to QuerySize, changed Cassert to
UIASSERT
RustanL 05-Dec-90 Added ARRAY_LIST
RustanL 06-Dec-90 Split DECLARE_ARRAY_OF into itself and
DEFINE_ARRAY_OF
RustanL 07-Dec-90 Added several enhancements to ARRAY class
RustanL 31-Dec-90 Removed 'inline' from ARRAY_LIST::Remove
*/
#ifndef _ARRAY_HXX_
#define _ARRAY_HXX_
/*************************************************************************
NAME: ARRAY
SYNOPSIS: Generic array implementation that provides bounds checking
INTERFACE:
DECLARE_ARRAY_OF( ) - Declares array package (interface) for type
DEFINE_ARRAY_OF() - Defines array package (implementation) for type
ARRAY_OF() - Declares an array object of type "type"
ARRAY_type( ) - Constructor, takes a suggested size of array,
and attempts to allocate the array of that
size. If the allocation fails, the size is
set to 0, but the array is still in a valid
state. In fact, the array will always be in
a valid state.
Initial allocations of size 0 are always
guaranteed to succeed. Hence, the client
can always check if QueryCount returns a
different value than was passed to the
constructor in order to determine the
space actually allocated. (Please note that
checking QueryCount for being is, in general,
not enough to determine if the allocation
was successful.)
An alternate version of the constructor accepts
an existing vector of objects. This version can
either use that vector as its own storage,
resizing as needed - in which case the creator
must surrender all rights to the presumably
dynamically allocated vector - or else treats
it as nonresizable. The default is never to
resize such, since the vector might not come
from freestore, or else may be shared with
another object.
~ARRAY_type() - Deletes the array
operator[] - Gives random access to array with bounds checking
(asserts out if you go out of bounds)
operator= - Copies one array to another (note, they must be of
the same size).
QueryCount() - Returns the count of elements of this array.
Resize() - Resizes the array to the number of elements
specified by size. (NOTE: the array only
reallocates memory if the size changes by more
than ARRAY_RESIZE_MEM bytes or when the new
size exceeds the current.) Returns TRUE if
successful. If FALSE is returned, then a memory
error occurred, and the array remains untouched.
Resize is guaranteed to work whenever the new
size does not exceed the previous.
CAVEATS:
NOTES:
CODEWORK. The Resize method could be made much more efficient if it
used a proper buffer object that could be realloced, rather than
having to allocate a brand new chunk of memory, call the
constructor on each item in this array, copy items into the new
array, call the destructor on every previously allocated item,
and finally deallocate the previous chunk of memory. This should
become a new class (BUFFER_ARRAY_OF or the like).
HISTORY:
Johnl 15-Jul-1990 Created
RustanL 06-Dec-1990 Created DEFINE_ARRAY_OF from DECLARE_ARRAY_OF
RustanL 07-Dec-1990 Added several enhancements
beng 14-Aug-1991 Added vector-adopt mode; renamed QCount;
op= relaxed; enhanced further
Yi-HsinS 28-Oct-1992 Add a flag to Resize indicating not to
downsize
**************************************************************************/
#define ARRAY_OF(type) ARRAY_##type
#define ARRAY_RESIZE_MEM 4096 // If the array loses more than 4096 bytes
// on a resize, then go ahead and reallocate
// memory etc., else just change _carray
/************************************************************/
#define DECL_ARRAY_OF(type,dec) \
\
class dec ARRAY_OF(type) \
{ \
private: \
type *_parray; \
UINT _carray; \
UINT _carrayAlloc; \
BOOL _fMayResize; \
\
BOOL WithinRange( UINT iIndex ) const; \
\
public: \
ARRAY_OF(type)( UINT cElem ); \
ARRAY_OF(type)( type* ptype, UINT ctype, \
BOOL fMayResize = FALSE); \
\
~ARRAY_OF(type)(); \
\
type& operator[]( UINT iIndex ) const \
{ \
ASSERT( WithinRange(iIndex) ); \
return _parray[ iIndex ]; \
} \
\
UINT QueryCount() const \
{ \
return _carray; \
} \
\
ARRAY_OF(type) & operator=( ARRAY_OF(type)& a ); \
\
BOOL Resize( UINT cElemNew, BOOL fDownSize = TRUE ); \
};
/************************************************************************/
#define DEFINE_ARRAY_OF( type ) \
\
ARRAY_OF(type)::ARRAY_OF(type)( UINT cElem ) \
: _parray(NULL), \
_carray(0), \
_carrayAlloc(0), \
_fMayResize(TRUE) \
{ \
if ( cElem > 0 ) \
{ \
_parray = new type[ cElem ]; \
} \
\
if ( _parray != NULL ) \
{ \
_carray = _carrayAlloc = cElem; \
} \
} \
\
\
ARRAY_OF(type)::ARRAY_OF(type)(type *px, UINT cx, BOOL fMayResize) \
: _parray(px), \
_carray(cx), \
_carrayAlloc(cx), \
_fMayResize(fMayResize) \
{ \
/* nothing doing. */ \
} \
\
\
ARRAY_OF(type)::~ARRAY_OF(type)() \
{ \
if ( _fMayResize && _carrayAlloc > 0 ) \
delete [ _carrayAlloc ] _parray; \
} \
\
\
ARRAY_OF(type) & ARRAY_OF(type)::operator=( ARRAY_OF(type)& a ) \
{ \
ASSERT(_carrayAlloc <= a.QueryCount()); \
\
UINT iLim = ( a.QueryCount() <= _carrayAlloc) \
? a.QueryCount() \
: _carrayAlloc; \
\
for ( UINT i = 0; i < iLim; i++ ) \
_parray[i] = a._parray[i]; \
\
_carray = iLim; \
return *this; \
} \
\
\
BOOL ARRAY_OF(type)::WithinRange( UINT iIndex ) const \
{ \
return (iIndex < _carray); \
} \
\
\
BOOL ARRAY_OF(type)::Resize( UINT cElemNew, BOOL fDownSize ) \
{ \
/* Prevent resizing owner-alloc arrays */ \
if (!_fMayResize) \
return FALSE; \
\
if ( ( cElemNew > _carrayAlloc ) \
|| ( fDownSize && \
(( _carrayAlloc - cElemNew )*sizeof(type) > ARRAY_RESIZE_MEM))\
) \
{ \
type * parrayNew; \
if ( cElemNew > 0 ) \
{ \
parrayNew = new type[ cElemNew ]; \
if ( parrayNew == NULL ) \
{ \
/* Memory failure */ \
if ( cElemNew > _carrayAlloc ) \
{ \
/* The array has been not been modified */ \
return FALSE; \
} \
else \
{ \
/* Guarantee: resizing the array for */ \
/* new sizes not exceeding the current */ \
/* will always succeed. */ \
_carray = cElemNew; \
return TRUE; \
} \
} \
\
/* Copy each existing item */ \
/* that will fit into the new array */ \
for ( UINT i = 0 ; \
i < ( _carray < cElemNew ? _carray : cElemNew); \
i++ ) \
{ \
parrayNew[i] = _parray[i]; \
} \
\
} \
else \
{ \
parrayNew = NULL; \
} \
\
if ( _carrayAlloc > 0 ) \
{ \
delete[ _carrayAlloc ] _parray; \
} \
else \
{ \
ASSERT( _parray == NULL ); \
} \
_parray = parrayNew; \
_carray = _carrayAlloc = cElemNew; \
} \
else \
{ \
/* (Array does not report errors.) New size is less */ \
/* than current size, but not so much less that we want */ \
/* to realloc to save memory. */ \
_carray = cElemNew; \
} \
\
return TRUE; \
}
/*************************************************************************
NAME: ARRAY_LIST
SYNOPSIS: Generic unordered array collection implementation
which provides bounds checking, easy ways to add/remove
items, etc. In some respects, this collection can be
thought of as an efficient singly linked list, for singly
linked lists which don't change a lot dynamically. Hence
the name "array list".
INTERFACE:
DECLARE_ARRAY_LIST_OF( )
Declares array list package for type 'type'
DEFINE_ARRAY_LIST_OF()
Defines array list package for type 'type', excluding
the Sort and BinarySearch methods (see Notes below)
DEFINE_EXT_ARRAY_LIST_OF()
Defines the entire array list package for type 'type'
WARNING: If you use EXT_ARRAY_LIST, the Compare()
method for the parameter class must be _CRTAPI1.
Otherwise the underlying qsort() or BinarySearch() will fail.
ARRAY_LIST_OF()
Declares an array list object of type 'type'
ARRAY_LIST_type()
Constructor. Takes an optional parameter, nInitialSize,
which specifies the initial allocation size of the array
list in number of array list items. Note, any call to
Add or AddIdemp may cause the allocated size to be
enlarged, and any call to Remove may reduce the allocated
size. These allocations, however, are all kept away
from the user, except for the nInitialSize parameter to
the constructor.
Add()
Adds an item to the array list. Note, this will
not sort the list.
AddIdemp()
Idempotently adds an item to the array list. Note,
this will not sort the list.
Remove()
Removes an item from the array list. This may
result in shrinking the allocated size of the array,
and will not sort the list.
Find()
Finds the first occurrence of a given item in the array
list. Since the list is not assumed to be sorted, a
linear search is performed.
Clear()
Clears the array list of all of items items
operator[]()
Same as for the ARRAY class
operator=()
Same as for the ARRAY class
QueryCount()
Same as for the ARRAY class
Sort()
Sorts the array (see Notes below for Sort definition)
BinarySearch()
Works like Find, but performs a binary search rather
than a linear. This method assumes that the list is
sorted. (See Notes below for BinarySearch definition.)
CAVEATS:
Add and Remove will alter the ordering of the list in ways
unexpected. See the implementation of Remove for details.
The Sort routine calls qsort, which may swap the items.
Although qsort is given a pointer to the Compare function,
it does not have access to, or knowledge of, the assignment
operator; rather, it performs a straight binary copy of each
item. Hence, all types which require a custom assignment
operation should not use the Sort method.
CODEWORK. The debug version of BinarySearch could assert that
the array list is sorted. This, however, would require adding
an operator=( ARRAY ) and an operator=( ARRAY_LIST ) to this
class, since these would function differently with an additional
member keeping track of whether the array list is sorted.
CODEWORK. We have a C++ heapsort, which we could emend to
recognize type::Compare.
NOTES:
The array list declaration and definitions automatically
take care of declaring and defining, respectively, the
superclass ARRAY or the same type, since ARRAY_LIST inherits
from ARRAY.
The Find, Sort, and BinarySearch functions require the 'type'
class to define a Compare method.
The retail version of this class does not claim nor attempt to
keep track of if the list is sorted. Thus, it is always up to
the client to keep track of this. For the same reason, Remove
and AddIdemp always call Find, rather than BinarySearch. The
debug version could assert out if BinarySearch is called when
the list is not sorted (see Caveats above).
Since the Sort and BinarySearch definitions methods depend on
definitions in stdlib.h and search.h, the definitions for these
methods are only provided in the extended definition macro.
This way, clients who do not wish to make use of Sort and
BinarySearch, need not include stdlib.h and search.h.
HISTORY:
RustanL 5-Dec-1990 Created
beng 14-Aug-1991 Remove inlines; changes in ARRAY
Yi-HsinS 28-Oct-1992 Add a flag to Resize indicating not to
downsize
**************************************************************************/
#define ARRAY_LIST_OF(type) ARRAY_LIST_##type
/************************************************************/
#define DECL_ARRAY_LIST_OF(type,dec) \
\
DECL_ARRAY_OF(type,dec); \
\
class dec ARRAY_LIST_OF( type ) : public ARRAY_OF( type ) \
{ \
public: \
ARRAY_LIST_OF( type )( UINT cAlloc = 0 ); \
\
BOOL Add( const type & t ); \
BOOL AddIdemp( const type & t ); \
BOOL Remove( const type & t ); \
INT Find( const type & t ) const; \
VOID Clear(); \
\
VOID Sort(); \
INT BinarySearch( const type & t ) const; \
static int __cdecl SortFunc ( \
const void * p1, const void * p2 ) ; \
};
/********************************************************************/
#define DEFINE_ARRAY_LIST_OF( type ) \
\
DEFINE_ARRAY_OF( type ); \
\
ARRAY_LIST_OF( type )::ARRAY_LIST_OF( type )( UINT cAlloc ) \
: ARRAY_OF(type) ( cAlloc ) \
{ \
Resize(0, FALSE); /* do not downsize */ \
} \
\
BOOL ARRAY_LIST_OF( type )::Add( const type & t ) \
{ \
UINT n = QueryCount(); \
\
if ( ! Resize( n + 1 )) \
return FALSE; /* Out of memory */ \
\
(*this)[n] = t; \
return TRUE; \
} \
\
\
BOOL ARRAY_LIST_OF( type )::AddIdemp( const type & t ) \
{ \
return (( Find( t ) >= 0 ) || Add( t )); \
} \
\
\
BOOL ARRAY_LIST_OF( type )::Remove( const type & t ) \
{ \
INT i = Find( t ); \
if ( i < 0 ) \
return FALSE; /* item not found */ \
\
/* Since Find was able to find the item, the array list */ \
/* cannot be empty. */ \
ASSERT(QueryCount() > 0); \
UINT n = QueryCount() - 1; \
if ( (UINT)i < n ) \
(*this)[i] = (*this)[n]; \
\
/* Reducing the size of an array always succeeds */ \
REQUIRE( Resize( n )); \
\
return TRUE; \
} \
\
\
INT ARRAY_LIST_OF( type )::Find( const type & t ) const \
{ \
for ( UINT i = 0; i < QueryCount(); i++ ) \
{ \
if ( t.Compare( &(operator[]( i ))) == 0 ) \
return i; \
} \
\
return -1; /* not found */ \
} \
\
\
VOID ARRAY_LIST_OF( type )::Clear() \
{ \
/* Resizing to size 0 is guaranteed always to work. */ \
REQUIRE( Resize( 0 )); \
}
/********************************************************************/
#define DEFINE_EXT_ARRAY_LIST_OF( type ) \
\
DEFINE_ARRAY_LIST_OF( type ); \
\
int __cdecl ARRAY_LIST_OF( type )::SortFunc ( \
const void * p1, const void * p2 ) \
{ \
const type * pt1 = (const type *) p1 ; \
const type * pt2 = (const type *) p2 ; \
return pt1->Compare( pt2 ) ; \
} \
\
VOID ARRAY_LIST_OF( type )::Sort() \
{ \
qsort( &((*this)[0]), QueryCount(), sizeof( type ), \
ARRAY_LIST_OF( type )::SortFunc ); \
} \
\
\
INT ARRAY_LIST_OF( type )::BinarySearch( const type & t ) const \
{ \
type * ptBase = &((*this)[0]); \
type * pt = (type *)bsearch( (VOID *)&t, \
(VOID *)ptBase, \
QueryCount(), \
sizeof( type ), \
ARRAY_LIST_OF( type)::SortFunc ); \
\
return (( pt == NULL ) ? -1 : (INT)(pt - ptBase) ); \
}
// Helper macros for code preservation
#define DECLARE_ARRAY_OF(type) \
DECL_ARRAY_OF(type,DLL_TEMPLATE)
#define DECLARE_ARRAY_LIST_OF(type) \
DECL_ARRAY_LIST_OF(type,DLL_TEMPLATE)
#endif //_ARRAY_HXX_