2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * PURPOSE: Unicode Prefix implementation
5 * FILE: lib/rtl/unicodeprefix.c
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
9 /* INCLUDES *****************************************************************/
17 * FIXME: Try to find the official names and add to NDK
18 * Definitions come from fastfat driver.
20 typedef USHORT NODE_TYPE_CODE
;
21 #define PFX_NTC_TABLE ((NODE_TYPE_CODE)0x0800)
22 #define PFX_NTC_ROOT ((NODE_TYPE_CODE)0x0801)
23 #define PFX_NTC_CHILD ((NODE_TYPE_CODE)0x0802)
24 #define PFX_NTC_CASE_MATCH ((NODE_TYPE_CODE)0x0803)
26 /* FUNCTIONS ***************************************************************/
31 ComputeUnicodeNameLength(IN PUNICODE_STRING UnicodeName
)
33 ULONG Chars
= UnicodeName
->Length
/ sizeof(WCHAR
);
34 ULONG i
, NamesFound
= 1;
37 for (i
= 0; i
< (Chars
- 1); i
++)
39 /* Check if we found a backslash, meaning another name */
40 if (UnicodeName
->Buffer
[i
] == '\\') NamesFound
++;
43 /* Return the number of names found */
49 RTL_GENERIC_COMPARE_RESULTS
51 CompareUnicodeStrings(IN PUNICODE_STRING Prefix
,
52 IN PUNICODE_STRING String
,
53 IN ULONG CaseCheckChar
)
55 ULONG StringLength
= String
->Length
/ sizeof(WCHAR
);
56 ULONG PrefixLength
= Prefix
->Length
/ sizeof(WCHAR
);
57 ULONG ScanLength
= min(StringLength
, PrefixLength
);
59 WCHAR FoundPrefix
, FoundString
;
61 DPRINT("CompareUnicodeStrings: %wZ %wZ\n", String
, Prefix
);
63 /* Handle case noticed in npfs when Prefix = '\' and name starts with '\' */
64 if ((PrefixLength
== 1) &&
65 (Prefix
->Buffer
[0] == '\\') &&
67 (String
->Buffer
[0] == '\\'))
69 /* The string is actually a prefix */
73 /* Validate the Case Check Character Position */
74 if (CaseCheckChar
> ScanLength
) CaseCheckChar
= ScanLength
;
76 /* Do the case sensitive comparison first */
77 for (i
= 0; i
< CaseCheckChar
; i
++)
79 /* Compare the two characters */
80 if (Prefix
->Buffer
[i
] != String
->Buffer
[i
]) break;
83 /* Save the characters we found */
84 FoundPrefix
= Prefix
->Buffer
[i
];
85 FoundString
= String
->Buffer
[i
];
87 /* Check if we exausted the search above */
88 if (i
== CaseCheckChar
)
90 /* Do a case-insensitive search */
91 p
= &Prefix
->Buffer
[i
];
92 p1
= &String
->Buffer
[i
];
95 /* Move to the next character */
100 if (FoundPrefix
!= FoundString
)
102 /* Upcase the characters */
103 FoundPrefix
= RtlUpcaseUnicodeChar(FoundPrefix
);
104 FoundString
= RtlUpcaseUnicodeChar(FoundString
);
106 /* Compare them again */
107 if (FoundPrefix
!= FoundString
) break;
110 /* Move to the next char */
112 } while (i
< ScanLength
);
115 /* Check if we weren't able to find a match in the loops */
118 /* If the prefix character found was a backslash, this is a less */
119 if (FoundPrefix
== '\\') return GenericLessThan
;
121 /* If the string character found was a backslack, then this is a more */
122 if (FoundString
== '\\') return GenericGreaterThan
;
124 /* None of those two special cases, do a normal check */
125 if (FoundPrefix
< FoundString
) return GenericLessThan
;
127 /* The only choice left is that Prefix > String */
128 return GenericGreaterThan
;
131 /* If we got here, a match was found. Check if the prefix is smaller */
132 if (PrefixLength
< StringLength
)
134 /* Check if the string is actually a prefix */
135 if (String
->Buffer
[PrefixLength
] == '\\') return -1;
137 /* It's not a prefix, and it's shorter, so it's a less */
138 return GenericLessThan
;
141 /* Check if the prefix is longer */
142 if (PrefixLength
> StringLength
) return GenericGreaterThan
;
144 /* If we got here, then they are 100% equal */
151 PUNICODE_PREFIX_TABLE_ENTRY
153 RtlFindUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
154 PUNICODE_STRING FullName
,
155 ULONG CaseInsensitiveIndex
)
158 PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry
, PreviousEntry
, Entry
, NextEntry
;
159 PRTL_SPLAY_LINKS SplayLinks
;
160 RTL_GENERIC_COMPARE_RESULTS Result
;
161 DPRINT("RtlFindUnicodePrefix\n");
163 /* Find out how many names there are */
164 NameCount
= ComputeUnicodeNameLength(FullName
);
166 /* Find the right spot where to start looking for this entry */
167 PreviousEntry
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
168 CurrentEntry
= PreviousEntry
->NextPrefixTree
;
169 while (CurrentEntry
->NameLength
> NameCount
)
171 /* Not a match, move to the next entry */
172 PreviousEntry
= CurrentEntry
;
173 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
176 /* Loop every entry which has valid entries */
177 while (CurrentEntry
->NameLength
)
179 DPRINT("CurrentEntry->NameLength %lx\n", CurrentEntry
->NameLength
);
181 /* Get the splay links and loop */
182 SplayLinks
= &CurrentEntry
->Links
;
186 DPRINT("SplayLinks %p\n", SplayLinks
);
187 Entry
= CONTAINING_RECORD(SplayLinks
,
188 UNICODE_PREFIX_TABLE_ENTRY
,
191 /* Do the comparison */
192 Result
= CompareUnicodeStrings(Entry
->Prefix
, FullName
, 0);
193 DPRINT("Result %lx\n", Result
);
194 if (Result
== GenericGreaterThan
)
196 /* Prefix is greater, so restart on the left child */
197 SplayLinks
= RtlLeftChild(SplayLinks
);
200 else if (Result
== GenericLessThan
)
202 /* Prefix is smaller, so restart on the right child */
203 SplayLinks
= RtlRightChild(SplayLinks
);
208 * We have a match, check if this was a case-sensitive search
209 * NOTE: An index of 0 means case-insensitive(ie, we'll be case
210 * insensitive since index 0, ie, all the time)
212 DPRINT("CaseInsensitiveIndex %lx\n", CaseInsensitiveIndex
);
213 if (!CaseInsensitiveIndex
)
216 * Check if this entry was a child. We need to return the root,
217 * so if this entry was a child, we'll splay the tree and get
218 * the root, and set the current entry as a child.
220 if (Entry
->NodeTypeCode
== PFX_NTC_CHILD
)
222 /* Get the next entry */
223 NextEntry
= CurrentEntry
->NextPrefixTree
;
225 /* Make the current entry become a child */
226 CurrentEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
227 CurrentEntry
->NextPrefixTree
= NULL
;
230 SplayLinks
= RtlSplay(&Entry
->Links
);
232 /* Get the new root entry */
233 Entry
= CONTAINING_RECORD(SplayLinks
,
234 UNICODE_PREFIX_TABLE_ENTRY
,
237 /* Set it as a root entry */
238 Entry
->NodeTypeCode
= PFX_NTC_ROOT
;
240 /* Add it to the root entries list */
241 PreviousEntry
->NextPrefixTree
= Entry
;
242 Entry
->NextPrefixTree
= NextEntry
;
245 /* Return the entry */
246 DPRINT("RtlFindUnicodePrefix: %p\n", Entry
);
250 /* We'll do a case-sensitive search if we've reached this point */
254 /* Do the case-sensitive search */
255 Result
= CompareUnicodeStrings(NextEntry
->Prefix
,
257 CaseInsensitiveIndex
);
258 if ((Result
!= GenericLessThan
) &&
259 (Result
!= GenericGreaterThan
))
261 /* This is a positive match, return it */
262 DPRINT("RtlFindUnicodePrefix: %p\n", NextEntry
);
266 /* No match yet, continue looping the circular list */
267 NextEntry
= NextEntry
->CaseMatch
;
268 DPRINT("NextEntry %p\n", NextEntry
);
269 } while (NextEntry
!= Entry
);
272 * If we got here, then we found a non-case-sensitive match, but
273 * we need to find a case-sensitive match, so we'll just keep
274 * searching the next tree (NOTE: we need to break out for this).
279 /* Splay links exausted, move to next entry */
280 PreviousEntry
= CurrentEntry
;
281 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
282 DPRINT("CurrentEntry %p\n", CurrentEntry
);
285 /* If we got here, nothing was found */
286 DPRINT("RtlFindUnicodePrefix: %p\n", NULL
);
295 RtlInitializeUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
)
297 /* Setup the table */
298 PrefixTable
->NameLength
= 0;
299 PrefixTable
->LastNextEntry
= NULL
;
300 PrefixTable
->NodeTypeCode
= PFX_NTC_TABLE
;
301 PrefixTable
->NextPrefixTree
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
309 RtlInsertUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
310 PUNICODE_STRING Prefix
,
311 PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
)
313 PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry
, PreviousEntry
, Entry
, NextEntry
;
315 RTL_GENERIC_COMPARE_RESULTS Result
;
316 PRTL_SPLAY_LINKS SplayLinks
;
317 DPRINT("RtlInsertUnicodePrefix\n");
319 /* Find out how many names there are */
320 NameCount
= ComputeUnicodeNameLength(Prefix
);
322 /* Set up the initial entry data */
323 PrefixTableEntry
->NameLength
= NameCount
;
324 PrefixTableEntry
->Prefix
= Prefix
;
325 RtlInitializeSplayLinks(&PrefixTableEntry
->Links
);
327 /* Find the right spot where to insert this entry */
328 PreviousEntry
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
329 CurrentEntry
= PreviousEntry
->NextPrefixTree
;
330 while (CurrentEntry
->NameLength
> NameCount
)
332 /* Not a match, move to the next entry */
333 PreviousEntry
= CurrentEntry
;
334 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
337 /* Check if we did find a tree by now */
338 if (CurrentEntry
->NameLength
!= NameCount
)
340 /* We didn't, so insert a new entry in the list */
341 PreviousEntry
->NextPrefixTree
= PrefixTableEntry
;
342 PrefixTableEntry
->NextPrefixTree
= CurrentEntry
;
344 /* This is now a root entry with case match */
345 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_ROOT
;
346 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
349 DPRINT("RtlInsertUnicodePrefix TRUE\n");
353 /* We found a tree, so star thte search loop */
354 Entry
= CurrentEntry
;
357 /* Do a case-insensitive comparison to find out the match level */
358 Result
= CompareUnicodeStrings(Entry
->Prefix
, Prefix
, 0);
359 if (Result
== GenericEqual
)
361 /* We have a match, start doing a case-sensitive search */
364 /* Search the circular case-match list */
367 /* Check if we found a match */
368 if (CompareUnicodeStrings(NextEntry
->Prefix
, Prefix
, -1) ==
371 /* We must fail the insert: it already exists */
372 DPRINT("RtlInsertUnicodePrefix FALSE\n");
376 /* Move to the next entry in the circular list */
377 NextEntry
= NextEntry
->CaseMatch
;
379 while (NextEntry
!= Entry
);
382 * No match found, so we can safely insert it. Remember that a
383 * case insensitive match was found, so this is not a ROOT NTC
384 * but a Case Match NTC instead.
386 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CASE_MATCH
;
387 PrefixTableEntry
->NextPrefixTree
= NULL
;
389 /* Insert it into the circular list */
390 PrefixTableEntry
->CaseMatch
= Entry
->CaseMatch
;
391 Entry
->CaseMatch
= PrefixTableEntry
;
395 /* Check if the result was greater or lesser than */
396 if (Result
== GenericGreaterThan
)
398 /* Check out if we have a left child */
399 if (RtlLeftChild(&Entry
->Links
))
401 /* We do, enter it and restart the loop */
402 SplayLinks
= RtlLeftChild(&Entry
->Links
);
403 Entry
= CONTAINING_RECORD(SplayLinks
,
404 UNICODE_PREFIX_TABLE_ENTRY
,
409 /* We don't, set this entry as a child */
410 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
411 PrefixTableEntry
->NextPrefixTree
= NULL
;
412 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
414 /* Insert us into the tree */
415 RtlInsertAsLeftChild(&Entry
->Links
, &PrefixTableEntry
->Links
);
421 /* Check out if we have a right child */
422 if (RtlRightChild(&Entry
->Links
))
424 /* We do, enter it and restart the loop */
425 SplayLinks
= RtlLeftChild(&Entry
->Links
);
426 Entry
= CONTAINING_RECORD(SplayLinks
,
427 UNICODE_PREFIX_TABLE_ENTRY
,
432 /* We don't, set this entry as a child */
433 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
434 PrefixTableEntry
->NextPrefixTree
= NULL
;
435 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
437 /* Insert us into the tree */
438 RtlInsertAsRightChild(&Entry
->Links
, &PrefixTableEntry
->Links
);
444 /* Get the next tree entry */
445 NextEntry
= CurrentEntry
->NextPrefixTree
;
447 /* Set what was the current entry to a child entry */
448 CurrentEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
449 CurrentEntry
->NextPrefixTree
= NULL
;
452 SplayLinks
= RtlSplay(&Entry
->Links
);
454 /* The link points to the root, get it */
455 Entry
= CONTAINING_RECORD(SplayLinks
, UNICODE_PREFIX_TABLE_ENTRY
, Links
);
457 /* Mark the root as a root entry */
458 Entry
->NodeTypeCode
= PFX_NTC_ROOT
;
460 /* Add it to the tree list */
461 PreviousEntry
->NextPrefixTree
= Entry
;
462 Entry
->NextPrefixTree
= NextEntry
;
465 DPRINT("RtlInsertUnicodePrefix TRUE\n");
472 PUNICODE_PREFIX_TABLE_ENTRY
474 RtlNextUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
477 PRTL_SPLAY_LINKS SplayLinks
;
478 PUNICODE_PREFIX_TABLE_ENTRY Entry
, CaseMatchEntry
;
479 DPRINT("RtlNextUnicodePrefix\n");
481 /* We might need this entry 2/3rd of the time, so cache it now */
482 CaseMatchEntry
= PrefixTable
->LastNextEntry
->CaseMatch
;
484 /* Check if this is a restart or if we don't have a last entry */
485 if ((Restart
) || !(PrefixTable
->LastNextEntry
))
487 /* Get the next entry and validate it */
488 Entry
= PrefixTable
->NextPrefixTree
;
489 if (Entry
->NodeTypeCode
== PFX_NTC_TABLE
) return NULL
;
491 /* Now get the Splay Tree Links */
492 SplayLinks
= &Entry
->Links
;
494 /* Loop until we get the first node in the tree */
495 while (RtlLeftChild(SplayLinks
)) SplayLinks
= RtlLeftChild(SplayLinks
);
497 /* Get the entry from it */
498 Entry
= CONTAINING_RECORD(SplayLinks
,
499 UNICODE_PREFIX_TABLE_ENTRY
,
502 else if (CaseMatchEntry
->NodeTypeCode
== PFX_NTC_CASE_MATCH
)
504 /* If the last entry was a Case Match, then return it */
505 Entry
= CaseMatchEntry
;
509 /* Find the successor */
510 SplayLinks
= RtlRealSuccessor(&CaseMatchEntry
->Links
);
513 /* Didn't find one, we'll have to search the tree */
514 SplayLinks
= &PrefixTable
->LastNextEntry
->Links
;
516 /* Get the topmost node (root) */
517 while (!RtlIsRoot(SplayLinks
)) SplayLinks
= RtlParent(SplayLinks
);
518 Entry
= CONTAINING_RECORD(SplayLinks
,
519 UNICODE_PREFIX_TABLE_ENTRY
,
522 /* Get its tree and make sure somethign is in it */
523 Entry
= Entry
->NextPrefixTree
;
524 if (Entry
->NameLength
<= 0) return NULL
;
526 /* Select these new links and find the first node */
527 while (RtlLeftChild(SplayLinks
)) SplayLinks
= RtlLeftChild(SplayLinks
);
530 /* Get the entry from it */
531 Entry
= CONTAINING_RECORD(SplayLinks
,
532 UNICODE_PREFIX_TABLE_ENTRY
,
536 /* Save this entry as the last one returned, and return it */
537 PrefixTable
->LastNextEntry
= Entry
;
538 DPRINT("RtlNextUnicodePrefix: %p\n", Entry
);
547 RtlRemoveUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
548 PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
)
550 PUNICODE_PREFIX_TABLE_ENTRY Entry
, RefEntry
, NewEntry
;
551 PRTL_SPLAY_LINKS SplayLinks
;
552 DPRINT("RtlRemoveUnicodePrefix\n");
554 /* Erase the last entry */
555 PrefixTable
->LastNextEntry
= NULL
;
557 /* Check if this was a Case Match Entry */
558 if (PrefixTableEntry
->NodeTypeCode
== PFX_NTC_CASE_MATCH
)
560 /* Get the case match entry */
561 Entry
= PrefixTableEntry
->CaseMatch
;
563 /* Now loop until we find one referencing what the caller sent */
564 while (Entry
->CaseMatch
!= PrefixTableEntry
) Entry
= Entry
->CaseMatch
;
566 /* We found the entry that was sent, link them to delete this entry */
567 Entry
->CaseMatch
= PrefixTableEntry
->CaseMatch
;
569 else if ((PrefixTableEntry
->NodeTypeCode
== PFX_NTC_ROOT
) ||
570 (PrefixTableEntry
->NodeTypeCode
== PFX_NTC_CHILD
))
572 /* Check if this entry is a case match */
573 if (PrefixTableEntry
->CaseMatch
!= PrefixTableEntry
)
575 /* Get the case match entry */
576 Entry
= PrefixTableEntry
->CaseMatch
;
578 /* Now loop until we find one referencing what the caller sent */
579 while (Entry
->CaseMatch
!= PrefixTableEntry
) Entry
= Entry
->CaseMatch
;
581 /* We found the entry that was sent, link them to delete this entry */
582 Entry
->CaseMatch
= PrefixTableEntry
->CaseMatch
;
585 Entry
->NodeTypeCode
= PrefixTableEntry
->NodeTypeCode
;
586 Entry
->NextPrefixTree
= PrefixTableEntry
->NextPrefixTree
;
587 Entry
->Links
= PrefixTableEntry
->Links
;
589 /* Now check if we are a root entry */
590 if (RtlIsRoot(&PrefixTableEntry
->Links
))
592 /* We are, so make this entry root as well */
593 Entry
->Links
.Parent
= &Entry
->Links
;
595 /* Find the entry referencing us */
596 RefEntry
= Entry
->NextPrefixTree
;
597 while (RefEntry
->NextPrefixTree
!= Entry
)
599 /* Not this one, move to the next entry */
600 RefEntry
= RefEntry
->NextPrefixTree
;
603 /* Link them to us now */
604 RefEntry
->NextPrefixTree
= Entry
;
606 else if (RtlIsLeftChild(&PrefixTableEntry
->Links
))
608 /* We were the left child, so make us as well */
609 RtlParent(&PrefixTableEntry
->Links
)->LeftChild
= &Entry
->Links
;
613 /* We were the right child, so make us as well */
614 RtlParent(&PrefixTableEntry
->Links
)->RightChild
= &Entry
->Links
;
617 /* Check if we have a left child */
618 if (RtlLeftChild(&Entry
->Links
))
620 /* Update its parent link */
621 RtlLeftChild(&Entry
->Links
)->Parent
= &Entry
->Links
;
623 /* Check if we have a right child */
624 if (RtlRightChild(&Entry
->Links
))
626 /* Update its parent link */
627 RtlRightChild(&Entry
->Links
)->Parent
= &Entry
->Links
;
632 /* It's not a case match, so we'll delete the actual entry */
633 SplayLinks
= &PrefixTableEntry
->Links
;
635 /* Find the root entry */
636 while (!RtlIsRoot(SplayLinks
)) SplayLinks
= RtlParent(SplayLinks
);
637 Entry
= CONTAINING_RECORD(SplayLinks
,
638 UNICODE_PREFIX_TABLE_ENTRY
,
641 /* Delete the entry and check if the whole tree is gone */
642 SplayLinks
= RtlDelete(&PrefixTableEntry
->Links
);
645 /* The tree is also gone now, find the entry referencing us */
646 RefEntry
= Entry
->NextPrefixTree
;
647 while (RefEntry
->NextPrefixTree
!= Entry
)
649 /* Not this one, move to the next entry */
650 RefEntry
= RefEntry
->NextPrefixTree
;
653 /* Link them so this entry stops being referenced */
654 RefEntry
->NextPrefixTree
= Entry
->NextPrefixTree
;
656 else if (&Entry
->Links
!= SplayLinks
)
658 /* The tree is still here, but we got moved to a new one */
659 NewEntry
= CONTAINING_RECORD(SplayLinks
,
660 UNICODE_PREFIX_TABLE_ENTRY
,
663 /* Find the entry referencing us */
664 RefEntry
= Entry
->NextPrefixTree
;
665 while (RefEntry
->NextPrefixTree
!= Entry
)
667 /* Not this one, move to the next entry */
668 RefEntry
= RefEntry
->NextPrefixTree
;
671 /* Since we got moved, make us the new root entry */
672 NewEntry
->NodeTypeCode
= PFX_NTC_ROOT
;
674 /* Link us with the entry referencing the old root */
675 RefEntry
->NextPrefixTree
= NewEntry
;
677 /* And link us with the old tree */
678 NewEntry
->NextPrefixTree
= Entry
->NextPrefixTree
;
680 /* Set the old tree as a child */
681 Entry
->NodeTypeCode
= PFX_NTC_CHILD
;
682 Entry
->NextPrefixTree
= NULL
;