- Moved ntdll's atom table implementation to rtl, rewrote it to use proper structures...
[reactos.git] / reactos / lib / rtl / atom.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: lib/rtl/atom.c
6 * PURPOSE: Atom managment
7 * PROGRAMMER: Nobody
8 * UPDATE HISTORY:
9 * Created 22/05/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include "rtl.h"
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* PROTOTYPES ****************************************************************/
20
21 extern NTSTATUS RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable);
22 extern VOID RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable);
23 extern BOOLEAN RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable);
24 extern VOID RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable);
25
26 extern BOOLEAN RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable);
27 extern VOID RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable);
28
29 extern PRTL_ATOM_TABLE RtlpAllocAtomTable(ULONG Size);
30 extern VOID RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable);
31 extern PRTL_ATOM_TABLE_ENTRY RtlpAllocAtomTableEntry(ULONG Size);
32 extern VOID RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry);
33
34 extern BOOLEAN RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry);
35 extern VOID RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry);
36 extern PRTL_ATOM_TABLE_ENTRY RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index);
37
38 /* FUNCTIONS *****************************************************************/
39
40 static PRTL_ATOM_TABLE_ENTRY
41 RtlpHashAtomName(IN PRTL_ATOM_TABLE AtomTable,
42 IN PWSTR AtomName,
43 OUT PRTL_ATOM_TABLE_ENTRY **HashLink)
44 {
45 UNICODE_STRING Name;
46 ULONG Hash;
47
48 RtlInitUnicodeString(&Name,
49 AtomName);
50
51 if (Name.Length != 0 &&
52 NT_SUCCESS(RtlHashUnicodeString(&Name,
53 TRUE,
54 HASH_STRING_ALGORITHM_X65599,
55 &Hash)))
56 {
57 PRTL_ATOM_TABLE_ENTRY Current;
58 PRTL_ATOM_TABLE_ENTRY *Link;
59
60 Link = &AtomTable->Buckets[Hash % AtomTable->NumberOfBuckets];
61
62 /* search for an existing entry */
63 Current = *Link;
64 while (Current != NULL)
65 {
66 if (Current->NameLength == Name.Length / sizeof(WCHAR) &&
67 !_wcsicmp(Current->Name, Name.Buffer))
68 {
69 *HashLink = Link;
70 return Current;
71 }
72 Link = &Current->HashLink;
73 Current = Current->HashLink;
74 }
75
76 /* no matching atom found, return the hash link */
77 *HashLink = Link;
78 }
79 else
80 *HashLink = NULL;
81
82 return NULL;
83 }
84
85 static BOOLEAN
86 RtlpCheckIntegerAtom(PWSTR AtomName,
87 PUSHORT AtomValue)
88 {
89 UNICODE_STRING AtomString;
90 ULONG LongValue;
91 USHORT LoValue;
92 PWCHAR p;
93
94 DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n",
95 AtomName, AtomValue);
96
97 if (!((ULONG)AtomName & 0xFFFF0000))
98 {
99 LoValue = (USHORT)((ULONG)AtomName & 0xFFFF);
100
101 if (LoValue >= 0xC000)
102 return FALSE;
103
104 if (LoValue == 0)
105 LoValue = 0xC000;
106
107 if (AtomValue != NULL)
108 *AtomValue = LoValue;
109
110 return TRUE;
111 }
112
113 if (*AtomName != L'#')
114 return FALSE;
115
116 p = AtomName;
117 p++;
118 while (*p)
119 {
120 if ((*p < L'0') || (*p > L'9'))
121 return FALSE;
122 p++;
123 }
124
125 p = AtomName;
126 p++;
127 RtlInitUnicodeString(&AtomString,
128 p);
129
130 DPRINT("AtomString: %wZ\n", &AtomString);
131
132 RtlUnicodeStringToInteger(&AtomString,10, &LongValue);
133
134 DPRINT("LongValue: %lu\n", LongValue);
135
136 *AtomValue = (USHORT)(LongValue & 0x0000FFFF);
137
138 return TRUE;
139 }
140
141
142 /*
143 * @implemented
144 */
145 NTSTATUS STDCALL
146 RtlCreateAtomTable(IN ULONG TableSize,
147 IN OUT PRTL_ATOM_TABLE *AtomTable)
148 {
149 PRTL_ATOM_TABLE Table;
150 NTSTATUS Status;
151
152 DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n",
153 TableSize, AtomTable);
154
155 if (*AtomTable != NULL)
156 {
157 return STATUS_SUCCESS;
158 }
159
160 /* allocate atom table */
161 Table = RtlpAllocAtomTable(((TableSize - 1) * sizeof(PRTL_ATOM_TABLE_ENTRY)) +
162 sizeof(RTL_ATOM_TABLE));
163 if (Table == NULL)
164 {
165 return STATUS_NO_MEMORY;
166 }
167
168 /* initialize atom table */
169 Table->NumberOfBuckets = TableSize;
170
171 Status = RtlpInitAtomTableLock(Table);
172 if (!NT_SUCCESS(Status))
173 {
174 RtlpFreeAtomTable(Table);
175 return Status;
176 }
177
178 if (!RtlpCreateAtomHandleTable(Table))
179 {
180 RtlpDestroyAtomTableLock(Table);
181 RtlpFreeAtomTable(Table);
182 return STATUS_NO_MEMORY;
183 }
184
185 *AtomTable = Table;
186 return STATUS_SUCCESS;
187 }
188
189
190 /*
191 * @implemented
192 */
193 NTSTATUS STDCALL
194 RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable)
195 {
196 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
197 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry;
198
199 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable);
200
201 if (!RtlpLockAtomTable(AtomTable))
202 {
203 return (STATUS_INVALID_PARAMETER);
204 }
205
206 /* delete all atoms */
207 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
208 for (CurrentBucket = AtomTable->Buckets;
209 CurrentBucket != LastBucket;
210 CurrentBucket++)
211 {
212 NextEntry = *CurrentBucket;
213 *CurrentBucket = NULL;
214
215 while (NextEntry != NULL)
216 {
217 CurrentEntry = NextEntry;
218 NextEntry = NextEntry->HashLink;
219
220 /* no need to delete the atom handle, the handles will all be freed
221 up when destroying the atom handle table! */
222
223 RtlpFreeAtomTableEntry(CurrentEntry);
224 }
225 }
226
227 RtlpDestroyAtomHandleTable(AtomTable);
228
229 RtlpUnlockAtomTable(AtomTable);
230
231 RtlpDestroyAtomTableLock(AtomTable);
232
233 RtlpFreeAtomTable(AtomTable);
234
235 return STATUS_SUCCESS;
236 }
237
238
239 /*
240 * @implemented
241 */
242 NTSTATUS STDCALL
243 RtlEmptyAtomTable(PRTL_ATOM_TABLE AtomTable,
244 BOOLEAN DeletePinned)
245 {
246 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
247 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry;
248
249 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
250 AtomTable, DeletePinned);
251
252 if (RtlpLockAtomTable(AtomTable) == FALSE)
253 {
254 return (STATUS_INVALID_PARAMETER);
255 }
256
257 /* delete all atoms */
258 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
259 for (CurrentBucket = AtomTable->Buckets;
260 CurrentBucket != LastBucket;
261 CurrentBucket++)
262 {
263 NextEntry = *CurrentBucket;
264 *CurrentBucket = NULL;
265
266 while (NextEntry != NULL)
267 {
268 CurrentEntry = NextEntry;
269 NextEntry = NextEntry->HashLink;
270
271 RtlpFreeAtomHandle(AtomTable,
272 CurrentEntry);
273
274 RtlpFreeAtomTableEntry(CurrentEntry);
275 }
276 }
277
278 RtlpUnlockAtomTable(AtomTable);
279
280 return STATUS_SUCCESS;
281 }
282
283
284 /*
285 * @implemented
286 */
287 NTSTATUS STDCALL
288 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable,
289 IN PWSTR AtomName,
290 OUT PRTL_ATOM Atom)
291 {
292 USHORT AtomValue;
293 PRTL_ATOM_TABLE_ENTRY *HashLink;
294 PRTL_ATOM_TABLE_ENTRY Entry = NULL;
295 NTSTATUS Status = STATUS_SUCCESS;
296
297 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
298 AtomTable, AtomName, Atom);
299
300 if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
301 {
302 /* integer atom */
303 if (AtomValue >= 0xC000)
304 {
305 Status = STATUS_INVALID_PARAMETER;
306 }
307 else if (Atom != NULL)
308 {
309 *Atom = (RTL_ATOM)AtomValue;
310 }
311
312 return Status;
313 }
314
315 RtlpLockAtomTable(AtomTable);
316
317 /* string atom, hash it and try to find an existing atom with the same name */
318 Entry = RtlpHashAtomName(AtomTable,
319 AtomName,
320 &HashLink);
321
322 if (Entry != NULL)
323 {
324 /* found another atom, increment the reference counter unless it's pinned */
325
326 if (!(Entry->Flags & RTL_ATOM_IS_PINNED))
327 {
328 if (++Entry->ReferenceCount == 0)
329 {
330 /* FIXME - references overflowed, pin the atom? */
331 Entry->Flags |= RTL_ATOM_IS_PINNED;
332 }
333 }
334
335 if (Atom != NULL)
336 {
337 *Atom = (RTL_ATOM)Entry->Atom;
338 }
339 }
340 else
341 {
342 /* couldn't find an existing atom, HashLink now points to either the
343 HashLink pointer of the previous atom or to the bucket so we can
344 simply add it to the list */
345 if (HashLink != NULL)
346 {
347 ULONG AtomNameLen = wcslen(AtomName);
348
349 Entry = RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY) -
350 sizeof(Entry->Name) +
351 (AtomNameLen + 1) * sizeof(WCHAR));
352 if (Entry != NULL)
353 {
354 Entry->HashLink = NULL;
355 Entry->ReferenceCount = 0;
356 Entry->Flags = 0x0;
357
358 Entry->NameLength = AtomNameLen;
359 RtlCopyMemory(Entry->Name,
360 AtomName,
361 (AtomNameLen + 1) * sizeof(WCHAR));
362
363 if (RtlpCreateAtomHandle(AtomTable,
364 Entry))
365 {
366 /* append the atom to the list */
367 *HashLink = Entry;
368
369 if (Atom != NULL)
370 {
371 *Atom = (RTL_ATOM)Entry->Atom;
372 }
373 }
374 else
375 {
376 RtlpFreeAtomTableEntry(Entry);
377 Status = STATUS_NO_MEMORY;
378 }
379 }
380 else
381 {
382 Status = STATUS_NO_MEMORY;
383 }
384 }
385 else
386 {
387 /* The caller supplied an empty atom name! */
388 Status = STATUS_INVALID_PARAMETER;
389 }
390 }
391
392 RtlpUnlockAtomTable(AtomTable);
393
394 return Status;
395 }
396
397
398 /*
399 * @implemented
400 */
401 NTSTATUS STDCALL
402 RtlDeleteAtomFromAtomTable(IN PRTL_ATOM_TABLE AtomTable,
403 IN RTL_ATOM Atom)
404 {
405 PRTL_ATOM_TABLE_ENTRY Entry;
406 NTSTATUS Status = STATUS_SUCCESS;
407
408 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
409 AtomTable, Atom);
410
411 if (Atom >= 0xC000)
412 {
413 RtlpLockAtomTable(AtomTable);
414
415 Entry = RtlpGetAtomEntry(AtomTable,
416 (ULONG)((USHORT)Atom - 0xC000));
417
418 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
419 {
420 if (!(Entry->Flags & RTL_ATOM_IS_PINNED))
421 {
422 if (--Entry->ReferenceCount == 0)
423 {
424 PRTL_ATOM_TABLE_ENTRY *HashLink;
425
426 /* it's time to delete the atom. we need to unlink it from
427 the list. The easiest way is to take the atom name and
428 hash it again, this way we get the pointer to either
429 the hash bucket or the previous atom that links to the
430 one we want to delete. This way we can easily bypass
431 this item. */
432 if (RtlpHashAtomName(AtomTable,
433 Entry->Name,
434 &HashLink) != NULL)
435 {
436 /* bypass this atom */
437 *HashLink = Entry->HashLink;
438
439 RtlpFreeAtomHandle(AtomTable,
440 Entry);
441
442 RtlpFreeAtomTableEntry(Entry);
443 }
444 else
445 {
446 /* WTF?! This should never happen!!! */
447 ASSERT(FALSE);
448 }
449 }
450 }
451 else
452 {
453 /* tried to delete a pinned atom, do nothing and return
454 STATUS_WAS_LOCKED, which is NOT a failure code! */
455 Status = STATUS_WAS_LOCKED;
456 }
457 }
458 else
459 {
460 Status = STATUS_INVALID_HANDLE;
461 }
462
463 RtlpUnlockAtomTable(AtomTable);
464 }
465
466 return Status;
467 }
468
469
470 /*
471 * @implemented
472 */
473 NTSTATUS STDCALL
474 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
475 IN PWSTR AtomName,
476 OUT PRTL_ATOM Atom)
477 {
478 PRTL_ATOM_TABLE_ENTRY Entry, *HashLink;
479 USHORT AtomValue;
480 RTL_ATOM FoundAtom = 0;
481 NTSTATUS Status = STATUS_SUCCESS;
482
483 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
484 AtomTable, AtomName, Atom);
485
486 if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
487 {
488 /* integer atom */
489 if (AtomValue >= 0xC000)
490 {
491 Status = STATUS_INVALID_PARAMETER;
492 }
493 else if (Atom != NULL)
494 {
495 *Atom = (RTL_ATOM)AtomValue;
496 }
497
498 return Status;
499 }
500
501 RtlpLockAtomTable(AtomTable);
502
503 Status = STATUS_OBJECT_NAME_NOT_FOUND;
504
505 /* string atom */
506 Entry = RtlpHashAtomName(AtomTable,
507 AtomName,
508 &HashLink);
509
510 if (Entry != NULL)
511 {
512 Status = STATUS_SUCCESS;
513 FoundAtom = (RTL_ATOM)Entry->Atom;
514 }
515
516 RtlpUnlockAtomTable(AtomTable);
517
518 if (NT_SUCCESS(Status) && Atom != NULL)
519 {
520 *Atom = FoundAtom;
521 }
522
523 return Status;
524 }
525
526
527 /*
528 * @implemented
529 */
530 NTSTATUS STDCALL
531 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
532 IN RTL_ATOM Atom)
533 {
534 NTSTATUS Status = STATUS_SUCCESS;
535
536 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
537 AtomTable, Atom);
538
539 if (Atom >= 0xC000)
540 {
541 PRTL_ATOM_TABLE_ENTRY Entry;
542
543 RtlpLockAtomTable(AtomTable);
544
545 Entry = RtlpGetAtomEntry(AtomTable,
546 (ULONG)((USHORT)Atom - 0xC000));
547
548 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
549 {
550 Entry->Flags |= RTL_ATOM_IS_PINNED;
551 }
552 else
553 {
554 Status = STATUS_INVALID_HANDLE;
555 }
556
557 RtlpUnlockAtomTable(AtomTable);
558 }
559
560 RtlpLockAtomTable(AtomTable);
561
562 return Status;
563 }
564
565
566 /*
567 * @implemented
568 */
569 NTSTATUS STDCALL
570 RtlQueryAtomInAtomTable(PRTL_ATOM_TABLE AtomTable,
571 RTL_ATOM Atom,
572 PULONG RefCount,
573 PULONG PinCount,
574 PWSTR AtomName,
575 PULONG NameLength)
576 {
577 ULONG Length;
578 PRTL_ATOM_TABLE_ENTRY Entry;
579 NTSTATUS Status = STATUS_SUCCESS;
580
581 if (Atom < 0xC000)
582 {
583 if (RefCount != NULL)
584 {
585 *RefCount = 1;
586 }
587
588 if (PinCount != NULL)
589 {
590 *PinCount = 1;
591 }
592
593 if ((AtomName != NULL) && (NameLength != NULL) && (NameLength > 0))
594 {
595 WCHAR NameString[12];
596
597 Length = swprintf(NameString, L"#%lu", (ULONG)Atom) * sizeof(WCHAR);
598
599 if (*NameLength < Length + sizeof(WCHAR))
600 {
601 /* prevent underflow! */
602 if (*NameLength >= sizeof(WCHAR))
603 {
604 Length = *NameLength - sizeof(WCHAR);
605 }
606 else
607 {
608 Length = 0;
609 Status = STATUS_BUFFER_TOO_SMALL;
610 }
611 }
612
613 if (Length)
614 {
615 RtlCopyMemory(AtomName,
616 NameString,
617 Length);
618 AtomName[Length / sizeof(WCHAR)] = L'\0';
619 *NameLength = Length;
620 }
621 }
622
623 return Status;
624 }
625
626 RtlpLockAtomTable(AtomTable);
627
628 Entry = RtlpGetAtomEntry(AtomTable,
629 (ULONG)((USHORT)Atom - 0xC000));
630
631 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
632 {
633 DPRINT("Atom name: %wZ\n", &Entry->Name);
634
635 if (RefCount != NULL)
636 {
637 *RefCount = Entry->ReferenceCount;
638 }
639
640 if (PinCount != NULL)
641 {
642 *PinCount = ((Entry->Flags & RTL_ATOM_IS_PINNED) != 0);
643 }
644
645 if ((AtomName != NULL) && (NameLength != NULL))
646 {
647 Length = Entry->NameLength * sizeof(WCHAR);
648
649 if (*NameLength < Length + sizeof(WCHAR))
650 {
651 /* prevent underflow! */
652 if (*NameLength >= sizeof(WCHAR))
653 {
654 Length = *NameLength - sizeof(WCHAR);
655 }
656 else
657 {
658 Length = 0;
659 Status = STATUS_BUFFER_TOO_SMALL;
660 }
661 }
662
663 if (Length)
664 {
665 RtlCopyMemory(AtomName,
666 Entry->Name,
667 Length);
668 AtomName[Length / sizeof(WCHAR)] = L'\0';
669 *NameLength = Length;
670 }
671 }
672 }
673 else
674 {
675 Status = STATUS_INVALID_HANDLE;
676 }
677
678 RtlpUnlockAtomTable(AtomTable);
679
680 return Status;
681 }
682
683
684 /*
685 * @private - only used by NtQueryInformationAtom
686 */
687 NTSTATUS STDCALL
688 RtlQueryAtomListInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
689 IN ULONG MaxAtomCount,
690 OUT ULONG *AtomCount,
691 OUT RTL_ATOM *AtomList)
692 {
693 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
694 PRTL_ATOM_TABLE_ENTRY CurrentEntry;
695 ULONG Atoms = 0;
696 NTSTATUS Status = STATUS_SUCCESS;
697
698 RtlpLockAtomTable(AtomTable);
699
700 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
701 for (CurrentBucket = AtomTable->Buckets;
702 CurrentBucket != LastBucket;
703 CurrentBucket++)
704 {
705 CurrentEntry = *CurrentBucket;
706
707 while (CurrentEntry != NULL)
708 {
709 if (MaxAtomCount > 0)
710 {
711 *(AtomList++) = (RTL_ATOM)CurrentEntry->Atom;
712 MaxAtomCount--;
713 }
714 else
715 {
716 /* buffer too small, but don't bail. we need to determine the
717 total number of atoms in the table! */
718 Status = STATUS_INFO_LENGTH_MISMATCH;
719 }
720
721 Atoms++;
722 CurrentEntry = CurrentEntry->HashLink;
723 }
724 }
725
726 *AtomCount = Atoms;
727
728 RtlpUnlockAtomTable(AtomTable);
729
730 return Status;
731 }
732