/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * PURPOSE: Unicode Prefix implementation * FILE: lib/rtl/unicodeprefix.c * PROGRAMMER: Alex Ionescu (alex@relsoft.net) */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include /* * FIXME: Try to find the official names and add to NDK * Definitions come from fastfat driver. */ typedef USHORT NODE_TYPE_CODE; #define PFX_NTC_TABLE ((NODE_TYPE_CODE)0x0800) #define PFX_NTC_ROOT ((NODE_TYPE_CODE)0x0801) #define PFX_NTC_CHILD ((NODE_TYPE_CODE)0x0802) #define PFX_NTC_CASE_MATCH ((NODE_TYPE_CODE)0x0803) /* FUNCTIONS ***************************************************************/ ULONG NTAPI ComputeUnicodeNameLength(IN PUNICODE_STRING UnicodeName) { ULONG Chars = UnicodeName->Length / sizeof(WCHAR); ULONG i, NamesFound = 1; /* Loop the string */ for (i = 0; i < (Chars - 1); i++) { /* Check if we found a backslash, meaning another name */ if (UnicodeName->Buffer[i] == '\\') NamesFound++; } /* Return the number of names found */ return NamesFound; } RTL_GENERIC_COMPARE_RESULTS NTAPI CompareUnicodeStrings(IN PUNICODE_STRING Prefix, IN PUNICODE_STRING String, IN ULONG CaseCheckChar) { ULONG StringLength = String->Length / sizeof(WCHAR); ULONG PrefixLength = Prefix->Length / sizeof(WCHAR); ULONG ScanLength = min(StringLength, PrefixLength); ULONG i; WCHAR FoundPrefix, FoundString; PWCHAR p, p1; /* Handle case noticed in npfs when Prefix = '\' and name starts with '\' */ if ((PrefixLength == 1) && (Prefix->Buffer[0] == '\\') && (StringLength > 1) && (String->Buffer[0] == '\\')) { /* The string is actually a prefix */ return -1; } /* Validate the Case Check Character Position */ if (CaseCheckChar > ScanLength) CaseCheckChar = ScanLength; /* Do the case sensitive comparison first */ for (i = 0; i < CaseCheckChar; i++) { /* Compare the two characters */ if (Prefix->Buffer[i] != String->Buffer[i]) break; } /* Save the characters we found */ FoundPrefix = Prefix->Buffer[i]; FoundString = String->Buffer[i]; /* Check if we exausted the search above */ if (i == CaseCheckChar) { /* Do a case-insensitive search */ p = &Prefix->Buffer[i]; p1 = &String->Buffer[i]; do { /* Move to the next character */ FoundPrefix = *p++; FoundString = *p1++; /* Compare it */ if (FoundPrefix != FoundString) { /* Upcase the characters */ FoundPrefix = RtlpUpcaseUnicodeChar(FoundPrefix); FoundString = RtlpUpcaseUnicodeChar(FoundString); /* Compare them again */ if (FoundPrefix != FoundString) break; } /* Move to the next char */ i++; } while (i < ScanLength); } /* Check if we weren't able to find a match in the loops */ if (i < ScanLength) { /* If the prefix character found was a backslash, this is a less */ if (FoundPrefix == '\\') return GenericLessThan; /* If the string character found was a backslack, then this is a more */ if (FoundString == '\\') return GenericGreaterThan; /* None of those two special cases, do a normal check */ if (FoundPrefix < FoundString) return GenericLessThan; /* The only choice left is that Prefix > String */ return GenericGreaterThan; } /* If we got here, a match was found. Check if the prefix is smaller */ if (PrefixLength < StringLength) { /* Check if the string is actually a prefix */ if (String->Buffer[PrefixLength] == '\\') return -1; /* It's not a prefix, and it's shorter, so it's a less */ return GenericLessThan; } /* Check if the prefix is longer */ if (PrefixLength > StringLength) return GenericGreaterThan; /* If we got here, then they are 100% equal */ return GenericEqual; } /* * @implemented */ PUNICODE_PREFIX_TABLE_ENTRY NTAPI RtlFindUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable, PUNICODE_STRING FullName, ULONG CaseInsensitiveIndex) { ULONG NameCount; PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry, PreviousEntry, Entry, NextEntry; PRTL_SPLAY_LINKS SplayLinks; RTL_GENERIC_COMPARE_RESULTS Result; DPRINT("RtlFindUnicodePrefix(): Table %p, FullName %wZ, " "CaseInsensitive %lu\n", PrefixTable, FullName, CaseInsensitiveIndex); /* Find out how many names there are */ NameCount = ComputeUnicodeNameLength(FullName); /* Find the right spot where to start looking for this entry */ PreviousEntry = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable; CurrentEntry = PreviousEntry->NextPrefixTree; while (CurrentEntry->NameLength > (CSHORT)NameCount) { /* Not a match, move to the next entry */ PreviousEntry = CurrentEntry; CurrentEntry = CurrentEntry->NextPrefixTree; } /* Loop every entry which has valid entries */ while (CurrentEntry->NameLength) { /* Get the splay links and loop */ SplayLinks = &CurrentEntry->Links; while (SplayLinks) { /* Get the entry */ Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); /* Do the comparison */ Result = CompareUnicodeStrings(Entry->Prefix, FullName, 0); if (Result == GenericGreaterThan) { /* Prefix is greater, so restart on the left child */ SplayLinks = RtlLeftChild(SplayLinks); continue; } else if (Result == GenericLessThan) { /* Prefix is smaller, so restart on the right child */ SplayLinks = RtlRightChild(SplayLinks); continue; } /* * We have a match, check if this was a case-sensitive search * NOTE: An index of 0 means case-insensitive(ie, we'll be case * insensitive since index 0, ie, all the time) */ if (!CaseInsensitiveIndex) { /* * Check if this entry was a child. We need to return the root, * so if this entry was a child, we'll splay the tree and get * the root, and set the current entry as a child. */ if (Entry->NodeTypeCode == PFX_NTC_CHILD) { /* Get the next entry */ NextEntry = CurrentEntry->NextPrefixTree; /* Make the current entry become a child */ CurrentEntry->NodeTypeCode = PFX_NTC_CHILD; CurrentEntry->NextPrefixTree = NULL; /* Splay the tree */ SplayLinks = RtlSplay(&Entry->Links); /* Get the new root entry */ Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); /* Set it as a root entry */ Entry->NodeTypeCode = PFX_NTC_ROOT; /* Add it to the root entries list */ PreviousEntry->NextPrefixTree = Entry; Entry->NextPrefixTree = NextEntry; } /* Return the entry */ return Entry; } /* We'll do a case-sensitive search if we've reached this point */ NextEntry = Entry; do { /* Do the case-sensitive search */ Result = CompareUnicodeStrings(NextEntry->Prefix, FullName, CaseInsensitiveIndex); if ((Result != GenericLessThan) && (Result != GenericGreaterThan)) { /* This is a positive match, return it */ return NextEntry; } /* No match yet, continue looping the circular list */ NextEntry = NextEntry->CaseMatch; } while (NextEntry != Entry); /* * If we got here, then we found a non-case-sensitive match, but * we need to find a case-sensitive match, so we'll just keep * searching the next tree (NOTE: we need to break out for this). */ break; } /* Splay links exhausted, move to next entry */ PreviousEntry = CurrentEntry; CurrentEntry = CurrentEntry->NextPrefixTree; } /* If we got here, nothing was found */ return NULL; } /* * @implemented */ VOID NTAPI RtlInitializeUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable) { DPRINT("RtlInitializeUnicodePrefix(): Table %p\n", PrefixTable); /* Setup the table */ PrefixTable->NameLength = 0; PrefixTable->LastNextEntry = NULL; PrefixTable->NodeTypeCode = PFX_NTC_TABLE; PrefixTable->NextPrefixTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable; } /* * @implemented */ BOOLEAN NTAPI RtlInsertUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable, PUNICODE_STRING Prefix, PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry) { PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry, PreviousEntry, Entry, NextEntry; ULONG NameCount; RTL_GENERIC_COMPARE_RESULTS Result; PRTL_SPLAY_LINKS SplayLinks; DPRINT("RtlInsertUnicodePrefix(): Table %p, Prefix %wZ, " "TableEntry %p\n", PrefixTable, Prefix, PrefixTableEntry); /* Find out how many names there are */ NameCount = ComputeUnicodeNameLength(Prefix); /* Set up the initial entry data */ PrefixTableEntry->NameLength = (CSHORT)NameCount; PrefixTableEntry->Prefix = Prefix; RtlInitializeSplayLinks(&PrefixTableEntry->Links); /* Find the right spot where to insert this entry */ PreviousEntry = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable; CurrentEntry = PreviousEntry->NextPrefixTree; while (CurrentEntry->NameLength > (CSHORT)NameCount) { /* Not a match, move to the next entry */ PreviousEntry = CurrentEntry; CurrentEntry = CurrentEntry->NextPrefixTree; } /* Check if we did find a tree by now */ if (CurrentEntry->NameLength != (CSHORT)NameCount) { /* We didn't, so insert a new entry in the list */ PreviousEntry->NextPrefixTree = PrefixTableEntry; PrefixTableEntry->NextPrefixTree = CurrentEntry; /* This is now a root entry with case match */ PrefixTableEntry->NodeTypeCode = PFX_NTC_ROOT; PrefixTableEntry->CaseMatch = PrefixTableEntry; /* Quick return */ return TRUE; } /* We found a tree, so start the search loop */ Entry = CurrentEntry; while (TRUE) { /* Do a case-insensitive comparison to find out the match level */ Result = CompareUnicodeStrings(Entry->Prefix, Prefix, 0); if (Result == GenericEqual) { /* We have a match, start doing a case-sensitive search */ NextEntry = Entry; /* Search the circular case-match list */ do { /* Check if we found a match */ if (CompareUnicodeStrings(NextEntry->Prefix, Prefix, -1) == (GenericEqual)) { /* We must fail the insert: it already exists */ return FALSE; } /* Move to the next entry in the circular list */ NextEntry = NextEntry->CaseMatch; } while (NextEntry != Entry); /* * No match found, so we can safely insert it. Remember that a * case insensitive match was found, so this is not a ROOT NTC * but a Case Match NTC instead. */ PrefixTableEntry->NodeTypeCode = PFX_NTC_CASE_MATCH; PrefixTableEntry->NextPrefixTree = NULL; /* Insert it into the circular list */ PrefixTableEntry->CaseMatch = Entry->CaseMatch; Entry->CaseMatch = PrefixTableEntry; break; } /* Check if the result was greater or lesser than */ if (Result == GenericGreaterThan) { /* Check out if we have a left child */ if (RtlLeftChild(&Entry->Links)) { /* We do, enter it and restart the loop */ SplayLinks = RtlLeftChild(&Entry->Links); Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); } else { /* We don't, set this entry as a child */ PrefixTableEntry->NodeTypeCode = PFX_NTC_CHILD; PrefixTableEntry->NextPrefixTree = NULL; PrefixTableEntry->CaseMatch = PrefixTableEntry; /* Insert us into the tree */ RtlInsertAsLeftChild(&Entry->Links, &PrefixTableEntry->Links); break; } } else { /* Check out if we have a right child */ if (RtlRightChild(&Entry->Links)) { /* We do, enter it and restart the loop */ SplayLinks = RtlRightChild(&Entry->Links); Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); } else { /* We don't, set this entry as a child */ PrefixTableEntry->NodeTypeCode = PFX_NTC_CHILD; PrefixTableEntry->NextPrefixTree = NULL; PrefixTableEntry->CaseMatch = PrefixTableEntry; /* Insert us into the tree */ RtlInsertAsRightChild(&Entry->Links, &PrefixTableEntry->Links); break; } } } /* Get the next tree entry */ NextEntry = CurrentEntry->NextPrefixTree; /* Set what was the current entry to a child entry */ CurrentEntry->NodeTypeCode = PFX_NTC_CHILD; CurrentEntry->NextPrefixTree = NULL; /* Splay the tree */ SplayLinks = RtlSplay(&Entry->Links); /* The link points to the root, get it */ Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); /* Mark the root as a root entry */ Entry->NodeTypeCode = PFX_NTC_ROOT; /* Add it to the tree list */ PreviousEntry->NextPrefixTree = Entry; Entry->NextPrefixTree = NextEntry; /* Return success */ return TRUE; } /* * @implemented */ PUNICODE_PREFIX_TABLE_ENTRY NTAPI RtlNextUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable, BOOLEAN Restart) { PRTL_SPLAY_LINKS SplayLinks; PUNICODE_PREFIX_TABLE_ENTRY Entry, CaseMatchEntry = NULL; DPRINT("RtlNextUnicodePrefix(): Table %p Restart %u\n", PrefixTable, Restart); /* We might need this entry 2/3rd of the time, so cache it now */ if (PrefixTable->LastNextEntry) CaseMatchEntry = PrefixTable->LastNextEntry->CaseMatch; /* Check if this is a restart or if we don't have a last entry */ if ((Restart) || !(PrefixTable->LastNextEntry)) { /* Get the next entry and validate it */ Entry = PrefixTable->NextPrefixTree; if (Entry->NodeTypeCode == PFX_NTC_TABLE) return NULL; /* Now get the Splay Tree Links */ SplayLinks = &Entry->Links; /* Loop until we get the first node in the tree */ while (RtlLeftChild(SplayLinks)) SplayLinks = RtlLeftChild(SplayLinks); /* Get the entry from it */ Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); } else if (CaseMatchEntry->NodeTypeCode == PFX_NTC_CASE_MATCH) { /* If the last entry was a Case Match, then return it */ Entry = CaseMatchEntry; } else { /* Find the successor */ SplayLinks = RtlRealSuccessor(&CaseMatchEntry->Links); if (!SplayLinks) { /* Didn't find one, we'll have to search the tree */ SplayLinks = &PrefixTable->LastNextEntry->Links; /* Get the topmost node (root) */ while (!RtlIsRoot(SplayLinks)) SplayLinks = RtlParent(SplayLinks); Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); /* Get its tree and make sure somethign is in it */ Entry = Entry->NextPrefixTree; if (Entry->NameLength <= 0) return NULL; /* Select these new links and find the first node */ while (RtlLeftChild(SplayLinks)) SplayLinks = RtlLeftChild(SplayLinks); } /* Get the entry from it */ Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); } /* Save this entry as the last one returned, and return it */ PrefixTable->LastNextEntry = Entry; return Entry; } /* * @implemented */ VOID NTAPI RtlRemoveUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable, PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry) { PUNICODE_PREFIX_TABLE_ENTRY Entry, RefEntry, NewEntry; PRTL_SPLAY_LINKS SplayLinks; DPRINT("RtlRemoveUnicodePrefix(): Table %p, TableEntry %p\n", PrefixTable, PrefixTableEntry); /* Erase the last entry */ PrefixTable->LastNextEntry = NULL; /* Check if this was a Case Match Entry */ if (PrefixTableEntry->NodeTypeCode == PFX_NTC_CASE_MATCH) { /* Get the case match entry */ Entry = PrefixTableEntry->CaseMatch; /* Now loop until we find one referencing what the caller sent */ while (Entry->CaseMatch != PrefixTableEntry) Entry = Entry->CaseMatch; /* We found the entry that was sent, link them to delete this entry */ Entry->CaseMatch = PrefixTableEntry->CaseMatch; } else if ((PrefixTableEntry->NodeTypeCode == PFX_NTC_ROOT) || (PrefixTableEntry->NodeTypeCode == PFX_NTC_CHILD)) { /* Check if this entry is a case match */ if (PrefixTableEntry->CaseMatch != PrefixTableEntry) { /* Get the case match entry */ Entry = PrefixTableEntry->CaseMatch; /* Now loop until we find one referencing what the caller sent */ while (Entry->CaseMatch != PrefixTableEntry) Entry = Entry->CaseMatch; /* We found the entry that was sent, link them to delete this entry */ Entry->CaseMatch = PrefixTableEntry->CaseMatch; /* Copy the data */ Entry->NodeTypeCode = PrefixTableEntry->NodeTypeCode; Entry->NextPrefixTree = PrefixTableEntry->NextPrefixTree; Entry->Links = PrefixTableEntry->Links; /* Now check if we are a root entry */ if (RtlIsRoot(&PrefixTableEntry->Links)) { /* We are, so make this entry root as well */ Entry->Links.Parent = &Entry->Links; /* Find the entry referencing us */ RefEntry = Entry->NextPrefixTree; while (RefEntry->NextPrefixTree != Entry) { /* Not this one, move to the next entry */ RefEntry = RefEntry->NextPrefixTree; } /* Link them to us now */ RefEntry->NextPrefixTree = Entry; } else if (RtlIsLeftChild(&PrefixTableEntry->Links)) { /* We were the left child, so make us as well */ RtlParent(&PrefixTableEntry->Links)->LeftChild = &Entry->Links; } else { /* We were the right child, so make us as well */ RtlParent(&PrefixTableEntry->Links)->RightChild = &Entry->Links; } /* Check if we have a left child */ if (RtlLeftChild(&Entry->Links)) { /* Update its parent link */ RtlLeftChild(&Entry->Links)->Parent = &Entry->Links; } /* Check if we have a right child */ if (RtlRightChild(&Entry->Links)) { /* Update its parent link */ RtlRightChild(&Entry->Links)->Parent = &Entry->Links; } } else { /* It's not a case match, so we'll delete the actual entry */ SplayLinks = &PrefixTableEntry->Links; /* Find the root entry */ while (!RtlIsRoot(SplayLinks)) SplayLinks = RtlParent(SplayLinks); Entry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); /* Delete the entry and check if the whole tree is gone */ SplayLinks = RtlDelete(&PrefixTableEntry->Links); if (!SplayLinks) { /* The tree is also gone now, find the entry referencing us */ RefEntry = Entry->NextPrefixTree; while (RefEntry->NextPrefixTree != Entry) { /* Not this one, move to the next entry */ RefEntry = RefEntry->NextPrefixTree; } /* Link them so this entry stops being referenced */ RefEntry->NextPrefixTree = Entry->NextPrefixTree; } else if (&Entry->Links != SplayLinks) { /* The tree is still here, but we got moved to a new one */ NewEntry = CONTAINING_RECORD(SplayLinks, UNICODE_PREFIX_TABLE_ENTRY, Links); /* Find the entry referencing us */ RefEntry = Entry->NextPrefixTree; while (RefEntry->NextPrefixTree != Entry) { /* Not this one, move to the next entry */ RefEntry = RefEntry->NextPrefixTree; } /* Since we got moved, make us the new root entry */ NewEntry->NodeTypeCode = PFX_NTC_ROOT; /* Link us with the entry referencing the old root */ RefEntry->NextPrefixTree = NewEntry; /* And link us with the old tree */ NewEntry->NextPrefixTree = Entry->NextPrefixTree; /* Set the old tree as a child */ Entry->NodeTypeCode = PFX_NTC_CHILD; Entry->NextPrefixTree = NULL; } } } } /* EOF */