:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / lib / ntdll / rtl / atom.c
1 /* $Id$
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            lib/ntdll/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 <ddk/ntddk.h>
15 #include <ntdll/rtl.h>
16 #include <ntos/heap.h>
17
18 #define NDEBUG
19 #include <ntdll/ntdll.h>
20
21
22 /* LOCAL TYPES ***************************************************************/
23
24 typedef struct _RTL_ATOM_ENTRY
25 {
26    LIST_ENTRY List;
27    UNICODE_STRING Name;
28    ULONG RefCount;
29    BOOLEAN Locked;
30    ULONG Index;
31    PRTL_HANDLE Handle;
32 } RTL_ATOM_ENTRY, *PRTL_ATOM_ENTRY;
33
34 typedef struct _RTL_ATOM_HANDLE
35 {
36    RTL_HANDLE Handle;
37    PRTL_ATOM_ENTRY Entry;
38 } RTL_ATOM_HANDLE, *PRTL_ATOM_HANDLE;
39
40
41 /* PROTOTYPES ****************************************************************/
42
43 static ULONG RtlpHashAtomName(ULONG TableSize, PWSTR AtomName);
44 static BOOLEAN RtlpCheckIntegerAtom(PWSTR AtomName, PUSHORT AtomValue);
45
46 static NTSTATUS RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable);
47 static VOID RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable);
48 static BOOLEAN RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable);
49 static VOID RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable);
50
51 static BOOLEAN RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable);
52 static VOID RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable);
53
54
55 /* FUNCTIONS *****************************************************************/
56
57
58 NTSTATUS STDCALL
59 RtlCreateAtomTable(ULONG TableSize,
60                    PRTL_ATOM_TABLE *AtomTable)
61 {
62    PRTL_ATOM_TABLE Table;
63    ULONG i;
64    NTSTATUS Status;
65
66    DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n",
67           TableSize, AtomTable);
68
69    if (*AtomTable != NULL)
70      {
71         return STATUS_SUCCESS;
72      }
73
74    /* allocate atom table */
75    Table = RtlAllocateHeap(RtlGetProcessHeap(),
76                            HEAP_ZERO_MEMORY,
77                            TableSize * sizeof(LIST_ENTRY) +
78                            sizeof(RTL_ATOM_TABLE));
79    if (Table == NULL)
80      {
81         return STATUS_NO_MEMORY;
82      }
83
84    /* initialize atom table */
85    Table->TableSize = TableSize;
86
87    for (i = 0; i < TableSize; i++)
88      {
89         InitializeListHead(&Table->Slot[i]);
90      }
91
92    Status = RtlpInitAtomTableLock(Table);
93    if (!NT_SUCCESS(Status))
94      {
95         RtlFreeHeap(RtlGetProcessHeap(),
96                     0,
97                     Table);
98         return Status;
99      }
100
101    if (RtlpCreateAtomHandleTable(Table) == FALSE)
102      {
103         RtlpDestroyAtomTableLock(Table);
104         RtlFreeHeap(RtlGetProcessHeap(),
105                     0,
106                     Table);
107         return STATUS_NO_MEMORY;
108      }
109
110    *AtomTable = Table;
111    return STATUS_SUCCESS;
112 }
113
114
115 NTSTATUS STDCALL
116 RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable)
117 {
118    PLIST_ENTRY Current;
119    PRTL_ATOM_ENTRY AtomEntry;
120    ULONG i;
121
122    if (RtlpLockAtomTable(AtomTable) == FALSE)
123      {
124         return (STATUS_INVALID_PARAMETER);
125      }
126
127    /* delete all atoms */
128    for (i = 0; i < AtomTable->TableSize; i++)
129      {
130
131         Current = AtomTable->Slot[i].Flink;
132         while (Current != &AtomTable->Slot[i])
133           {
134              AtomEntry = (PRTL_ATOM_ENTRY)Current;
135              RtlFreeUnicodeString(&AtomEntry->Name);
136              RemoveEntryList(&AtomEntry->List);
137              RtlFreeHeap(RtlGetProcessHeap(),
138                          0,
139                          AtomEntry);
140              Current = AtomTable->Slot[i].Flink;
141           }
142
143      }
144
145    RtlpDestroyAtomHandleTable(AtomTable);
146
147    RtlpUnlockAtomTable(AtomTable);
148
149    RtlpDestroyAtomTableLock(AtomTable);
150
151    RtlFreeHeap(RtlGetProcessHeap(),
152                0,
153                AtomTable);
154
155    return STATUS_SUCCESS;
156 }
157
158
159 NTSTATUS STDCALL
160 RtlEmptyAtomTable(PRTL_ATOM_TABLE AtomTable,
161                   BOOLEAN DeletePinned)
162 {
163    PLIST_ENTRY Current, Next;
164    PRTL_ATOM_ENTRY AtomEntry;
165    ULONG i;
166
167    DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
168           AtomTable, DeletePinned);
169
170    if (RtlpLockAtomTable(AtomTable) == FALSE)
171      {
172         return (STATUS_INVALID_PARAMETER);
173      }
174
175    /* delete all atoms */
176    for (i = 0; i < AtomTable->TableSize; i++)
177      {
178         Current = AtomTable->Slot[i].Flink;
179         while (Current != &AtomTable->Slot[i])
180           {
181              Next = Current->Flink;
182              AtomEntry = (PRTL_ATOM_ENTRY)Current;
183
184              if ((AtomEntry->Locked == FALSE) ||
185                  ((AtomEntry->Locked == TRUE) && (DeletePinned == TRUE)))
186                {
187                   RtlFreeUnicodeString(&AtomEntry->Name);
188
189                   RtlFreeHandle(AtomTable->HandleTable,
190                                 AtomEntry->Handle);
191
192                   RemoveEntryList(&AtomEntry->List);
193                   RtlFreeHeap(RtlGetProcessHeap(),
194                               0,
195                               AtomEntry);
196                }
197              Current = Next;
198           }
199
200      }
201
202    RtlpUnlockAtomTable(AtomTable);
203
204    return STATUS_SUCCESS;
205 }
206
207
208 NTSTATUS STDCALL
209 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable,
210                       IN PWSTR AtomName,
211                       OUT PRTL_ATOM Atom)
212 {
213    ULONG Hash;
214    PLIST_ENTRY Current;
215    PRTL_ATOM_ENTRY Entry;
216    USHORT AtomValue;
217    NTSTATUS Status;
218    PRTL_ATOM_HANDLE AtomHandle;
219    ULONG AtomIndex;
220
221    DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
222           AtomTable, AtomName, Atom);
223
224    if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
225      {
226         /* integer atom */
227         if (AtomValue >= 0xC000)
228           {
229              AtomValue = 0;
230              Status = STATUS_INVALID_PARAMETER;
231           }
232         else
233           {
234              Status = STATUS_SUCCESS;
235           }
236
237         if (Atom)
238           *Atom = (RTL_ATOM)AtomValue;
239
240         return Status;
241      }
242
243    RtlpLockAtomTable(AtomTable);
244
245    /* string atom */
246    Hash = RtlpHashAtomName(AtomTable->TableSize, AtomName);
247
248    /* search for existing atom */
249    Current = AtomTable->Slot[Hash].Flink;
250    while (Current != &AtomTable->Slot[Hash])
251      {
252         Entry = (PRTL_ATOM_ENTRY)Current;
253
254         DPRINT("Comparing %S and %S\n", Entry->Name.Buffer, AtomName);
255         if (_wcsicmp(Entry->Name.Buffer, AtomName) == 0)
256           {
257              Entry->RefCount++;
258              if (Atom)
259                *Atom = (RTL_ATOM)(Entry->Index + 0xC000);
260              RtlpUnlockAtomTable(AtomTable);
261              return STATUS_SUCCESS;
262           }
263         Current = Current->Flink;
264      }
265
266    /* insert new atom */
267    Entry = RtlAllocateHeap(RtlGetProcessHeap(),
268                            HEAP_ZERO_MEMORY,
269                            sizeof(RTL_ATOM_ENTRY));
270    if (Entry == NULL)
271      {
272         RtlpUnlockAtomTable(AtomTable);
273         return STATUS_NO_MEMORY;
274      }
275
276    InsertTailList(&AtomTable->Slot[Hash], &Entry->List)
277    RtlCreateUnicodeString (&Entry->Name,
278                            AtomName);
279    Entry->RefCount = 1;
280    Entry->Locked = FALSE;
281
282    /* FIXME: use general function instead !! */
283    AtomHandle = (PRTL_ATOM_HANDLE)RtlAllocateHandle(AtomTable->HandleTable,
284                                                     &AtomIndex);
285
286    DPRINT("AtomHandle %p AtomIndex %x\n", AtomHandle, AtomIndex);
287
288    AtomHandle->Entry = Entry;
289    Entry->Index = AtomIndex;
290    Entry->Handle = (PRTL_HANDLE)AtomHandle;
291
292    if (Atom)
293      *Atom = (RTL_ATOM)(AtomIndex + 0xC000);
294
295    RtlpUnlockAtomTable(AtomTable);
296
297    return STATUS_SUCCESS;
298 }
299
300
301 NTSTATUS STDCALL
302 RtlDeleteAtomFromAtomTable(IN PRTL_ATOM_TABLE AtomTable,
303                            IN RTL_ATOM Atom)
304 {
305    PRTL_ATOM_HANDLE AtomHandle;
306    PRTL_ATOM_ENTRY AtomEntry;
307
308    DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
309           AtomTable, Atom);
310
311    if (Atom < 0xC000)
312      {
313         return STATUS_SUCCESS;
314      }
315
316    RtlpLockAtomTable(AtomTable);
317
318    /* FIXME: use general function instead !! */
319    if (!RtlIsValidIndexHandle(AtomTable->HandleTable,
320                               (PRTL_HANDLE *)&AtomHandle,
321                               (ULONG)Atom - 0xC000))
322      {
323         RtlpUnlockAtomTable(AtomTable);
324         return STATUS_INVALID_HANDLE;
325      }
326
327    DPRINT("AtomHandle %x\n", AtomHandle);
328    DPRINT("AtomHandle->Entry %x\n", AtomHandle->Entry);
329
330    AtomEntry = AtomHandle->Entry;
331
332    DPRINT("Atom name: %wZ\n", &AtomEntry->Name);
333
334    AtomEntry->RefCount--;
335
336    if (AtomEntry->RefCount == 0)
337      {
338         if (AtomEntry->Locked == TRUE)
339           {
340              DPRINT("Atom %wZ is locked!\n", &AtomEntry->Name);
341
342              RtlpUnlockAtomTable(AtomTable);
343              return STATUS_WAS_LOCKED;
344           }
345
346         DPRINT("Removing atom: %wZ\n", &AtomEntry->Name);
347
348         RtlFreeUnicodeString(&AtomEntry->Name);
349         RemoveEntryList(&AtomEntry->List);
350         RtlFreeHeap(RtlGetProcessHeap(),
351                     0,
352                     AtomEntry);
353         RtlFreeHandle(AtomTable->HandleTable,
354                       (PRTL_HANDLE)AtomHandle);
355      }
356
357    RtlpUnlockAtomTable(AtomTable);
358
359    return STATUS_SUCCESS;
360 }
361
362
363 NTSTATUS STDCALL
364 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
365                          IN PWSTR AtomName,
366                          OUT PRTL_ATOM Atom)
367 {
368    ULONG Hash;
369    PLIST_ENTRY Current;
370    PRTL_ATOM_ENTRY Entry;
371    USHORT AtomValue;
372    NTSTATUS Status;
373
374    DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
375           AtomTable, AtomName, Atom);
376
377    if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
378      {
379         /* integer atom */
380         if (AtomValue >= 0xC000)
381           {
382              AtomValue = 0;
383              Status = STATUS_INVALID_PARAMETER;
384           }
385         else
386           {
387              Status = STATUS_SUCCESS;
388           }
389
390         if (Atom)
391           *Atom = (RTL_ATOM)AtomValue;
392
393         return Status;
394      }
395
396    RtlpLockAtomTable(AtomTable);
397
398    /* string atom */
399    Hash = RtlpHashAtomName(AtomTable->TableSize, AtomName);
400
401    /* search for existing atom */
402    Current = AtomTable->Slot[Hash].Flink;
403    while (Current != &AtomTable->Slot[Hash])
404      {
405         Entry = (PRTL_ATOM_ENTRY)Current;
406
407         DPRINT("Comparing %S and %S\n", Entry->Name.Buffer, AtomName);
408         if (_wcsicmp(Entry->Name.Buffer, AtomName) == 0)
409           {
410              if (Atom)
411                *Atom = (RTL_ATOM)(Entry->Index + 0xC000);
412              RtlpUnlockAtomTable(AtomTable);
413              return STATUS_SUCCESS;
414           }
415
416         Current = Current->Flink;
417      }
418
419    return STATUS_OBJECT_NAME_NOT_FOUND;
420 }
421
422
423 NTSTATUS STDCALL
424 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
425                       IN RTL_ATOM Atom)
426 {
427    PRTL_ATOM_HANDLE AtomHandle;
428    PRTL_ATOM_ENTRY AtomEntry;
429
430    DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
431           AtomTable, Atom);
432
433    if (Atom < 0xC000)
434      {
435         return STATUS_SUCCESS;
436      }
437
438    RtlpLockAtomTable(AtomTable);
439
440    /* FIXME: use general function instead !! */
441    if (!RtlIsValidIndexHandle(AtomTable->HandleTable,
442                               (PRTL_HANDLE *)&AtomHandle,
443                               (ULONG)Atom - 0xC000))
444      {
445         RtlpUnlockAtomTable(AtomTable);
446         return STATUS_INVALID_HANDLE;
447      }
448
449    DPRINT("AtomHandle %x\n", AtomHandle);
450    DPRINT("AtomHandle->Entry %x\n", AtomHandle->Entry);
451
452    AtomEntry = AtomHandle->Entry;
453
454    DPRINT("Atom name: %wZ\n", &AtomEntry->Name);
455
456    AtomEntry->Locked = TRUE;
457
458    RtlpUnlockAtomTable(AtomTable);
459
460    return STATUS_SUCCESS;
461 }
462
463
464 NTSTATUS STDCALL
465 RtlQueryAtomInAtomTable(PRTL_ATOM_TABLE AtomTable,
466                         RTL_ATOM Atom,
467                         PULONG RefCount,
468                         PULONG PinCount,
469                         PWSTR AtomName,
470                         PULONG NameLength)
471 {
472    ULONG Length;
473    PRTL_ATOM_HANDLE AtomHandle;
474    PRTL_ATOM_ENTRY AtomEntry;
475
476    if (Atom < 0xC000)
477      {
478         if (RefCount != NULL)
479           {
480              *RefCount = 1;
481           }
482
483         if (PinCount != NULL)
484           {
485              *PinCount = 1;
486           }
487
488         if ((AtomName != NULL) && (NameLength != NULL) && (NameLength > 0))
489           {
490              Length = swprintf(AtomName, L"#%lu", (ULONG)Atom);
491              *NameLength = Length * sizeof(WCHAR);
492           }
493
494         return STATUS_SUCCESS;
495      }
496
497    RtlpLockAtomTable(AtomTable);
498
499    /* FIXME: use general function instead !! */
500    if (!RtlIsValidIndexHandle(AtomTable->HandleTable,
501                               (PRTL_HANDLE *)&AtomHandle,
502                               (ULONG)Atom - 0xC000))
503      {
504         RtlpUnlockAtomTable(AtomTable);
505         return STATUS_INVALID_HANDLE;
506      }
507
508    DPRINT("AtomHandle %x\n", AtomHandle);
509    DPRINT("AtomHandle->Entry %x\n", AtomHandle->Entry);
510
511    AtomEntry = AtomHandle->Entry;
512
513    DPRINT("Atom name: %wZ\n", &AtomEntry->Name);
514
515    if (RefCount != NULL)
516      {
517         *RefCount = AtomEntry->RefCount;
518      }
519
520    if (PinCount != NULL)
521      {
522         *PinCount = (ULONG)AtomEntry->Locked;
523      }
524
525    if ((AtomName != NULL) && (NameLength != NULL))
526      {
527         if (*NameLength < AtomEntry->Name.Length)
528           {
529              *NameLength = AtomEntry->Name.Length;
530              RtlpUnlockAtomTable(AtomTable);
531              return STATUS_BUFFER_TOO_SMALL;
532           }
533
534         Length = swprintf(AtomName, L"%s", AtomEntry->Name.Buffer);
535         *NameLength = Length * sizeof(WCHAR);
536      }
537
538    RtlpUnlockAtomTable(AtomTable);
539
540    return STATUS_SUCCESS;
541 }
542
543
544 /* INTERNAL FUNCTIONS ********************************************************/
545
546 static ULONG
547 RtlpHashAtomName(ULONG TableSize,
548                  PWSTR AtomName)
549 {
550    ULONG q = 0;
551    PWCHAR p;
552
553    DPRINT("RtlpHashAtomName(TableSize %ld AtomName '%S')\n",
554           TableSize, AtomName);
555
556    /* convert the string to an internal representation */
557    p = AtomName;
558    while (*p != 0)
559      {
560         q += (ULONG)towupper(*p);
561         p++;
562      }
563
564    DPRINT("q %lu Hash %lu\n", q, q % TableSize);
565
566    return (q % TableSize);
567 }
568
569
570 static BOOLEAN
571 RtlpCheckIntegerAtom(PWSTR AtomName,
572                      PUSHORT AtomValue)
573 {
574    UNICODE_STRING AtomString;
575    USHORT LoValue;
576    ULONG LongValue;
577    PWCHAR p;
578
579    DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n",
580           AtomName, AtomValue);
581
582    if (!((ULONG)AtomName & 0xFFFF0000))
583      {
584         LoValue = (USHORT)((ULONG)AtomName & 0xFFFF);
585
586         if (LoValue >= 0xC000)
587           return FALSE;
588
589         if (LoValue == 0)
590           LoValue = 0xC000;
591
592         if (AtomValue != NULL)
593           *AtomValue = LoValue;
594
595         return TRUE;
596      }
597
598    if (*AtomName != L'#')
599      return FALSE;
600
601    p = AtomName;
602    p++;
603    while (*p)
604      {
605         if ((*p < L'0') || (*p > L'9'))
606           return FALSE;
607         p++;
608      }
609
610    p = AtomName;
611    p++;
612    RtlInitUnicodeString(&AtomString,
613                         p);
614
615    RtlUnicodeStringToInteger(&AtomString,10, &LongValue);
616
617    *AtomValue = (USHORT)(LongValue & 0x0000FFFF);
618
619    return TRUE;
620 }
621
622
623 /* lock functions */
624
625 static NTSTATUS
626 RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable)
627 {
628    AtomTable->Lock = RtlAllocateHeap(RtlGetProcessHeap(),
629                                      HEAP_ZERO_MEMORY,
630                                      sizeof(CRITICAL_SECTION));
631    if (AtomTable->Lock == NULL)
632      return STATUS_NO_MEMORY;
633
634    RtlInitializeCriticalSection((PCRITICAL_SECTION)AtomTable->Lock);
635
636    return STATUS_SUCCESS;
637 }
638
639
640 static VOID
641 RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable)
642 {
643    if (AtomTable->Lock)
644      {
645         RtlDeleteCriticalSection((PCRITICAL_SECTION)AtomTable->Lock);
646         RtlFreeHeap(RtlGetProcessHeap(),
647                     0,
648                     AtomTable->Lock);
649         AtomTable->Lock = NULL;
650      }
651 }
652
653
654 static BOOLEAN
655 RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable)
656 {
657    RtlEnterCriticalSection((PCRITICAL_SECTION)AtomTable->Lock);
658    return TRUE;
659 }
660
661
662 static VOID
663 RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable)
664 {
665    RtlLeaveCriticalSection((PCRITICAL_SECTION)AtomTable->Lock);
666 }
667
668
669 /* handle functions */
670
671 static BOOLEAN
672 RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
673 {
674    AtomTable->HandleTable = RtlAllocateHeap(RtlGetProcessHeap(),
675                                             HEAP_ZERO_MEMORY,
676                                             sizeof(RTL_HANDLE_TABLE));
677    if (AtomTable->HandleTable == NULL)
678      return FALSE;
679
680    RtlInitializeHandleTable(0xCFFF,
681                             sizeof(RTL_ATOM_HANDLE),
682                             (PRTL_HANDLE_TABLE)AtomTable->HandleTable);
683
684    return TRUE;
685 }
686
687 static VOID
688 RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable)
689 {
690    if (AtomTable->HandleTable)
691      {
692         RtlDestroyHandleTable((PRTL_HANDLE_TABLE)AtomTable->HandleTable);
693         RtlFreeHeap(RtlGetProcessHeap(),
694                     0,
695                     AtomTable->HandleTable);
696         AtomTable->HandleTable = NULL;
697      }
698 }
699
700 /* EOF */