2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
5 * PURPOSE: Atom managment
6 * PROGRAMMER: Thomas Weidenmueller
9 /* INCLUDES *****************************************************************/
16 /* PROTOTYPES ****************************************************************/
18 extern NTSTATUS
RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable
);
19 extern VOID
RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable
);
20 extern BOOLEAN
RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable
);
21 extern VOID
RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable
);
23 extern BOOLEAN
RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable
);
24 extern VOID
RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable
);
26 extern PRTL_ATOM_TABLE
RtlpAllocAtomTable(ULONG Size
);
27 extern VOID
RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable
);
28 extern PRTL_ATOM_TABLE_ENTRY
RtlpAllocAtomTableEntry(ULONG Size
);
29 extern VOID
RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry
);
31 extern BOOLEAN
RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable
, PRTL_ATOM_TABLE_ENTRY Entry
);
32 extern VOID
RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable
, PRTL_ATOM_TABLE_ENTRY Entry
);
33 extern PRTL_ATOM_TABLE_ENTRY
RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable
, ULONG Index
);
35 /* FUNCTIONS *****************************************************************/
40 IN PRTL_ATOM_TABLE AtomTable
,
42 OUT PRTL_ATOM_TABLE_ENTRY
**HashLink
)
47 RtlInitUnicodeString(&Name
, AtomName
);
49 if (Name
.Length
!= 0 &&
50 NT_SUCCESS(RtlHashUnicodeString(&Name
,
52 HASH_STRING_ALGORITHM_X65599
,
55 PRTL_ATOM_TABLE_ENTRY Current
;
56 PRTL_ATOM_TABLE_ENTRY
*Link
;
58 Link
= &AtomTable
->Buckets
[Hash
% AtomTable
->NumberOfBuckets
];
60 /* search for an existing entry */
62 while (Current
!= NULL
)
64 if (Current
->NameLength
== Name
.Length
/ sizeof(WCHAR
) &&
65 !_wcsicmp(Current
->Name
, Name
.Buffer
))
71 Link
= &Current
->HashLink
;
72 Current
= Current
->HashLink
;
75 /* no matching atom found, return the hash link */
90 UNICODE_STRING AtomString
;
95 DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n",
98 if (!((ULONG_PTR
)AtomName
& 0xFFFF0000))
100 LoValue
= (USHORT
)((ULONG_PTR
)AtomName
& 0xFFFF);
105 if (AtomValue
!= NULL
)
106 *AtomValue
= LoValue
;
112 * AtomName cannot be NULL because this
113 * case was caught by the previous test.
115 ASSERT(AtomName
!= NULL
);
117 if (*AtomName
!= L
'#')
124 if ((*p
< L
'0') || (*p
> L
'9'))
131 RtlInitUnicodeString(&AtomString
, p
);
133 DPRINT("AtomString: %wZ\n", &AtomString
);
135 RtlUnicodeStringToInteger(&AtomString
, 10, &LongValue
);
137 DPRINT("LongValue: %lu\n", LongValue
);
139 *AtomValue
= (USHORT
)(LongValue
& 0x0000FFFF);
152 IN OUT PRTL_ATOM_TABLE
*AtomTable
)
154 PRTL_ATOM_TABLE Table
;
157 DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n",
158 TableSize
, AtomTable
);
160 if (*AtomTable
!= NULL
)
162 return STATUS_SUCCESS
;
165 /* Use default if size was incorrect */
166 if (TableSize
<= 1) TableSize
= 37;
168 /* allocate atom table */
169 Table
= RtlpAllocAtomTable(((TableSize
- 1) * sizeof(PRTL_ATOM_TABLE_ENTRY
)) +
170 sizeof(RTL_ATOM_TABLE
));
173 return STATUS_NO_MEMORY
;
176 /* initialize atom table */
177 Table
->NumberOfBuckets
= TableSize
;
179 Status
= RtlpInitAtomTableLock(Table
);
180 if (!NT_SUCCESS(Status
))
182 RtlpFreeAtomTable(Table
);
186 if (!RtlpCreateAtomHandleTable(Table
))
188 RtlpDestroyAtomTableLock(Table
);
189 RtlpFreeAtomTable(Table
);
190 return STATUS_NO_MEMORY
;
194 return STATUS_SUCCESS
;
204 IN PRTL_ATOM_TABLE AtomTable
)
206 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
207 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
;
209 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable
);
211 if (!RtlpLockAtomTable(AtomTable
))
213 return (STATUS_INVALID_PARAMETER
);
216 /* delete all atoms */
217 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
218 for (CurrentBucket
= AtomTable
->Buckets
;
219 CurrentBucket
!= LastBucket
;
222 NextEntry
= *CurrentBucket
;
223 *CurrentBucket
= NULL
;
225 while (NextEntry
!= NULL
)
227 CurrentEntry
= NextEntry
;
228 NextEntry
= NextEntry
->HashLink
;
230 /* no need to delete the atom handle, the handles will all be freed
231 up when destroying the atom handle table! */
233 RtlpFreeAtomTableEntry(CurrentEntry
);
237 RtlpDestroyAtomHandleTable(AtomTable
);
239 RtlpUnlockAtomTable(AtomTable
);
241 RtlpDestroyAtomTableLock(AtomTable
);
243 RtlpFreeAtomTable(AtomTable
);
245 return STATUS_SUCCESS
;
255 PRTL_ATOM_TABLE AtomTable
,
256 BOOLEAN DeletePinned
)
258 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
259 PRTL_ATOM_TABLE_ENTRY CurrentEntry
, NextEntry
, *PtrEntry
;
261 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
262 AtomTable
, DeletePinned
);
264 if (RtlpLockAtomTable(AtomTable
) == FALSE
)
266 return (STATUS_INVALID_PARAMETER
);
269 /* delete all atoms */
270 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
271 for (CurrentBucket
= AtomTable
->Buckets
;
272 CurrentBucket
!= LastBucket
;
275 NextEntry
= *CurrentBucket
;
276 PtrEntry
= CurrentBucket
;
278 while (NextEntry
!= NULL
)
280 CurrentEntry
= NextEntry
;
281 NextEntry
= NextEntry
->HashLink
;
283 if (DeletePinned
|| !(CurrentEntry
->Flags
& RTL_ATOM_IS_PINNED
))
285 *PtrEntry
= NextEntry
;
287 RtlpFreeAtomHandle(AtomTable
, CurrentEntry
);
289 RtlpFreeAtomTableEntry(CurrentEntry
);
293 PtrEntry
= &CurrentEntry
->HashLink
;
298 RtlpUnlockAtomTable(AtomTable
);
300 return STATUS_SUCCESS
;
308 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
313 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
314 PRTL_ATOM_TABLE_ENTRY Entry
= NULL
;
315 NTSTATUS Status
= STATUS_SUCCESS
;
317 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
318 AtomTable
, AtomName
, Atom
);
320 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
323 if (AtomValue
>= 0xC000)
325 Status
= STATUS_INVALID_PARAMETER
;
327 else if (Atom
!= NULL
)
329 *Atom
= (RTL_ATOM
)AtomValue
;
335 RtlpLockAtomTable(AtomTable
);
337 /* string atom, hash it and try to find an existing atom with the same name */
338 Entry
= RtlpHashAtomName(AtomTable
, AtomName
, &HashLink
);
342 /* found another atom, increment the reference counter unless it's pinned */
344 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
346 if (++Entry
->ReferenceCount
== 0)
348 /* FIXME - references overflowed, pin the atom? */
349 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
355 *Atom
= (RTL_ATOM
)Entry
->Atom
;
360 /* couldn't find an existing atom, HashLink now points to either the
361 HashLink pointer of the previous atom or to the bucket so we can
362 simply add it to the list */
363 if (HashLink
!= NULL
)
365 ULONG AtomNameLen
= (ULONG
)wcslen(AtomName
);
367 if (AtomNameLen
> RTL_MAXIMUM_ATOM_LENGTH
)
369 Status
= STATUS_INVALID_PARAMETER
;
373 Entry
= RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY
) -
374 sizeof(Entry
->Name
) +
375 (AtomNameLen
+ 1) * sizeof(WCHAR
));
378 Entry
->HashLink
= NULL
;
379 Entry
->ReferenceCount
= 1;
382 Entry
->NameLength
= (UCHAR
)AtomNameLen
;
383 RtlCopyMemory(Entry
->Name
,
385 (AtomNameLen
+ 1) * sizeof(WCHAR
));
387 if (RtlpCreateAtomHandle(AtomTable
, Entry
))
389 /* append the atom to the list */
394 *Atom
= (RTL_ATOM
)Entry
->Atom
;
399 RtlpFreeAtomTableEntry(Entry
);
400 Status
= STATUS_NO_MEMORY
;
405 Status
= STATUS_NO_MEMORY
;
410 /* The caller supplied an empty atom name! */
411 Status
= STATUS_OBJECT_NAME_INVALID
;
415 RtlpUnlockAtomTable(AtomTable
);
426 RtlDeleteAtomFromAtomTable(
427 IN PRTL_ATOM_TABLE AtomTable
,
430 PRTL_ATOM_TABLE_ENTRY Entry
;
431 NTSTATUS Status
= STATUS_SUCCESS
;
433 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
438 RtlpLockAtomTable(AtomTable
);
440 Entry
= RtlpGetAtomEntry(AtomTable
, (ULONG
)((USHORT
)Atom
- 0xC000));
442 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
444 if (!(Entry
->Flags
& RTL_ATOM_IS_PINNED
))
446 if (--Entry
->ReferenceCount
== 0)
448 PRTL_ATOM_TABLE_ENTRY
*HashLink
;
450 /* it's time to delete the atom. we need to unlink it from
451 the list. The easiest way is to take the atom name and
452 hash it again, this way we get the pointer to either
453 the hash bucket or the previous atom that links to the
454 one we want to delete. This way we can easily bypass
456 if (RtlpHashAtomName(AtomTable
, Entry
->Name
, &HashLink
) != NULL
)
458 /* bypass this atom */
459 *HashLink
= Entry
->HashLink
;
461 RtlpFreeAtomHandle(AtomTable
, Entry
);
463 RtlpFreeAtomTableEntry(Entry
);
467 /* WTF?! This should never happen!!! */
474 /* tried to delete a pinned atom, do nothing and return
475 STATUS_WAS_LOCKED, which is NOT a failure code! */
476 Status
= STATUS_WAS_LOCKED
;
481 Status
= STATUS_INVALID_HANDLE
;
484 RtlpUnlockAtomTable(AtomTable
);
495 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
499 PRTL_ATOM_TABLE_ENTRY Entry
, *HashLink
;
501 RTL_ATOM FoundAtom
= 0;
502 NTSTATUS Status
= STATUS_SUCCESS
;
504 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
505 AtomTable
, AtomName
, Atom
);
507 if (RtlpCheckIntegerAtom (AtomName
, &AtomValue
))
510 if (AtomValue
>= 0xC000)
512 Status
= STATUS_INVALID_PARAMETER
;
514 else if (Atom
!= NULL
)
516 *Atom
= (RTL_ATOM
)AtomValue
;
522 RtlpLockAtomTable(AtomTable
);
523 Status
= STATUS_OBJECT_NAME_NOT_FOUND
;
526 Entry
= RtlpHashAtomName(AtomTable
, AtomName
, &HashLink
);
529 Status
= STATUS_SUCCESS
;
530 FoundAtom
= (RTL_ATOM
)Entry
->Atom
;
533 RtlpUnlockAtomTable(AtomTable
);
534 if (NT_SUCCESS(Status
) && Atom
!= NULL
)
546 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable
,
549 NTSTATUS Status
= STATUS_SUCCESS
;
551 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
556 PRTL_ATOM_TABLE_ENTRY Entry
;
558 RtlpLockAtomTable(AtomTable
);
560 Entry
= RtlpGetAtomEntry(AtomTable
, (ULONG
)((USHORT
)Atom
- 0xC000));
562 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
564 Entry
->Flags
|= RTL_ATOM_IS_PINNED
;
568 Status
= STATUS_INVALID_HANDLE
;
571 RtlpUnlockAtomTable(AtomTable
);
581 * This API is really messed up with regards to NameLength. If you pass in a
582 * valid buffer for AtomName, NameLength should be the size of the buffer
583 * (in bytes, not characters). So if you expect the string to be 6 char long,
584 * you need to allocate a buffer of 7 WCHARs and pass 14 for NameLength.
585 * The AtomName returned is always null terminated. If the NameLength you pass
586 * is smaller than 4 (4 would leave room for 1 character) the function will
587 * return with status STATUS_BUFFER_TOO_SMALL. If you pass more than 4, the
588 * return status will be STATUS_SUCCESS, even if the buffer is not large enough
589 * to hold the complete string. In that case, the string is silently truncated
590 * and made to fit in the provided buffer. On return NameLength is set to the
591 * number of bytes (but EXCLUDING the bytes for the null terminator) copied.
592 * So, if the string is 6 char long, you pass a buffer of 10 bytes, on return
593 * NameLength will be set to 8.
594 * If you pass in a NULL value for AtomName, the length of the string in bytes
595 * (again EXCLUDING the null terminator) is returned in NameLength, at least
596 * on Win2k, XP and ReactOS. NT4 will return 0 in that case.
600 RtlQueryAtomInAtomTable(
601 PRTL_ATOM_TABLE AtomTable
,
613 /* A RTL_ATOM_TABLE_ENTRY has a "WCHAR Name[1]" entry at the end.
614 * Make sure we reserve enough room to facilitate a 12 character name */
615 RTL_ATOM_TABLE_ENTRY AtomTableEntry
;
616 WCHAR StringBuffer
[sizeof(RTL_ATOM_TABLE_ENTRY
) / sizeof(WCHAR
) + 12];
618 PRTL_ATOM_TABLE_ENTRY Entry
;
619 NTSTATUS Status
= STATUS_SUCCESS
;
623 /* Synthesize an entry */
624 NumberEntry
.AtomTableEntry
.Atom
= Atom
;
625 NumberEntry
.AtomTableEntry
.NameLength
= swprintf(NumberEntry
.AtomTableEntry
.Name
,
628 NumberEntry
.AtomTableEntry
.ReferenceCount
= 1;
629 NumberEntry
.AtomTableEntry
.Flags
= RTL_ATOM_IS_PINNED
;
630 Entry
= &NumberEntry
.AtomTableEntry
;
634 RtlpLockAtomTable(AtomTable
);
637 Entry
= RtlpGetAtomEntry(AtomTable
, (ULONG
)((USHORT
)Atom
- 0xC000));
640 if (Entry
!= NULL
&& Entry
->Atom
== (USHORT
)Atom
)
642 DPRINT("Atom name: %wZ\n", &Entry
->Name
);
644 if (RefCount
!= NULL
)
646 *RefCount
= Entry
->ReferenceCount
;
649 if (PinCount
!= NULL
)
651 *PinCount
= ((Entry
->Flags
& RTL_ATOM_IS_PINNED
) != 0);
654 if (NULL
!= NameLength
)
656 Length
= Entry
->NameLength
* sizeof(WCHAR
);
657 if (NULL
!= AtomName
)
659 if (*NameLength
< Length
+ sizeof(WCHAR
))
663 *NameLength
= Length
;
664 Status
= STATUS_BUFFER_TOO_SMALL
;
668 Length
= *NameLength
- sizeof(WCHAR
);
671 if (NT_SUCCESS(Status
))
673 RtlCopyMemory(AtomName
, Entry
->Name
, Length
);
674 AtomName
[Length
/ sizeof(WCHAR
)] = L
'\0';
675 *NameLength
= Length
;
680 *NameLength
= Length
;
683 else if (NULL
!= AtomName
)
685 Status
= STATUS_INVALID_PARAMETER
;
690 Status
= STATUS_INVALID_HANDLE
;
693 if (Unlock
) RtlpUnlockAtomTable(AtomTable
);
700 * @private - only used by NtQueryInformationAtom
704 RtlQueryAtomListInAtomTable(
705 IN PRTL_ATOM_TABLE AtomTable
,
706 IN ULONG MaxAtomCount
,
707 OUT ULONG
*AtomCount
,
708 OUT RTL_ATOM
*AtomList
)
710 PRTL_ATOM_TABLE_ENTRY
*CurrentBucket
, *LastBucket
;
711 PRTL_ATOM_TABLE_ENTRY CurrentEntry
;
713 NTSTATUS Status
= STATUS_SUCCESS
;
715 RtlpLockAtomTable(AtomTable
);
717 LastBucket
= AtomTable
->Buckets
+ AtomTable
->NumberOfBuckets
;
718 for (CurrentBucket
= AtomTable
->Buckets
;
719 CurrentBucket
!= LastBucket
;
722 CurrentEntry
= *CurrentBucket
;
724 while (CurrentEntry
!= NULL
)
726 if (MaxAtomCount
> 0)
728 *(AtomList
++) = (RTL_ATOM
)CurrentEntry
->Atom
;
733 /* buffer too small, but don't bail. we need to determine the
734 total number of atoms in the table! */
735 Status
= STATUS_INFO_LENGTH_MISMATCH
;
739 CurrentEntry
= CurrentEntry
->HashLink
;
745 RtlpUnlockAtomTable(AtomTable
);