Leaked source code of windows server 2003
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

#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;
}