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
;
61 /* Validate the Case Check Character Position */
62 if (CaseCheckChar
> ScanLength
) CaseCheckChar
= ScanLength
;
64 /* Do the case sensitive comparison first */
65 for (i
= 0; i
< CaseCheckChar
; i
++)
67 /* Compare the two characters */
68 if (Prefix
->Buffer
[i
] != String
->Buffer
[i
]) break;
71 /* Save the characters we found */
72 FoundPrefix
= Prefix
->Buffer
[i
];
73 FoundString
= String
->Buffer
[i
];
75 /* Check if we exausted the search above */
76 if (i
== CaseCheckChar
)
78 /* FIXME: Do a case-insensitive search */
81 /* Check if we weren't able to find a match in the loops */
84 /* If the prefix character found was a backslash, this is a less */
85 if (FoundPrefix
== '\\') return GenericLessThan
;
87 /* If the string character found was a backslack, then this is a more */
88 if (FoundString
== '\\') return GenericGreaterThan
;
90 /* None of those two special cases, do a normal check */
91 if (FoundPrefix
< FoundString
) return GenericLessThan
;
93 /* The only choice left is that Prefix > String */
94 return GenericGreaterThan
;
97 /* If we got here, a match was found. Check if the prefix is smaller */
98 if (PrefixLength
< StringLength
)
100 /* Check if the string is actually a prefix */
101 if (String
->Buffer
[PrefixLength
] == '\\') return -1;
103 /* It's not a prefix, and it's shorter, so it's a less */
104 return GenericLessThan
;
107 /* Check if the prefix is longer */
108 if (PrefixLength
> StringLength
) return GenericGreaterThan
;
110 /* If we got here, then they are 100% equal */
117 PUNICODE_PREFIX_TABLE_ENTRY
119 RtlFindUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
120 PUNICODE_STRING FullName
,
121 ULONG CaseInsensitiveIndex
)
132 RtlInitializeUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
)
134 /* Setup the table */
135 PrefixTable
->NameLength
= 0;
136 PrefixTable
->LastNextEntry
= NULL
;
137 PrefixTable
->NodeTypeCode
= PFX_NTC_TABLE
;
138 PrefixTable
->NextPrefixTree
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
146 RtlInsertUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
147 PUNICODE_STRING Prefix
,
148 PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
)
151 * implementation notes:
152 * - get name length (number of names) DONE
153 * - init splay links DONE
154 * - find a matching tree DONE
155 * - if !found, insert a new NTC_ROOT entry and return TRUE; DONE
156 * - if found, loop tree and compare strings: DONE
157 * if equal, handle casematch/nomatch DONE
158 * if greater or lesser equal, then add left/right childs accordingly
159 * - splay the tree DONE
161 PUNICODE_PREFIX_TABLE_ENTRY CurrentEntry
, PreviousEntry
, Entry
, NextEntry
;
163 RTL_GENERIC_COMPARE_RESULTS Result
;
164 PRTL_SPLAY_LINKS SplayLinks
;
166 /* Find out how many names there are */
167 NameCount
= ComputeUnicodeNameLength(Prefix
);
169 /* Set up the initial entry data */
170 PrefixTableEntry
->NameLength
= NameCount
;
171 PrefixTableEntry
->Prefix
= Prefix
;
172 RtlInitializeSplayLinks(&PrefixTableEntry
->Links
);
174 /* Find the right spot where to insert this entry */
175 PreviousEntry
= (PUNICODE_PREFIX_TABLE_ENTRY
)PrefixTable
;
176 CurrentEntry
= PreviousEntry
->NextPrefixTree
;
177 while (CurrentEntry
->NameLength
> NameCount
)
179 /* Not a match, move to the next entry */
180 PreviousEntry
= CurrentEntry
;
181 CurrentEntry
= CurrentEntry
->NextPrefixTree
;
184 /* Check if we did find a tree by now */
185 if (CurrentEntry
->NameLength
!= NameCount
)
187 /* We didn't, so insert a new entry in the list */
188 PreviousEntry
->NextPrefixTree
= PrefixTableEntry
;
189 PrefixTableEntry
->NextPrefixTree
= CurrentEntry
;
191 /* This is now a root entry with case match */
192 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_ROOT
;
193 PrefixTableEntry
->CaseMatch
= PrefixTableEntry
;
199 /* We found a tree, so star thte search loop */
200 Entry
= CurrentEntry
;
203 /* Do a case-insensitive comparison to find out the match level */
204 Result
= CompareUnicodeStrings(Entry
->Prefix
, Prefix
, 0);
205 if (Result
== GenericEqual
)
207 /* We have a match, start doing a case-sensitive search */
210 /* Search the circular case-match list */
213 /* Check if we found a match */
214 if (CompareUnicodeStrings(NextEntry
->Prefix
, Prefix
, -1) ==
217 /* We must fail the insert: it already exists */
221 /* Move to the next entry in the circular list */
222 NextEntry
= NextEntry
->CaseMatch
;
224 while (NextEntry
!= Entry
);
227 * No match found, so we can safely insert it. Remember that a
228 * case insensitive match was found, so this is not a ROOT NTC
229 * but a Case Match NTC instead.
231 PrefixTableEntry
->NodeTypeCode
= PFX_NTC_CASE_MATCH
;
232 PrefixTableEntry
->NextPrefixTree
= NULL
;
234 /* Insert it into the circular list */
235 PrefixTableEntry
->CaseMatch
= Entry
->CaseMatch
;
236 Entry
->CaseMatch
= PrefixTableEntry
;
238 else if (Result
== GenericGreaterThan
)
240 /* TODO: Check out the left child and add us */
244 /* TODO: Check out the right child and add us */
248 /* Get the next tree entry */
249 NextEntry
= CurrentEntry
->NextPrefixTree
;
251 /* Set what was the current entry to a child entry */
252 CurrentEntry
->NodeTypeCode
= PFX_NTC_CHILD
;
253 CurrentEntry
->NextPrefixTree
= NULL
;
256 SplayLinks
= RtlSplay(&Entry
->Links
);
258 /* The link points to the root, get it */
259 Entry
= CONTAINING_RECORD(SplayLinks
, UNICODE_PREFIX_TABLE_ENTRY
, Links
);
261 /* Mark the root as a root entry */
262 Entry
->NodeTypeCode
= PFX_NTC_ROOT
;
264 /* Add it to the tree list */
265 PreviousEntry
->NextPrefixTree
= Entry
;
266 Entry
->NextPrefixTree
= NextEntry
;
275 PUNICODE_PREFIX_TABLE_ENTRY
277 RtlNextUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
280 PRTL_SPLAY_LINKS SplayLinks
;
281 PUNICODE_PREFIX_TABLE_ENTRY Entry
, CaseMatchEntry
;
283 /* We might need this entry 2/3rd of the time, so cache it now */
284 CaseMatchEntry
= PrefixTable
->LastNextEntry
->CaseMatch
;
286 /* Check if this is a restart or if we don't have a last entry */
287 if ((Restart
) || !(PrefixTable
->LastNextEntry
))
289 /* Get the next entry and validate it */
290 Entry
= PrefixTable
->NextPrefixTree
;
291 if (Entry
->NodeTypeCode
== PFX_NTC_TABLE
) return NULL
;
293 /* Now get the Splay Tree Links */
294 SplayLinks
= &Entry
->Links
;
296 /* Loop until we get the first node in the tree */
297 while (RtlLeftChild(SplayLinks
)) SplayLinks
= RtlLeftChild(SplayLinks
);
299 /* Get the entry from it */
300 Entry
= CONTAINING_RECORD(SplayLinks
,
301 UNICODE_PREFIX_TABLE_ENTRY
,
304 else if (CaseMatchEntry
->NodeTypeCode
== PFX_NTC_CASE_MATCH
)
306 /* If the last entry was a Case Match, then return it */
307 Entry
= CaseMatchEntry
;
311 /* Find the successor */
312 SplayLinks
= RtlRealSuccessor(&CaseMatchEntry
->Links
);
315 /* Didn't find one, we'll have to search the tree */
316 SplayLinks
= &PrefixTable
->LastNextEntry
->Links
;
318 /* Get the topmost node (root) */
319 while (!RtlIsRoot(SplayLinks
)) SplayLinks
= RtlParent(SplayLinks
);
320 Entry
= CONTAINING_RECORD(SplayLinks
,
321 UNICODE_PREFIX_TABLE_ENTRY
,
324 /* Get its tree and make sure somethign is in it */
325 Entry
= Entry
->NextPrefixTree
;
326 if (Entry
->NameLength
<= 0) return NULL
;
328 /* Select these new links and find the first node */
329 while (RtlLeftChild(SplayLinks
)) SplayLinks
= RtlLeftChild(SplayLinks
);
332 /* Get the entry from it */
333 Entry
= CONTAINING_RECORD(SplayLinks
,
334 UNICODE_PREFIX_TABLE_ENTRY
,
338 /* Save this entry as the last one returned, and return it */
339 PrefixTable
->LastNextEntry
= Entry
;
348 RtlRemoveUnicodePrefix(PUNICODE_PREFIX_TABLE PrefixTable
,
349 PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
)
351 PUNICODE_PREFIX_TABLE_ENTRY Entry
, RefEntry
, NewEntry
;
352 PRTL_SPLAY_LINKS SplayLinks
;
354 /* Erase the last entry */
355 PrefixTable
->LastNextEntry
= NULL
;
357 /* Check if this was a Case Match Entry */
358 if (PrefixTableEntry
->NodeTypeCode
== PFX_NTC_CASE_MATCH
)
360 /* Get the case match entry */
361 Entry
= PrefixTableEntry
->CaseMatch
;
363 /* Now loop until we find one referencing what the caller sent */
364 while (Entry
->CaseMatch
!= PrefixTableEntry
) Entry
= Entry
->CaseMatch
;
366 /* We found the entry that was sent, link them to delete this entry */
367 Entry
->CaseMatch
= PrefixTableEntry
->CaseMatch
;
369 else if ((PrefixTableEntry
->NodeTypeCode
== PFX_NTC_ROOT
) ||
370 (PrefixTableEntry
->NodeTypeCode
== PFX_NTC_CHILD
))
372 /* Check if this entry is a case match */
373 if (PrefixTableEntry
->CaseMatch
!= PrefixTableEntry
)
375 /* Get the case match entry */
376 Entry
= PrefixTableEntry
->CaseMatch
;
378 /* Now loop until we find one referencing what the caller sent */
379 while (Entry
->CaseMatch
!= PrefixTableEntry
) Entry
= Entry
->CaseMatch
;
381 /* We found the entry that was sent, link them to delete this entry */
382 Entry
->CaseMatch
= PrefixTableEntry
->CaseMatch
;
385 Entry
->NodeTypeCode
= PrefixTableEntry
->NodeTypeCode
;
386 Entry
->NextPrefixTree
= PrefixTableEntry
->NextPrefixTree
;
387 Entry
->Links
= PrefixTableEntry
->Links
;
389 /* Now check if we are a root entry */
390 if (RtlIsRoot(&PrefixTableEntry
->Links
))
392 /* We are, so make this entry root as well */
393 Entry
->Links
.Parent
= &Entry
->Links
;
395 /* Find the entry referencing us */
396 RefEntry
= Entry
->NextPrefixTree
;
397 while (RefEntry
->NextPrefixTree
!= Entry
)
399 /* Not this one, move to the next entry */
400 RefEntry
= RefEntry
->NextPrefixTree
;
403 /* Link them to us now */
404 RefEntry
->NextPrefixTree
= Entry
;
406 else if (RtlIsLeftChild(&PrefixTableEntry
->Links
))
408 /* We were the left child, so make us as well */
409 PrefixTableEntry
->Links
.LeftChild
= &Entry
->Links
;
413 /* We were the right child, so make us as well */
414 PrefixTableEntry
->Links
.RightChild
= &Entry
->Links
;
417 /* Check if we have a left child */
418 if (RtlLeftChild(&Entry
->Links
))
420 /* Update its parent link */
421 RtlLeftChild(&Entry
->Links
)->Parent
= &Entry
->Links
;
423 /* Check if we have a right child */
424 if (RtlRightChild(&Entry
->Links
))
426 /* Update its parent link */
427 RtlRightChild(&Entry
->Links
)->Parent
= &Entry
->Links
;
432 /* It's not a case match, so we'll delete the actual entry */
433 SplayLinks
= &PrefixTableEntry
->Links
;
435 /* Find the root entry */
436 while (!RtlIsRoot(SplayLinks
)) SplayLinks
= RtlParent(SplayLinks
);
437 Entry
= CONTAINING_RECORD(SplayLinks
,
438 UNICODE_PREFIX_TABLE_ENTRY
,
441 /* Delete the entry and check if the whole tree is gone */
442 SplayLinks
= RtlDelete(&PrefixTableEntry
->Links
);
445 /* The tree is also gone now, find the entry referencing us */
446 RefEntry
= Entry
->NextPrefixTree
;
447 while (RefEntry
->NextPrefixTree
!= Entry
)
449 /* Not this one, move to the next entry */
450 RefEntry
= RefEntry
->NextPrefixTree
;
453 /* Link them so this entry stops being referenced */
454 RefEntry
->NextPrefixTree
= Entry
->NextPrefixTree
;
456 else if (&Entry
->Links
!= SplayLinks
)
458 /* The tree is still here, but we got moved to a new one */
459 NewEntry
= CONTAINING_RECORD(SplayLinks
,
460 UNICODE_PREFIX_TABLE_ENTRY
,
463 /* Find the entry referencing us */
464 RefEntry
= Entry
->NextPrefixTree
;
465 while (RefEntry
->NextPrefixTree
!= Entry
)
467 /* Not this one, move to the next entry */
468 RefEntry
= RefEntry
->NextPrefixTree
;
471 /* Since we got moved, make us the new root entry */
472 NewEntry
->NodeTypeCode
= PFX_NTC_ROOT
;
474 /* Link us with the entry referencing the old root */
475 RefEntry
->NextPrefixTree
= NewEntry
;
477 /* And link us with the old tree */
478 NewEntry
->NextPrefixTree
= Entry
->NextPrefixTree
;
480 /* Set the old tree as a child */
481 Entry
->NodeTypeCode
= PFX_NTC_CHILD
;
482 Entry
->NextPrefixTree
= NULL
;