windows-nt/Source/XPSP1/NT/ds/security/services/scerpc/splay.cpp
2020-09-26 16:20:57 +08:00

598 lines
14 KiB
C++

/*
Copyright (c) 2000 Microsoft Corporation
Module Name:
splay.cpp
Abstract:
Splay trees are simpler, more space-efficient, more flexible, and faster than any other balanced tree
scheme for storing an ordered set. This data structure satisfies all the invariants of a binary tree.
Searching, insertion, deletion, and many other operations can all be done with amortized logarithmic
performance. Since the trees adapt to the sequence of requests, their performance on real access
patterns is typically even better.
Author:
Vishnu Patankar (vishnup) 15-Aug-2000 created
Jin Huang (jinhuang) 06-Apr-2001 modified for supporting string value and to handle multiple clients
--*/
#include "splay.h"
//#include "dumpnt.h"
#define SCEP_MIN(a, b) (a < b ? a : b)
static
PSCEP_SPLAY_NODE
ScepSplaySplay(
IN SCEP_NODE_VALUE_TYPE Value,
IN PSCEP_SPLAY_NODE pNodeToSplayAbout,
IN PSCEP_SPLAY_NODE pSentinel,
IN SCEP_NODE_VALUE_TYPE Type
);
int
ScepValueCompare(
PVOID pValue1,
PVOID pValue2,
SCEP_NODE_VALUE_TYPE Type
);
static VOID
ScepSplayFreeNodes(
IN PSCEP_SPLAY_NODE pNode,
IN PSCEP_SPLAY_NODE pSentinel
);
PSCEP_SPLAY_TREE
ScepSplayInitialize(
SCEP_NODE_VALUE_TYPE Type
)
/*
Routine Description:
This function initializes the splay tree with a sentinel (equivalent to a NULL ptr).
Arguments:
Type - the type for the splay value
Return Value:
Pointer to the splay tree root.
*/
{
if ( Type != SplayNodeSidType && Type != SplayNodeStringType ) {
return NULL;
}
PSCEP_SPLAY_TREE pRoot = (PSCEP_SPLAY_TREE)LocalAlloc(LPTR, sizeof( SCEP_SPLAY_TREE ) );
if ( pRoot ) {
pRoot->Sentinel = (PSCEP_SPLAY_NODE) LocalAlloc(LPTR, sizeof( SCEP_SPLAY_NODE ) );
if ( pRoot->Sentinel ) {
pRoot->Sentinel->Left = pRoot->Sentinel->Right = pRoot->Sentinel;
pRoot->Root = pRoot->Sentinel;
pRoot->Type = Type;
return pRoot;
} else {
LocalFree(pRoot);
}
}
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
VOID
ScepSplayFreeTree(
IN PSCEP_SPLAY_TREE *ppTreeRoot,
IN BOOL bDestroyTree
)
/*
Routine Description:
This function frees the splay tree including the satellite data "Value".
Arguments:
ppTreeRoot - address of the root of the tree
bDestroyTree - if the tree root should be destroyed (freed)
Return Value:
VOID
*/
{
if ( ppTreeRoot == NULL || *ppTreeRoot == NULL )
return;
ScepSplayFreeNodes( (*ppTreeRoot)->Root, (*ppTreeRoot)->Sentinel );
if ( bDestroyTree ) {
//
// free Sentinel
//
LocalFree( (*ppTreeRoot)->Sentinel);
LocalFree( *ppTreeRoot );
*ppTreeRoot = NULL;
} else {
( *ppTreeRoot)->Root = (*ppTreeRoot)->Sentinel;
}
return;
}
static VOID
ScepSplayFreeNodes(
IN PSCEP_SPLAY_NODE pNode,
IN PSCEP_SPLAY_NODE pSentinel
)
{
if ( pNode != pSentinel ) {
ScepSplayFreeNodes( pNode->Left, pSentinel );
ScepSplayFreeNodes( pNode->Right, pSentinel );
if (pNode->Value)
LocalFree( pNode->Value );
LocalFree (pNode);
}
}
static PSCEP_SPLAY_NODE
ScepSplaySingleRotateWithLeft(
IN PSCEP_SPLAY_NODE pNodeLeftRotate
)
/*
Routine Description:
This function can be called only if pNodeLeftRotate has a left child
Perform a rotate between a node (pNodeLeftRotate) and its left child
Update heights, then return new local root
Arguments:
pNodeLeftRotate - the node to rotate left about (local to this module)
Return Value:
New local root after rotation
*/
{
if ( pNodeLeftRotate == NULL ) return pNodeLeftRotate;
PSCEP_SPLAY_NODE pNodeRightRotate;
pNodeRightRotate = pNodeLeftRotate->Left;
pNodeLeftRotate->Left = pNodeRightRotate->Right;
pNodeRightRotate->Right = pNodeLeftRotate;
return pNodeRightRotate;
}
static PSCEP_SPLAY_NODE
ScepSplaySingleRotateWithRight(
IN PSCEP_SPLAY_NODE pNodeRightRotate
)
/*
Routine Description:
This function can be called only if pNodeRightRotate has a right child
Perform a rotate between a node (pNodeRightRotate) and its right child
Update heights, then return new root
Arguments:
pNodeRightRotate - the node to rotate right about (local to this module)
Return Value:
New local root after rotation
*/
{
if ( pNodeRightRotate == NULL ) return pNodeRightRotate;
PSCEP_SPLAY_NODE pNodeLeftRotate;
pNodeLeftRotate = pNodeRightRotate->Right;
pNodeRightRotate->Right = pNodeLeftRotate->Left;
pNodeLeftRotate->Left = pNodeRightRotate;
return pNodeLeftRotate;
}
static
PSCEP_SPLAY_NODE
ScepSplaySplay(
IN PVOID Value,
IN PSCEP_SPLAY_NODE pNodeToSplayAbout,
IN PSCEP_SPLAY_NODE pSentinel,
IN SCEP_NODE_VALUE_TYPE Type
)
/*
Routine Description:
This is really the key routine doing all the balancing (splaying)
Top-down splay procedure that does not requiring Value to be in tree.
Arguments:
Value - the value to splay the tree about
pNodeToSplayAbout - the node to splay about (external routines such as ScepSplayInsert()
usually pass in the tree root). This routine is local to this module.
pSentinel - the Sentinel (terminating node)
Type - type of the splay values
Return Value:
New local root after rotation
*/
{
if ( pNodeToSplayAbout == NULL || pSentinel == NULL || Value == NULL) return pNodeToSplayAbout;
SCEP_SPLAY_NODE Header;
PSCEP_SPLAY_NODE LeftTreeMax, RightTreeMin;
Header.Left = Header.Right = pSentinel;
LeftTreeMax = RightTreeMin = &Header;
pSentinel->Value = Value;
int iRes=0;
while ( 0 != (iRes=ScepValueCompare(Value, pNodeToSplayAbout->Value, Type)) ) {
if ( 0 > iRes ) {
if ( 0 > ScepValueCompare(Value, pNodeToSplayAbout->Left->Value, Type) )
pNodeToSplayAbout = ScepSplaySingleRotateWithLeft( pNodeToSplayAbout );
if ( pNodeToSplayAbout->Left == pSentinel )
break;
//
// Link right
//
RightTreeMin->Left = pNodeToSplayAbout;
RightTreeMin = pNodeToSplayAbout;
pNodeToSplayAbout = pNodeToSplayAbout->Left;
} else {
if ( 0 < ScepValueCompare(Value, pNodeToSplayAbout->Right->Value, Type) )
pNodeToSplayAbout = ScepSplaySingleRotateWithRight( pNodeToSplayAbout );
if ( pNodeToSplayAbout->Right == pSentinel )
break;
//
// Link left
//
LeftTreeMax->Right = pNodeToSplayAbout;
LeftTreeMax = pNodeToSplayAbout;
pNodeToSplayAbout = pNodeToSplayAbout->Right;
}
}
//
// reassemble
//
LeftTreeMax->Right = pNodeToSplayAbout->Left;
RightTreeMin->Left = pNodeToSplayAbout->Right;
pNodeToSplayAbout->Left = Header.Right;
pNodeToSplayAbout->Right = Header.Left;
//
// reset Sentinel so that it does not point to some invalid buffer after
// this function returns.
//
pSentinel->Value = NULL;
return pNodeToSplayAbout;
}
DWORD
ScepSplayInsert(
IN PVOID Value,
IN OUT PSCEP_SPLAY_TREE pTreeRoot,
OUT BOOL *pbExists
)
/*
Routine Description:
This function is called to insert a particular Value
Arguments:
Value - the value to insert into the tree
pTreeRoot - the node to splay about (usually pass in the tree root)
pbExists - pointer to boolean that says if actual insertion was done or not
Return Value:
error code
*/
{
PSCEP_SPLAY_NODE pNewNode = NULL;
NTSTATUS Status;
DWORD dwValueLen;
DWORD rc=ERROR_SUCCESS;
if (pbExists)
*pbExists = FALSE;
if (Value == NULL || pTreeRoot == NULL ) {
return ERROR_INVALID_PARAMETER;
}
//
// check parameter type
//
switch (pTreeRoot->Type) {
case SplayNodeSidType:
if ( !RtlValidSid((PSID)Value) ) {
return ERROR_INVALID_PARAMETER;
}
dwValueLen = RtlLengthSid((PSID)Value);
break;
case SplayNodeStringType:
if ( *((PWSTR)Value) == L'\0') {
return ERROR_INVALID_PARAMETER;
}
dwValueLen = (wcslen((PWSTR)Value)+1)*sizeof(TCHAR);
break;
default:
return ERROR_INVALID_PARAMETER;
}
pNewNode = (PSCEP_SPLAY_NODE) LocalAlloc(LMEM_ZEROINIT, sizeof( SCEP_SPLAY_NODE ) );
if ( pNewNode == NULL ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
pNewNode->dwByteLength = dwValueLen;
pNewNode->Value = (PSID) LocalAlloc(LMEM_ZEROINIT, pNewNode->dwByteLength);
if (pNewNode->Value == NULL) {
LocalFree(pNewNode);
return ERROR_NOT_ENOUGH_MEMORY;
}
switch (pTreeRoot->Type) {
case SplayNodeSidType:
Status = RtlCopySid(pNewNode->dwByteLength, (PSID)(pNewNode->Value), (PSID)Value);
if (!NT_SUCCESS(Status)) {
LocalFree(pNewNode->Value);
LocalFree(pNewNode);
return RtlNtStatusToDosError(Status);
}
break;
case SplayNodeStringType:
memcpy( pNewNode->Value, Value, dwValueLen );
break;
}
if ( pTreeRoot->Root == pTreeRoot->Sentinel ) {
pNewNode->Left = pNewNode->Right = pTreeRoot->Sentinel;
pTreeRoot->Root = pNewNode;
} else {
pTreeRoot->Root = ScepSplaySplay( Value, pTreeRoot->Root, pTreeRoot->Sentinel, pTreeRoot->Type );
int iRes;
if ( 0 > (iRes=ScepValueCompare(Value, pTreeRoot->Root->Value, pTreeRoot->Type)) ) {
pNewNode->Left = pTreeRoot->Root->Left;
pNewNode->Right = pTreeRoot->Root;
pTreeRoot->Root->Left = pTreeRoot->Sentinel;
pTreeRoot->Root = pNewNode;
} else if ( 0 < iRes ) {
pNewNode->Right = pTreeRoot->Root->Right;
pNewNode->Left = pTreeRoot->Root;
pTreeRoot->Root->Right = pTreeRoot->Sentinel;
pTreeRoot->Root = pNewNode;
} else {
//
// Already in the tree
//
if (pbExists)
*pbExists = TRUE;
LocalFree(pNewNode->Value);
LocalFree(pNewNode);
return rc;
}
}
return rc;
}
DWORD
ScepSplayDelete(
IN PVOID Value,
IN PSCEP_SPLAY_TREE pTreeRoot
)
/*
Routine Description:
This function is called to delete a particular Value
Arguments:
Value - the value to insert into the tree
pTreeRoot - the node to splay about (usually pass in the tree root)
Return Value:
New root after deletion and splaying
*/
{
if ( pTreeRoot == NULL ) return ERROR_INVALID_PARAMETER;
PSCEP_SPLAY_NODE NewTree;
if ( pTreeRoot->Root != pTreeRoot->Sentinel ) {
//
// strong type check
//
if ( ( pTreeRoot->Type != SplayNodeSidType &&
pTreeRoot->Type != SplayNodeStringType ) ||
( pTreeRoot->Type == SplayNodeSidType &&
!RtlValidSid((PSID)Value) ) ||
( pTreeRoot->Type == SplayNodeStringType &&
*((PWSTR)Value) == L'\0' ) ) {
//
// invalid value/type
//
return ERROR_INVALID_PARAMETER;
}
pTreeRoot->Root = ScepSplaySplay( Value, pTreeRoot->Root, pTreeRoot->Sentinel, pTreeRoot->Type );
if ( 0 == ScepValueCompare(Value, pTreeRoot->Root->Value, pTreeRoot->Type) ) {
//
// found it
//
if ( pTreeRoot->Root->Left == pTreeRoot->Sentinel )
NewTree = pTreeRoot->Root->Right;
else {
NewTree = pTreeRoot->Root->Left;
NewTree = ScepSplaySplay( Value, NewTree, pTreeRoot->Sentinel, pTreeRoot->Type );
NewTree->Right = pTreeRoot->Root->Right;
}
if (pTreeRoot->Root->Value)
LocalFree( pTreeRoot->Root->Value);
LocalFree( pTreeRoot->Root );
pTreeRoot->Root = NewTree;
}
}
return ERROR_SUCCESS;
}
BOOL
ScepSplayValueExist(
IN PVOID Value,
IN OUT PSCEP_SPLAY_TREE pTreeRoot
)
{
PSCEP_SPLAY_NODE pMatchedNode = NULL;
if ( pTreeRoot == NULL || Value == NULL) {
return FALSE;
}
//
// strong type check
//
if ( ( pTreeRoot->Type != SplayNodeSidType &&
pTreeRoot->Type != SplayNodeStringType ) ||
( pTreeRoot->Type == SplayNodeSidType &&
!RtlValidSid((PSID)Value) ) ||
( pTreeRoot->Type == SplayNodeStringType &&
*((PWSTR)Value) == L'\0' ) ) {
//
// invalid value/type
//
return FALSE;
}
pTreeRoot->Root = ScepSplaySplay( Value, pTreeRoot->Root, pTreeRoot->Sentinel, pTreeRoot->Type );
pMatchedNode = pTreeRoot->Root;
if (pMatchedNode && pMatchedNode->Value) {
if (ScepValueCompare(pMatchedNode->Value, Value, pTreeRoot->Type) == 0) {
return TRUE;
}
}
return FALSE;
}
int
ScepValueCompare(
PVOID pValue1,
PVOID pValue2,
SCEP_NODE_VALUE_TYPE Type
)
/*
Routine Description:
Lexical sid byte compare
Arguments:
pValue1 - ptr to first value
pValue2 - ptr to second value
Type - the type of the value
Return Value:
0 if Value1 == Value2
+ve if Value1 > Value2
-ve if Value1 < Value2
*/
{
DWORD dwValue1 = 0;
DWORD dwValue2 = 0;
int iByteCmpResult = 0;
switch ( Type ) {
case SplayNodeSidType:
dwValue1 = RtlLengthSid((PSID)pValue1);
dwValue2 = RtlLengthSid((PSID)pValue2);
iByteCmpResult = memcmp(pValue1, pValue2, SCEP_MIN(dwValue1, dwValue2));
if (dwValue1 == dwValue2)
return iByteCmpResult;
else if (iByteCmpResult == 0)
return dwValue1-dwValue2;
return iByteCmpResult;
case SplayNodeStringType:
iByteCmpResult = _wcsicmp((PWSTR)pValue1, (PWSTR)pValue2);
return iByteCmpResult;
}
return 0;
}
BOOL
ScepSplayTreeEmpty(
IN PSCEP_SPLAY_TREE pTreeRoot
)
{
if ( pTreeRoot == NULL ||
pTreeRoot->Root == NULL ||
pTreeRoot->Root == pTreeRoot->Sentinel ) {
return TRUE;
}
return FALSE;
}