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.
635 lines
16 KiB
635 lines
16 KiB
#include "nt.h"
|
|
#include "ntdef.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "sxs-rtl.h"
|
|
#include "skiplist.h"
|
|
#include "xmlassert.h"
|
|
|
|
typedef unsigned char *PBYTE;
|
|
|
|
|
|
NTSTATUS
|
|
RtlpFindChunkForElementIndex(
|
|
PRTL_GROWING_LIST pList,
|
|
ULONG ulIndex,
|
|
PRTL_GROWING_LIST_CHUNK *ppListChunk,
|
|
SIZE_T *pulChunkOffset
|
|
)
|
|
/*++
|
|
|
|
|
|
Purpose:
|
|
|
|
Finds the chunk for the given index. This could probably be made faster if
|
|
and when we start using skiplists. As it stands, we just have to walk through
|
|
the list until the index looked for is inside one of the lists.
|
|
|
|
Parameters:
|
|
|
|
pList - Growing list management structure
|
|
|
|
ulIndex - Index requested by the caller
|
|
|
|
ppListChunk - Pointer to a pointer to a list chunk. On return, points to
|
|
the list chunk containing the index.
|
|
|
|
pulChunkOffset - Offset into the chunk (in elements) that was requested
|
|
|
|
Returns:
|
|
|
|
STATUS_SUCCESS - Chunk was found, ppListChunk and pulChunkOffset point to
|
|
the values listed in the 'parameters' section.
|
|
|
|
STATUS_NOT_FOUND - The index was beyond the end of the chunk sections.
|
|
|
|
--*/
|
|
{
|
|
PRTL_GROWING_LIST_CHUNK pHere = NULL;
|
|
|
|
//
|
|
// Is the index in the internal list?
|
|
//
|
|
ASSERT(ulIndex >= pList->cInternalElements);
|
|
ASSERT(pList != NULL);
|
|
ASSERT(ppListChunk != NULL);
|
|
|
|
*ppListChunk = NULL;
|
|
|
|
if (pulChunkOffset) {
|
|
*pulChunkOffset = 0;
|
|
}
|
|
|
|
//
|
|
// Chop off the number of elements in the internal list
|
|
//
|
|
ulIndex -= pList->cInternalElements;
|
|
|
|
|
|
//
|
|
// Move through list chunks until the index is inside one
|
|
// of them. A smarter bear would have made all the chunks the
|
|
// same size and could then have just skipped ahead the right
|
|
// number, avoiding comparisons.
|
|
//
|
|
pHere = pList->pFirstChunk;
|
|
|
|
while ((ulIndex >= pList->cElementsPerChunk) && pHere) {
|
|
pHere = pHere->pNextChunk;
|
|
ulIndex -= pList->cElementsPerChunk;
|
|
}
|
|
|
|
//
|
|
// Set pointer over
|
|
//
|
|
if (ulIndex < pList->cElementsPerChunk) {
|
|
*ppListChunk = pHere;
|
|
}
|
|
|
|
//
|
|
// And if the caller cared what chunk this was in, then tell them.
|
|
//
|
|
if (pulChunkOffset && *ppListChunk) {
|
|
*pulChunkOffset = ulIndex;
|
|
}
|
|
|
|
return pHere ? STATUS_SUCCESS : STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlInitializeGrowingList(
|
|
PRTL_GROWING_LIST pList,
|
|
SIZE_T cbElementSize,
|
|
ULONG cElementsPerChunk,
|
|
PVOID pvInitialListBuffer,
|
|
SIZE_T cbInitialListBuffer,
|
|
PRTL_ALLOCATOR Allocation
|
|
)
|
|
{
|
|
|
|
if ((pList == NULL) ||
|
|
(cElementsPerChunk == 0) ||
|
|
(cbElementSize == 0))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
RtlZeroMemory(pList, sizeof(*pList));
|
|
|
|
pList->cbElementSize = cbElementSize;
|
|
pList->cElementsPerChunk = cElementsPerChunk;
|
|
pList->Allocator = *Allocation;
|
|
|
|
//
|
|
// Set up the initial list pointer
|
|
//
|
|
if (pvInitialListBuffer != NULL) {
|
|
|
|
pList->pvInternalList = pvInitialListBuffer;
|
|
|
|
// Conversion downwards to a ulong, but it's still valid, right?
|
|
pList->cInternalElements = (ULONG)(cbInitialListBuffer / cbElementSize);
|
|
|
|
pList->cTotalElements = pList->cInternalElements;
|
|
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlpExpandGrowingList(
|
|
PRTL_GROWING_LIST pList,
|
|
ULONG ulMinimalIndexCount
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Given a growing list, expand it to be able to contain at least
|
|
ulMinimalIndexCount elements. Does this by allocating chunks via the
|
|
allocator in the list structure and adding them to the growing list
|
|
chunk set.
|
|
|
|
Parameters:
|
|
|
|
pList - Growing list structure to be expanded
|
|
|
|
ulMinimalIndexCount - On return, the pList will have at least enough
|
|
slots to contain this many elements.
|
|
|
|
Return codes:
|
|
|
|
STATUS_SUCCESS - Enough list chunks were allocated to hold the
|
|
requested number of elements.
|
|
|
|
STATUS_NO_MEMORY - Ran out of memory during allocation. Any allocated
|
|
chunks were left allocated and remain owned by the growing list
|
|
until destruction.
|
|
|
|
STATUS_INVALID_PARAMETER - pList was NULL or invalid.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ulNecessaryChunks = 0;
|
|
ULONG ulExtraElements = ulMinimalIndexCount;
|
|
SIZE_T BytesInChunk;
|
|
|
|
if ((pList == NULL) || (pList->Allocator.pfnAlloc == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Already got enough elements in the list? Great. The caller
|
|
// was a bit overactive.
|
|
//
|
|
if (pList->cTotalElements > ulMinimalIndexCount) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Whack off the number of elements already on the list.
|
|
//
|
|
ulExtraElements -= pList->cTotalElements;
|
|
|
|
//
|
|
// How many chunks is that? Remember to round up.
|
|
//
|
|
ulNecessaryChunks = ulExtraElements / pList->cElementsPerChunk;
|
|
ulNecessaryChunks++;
|
|
|
|
//
|
|
// Let's go allocate them, one by one
|
|
//
|
|
BytesInChunk = (pList->cbElementSize * pList->cElementsPerChunk) +
|
|
sizeof(RTL_GROWING_LIST_CHUNK);
|
|
|
|
while (ulNecessaryChunks--) {
|
|
|
|
PRTL_GROWING_LIST_CHUNK pNewChunk = NULL;
|
|
|
|
//
|
|
// Allocate some memory for the chunk
|
|
//
|
|
status = pList->Allocator.pfnAlloc(BytesInChunk, (PVOID*)&pNewChunk, pList->Allocator.pvContext);
|
|
if (!NT_SUCCESS(status)) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Set up the new chunk
|
|
//
|
|
pNewChunk->pGrowingListParent = pList;
|
|
pNewChunk->pNextChunk = NULL;
|
|
|
|
if (pList->pLastChunk) {
|
|
//
|
|
// Swizzle the list of chunks to include this one
|
|
//
|
|
pList->pLastChunk->pNextChunk = pNewChunk;
|
|
}
|
|
|
|
pList->pLastChunk = pNewChunk;
|
|
pList->cTotalElements += pList->cElementsPerChunk;
|
|
|
|
//
|
|
// If there wasn't a first chunk, this one is.
|
|
//
|
|
if (pList->pFirstChunk == NULL) {
|
|
pList->pFirstChunk = pNewChunk;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlIndexIntoGrowingList(
|
|
PRTL_GROWING_LIST pList,
|
|
ULONG ulIndex,
|
|
PVOID *ppvPointerToSpace,
|
|
BOOLEAN fGrowingAllowed
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
if ((pList == NULL) || (ppvPointerToSpace == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
*ppvPointerToSpace = NULL;
|
|
|
|
//
|
|
// If the index is beyond the current total number of elements, but we're
|
|
// not allowing growing, then say it wasn't found. Otherwise, we'll always
|
|
// grow the array as necessary to contain the index passed.
|
|
//
|
|
if ((ulIndex >= pList->cTotalElements) && !fGrowingAllowed) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// This element is in the internal list, so just figure out where
|
|
// and point at it. Do this only if there's an internal element
|
|
// list.
|
|
//
|
|
if ((ulIndex < pList->cInternalElements) && pList->cInternalElements) {
|
|
|
|
//
|
|
// The pointer to the space they want is ulIndex*pList->cbElementSize
|
|
// bytes down the pointer pList->pvInternalList
|
|
//
|
|
*ppvPointerToSpace = ((PBYTE)(pList->pvInternalList)) + (ulIndex * pList->cbElementSize);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// Otherwise, the index is outside the internal list, find out which one
|
|
// it was supposed to be in.
|
|
//
|
|
else {
|
|
|
|
PRTL_GROWING_LIST_CHUNK pThisChunk = NULL;
|
|
SIZE_T ulNewOffset = 0;
|
|
PBYTE pbData = NULL;
|
|
|
|
status = RtlpFindChunkForElementIndex(pList, ulIndex, &pThisChunk, &ulNewOffset);
|
|
|
|
//
|
|
// Success! Go move the chunk pointer past the header of the growing list
|
|
// chunk, and then index off it to find the right place.
|
|
//
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
pbData = ((PBYTE)(pThisChunk + 1)) + (pList->cbElementSize * ulNewOffset);
|
|
|
|
}
|
|
//
|
|
// Otherwise, the chunk wasn't found, so we have to go allocate some new
|
|
// chunks to hold it, then try again.
|
|
//
|
|
else if (status == STATUS_NOT_FOUND) {
|
|
|
|
//
|
|
// Expand the list
|
|
//
|
|
if (!NT_SUCCESS(status = RtlpExpandGrowingList(pList, ulIndex))) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Look again
|
|
//
|
|
status = RtlpFindChunkForElementIndex(pList, ulIndex, &pThisChunk, &ulNewOffset);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Adjust pointers
|
|
//
|
|
pbData = ((PBYTE)(pThisChunk + 1)) + (pList->cbElementSize * ulNewOffset);
|
|
|
|
|
|
}
|
|
else {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// One of the above should have set the pbData pointer to point at the requested
|
|
// grown-list space.
|
|
//
|
|
*ppvPointerToSpace = pbData;
|
|
|
|
|
|
}
|
|
|
|
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlDestroyGrowingList(
|
|
PRTL_GROWING_LIST pList
|
|
)
|
|
/*++
|
|
|
|
Purpose:
|
|
|
|
Destroys (deallocates) all the chunks that had been allocated to this
|
|
growing list structure. Returns the list to the "fresh" state of having
|
|
only the 'internal' element count.
|
|
|
|
Parameters:
|
|
|
|
pList - List structure to be destroyed
|
|
|
|
Returns:
|
|
|
|
STATUS_SUCCESS - Structure was completely cleaned out
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
if ((pList == NULL) || (pList->Allocator.pfnFree == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Zing through and kill all the list bits
|
|
//
|
|
while (pList->pFirstChunk != NULL) {
|
|
|
|
PRTL_GROWING_LIST_CHUNK pHere;
|
|
|
|
pHere = pList->pFirstChunk;
|
|
pList->pFirstChunk = pList->pFirstChunk->pNextChunk;
|
|
|
|
if (!NT_SUCCESS(status = pList->Allocator.pfnFree(pHere, pList->Allocator.pvContext))) {
|
|
return status;
|
|
}
|
|
|
|
pList->cTotalElements -= pList->cElementsPerChunk;
|
|
|
|
}
|
|
|
|
ASSERT(pList->pFirstChunk == NULL);
|
|
|
|
//
|
|
// Reset the things that change as we expand the list
|
|
//
|
|
pList->pLastChunk = pList->pFirstChunk = NULL;
|
|
pList->cTotalElements = pList->cInternalElements;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RtlCloneGrowingList(
|
|
ULONG ulFlags,
|
|
PRTL_GROWING_LIST pDestination,
|
|
PRTL_GROWING_LIST pSource,
|
|
ULONG ulSourceCount
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ul;
|
|
PVOID pvSourceCursor, pvDestCursor;
|
|
SIZE_T cbBytes;
|
|
|
|
//
|
|
// No flags, no null values, element byte size has to match,
|
|
// and the source/dest can't be the same.
|
|
//
|
|
if (((ulFlags != 0) || !pDestination || !pSource) ||
|
|
(pDestination->cbElementSize != pSource->cbElementSize) ||
|
|
(pDestination == pSource))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
cbBytes = pDestination->cbElementSize;
|
|
|
|
//
|
|
// Now copy bytes around
|
|
//
|
|
for (ul = 0; ul < ulSourceCount; ul++) {
|
|
status = RtlIndexIntoGrowingList(pSource, ul, &pvSourceCursor, FALSE);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
status = RtlIndexIntoGrowingList(pDestination, ul, &pvDestCursor, TRUE);
|
|
if (!NT_SUCCESS(status))
|
|
goto Exit;
|
|
|
|
RtlCopyMemory(pvDestCursor, pvSourceCursor, cbBytes);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlAllocateGrowingList(
|
|
PRTL_GROWING_LIST *ppGrowingList,
|
|
SIZE_T cbThingSize,
|
|
PRTL_ALLOCATOR Allocation
|
|
)
|
|
{
|
|
PRTL_GROWING_LIST pvWorkingList = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
if (ppGrowingList != NULL)
|
|
*ppGrowingList = NULL;
|
|
else
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
if (!Allocation)
|
|
return STATUS_INVALID_PARAMETER_3;
|
|
|
|
//
|
|
// Allocate space
|
|
//
|
|
status = Allocation->pfnAlloc(sizeof(RTL_GROWING_LIST), &pvWorkingList, Allocation->pvContext);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Set up the structure
|
|
//
|
|
status = RtlInitializeGrowingList(
|
|
pvWorkingList,
|
|
cbThingSize,
|
|
8,
|
|
NULL,
|
|
0,
|
|
Allocation);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
*ppGrowingList = pvWorkingList;
|
|
pvWorkingList = NULL;
|
|
status = STATUS_SUCCESS;
|
|
Exit:
|
|
if (pvWorkingList) {
|
|
Allocation->pfnFree(pvWorkingList, Allocation->pvContext);
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RtlSearchGrowingList(
|
|
PRTL_GROWING_LIST TheList,
|
|
ULONG ItemCount,
|
|
PFN_LIST_COMPARISON_CALLBACK SearchCallback,
|
|
PVOID SearchTarget,
|
|
PVOID SearchContext,
|
|
PVOID *pvFoundItem
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG ul;
|
|
int CompareResult = 0;
|
|
|
|
if (pvFoundItem)
|
|
*pvFoundItem = NULL;
|
|
|
|
// if (TheList->ulFlags & GROWING_LIST_FLAG_IS_SORTED) {
|
|
if (0) {
|
|
}
|
|
else {
|
|
|
|
ULONG uTemp = ItemCount;
|
|
ULONG uOffset = 0;
|
|
PRTL_GROWING_LIST_CHUNK Chunklet;
|
|
|
|
ul = 0;
|
|
|
|
//
|
|
// Scan the internal item list.
|
|
//
|
|
while ((ul < ItemCount) && (ul < TheList->cInternalElements)) {
|
|
|
|
PVOID pvHere = (PVOID)(((ULONG_PTR)TheList->pvInternalList) + uOffset);
|
|
|
|
status = SearchCallback(TheList, SearchTarget, pvHere, SearchContext, &CompareResult);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (CompareResult == 0) {
|
|
if (pvFoundItem)
|
|
*pvFoundItem = pvHere;
|
|
status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
uOffset += TheList->cbElementSize;
|
|
ul++;
|
|
}
|
|
|
|
//
|
|
// Ok, we ran out of internal elements, do the same thing here but on the chunk list
|
|
//
|
|
Chunklet = TheList->pFirstChunk;
|
|
while ((ul < ItemCount) && Chunklet) {
|
|
|
|
PVOID Data = (PVOID)(Chunklet + 1);
|
|
ULONG ulHighOffset = TheList->cElementsPerChunk * TheList->cbElementSize;
|
|
|
|
uOffset = 0;
|
|
|
|
//
|
|
// Spin through the items in this chunklet
|
|
//
|
|
while (uOffset < ulHighOffset) {
|
|
|
|
PVOID pvHere = (PVOID)(((ULONG_PTR)Data) + uOffset);
|
|
|
|
status = SearchCallback(TheList, SearchTarget, pvHere, SearchContext, &CompareResult);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (CompareResult == 0) {
|
|
if (pvFoundItem)
|
|
*pvFoundItem = pvHere;
|
|
status = STATUS_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
uOffset += TheList->cbElementSize;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we got here, we didn't find it in either the internal list or the external one.
|
|
//
|
|
status = STATUS_NOT_FOUND;
|
|
if (pvFoundItem)
|
|
*pvFoundItem = NULL;
|
|
|
|
}
|
|
|
|
Exit:
|
|
return status;
|
|
}
|
|
|
|
|