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