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