blob: afc7c8219ed3ab12f26b02481a0c94695595703b [file] [log] [blame]
// 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)
: 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_Free(m_pHashTable);
m_pHashTable = NULL;
}
m_nCount = 0;
m_pFreeList = NULL;
m_pBlocks->FreeDataChain();
m_pBlocks = NULL;
}
CFX_MapPtrToPtr::~CFX_MapPtrToPtr()
{
RemoveAll();
ASSERT(m_nCount == 0);
}
FX_DWORD CFX_MapPtrToPtr::HashKey(void* key) const
{
return ((FX_DWORD)(uintptr_t)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_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_Free(m_pHashTable);
m_pHashTable = NULL;
}
if (bAllocNow) {
m_pHashTable = FX_Alloc(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)
: 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_Free(m_pHashTable);
m_pHashTable = NULL;
}
m_nCount = 0;
m_pFreeList = NULL;
m_pBlocks->FreeDataChain();
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;
}
void* 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[](const CFX_ByteStringC& 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_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(const CFX_ByteStringC& 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(const CFX_ByteStringC& 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_Free(m_pHashTable);
m_pHashTable = NULL;
}
if (bAllocNow) {
m_pHashTable = FX_Alloc(CAssoc*, nHashSize);
}
m_nHashTableSize = nHashSize;
}
inline FX_DWORD CFX_MapByteStringToPtr::HashKey(const CFX_ByteStringC& key) const
{
FX_DWORD nHash = 0;
int len = key.GetLength();
const uint8_t* buf = key.GetPtr();
for (int i = 0; i < len; i ++) {
nHash = (nHash << 5) + nHash + buf[i];
}
return nHash;
}
FX_BOOL CFX_MapByteStringToPtr::RemoveKey(const CFX_ByteStringC& 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 {
uint8_t m_CompactLen;
uint8_t m_LenHigh;
uint8_t m_LenLow;
uint8_t m_Unused;
uint8_t* m_pBuffer;
};
static void _CompactStringRelease(_CompactString* pCompact)
{
if (pCompact->m_CompactLen == 0xff) {
FX_Free(pCompact->m_pBuffer);
}
}
static FX_BOOL _CompactStringSame(_CompactString* pCompact, const uint8_t* 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(_CompactString* pCompact, const uint8_t* pStr, int len)
{
if (len < (int)sizeof(_CompactString)) {
pCompact->m_CompactLen = (uint8_t)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_Alloc(uint8_t, len);
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()
: m_Buffer(sizeof(_CompactString) + sizeof(void*), CMAP_ALLOC_STEP, CMAP_INDEX_SIZE)
{
}
CFX_CMapByteStringToPtr::~CFX_CMapByteStringToPtr()
{
RemoveAll();
}
void CFX_CMapByteStringToPtr::RemoveAll()
{
int size = m_Buffer.GetSize();
for (int i = 0; i < size; i++) {
_CompactStringRelease((_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)(uintptr_t)(i + 1);
}
}
return NULL;
}
void CFX_CMapByteStringToPtr::GetNextAssoc(FX_POSITION& rNextPosition, CFX_ByteString& rKey, void*& rValue) const
{
if (rNextPosition == NULL) {
return;
}
int index = (int)(uintptr_t)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)(uintptr_t)(index + 1);
return;
}
index ++;
}
rNextPosition = NULL;
}
void* CFX_CMapByteStringToPtr::GetNextValue(FX_POSITION& rNextPosition) const
{
if (rNextPosition == NULL) {
return NULL;
}
int index = (int)(uintptr_t)rNextPosition - 1;
_CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
void* 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)(uintptr_t)(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(const CFX_ByteStringC& 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(const CFX_ByteStringC& 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, key.GetPtr(), key_len)) {
continue;
}
*(void**)(pKey + 1) = value;
return;
}
for (index = 0; index < size; index ++) {
_CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
if (pKey->m_CompactLen) {
continue;
}
_CompactStringStore(pKey, key.GetPtr(), key_len);
*(void**)(pKey + 1) = value;
return;
}
_CompactString* pKey = (_CompactString*)m_Buffer.Add();
_CompactStringStore(pKey, key.GetPtr(), key_len);
*(void**)(pKey + 1) = value;
}
void CFX_CMapByteStringToPtr::AddValue(const CFX_ByteStringC& key, void* value)
{
ASSERT(value != NULL);
_CompactString* pKey = (_CompactString*)m_Buffer.Add();
_CompactStringStore(pKey, key.GetPtr(), key.GetLength());
*(void**)(pKey + 1) = value;
}
void CFX_CMapByteStringToPtr::RemoveKey(const CFX_ByteStringC& key)
{
int key_len = key.GetLength();
int size = m_Buffer.GetSize();
for (int index = 0; index < size; index++) {
_CompactString* pKey = (_CompactString*)m_Buffer.GetAt(index);
if (!_CompactStringSame(pKey, key.GetPtr(), key_len)) {
continue;
}
_CompactStringRelease(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
{
void* 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)(uintptr_t)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)((uintptr_t)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);
}