[PSDK]
[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_PTR)AtomName & 0xFFFF0000))
94 {
95 LoValue = (USHORT)((ULONG_PTR)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 /* Use default if size was incorrect */
154 if (TableSize <= 1) TableSize = 37;
155
156 /* allocate atom table */
157 Table = RtlpAllocAtomTable(((TableSize - 1) * sizeof(PRTL_ATOM_TABLE_ENTRY)) +
158 sizeof(RTL_ATOM_TABLE));
159 if (Table == NULL)
160 {
161 return STATUS_NO_MEMORY;
162 }
163
164 /* initialize atom table */
165 Table->NumberOfBuckets = TableSize;
166
167 Status = RtlpInitAtomTableLock(Table);
168 if (!NT_SUCCESS(Status))
169 {
170 RtlpFreeAtomTable(Table);
171 return Status;
172 }
173
174 if (!RtlpCreateAtomHandleTable(Table))
175 {
176 RtlpDestroyAtomTableLock(Table);
177 RtlpFreeAtomTable(Table);
178 return STATUS_NO_MEMORY;
179 }
180
181 *AtomTable = Table;
182 return STATUS_SUCCESS;
183 }
184
185
186 /*
187 * @implemented
188 */
189 NTSTATUS NTAPI
190 RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable)
191 {
192 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
193 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry;
194
195 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable);
196
197 if (!RtlpLockAtomTable(AtomTable))
198 {
199 return (STATUS_INVALID_PARAMETER);
200 }
201
202 /* delete all atoms */
203 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
204 for (CurrentBucket = AtomTable->Buckets;
205 CurrentBucket != LastBucket;
206 CurrentBucket++)
207 {
208 NextEntry = *CurrentBucket;
209 *CurrentBucket = NULL;
210
211 while (NextEntry != NULL)
212 {
213 CurrentEntry = NextEntry;
214 NextEntry = NextEntry->HashLink;
215
216 /* no need to delete the atom handle, the handles will all be freed
217 up when destroying the atom handle table! */
218
219 RtlpFreeAtomTableEntry(CurrentEntry);
220 }
221 }
222
223 RtlpDestroyAtomHandleTable(AtomTable);
224
225 RtlpUnlockAtomTable(AtomTable);
226
227 RtlpDestroyAtomTableLock(AtomTable);
228
229 RtlpFreeAtomTable(AtomTable);
230
231 return STATUS_SUCCESS;
232 }
233
234
235 /*
236 * @implemented
237 */
238 NTSTATUS NTAPI
239 RtlEmptyAtomTable(PRTL_ATOM_TABLE AtomTable,
240 BOOLEAN DeletePinned)
241 {
242 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
243 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry, *PtrEntry;
244
245 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
246 AtomTable, DeletePinned);
247
248 if (RtlpLockAtomTable(AtomTable) == FALSE)
249 {
250 return (STATUS_INVALID_PARAMETER);
251 }
252
253 /* delete all atoms */
254 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
255 for (CurrentBucket = AtomTable->Buckets;
256 CurrentBucket != LastBucket;
257 CurrentBucket++)
258 {
259 NextEntry = *CurrentBucket;
260 PtrEntry = CurrentBucket;
261
262 while (NextEntry != NULL)
263 {
264 CurrentEntry = NextEntry;
265 NextEntry = NextEntry->HashLink;
266
267 if (DeletePinned || !(CurrentEntry->Flags & RTL_ATOM_IS_PINNED))
268 {
269 *PtrEntry = NextEntry;
270
271 RtlpFreeAtomHandle(AtomTable,
272 CurrentEntry);
273
274 RtlpFreeAtomTableEntry(CurrentEntry);
275 }
276 else
277 {
278 PtrEntry = &CurrentEntry->HashLink;
279 }
280 }
281 }
282
283 RtlpUnlockAtomTable(AtomTable);
284
285 return STATUS_SUCCESS;
286 }
287
288
289 /*
290 * @implemented
291 */
292 NTSTATUS NTAPI
293 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable,
294 IN PWSTR AtomName,
295 OUT PRTL_ATOM Atom)
296 {
297 USHORT AtomValue;
298 PRTL_ATOM_TABLE_ENTRY *HashLink;
299 PRTL_ATOM_TABLE_ENTRY Entry = NULL;
300 NTSTATUS Status = STATUS_SUCCESS;
301
302 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
303 AtomTable, AtomName, Atom);
304
305 if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
306 {
307 /* integer atom */
308 if (AtomValue >= 0xC000)
309 {
310 Status = STATUS_INVALID_PARAMETER;
311 }
312 else if (Atom != NULL)
313 {
314 *Atom = (RTL_ATOM)AtomValue;
315 }
316
317 return Status;
318 }
319
320 RtlpLockAtomTable(AtomTable);
321
322 /* string atom, hash it and try to find an existing atom with the same name */
323 Entry = RtlpHashAtomName(AtomTable,
324 AtomName,
325 &HashLink);
326
327 if (Entry != NULL)
328 {
329 /* found another atom, increment the reference counter unless it's pinned */
330
331 if (!(Entry->Flags & RTL_ATOM_IS_PINNED))
332 {
333 if (++Entry->ReferenceCount == 0)
334 {
335 /* FIXME - references overflowed, pin the atom? */
336 Entry->Flags |= RTL_ATOM_IS_PINNED;
337 }
338 }
339
340 if (Atom != NULL)
341 {
342 *Atom = (RTL_ATOM)Entry->Atom;
343 }
344 }
345 else
346 {
347 /* couldn't find an existing atom, HashLink now points to either the
348 HashLink pointer of the previous atom or to the bucket so we can
349 simply add it to the list */
350 if (HashLink != NULL)
351 {
352 ULONG AtomNameLen = wcslen(AtomName);
353
354 if (AtomNameLen > RTL_MAXIMUM_ATOM_LENGTH)
355 {
356 Status = STATUS_INVALID_PARAMETER;
357 goto end;
358 }
359
360 Entry = RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY) -
361 sizeof(Entry->Name) +
362 (AtomNameLen + 1) * sizeof(WCHAR));
363 if (Entry != NULL)
364 {
365 Entry->HashLink = NULL;
366 Entry->ReferenceCount = 1;
367 Entry->Flags = 0x0;
368
369 Entry->NameLength = (UCHAR)AtomNameLen;
370 RtlCopyMemory(Entry->Name,
371 AtomName,
372 (AtomNameLen + 1) * sizeof(WCHAR));
373
374 if (RtlpCreateAtomHandle(AtomTable,
375 Entry))
376 {
377 /* append the atom to the list */
378 *HashLink = Entry;
379
380 if (Atom != NULL)
381 {
382 *Atom = (RTL_ATOM)Entry->Atom;
383 }
384 }
385 else
386 {
387 RtlpFreeAtomTableEntry(Entry);
388 Status = STATUS_NO_MEMORY;
389 }
390 }
391 else
392 {
393 Status = STATUS_NO_MEMORY;
394 }
395 }
396 else
397 {
398 /* The caller supplied an empty atom name! */
399 Status = STATUS_OBJECT_NAME_INVALID;
400 }
401 }
402 end:
403 RtlpUnlockAtomTable(AtomTable);
404
405 return Status;
406 }
407
408
409 /*
410 * @implemented
411 */
412 NTSTATUS NTAPI
413 RtlDeleteAtomFromAtomTable(IN PRTL_ATOM_TABLE AtomTable,
414 IN RTL_ATOM Atom)
415 {
416 PRTL_ATOM_TABLE_ENTRY Entry;
417 NTSTATUS Status = STATUS_SUCCESS;
418
419 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
420 AtomTable, Atom);
421
422 if (Atom >= 0xC000)
423 {
424 RtlpLockAtomTable(AtomTable);
425
426 Entry = RtlpGetAtomEntry(AtomTable,
427 (ULONG)((USHORT)Atom - 0xC000));
428
429 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
430 {
431 if (!(Entry->Flags & RTL_ATOM_IS_PINNED))
432 {
433 if (--Entry->ReferenceCount == 0)
434 {
435 PRTL_ATOM_TABLE_ENTRY *HashLink;
436
437 /* it's time to delete the atom. we need to unlink it from
438 the list. The easiest way is to take the atom name and
439 hash it again, this way we get the pointer to either
440 the hash bucket or the previous atom that links to the
441 one we want to delete. This way we can easily bypass
442 this item. */
443 if (RtlpHashAtomName(AtomTable,
444 Entry->Name,
445 &HashLink) != NULL)
446 {
447 /* bypass this atom */
448 *HashLink = Entry->HashLink;
449
450 RtlpFreeAtomHandle(AtomTable,
451 Entry);
452
453 RtlpFreeAtomTableEntry(Entry);
454 }
455 else
456 {
457 /* WTF?! This should never happen!!! */
458 ASSERT(FALSE);
459 }
460 }
461 }
462 else
463 {
464 /* tried to delete a pinned atom, do nothing and return
465 STATUS_WAS_LOCKED, which is NOT a failure code! */
466 Status = STATUS_WAS_LOCKED;
467 }
468 }
469 else
470 {
471 Status = STATUS_INVALID_HANDLE;
472 }
473
474 RtlpUnlockAtomTable(AtomTable);
475 }
476
477 return Status;
478 }
479
480
481 /*
482 * @implemented
483 */
484 NTSTATUS NTAPI
485 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
486 IN PWSTR AtomName,
487 OUT PRTL_ATOM Atom)
488 {
489 PRTL_ATOM_TABLE_ENTRY Entry, *HashLink;
490 USHORT AtomValue;
491 RTL_ATOM FoundAtom = 0;
492 NTSTATUS Status = STATUS_SUCCESS;
493
494 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
495 AtomTable, AtomName, Atom);
496
497 if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
498 {
499 /* integer atom */
500 if (AtomValue >= 0xC000)
501 {
502 Status = STATUS_INVALID_PARAMETER;
503 }
504 else if (Atom != NULL)
505 {
506 *Atom = (RTL_ATOM)AtomValue;
507 }
508
509 return Status;
510 }
511
512 RtlpLockAtomTable(AtomTable);
513 Status = STATUS_OBJECT_NAME_NOT_FOUND;
514
515 /* string atom */
516 Entry = RtlpHashAtomName(AtomTable,
517 AtomName,
518 &HashLink);
519 if (Entry != NULL)
520 {
521 Status = STATUS_SUCCESS;
522 FoundAtom = (RTL_ATOM)Entry->Atom;
523 }
524 RtlpUnlockAtomTable(AtomTable);
525 if (NT_SUCCESS(Status) && Atom != NULL)
526 {
527 *Atom = FoundAtom;
528 }
529 return Status;
530 }
531
532
533 /*
534 * @implemented
535 */
536 NTSTATUS NTAPI
537 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
538 IN RTL_ATOM Atom)
539 {
540 NTSTATUS Status = STATUS_SUCCESS;
541
542 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
543 AtomTable, Atom);
544
545 if (Atom >= 0xC000)
546 {
547 PRTL_ATOM_TABLE_ENTRY Entry;
548
549 RtlpLockAtomTable(AtomTable);
550
551 Entry = RtlpGetAtomEntry(AtomTable,
552 (ULONG)((USHORT)Atom - 0xC000));
553
554 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
555 {
556 Entry->Flags |= RTL_ATOM_IS_PINNED;
557 }
558 else
559 {
560 Status = STATUS_INVALID_HANDLE;
561 }
562
563 RtlpUnlockAtomTable(AtomTable);
564 }
565
566 return Status;
567 }
568
569
570 /*
571 * @implemented
572 *
573 * This API is really messed up with regards to NameLength. If you pass in a
574 * valid buffer for AtomName, NameLength should be the size of the buffer
575 * (in bytes, not characters). So if you expect the string to be 6 char long,
576 * you need to allocate a buffer of 7 WCHARs and pass 14 for NameLength.
577 * The AtomName returned is always null terminated. If the NameLength you pass
578 * is smaller than 4 (4 would leave room for 1 character) the function will
579 * return with status STATUS_BUFFER_TOO_SMALL. If you pass more than 4, the
580 * return status will be STATUS_SUCCESS, even if the buffer is not large enough
581 * to hold the complete string. In that case, the string is silently truncated
582 * and made to fit in the provided buffer. On return NameLength is set to the
583 * number of bytes (but EXCLUDING the bytes for the null terminator) copied.
584 * So, if the string is 6 char long, you pass a buffer of 10 bytes, on return
585 * NameLength will be set to 8.
586 * If you pass in a NULL value for AtomName, the length of the string in bytes
587 * (again EXCLUDING the null terminator) is returned in NameLength, at least
588 * on Win2k, XP and ReactOS. NT4 will return 0 in that case.
589 */
590 NTSTATUS NTAPI
591 RtlQueryAtomInAtomTable(PRTL_ATOM_TABLE AtomTable,
592 RTL_ATOM Atom,
593 PULONG RefCount,
594 PULONG PinCount,
595 PWSTR AtomName,
596 PULONG NameLength)
597 {
598 ULONG Length;
599 BOOL Unlock = FALSE;
600
601 union
602 {
603 /* A RTL_ATOM_TABLE_ENTRY has a "WCHAR Name[1]" entry at the end.
604 * Make sure we reserve enough room to facilitate a 12 character name */
605 RTL_ATOM_TABLE_ENTRY AtomTableEntry;
606 WCHAR StringBuffer[sizeof(RTL_ATOM_TABLE_ENTRY) / sizeof(WCHAR) + 12];
607 } NumberEntry;
608 PRTL_ATOM_TABLE_ENTRY Entry;
609 NTSTATUS Status = STATUS_SUCCESS;
610
611 if (Atom < 0xC000)
612 {
613 /* Synthesize an entry */
614 NumberEntry.AtomTableEntry.Atom = Atom;
615 NumberEntry.AtomTableEntry.NameLength = swprintf(NumberEntry.AtomTableEntry.Name,
616 L"#%lu",
617 (ULONG)Atom);
618 NumberEntry.AtomTableEntry.ReferenceCount = 1;
619 NumberEntry.AtomTableEntry.Flags = RTL_ATOM_IS_PINNED;
620 Entry = &NumberEntry.AtomTableEntry;
621 }
622 else
623 {
624 RtlpLockAtomTable(AtomTable);
625 Unlock = TRUE;
626
627 Entry = RtlpGetAtomEntry(AtomTable,
628 (ULONG)((USHORT)Atom - 0xC000));
629 }
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 (NULL != NameLength)
646 {
647 Length = Entry->NameLength * sizeof(WCHAR);
648 if (NULL != AtomName)
649 {
650 if (*NameLength < Length + sizeof(WCHAR))
651 {
652 if (*NameLength < 4)
653 {
654 *NameLength = Length;
655 Status = STATUS_BUFFER_TOO_SMALL;
656 }
657 else
658 {
659 Length = *NameLength - sizeof(WCHAR);
660 }
661 }
662 if (NT_SUCCESS(Status))
663 {
664 RtlCopyMemory(AtomName,
665 Entry->Name,
666 Length);
667 AtomName[Length / sizeof(WCHAR)] = L'\0';
668 *NameLength = Length;
669 }
670 }
671 else
672 {
673 *NameLength = Length;
674 }
675 }
676 else if (NULL != AtomName)
677 {
678 Status = STATUS_INVALID_PARAMETER;
679 }
680 }
681 else
682 {
683 Status = STATUS_INVALID_HANDLE;
684 }
685
686 if (Unlock) RtlpUnlockAtomTable(AtomTable);
687
688 return Status;
689 }
690
691
692 /*
693 * @private - only used by NtQueryInformationAtom
694 */
695 NTSTATUS NTAPI
696 RtlQueryAtomListInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
697 IN ULONG MaxAtomCount,
698 OUT ULONG *AtomCount,
699 OUT RTL_ATOM *AtomList)
700 {
701 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
702 PRTL_ATOM_TABLE_ENTRY CurrentEntry;
703 ULONG Atoms = 0;
704 NTSTATUS Status = STATUS_SUCCESS;
705
706 RtlpLockAtomTable(AtomTable);
707
708 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
709 for (CurrentBucket = AtomTable->Buckets;
710 CurrentBucket != LastBucket;
711 CurrentBucket++)
712 {
713 CurrentEntry = *CurrentBucket;
714
715 while (CurrentEntry != NULL)
716 {
717 if (MaxAtomCount > 0)
718 {
719 *(AtomList++) = (RTL_ATOM)CurrentEntry->Atom;
720 MaxAtomCount--;
721 }
722 else
723 {
724 /* buffer too small, but don't bail. we need to determine the
725 total number of atoms in the table! */
726 Status = STATUS_INFO_LENGTH_MISMATCH;
727 }
728
729 Atoms++;
730 CurrentEntry = CurrentEntry->HashLink;
731 }
732 }
733
734 *AtomCount = Atoms;
735
736 RtlpUnlockAtomTable(AtomTable);
737
738 return Status;
739 }
740