blob: ab9b29d9853ec50e77d52879beff2845ca4dddeb [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
template<class T>
class SAChunk
{
public:
SAChunk<T> * next;
uint32 startIndex;
T * data[];
};
template<class T>
class SparseArray
{
private:
ArenaAllocator * alloc;
uint32 chunkSize;
SAChunk<T> * firstChunk;
public:
static SparseArray<T> * New(ArenaAllocator *allocator, uint32 chunkSize)
{
SparseArray<T> * array;
if (!Math::IsPow2(chunkSize))
{
chunkSize = Math::NextPowerOf2(chunkSize);
}
// Throw early if this overflows, since chunkSize never changes, subsequent operations will be safe
UInt32Math::MulAdd<sizeof(T*), sizeof(SAChunk<T>)>(chunkSize);
array = Anew(allocator, SparseArray<T>);
array->alloc = allocator;
array->chunkSize = chunkSize;
array->firstChunk = NULL;
return array;
}
void Set(uint32 index, T *element)
{
SAChunk<T> * chunk, **pPrev = &(this->firstChunk);
uint32 indexInChunk = (index % this->chunkSize);
for (chunk = this->firstChunk; chunk; chunk = chunk->next)
{
if (index < chunk->startIndex)
{
// Need a new chunk...
chunk = NULL;
break;
}
if (index < chunk->startIndex + this->chunkSize)
{
break;
}
pPrev = &(chunk->next);
}
if (chunk == NULL)
{
chunk = (SAChunk<T> *)this->alloc->AllocZero(sizeof(SAChunk<T>) + (chunkSize * sizeof(T *)));
chunk->startIndex = index - indexInChunk;
// Since startIndex and chunkSize don't change, check now if this overflows.
// Cache the result or save memory ?
UInt32Math::Add(chunk->startIndex, chunkSize);
chunk->next = *pPrev;
*pPrev = chunk;
}
chunk->data[indexInChunk] = element;
}
T * Get(uint32 index)
{
SAChunk<T> * chunk;
uint32 indexInChunk = (index % this->chunkSize);
for (chunk = this->firstChunk; chunk; chunk = chunk->next)
{
if (index < chunk->startIndex)
{
return NULL;
}
if (index < chunk->startIndex + this->chunkSize)
{
return chunk->data[indexInChunk];
}
}
return NULL;
}
SparseArray<T> * Copy()
{
SparseArray<T> * newSA = SparseArray<T>::New(this->alloc, this->chunkSize);
SAChunk<T> * chunk, *pred = NULL;
for (chunk = this->firstChunk; chunk; chunk = chunk->next)
{
SAChunk<T> *newChunk = (SAChunk<T> *)this->alloc->Alloc(sizeof(SAChunk<T>) + (sizeof(T *) * this->chunkSize));
newChunk->startIndex = chunk->startIndex;
js_memcpy_s(newChunk->data, sizeof(T *) * this->chunkSize, chunk->data, sizeof(T *) * this->chunkSize);
if (pred)
{
pred->next = newChunk;
}
else
{
newSA->firstChunk = newChunk;
}
pred = newChunk;
}
if (pred)
{
pred->next = NULL;
}
else
{
newSA->firstChunk = NULL;
}
return newSA;
}
void And(SparseArray<T> *this2)
{
SAChunk<T> * chunk, *pred = NULL;
SAChunk<T> * chunk2;
AssertMsg(this->chunkSize == this2->chunkSize, "Anding incompatible arrays");
chunk2 = this2->firstChunk;
for (chunk = this->firstChunk; chunk; chunk = chunk->next)
{
while (chunk2 && chunk->startIndex > chunk2->startIndex)
{
chunk2 = chunk2->next;
}
if (chunk2 == NULL || chunk->startIndex < chunk2->startIndex)
{
if (pred)
{
pred->next = chunk->next;
}
else
{
this->firstChunk = chunk->next;
}
continue;
}
AssertMsg(chunk->startIndex == chunk2->startIndex, "Huh??");
for (int i = 0; i < this->chunkSize; i++)
{
if (chunk->data[i])
{
if (chunk2->data[i])
{
if (*(chunk->data[i]) == *(chunk2->data[i]))
{
continue;
}
}
chunk->data[i] = NULL;
}
}
chunk2 = chunk2->next;
pred = chunk;
}
}
void Clear()
{
this->firstChunk = NULL;
}
#if DBG_DUMP
void Dump()
{
for (SAChunk<T> *chunk = this->firstChunk; chunk; chunk = chunk->next)
{
for (int index = chunk->startIndex; index < this->chunkSize; index++)
{
if (chunk->data[index])
{
Output::Print(_u("Index %4d => "), index);
chunk->data[index]->Dump();
Output::Print(_u("\n"));
}
}
}
}
#endif
};