1 /* COPYRIGHT: See COPYING in the top level directory
2 * PROJECT: ReactOS system libraries
4 * PURPOSE: Atom managment
5 * PROGRAMMER: Thomas Weidenmueller
8 /* INCLUDES *****************************************************************/
15 /* PROTOTYPES ****************************************************************/
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
);
22 extern BOOLEAN
RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable
);
23 extern VOID
RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable
);
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
);
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
);
34 /* FUNCTIONS *****************************************************************/
36 static PRTL_ATOM_TABLE_ENTRY
37 RtlpHashAtomName(IN PRTL_ATOM_TABLE AtomTable
,
39 OUT PRTL_ATOM_TABLE_ENTRY
**HashLink
)
44 RtlInitUnicodeString(&Name
,
47 if (Name
.Length
!= 0 &&
48 NT_SUCCESS(RtlHashUnicodeString(&Name
,
50 HASH_STRING_ALGORITHM_X65599
,
53 PRTL_ATOM_TABLE_ENTRY Current
;
54 PRTL_ATOM_TABLE_ENTRY
*Link
;
56 Link
= &AtomTable
->Buckets
[Hash
% AtomTable
->NumberOfBuckets
];
58 /* search for an existing entry */
60 while (Current
!= NULL
)
62 if (Current
->NameLength
== Name
.Length
/ sizeof(WCHAR
) &&
63 !_wcsicmp(Current
->Name
, Name
.Buffer
))
68 Link
= &Current
->HashLink
;
69 Current
= Current
->HashLink
;
72 /* no matching atom found, return the hash link */
82 RtlpCheckIntegerAtom(PWSTR AtomName
,
85 UNICODE_STRING AtomString
;
90 DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n",
93 if (!((ULONG
)AtomName
& 0xFFFF0000))
95 LoValue
= (USHORT
)((ULONG
)AtomName
& 0xFFFF);
100 if (AtomValue
!= NULL
)
101 *AtomValue
= LoValue
;
106 if (*AtomName
!= L
'#')
113 if ((*p
< L
'0') || (*p
> L
'9'))
120 RtlInitUnicodeString(&AtomString
,
123 DPRINT("AtomString: %wZ\n", &AtomString
);
125 RtlUnicodeStringToInteger(&AtomString
,10, &LongValue
);
127 DPRINT("LongValue: %lu\n", LongValue
);
129 *AtomValue
= (USHORT
)(LongValue
& 0x0000FFFF);
139 RtlCreateAtomTable(IN ULONG TableSize
,
140 IN OUT PRTL_ATOM_TABLE
*AtomTable
)
142 PRTL_ATOM_TABLE Table
;
145 DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n",
146 TableSize
, AtomTable
);
148 if (*AtomTable
!= NULL
)
150 return STATUS_SUCCESS
;
153 /* allocate atom table */
154 Table
= RtlpAllocAtomTable(((TableSize
- 1) * sizeof(PRTL_ATOM_TABLE_ENTRY
)) +
155 sizeof(RTL_ATOM_TABLE
));
158 return STATUS_NO_MEMORY
;
161 /* initialize atom table */
162 Table
->NumberOfBuckets
= TableSize
;
164 Status
= RtlpInitAtomTableLock(Table
);
165 if (!NT_SUCCESS(Status
))
167 RtlpFreeAtomTable(Table
);
171 if (!RtlpCreateAtomHandleTable(Table
))
173 RtlpDestroyAtomTableLock(Table
);
174 RtlpFreeAtomTable(Table
);
175 return STATUS_NO_MEMORY
;
179 return STATUS_SUCCESS
;
187 RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable
)
189 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
190 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
;
192 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable
);
194 if (!RtlpLockAtomTable(AtomTable
))
196 return (STATUS_INVALID_PARAMETER
);
199 /* delete all atoms */
200 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
201 for (CurrentBucket
= AtomTable
->Buckets
;
202 CurrentBucket
!= LastBucket
;
205 NextEntry
= *CurrentBucket
;
206 *CurrentBucket
= NULL
;
208 while (NextEntry
!= NULL
)
210 CurrentEntry
= NextEntry
;
211 NextEntry
= NextEntry
->HashLink
;
213 /* no need to delete the atom handle, the handles will all be freed
214 up when destroying the atom handle table! */
216 RtlpFreeAtomTableEntry(CurrentEntry
);
220 RtlpDestroyAtomHandleTable(AtomTable
);
222 RtlpUnlockAtomTable(AtomTable
);
224 RtlpDestroyAtomTableLock(AtomTable
);
226 RtlpFreeAtomTable(AtomTable
);
228 return STATUS_SUCCESS
;
236 RtlEmptyAtomTable(PRTL_ATOM_TABLE AtomTable
,
237 BOOLEAN DeletePinned
)
239 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
240 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
, *PtrEntry
;
242 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
243 AtomTable
, DeletePinned
);
245 if (RtlpLockAtomTable(AtomTable
) == FALSE
)
247 return (STATUS_INVALID_PARAMETER
);
250 /* delete all atoms */
251 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
252 for (CurrentBucket
= AtomTable
->Buckets
;
253 CurrentBucket
!= LastBucket
;
256 NextEntry
= *CurrentBucket
;
257 PtrEntry
= CurrentBucket
;
259 while (NextEntry
!= NULL
)
261 CurrentEntry
= NextEntry
;
262 NextEntry
= NextEntry
->HashLink
;
264 if (DeletePinned
|| !(CurrentEntry
->Flags
& RTL_ATOM_IS_PINNED
))
266 *PtrEntry
= NextEntry
;
268 RtlpFreeAtomHandle(AtomTable
,
271 RtlpFreeAtomTableEntry(CurrentEntry
);
275 PtrEntry
= &CurrentEntry
->HashLink
;
280 RtlpUnlockAtomTable(AtomTable
);
282 return STATUS_SUCCESS
;
290 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
295 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
296 PRTL_ATOM_TABLE_ENTRY Entry
= NULL
;
297 NTSTATUS Status
= STATUS_SUCCESS
;
299 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
300 AtomTable
, AtomName
, Atom
);
302 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
305 if (AtomValue
>= 0xC000)
307 Status
= STATUS_INVALID_PARAMETER
;
309 else if (Atom
!= NULL
)
311 *Atom
= (RTL_ATOM
)AtomValue
;
317 RtlpLockAtomTable(AtomTable
);
319 /* string atom, hash it and try to find an existing atom with the same name */
320 Entry
= RtlpHashAtomName(AtomTable
,
326 /* found another atom, increment the reference counter unless it's pinned */
328 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
330 if (++Entry
->ReferenceCount
== 0)
332 /* FIXME - references overflowed, pin the atom? */
333 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
339 *Atom
= (RTL_ATOM
)Entry
->Atom
;
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
)
349 ULONG AtomNameLen
= wcslen(AtomName
);
351 if (AtomNameLen
> MAX_ATOM_LEN
)
353 Status
= STATUS_INVALID_PARAMETER
;
357 Entry
= RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY
) -
358 sizeof(Entry
->Name
) +
359 (AtomNameLen
+ 1) * sizeof(WCHAR
));
362 Entry
->HashLink
= NULL
;
363 Entry
->ReferenceCount
= 1;
366 Entry
->NameLength
= AtomNameLen
;
367 RtlCopyMemory(Entry
->Name
,
369 (AtomNameLen
+ 1) * sizeof(WCHAR
));
371 if (RtlpCreateAtomHandle(AtomTable
,
374 /* append the atom to the list */
379 *Atom
= (RTL_ATOM
)Entry
->Atom
;
384 RtlpFreeAtomTableEntry(Entry
);
385 Status
= STATUS_NO_MEMORY
;
390 Status
= STATUS_NO_MEMORY
;
395 /* The caller supplied an empty atom name! */
396 Status
= STATUS_OBJECT_NAME_INVALID
;
400 RtlpUnlockAtomTable(AtomTable
);
410 RtlDeleteAtomFromAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
413 PRTL_ATOM_TABLE_ENTRY Entry
;
414 NTSTATUS Status
= STATUS_SUCCESS
;
416 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
421 RtlpLockAtomTable(AtomTable
);
423 Entry
= RtlpGetAtomEntry(AtomTable
,
424 (ULONG
)((USHORT
)Atom
- 0xC000));
426 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
428 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
430 if (--Entry
->ReferenceCount
== 0)
432 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
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
440 if (RtlpHashAtomName(AtomTable
,
444 /* bypass this atom */
445 *HashLink
= Entry
->HashLink
;
447 RtlpFreeAtomHandle(AtomTable
,
450 RtlpFreeAtomTableEntry(Entry
);
454 /* WTF?! This should never happen!!! */
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
;
468 Status
= STATUS_INVALID_HANDLE
;
471 RtlpUnlockAtomTable(AtomTable
);
482 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
486 PRTL_ATOM_TABLE_ENTRY Entry
, *HashLink
;
488 RTL_ATOM FoundAtom
= 0;
489 NTSTATUS Status
= STATUS_SUCCESS
;
491 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
492 AtomTable
, AtomName
, Atom
);
494 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
497 if (AtomValue
>= 0xC000)
499 Status
= STATUS_INVALID_PARAMETER
;
501 else if (Atom
!= NULL
)
503 *Atom
= (RTL_ATOM
)AtomValue
;
509 RtlpLockAtomTable(AtomTable
);
511 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
514 Entry
= RtlpHashAtomName(AtomTable
,
520 Status
= STATUS_SUCCESS
;
521 FoundAtom
= (RTL_ATOM
)Entry
->Atom
;
524 RtlpUnlockAtomTable(AtomTable
);
526 if (NT_SUCCESS(Status
) && Atom
!= NULL
)
539 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
542 NTSTATUS Status
= STATUS_SUCCESS
;
544 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
549 PRTL_ATOM_TABLE_ENTRY Entry
;
551 RtlpLockAtomTable(AtomTable
);
553 Entry
= RtlpGetAtomEntry(AtomTable
,
554 (ULONG
)((USHORT
)Atom
- 0xC000));
556 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
558 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
562 Status
= STATUS_INVALID_HANDLE
;
565 RtlpUnlockAtomTable(AtomTable
);
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.
593 RtlQueryAtomInAtomTable(PRTL_ATOM_TABLE AtomTable
,
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];
610 PRTL_ATOM_TABLE_ENTRY Entry
;
611 NTSTATUS Status
= STATUS_SUCCESS
;
615 /* Synthesize an entry */
616 NumberEntry
.AtomTableEntry
.Atom
= Atom
;
617 NumberEntry
.AtomTableEntry
.NameLength
= swprintf(NumberEntry
.AtomTableEntry
.Name
,
620 NumberEntry
.AtomTableEntry
.ReferenceCount
= 1;
621 NumberEntry
.AtomTableEntry
.Flags
= RTL_ATOM_IS_PINNED
;
622 Entry
= &NumberEntry
.AtomTableEntry
;
626 RtlpLockAtomTable(AtomTable
);
629 Entry
= RtlpGetAtomEntry(AtomTable
,
630 (ULONG
)((USHORT
)Atom
- 0xC000));
633 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
635 DPRINT("Atom name: %wZ\n", &Entry
->Name
);
637 if (RefCount
!= NULL
)
639 *RefCount
= Entry
->ReferenceCount
;
642 if (PinCount
!= NULL
)
644 *PinCount
= ((Entry
->Flags
& RTL_ATOM_IS_PINNED
) != 0);
647 if (NULL
!= NameLength
)
649 Length
= Entry
->NameLength
* sizeof(WCHAR
);
650 if (NULL
!= AtomName
)
652 if (*NameLength
< Length
+ sizeof(WCHAR
))
656 *NameLength
= Length
;
657 Status
= STATUS_BUFFER_TOO_SMALL
;
661 Length
= *NameLength
- sizeof(WCHAR
);
664 if (NT_SUCCESS(Status
))
666 RtlCopyMemory(AtomName
,
669 AtomName
[Length
/ sizeof(WCHAR
)] = L
'\0';
670 *NameLength
= Length
;
675 *NameLength
= Length
;
678 else if (NULL
!= AtomName
)
680 Status
= STATUS_INVALID_PARAMETER
;
685 Status
= STATUS_INVALID_HANDLE
;
688 if (Unlock
) RtlpUnlockAtomTable(AtomTable
);
695 * @private - only used by NtQueryInformationAtom
698 RtlQueryAtomListInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
699 IN ULONG MaxAtomCount
,
700 OUT ULONG
*AtomCount
,
701 OUT RTL_ATOM
*AtomList
)
703 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
704 PRTL_ATOM_TABLE_ENTRY CurrentEntry
;
706 NTSTATUS Status
= STATUS_SUCCESS
;
708 RtlpLockAtomTable(AtomTable
);
710 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
711 for (CurrentBucket
= AtomTable
->Buckets
;
712 CurrentBucket
!= LastBucket
;
715 CurrentEntry
= *CurrentBucket
;
717 while (CurrentEntry
!= NULL
)
719 if (MaxAtomCount
> 0)
721 *(AtomList
++) = (RTL_ATOM
)CurrentEntry
->Atom
;
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
;
732 CurrentEntry
= CurrentEntry
->HashLink
;
738 RtlpUnlockAtomTable(AtomTable
);