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