windows-nt/Source/XPSP1/NT/windows/winstate/cobra/utils/memdb/keystruct.c
2020-09-26 16:20:57 +08:00

1309 lines
29 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
keystruct.c
Abstract:
Routines that manage the memdb key structures.
Author:
Jim Schmidt (jimschm) 8-Aug-1996
Revision History:
mvander 13-Aug-1999 major restructuring
jimschm 30-Dec-1998 Hacked in AVL balancing
jimschm 23-Sep-1998 Proxy nodes, so MemDbMoveTree can replace end nodes too
jimschm 29-May-1998 Ability to replace center nodes in key strings
jimschm 21-Oct-1997 Split from memdb.c
--*/
#include "pch.h"
#include "memdbp.h"
#include "bintree.h"
// LINT - in the next function keystruct is thought to be possibly NULL.
// If we examine the code we'll see that this is not a possibility so...
//lint -save -e794
UINT g_TotalKeys = 0;
UINT
pAllocKeyStruct (
IN PCWSTR KeyName,
IN UINT PrevLevelIndex
)
/*++
Routine Description:
pAllocKeyStruct allocates a block of memory in the single
heap, expanding it if necessary.
The KeyName must not already be in the tree, and
PrevLevelIndex must point to a valid UINT Index
variable.
This function may move the database buffer. Pointers
into the database might not be valid afterwards.
Arguments:
KeyName - The string identifying the key. It cannot
contain backslashes. The new struct will
be initialized and this name will be copied
into the struct.
PrevLevelIndex - Specifies the previous level root Index
Return Value:
An Index to the new structure.
--*/
{
UINT size;
PKEYSTRUCT KeyStruct = NULL;
UINT Offset;
UINT PrevDel;
UINT TreeOffset;
UINT Index;
MYASSERT (g_CurrentDatabase);
size = SizeOfStringW (KeyName) + KEYSTRUCT_SIZE;
//
// Look for free block
//
PrevDel = INVALID_OFFSET;
Offset = g_CurrentDatabase->FirstKeyDeleted;
while (Offset != INVALID_OFFSET) {
KeyStruct = GetKeyStructFromOffset(Offset);
MYASSERT(KeyStruct);
if (KeyStruct->Size >= size && KeyStruct->Size < (size + ALLOC_TOLERANCE)) {
break;
}
PrevDel = Offset;
Offset = KeyStruct->NextDeleted;
}
if (Offset == INVALID_OFFSET) {
//
// Alloc new block if no free space
//
g_TotalKeys ++;
Offset = DatabaseAllocBlock (size);
if (Offset == INVALID_OFFSET) {
return INVALID_OFFSET;
}
#ifdef DEBUG
//
// if we are in debug mode, and we are using debug structs, set
// pointer normally and set Signature DWORD. if we are not using
// debug structs, then set pointer to 4 bytes below actual offset,
// so all members are shifted down.
//
if (g_UseDebugStructs) {
KeyStruct = (PKEYSTRUCT)OFFSET_TO_PTR(Offset);
KeyStruct->Signature = KEYSTRUCT_SIGNATURE;
} else {
KeyStruct = (PKEYSTRUCT)OFFSET_TO_PTR(Offset - KEYSTRUCT_HEADER_SIZE);
}
#else
KeyStruct = (PKEYSTRUCT)OFFSET_TO_PTR(Offset);
#endif
KeyStruct->Size = size;
} else {
//
// Delink free block if recovering free space
//
if (PrevDel != INVALID_OFFSET) {
GetKeyStructFromOffset(PrevDel)->NextDeleted = KeyStruct->NextDeleted;
} else {
g_CurrentDatabase->FirstKeyDeleted = KeyStruct->NextDeleted;
}
#ifdef DEBUG
KeyStruct->KeyFlags &= ~KSF_DELETED;
#endif
}
//
// Init new block
//
KeyStruct->DataStructIndex = INVALID_OFFSET;
KeyStruct->NextLevelTree = INVALID_OFFSET;
KeyStruct->PrevLevelIndex = PrevLevelIndex;
KeyStruct->Value = 0;
KeyStruct->KeyFlags = 0;
KeyStruct->DataFlags = 0;
StringPasCopyConvertTo (KeyStruct->KeyName, KeyName);
Index = AddKeyOffsetToBuffer(Offset);
//
// Put it in the tree
//
TreeOffset = (KeyStruct->PrevLevelIndex == INVALID_OFFSET) ?
g_CurrentDatabase->FirstLevelTree :
GetKeyStruct(KeyStruct->PrevLevelIndex)->NextLevelTree;
if (TreeOffset == INVALID_OFFSET) {
TreeOffset = BinTreeNew();
if (TreeOffset == INVALID_OFFSET) {
return INVALID_OFFSET;
}
if (PrevLevelIndex == INVALID_OFFSET) {
g_CurrentDatabase->FirstLevelTree = TreeOffset;
} else {
GetKeyStruct(PrevLevelIndex)->NextLevelTree = TreeOffset;
}
}
if (!BinTreeAddNode(TreeOffset, Index)) {
return INVALID_OFFSET;
}
return Index;
}
//lint -restore
UINT
pNewKey (
IN PCWSTR KeyStr,
IN BOOL Endpoint
)
/*++
Routine Description:
NewKey allocates a key struct off our heap, and links it into the binary
tree. KeyStr must be a full key path, and any part of the path that does
not exist will be created. KeyStr must not already exist (though parts
of it can exist).
This function may move the database buffer. Pointers
into the database might not be valid afterwards.
Arguments:
KeyStr - The full path to the value, separated by backslashes.
Each string between backslashes will cause a key
struct to be allocated and linked. Some of the
structs may already have been allocated.
Endpoint - Specifies TRUE if new node is an endpoint, or FALSE if
it is not.
Return Value:
An Index to the last node of the new structure, or
INVALID_OFFSET if the key could not be allocated.
--*/
{
WCHAR Path[MEMDB_MAX];
PWSTR p;
PWSTR Start, End;
UINT Index, ThisLevelTree;
PKEYSTRUCT KeyStruct;
UINT PrevLevelIndex;
BOOL NewNodeCreated = FALSE;
MYASSERT (g_CurrentDatabase);
StringCopyW (Path, KeyStr);
End = Path;
ThisLevelTree = g_CurrentDatabase->FirstLevelTree;
PrevLevelIndex = INVALID_OFFSET;
do {
// Split string at backslash
Start = End;
p = wcschr (End, L'\\');
if (p) {
End = p + 1;
*p = 0;
}
else
End = NULL;
// Look in tree for key
if (!NewNodeCreated) {
Index = FindKeyStructInTree (ThisLevelTree, Start, FALSE);
} else {
Index = INVALID_OFFSET;
}
if (Index == INVALID_OFFSET) {
// Add a new key if it was not found
Index = pAllocKeyStruct (Start, PrevLevelIndex);
if (Index == INVALID_OFFSET) {
return INVALID_OFFSET;
}
NewNodeCreated = TRUE;
}
// Continue to next level
KeyStruct = GetKeyStruct (Index);
PrevLevelIndex = Index;
ThisLevelTree = KeyStruct->NextLevelTree;
} while (End);
if (Endpoint) {
if (!(KeyStruct->KeyFlags & KSF_ENDPOINT)) {
NewNodeCreated = TRUE;
}
KeyStruct->KeyFlags |= KSF_ENDPOINT;
if (NewNodeCreated) {
(void)AddHashTableEntry (g_CurrentDatabase->HashTable, KeyStr, Index);
}
}
return Index;
}
UINT
NewKey (
IN PCWSTR KeyStr
)
/*++
Routine Description:
creates a new key that is an endpoint.
Arguments:
KeyStr - The string identifying the key.
Return Value:
An Index to the new structure.
--*/
{
return pNewKey (KeyStr, TRUE);
}
UINT
NewEmptyKey (
IN PCWSTR KeyStr
)
/*++
Routine Description:
creates an empty new key that is NOT an endpoint.
This function may move the database buffer. Pointers
into the database might not be valid afterwards.
Arguments:
KeyStr - The string identifying the key.
Return Value:
An Index to the new structure.
--*/
{
return pNewKey (KeyStr, TRUE);
}
VOID
pRemoveKeyFromTree (
IN PKEYSTRUCT pKey
)
{
BOOL LastNode;
PUINT pTreeOffset;
MYASSERT(pKey);
MYASSERT (g_CurrentDatabase);
if (pKey->PrevLevelIndex==INVALID_OFFSET) {
pTreeOffset = &g_CurrentDatabase->FirstLevelTree;
} else {
pTreeOffset = &GetKeyStruct(pKey->PrevLevelIndex)->NextLevelTree;
}
MYASSERT(*pTreeOffset!=INVALID_OFFSET);
BinTreeDeleteNode (*pTreeOffset, pKey->KeyName, &LastNode);
if (LastNode) {
BinTreeDestroy(*pTreeOffset);
*pTreeOffset = INVALID_OFFSET;
}
}
VOID
pDeallocKeyStruct (
IN UINT Index,
IN BOOL ClearFlag,
IN BOOL DelinkFlag,
IN BOOL FreeIndexFlag
)
/*++
Routine Description:
pDeallocKeyStruct first deletes all structures pointed to by
NextLevelTree. After all items are deleted from the next
level, pDeallocKeyStruct optionally delinks the struct from
the binary tree. Before exiting, the struct is given to the
deleted block chain.
Arguments:
Index - An index in g_CurrentDatabase->OffsetBuffer
ClearFlag - Specifies TRUE if the key struct's children are to
be deleted, or FALSE if the current key struct should
simply be cleaned up but left allocated.
DelinkFlag - A flag indicating TRUE to delink the struct from
the binary tree it is in, or FALSE if the struct is
only to be added to the deleted block chain.
FreeIndexFlag - This argument is only used if ClearFlag is true.
It is FALSE if we do not want to free the index in
g_CurrentDatabase->OffsetBuffer (i.e., we are moving the key and we do
not want to deallocate the space in the buffer), or
TRUE if we are just deleting the key, so we no longer
need the g_CurrentDatabase->OffsetBuffer space at Index.
Return Value:
none
--*/
{
PKEYSTRUCT KeyStruct;
UINT KeyIndex, TreeEnum;
WCHAR TempStr[MEMDB_MAX];
PUINT linkageList;
UINT linkageSize;
BYTE instance;
BYTE oldDbIndex;
MYASSERT (g_CurrentDatabase);
KeyStruct = GetKeyStruct (Index);
if (FreeIndexFlag && (KeyStruct->DataFlags & DATAFLAG_DOUBLELINK)) {
//
// we have some double linkage here. Let's go to the other
// key and remove the linkage that points to this one
//
for (instance = 0; instance <= DATAFLAG_INSTANCEMASK; instance ++) {
// First, retrieve the linkage list
linkageSize = 0;
linkageList = (PUINT) KeyStructGetBinaryData (
Index,
DATAFLAG_DOUBLELINK,
instance,
&linkageSize,
NULL
);
if (linkageList) {
oldDbIndex = g_CurrentDatabaseIndex;
while (linkageSize) {
SelectDatabase (GET_DATABASE (*linkageList));
KeyStructDeleteLinkage (
GET_INDEX (*linkageList),
DATAFLAG_DOUBLELINK,
instance,
GET_EXTERNAL_INDEX (Index),
FALSE
);
linkageSize -= SIZEOF (UINT);
linkageList ++;
if (linkageSize < SIZEOF (UINT)) {
break;
}
}
SelectDatabase (oldDbIndex);
}
}
}
if (KeyStruct->KeyFlags & KSF_ENDPOINT) {
//
// Remove endpoints from hash table and free key data
//
if (PrivateBuildKeyFromIndex (0, Index, TempStr, NULL, NULL, NULL)) {
RemoveHashTableEntry (g_CurrentDatabase->HashTable, TempStr);
}
KeyStructFreeAllData (KeyStruct);
KeyStruct->KeyFlags &= ~KSF_ENDPOINT;
}
if (ClearFlag) {
//
// Call recursively if there are sublevels to this key
//
if (KeyStruct->NextLevelTree != INVALID_OFFSET) {
KeyIndex = GetFirstIndex(KeyStruct->NextLevelTree, &TreeEnum);
while (KeyIndex != INVALID_OFFSET) {
pDeallocKeyStruct (KeyIndex, TRUE, FALSE, FreeIndexFlag);
KeyIndex = GetNextIndex (&TreeEnum);
}
BinTreeDestroy(KeyStruct->NextLevelTree);
}
//
// Remove the item from its binary tree
//
if (DelinkFlag) {
pRemoveKeyFromTree(KeyStruct);
}
//
// Donate block to free space unless caller does not
// want child structs freed.
//
KeyStruct->NextDeleted = g_CurrentDatabase->FirstKeyDeleted;
g_CurrentDatabase->FirstKeyDeleted = KeyIndexToOffset(Index);
#ifdef DEBUG
KeyStruct->KeyFlags |= KSF_DELETED;
#endif
// let's empty the keystruct (for better compression)
ZeroMemory (KeyStruct->KeyName, KeyStruct->Size - KEYSTRUCT_SIZE);
if (FreeIndexFlag) {
RemoveKeyOffsetFromBuffer(Index);
}
}
}
BOOL
PrivateDeleteKeyByIndex (
IN UINT Index
)
/*++
Routine Description:
PrivateDeleteKeyByIndex will completely destroy the key struct
that Index points to (along with all sub-levels. Furthermore,
it goes back recursively and removes the parent structures as well
if they no longer have a child (the current one was the only one).
Arguments:
Index - Index of the key structure.
Return Value:
TRUE if successfull, FALSE otherwise
--*/
{
PKEYSTRUCT keyStruct;
UINT prevLevelIndex;
BOOL result = TRUE;
keyStruct = GetKeyStruct (Index);
prevLevelIndex = keyStruct->PrevLevelIndex;
pDeallocKeyStruct (Index, TRUE, TRUE, TRUE);
if (prevLevelIndex != INVALID_OFFSET) {
keyStruct = GetKeyStruct (prevLevelIndex);
if (keyStruct->NextLevelTree != INVALID_OFFSET) {
result = PrivateDeleteKeyByIndex (prevLevelIndex);
}
}
return result;
}
BOOL
DeleteKey (
IN PCWSTR KeyStr,
IN UINT TreeOffset,
IN BOOL MustMatch
)
/*++
Routine Description:
DeleteKey takes a key path and puts the key struct in the deleted
block chain. Any sub-levels are deleted as well.
Arguments:
KeyStr - The full path to the value, separated by backslashes.
TreeOffset - A pointer to the level's binary tree root variable.
MustMatch - A flag indicating if the delete only applies to
end points or if any matching struct is to be deleted.
TRUE indicates only endpoints can be deleted.
Return Value:
none
--*/
{
WCHAR Path[MEMDB_MAX];
PWSTR p;
PWSTR Start, End;
UINT Index, NextIndex, TreeEnum=INVALID_OFFSET;
PKEYSTRUCT KeyStruct;
StringCopyW (Path, KeyStr);
End = Path;
//
// Split string at backslash
//
Start = End;
p = wcschr (End, L'\\');
if (p) {
End = p + 1;
*p = 0;
} else {
End = NULL;
}
//
// Look at this level for the very first key
//
Index = FindKeyStructUsingTreeOffset (TreeOffset, &TreeEnum, Start);
//
// If this is the last level, delete the matching keys
// (may need to be endpoints if MustMatch is TRUE)
//
if (!End) {
while (Index != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (Index);
NextIndex = FindKeyStructUsingTreeOffset (TreeOffset, &TreeEnum, Start);
//
// If must match and lower levels exist, don't delete, just turn
// off the endpoint flag
//
if (MustMatch && KeyStruct->NextLevelTree != INVALID_OFFSET) {
// Call to clean up, not to delink or recurse
pDeallocKeyStruct (Index, FALSE, FALSE, FALSE);
}
//
// Else delete the struct if an endpoint or don't care about
// endpoints
//
else if (!MustMatch || (KeyStruct->KeyFlags & KSF_ENDPOINT)) {
// Call to free the entire key struct and all children
pDeallocKeyStruct (Index, TRUE, TRUE, TRUE);
}
Index = NextIndex;
}
}
//
// Otherwise recursively examine next level for each match
//
else {
while (Index != INVALID_OFFSET) {
//
// Delete all matching subkeys
//
NextIndex = FindKeyStructUsingTreeOffset (TreeOffset, &TreeEnum, Start);
KeyStruct = GetKeyStruct (Index);
DeleteKey (End, KeyStruct->NextLevelTree, MustMatch);
//
// If this is not an endpoint and has no children, delete it
//
if (KeyStruct->NextLevelTree == INVALID_OFFSET &&
!(KeyStruct->KeyFlags & KSF_ENDPOINT)
) {
// Call to free the entire key struct
pDeallocKeyStruct (Index, TRUE, TRUE, TRUE);
}
//
// Continue looking in this level for another match
//
Index = NextIndex;
}
}
return TRUE;
}
VOID
pRemoveHashEntriesForNode (
IN PCWSTR Root,
IN UINT Index
)
/*++
Routine Description:
pRemoveHashEntriesFromNode removes all hash table entries from all children
of the specified node. This function is called recursively.
Arguments:
Root - Specifies the root string that corresponds with Index. This must
also contain the temporary hive root.
Index - Specifies the Index of the node to process. The node and all of
its children will be removed from the hash table.
Return Value:
None.
--*/
{
UINT ChildIndex, TreeEnum;
PKEYSTRUCT KeyStruct;
WCHAR ChildRoot[MEMDB_MAX];
PWSTR End;
MYASSERT (g_CurrentDatabase);
//
// Remove hash entry if this root is an endpoint
//
KeyStruct = GetKeyStruct (Index);
if (KeyStruct->KeyFlags & KSF_ENDPOINT) {
RemoveHashTableEntry (g_CurrentDatabase->HashTable, Root);
#ifdef DEBUG
{
UINT HashIndex;
HashIndex = FindStringInHashTable (g_CurrentDatabase->HashTable, Root);
if (HashIndex != INVALID_OFFSET) {
DEBUGMSG ((DBG_WARNING, "Memdb move duplicate: %s", Root));
}
}
#endif
}
//
// Recurse for all children, removing hash entries for all endpoints found
//
StringCopyW (ChildRoot, Root);
End = GetEndOfStringW (ChildRoot);
*End = L'\\';
End++;
*End = 0;
ChildIndex = GetFirstIndex(KeyStruct->NextLevelTree, &TreeEnum);
while (ChildIndex != INVALID_OFFSET) {
KeyStruct = GetKeyStruct (ChildIndex);
StringPasCopyConvertFrom (End, KeyStruct->KeyName);
pRemoveHashEntriesForNode (ChildRoot, ChildIndex);
ChildIndex = GetNextIndex(&TreeEnum);
}
}
VOID
pAddHashEntriesForNode (
IN PCWSTR Root,
IN UINT Index,
IN BOOL AddRoot
)
/*++
Routine Description:
pAddHashEntriesForNode adds hash table entries for the specified root and
all of its children.
Arguments:
Root - Specifies the root string that corresponds to Index. This string
must also include the temporary hive root.
Index - Specifies the node Index to begin processing. The node and all
of its children are added to the hash table.
AddRoot - Specifies TRUE if the root should be added to the hash table,
FALSE otherwise.
Return Value:
None.
--*/
{
UINT ChildIndex, TreeEnum;
PKEYSTRUCT KeyStruct;
WCHAR ChildRoot[MEMDB_MAX];
PWSTR End;
UINT HashIndex;
MYASSERT (g_CurrentDatabase);
//
// Add hash entry if this root is an endpoint
//
KeyStruct = GetKeyStruct (Index);
if (AddRoot && KeyStruct->KeyFlags & KSF_ENDPOINT) {
HashIndex = FindStringInHashTable (g_CurrentDatabase->HashTable, Root);
if (HashIndex != Index) {
#ifdef DEBUG
if (HashIndex != INVALID_OFFSET) {
DEBUGMSG ((DBG_WARNING, "Memdb duplicate: %s", Root));
}
#endif
AddHashTableEntry (g_CurrentDatabase->HashTable, Root, Index);
}
}
//
// Recurse for all children, adding hash entries for all endpoints found
//
StringCopyW (ChildRoot, Root);
End = GetEndOfStringW (ChildRoot);
*End = L'\\';
End++;
*End = 0;
ChildIndex = GetFirstIndex(KeyStruct->NextLevelTree, &TreeEnum);
while (ChildIndex != INVALID_OFFSET) {
KeyStruct = GetKeyStruct(ChildIndex);
StringPasCopyConvertFrom (End, KeyStruct->KeyName);
pAddHashEntriesForNode(ChildRoot, ChildIndex, TRUE);
ChildIndex = GetNextIndex(&TreeEnum);
}
}
#ifdef DEBUG
//
// in non-DEBUG mode, GetKeyStructFromOffset
// and GetKeyStruct are implemented as macros
//
PKEYSTRUCT
GetKeyStructFromOffset (
IN UINT Offset
)
/*++
Routine Description:
GetKeyStruct returns a pointer given an Offset. The debug version
checks the signature and validity of each Index. It is assumed that
Offset is always valid.
Arguments:
Offset - Specifies the Offset to the node
Return Value:
The pointer to the node.
--*/
{
PKEYSTRUCT KeyStruct;
MYASSERT (g_CurrentDatabase);
if (Offset == INVALID_OFFSET) {
DEBUGMSG ((DBG_ERROR, "Invalid root accessed in GetKeyStruct at offset %u", Offset));
return NULL;
}
if (!g_CurrentDatabase) {
DEBUGMSG ((DBG_ERROR, "Attempt to access non-existent buffer at %u", Offset));
return NULL;
}
if (Offset > g_CurrentDatabase->End) {
DEBUGMSG ((DBG_ERROR, "Access beyond length of buffer in GetKeyStruct (offset %u)", Offset));
return NULL;
}
if (!g_UseDebugStructs) {
KeyStruct = (PKEYSTRUCT) OFFSET_TO_PTR (Offset - KEYSTRUCT_HEADER_SIZE);
return KeyStruct;
}
KeyStruct = (PKEYSTRUCT) OFFSET_TO_PTR (Offset);
if (KeyStruct->Signature != KEYSTRUCT_SIGNATURE) {
DEBUGMSG ((DBG_ERROR, "Signature does not match in GetKeyStruct at offset %u!", Offset));
return NULL;
}
return KeyStruct;
}
PKEYSTRUCT
GetKeyStruct (
IN UINT Index
)
/*++
Routine Description:
GetKeyStruct returns a pointer given an Index. The debug version
checks the signature and validity of each Index. It is assumed that Index
is always valid.
Arguments:
Index - Specifies the Index to the node
Return Value:
The pointer to the node.
--*/
{
UINT Offset;
if (Index == INVALID_OFFSET) {
DEBUGMSG ((DBG_ERROR, "Invalid root accessed in GetKeyStruct at index %u", Index));
return NULL;
}
Offset = KeyIndexToOffset(Index);
if (Offset == INVALID_OFFSET) {
return NULL;
}
return GetKeyStructFromOffset(Offset);
}
#endif
BOOL
PrivateBuildKeyFromIndex (
IN UINT StartLevel, // zero-based
IN UINT TailIndex,
OUT PWSTR Buffer, OPTIONAL
OUT PUINT ValPtr, OPTIONAL
OUT PUINT UserFlagsPtr, OPTIONAL
OUT PUINT Chars OPTIONAL
)
/*++
Routine Description:
PrivateBuildKeyFromIndex generates the key string given an Index. The
caller can specify the start level to skip root nodes. It is assumed that
TailIndex is always valid.
Arguments:
StartLevel - Specifies the zero-based level to begin building the key
string. This is used to skip the root portion of the key
string.
TailIndex - Specifies the Index to the last level of the key string.
Buffer - Receives the key string, must be able to hold MEMDB_MAX
characters.
ValPtr - Receives the key's value
UserFlagsPtr - Receives the user flags
Chars - Receives the number of characters in Buffer
Return Value:
TRUE if the key was build properly, FALSE otherwise.
--*/
{
static UINT Indices[MEMDB_MAX];
PKEYSTRUCT KeyStruct;
UINT CurrentIndex;
UINT IndexEnd;
UINT IndexStart;
register PWSTR p;
//
// Build string
//
IndexEnd = MEMDB_MAX;
IndexStart = MEMDB_MAX;
CurrentIndex = TailIndex;
while (CurrentIndex != INVALID_OFFSET) {
//
// Record offset
//
IndexStart--;
Indices[IndexStart] = CurrentIndex;
//
// Dec for start level and go to parent
//
KeyStruct = GetKeyStruct (CurrentIndex);
if (!KeyStruct) {
return FALSE;
}
CurrentIndex = KeyStruct->PrevLevelIndex;
}
//
// Filter for "string is not long enough"
//
IndexStart += StartLevel;
if (IndexStart >= IndexEnd) {
return FALSE;
}
//
// Transfer node's value and flags to caller's variables
//
if (ValPtr) {
KeyStructGetValue (GetKeyStruct(TailIndex), ValPtr);
}
if (UserFlagsPtr) {
KeyStructGetFlags (GetKeyStruct(TailIndex), UserFlagsPtr);
}
//
// Copy each piece of the string to Buffer and calculate character count
//
if (Buffer) {
p = Buffer;
for (CurrentIndex = IndexStart ; CurrentIndex < IndexEnd ; CurrentIndex++) {
KeyStruct = GetKeyStruct (Indices[CurrentIndex]);
CopyMemory(p, KeyStruct->KeyName + 1, *KeyStruct->KeyName * sizeof(WCHAR));
p += *KeyStruct->KeyName;
*p++ = L'\\';
}
p--;
*p = 0;
if (Chars) {
*Chars = (UINT)(((UBINT)p - (UBINT)Buffer) / sizeof (WCHAR));
}
} else if (Chars) {
*Chars = 0;
for (CurrentIndex = IndexStart ; CurrentIndex < IndexEnd ; CurrentIndex++) {
KeyStruct = GetKeyStruct (Indices[CurrentIndex]);
*Chars += StringPasCharCount(KeyStruct->KeyName) + 1;
}
*Chars -= 1;
}
return TRUE;
}
BOOL
KeyStructSetInsertionOrdered (
IN PKEYSTRUCT pKey
)
/*++
Routine Description:
KeyStructSetInsertionOrdered sets the enumeration order of the children
of Key to be in the order they were inserted.
This function may move the database buffer. Pointers
into the database might not be valid afterwards.
Arguments:
Key - key to make insertion ordered
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
return BinTreeSetInsertionOrdered(pKey->NextLevelTree);
}
UINT
GetFirstIndex (
IN UINT TreeOffset,
OUT PUINT pTreeEnum
)
/*++
Routine Description:
GetFirstIndex walks down the left side of the binary tree
pointed to by TreeOffset, and returns the left-most node.
Arguments:
TreeOffset - An offset to the root of the tree
TreeEnum - a pointer to a UINT which will hold enumeration
information for future calls of GetNextIndex
Return Value:
An Index to the leftmost structure, or INVALID_OFFSET if the
root was invalid.
--*/
{
return BinTreeEnumFirst(TreeOffset, pTreeEnum);
}
UINT
GetNextIndex (
IN OUT PUINT pTreeEnum
)
/*++
Routine Description:
GetNextIndex traverses the binary tree in order.
Arguments:
TreeEnum - Enumerator filled by GetFirstIndex which
will be changed by this function
Return Value:
An Index to the next structure, or INVALID_OFFSET if the
end is reached.
--*/
{
return BinTreeEnumNext(pTreeEnum);
}
UINT KeyStructGetChildCount (
IN PKEYSTRUCT pKey
)
{
if (!pKey) {
return 0;
}
return BinTreeSize(pKey->NextLevelTree);
}
UINT
FindKeyStructInTree (
IN UINT TreeOffset,
IN PWSTR KeyName,
IN BOOL IsPascalString
)
/*++
Routine Description:
FindKeyStructInTree takes a key name and looks for the
Index in the tree specified by TreeOffset. The key
name must not contain backslashes.
Arguments:
TreeOffset - An offset to the root of the level
KeyName - The name of the key to find in the binary tree
(not the full key path; just the name of this level).
IsPascalString - TRUE if string is in pascal format (char
count is first WCHAR, no null terminator) otherwise FALSE
Return Value:
An Index to the structure, or INVALID_OFFSET if the key
was not found.
--*/
{
UINT Index;
if (!IsPascalString) {
StringPasConvertTo(KeyName);
}
Index = BinTreeFindNode(TreeOffset, KeyName);
if (!IsPascalString) {
StringPasConvertFrom(KeyName);
}
return Index;
}
#ifdef DEBUG
BOOL
CheckLevel(UINT TreeOffset,
UINT PrevLevelIndex
)
{
PKEYSTRUCT pKey;
UINT KeyIndex, TreeEnum;
WCHAR key[MEMDB_MAX];
if (TreeOffset==INVALID_OFFSET) {
return TRUE;
}
BinTreeCheck(TreeOffset);
#if MEMDB_VERBOSE
if (PrevLevelIndex!=INVALID_OFFSET) {
wprintf(L"children of %.*s:\n",*GetKeyStruct(PrevLevelIndex)->KeyName,GetKeyStruct(PrevLevelIndex)->KeyName+1);
} else {
printf("top level children:\n");
}
BinTreePrint(TreeOffset);
#endif
if ((KeyIndex=BinTreeEnumFirst(TreeOffset,&TreeEnum))!=INVALID_OFFSET) {
do {
pKey=GetKeyStruct(KeyIndex);
if (pKey->PrevLevelIndex!=PrevLevelIndex) {
wprintf(L"MemDbCheckDatabase: PrevLevelIndex of Keystruct %s incorrect!", StringPasCopyConvertFrom (key, pKey->KeyName));
}
if (!CheckLevel(pKey->NextLevelTree, KeyIndex)) {
wprintf(L"Child tree of %s bad!\n", StringPasCopyConvertFrom (key, pKey->KeyName));
}
} while ((KeyIndex=BinTreeEnumNext(&TreeEnum))!=INVALID_OFFSET);
} else {
printf("MemDbCheckDatabase: non-null binary tree has no children!");
return FALSE;
}
return TRUE;
}
#endif