1220 lines
30 KiB
C
1220 lines
30 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Gentable.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the generic table package.
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 23-May-1989
|
||
|
||
Environment:
|
||
|
||
Pure Utility Routines
|
||
|
||
Revision History:
|
||
|
||
Anthony V. Ercolano [tonye] 23-May-1990
|
||
|
||
Implement package.
|
||
|
||
Anthony V. Ercolano [tonye] 1-Jun-1990
|
||
|
||
Added ability to get elements out in the order
|
||
inserted. *NOTE* *NOTE* This depends on the implicit
|
||
ordering of record fields:
|
||
|
||
SPLAY_LINKS,
|
||
LIST_ENTRY,
|
||
USER_DATA
|
||
|
||
--*/
|
||
|
||
#include <nt.h>
|
||
|
||
#include <ntrtl.h>
|
||
|
||
#pragma pack(8)
|
||
|
||
//
|
||
// This structure is the header for a generic table entry.
|
||
// Align this structure on a 8 byte boundary so the user
|
||
// data is correctly aligned.
|
||
//
|
||
|
||
typedef struct _TABLE_ENTRY_HEADER {
|
||
|
||
RTL_SPLAY_LINKS SplayLinks;
|
||
LIST_ENTRY ListEntry;
|
||
LONGLONG UserData;
|
||
|
||
} TABLE_ENTRY_HEADER, *PTABLE_ENTRY_HEADER;
|
||
|
||
#pragma pack()
|
||
|
||
|
||
static
|
||
TABLE_SEARCH_RESULT
|
||
FindNodeOrParent(
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PVOID Buffer,
|
||
OUT PRTL_SPLAY_LINKS *NodeOrParent
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used by all of the routines of the generic
|
||
table package to locate the a node in the tree. It will
|
||
find and return (via the NodeOrParent parameter) the node
|
||
with the given key, or if that node is not in the tree it
|
||
will return (via the NodeOrParent parameter) a pointer to
|
||
the parent.
|
||
|
||
Arguments:
|
||
|
||
Table - The generic table to search for the key.
|
||
|
||
Buffer - Pointer to a buffer holding the key. The table
|
||
package doesn't examine the key itself. It leaves
|
||
this up to the user supplied compare routine.
|
||
|
||
NodeOrParent - Will be set to point to the node containing the
|
||
the key or what should be the parent of the node
|
||
if it were in the tree. Note that this will *NOT*
|
||
be set if the search result is TableEmptyTree.
|
||
|
||
Return Value:
|
||
|
||
TABLE_SEARCH_RESULT - TableEmptyTree: The tree was empty. NodeOrParent
|
||
is *not* altered.
|
||
|
||
TableFoundNode: A node with the key is in the tree.
|
||
NodeOrParent points to that node.
|
||
|
||
TableInsertAsLeft: Node with key was not found.
|
||
NodeOrParent points to what would be
|
||
parent. The node would be the left
|
||
child.
|
||
|
||
TableInsertAsRight: Node with key was not found.
|
||
NodeOrParent points to what would be
|
||
parent. The node would be the right
|
||
child.
|
||
|
||
--*/
|
||
|
||
|
||
|
||
{
|
||
|
||
if (RtlIsGenericTableEmpty(Table)) {
|
||
|
||
return TableEmptyTree;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Used as the iteration variable while stepping through
|
||
// the generic table.
|
||
//
|
||
PRTL_SPLAY_LINKS NodeToExamine = Table->TableRoot;
|
||
|
||
//
|
||
// Just a temporary. Hopefully a good compiler will get
|
||
// rid of it.
|
||
//
|
||
PRTL_SPLAY_LINKS Child;
|
||
|
||
//
|
||
// Holds the value of the comparasion.
|
||
//
|
||
RTL_GENERIC_COMPARE_RESULTS Result;
|
||
|
||
while (TRUE) {
|
||
|
||
//
|
||
// Compare the buffer with the key in the tree element.
|
||
//
|
||
|
||
Result = Table->CompareRoutine(
|
||
Table,
|
||
Buffer,
|
||
&((PTABLE_ENTRY_HEADER) NodeToExamine)->UserData
|
||
);
|
||
|
||
if (Result == GenericLessThan) {
|
||
|
||
if (Child = RtlLeftChild(NodeToExamine)) {
|
||
|
||
NodeToExamine = Child;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Node is not in the tree. Set the output
|
||
// parameter to point to what would be its
|
||
// parent and return which child it would be.
|
||
//
|
||
|
||
*NodeOrParent = NodeToExamine;
|
||
return TableInsertAsLeft;
|
||
|
||
}
|
||
|
||
} else if (Result == GenericGreaterThan) {
|
||
|
||
if (Child = RtlRightChild(NodeToExamine)) {
|
||
|
||
NodeToExamine = Child;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Node is not in the tree. Set the output
|
||
// parameter to point to what would be its
|
||
// parent and return which child it would be.
|
||
//
|
||
|
||
*NodeOrParent = NodeToExamine;
|
||
return TableInsertAsRight;
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Node is in the tree (or it better be because of the
|
||
// assert). Set the output parameter to point to
|
||
// the node and tell the caller that we found the node.
|
||
//
|
||
|
||
ASSERT(Result == GenericEqual);
|
||
*NodeOrParent = NodeToExamine;
|
||
return TableFoundNode;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
RtlInitializeGenericTable (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine,
|
||
IN PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine,
|
||
IN PRTL_GENERIC_FREE_ROUTINE FreeRoutine,
|
||
IN PVOID TableContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The procedure InitializeGenericTable takes as input an uninitialized
|
||
generic table variable and pointers to the three user supplied routines.
|
||
This must be called for every individual generic table variable before
|
||
it can be used.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the generic table to be initialized.
|
||
|
||
CompareRoutine - User routine to be used to compare to keys in the
|
||
table.
|
||
|
||
AllocateRoutine - User routine to call to allocate memory for a new
|
||
node in the generic table.
|
||
|
||
FreeRoutine - User routine to call to deallocate memory for
|
||
a node in the generic table.
|
||
|
||
TableContext - Supplies user supplied context for the table.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Initialize each field of the Table parameter.
|
||
//
|
||
|
||
Table->TableRoot = NULL;
|
||
InitializeListHead(&Table->InsertOrderList);
|
||
Table->NumberGenericTableElements = 0;
|
||
Table->OrderedPointer = &Table->InsertOrderList;
|
||
Table->WhichOrderedElement = 0;
|
||
Table->CompareRoutine = CompareRoutine;
|
||
Table->AllocateRoutine = AllocateRoutine;
|
||
Table->FreeRoutine = FreeRoutine;
|
||
Table->TableContext = TableContext;
|
||
|
||
}
|
||
|
||
|
||
PVOID
|
||
RtlInsertElementGenericTable (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PVOID Buffer,
|
||
IN CLONG BufferSize,
|
||
OUT PBOOLEAN NewElement OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function InsertElementGenericTable will insert a new element
|
||
in a table. It does this by allocating space for the new element
|
||
(this includes splay links), inserting the element in the table, and
|
||
then returning to the user a pointer to the new element (which is
|
||
the first available space after the splay links). If an element
|
||
with the same key already exists in the table the return value is a pointer
|
||
to the old element. The optional output parameter NewElement is used
|
||
to indicate if the element previously existed in the table. Note: the user
|
||
supplied Buffer is only used for searching the table, upon insertion its
|
||
contents are copied to the newly created element. This means that
|
||
pointer to the input buffer will not point to the new element.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the table in which to (possibly) insert the
|
||
key buffer.
|
||
|
||
Buffer - Passed to the user comparasion routine. Its contents are
|
||
up to the user but one could imagine that it contains some
|
||
sort of key value.
|
||
|
||
BufferSize - The amount of space to allocate when the (possible)
|
||
insertion is made. Note that if we actually do
|
||
not find the node and we do allocate space then we
|
||
will add the size of the SPLAY_LINKS to this buffer
|
||
size. The user should really take care not to depend
|
||
on anything in the first sizeof(SPLAY_LINKS) bytes
|
||
of the memory allocated via the memory allocation
|
||
routine.
|
||
|
||
NewElement - Optional Flag. If present then it will be set to
|
||
TRUE if the buffer was not "found" in the generic
|
||
table.
|
||
|
||
Return Value:
|
||
|
||
PVOID - Pointer to the user defined data.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Holds a pointer to the node in the table or what would be the
|
||
// parent of the node.
|
||
//
|
||
PRTL_SPLAY_LINKS NodeOrParent;
|
||
|
||
//
|
||
// Holds the result of the table lookup.
|
||
//
|
||
TABLE_SEARCH_RESULT Lookup;
|
||
|
||
Lookup = FindNodeOrParent(
|
||
Table,
|
||
Buffer,
|
||
&NodeOrParent
|
||
);
|
||
|
||
//
|
||
// Call the full routine to do the real work.
|
||
//
|
||
|
||
return RtlInsertElementGenericTableFull(
|
||
Table,
|
||
Buffer,
|
||
BufferSize,
|
||
NewElement,
|
||
NodeOrParent,
|
||
Lookup
|
||
);
|
||
}
|
||
|
||
|
||
PVOID
|
||
RtlInsertElementGenericTableFull (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PVOID Buffer,
|
||
IN CLONG BufferSize,
|
||
OUT PBOOLEAN NewElement OPTIONAL,
|
||
PVOID NodeOrParent,
|
||
TABLE_SEARCH_RESULT SearchResult
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function InsertElementGenericTableFull will insert a new element
|
||
in a table. It does this by allocating space for the new element
|
||
(this includes splay links), inserting the element in the table, and
|
||
then returning to the user a pointer to the new element. If an element
|
||
with the same key already exists in the table the return value is a pointer
|
||
to the old element. The optional output parameter NewElement is used
|
||
to indicate if the element previously existed in the table. Note: the user
|
||
supplied Buffer is only used for searching the table, upon insertion its
|
||
contents are copied to the newly created element. This means that
|
||
pointer to the input buffer will not point to the new element.
|
||
This routine is passed the NodeOrParent and SearchResult from a
|
||
previous RtlLookupElementGenericTableFull.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the table in which to (possibly) insert the
|
||
key buffer.
|
||
|
||
Buffer - Passed to the user comparasion routine. Its contents are
|
||
up to the user but one could imagine that it contains some
|
||
sort of key value.
|
||
|
||
BufferSize - The amount of space to allocate when the (possible)
|
||
insertion is made. Note that if we actually do
|
||
not find the node and we do allocate space then we
|
||
will add the size of the SPLAY_LINKS to this buffer
|
||
size. The user should really take care not to depend
|
||
on anything in the first sizeof(SPLAY_LINKS) bytes
|
||
of the memory allocated via the memory allocation
|
||
routine.
|
||
|
||
NewElement - Optional Flag. If present then it will be set to
|
||
TRUE if the buffer was not "found" in the generic
|
||
table.
|
||
|
||
NodeOrParent - Result of prior RtlLookupElementGenericTableFull.
|
||
|
||
SearchResult - Result of prior RtlLookupElementGenericTableFull.
|
||
|
||
Return Value:
|
||
|
||
PVOID - Pointer to the user defined data.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Node will point to the splay links of what
|
||
// will be returned to the user.
|
||
//
|
||
|
||
PRTL_SPLAY_LINKS NodeToReturn;
|
||
|
||
if (SearchResult != TableFoundNode) {
|
||
|
||
//
|
||
// We just check that the table isn't getting
|
||
// too big.
|
||
//
|
||
|
||
ASSERT(Table->NumberGenericTableElements != (MAXULONG-1));
|
||
|
||
//
|
||
// The node wasn't in the (possibly empty) tree.
|
||
// Call the user allocation routine to get space
|
||
// for the new node.
|
||
//
|
||
|
||
NodeToReturn = Table->AllocateRoutine(
|
||
Table,
|
||
BufferSize+FIELD_OFFSET( TABLE_ENTRY_HEADER, UserData )
|
||
);
|
||
|
||
//
|
||
// If the return is NULL, return NULL from here to indicate that
|
||
// the entry could not be added.
|
||
//
|
||
|
||
if (NodeToReturn == NULL) {
|
||
|
||
if (ARGUMENT_PRESENT(NewElement)) {
|
||
|
||
*NewElement = FALSE;
|
||
}
|
||
|
||
return(NULL);
|
||
}
|
||
|
||
RtlInitializeSplayLinks(NodeToReturn);
|
||
|
||
//
|
||
// Insert the new node at the end of the ordered linked list.
|
||
//
|
||
|
||
InsertTailList(
|
||
&Table->InsertOrderList,
|
||
&((PTABLE_ENTRY_HEADER) NodeToReturn)->ListEntry
|
||
);
|
||
|
||
Table->NumberGenericTableElements++;
|
||
|
||
//
|
||
// Insert the new node in the tree.
|
||
//
|
||
|
||
if (SearchResult == TableEmptyTree) {
|
||
|
||
Table->TableRoot = NodeToReturn;
|
||
|
||
} else {
|
||
|
||
if (SearchResult == TableInsertAsLeft) {
|
||
|
||
RtlInsertAsLeftChild(
|
||
NodeOrParent,
|
||
NodeToReturn
|
||
);
|
||
|
||
} else {
|
||
|
||
RtlInsertAsRightChild(
|
||
NodeOrParent,
|
||
NodeToReturn
|
||
);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Copy the users buffer into the user data area of the table.
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
&((PTABLE_ENTRY_HEADER) NodeToReturn)->UserData,
|
||
Buffer,
|
||
BufferSize
|
||
);
|
||
|
||
} else {
|
||
|
||
NodeToReturn = NodeOrParent;
|
||
}
|
||
|
||
//
|
||
// Always splay the (possibly) new node.
|
||
//
|
||
|
||
Table->TableRoot = RtlSplay(NodeToReturn);
|
||
|
||
if (ARGUMENT_PRESENT(NewElement)) {
|
||
|
||
*NewElement = ((SearchResult == TableFoundNode)?(FALSE):(TRUE));
|
||
}
|
||
|
||
//
|
||
// Insert the element on the ordered list;
|
||
//
|
||
|
||
return &((PTABLE_ENTRY_HEADER) NodeToReturn)->UserData;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RtlDeleteElementGenericTable (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function DeleteElementGenericTable will find and delete an element
|
||
from a generic table. If the element is located and deleted the return
|
||
value is TRUE, otherwise if the element is not located the return value
|
||
is FALSE. The user supplied input buffer is only used as a key in
|
||
locating the element in the table.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the table in which to (possibly) delete the
|
||
memory accessed by the key buffer.
|
||
|
||
Buffer - Passed to the user comparasion routine. Its contents are
|
||
up to the user but one could imagine that it contains some
|
||
sort of key value.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - If the table contained the key then true, otherwise false.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Holds a pointer to the node in the table or what would be the
|
||
// parent of the node.
|
||
//
|
||
PRTL_SPLAY_LINKS NodeOrParent;
|
||
|
||
//
|
||
// Holds the result of the table lookup.
|
||
//
|
||
TABLE_SEARCH_RESULT Lookup;
|
||
|
||
Lookup = FindNodeOrParent(
|
||
Table,
|
||
Buffer,
|
||
&NodeOrParent
|
||
);
|
||
|
||
if ((Lookup == TableEmptyTree) || (Lookup != TableFoundNode)) {
|
||
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Delete the node from the splay tree.
|
||
//
|
||
|
||
Table->TableRoot = RtlDelete(NodeOrParent);
|
||
|
||
//
|
||
// Delete the element from the linked list.
|
||
//
|
||
|
||
RemoveEntryList(&((PTABLE_ENTRY_HEADER) NodeOrParent)->ListEntry);
|
||
Table->NumberGenericTableElements--;
|
||
Table->WhichOrderedElement = 0;
|
||
Table->OrderedPointer = &Table->InsertOrderList;
|
||
|
||
//
|
||
// The node has been deleted from the splay table.
|
||
// Now give the node to the user deletion routine.
|
||
// NOTE: We are giving the deletion routine a pointer
|
||
// to the splay links rather then the user data. It
|
||
// is assumed that the deallocation is rather bad.
|
||
//
|
||
|
||
Table->FreeRoutine(Table,NodeOrParent);
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
PVOID
|
||
RtlLookupElementGenericTable (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function LookupElementGenericTable will find an element in a generic
|
||
table. If the element is located the return value is a pointer to
|
||
the user defined structure associated with the element, otherwise if
|
||
the element is not located the return value is NULL. The user supplied
|
||
input buffer is only used as a key in locating the element in the table.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the users Generic table to search for the key.
|
||
|
||
Buffer - Used for the comparasion.
|
||
|
||
Return Value:
|
||
|
||
PVOID - returns a pointer to the user data.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Holds a pointer to the node in the table or what would be the
|
||
// parent of the node.
|
||
//
|
||
PRTL_SPLAY_LINKS NodeOrParent;
|
||
|
||
//
|
||
// Holds the result of the table lookup.
|
||
//
|
||
TABLE_SEARCH_RESULT Lookup;
|
||
|
||
return RtlLookupElementGenericTableFull(
|
||
Table,
|
||
Buffer,
|
||
&NodeOrParent,
|
||
&Lookup
|
||
);
|
||
}
|
||
|
||
|
||
PVOID
|
||
NTAPI
|
||
RtlLookupElementGenericTableFull (
|
||
PRTL_GENERIC_TABLE Table,
|
||
PVOID Buffer,
|
||
OUT PVOID *NodeOrParent,
|
||
OUT TABLE_SEARCH_RESULT *SearchResult
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function LookupElementGenericTableFull will find an element in a generic
|
||
table. If the element is located the return value is a pointer to
|
||
the user defined structure associated with the element. If the element is not
|
||
located then a pointer to the parent for the insert location is returned. The
|
||
user must look at the SearchResult value to determine which is being returned.
|
||
The user can use the SearchResult and parent for a subsequent FullInsertElement
|
||
call to optimize the insert.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the users Generic table to search for the key.
|
||
|
||
Buffer - Used for the comparasion.
|
||
|
||
NodeOrParent - Address to store the desired Node or parent of the desired node.
|
||
|
||
SearchResult - Describes the relationship of the NodeOrParent with the desired Node.
|
||
|
||
Return Value:
|
||
|
||
PVOID - returns a pointer to the user data.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Lookup the element and save the result.
|
||
//
|
||
|
||
*SearchResult = FindNodeOrParent(
|
||
Table,
|
||
Buffer,
|
||
(PRTL_SPLAY_LINKS *)NodeOrParent
|
||
);
|
||
|
||
if ((*SearchResult == TableEmptyTree) || (*SearchResult != TableFoundNode)) {
|
||
|
||
return NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Splay the tree with this node.
|
||
//
|
||
|
||
Table->TableRoot = RtlSplay(*NodeOrParent);
|
||
|
||
//
|
||
// Return a pointer to the user data.
|
||
//
|
||
|
||
return &((PTABLE_ENTRY_HEADER)*NodeOrParent)->UserData;
|
||
}
|
||
}
|
||
|
||
|
||
PVOID
|
||
RtlEnumerateGenericTable (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN BOOLEAN Restart
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function EnumerateGenericTable will return to the caller one-by-one
|
||
the elements of of a table. The return value is a pointer to the user
|
||
defined structure associated with the element. The input parameter
|
||
Restart indicates if the enumeration should start from the beginning
|
||
or should return the next element. If the are no more new elements to
|
||
return the return value is NULL. As an example of its use, to enumerate
|
||
all of the elements in a table the user would write:
|
||
|
||
for (ptr = EnumerateGenericTable(Table,TRUE);
|
||
ptr != NULL;
|
||
ptr = EnumerateGenericTable(Table, FALSE)) {
|
||
:
|
||
}
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the generic table to enumerate.
|
||
|
||
Restart - Flag that if true we should start with the least
|
||
element in the tree otherwise, return we return
|
||
a pointer to the user data for the root and make
|
||
the real successor to the root the new root.
|
||
|
||
Return Value:
|
||
|
||
PVOID - Pointer to the user data.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
if (RtlIsGenericTableEmpty(Table)) {
|
||
|
||
//
|
||
// Nothing to do if the table is empty.
|
||
//
|
||
|
||
return NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Will be used as the "iteration" through the tree.
|
||
//
|
||
PRTL_SPLAY_LINKS NodeToReturn;
|
||
|
||
//
|
||
// If the restart flag is true then go to the least element
|
||
// in the tree.
|
||
//
|
||
|
||
if (Restart) {
|
||
|
||
//
|
||
// We just loop until we find the leftmost child of the root.
|
||
//
|
||
|
||
for (
|
||
NodeToReturn = Table->TableRoot;
|
||
RtlLeftChild(NodeToReturn);
|
||
NodeToReturn = RtlLeftChild(NodeToReturn)
|
||
) {
|
||
;
|
||
}
|
||
|
||
Table->TableRoot = RtlSplay(NodeToReturn);
|
||
|
||
} else {
|
||
|
||
//
|
||
// The assumption here is that the root of the
|
||
// tree is the last node that we returned. We
|
||
// find the real successor to the root and return
|
||
// it as next element of the enumeration. The
|
||
// node that is to be returned is splayed (thereby
|
||
// making it the root of the tree). Note that we
|
||
// need to take care when there are no more elements.
|
||
//
|
||
|
||
NodeToReturn = RtlRealSuccessor(Table->TableRoot);
|
||
|
||
if (NodeToReturn) {
|
||
|
||
Table->TableRoot = RtlSplay(NodeToReturn);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If there actually is a next element in the enumeration
|
||
// then the pointer to return is right after the list links.
|
||
//
|
||
|
||
return ((NodeToReturn)?
|
||
((PVOID)&((PTABLE_ENTRY_HEADER)NodeToReturn)->UserData)
|
||
:((PVOID)(NULL)));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RtlIsGenericTableEmpty (
|
||
IN PRTL_GENERIC_TABLE Table
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function IsGenericTableEmpty will return to the caller TRUE if
|
||
the input table is empty (i.e., does not contain any elements) and
|
||
FALSE otherwise.
|
||
|
||
Arguments:
|
||
|
||
Table - Supplies a pointer to the Generic Table.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - if enabled the tree is empty.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Table is empty if the root pointer is null.
|
||
//
|
||
|
||
return ((Table->TableRoot)?(FALSE):(TRUE));
|
||
|
||
}
|
||
|
||
PVOID
|
||
RtlGetElementGenericTable (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN ULONG I
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
The function GetElementGenericTable will return the i'th element
|
||
inserted in the generic table. I = 0 implies the first element,
|
||
I = (RtlNumberGenericTableElements(Table)-1) will return the last element
|
||
inserted into the generic table. The type of I is ULONG. Values
|
||
of I > than (NumberGenericTableElements(Table)-1) will return NULL. If
|
||
an arbitrary element is deleted from the generic table it will cause
|
||
all elements inserted after the deleted element to "move up".
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the generic table from which to get the ith element.
|
||
|
||
I - Which element to get.
|
||
|
||
|
||
Return Value:
|
||
|
||
PVOID - Pointer to the user data.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
//
|
||
// Current location in the table.
|
||
//
|
||
ULONG CurrentLocation = Table->WhichOrderedElement;
|
||
|
||
//
|
||
// Hold the number of elements in the table.
|
||
//
|
||
ULONG NumberInTable = Table->NumberGenericTableElements;
|
||
|
||
//
|
||
// Holds the value of I+1.
|
||
//
|
||
// Note that we don't care if this value overflows.
|
||
// If we end up accessing it we know that it didn't.
|
||
//
|
||
ULONG NormalizedI = I + 1;
|
||
|
||
//
|
||
// Will hold distances to travel to the desired node;
|
||
//
|
||
ULONG ForwardDistance,BackwardDistance;
|
||
|
||
//
|
||
// Will point to the current element in the linked list.
|
||
//
|
||
PLIST_ENTRY CurrentNode = Table->OrderedPointer;
|
||
|
||
|
||
//
|
||
// If it's out of bounds get out quick.
|
||
//
|
||
|
||
if ((I == MAXULONG) || (NormalizedI > NumberInTable)) return NULL;
|
||
|
||
//
|
||
// If we're already at the node then return it.
|
||
//
|
||
|
||
if (NormalizedI == CurrentLocation) {
|
||
|
||
return &((PTABLE_ENTRY_HEADER) CONTAINING_RECORD(CurrentNode, TABLE_ENTRY_HEADER, ListEntry))->UserData;
|
||
}
|
||
|
||
//
|
||
// Calculate the forward and backward distance to the node.
|
||
//
|
||
|
||
if (CurrentLocation > NormalizedI) {
|
||
|
||
//
|
||
// When CurrentLocation is greater than where we want to go,
|
||
// if moving forward gets us there quicker than moving backward
|
||
// then it follows that moving forward from the listhead is
|
||
// going to take fewer steps. (This is because, moving forward
|
||
// in this case must move *through* the listhead.)
|
||
//
|
||
// The work here is to figure out if moving backward would be quicker.
|
||
//
|
||
// Moving backward would be quicker only if the location we wish to
|
||
// go to is more than half way between the listhead and where we
|
||
// currently are.
|
||
//
|
||
|
||
if (NormalizedI > (CurrentLocation/2)) {
|
||
|
||
//
|
||
// Where we want to go is more than half way from the listhead
|
||
// We can traval backwards from our current location.
|
||
//
|
||
|
||
for (
|
||
BackwardDistance = CurrentLocation - NormalizedI;
|
||
BackwardDistance;
|
||
BackwardDistance--
|
||
) {
|
||
|
||
CurrentNode = CurrentNode->Blink;
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Where we want to go is less than halfway between the start
|
||
// and where we currently are. Start from the listhead.
|
||
//
|
||
|
||
for (
|
||
CurrentNode = &Table->InsertOrderList;
|
||
NormalizedI;
|
||
NormalizedI--
|
||
) {
|
||
|
||
CurrentNode = CurrentNode->Flink;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
|
||
//
|
||
// When CurrentLocation is less than where we want to go,
|
||
// if moving backwards gets us there quicker than moving forwards
|
||
// then it follows that moving backwards from the listhead is
|
||
// going to take fewer steps. (This is because, moving backwards
|
||
// in this case must move *through* the listhead.)
|
||
//
|
||
|
||
ForwardDistance = NormalizedI - CurrentLocation;
|
||
|
||
//
|
||
// Do the backwards calculation as if we are starting from the
|
||
// listhead.
|
||
//
|
||
|
||
BackwardDistance = (NumberInTable - NormalizedI) + 1;
|
||
|
||
if (ForwardDistance <= BackwardDistance) {
|
||
|
||
for (
|
||
;
|
||
ForwardDistance;
|
||
ForwardDistance--
|
||
) {
|
||
|
||
CurrentNode = CurrentNode->Flink;
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
for (
|
||
CurrentNode = &Table->InsertOrderList;
|
||
BackwardDistance;
|
||
BackwardDistance--
|
||
) {
|
||
|
||
CurrentNode = CurrentNode->Blink;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// We're where we want to be. Save our current location and return
|
||
// a pointer to the data to the user.
|
||
//
|
||
|
||
Table->OrderedPointer = CurrentNode;
|
||
Table->WhichOrderedElement = I+1;
|
||
|
||
return &((PTABLE_ENTRY_HEADER) CONTAINING_RECORD(CurrentNode, TABLE_ENTRY_HEADER, ListEntry))->UserData;
|
||
|
||
}
|
||
|
||
|
||
ULONG
|
||
RtlNumberGenericTableElements(
|
||
IN PRTL_GENERIC_TABLE Table
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function NumberGenericTableElements returns a ULONG value
|
||
which is the number of generic table elements currently inserted
|
||
in the generic table.
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the generic table from which to find out the number
|
||
of elements.
|
||
|
||
|
||
Return Value:
|
||
|
||
ULONG - The number of elements in the generic table.
|
||
|
||
--*/
|
||
{
|
||
|
||
return Table->NumberGenericTableElements;
|
||
|
||
}
|
||
|
||
|
||
PVOID
|
||
RtlEnumerateGenericTableWithoutSplaying (
|
||
IN PRTL_GENERIC_TABLE Table,
|
||
IN PVOID *RestartKey
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The function EnumerateGenericTableWithoutSplaying will return to the
|
||
caller one-by-one the elements of of a table. The return value is a
|
||
pointer to the user defined structure associated with the element.
|
||
The input parameter RestartKey indicates if the enumeration should
|
||
start from the beginning or should return the next element. If the
|
||
are no more new elements to return the return value is NULL. As an
|
||
example of its use, to enumerate all of the elements in a table the
|
||
user would write:
|
||
|
||
PVOID RestartKey = NULL;
|
||
|
||
for (ptr = RtlEnumerateGenericTableWithoutSplaying(Table, &RestartKey);
|
||
ptr != NULL;
|
||
ptr = RtlEnumerateGenericTableWithoutSplaying(Table, &RestartKey)) {
|
||
:
|
||
}
|
||
|
||
Arguments:
|
||
|
||
Table - Pointer to the generic table to enumerate.
|
||
|
||
RestartKey - Pointer that indicates if we should restart or return the next
|
||
element. If the contents of RestartKey is NULL, the search
|
||
will be started from the beginning.
|
||
|
||
Return Value:
|
||
|
||
PVOID - Pointer to the user data.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
if (RtlIsGenericTableEmpty(Table)) {
|
||
|
||
//
|
||
// Nothing to do if the table is empty.
|
||
//
|
||
|
||
return NULL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Will be used as the "iteration" through the tree.
|
||
//
|
||
PRTL_SPLAY_LINKS NodeToReturn;
|
||
|
||
//
|
||
// If the restart flag is true then go to the least element
|
||
// in the tree.
|
||
//
|
||
|
||
if (*RestartKey == NULL) {
|
||
|
||
//
|
||
// We just loop until we find the leftmost child of the root.
|
||
//
|
||
|
||
for (
|
||
NodeToReturn = Table->TableRoot;
|
||
RtlLeftChild(NodeToReturn);
|
||
NodeToReturn = RtlLeftChild(NodeToReturn)
|
||
) {
|
||
;
|
||
}
|
||
|
||
*RestartKey = NodeToReturn;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The caller has passed in the previous entry found
|
||
// in the table to enable us to continue the search. We call
|
||
// RtlRealSuccessor to step to the next element in the tree.
|
||
//
|
||
|
||
NodeToReturn = RtlRealSuccessor(*RestartKey);
|
||
|
||
if (NodeToReturn) {
|
||
|
||
*RestartKey = NodeToReturn;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If there actually is a next element in the enumeration
|
||
// then the pointer to return is right after the list links.
|
||
//
|
||
|
||
return ((NodeToReturn)?
|
||
((PVOID)&((PTABLE_ENTRY_HEADER)NodeToReturn)->UserData)
|
||
:((PVOID)(NULL)));
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|