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.
1241 lines
28 KiB
1241 lines
28 KiB
/*++
|
|
|
|
Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
template.h
|
|
|
|
Abstract:
|
|
|
|
This file defines the Template Class.
|
|
|
|
Author:
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
#ifndef _TEMPLATE_H_
|
|
#define _TEMPLATE_H_
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Basic types
|
|
|
|
// abstract iteration position
|
|
struct __POSITION { };
|
|
typedef __POSITION* POSITION;
|
|
|
|
const POSITION BEFORE_START_POSITION = (POSITION)-1;
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// global helpers (can be overridden)
|
|
|
|
#undef new
|
|
inline void * __cdecl operator new(size_t, void *_P)
|
|
{
|
|
return (_P);
|
|
}
|
|
|
|
template<class TYPE>
|
|
void
|
|
ConstructElements(
|
|
TYPE* pElements,
|
|
INT_PTR nCount
|
|
)
|
|
{
|
|
ASSERT(nCount);
|
|
|
|
// first do bit-wise zero initialization
|
|
memset((void*)pElements, 0, (size_t)nCount * sizeof(TYPE));
|
|
|
|
// then call the constructor(s)
|
|
for (; nCount--; pElements++)
|
|
::new((void*)pElements) TYPE;
|
|
}
|
|
|
|
#undef new
|
|
// retore mem.h trick
|
|
#ifdef DEBUG
|
|
#define new new(TEXT(__FILE__), __LINE__)
|
|
#endif // DEBUG
|
|
|
|
template<class TYPE>
|
|
void
|
|
DestructElements(
|
|
TYPE* pElements,
|
|
INT_PTR nCount
|
|
)
|
|
{
|
|
ASSERT(nCount);
|
|
|
|
// call the destructor(s)
|
|
for (; nCount--; pElements++)
|
|
pElements->~TYPE();
|
|
}
|
|
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
BOOL
|
|
CompareElements(
|
|
const TYPE* pElement1,
|
|
const ARG_TYPE* pElement2
|
|
)
|
|
{
|
|
return *pElement1 == *pElement2;
|
|
}
|
|
|
|
|
|
template<class ARG_KEY>
|
|
UINT
|
|
HashKey(
|
|
ARG_KEY key
|
|
)
|
|
{
|
|
// default identity hash - works for most primitive values
|
|
return ((UINT)(ULONG_PTR)key) >> 4;
|
|
}
|
|
|
|
|
|
struct CPlex // warning variable length structure
|
|
{
|
|
CPlex* pNext;
|
|
|
|
// BYTE data[maxNum*elementSize];
|
|
|
|
void* data() { return this+1; }
|
|
|
|
static CPlex* PASCAL Create(CPlex*& head, UINT nMax, UINT cbElement);
|
|
// like 'calloc' but no zero fill
|
|
// may throw memory exceptions
|
|
|
|
void FreeDataChain(); // free this one and links
|
|
};
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
class CMap
|
|
{
|
|
public:
|
|
CMap(int nBlockSize = default_block_size);
|
|
~CMap();
|
|
|
|
INT_PTR GetCount() const;
|
|
|
|
BOOL Lookup(ARG_KEY key, VALUE& rValue) const;
|
|
|
|
VALUE& operator[](ARG_KEY key);
|
|
|
|
void SetAt(ARG_KEY key, ARG_VALUE newValue);
|
|
|
|
BOOL RemoveKey(ARG_KEY key);
|
|
void RemoveAll();
|
|
|
|
POSITION GetStartPosition() const;
|
|
void GetNextAssoc(POSITION& rNextPosition, KEY& rKey, VALUE& rValue) const;
|
|
|
|
void InitHashTable(UINT hashSize, BOOL bAllocNow = TRUE);
|
|
|
|
private:
|
|
// Association
|
|
struct CAssoc {
|
|
CAssoc* pNext;
|
|
UINT nHashValue; // needed for efficient iteration
|
|
KEY key;
|
|
VALUE value;
|
|
};
|
|
|
|
static const int default_block_size = 10;
|
|
|
|
private:
|
|
CAssoc* NewAssoc();
|
|
void FreeAssoc(CAssoc*);
|
|
CAssoc* GetAssocAt(ARG_KEY, UINT&) const;
|
|
|
|
private:
|
|
CAssoc** m_pHashTable;
|
|
UINT m_nHashTableSize;
|
|
INT_PTR m_nCount;
|
|
CAssoc* m_pFreeList;
|
|
struct CPlex* m_pBlocks;
|
|
int m_nBlockSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
INT_PTR
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetCount(
|
|
) const
|
|
{
|
|
return m_nCount;
|
|
}
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
void
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::SetAt(
|
|
ARG_KEY key,
|
|
ARG_VALUE newValue
|
|
)
|
|
{
|
|
(*this)[key] = newValue;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CMap(
|
|
int nBlockSize
|
|
)
|
|
{
|
|
ASSERT(nBlockSize > 0);
|
|
|
|
if (nBlockSize <= 0)
|
|
nBlockSize = default_block_size;
|
|
|
|
m_pHashTable = NULL;
|
|
m_nHashTableSize = 17; // default size
|
|
m_nCount = 0;
|
|
m_pFreeList = NULL;
|
|
m_pBlocks = NULL;
|
|
m_nBlockSize = nBlockSize;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
void
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::InitHashTable(
|
|
UINT nHashSize,
|
|
BOOL bAllocNow
|
|
)
|
|
{
|
|
ASSERT(m_nCount == 0);
|
|
ASSERT(nHashSize > 0);
|
|
|
|
if (m_pHashTable != NULL) {
|
|
// free hash table
|
|
delete[] m_pHashTable;
|
|
m_pHashTable = NULL;
|
|
}
|
|
|
|
if (bAllocNow) {
|
|
m_pHashTable = new CAssoc* [nHashSize];
|
|
if (! m_pHashTable)
|
|
return;
|
|
memset(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize);
|
|
}
|
|
|
|
m_nHashTableSize = nHashSize;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
void
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveAll(
|
|
)
|
|
{
|
|
if (m_pHashTable != NULL) {
|
|
// destroy elements (values and keys)
|
|
for (UINT nHash = 0; nHash < m_nHashTableSize; nHash++) {
|
|
CAssoc* pAssoc;
|
|
for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) {
|
|
DestructElements<VALUE>(&pAssoc->value, 1);
|
|
DestructElements<KEY>(&pAssoc->key, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// free hash table
|
|
delete[] m_pHashTable;
|
|
m_pHashTable = NULL;
|
|
|
|
m_nCount = 0;
|
|
m_pFreeList = NULL;
|
|
|
|
m_pBlocks->FreeDataChain();
|
|
m_pBlocks = NULL;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::~CMap(
|
|
)
|
|
{
|
|
RemoveAll();
|
|
|
|
ASSERT(m_nCount == 0);
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CAssoc*
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::NewAssoc(
|
|
)
|
|
{
|
|
if (m_pFreeList == NULL) {
|
|
// add another block
|
|
CPlex* newBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CMap::CAssoc));
|
|
// chain them into free list;
|
|
CMap::CAssoc* pAssoc = (CMap::CAssoc*) newBlock->data();
|
|
// free in reverse order to make it easier to debug
|
|
pAssoc += m_nBlockSize - 1;
|
|
for (int i = m_nBlockSize-1; i >= 0; i--, pAssoc--) {
|
|
pAssoc->pNext = m_pFreeList;
|
|
m_pFreeList = pAssoc;
|
|
}
|
|
}
|
|
ASSERT(m_pFreeList != NULL); // we must have something
|
|
|
|
CMap::CAssoc* pAssoc = m_pFreeList;
|
|
m_pFreeList = m_pFreeList->pNext;
|
|
|
|
m_nCount++;
|
|
ASSERT(m_nCount > 0); // make sure we don't overflow
|
|
|
|
ConstructElements<KEY>(&pAssoc->key, 1);
|
|
ConstructElements<VALUE>(&pAssoc->value, 1); // special construct values
|
|
|
|
return pAssoc;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
void
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::FreeAssoc(
|
|
CAssoc* pAssoc
|
|
)
|
|
{
|
|
DestructElements<VALUE>(&pAssoc->value, 1);
|
|
DestructElements<KEY>(&pAssoc->key, 1);
|
|
|
|
pAssoc->pNext = m_pFreeList;
|
|
m_pFreeList = pAssoc;
|
|
m_nCount--;
|
|
ASSERT(m_nCount >= 0); // make sure we don't underflow
|
|
|
|
// if no more elements, cleanup completely
|
|
if (m_nCount == 0)
|
|
RemoveAll();
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
typename CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::CAssoc*
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetAssocAt(
|
|
ARG_KEY key,
|
|
UINT& nHash
|
|
) const
|
|
{
|
|
nHash = HashKey<ARG_KEY>(key) % m_nHashTableSize;
|
|
|
|
if (m_pHashTable == NULL)
|
|
return NULL;
|
|
|
|
// see if it exists
|
|
CAssoc* pAssoc;
|
|
for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) {
|
|
if (CompareElements(&pAssoc->key, &key))
|
|
return pAssoc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
BOOL
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::Lookup(
|
|
ARG_KEY key,
|
|
VALUE& rValue
|
|
) const
|
|
{
|
|
UINT nHash;
|
|
CAssoc* pAssoc = GetAssocAt(key, nHash);
|
|
if (pAssoc == NULL)
|
|
return FALSE; // not in map
|
|
|
|
rValue = pAssoc->value;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
VALUE&
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::operator[](
|
|
ARG_KEY key
|
|
)
|
|
{
|
|
UINT nHash;
|
|
CAssoc* pAssoc;
|
|
if ((pAssoc = GetAssocAt(key, nHash)) == NULL) {
|
|
if (m_pHashTable == NULL)
|
|
InitHashTable(m_nHashTableSize);
|
|
|
|
// it doesn't exist, add a new Association
|
|
pAssoc = NewAssoc();
|
|
pAssoc->nHashValue = nHash;
|
|
pAssoc->key = key;
|
|
// 'pAssoc->value' is a constructed object, nothing more
|
|
|
|
// put into hash table
|
|
pAssoc->pNext = m_pHashTable[nHash];
|
|
m_pHashTable[nHash] = pAssoc;
|
|
}
|
|
return pAssoc->value; // return new reference
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
BOOL
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::RemoveKey(
|
|
ARG_KEY key
|
|
)
|
|
{
|
|
if (m_pHashTable == NULL)
|
|
return FALSE; // nothing in the table
|
|
|
|
CAssoc** ppAssocPrev;
|
|
ppAssocPrev = &m_pHashTable[HashKey<ARG_KEY>(key) % m_nHashTableSize];
|
|
|
|
CAssoc* pAssoc;
|
|
for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext) {
|
|
if (CompareElements(&pAssoc->key, &key)) {
|
|
// remove it
|
|
*ppAssocPrev = pAssoc->pNext; // remove from list
|
|
FreeAssoc(pAssoc);
|
|
return TRUE;
|
|
}
|
|
ppAssocPrev = &pAssoc->pNext;
|
|
}
|
|
|
|
return FALSE; // not found
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
POSITION
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetStartPosition(
|
|
) const
|
|
{
|
|
return (m_nCount == 0) ? NULL : BEFORE_START_POSITION;
|
|
}
|
|
|
|
|
|
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
|
|
void
|
|
CMap<KEY, ARG_KEY, VALUE, ARG_VALUE>::GetNextAssoc(
|
|
POSITION& rNextPosition,
|
|
KEY& rKey,
|
|
VALUE& rValue
|
|
) const
|
|
{
|
|
ASSERT(m_pHashTable != NULL); // never call on empty map
|
|
|
|
CAssoc* pAssocRet = (CAssoc*)rNextPosition;
|
|
ASSERT(pAssocRet != NULL);
|
|
|
|
if (pAssocRet == (CAssoc*) BEFORE_START_POSITION) {
|
|
// find the first association
|
|
for (UINT nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
|
|
if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
|
|
break;
|
|
ASSERT(pAssocRet != NULL); // must find something
|
|
}
|
|
|
|
// find next association
|
|
CAssoc* pAssocNext;
|
|
if ((pAssocNext = pAssocRet->pNext) == NULL) {
|
|
// go to next bucket
|
|
for (UINT nBucket = pAssocRet->nHashValue + 1; nBucket < m_nHashTableSize; nBucket++)
|
|
if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
|
|
break;
|
|
}
|
|
|
|
rNextPosition = (POSITION) pAssocNext;
|
|
|
|
// fill in return data
|
|
rKey = pAssocRet->key;
|
|
rValue = pAssocRet->value;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CArray<TYPE, ARG_TYPE>
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
class CArray
|
|
{
|
|
public:
|
|
CArray();
|
|
~CArray();
|
|
|
|
INT_PTR GetSize() const;
|
|
BOOL SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1);
|
|
|
|
void RemoveAll();
|
|
|
|
TYPE GetAt(INT_PTR nIndex) const;
|
|
void SetAt(INT_PTR nIndex, ARG_TYPE newElement);
|
|
|
|
const TYPE* GetData() const;
|
|
TYPE* GetData();
|
|
|
|
void SetAtGrow(INT_PTR nIndex, ARG_TYPE newElement);
|
|
INT_PTR Add(ARG_TYPE newElement);
|
|
|
|
void RemoveAt(int nIndex, int nCount = 1);
|
|
|
|
private:
|
|
TYPE* m_pData; // the actual array of data
|
|
INT_PTR m_nSize; // # of elements (upperBound - 1)
|
|
INT_PTR m_nMaxSize; // max allocated
|
|
INT_PTR m_nGrowBy; // grow amount
|
|
};
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
CArray<TYPE, ARG_TYPE>::CArray(
|
|
)
|
|
{
|
|
m_pData = NULL;
|
|
m_nSize = m_nMaxSize = m_nGrowBy = 0;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
CArray<TYPE, ARG_TYPE>::~CArray(
|
|
)
|
|
{
|
|
if (m_pData != NULL) {
|
|
DestructElements<TYPE>(m_pData, m_nSize);
|
|
delete[] (BYTE*)m_pData;
|
|
}
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
INT_PTR
|
|
CArray<TYPE, ARG_TYPE>::GetSize(
|
|
) const
|
|
{
|
|
return m_nSize;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
BOOL
|
|
CArray<TYPE, ARG_TYPE>::SetSize(
|
|
INT_PTR nNewSize,
|
|
INT_PTR nGrowBy
|
|
)
|
|
{
|
|
if (nNewSize < 0)
|
|
return FALSE;
|
|
|
|
if ((nGrowBy == 0) || (nGrowBy < -1))
|
|
return FALSE;
|
|
|
|
if (nGrowBy != -1)
|
|
m_nGrowBy = nGrowBy; // set new size
|
|
|
|
if (nNewSize == 0) {
|
|
// shrink to nothing
|
|
if (m_pData != NULL) {
|
|
DestructElements<TYPE>(m_pData, m_nSize);
|
|
delete[] (BYTE*)m_pData;
|
|
m_pData = NULL;
|
|
}
|
|
m_nSize = m_nMaxSize = 0;
|
|
}
|
|
else if (m_pData == NULL) {
|
|
// create one with exact size
|
|
m_pData = (TYPE*) new BYTE[(size_t)nNewSize * sizeof(TYPE)];
|
|
if (! m_pData)
|
|
return FALSE;
|
|
ConstructElements<TYPE>(m_pData, nNewSize);
|
|
m_nSize = m_nMaxSize = nNewSize;
|
|
}
|
|
else if (nNewSize <= m_nMaxSize) {
|
|
// it fits
|
|
if (nNewSize > m_nSize) {
|
|
// initialize the new elements
|
|
ConstructElements<TYPE>(&m_pData[m_nSize], nNewSize-m_nSize);
|
|
}
|
|
else if (m_nSize > nNewSize) {
|
|
// destroy the old elements
|
|
DestructElements<TYPE>(&m_pData[nNewSize], m_nSize-nNewSize);
|
|
}
|
|
m_nSize = nNewSize;
|
|
}
|
|
else {
|
|
// otherwise, grow array
|
|
INT_PTR nTempGrowBy = m_nGrowBy;
|
|
if (nTempGrowBy == 0) {
|
|
// heuristically determine growth when nTempGrowBy == 0
|
|
// (this avoids heap fragmentation in many situations)
|
|
nTempGrowBy = m_nSize / 8;
|
|
nTempGrowBy = (nTempGrowBy < 4) ? 4 : ((nTempGrowBy > 1024) ? 1024 : nTempGrowBy);
|
|
}
|
|
INT_PTR nNewMax;
|
|
if (nNewSize < m_nMaxSize + nTempGrowBy)
|
|
nNewMax = m_nMaxSize + nTempGrowBy; // granularity
|
|
else
|
|
nNewMax = nNewSize; // no slush
|
|
|
|
ASSERT(nNewMax >= m_nMaxSize); // no wrap around
|
|
|
|
TYPE* pNewData = (TYPE*) new BYTE[(size_t)nNewMax * sizeof(TYPE)];
|
|
if (! pNewData)
|
|
return FALSE;
|
|
|
|
// copy new data from old
|
|
memcpy(pNewData, m_pData, (size_t)m_nSize * sizeof(TYPE));
|
|
|
|
// construct remaining elements
|
|
ASSERT(nNewSize > m_nSize);
|
|
ConstructElements<TYPE>(&pNewData[m_nSize], nNewSize-m_nSize);
|
|
|
|
// get rid of old stuff (note: no destructors called)
|
|
delete[] (BYTE*)m_pData;
|
|
m_pData = pNewData;
|
|
m_nSize = nNewSize;
|
|
m_nMaxSize = nNewMax;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
void
|
|
CArray<TYPE, ARG_TYPE>::RemoveAll(
|
|
)
|
|
{
|
|
SetSize(0);
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
TYPE
|
|
CArray<TYPE, ARG_TYPE>::GetAt(
|
|
INT_PTR nIndex
|
|
) const
|
|
{
|
|
ASSERT(nIndex >= 0 && nIndex < m_nSize);
|
|
return m_pData[nIndex];
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
void
|
|
CArray<TYPE, ARG_TYPE>::SetAt(
|
|
INT_PTR nIndex,
|
|
ARG_TYPE newElement
|
|
)
|
|
{
|
|
ASSERT(nIndex >= 0 && nIndex < m_nSize);
|
|
m_pData[nIndex] = newElement;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
const TYPE*
|
|
CArray<TYPE, ARG_TYPE>::GetData(
|
|
) const
|
|
{
|
|
return (const TYPE*)m_pData;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
TYPE*
|
|
CArray<TYPE, ARG_TYPE>::GetData(
|
|
)
|
|
{
|
|
return (TYPE*)m_pData;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
void
|
|
CArray<TYPE, ARG_TYPE>::SetAtGrow(
|
|
INT_PTR nIndex,
|
|
ARG_TYPE newElement
|
|
)
|
|
{
|
|
ASSERT(nIndex >= 0);
|
|
|
|
if (nIndex >= m_nSize)
|
|
if (! SetSize(nIndex+1))
|
|
return;
|
|
|
|
m_pData[nIndex] = newElement;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
INT_PTR
|
|
CArray<TYPE, ARG_TYPE>::Add(
|
|
ARG_TYPE newElement
|
|
)
|
|
{
|
|
INT_PTR nIndex = m_nSize;
|
|
SetAtGrow(nIndex, newElement);
|
|
return nIndex;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
void
|
|
CArray<TYPE, ARG_TYPE>::RemoveAt(
|
|
int nIndex,
|
|
int nCount
|
|
)
|
|
{
|
|
// just remove a range
|
|
INT_PTR nMoveCount = m_nSize - (nIndex + nCount);
|
|
DestructElements<TYPE>(&m_pData[nIndex], nCount);
|
|
if (nMoveCount)
|
|
memmove(&m_pData[nIndex], &m_pData[nIndex + nCount],
|
|
nMoveCount * sizeof(TYPE));
|
|
m_nSize -= nCount;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CFirstInFirstOut<TYPE, ARG_TYPE>
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
class CFirstInFirstOut
|
|
{
|
|
public:
|
|
CFirstInFirstOut();
|
|
~CFirstInFirstOut();
|
|
|
|
INT_PTR GetSize() const;
|
|
BOOL GetData(TYPE& data);
|
|
VOID SetData(TYPE& data);
|
|
|
|
void GrowBuffer(INT_PTR nGrowBy = 3);
|
|
|
|
private:
|
|
TYPE* m_pData; // the actual ring buffer of data
|
|
INT_PTR m_nMaxSize; // max allocated
|
|
|
|
INT_PTR m_In; // Index of First In of ring buffer
|
|
INT_PTR m_Out; // Index of First Out of ring buffer
|
|
};
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
CFirstInFirstOut<TYPE, ARG_TYPE>::CFirstInFirstOut(
|
|
)
|
|
{
|
|
m_pData = NULL;
|
|
m_In = m_Out = 0;
|
|
m_nMaxSize = 0;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
CFirstInFirstOut<TYPE, ARG_TYPE>::~CFirstInFirstOut(
|
|
)
|
|
{
|
|
if (m_pData != NULL) {
|
|
DestructElements<TYPE>(m_pData, m_nMaxSize);
|
|
delete[] (BYTE*)m_pData;
|
|
}
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
INT_PTR
|
|
CFirstInFirstOut<TYPE, ARG_TYPE>::GetSize(
|
|
) const
|
|
{
|
|
if (m_Out == m_In) {
|
|
return 0; // no more data
|
|
}
|
|
else if (m_In > m_Out) {
|
|
return (m_In - m_Out);
|
|
}
|
|
else {
|
|
return (m_nMaxSize - m_Out) + m_In;
|
|
}
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
BOOL
|
|
CFirstInFirstOut<TYPE, ARG_TYPE>::GetData(
|
|
TYPE& data
|
|
)
|
|
{
|
|
if (m_Out == m_In) {
|
|
return FALSE; // no more data
|
|
}
|
|
|
|
data = m_pData[m_Out++];
|
|
|
|
if (m_Out == m_nMaxSize)
|
|
m_Out = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
VOID
|
|
CFirstInFirstOut<TYPE, ARG_TYPE>::SetData(
|
|
TYPE& data
|
|
)
|
|
{
|
|
if (m_nMaxSize == 0 || GetSize() >= m_nMaxSize - 1)
|
|
GrowBuffer();
|
|
|
|
m_pData[m_In++] = data;
|
|
|
|
if (m_In == m_nMaxSize)
|
|
m_In = 0;
|
|
}
|
|
|
|
template<class TYPE, class ARG_TYPE>
|
|
void
|
|
CFirstInFirstOut<TYPE, ARG_TYPE>::GrowBuffer(
|
|
INT_PTR nGrowBy
|
|
)
|
|
{
|
|
ASSERT(nGrowBy >= 0);
|
|
|
|
if (m_pData == NULL) {
|
|
// create one with exact size
|
|
m_pData = (TYPE*) new BYTE[(size_t)nGrowBy * sizeof(TYPE)];
|
|
if (! m_pData)
|
|
return;
|
|
ConstructElements<TYPE>(m_pData, nGrowBy);
|
|
m_nMaxSize = nGrowBy;
|
|
}
|
|
else {
|
|
// otherwise, grow ring buffer
|
|
INT_PTR nNewMax = m_nMaxSize + nGrowBy;
|
|
TYPE* pNewData = (TYPE*) new BYTE[(size_t)nNewMax * sizeof(TYPE)];
|
|
if (! pNewData)
|
|
return;
|
|
|
|
// copy new data from old
|
|
memcpy(pNewData, m_pData, (size_t)m_nMaxSize * sizeof(TYPE));
|
|
|
|
// construct remaining elements
|
|
ASSERT(nNewMax > m_nMaxSize);
|
|
ConstructElements<TYPE>(&pNewData[m_nMaxSize], nGrowBy);
|
|
|
|
if (m_Out > m_In) {
|
|
// move data to new momory, if out data is remain into the tail of buffer.
|
|
memcpy(&pNewData[m_Out+nGrowBy], &m_pData[m_Out], (size_t)(m_nMaxSize-m_Out) * sizeof(TYPE));
|
|
m_Out += nGrowBy;
|
|
}
|
|
|
|
// get rid of old stuff (note: no destructors called)
|
|
delete[] (BYTE*)m_pData;
|
|
m_pData = pNewData;
|
|
m_nMaxSize = nNewMax;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// _Interface<T>
|
|
|
|
//
|
|
// Auto-interface classes
|
|
//
|
|
|
|
template <class T>
|
|
class _Interface {
|
|
public:
|
|
_Interface(T* p = NULL)
|
|
: m_p(p)
|
|
{
|
|
}
|
|
virtual ~_Interface() = 0
|
|
{
|
|
}
|
|
|
|
bool Valid() { return m_p != NULL; }
|
|
bool Invalid() { return !Valid(); }
|
|
|
|
operator void**() { return (void**)&m_p; }
|
|
|
|
operator T*() { return m_p; }
|
|
operator T**() { return &m_p; }
|
|
T* operator->() { return m_p; }
|
|
|
|
void Attach(T* ip)
|
|
{
|
|
ASSERT(ip);
|
|
m_p = ip;
|
|
}
|
|
|
|
protected:
|
|
T* m_p;
|
|
|
|
private:
|
|
// Do not allow to make a copy
|
|
_Interface(_Interface<T>&);
|
|
void operator=(_Interface<T>&);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Interface_RefCnt<T>
|
|
|
|
template <class T>
|
|
class Interface_RefCnt : public _Interface<T> {
|
|
public:
|
|
Interface_RefCnt(T* p = NULL) : _Interface<T>(p)
|
|
{
|
|
if (m_p) {
|
|
m_p->AddRef();
|
|
}
|
|
}
|
|
|
|
virtual ~Interface_RefCnt()
|
|
{
|
|
if (m_p) {
|
|
m_p->Release();
|
|
}
|
|
}
|
|
|
|
private:
|
|
// Do not allow to make a copy
|
|
Interface_RefCnt(Interface_RefCnt<T>&);
|
|
void operator=(Interface_RefCnt<T>&);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Interface_Attach<T>
|
|
|
|
//
|
|
// The specialized class for auto-release
|
|
//
|
|
template <class T>
|
|
class Interface_Attach : public Interface_RefCnt<T> {
|
|
public:
|
|
// Only the way to create an object of this type is
|
|
// from the similar object.
|
|
Interface_Attach(T* p) : Interface_RefCnt<T>(p) {}
|
|
Interface_Attach(const Interface_Attach<T>& src) : Interface_RefCnt<T>(src.m_p) {}
|
|
|
|
virtual ~Interface_Attach() {}
|
|
|
|
// Since this class is extremely naive, get the pointer to
|
|
// COM interface through the explicit member function.
|
|
T* GetPtr() { return m_p; }
|
|
|
|
public:
|
|
// Do not allow to retrive a pointer
|
|
operator T*();
|
|
|
|
private:
|
|
// Do not allow to make a copy
|
|
// Interface_Attach(Interface_Attach<T>&);
|
|
void operator=(Interface_Attach<T>&);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Interface<T>
|
|
|
|
//
|
|
// The specialized class for auto-release without AddRef.
|
|
//
|
|
template <class T>
|
|
class Interface : public Interface_RefCnt<T> {
|
|
public:
|
|
Interface() {};
|
|
|
|
virtual ~Interface() {}
|
|
|
|
operator T*() { return m_p; }
|
|
|
|
private:
|
|
// Do not allow to make a copy
|
|
Interface(Interface<T>&);
|
|
void operator=(Interface<T>&);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Interface_Creator<T>
|
|
|
|
//
|
|
// This class should be used only by the creator of the series of
|
|
// the objects.
|
|
// The specialized class for auto-release without AddRef.
|
|
//
|
|
template <class T>
|
|
class Interface_Creator : public Interface_RefCnt<T> {
|
|
public:
|
|
Interface_Creator() {};
|
|
|
|
Interface_Creator(T* p)
|
|
{
|
|
Attach(p);
|
|
}
|
|
|
|
virtual ~Interface_Creator() {}
|
|
|
|
bool Valid()
|
|
{
|
|
if (! Interface_RefCnt<T>::Valid())
|
|
return false;
|
|
else
|
|
return m_p ? m_p->Valid() : false;
|
|
}
|
|
|
|
bool Invalid()
|
|
{
|
|
if (Interface_RefCnt<T>::Invalid())
|
|
return true;
|
|
else
|
|
return m_p ? m_p->Invalid() : true;
|
|
}
|
|
|
|
private:
|
|
// Do not allow to make a copy
|
|
Interface_Creator(Interface_Creator<T>&);
|
|
void operator=(Interface_Creator<T>&);
|
|
};
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Interface_TFSELECTION<>
|
|
|
|
//
|
|
// Specialized interface class for TFSELECTION
|
|
// who has a COM pointer in it
|
|
//
|
|
class Interface_TFSELECTION : public _Interface<TF_SELECTION> {
|
|
public:
|
|
Interface_TFSELECTION()
|
|
{
|
|
Attach(&m_sel);
|
|
m_p->range = NULL;
|
|
}
|
|
~Interface_TFSELECTION()
|
|
{
|
|
if (m_p && m_p->range) {
|
|
m_p->range->Release();
|
|
}
|
|
}
|
|
|
|
operator TF_SELECTION*() { return m_p; }
|
|
|
|
void Release()
|
|
{
|
|
ASSERT(m_p && m_p->range);
|
|
m_p->range->Release();
|
|
m_p = NULL;
|
|
}
|
|
|
|
TF_SELECTION m_sel;
|
|
|
|
private:
|
|
// Do not allow to make a copy
|
|
Interface_TFSELECTION(Interface_TFSELECTION&);
|
|
void operator=(Interface_TFSELECTION&);
|
|
};
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CEnumrateInterface<ENUM, CALLBACK>
|
|
|
|
typedef enum {
|
|
ENUM_FIND = 0,
|
|
ENUM_CONTINUE, // DoEnumrate never return ENUM_CONTINUE
|
|
ENUM_NOMOREDATA
|
|
} ENUM_RET;
|
|
|
|
template<class IF_ENUM, class IF_CALLBACK, class IF_ARG>
|
|
class CEnumrateInterface
|
|
{
|
|
public:
|
|
CEnumrateInterface(
|
|
Interface<IF_ENUM>& Enum,
|
|
ENUM_RET (*pCallback)(IF_CALLBACK* pObj, IF_ARG* Arg),
|
|
IF_ARG* Arg = NULL
|
|
) : m_Enum(Enum), m_pfnCallback(pCallback), m_Arg(Arg) {};
|
|
|
|
ENUM_RET DoEnumrate(void);
|
|
|
|
private:
|
|
Interface<IF_ENUM>& m_Enum;
|
|
ENUM_RET (*m_pfnCallback)(IF_CALLBACK* pObj, IF_ARG* Arg);
|
|
IF_ARG* m_Arg;
|
|
};
|
|
|
|
template<class IF_ENUM, class IF_CALLBACK, class IF_ARG>
|
|
ENUM_RET
|
|
CEnumrateInterface<IF_ENUM, IF_CALLBACK, IF_ARG>::DoEnumrate(
|
|
void
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
IF_CALLBACK* pObj;
|
|
|
|
while ((hr = m_Enum->Next(1, &pObj, NULL)) == S_OK) {
|
|
ENUM_RET ret = (*m_pfnCallback)(pObj, m_Arg);
|
|
pObj->Release();
|
|
if (ret == ENUM_FIND)
|
|
return ret;
|
|
}
|
|
return ENUM_NOMOREDATA;
|
|
}
|
|
|
|
|
|
|
|
template<class IF_ENUM, class VAL_CALLBACK, class VAL_ARG>
|
|
class CEnumrateValue
|
|
{
|
|
public:
|
|
CEnumrateValue(
|
|
Interface<IF_ENUM>& Enum,
|
|
ENUM_RET (*pCallback)(VAL_CALLBACK Obj, VAL_ARG* Arg),
|
|
VAL_ARG* Arg = NULL
|
|
) : m_Enum(Enum), m_pfnCallback(pCallback), m_Arg(Arg) {};
|
|
|
|
ENUM_RET DoEnumrate(void);
|
|
|
|
private:
|
|
Interface<IF_ENUM>& m_Enum;
|
|
ENUM_RET (*m_pfnCallback)(VAL_CALLBACK Obj, VAL_ARG* Arg);
|
|
VAL_ARG* m_Arg;
|
|
};
|
|
|
|
template<class IF_ENUM, class VAL_CALLBACK, class VAL_ARG>
|
|
ENUM_RET
|
|
CEnumrateValue<IF_ENUM, VAL_CALLBACK, VAL_ARG>::DoEnumrate(
|
|
void
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
VAL_CALLBACK Obj;
|
|
|
|
while ((hr = m_Enum->Next(1, &Obj, NULL)) == S_OK) {
|
|
ENUM_RET ret = (*m_pfnCallback)(Obj, m_Arg);
|
|
if (ret == ENUM_FIND)
|
|
return ret;
|
|
}
|
|
return ENUM_NOMOREDATA;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Alignment template for IA64 and x86
|
|
|
|
//
|
|
// On the assumption that Win32 (user32/gdi32) handle is 32bit length even IA64 platform.
|
|
//
|
|
template <class TYPE>
|
|
class CAlignWinHandle
|
|
{
|
|
public:
|
|
operator TYPE() { return (TYPE)ULongToPtr(dw); }
|
|
TYPE operator -> () { return (TYPE)ULongToPtr(dw); }
|
|
void operator = (TYPE a)
|
|
{
|
|
dw = (ULONG)(ULONG_PTR)(a);
|
|
}
|
|
|
|
protected:
|
|
DWORD dw; // Alignment is always 32bit.
|
|
};
|
|
|
|
|
|
//
|
|
// Exception HKL. User32 uses "IntToPtr".
|
|
//
|
|
class CAlignWinHKL : public CAlignWinHandle<HKL>
|
|
{
|
|
public:
|
|
operator HKL() { return (HKL)IntToPtr(dw); }
|
|
HKL operator -> () { return (HKL)IntToPtr(dw); }
|
|
void operator = (HKL a)
|
|
{
|
|
dw = (ULONG)(ULONG_PTR)(a);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class TYPE>
|
|
union CAlignPointer
|
|
{
|
|
public:
|
|
operator TYPE() { return h; }
|
|
TYPE operator -> () { return h; }
|
|
void operator = (TYPE a)
|
|
{
|
|
#ifndef _WIN64
|
|
u = 0; // NULL out high dword.
|
|
#endif
|
|
h = a;
|
|
}
|
|
|
|
protected:
|
|
TYPE h;
|
|
|
|
private:
|
|
__int64 u; // Alignment is always __int64.
|
|
};
|
|
|
|
|
|
|
|
template <class TYPE>
|
|
struct CNativeOrWow64_WinHandle
|
|
{
|
|
public:
|
|
TYPE GetHandle(BOOL _bOnWow64) { return ! _bOnWow64 ? _h : _h_wow6432; }
|
|
TYPE SetHandle(BOOL _bOnWow64, TYPE a)
|
|
{
|
|
if ( ! _bOnWow64)
|
|
_h = a;
|
|
else
|
|
_h_wow6432 = a;
|
|
return a;
|
|
}
|
|
|
|
private:
|
|
// Native system HHOOK
|
|
CAlignWinHandle<TYPE> _h;
|
|
|
|
// WOW6432 system HHOOK
|
|
CAlignWinHandle<TYPE> _h_wow6432;
|
|
};
|
|
|
|
|
|
|
|
template <class TYPE>
|
|
struct CNativeOrWow64_Pointer
|
|
{
|
|
public:
|
|
TYPE GetPunk(BOOL _bOnWow64) { return ! _bOnWow64 ? _pv : _pv_wow6432; }
|
|
TYPE SetPunk(BOOL _bOnWow64, TYPE a)
|
|
{
|
|
if ( ! _bOnWow64)
|
|
_pv = a;
|
|
else
|
|
_pv_wow6432 = a;
|
|
return a;
|
|
}
|
|
|
|
private:
|
|
// Native system ITfLangBarEventSink
|
|
CAlignPointer<TYPE> _pv;
|
|
|
|
// WOW6432 system ITfLangBarEventSink
|
|
CAlignPointer<TYPE> _pv_wow6432;
|
|
};
|
|
|
|
|
|
#endif // _TEMPLATE_H_
|