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 *****************************************************************/
39 IN PRTL_ATOM_TABLE AtomTable
,
41 OUT PRTL_ATOM_TABLE_ENTRY
**HashLink
)
46 RtlInitUnicodeString(&Name
, AtomName
);
48 if (Name
.Length
!= 0 &&
49 NT_SUCCESS(RtlHashUnicodeString(&Name
,
51 HASH_STRING_ALGORITHM_X65599
,
54 PRTL_ATOM_TABLE_ENTRY Current
;
55 PRTL_ATOM_TABLE_ENTRY
*Link
;
57 Link
= &AtomTable
->Buckets
[Hash
% AtomTable
->NumberOfBuckets
];
59 /* search for an existing entry */
61 while (Current
!= NULL
)
63 if (Current
->NameLength
== Name
.Length
/ sizeof(WCHAR
) &&
64 !_wcsicmp(Current
->Name
, Name
.Buffer
))
70 Link
= &Current
->HashLink
;
71 Current
= Current
->HashLink
;
74 /* no matching atom found, return the hash link */
89 UNICODE_STRING AtomString
;
94 DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n",
97 if (!((ULONG_PTR
)AtomName
& 0xFFFF0000))
99 LoValue
= (USHORT
)((ULONG_PTR
)AtomName
& 0xFFFF);
104 if (AtomValue
!= NULL
)
105 *AtomValue
= LoValue
;
111 * AtomName cannot be NULL because this
112 * case was caught by the previous test.
114 ASSERT(AtomName
!= NULL
);
116 if (*AtomName
!= L
'#')
123 if ((*p
< L
'0') || (*p
> L
'9'))
130 RtlInitUnicodeString(&AtomString
, p
);
132 DPRINT("AtomString: %wZ\n", &AtomString
);
134 RtlUnicodeStringToInteger(&AtomString
, 10, &LongValue
);
136 DPRINT("LongValue: %lu\n", LongValue
);
138 *AtomValue
= (USHORT
)(LongValue
& 0x0000FFFF);
151 IN OUT PRTL_ATOM_TABLE
*AtomTable
)
153 PRTL_ATOM_TABLE Table
;
156 DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n",
157 TableSize
, AtomTable
);
159 if (*AtomTable
!= NULL
)
161 return STATUS_SUCCESS
;
164 /* Use default if size was incorrect */
165 if (TableSize
<= 1) TableSize
= 37;
167 /* allocate atom table */
168 Table
= RtlpAllocAtomTable(((TableSize
- 1) * sizeof(PRTL_ATOM_TABLE_ENTRY
)) +
169 sizeof(RTL_ATOM_TABLE
));
172 return STATUS_NO_MEMORY
;
175 /* initialize atom table */
176 Table
->NumberOfBuckets
= TableSize
;
178 Status
= RtlpInitAtomTableLock(Table
);
179 if (!NT_SUCCESS(Status
))
181 RtlpFreeAtomTable(Table
);
185 if (!RtlpCreateAtomHandleTable(Table
))
187 RtlpDestroyAtomTableLock(Table
);
188 RtlpFreeAtomTable(Table
);
189 return STATUS_NO_MEMORY
;
193 return STATUS_SUCCESS
;
203 IN PRTL_ATOM_TABLE AtomTable
)
205 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
206 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
;
208 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable
);
210 if (!RtlpLockAtomTable(AtomTable
))
212 return (STATUS_INVALID_PARAMETER
);
215 /* delete all atoms */
216 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
217 for (CurrentBucket
= AtomTable
->Buckets
;
218 CurrentBucket
!= LastBucket
;
221 NextEntry
= *CurrentBucket
;
222 *CurrentBucket
= NULL
;
224 while (NextEntry
!= NULL
)
226 CurrentEntry
= NextEntry
;
227 NextEntry
= NextEntry
->HashLink
;
229 /* no need to delete the atom handle, the handles will all be freed
230 up when destroying the atom handle table! */
232 RtlpFreeAtomTableEntry(CurrentEntry
);
236 RtlpDestroyAtomHandleTable(AtomTable
);
238 RtlpUnlockAtomTable(AtomTable
);
240 RtlpDestroyAtomTableLock(AtomTable
);
242 RtlpFreeAtomTable(AtomTable
);
244 return STATUS_SUCCESS
;
254 PRTL_ATOM_TABLE AtomTable
,
255 BOOLEAN DeletePinned
)
257 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
258 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
, *PtrEntry
;
260 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
261 AtomTable
, DeletePinned
);
263 if (RtlpLockAtomTable(AtomTable
) == FALSE
)
265 return (STATUS_INVALID_PARAMETER
);
268 /* delete all atoms */
269 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
270 for (CurrentBucket
= AtomTable
->Buckets
;
271 CurrentBucket
!= LastBucket
;
274 NextEntry
= *CurrentBucket
;
275 PtrEntry
= CurrentBucket
;
277 while (NextEntry
!= NULL
)
279 CurrentEntry
= NextEntry
;
280 NextEntry
= NextEntry
->HashLink
;
282 if (DeletePinned
|| !(CurrentEntry
->Flags
& RTL_ATOM_IS_PINNED
))
284 *PtrEntry
= NextEntry
;
286 RtlpFreeAtomHandle(AtomTable
, CurrentEntry
);
288 RtlpFreeAtomTableEntry(CurrentEntry
);
292 PtrEntry
= &CurrentEntry
->HashLink
;
297 RtlpUnlockAtomTable(AtomTable
);
299 return STATUS_SUCCESS
;
307 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
312 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
313 PRTL_ATOM_TABLE_ENTRY Entry
= NULL
;
314 NTSTATUS Status
= STATUS_SUCCESS
;
316 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
317 AtomTable
, AtomName
, Atom
);
319 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
322 if (AtomValue
>= 0xC000)
324 Status
= STATUS_INVALID_PARAMETER
;
326 else if (Atom
!= NULL
)
328 *Atom
= (RTL_ATOM
)AtomValue
;
334 RtlpLockAtomTable(AtomTable
);
336 /* string atom, hash it and try to find an existing atom with the same name */
337 Entry
= RtlpHashAtomName(AtomTable
, AtomName
, &HashLink
);
341 /* found another atom, increment the reference counter unless it's pinned */
343 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
345 if (++Entry
->ReferenceCount
== 0)
347 /* FIXME - references overflowed, pin the atom? */
348 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
354 *Atom
= (RTL_ATOM
)Entry
->Atom
;
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
)
364 ULONG AtomNameLen
= (ULONG
)wcslen(AtomName
);
366 if (AtomNameLen
> RTL_MAXIMUM_ATOM_LENGTH
)
368 Status
= STATUS_INVALID_PARAMETER
;
372 Entry
= RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY
) -
373 sizeof(Entry
->Name
) +
374 (AtomNameLen
+ 1) * sizeof(WCHAR
));
377 Entry
->HashLink
= NULL
;
378 Entry
->ReferenceCount
= 1;
381 Entry
->NameLength
= (UCHAR
)AtomNameLen
;
382 RtlCopyMemory(Entry
->Name
,
384 (AtomNameLen
+ 1) * sizeof(WCHAR
));
386 if (RtlpCreateAtomHandle(AtomTable
, Entry
))
388 /* append the atom to the list */
393 *Atom
= (RTL_ATOM
)Entry
->Atom
;
398 RtlpFreeAtomTableEntry(Entry
);
399 Status
= STATUS_NO_MEMORY
;
404 Status
= STATUS_NO_MEMORY
;
409 /* The caller supplied an empty atom name! */
410 Status
= STATUS_OBJECT_NAME_INVALID
;
414 RtlpUnlockAtomTable(AtomTable
);
425 RtlDeleteAtomFromAtomTable(
426 IN PRTL_ATOM_TABLE AtomTable
,
429 PRTL_ATOM_TABLE_ENTRY Entry
;
430 NTSTATUS Status
= STATUS_SUCCESS
;
432 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
437 RtlpLockAtomTable(AtomTable
);
439 Entry
= RtlpGetAtomEntry(AtomTable
, (ULONG
)((USHORT
)Atom
- 0xC000));
441 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
443 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
445 if (--Entry
->ReferenceCount
== 0)
447 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
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
455 if (RtlpHashAtomName(AtomTable
, Entry
->Name
, &HashLink
) != NULL
)
457 /* bypass this atom */
458 *HashLink
= Entry
->HashLink
;
460 RtlpFreeAtomHandle(AtomTable
, Entry
);
462 RtlpFreeAtomTableEntry(Entry
);
466 /* WTF?! This should never happen!!! */
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
;
480 Status
= STATUS_INVALID_HANDLE
;
483 RtlpUnlockAtomTable(AtomTable
);
494 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
498 PRTL_ATOM_TABLE_ENTRY Entry
, *HashLink
;
500 RTL_ATOM FoundAtom
= 0;
501 NTSTATUS Status
= STATUS_SUCCESS
;
503 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
504 AtomTable
, AtomName
, Atom
);
506 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
509 if (AtomValue
>= 0xC000)
511 Status
= STATUS_INVALID_PARAMETER
;
513 else if (Atom
!= NULL
)
515 *Atom
= (RTL_ATOM
)AtomValue
;
521 RtlpLockAtomTable(AtomTable
);
522 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
525 Entry
= RtlpHashAtomName(AtomTable
, AtomName
, &HashLink
);
528 Status
= STATUS_SUCCESS
;
529 FoundAtom
= (RTL_ATOM
)Entry
->Atom
;
532 RtlpUnlockAtomTable(AtomTable
);
533 if (NT_SUCCESS(Status
) && Atom
!= NULL
)
545 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
548 NTSTATUS Status
= STATUS_SUCCESS
;
550 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
555 PRTL_ATOM_TABLE_ENTRY Entry
;
557 RtlpLockAtomTable(AtomTable
);
559 Entry
= RtlpGetAtomEntry(AtomTable
, (ULONG
)((USHORT
)Atom
- 0xC000));
561 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
563 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
567 Status
= STATUS_INVALID_HANDLE
;
570 RtlpUnlockAtomTable(AtomTable
);
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.
599 RtlQueryAtomInAtomTable(
600 PRTL_ATOM_TABLE AtomTable
,
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];
617 PRTL_ATOM_TABLE_ENTRY Entry
;
618 NTSTATUS Status
= STATUS_SUCCESS
;
622 /* Synthesize an entry */
623 NumberEntry
.AtomTableEntry
.Atom
= Atom
;
624 NumberEntry
.AtomTableEntry
.NameLength
= swprintf(NumberEntry
.AtomTableEntry
.Name
,
627 NumberEntry
.AtomTableEntry
.ReferenceCount
= 1;
628 NumberEntry
.AtomTableEntry
.Flags
= RTL_ATOM_IS_PINNED
;
629 Entry
= &NumberEntry
.AtomTableEntry
;
633 RtlpLockAtomTable(AtomTable
);
636 Entry
= RtlpGetAtomEntry(AtomTable
, (ULONG
)((USHORT
)Atom
- 0xC000));
639 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
641 DPRINT("Atom name: %wZ\n", &Entry
->Name
);
643 if (RefCount
!= NULL
)
645 *RefCount
= Entry
->ReferenceCount
;
648 if (PinCount
!= NULL
)
650 *PinCount
= ((Entry
->Flags
& RTL_ATOM_IS_PINNED
) != 0);
653 if (NULL
!= NameLength
)
655 Length
= Entry
->NameLength
* sizeof(WCHAR
);
656 if (NULL
!= AtomName
)
658 if (*NameLength
< Length
+ sizeof(WCHAR
))
662 *NameLength
= Length
;
663 Status
= STATUS_BUFFER_TOO_SMALL
;
667 Length
= *NameLength
- sizeof(WCHAR
);
670 if (NT_SUCCESS(Status
))
672 RtlCopyMemory(AtomName
, Entry
->Name
, Length
);
673 AtomName
[Length
/ sizeof(WCHAR
)] = L
'\0';
674 *NameLength
= Length
;
679 *NameLength
= Length
;
682 else if (NULL
!= AtomName
)
684 Status
= STATUS_INVALID_PARAMETER
;
689 Status
= STATUS_INVALID_HANDLE
;
692 if (Unlock
) RtlpUnlockAtomTable(AtomTable
);
699 * @private - only used by NtQueryInformationAtom
703 RtlQueryAtomListInAtomTable(
704 IN PRTL_ATOM_TABLE AtomTable
,
705 IN ULONG MaxAtomCount
,
706 OUT ULONG
*AtomCount
,
707 OUT RTL_ATOM
*AtomList
)
709 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
710 PRTL_ATOM_TABLE_ENTRY CurrentEntry
;
712 NTSTATUS Status
= STATUS_SUCCESS
;
714 RtlpLockAtomTable(AtomTable
);
716 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
717 for (CurrentBucket
= AtomTable
->Buckets
;
718 CurrentBucket
!= LastBucket
;
721 CurrentEntry
= *CurrentBucket
;
723 while (CurrentEntry
!= NULL
)
725 if (MaxAtomCount
> 0)
727 *(AtomList
++) = (RTL_ATOM
)CurrentEntry
->Atom
;
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
;
738 CurrentEntry
= CurrentEntry
->HashLink
;
744 RtlpUnlockAtomTable(AtomTable
);