// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../include/fxcrt/fx_ext.h"
#include "plex.h"
static void ConstructElement(CFX_ByteString* pNewData)
{
    new (pNewData) CFX_ByteString();
}
static void DestructElement(CFX_ByteString* pOldData)
{
    pOldData->~CFX_ByteString();
}
CFX_MapPtrToPtr::CFX_MapPtrToPtr(int nBlockSize, IFX_Allocator* pAllocator)
    : m_pAllocator(pAllocator)
    , m_pHashTable(NULL)
    , m_nHashTableSize(17)
    , m_nCount(0)
    , m_pFreeList(NULL)
    , m_pBlocks(NULL)
    , m_nBlockSize(nBlockSize)
{
    ASSERT(m_nBlockSize > 0);
}
void CFX_MapPtrToPtr::RemoveAll()
{
    if (m_pHashTable) {
        FX_Allocator_Free(m_pAllocator, m_pHashTable);
        m_pHashTable = NULL;
    }
    m_nCount = 0;
    m_pFreeList = NULL;
    m_pBlocks->FreeDataChain(m_pAllocator);
    m_pBlocks = NULL;
}
CFX_MapPtrToPtr::~CFX_MapPtrToPtr()
{
    RemoveAll();
    ASSERT(m_nCount == 0);
}
FX_DWORD CFX_MapPtrToPtr::HashKey(void* key) const
{
    return ((FX_DWORD)(FX_UINTPTR)key) >> 4;
}
void CFX_MapPtrToPtr::GetNextAssoc(FX_POSITION& rNextPosition, void*& rKey, void*& rValue) const
{
    ASSERT(m_pHashTable != NULL);
    CAssoc* pAssocRet = (CAssoc*)rNextPosition;
    ASSERT(pAssocRet != NULL);
    if (pAssocRet == (CAssoc*) - 1) {
        for (FX_DWORD nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
            if ((pAssocRet = m_pHashTable[nBucket]) != NULL) {
                break;
            }
        ASSERT(pAssocRet != NULL);
    }
    CAssoc* pAssocNext;
    if ((pAssocNext = pAssocRet->pNext) == NULL) {
        for (FX_DWORD nBucket = (HashKey(pAssocRet->key) % m_nHashTableSize) + 1; nBucket < m_nHashTableSize; nBucket ++) {
            if ((pAssocNext = m_pHashTable[nBucket]) != NULL) {
                break;
            }
        }
    }
    rNextPosition = (FX_POSITION) pAssocNext;
    rKey = pAssocRet->key;
    rValue = pAssocRet->value;
}
FX_BOOL CFX_MapPtrToPtr::Lookup(void* key, void*& rValue) const
{
    FX_DWORD nHash;
    CAssoc* pAssoc = GetAssocAt(key, nHash);
    if (pAssoc == NULL) {
        return FALSE;
    }
    rValue = pAssoc->value;
    return TRUE;
}
void* CFX_MapPtrToPtr::GetValueAt(void* key) const
{
    FX_DWORD nHash;
    CAssoc* pAssoc = GetAssocAt(key, nHash);
    if (pAssoc == NULL) {
        return NULL;
    }
    return pAssoc->value;
}
void*& CFX_MapPtrToPtr::operator[](void* key)
{
    FX_DWORD nHash;
    CAssoc* pAssoc;
    if ((pAssoc = GetAssocAt(key, nHash)) == NULL) {
        if (m_pHashTable == NULL) {
            InitHashTable(m_nHashTableSize);
        }
        pAssoc = NewAssoc();
        pAssoc->key = key;
        pAssoc->pNext = m_pHashTable[nHash];
        m_pHashTable[nHash] = pAssoc;
    }
    return pAssoc->value;
}
CFX_MapPtrToPtr::CAssoc*
CFX_MapPtrToPtr::GetAssocAt(void* key, FX_DWORD& nHash) const
{
    nHash = HashKey(key) % m_nHashTableSize;
    if (m_pHashTable == NULL) {
        return NULL;
    }
    CAssoc* pAssoc;
    for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) {
        if (pAssoc->key == key) {
            return pAssoc;
        }
    }
    return NULL;
}
CFX_MapPtrToPtr::CAssoc*
CFX_MapPtrToPtr::NewAssoc()
{
    if (m_pFreeList == NULL) {
        CFX_Plex* newBlock = CFX_Plex::Create(m_pAllocator, m_pBlocks, m_nBlockSize, sizeof(CFX_MapPtrToPtr::CAssoc));
        CFX_MapPtrToPtr::CAssoc* pAssoc = (CFX_MapPtrToPtr::CAssoc*)newBlock->data();
        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);
    CFX_MapPtrToPtr::CAssoc* pAssoc = m_pFreeList;
    m_pFreeList = m_pFreeList->pNext;
    m_nCount++;
    ASSERT(m_nCount > 0);
    pAssoc->key = 0;
    pAssoc->value = 0;
    return pAssoc;
}
void CFX_MapPtrToPtr::InitHashTable(
    FX_DWORD nHashSize, FX_BOOL bAllocNow)
{
    ASSERT(m_nCount == 0);
    ASSERT(nHashSize > 0);
    if (m_pHashTable != NULL) {
        FX_Allocator_Free(m_pAllocator, m_pHashTable);
        m_pHashTable = NULL;
    }
    if (bAllocNow) {
        m_pHashTable = FX_Allocator_Alloc(m_pAllocator, CAssoc*, nHashSize);
        if (m_pHashTable) {
            FXSYS_memset32(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize);
        }
    }
    m_nHashTableSize = nHashSize;
}
FX_BOOL CFX_MapPtrToPtr::RemoveKey(void* key)
{
    if (m_pHashTable == NULL) {
        return FALSE;
    }
    CAssoc** ppAssocPrev;
    ppAssocPrev = &m_pHashTable[HashKey(key) % m_nHashTableSize];
    CAssoc* pAssoc;
    for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext) {
        if (pAssoc->key == key) {
            *ppAssocPrev = pAssoc->pNext;
            FreeAssoc(pAssoc);
            return TRUE;
        }
        ppAssocPrev = &pAssoc->pNext;
    }
    return FALSE;
}
void CFX_MapPtrToPtr::FreeAssoc(CFX_MapPtrToPtr::CAssoc* pAssoc)
{
    pAssoc->pNext = m_pFreeList;
    m_pFreeList = pAssoc;
    m_nCount--;
    ASSERT(m_nCount >= 0);
    if (m_nCount == 0) {
        RemoveAll();
    }
}
CFX_MapByteStringToPtr::CFX_MapByteStringToPtr(int nBlockSize, IFX_Allocator* pAllocator)
    : m_pAllocator(pAllocator)
    , m_pHashTable(NULL)
    , m_nHashTableSize(17)
    , m_nCount(0)
    , m_pFreeList(NULL)
    , m_pBlocks(NULL)
    , m_nBlockSize(nBlockSize)
{
    ASSERT(m_nBlockSize > 0);
}
void CFX_MapByteStringToPtr::RemoveAll()
{
    if (m_pHashTable != NULL) {
        for (FX_DWORD nHash = 0; nHash < m_nHashTableSize; nHash++) {
            CAssoc* pAssoc;
            for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
                    pAssoc = pAssoc->pNext) {
                DestructElement(&pAssoc->key);
            }
        }
        FX_Allocator_Free(m_pAllocator, m_pHashTable);
        m_pHashTable = NULL;
    }
    m_nCount = 0;
    m_pFreeList = NULL;
    m_pBlocks->FreeDataChain(m_pAllocator);
    m_pBlocks = NULL;
}
CFX_MapByteStringToPtr::~CFX_MapByteStringToPtr()
{
    RemoveAll();
    ASSERT(m_nCount == 0);
}
void CFX_MapByteStringToPtr::GetNextAssoc(FX_POSITION& rNextPosition,
        CFX_ByteString& rKey, void*& rValue) const
{
    ASSERT(m_pHashTable != NULL);
    CAssoc* pAssocRet = (CAssoc*)rNextPosition;
    ASSERT(pAssocRet != NULL);
    if (pAssocRet == (CAssoc*) - 1) {
        for (FX_DWORD nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
            if ((pAssocRet = m_pHashTable[nBucket]) != NULL) {
                break;
            }
        ASSERT(pAssocRet != NULL);
    }
    CAssoc* pAssocNext;
    if ((pAssocNext = pAssocRet->pNext) == NULL) {
        for (FX_DWORD nBucket = pAssocRet->nHashValue + 1;
                nBucket < m_nHashTableSize; nBucket++)
            if ((pAssocNext = m_pHashTable[nBucket]) != NULL) {
                break;
            }
    }
    rNextPosition = (FX_POSITION) pAssocNext;
    rKey = pAssocRet->key;
    rValue = pAssocRet->value;
}
FX_LPVOID CFX_MapByteStringToPtr::GetNextValue(FX_POSITION& rNextPosition) const
{
    ASSERT(m_pHashTable != NULL);
    CAssoc* pAssocRet = (CAssoc*)rNextPosition;
    ASSERT(pAssocRet != NULL);
    if (pAssocRet == (CAssoc*) - 1) {
        for (FX_DWORD nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
            if ((pAssocRet = m_pHashTable[nBucket]) != NULL) {
                break;
            }
        ASSERT(pAssocRet != NULL);
    }
    CAssoc* pAssocNext;
    if ((pAssocNext = pAssocRet->pNext) == NULL) {
        for (FX_DWORD nBucket = pAssocRet->nHashValue + 1;
                nBucket < m_nHashTableSize; nBucket++)
            if ((pAssocNext = m_pHashTable[nBucket]) != NULL) {
                break;
            }
    }
    rNextPosition = (FX_POSITION) pAssocNext;
    return pAssocRet->value;
}
void*& CFX_MapByteStringToPtr::operator[](FX_BSTR key)
{
    FX_DWORD nHash;
    CAssoc* pAssoc;
    if ((pAssoc = GetAssocAt(key, nHash)) == NULL) {
        if (m_pHashTable == NULL) {
            InitHashTable(m_nHashTableSize);
        }
        pAssoc = NewAssoc();
        pAssoc->nHashValue = nHash;
        pAssoc->key = key;
        pAssoc->pNext = m_pHashTable[nHash];
        m_pHashTable[nHash] = pAssoc;
    }
    return pAssoc->value;
}
CFX_MapByteStringToPtr::CAssoc*
CFX_MapByteStringToPtr::NewAssoc()
{
    if (m_pFreeList == NULL) {
        CFX_Plex* newBlock = CFX_Plex::Create(m_pAllocator, m_pBlocks, m_nBlockSize, sizeof(CFX_MapByteStringToPtr::CAssoc));
        CFX_MapByteStringToPtr::CAssoc* pAssoc = (CFX_MapByteStringToPtr::CAssoc*)newBlock->data();
        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);
    CFX_MapByteStringToPtr::CAssoc* pAssoc = m_pFreeList;
    m_pFreeList = m_pFreeList->pNext;
    m_nCount++;
    ASSERT(m_nCount > 0);
    ConstructElement(&pAssoc->key);
    pAssoc->value = 0;
    return pAssoc;
}
void CFX_MapByteStringToPtr::FreeAssoc(CFX_MapByteStringToPtr::CAssoc* pAssoc)
{
    DestructElement(&pAssoc->key);
    pAssoc->pNext = m_pFreeList;
    m_pFreeList = pAssoc;
    m_nCount--;
    ASSERT(m_nCount >= 0);
    if (m_nCount == 0) {
        RemoveAll();
    }
}
CFX_MapByteStringToPtr::CAssoc*
CFX_MapByteStringToPtr::GetAssocAt(FX_BSTR key, FX_DWORD& nHash) const
{
    nHash = HashKey(key) % m_nHashTableSize;
    if (m_pHashTable == NULL) {
        return NULL;
    }
    CAssoc* pAssoc;
    for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) {
        if (pAssoc->key == key) {
            return pAssoc;
        }
    }
    return NULL;
}
FX_BOOL CFX_MapByteStringToPtr::Lookup(FX_BSTR key, void*& rValue) const
{
    FX_DWORD nHash;
    CAssoc* pAssoc = GetAssocAt(key, nHash);
    if (pAssoc == NULL) {
        return FALSE;
    }
    rValue = pAssoc->value;
    return TRUE;
}
void CFX_MapByteStringToPtr::InitHashTable(
    FX_DWORD nHashSize, FX_BOOL bAllocNow)
{
    ASSERT(m_nCount == 0);
    ASSERT(nHashSize > 0);
    if (m_pHashTable != NULL) {
        FX_Allocator_Free(m_pAllocator, m_pHashTable);
        m_pHashTable = NULL;
    }
    if (bAllocNow) {
        m_pHashTable = FX_Allocator_Alloc(m_pAllocator, CAssoc*, nHashSize);
        if (m_pHashTable) {
            FXSYS_memset32(m_pHashTable, 0, sizeof(CAssoc*) * nHashSize);
        }
    }
    m_nHashTableSize = nHashSize;
}
inline FX_DWORD CFX_MapByteStringToPtr::HashKey(FX_BSTR key) const
{
    FX_DWORD nHash = 0;
    int len = key.GetLength();
    FX_LPCBYTE buf = key;
    for (int i = 0; i < len; i ++) {
        nHash = (nHash << 5) + nHash + buf[i];
    }
    return nHash;
}
FX_BOOL CFX_MapByteStringToPtr::RemoveKey(FX_BSTR key)
{
    if (m_pHashTable == NULL) {
        return FALSE;
    }
    CAssoc** ppAssocPrev;
    ppAssocPrev = &m_pHashTable[HashKey(key) % m_nHashTableSize];
    CAssoc* pAssoc;
    for (pAssoc = *ppAssocPrev; pAssoc != NULL; pAssoc = pAssoc->pNext) {
        if (pAssoc->key == key) {
            *ppAssocPrev = pAssoc->pNext;
            FreeAssoc(pAssoc);
            return TRUE;
        }
        ppAssocPrev = &pAssoc->pNext;
    }
    return FALSE;
}
struct _CompactString {
    FX_BYTE		m_CompactLen;
    FX_BYTE		m_LenHigh;
    FX_BYTE		m_LenLow;
    FX_BYTE		m_Unused;
    FX_LPBYTE	m_pBuffer;
};
static void _CompactStringRelease(IFX_Allocator* pAllocator, _CompactString* pCompact)
{
    if (pCompact->m_CompactLen == 0xff) {
        FX_Allocator_Free(pAllocator, pCompact->m_pBuffer);
    }
}
static FX_BOOL _CompactStringSame(_CompactString* pCompact, FX_LPCBYTE pStr, int len)
{
    if (len < sizeof(_CompactString)) {
        if (pCompact->m_CompactLen != len) {
            return FALSE;
        }
        return FXSYS_memcmp32(&pCompact->m_LenHigh, pStr, len) == 0;
    }
    if (pCompact->m_CompactLen != 0xff || pCompact->m_LenHigh * 256 + pCompact->m_LenLow != len) {
        return FALSE;
    }
    return FXSYS_memcmp32(pCompact->m_pBuffer, pStr, len) == 0;
}
static void _CompactStringStore(IFX_Allocator* pAllocator, _CompactString* pCompact, FX_LPCBYTE pStr, int len)
{
    if (len < (int)sizeof(_CompactString)) {
        pCompact->m_CompactLen = (FX_BYTE)len;
        FXSYS_memcpy32(&pCompact->m_LenHigh, pStr, len);
        return;
    }
    pCompact->m_CompactLen = 0xff;
    pCompact->m_LenHigh = len / 256;
    pCompact->m_LenLow = len % 256;
    pCompact->m_pBuffer = FX_Allocator_Alloc(pAllocator, FX_BYTE, len);
    if (pCompact->m_pBuffer) {
        FXSYS_memcpy32(pCompact->m_pBuffer, pStr, len);
    }
}
static CFX_ByteStringC _CompactStringGet(_CompactString* pCompact)
{
    if (pCompact->m_CompactLen == 0xff) {
        return CFX_ByteStringC(pCompact->m_pBuffer, pCompact->m_LenHigh * 256 + pCompact->m_LenLow);
    }
    if (pCompact->m_CompactLen == 0xfe) {
        return CFX_ByteStringC();
    }
    return CFX_ByteStringC(&pCompact->m_LenHigh, pCompact->m_CompactLen);
}
#define CMAP_ALLOC_STEP		8
#define CMAP_INDEX_SIZE		8
CFX_CMapByteStringToPtr::CFX_CMapByteStringToPtr(IFX_Allocator* pAllocator)
    : m_Buffer(sizeof(_CompactString) + sizeof(void*), CMAP_ALLOC_STEP, CMAP_INDEX_SIZE, pAllocator)
{
}
CFX_CMapByteStringToPtr::~CFX_CMapByteStringToPtr()
{
    RemoveAll();
}
void CFX_CMapByteStringToPtr::RemoveAll()
{
    IFX_Allocator* pAllocator = m_Buffer.m_pAllocator;
    int size = m_Buffer.GetSize();
    for (int i = 0; i < size; i ++) {
        _CompactStringRelease(pAllocator, (_CompactString*)m_Buffer.GetAt(i));
    }
    m_Buffer.RemoveAll();
}
FX_POSITION CFX_CMapByteStringToPtr::GetStartPosition() const
{
    int size = m_Buffer.GetSize();
    for (int i = 0; i < size; i ++) {
        _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(i);
        if (pKey->m_CompactLen != 0xfe) {
            return (FX_POSITION)(FX_UINTPTR)(i + 1);
        }
    }
    return NULL;
}
void CFX_CMapByteStringToPtr::GetNextAssoc(FX_POSITION& rNextPosition, CFX_ByteString& rKey, void*& rValue) const
{
    if (rNextPosition == NULL) {
        return;
    }
    int index = (int)(FX_UINTPTR)rNextPosition - 1;
    _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
    rKey = _CompactStringGet(pKey);
    rValue = *(void**)(pKey + 1);
    index ++;
    int size = m_Buffer.GetSize();
    while (index < size) {
        pKey = (_CompactString*)m_Buffer.GetAt(index);
        if (pKey->m_CompactLen != 0xfe) {
            rNextPosition = (FX_POSITION)(FX_UINTPTR)(index + 1);
            return;
        }
        index ++;
    }
    rNextPosition = NULL;
}
FX_LPVOID CFX_CMapByteStringToPtr::GetNextValue(FX_POSITION& rNextPosition) const
{
    if (rNextPosition == NULL) {
        return NULL;
    }
    int index = (int)(FX_UINTPTR)rNextPosition - 1;
    _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
    FX_LPVOID rValue = *(void**)(pKey + 1);
    index ++;
    int size = m_Buffer.GetSize();
    while (index < size) {
        pKey = (_CompactString*)m_Buffer.GetAt(index);
        if (pKey->m_CompactLen != 0xfe) {
            rNextPosition = (FX_POSITION)(FX_UINTPTR)(index + 1);
            return rValue;
        }
        index ++;
    }
    rNextPosition = NULL;
    return rValue;
}
FX_BOOL _CMapLookupCallback(void* param, void* pData)
{
    return !_CompactStringSame((_CompactString*)pData, ((CFX_ByteStringC*)param)->GetPtr(), ((CFX_ByteStringC*)param)->GetLength());
}
FX_BOOL CFX_CMapByteStringToPtr::Lookup(FX_BSTR key, void*& rValue) const
{
    void* p = m_Buffer.Iterate(_CMapLookupCallback, (void*)&key);
    if (!p) {
        return FALSE;
    }
    rValue = *(void**)((_CompactString*)p + 1);
    return TRUE;
}
void CFX_CMapByteStringToPtr::SetAt(FX_BSTR key, void* value)
{
    ASSERT(value != NULL);
    int index, key_len = key.GetLength();
    int size = m_Buffer.GetSize();
    for (index = 0; index < size; index ++) {
        _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
        if (!_CompactStringSame(pKey, (FX_LPCBYTE)key, key_len)) {
            continue;
        }
        *(void**)(pKey + 1) = value;
        return;
    }
    IFX_Allocator* pAllocator = m_Buffer.m_pAllocator;
    for (index = 0; index < size; index ++) {
        _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
        if (pKey->m_CompactLen) {
            continue;
        }
        _CompactStringStore(pAllocator, pKey, (FX_LPCBYTE)key, key_len);
        *(void**)(pKey + 1) = value;
        return;
    }
    _CompactString* pKey = (_CompactString*)m_Buffer.Add();
    _CompactStringStore(pAllocator, pKey, (FX_LPCBYTE)key, key_len);
    *(void**)(pKey + 1) = value;
}
void CFX_CMapByteStringToPtr::AddValue(FX_BSTR key, void* value)
{
    ASSERT(value != NULL);
    _CompactString* pKey = (_CompactString*)m_Buffer.Add();
    _CompactStringStore(m_Buffer.m_pAllocator, pKey, (FX_LPCBYTE)key, key.GetLength());
    *(void**)(pKey + 1) = value;
}
void CFX_CMapByteStringToPtr::RemoveKey(FX_BSTR key)
{
    int key_len = key.GetLength();
    IFX_Allocator* pAllocator = m_Buffer.m_pAllocator;
    int size = m_Buffer.GetSize();
    for (int index = 0; index < size; index ++) {
        _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
        if (!_CompactStringSame(pKey, (FX_LPCBYTE)key, key_len)) {
            continue;
        }
        _CompactStringRelease(pAllocator, pKey);
        pKey->m_CompactLen = 0xfe;
        return;
    }
}
int CFX_CMapByteStringToPtr::GetCount() const
{
    int count = 0;
    int size = m_Buffer.GetSize();
    for (int i = 0; i < size; i ++) {
        _CompactString* pKey = (_CompactString*)m_Buffer.GetAt(i);
        if (pKey->m_CompactLen != 0xfe) {
            count ++;
        }
    }
    return count;
}
extern "C" {
    static int _CompareDWord(const void* p1, const void* p2)
    {
        return (*(FX_DWORD*)p1) - (*(FX_DWORD*)p2);
    }
};
struct _DWordPair {
    FX_DWORD key;
    FX_DWORD value;
};
FX_BOOL CFX_CMapDWordToDWord::Lookup(FX_DWORD key, FX_DWORD& value) const
{
    FX_LPVOID pResult = FXSYS_bsearch(&key, m_Buffer.GetBuffer(), m_Buffer.GetSize() / sizeof(_DWordPair),
                                      sizeof(_DWordPair), _CompareDWord);
    if (pResult == NULL) {
        return FALSE;
    }
    value = ((FX_DWORD*)pResult)[1];
    return TRUE;
}
FX_POSITION CFX_CMapDWordToDWord::GetStartPosition() const
{
    FX_DWORD count = m_Buffer.GetSize() / sizeof(_DWordPair);
    if (count == 0) {
        return NULL;
    }
    return (FX_POSITION)1;
}
void CFX_CMapDWordToDWord::GetNextAssoc(FX_POSITION& pos, FX_DWORD& key, FX_DWORD& value) const
{
    if (pos == 0) {
        return;
    }
    FX_DWORD index = ((FX_DWORD)(FX_UINTPTR)pos) - 1;
    FX_DWORD count = m_Buffer.GetSize() / sizeof(_DWordPair);
    _DWordPair* buf = (_DWordPair*)m_Buffer.GetBuffer();
    key = buf[index].key;
    value = buf[index].value;
    if (index == count - 1) {
        pos = 0;
    } else {
        pos = (FX_POSITION)((FX_UINTPTR)pos + 1);
    }
}
void CFX_CMapDWordToDWord::SetAt(FX_DWORD key, FX_DWORD value)
{
    FX_DWORD count = m_Buffer.GetSize() / sizeof(_DWordPair);
    _DWordPair* buf = (_DWordPair*)m_Buffer.GetBuffer();
    _DWordPair pair = {key, value};
    if (count == 0 || key > buf[count - 1].key) {
        m_Buffer.AppendBlock(&pair, sizeof(_DWordPair));
        return;
    }
    int low = 0, high = count - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (buf[mid].key < key) {
            low = mid + 1;
        } else if (buf[mid].key > key) {
            high = mid - 1;
        } else {
            buf[mid].value = value;
            return;
        }
    }
    m_Buffer.InsertBlock(low * sizeof(_DWordPair), &pair, sizeof(_DWordPair));
}
void CFX_CMapDWordToDWord::EstimateSize(FX_DWORD size, FX_DWORD grow_by)
{
    m_Buffer.EstimateSize(size, grow_by);
}