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 String
,
52 IN PUNICODE_STRING Prefix
,
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
;
62 /* Validate the Case Check Character Position */
63 if (CaseCheckChar
> ScanLength
) CaseCheckChar
= ScanLength
;
65 /* Do the case sensitive comparison first */
66 for (i
= 0; i
< CaseCheckChar
; i
++)
68 /* Compare the two characters */
69 if (Prefix
->Buffer
[i
] != String
->Buffer
[i
]) break;
72 /* Save the characters we found */
73 FoundPrefix
= Prefix
->Buffer
[i
];
74 FoundString
= String
->Buffer
[i
];
76 /* Check if we exausted the search above */
77 if (i
== CaseCheckChar
)
79 /* Do a case-insensitive search */
80 p
= &Prefix
->Buffer
[i
];
81 p1
= &String
->Buffer
[i
];
84 /* Move to the next character */
89 if (FoundPrefix
!= FoundString
)
91 /* Upcase the characters */
92 FoundPrefix
= RtlUpcaseUnicodeChar(FoundPrefix
);
93 FoundString
= RtlUpcaseUnicodeChar(FoundString
);
95 /* Compare them again */
96 if (FoundPrefix
!= FoundString
) break;
99 /* Move to the next char */
101 } while (i
< ScanLength
);
104 /* Check if we weren't able to find a match in the loops */
107 /* If the prefix character found was a backslash, this is a less */
108 if (FoundPrefix
== '\\') return GenericLessThan
;
110 /* If the string character found was a backslack, then this is a more */
111 if (FoundString
== '\\') return GenericGreaterThan
;
113 /* None of those two special cases, do a normal check */
114 if (FoundPrefix
< FoundString
) return GenericLessThan
;
116 /* The only choice left is that Prefix > String */
117 return GenericGreaterThan
;
120 /* If we got here, a match was found. Check if the prefix is smaller */
121 if (PrefixLength
< StringLength
)
123 /* Check if the string is actually a prefix */
124 if (String
->Buffer
[PrefixLength
] == '\\') return -1;
126 /* It's not a prefix, and it's shorter, so it's a less */
127 return GenericLessThan
;
130 /* Check if the prefix is longer */
131 if (PrefixLength
> StringLength
) return GenericGreaterThan
;
133 /* If we got here, then they are 100% equal */
140 PUNICODE_PREFIX_TABLE_ENTRY
142 RtlFindUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
143 PUNICODE_STRING FullName
,
144 ULONG CaseInsensitiveIndex
)
147 PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry
, PreviousEntry
;
148 PRTL_SPLAY_LINKS SplayLinks
;
150 /* Find out how many names there are */
151 NameCount
= ComputeUnicodeNameLength(FullName
);
153 /* Find the right spot where to start looking for this entry */
154 PreviousEntry
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
155 CurrentEntry
= PreviousEntry
->NextPrefixTree
;
156 while (CurrentEntry
->NameLength
> NameCount
)
158 /* Not a match, move to the next entry */
159 PreviousEntry
= CurrentEntry
;
160 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
163 /* Loop every entry which has valid entries */
164 while (CurrentEntry
->NameLength
)
166 /* Get the splay links and loop */
167 while ((SplayLinks
= &CurrentEntry
->Links
))
170 * Implementation notes:
172 * - compare the entry's prefix with the fullname:
173 * if greater: restart on the left child
174 * if lesser: restart on the right child
176 * for caseinsensitive, just return the entry and
177 * splay it and set it as root if it's a child
178 * for casesensitive, loop the circular case match list and
179 * keep comparing for each entry
183 /* Splay links exausted, move to next entry */
184 PreviousEntry
= CurrentEntry
;
185 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
188 /* If we got here, nothing was found */
197 RtlInitializeUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
)
199 /* Setup the table */
200 PrefixTable
->NameLength
= 0;
201 PrefixTable
->LastNextEntry
= NULL
;
202 PrefixTable
->NodeTypeCode
= PFX_NTC_TABLE
;
203 PrefixTable
->NextPrefixTree
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
211 RtlInsertUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
212 PUNICODE_STRING Prefix
,
213 PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
)
215 PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry
, PreviousEntry
, Entry
, NextEntry
;
217 RTL_GENERIC_COMPARE_RESULTS Result
;
218 PRTL_SPLAY_LINKS SplayLinks
;
220 /* Find out how many names there are */
221 NameCount
= ComputeUnicodeNameLength(Prefix
);
223 /* Set up the initial entry data */
224 PrefixTableEntry
->NameLength
= NameCount
;
225 PrefixTableEntry
->Prefix
= Prefix
;
226 RtlInitializeSplayLinks(&PrefixTableEntry
->Links
);
228 /* Find the right spot where to insert this entry */
229 PreviousEntry
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
230 CurrentEntry
= PreviousEntry
->NextPrefixTree
;
231 while (CurrentEntry
->NameLength
> NameCount
)
233 /* Not a match, move to the next entry */
234 PreviousEntry
= CurrentEntry
;
235 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
238 /* Check if we did find a tree by now */
239 if (CurrentEntry
->NameLength
!= NameCount
)
241 /* We didn't, so insert a new entry in the list */
242 PreviousEntry
->NextPrefixTree
= PrefixTableEntry
;
243 PrefixTableEntry
->NextPrefixTree
= CurrentEntry
;
245 /* This is now a root entry with case match */
246 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_ROOT
;
247 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
253 /* We found a tree, so star thte search loop */
254 Entry
= CurrentEntry
;
257 /* Do a case-insensitive comparison to find out the match level */
258 Result
= CompareUnicodeStrings(Entry
->Prefix
, Prefix
, 0);
259 if (Result
== GenericEqual
)
261 /* We have a match, start doing a case-sensitive search */
264 /* Search the circular case-match list */
267 /* Check if we found a match */
268 if (CompareUnicodeStrings(NextEntry
->Prefix
, Prefix
, -1) ==
271 /* We must fail the insert: it already exists */
275 /* Move to the next entry in the circular list */
276 NextEntry
= NextEntry
->CaseMatch
;
278 while (NextEntry
!= Entry
);
281 * No match found, so we can safely insert it. Remember that a
282 * case insensitive match was found, so this is not a ROOT NTC
283 * but a Case Match NTC instead.
285 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CASE_MATCH
;
286 PrefixTableEntry
->NextPrefixTree
= NULL
;
288 /* Insert it into the circular list */
289 PrefixTableEntry
->CaseMatch
= Entry
->CaseMatch
;
290 Entry
->CaseMatch
= PrefixTableEntry
;
292 else if (Result
== GenericGreaterThan
)
294 /* Check out if we have a left child */
295 if (RtlLeftChild(&Entry
->Links
))
297 /* We do, enter it and restart the loop */
298 SplayLinks
= RtlLeftChild(&Entry
->Links
);
299 Entry
= CONTAINING_RECORD(SplayLinks
,
300 UNICODE_PREFIX_TABLE_ENTRY
,
305 /* We don't, set this entry as a child */
306 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
307 PrefixTableEntry
->NextPrefixTree
= NULL
;
308 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
310 /* Insert us into the tree */
311 RtlInsertAsLeftChild(&Entry
->Links
, &PrefixTableEntry
->Links
);
317 /* Check out if we have a right child */
318 if (RtlRightChild(&Entry
->Links
))
320 /* We do, enter it and restart the loop */
321 SplayLinks
= RtlLeftChild(&Entry
->Links
);
322 Entry
= CONTAINING_RECORD(SplayLinks
,
323 UNICODE_PREFIX_TABLE_ENTRY
,
328 /* We don't, set this entry as a child */
329 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
330 PrefixTableEntry
->NextPrefixTree
= NULL
;
331 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
333 /* Insert us into the tree */
334 RtlInsertAsRightChild(&Entry
->Links
, &PrefixTableEntry
->Links
);
340 /* Get the next tree entry */
341 NextEntry
= CurrentEntry
->NextPrefixTree
;
343 /* Set what was the current entry to a child entry */
344 CurrentEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
345 CurrentEntry
->NextPrefixTree
= NULL
;
348 SplayLinks
= RtlSplay(&Entry
->Links
);
350 /* The link points to the root, get it */
351 Entry
= CONTAINING_RECORD(SplayLinks
, UNICODE_PREFIX_TABLE_ENTRY
, Links
);
353 /* Mark the root as a root entry */
354 Entry
->NodeTypeCode
= PFX_NTC_ROOT
;
356 /* Add it to the tree list */
357 PreviousEntry
->NextPrefixTree
= Entry
;
358 Entry
->NextPrefixTree
= NextEntry
;
367 PUNICODE_PREFIX_TABLE_ENTRY
369 RtlNextUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
372 PRTL_SPLAY_LINKS SplayLinks
;
373 PUNICODE_PREFIX_TABLE_ENTRY Entry
, CaseMatchEntry
;
375 /* We might need this entry 2/3rd of the time, so cache it now */
376 CaseMatchEntry
= PrefixTable
->LastNextEntry
->CaseMatch
;
378 /* Check if this is a restart or if we don't have a last entry */
379 if ((Restart
) || !(PrefixTable
->LastNextEntry
))
381 /* Get the next entry and validate it */
382 Entry
= PrefixTable
->NextPrefixTree
;
383 if (Entry
->NodeTypeCode
== PFX_NTC_TABLE
) return NULL
;
385 /* Now get the Splay Tree Links */
386 SplayLinks
= &Entry
->Links
;
388 /* Loop until we get the first node in the tree */
389 while (RtlLeftChild(SplayLinks
)) SplayLinks
= RtlLeftChild(SplayLinks
);
391 /* Get the entry from it */
392 Entry
= CONTAINING_RECORD(SplayLinks
,
393 UNICODE_PREFIX_TABLE_ENTRY
,
396 else if (CaseMatchEntry
->NodeTypeCode
== PFX_NTC_CASE_MATCH
)
398 /* If the last entry was a Case Match, then return it */
399 Entry
= CaseMatchEntry
;
403 /* Find the successor */
404 SplayLinks
= RtlRealSuccessor(&CaseMatchEntry
->Links
);
407 /* Didn't find one, we'll have to search the tree */
408 SplayLinks
= &PrefixTable
->LastNextEntry
->Links
;
410 /* Get the topmost node (root) */
411 while (!RtlIsRoot(SplayLinks
)) SplayLinks
= RtlParent(SplayLinks
);
412 Entry
= CONTAINING_RECORD(SplayLinks
,
413 UNICODE_PREFIX_TABLE_ENTRY
,
416 /* Get its tree and make sure somethign is in it */
417 Entry
= Entry
->NextPrefixTree
;
418 if (Entry
->NameLength
<= 0) return NULL
;
420 /* Select these new links and find the first node */
421 while (RtlLeftChild(SplayLinks
)) SplayLinks
= RtlLeftChild(SplayLinks
);
424 /* Get the entry from it */
425 Entry
= CONTAINING_RECORD(SplayLinks
,
426 UNICODE_PREFIX_TABLE_ENTRY
,
430 /* Save this entry as the last one returned, and return it */
431 PrefixTable
->LastNextEntry
= Entry
;
440 RtlRemoveUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
441 PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
)
443 PUNICODE_PREFIX_TABLE_ENTRY Entry
, RefEntry
, NewEntry
;
444 PRTL_SPLAY_LINKS SplayLinks
;
446 /* Erase the last entry */
447 PrefixTable
->LastNextEntry
= NULL
;
449 /* Check if this was a Case Match Entry */
450 if (PrefixTableEntry
->NodeTypeCode
== PFX_NTC_CASE_MATCH
)
452 /* Get the case match entry */
453 Entry
= PrefixTableEntry
->CaseMatch
;
455 /* Now loop until we find one referencing what the caller sent */
456 while (Entry
->CaseMatch
!= PrefixTableEntry
) Entry
= Entry
->CaseMatch
;
458 /* We found the entry that was sent, link them to delete this entry */
459 Entry
->CaseMatch
= PrefixTableEntry
->CaseMatch
;
461 else if ((PrefixTableEntry
->NodeTypeCode
== PFX_NTC_ROOT
) ||
462 (PrefixTableEntry
->NodeTypeCode
== PFX_NTC_CHILD
))
464 /* Check if this entry is a case match */
465 if (PrefixTableEntry
->CaseMatch
!= PrefixTableEntry
)
467 /* Get the case match entry */
468 Entry
= PrefixTableEntry
->CaseMatch
;
470 /* Now loop until we find one referencing what the caller sent */
471 while (Entry
->CaseMatch
!= PrefixTableEntry
) Entry
= Entry
->CaseMatch
;
473 /* We found the entry that was sent, link them to delete this entry */
474 Entry
->CaseMatch
= PrefixTableEntry
->CaseMatch
;
477 Entry
->NodeTypeCode
= PrefixTableEntry
->NodeTypeCode
;
478 Entry
->NextPrefixTree
= PrefixTableEntry
->NextPrefixTree
;
479 Entry
->Links
= PrefixTableEntry
->Links
;
481 /* Now check if we are a root entry */
482 if (RtlIsRoot(&PrefixTableEntry
->Links
))
484 /* We are, so make this entry root as well */
485 Entry
->Links
.Parent
= &Entry
->Links
;
487 /* Find the entry referencing us */
488 RefEntry
= Entry
->NextPrefixTree
;
489 while (RefEntry
->NextPrefixTree
!= Entry
)
491 /* Not this one, move to the next entry */
492 RefEntry
= RefEntry
->NextPrefixTree
;
495 /* Link them to us now */
496 RefEntry
->NextPrefixTree
= Entry
;
498 else if (RtlIsLeftChild(&PrefixTableEntry
->Links
))
500 /* We were the left child, so make us as well */
501 RtlParent(&PrefixTableEntry
->Links
)->LeftChild
= &Entry
->Links
;
505 /* We were the right child, so make us as well */
506 RtlParent(&PrefixTableEntry
->Links
)->RightChild
= &Entry
->Links
;
509 /* Check if we have a left child */
510 if (RtlLeftChild(&Entry
->Links
))
512 /* Update its parent link */
513 RtlLeftChild(&Entry
->Links
)->Parent
= &Entry
->Links
;
515 /* Check if we have a right child */
516 if (RtlRightChild(&Entry
->Links
))
518 /* Update its parent link */
519 RtlRightChild(&Entry
->Links
)->Parent
= &Entry
->Links
;
524 /* It's not a case match, so we'll delete the actual entry */
525 SplayLinks
= &PrefixTableEntry
->Links
;
527 /* Find the root entry */
528 while (!RtlIsRoot(SplayLinks
)) SplayLinks
= RtlParent(SplayLinks
);
529 Entry
= CONTAINING_RECORD(SplayLinks
,
530 UNICODE_PREFIX_TABLE_ENTRY
,
533 /* Delete the entry and check if the whole tree is gone */
534 SplayLinks
= RtlDelete(&PrefixTableEntry
->Links
);
537 /* The tree is also gone now, find the entry referencing us */
538 RefEntry
= Entry
->NextPrefixTree
;
539 while (RefEntry
->NextPrefixTree
!= Entry
)
541 /* Not this one, move to the next entry */
542 RefEntry
= RefEntry
->NextPrefixTree
;
545 /* Link them so this entry stops being referenced */
546 RefEntry
->NextPrefixTree
= Entry
->NextPrefixTree
;
548 else if (&Entry
->Links
!= SplayLinks
)
550 /* The tree is still here, but we got moved to a new one */
551 NewEntry
= CONTAINING_RECORD(SplayLinks
,
552 UNICODE_PREFIX_TABLE_ENTRY
,
555 /* Find the entry referencing us */
556 RefEntry
= Entry
->NextPrefixTree
;
557 while (RefEntry
->NextPrefixTree
!= Entry
)
559 /* Not this one, move to the next entry */
560 RefEntry
= RefEntry
->NextPrefixTree
;
563 /* Since we got moved, make us the new root entry */
564 NewEntry
->NodeTypeCode
= PFX_NTC_ROOT
;
566 /* Link us with the entry referencing the old root */
567 RefEntry
->NextPrefixTree
= NewEntry
;
569 /* And link us with the old tree */
570 NewEntry
->NextPrefixTree
= Entry
->NextPrefixTree
;
572 /* Set the old tree as a child */
573 Entry
->NodeTypeCode
= PFX_NTC_CHILD
;
574 Entry
->NextPrefixTree
= NULL
;