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 /* Use default if size was incorrect */
154 if (TableSize
<= 1) TableSize
= 37;
156 /* allocate atom table */
157 Table
= RtlpAllocAtomTable(((TableSize
- 1) * sizeof(PRTL_ATOM_TABLE_ENTRY
)) +
158 sizeof(RTL_ATOM_TABLE
));
161 return STATUS_NO_MEMORY
;
164 /* initialize atom table */
165 Table
->NumberOfBuckets
= TableSize
;
167 Status
= RtlpInitAtomTableLock(Table
);
168 if (!NT_SUCCESS(Status
))
170 RtlpFreeAtomTable(Table
);
174 if (!RtlpCreateAtomHandleTable(Table
))
176 RtlpDestroyAtomTableLock(Table
);
177 RtlpFreeAtomTable(Table
);
178 return STATUS_NO_MEMORY
;
182 return STATUS_SUCCESS
;
190 RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable
)
192 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
193 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
;
195 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable
);
197 if (!RtlpLockAtomTable(AtomTable
))
199 return (STATUS_INVALID_PARAMETER
);
202 /* delete all atoms */
203 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
204 for (CurrentBucket
= AtomTable
->Buckets
;
205 CurrentBucket
!= LastBucket
;
208 NextEntry
= *CurrentBucket
;
209 *CurrentBucket
= NULL
;
211 while (NextEntry
!= NULL
)
213 CurrentEntry
= NextEntry
;
214 NextEntry
= NextEntry
->HashLink
;
216 /* no need to delete the atom handle, the handles will all be freed
217 up when destroying the atom handle table! */
219 RtlpFreeAtomTableEntry(CurrentEntry
);
223 RtlpDestroyAtomHandleTable(AtomTable
);
225 RtlpUnlockAtomTable(AtomTable
);
227 RtlpDestroyAtomTableLock(AtomTable
);
229 RtlpFreeAtomTable(AtomTable
);
231 return STATUS_SUCCESS
;
239 RtlEmptyAtomTable(PRTL_ATOM_TABLE AtomTable
,
240 BOOLEAN DeletePinned
)
242 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
243 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
, *PtrEntry
;
245 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
246 AtomTable
, DeletePinned
);
248 if (RtlpLockAtomTable(AtomTable
) == FALSE
)
250 return (STATUS_INVALID_PARAMETER
);
253 /* delete all atoms */
254 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
255 for (CurrentBucket
= AtomTable
->Buckets
;
256 CurrentBucket
!= LastBucket
;
259 NextEntry
= *CurrentBucket
;
260 PtrEntry
= CurrentBucket
;
262 while (NextEntry
!= NULL
)
264 CurrentEntry
= NextEntry
;
265 NextEntry
= NextEntry
->HashLink
;
267 if (DeletePinned
|| !(CurrentEntry
->Flags
& RTL_ATOM_IS_PINNED
))
269 *PtrEntry
= NextEntry
;
271 RtlpFreeAtomHandle(AtomTable
,
274 RtlpFreeAtomTableEntry(CurrentEntry
);
278 PtrEntry
= &CurrentEntry
->HashLink
;
283 RtlpUnlockAtomTable(AtomTable
);
285 return STATUS_SUCCESS
;
293 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
298 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
299 PRTL_ATOM_TABLE_ENTRY Entry
= NULL
;
300 NTSTATUS Status
= STATUS_SUCCESS
;
302 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
303 AtomTable
, AtomName
, Atom
);
305 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
308 if (AtomValue
>= 0xC000)
310 Status
= STATUS_INVALID_PARAMETER
;
312 else if (Atom
!= NULL
)
314 *Atom
= (RTL_ATOM
)AtomValue
;
320 RtlpLockAtomTable(AtomTable
);
322 /* string atom, hash it and try to find an existing atom with the same name */
323 Entry
= RtlpHashAtomName(AtomTable
,
329 /* found another atom, increment the reference counter unless it's pinned */
331 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
333 if (++Entry
->ReferenceCount
== 0)
335 /* FIXME - references overflowed, pin the atom? */
336 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
342 *Atom
= (RTL_ATOM
)Entry
->Atom
;
347 /* couldn't find an existing atom, HashLink now points to either the
348 HashLink pointer of the previous atom or to the bucket so we can
349 simply add it to the list */
350 if (HashLink
!= NULL
)
352 ULONG AtomNameLen
= wcslen(AtomName
);
354 if (AtomNameLen
> RTL_MAXIMUM_ATOM_LENGTH
)
356 Status
= STATUS_INVALID_PARAMETER
;
360 Entry
= RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY
) -
361 sizeof(Entry
->Name
) +
362 (AtomNameLen
+ 1) * sizeof(WCHAR
));
365 Entry
->HashLink
= NULL
;
366 Entry
->ReferenceCount
= 1;
369 Entry
->NameLength
= AtomNameLen
;
370 RtlCopyMemory(Entry
->Name
,
372 (AtomNameLen
+ 1) * sizeof(WCHAR
));
374 if (RtlpCreateAtomHandle(AtomTable
,
377 /* append the atom to the list */
382 *Atom
= (RTL_ATOM
)Entry
->Atom
;
387 RtlpFreeAtomTableEntry(Entry
);
388 Status
= STATUS_NO_MEMORY
;
393 Status
= STATUS_NO_MEMORY
;
398 /* The caller supplied an empty atom name! */
399 Status
= STATUS_OBJECT_NAME_INVALID
;
403 RtlpUnlockAtomTable(AtomTable
);
413 RtlDeleteAtomFromAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
416 PRTL_ATOM_TABLE_ENTRY Entry
;
417 NTSTATUS Status
= STATUS_SUCCESS
;
419 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
424 RtlpLockAtomTable(AtomTable
);
426 Entry
= RtlpGetAtomEntry(AtomTable
,
427 (ULONG
)((USHORT
)Atom
- 0xC000));
429 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
431 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
433 if (--Entry
->ReferenceCount
== 0)
435 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
437 /* it's time to delete the atom. we need to unlink it from
438 the list. The easiest way is to take the atom name and
439 hash it again, this way we get the pointer to either
440 the hash bucket or the previous atom that links to the
441 one we want to delete. This way we can easily bypass
443 if (RtlpHashAtomName(AtomTable
,
447 /* bypass this atom */
448 *HashLink
= Entry
->HashLink
;
450 RtlpFreeAtomHandle(AtomTable
,
453 RtlpFreeAtomTableEntry(Entry
);
457 /* WTF?! This should never happen!!! */
464 /* tried to delete a pinned atom, do nothing and return
465 STATUS_WAS_LOCKED, which is NOT a failure code! */
466 Status
= STATUS_WAS_LOCKED
;
471 Status
= STATUS_INVALID_HANDLE
;
474 RtlpUnlockAtomTable(AtomTable
);
485 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
489 PRTL_ATOM_TABLE_ENTRY Entry
, *HashLink
;
491 RTL_ATOM FoundAtom
= 0;
492 NTSTATUS Status
= STATUS_SUCCESS
;
494 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
495 AtomTable
, AtomName
, Atom
);
497 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
500 if (AtomValue
>= 0xC000)
502 Status
= STATUS_INVALID_PARAMETER
;
504 else if (Atom
!= NULL
)
506 *Atom
= (RTL_ATOM
)AtomValue
;
512 RtlpLockAtomTable(AtomTable
);
513 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
516 Entry
= RtlpHashAtomName(AtomTable
,
521 Status
= STATUS_SUCCESS
;
522 FoundAtom
= (RTL_ATOM
)Entry
->Atom
;
524 RtlpUnlockAtomTable(AtomTable
);
525 if (NT_SUCCESS(Status
) && Atom
!= NULL
)
537 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
540 NTSTATUS Status
= STATUS_SUCCESS
;
542 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
547 PRTL_ATOM_TABLE_ENTRY Entry
;
549 RtlpLockAtomTable(AtomTable
);
551 Entry
= RtlpGetAtomEntry(AtomTable
,
552 (ULONG
)((USHORT
)Atom
- 0xC000));
554 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
556 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
560 Status
= STATUS_INVALID_HANDLE
;
563 RtlpUnlockAtomTable(AtomTable
);
573 * This API is really messed up with regards to NameLength. If you pass in a
574 * valid buffer for AtomName, NameLength should be the size of the buffer
575 * (in bytes, not characters). So if you expect the string to be 6 char long,
576 * you need to allocate a buffer of 7 WCHARs and pass 14 for NameLength.
577 * The AtomName returned is always null terminated. If the NameLength you pass
578 * is smaller than 4 (4 would leave room for 1 character) the function will
579 * return with status STATUS_BUFFER_TOO_SMALL. If you pass more than 4, the
580 * return status will be STATUS_SUCCESS, even if the buffer is not large enough
581 * to hold the complete string. In that case, the string is silently truncated
582 * and made to fit in the provided buffer. On return NameLength is set to the
583 * number of bytes (but EXCLUDING the bytes for the null terminator) copied.
584 * So, if the string is 6 char long, you pass a buffer of 10 bytes, on return
585 * NameLength will be set to 8.
586 * If you pass in a NULL value for AtomName, the length of the string in bytes
587 * (again EXCLUDING the null terminator) is returned in NameLength, at least
588 * on Win2k, XP and ReactOS. NT4 will return 0 in that case.
591 RtlQueryAtomInAtomTable(PRTL_ATOM_TABLE AtomTable
,
603 /* A RTL_ATOM_TABLE_ENTRY has a "WCHAR Name[1]" entry at the end.
604 * Make sure we reserve enough room to facilitate a 12 character name */
605 RTL_ATOM_TABLE_ENTRY AtomTableEntry
;
606 WCHAR StringBuffer
[sizeof(RTL_ATOM_TABLE_ENTRY
) / sizeof(WCHAR
) + 12];
608 PRTL_ATOM_TABLE_ENTRY Entry
;
609 NTSTATUS Status
= STATUS_SUCCESS
;
613 /* Synthesize an entry */
614 NumberEntry
.AtomTableEntry
.Atom
= Atom
;
615 NumberEntry
.AtomTableEntry
.NameLength
= swprintf(NumberEntry
.AtomTableEntry
.Name
,
618 NumberEntry
.AtomTableEntry
.ReferenceCount
= 1;
619 NumberEntry
.AtomTableEntry
.Flags
= RTL_ATOM_IS_PINNED
;
620 Entry
= &NumberEntry
.AtomTableEntry
;
624 RtlpLockAtomTable(AtomTable
);
627 Entry
= RtlpGetAtomEntry(AtomTable
,
628 (ULONG
)((USHORT
)Atom
- 0xC000));
631 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
633 DPRINT("Atom name: %wZ\n", &Entry
->Name
);
635 if (RefCount
!= NULL
)
637 *RefCount
= Entry
->ReferenceCount
;
640 if (PinCount
!= NULL
)
642 *PinCount
= ((Entry
->Flags
& RTL_ATOM_IS_PINNED
) != 0);
645 if (NULL
!= NameLength
)
647 Length
= Entry
->NameLength
* sizeof(WCHAR
);
648 if (NULL
!= AtomName
)
650 if (*NameLength
< Length
+ sizeof(WCHAR
))
654 *NameLength
= Length
;
655 Status
= STATUS_BUFFER_TOO_SMALL
;
659 Length
= *NameLength
- sizeof(WCHAR
);
662 if (NT_SUCCESS(Status
))
664 RtlCopyMemory(AtomName
,
667 AtomName
[Length
/ sizeof(WCHAR
)] = L
'\0';
668 *NameLength
= Length
;
673 *NameLength
= Length
;
676 else if (NULL
!= AtomName
)
678 Status
= STATUS_INVALID_PARAMETER
;
683 Status
= STATUS_INVALID_HANDLE
;
686 if (Unlock
) RtlpUnlockAtomTable(AtomTable
);
693 * @private - only used by NtQueryInformationAtom
696 RtlQueryAtomListInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
697 IN ULONG MaxAtomCount
,
698 OUT ULONG
*AtomCount
,
699 OUT RTL_ATOM
*AtomList
)
701 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
702 PRTL_ATOM_TABLE_ENTRY CurrentEntry
;
704 NTSTATUS Status
= STATUS_SUCCESS
;
706 RtlpLockAtomTable(AtomTable
);
708 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
709 for (CurrentBucket
= AtomTable
->Buckets
;
710 CurrentBucket
!= LastBucket
;
713 CurrentEntry
= *CurrentBucket
;
715 while (CurrentEntry
!= NULL
)
717 if (MaxAtomCount
> 0)
719 *(AtomList
++) = (RTL_ATOM
)CurrentEntry
->Atom
;
724 /* buffer too small, but don't bail. we need to determine the
725 total number of atoms in the table! */
726 Status
= STATUS_INFO_LENGTH_MISMATCH
;
730 CurrentEntry
= CurrentEntry
->HashLink
;
736 RtlpUnlockAtomTable(AtomTable
);