[ATL] Add CString.AllocSysString
[reactos.git] / sdk / lib / rtl / atom.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/rtl/atom.c
5 * PURPOSE: Atom managment
6 * PROGRAMMER: Thomas Weidenmueller
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include <rtl.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PROTOTYPES ****************************************************************/
17
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);
22
23 extern BOOLEAN RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable);
24 extern VOID RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable);
25
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);
30
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);
34
35 /* FUNCTIONS *****************************************************************/
36
37 static
38 PRTL_ATOM_TABLE_ENTRY
39 RtlpHashAtomName(
40 IN PRTL_ATOM_TABLE AtomTable,
41 IN PWSTR AtomName,
42 OUT PRTL_ATOM_TABLE_ENTRY **HashLink)
43 {
44 UNICODE_STRING Name;
45 ULONG Hash;
46
47 RtlInitUnicodeString(&Name, AtomName);
48
49 if (Name.Length != 0 &&
50 NT_SUCCESS(RtlHashUnicodeString(&Name,
51 TRUE,
52 HASH_STRING_ALGORITHM_X65599,
53 &Hash)))
54 {
55 PRTL_ATOM_TABLE_ENTRY Current;
56 PRTL_ATOM_TABLE_ENTRY *Link;
57
58 Link = &AtomTable->Buckets[Hash % AtomTable->NumberOfBuckets];
59
60 /* search for an existing entry */
61 Current = *Link;
62 while (Current != NULL)
63 {
64 if (Current->NameLength == Name.Length / sizeof(WCHAR) &&
65 !_wcsicmp(Current->Name, Name.Buffer))
66 {
67 *HashLink = Link;
68 return Current;
69 }
70
71 Link = &Current->HashLink;
72 Current = Current->HashLink;
73 }
74
75 /* no matching atom found, return the hash link */
76 *HashLink = Link;
77 }
78 else
79 *HashLink = NULL;
80
81 return NULL;
82 }
83
84 static
85 BOOLEAN
86 RtlpCheckIntegerAtom(
87 PWSTR AtomName,
88 PUSHORT AtomValue)
89 {
90 UNICODE_STRING AtomString;
91 ULONG LongValue;
92 USHORT LoValue;
93 PWCHAR p;
94
95 DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n",
96 AtomName, AtomValue);
97
98 if (!((ULONG_PTR)AtomName & 0xFFFF0000))
99 {
100 LoValue = (USHORT)((ULONG_PTR)AtomName & 0xFFFF);
101
102 if (LoValue == 0)
103 LoValue = 0xC000;
104
105 if (AtomValue != NULL)
106 *AtomValue = LoValue;
107
108 return TRUE;
109 }
110
111 /*
112 * AtomName cannot be NULL because this
113 * case was caught by the previous test.
114 */
115 ASSERT(AtomName != NULL);
116
117 if (*AtomName != L'#')
118 return FALSE;
119
120 p = AtomName;
121 p++;
122 while (*p)
123 {
124 if ((*p < L'0') || (*p > L'9'))
125 return FALSE;
126 p++;
127 }
128
129 p = AtomName;
130 p++;
131 RtlInitUnicodeString(&AtomString, p);
132
133 DPRINT("AtomString: %wZ\n", &AtomString);
134
135 RtlUnicodeStringToInteger(&AtomString, 10, &LongValue);
136
137 DPRINT("LongValue: %lu\n", LongValue);
138
139 *AtomValue = (USHORT)(LongValue & 0x0000FFFF);
140
141 return TRUE;
142 }
143
144
145 /*
146 * @implemented
147 */
148 NTSTATUS
149 NTAPI
150 RtlCreateAtomTable(
151 IN ULONG TableSize,
152 IN OUT PRTL_ATOM_TABLE *AtomTable)
153 {
154 PRTL_ATOM_TABLE Table;
155 NTSTATUS Status;
156
157 DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n",
158 TableSize, AtomTable);
159
160 if (*AtomTable != NULL)
161 {
162 return STATUS_SUCCESS;
163 }
164
165 /* Use default if size was incorrect */
166 if (TableSize <= 1) TableSize = 37;
167
168 /* allocate atom table */
169 Table = RtlpAllocAtomTable(((TableSize - 1) * sizeof(PRTL_ATOM_TABLE_ENTRY)) +
170 sizeof(RTL_ATOM_TABLE));
171 if (Table == NULL)
172 {
173 return STATUS_NO_MEMORY;
174 }
175
176 /* initialize atom table */
177 Table->NumberOfBuckets = TableSize;
178
179 Status = RtlpInitAtomTableLock(Table);
180 if (!NT_SUCCESS(Status))
181 {
182 RtlpFreeAtomTable(Table);
183 return Status;
184 }
185
186 if (!RtlpCreateAtomHandleTable(Table))
187 {
188 RtlpDestroyAtomTableLock(Table);
189 RtlpFreeAtomTable(Table);
190 return STATUS_NO_MEMORY;
191 }
192
193 *AtomTable = Table;
194 return STATUS_SUCCESS;
195 }
196
197
198 /*
199 * @implemented
200 */
201 NTSTATUS
202 NTAPI
203 RtlDestroyAtomTable(
204 IN PRTL_ATOM_TABLE AtomTable)
205 {
206 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
207 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry;
208
209 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable);
210
211 if (!RtlpLockAtomTable(AtomTable))
212 {
213 return (STATUS_INVALID_PARAMETER);
214 }
215
216 /* delete all atoms */
217 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
218 for (CurrentBucket = AtomTable->Buckets;
219 CurrentBucket != LastBucket;
220 CurrentBucket++)
221 {
222 NextEntry = *CurrentBucket;
223 *CurrentBucket = NULL;
224
225 while (NextEntry != NULL)
226 {
227 CurrentEntry = NextEntry;
228 NextEntry = NextEntry->HashLink;
229
230 /* no need to delete the atom handle, the handles will all be freed
231 up when destroying the atom handle table! */
232
233 RtlpFreeAtomTableEntry(CurrentEntry);
234 }
235 }
236
237 RtlpDestroyAtomHandleTable(AtomTable);
238
239 RtlpUnlockAtomTable(AtomTable);
240
241 RtlpDestroyAtomTableLock(AtomTable);
242
243 RtlpFreeAtomTable(AtomTable);
244
245 return STATUS_SUCCESS;
246 }
247
248
249 /*
250 * @implemented
251 */
252 NTSTATUS
253 NTAPI
254 RtlEmptyAtomTable(
255 PRTL_ATOM_TABLE AtomTable,
256 BOOLEAN DeletePinned)
257 {
258 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
259 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry, *PtrEntry;
260
261 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n",
262 AtomTable, DeletePinned);
263
264 if (RtlpLockAtomTable(AtomTable) == FALSE)
265 {
266 return (STATUS_INVALID_PARAMETER);
267 }
268
269 /* delete all atoms */
270 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
271 for (CurrentBucket = AtomTable->Buckets;
272 CurrentBucket != LastBucket;
273 CurrentBucket++)
274 {
275 NextEntry = *CurrentBucket;
276 PtrEntry = CurrentBucket;
277
278 while (NextEntry != NULL)
279 {
280 CurrentEntry = NextEntry;
281 NextEntry = NextEntry->HashLink;
282
283 if (DeletePinned || !(CurrentEntry->Flags & RTL_ATOM_IS_PINNED))
284 {
285 *PtrEntry = NextEntry;
286
287 RtlpFreeAtomHandle(AtomTable, CurrentEntry);
288
289 RtlpFreeAtomTableEntry(CurrentEntry);
290 }
291 else
292 {
293 PtrEntry = &CurrentEntry->HashLink;
294 }
295 }
296 }
297
298 RtlpUnlockAtomTable(AtomTable);
299
300 return STATUS_SUCCESS;
301 }
302
303
304 /*
305 * @implemented
306 */
307 NTSTATUS NTAPI
308 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable,
309 IN PWSTR AtomName,
310 OUT PRTL_ATOM Atom)
311 {
312 USHORT AtomValue;
313 PRTL_ATOM_TABLE_ENTRY *HashLink;
314 PRTL_ATOM_TABLE_ENTRY Entry = NULL;
315 NTSTATUS Status = STATUS_SUCCESS;
316
317 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n",
318 AtomTable, AtomName, Atom);
319
320 if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
321 {
322 /* integer atom */
323 if (AtomValue >= 0xC000)
324 {
325 Status = STATUS_INVALID_PARAMETER;
326 }
327 else if (Atom != NULL)
328 {
329 *Atom = (RTL_ATOM)AtomValue;
330 }
331
332 return Status;
333 }
334
335 RtlpLockAtomTable(AtomTable);
336
337 /* string atom, hash it and try to find an existing atom with the same name */
338 Entry = RtlpHashAtomName(AtomTable, AtomName, &HashLink);
339
340 if (Entry != NULL)
341 {
342 /* found another atom, increment the reference counter unless it's pinned */
343
344 if (!(Entry->Flags & RTL_ATOM_IS_PINNED))
345 {
346 if (++Entry->ReferenceCount == 0)
347 {
348 /* FIXME - references overflowed, pin the atom? */
349 Entry->Flags |= RTL_ATOM_IS_PINNED;
350 }
351 }
352
353 if (Atom != NULL)
354 {
355 *Atom = (RTL_ATOM)Entry->Atom;
356 }
357 }
358 else
359 {
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)
364 {
365 ULONG AtomNameLen = (ULONG)wcslen(AtomName);
366
367 if (AtomNameLen > RTL_MAXIMUM_ATOM_LENGTH)
368 {
369 Status = STATUS_INVALID_PARAMETER;
370 goto end;
371 }
372
373 Entry = RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY) -
374 sizeof(Entry->Name) +
375 (AtomNameLen + 1) * sizeof(WCHAR));
376 if (Entry != NULL)
377 {
378 Entry->HashLink = NULL;
379 Entry->ReferenceCount = 1;
380 Entry->Flags = 0x0;
381
382 Entry->NameLength = (UCHAR)AtomNameLen;
383 RtlCopyMemory(Entry->Name,
384 AtomName,
385 (AtomNameLen + 1) * sizeof(WCHAR));
386
387 if (RtlpCreateAtomHandle(AtomTable, Entry))
388 {
389 /* append the atom to the list */
390 *HashLink = Entry;
391
392 if (Atom != NULL)
393 {
394 *Atom = (RTL_ATOM)Entry->Atom;
395 }
396 }
397 else
398 {
399 RtlpFreeAtomTableEntry(Entry);
400 Status = STATUS_NO_MEMORY;
401 }
402 }
403 else
404 {
405 Status = STATUS_NO_MEMORY;
406 }
407 }
408 else
409 {
410 /* The caller supplied an empty atom name! */
411 Status = STATUS_OBJECT_NAME_INVALID;
412 }
413 }
414 end:
415 RtlpUnlockAtomTable(AtomTable);
416
417 return Status;
418 }
419
420
421 /*
422 * @implemented
423 */
424 NTSTATUS
425 NTAPI
426 RtlDeleteAtomFromAtomTable(
427 IN PRTL_ATOM_TABLE AtomTable,
428 IN RTL_ATOM Atom)
429 {
430 PRTL_ATOM_TABLE_ENTRY Entry;
431 NTSTATUS Status = STATUS_SUCCESS;
432
433 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n",
434 AtomTable, Atom);
435
436 if (Atom >= 0xC000)
437 {
438 RtlpLockAtomTable(AtomTable);
439
440 Entry = RtlpGetAtomEntry(AtomTable, (ULONG)((USHORT)Atom - 0xC000));
441
442 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
443 {
444 if (!(Entry->Flags & RTL_ATOM_IS_PINNED))
445 {
446 if (--Entry->ReferenceCount == 0)
447 {
448 PRTL_ATOM_TABLE_ENTRY *HashLink;
449
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
455 this item. */
456 if (RtlpHashAtomName(AtomTable, Entry->Name, &HashLink) != NULL)
457 {
458 /* bypass this atom */
459 *HashLink = Entry->HashLink;
460
461 RtlpFreeAtomHandle(AtomTable, Entry);
462
463 RtlpFreeAtomTableEntry(Entry);
464 }
465 else
466 {
467 /* WTF?! This should never happen!!! */
468 ASSERT(FALSE);
469 }
470 }
471 }
472 else
473 {
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;
477 }
478 }
479 else
480 {
481 Status = STATUS_INVALID_HANDLE;
482 }
483
484 RtlpUnlockAtomTable(AtomTable);
485 }
486
487 return Status;
488 }
489
490
491 /*
492 * @implemented
493 */
494 NTSTATUS NTAPI
495 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
496 IN PWSTR AtomName,
497 OUT PRTL_ATOM Atom)
498 {
499 PRTL_ATOM_TABLE_ENTRY Entry, *HashLink;
500 USHORT AtomValue;
501 RTL_ATOM FoundAtom = 0;
502 NTSTATUS Status = STATUS_SUCCESS;
503
504 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n",
505 AtomTable, AtomName, Atom);
506
507 if (RtlpCheckIntegerAtom (AtomName, &AtomValue))
508 {
509 /* integer atom */
510 if (AtomValue >= 0xC000)
511 {
512 Status = STATUS_INVALID_PARAMETER;
513 }
514 else if (Atom != NULL)
515 {
516 *Atom = (RTL_ATOM)AtomValue;
517 }
518
519 return Status;
520 }
521
522 RtlpLockAtomTable(AtomTable);
523 Status = STATUS_OBJECT_NAME_NOT_FOUND;
524
525 /* string atom */
526 Entry = RtlpHashAtomName(AtomTable, AtomName, &HashLink);
527 if (Entry != NULL)
528 {
529 Status = STATUS_SUCCESS;
530 FoundAtom = (RTL_ATOM)Entry->Atom;
531 }
532
533 RtlpUnlockAtomTable(AtomTable);
534 if (NT_SUCCESS(Status) && Atom != NULL)
535 {
536 *Atom = FoundAtom;
537 }
538 return Status;
539 }
540
541
542 /*
543 * @implemented
544 */
545 NTSTATUS NTAPI
546 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,
547 IN RTL_ATOM Atom)
548 {
549 NTSTATUS Status = STATUS_SUCCESS;
550
551 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n",
552 AtomTable, Atom);
553
554 if (Atom >= 0xC000)
555 {
556 PRTL_ATOM_TABLE_ENTRY Entry;
557
558 RtlpLockAtomTable(AtomTable);
559
560 Entry = RtlpGetAtomEntry(AtomTable, (ULONG)((USHORT)Atom - 0xC000));
561
562 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
563 {
564 Entry->Flags |= RTL_ATOM_IS_PINNED;
565 }
566 else
567 {
568 Status = STATUS_INVALID_HANDLE;
569 }
570
571 RtlpUnlockAtomTable(AtomTable);
572 }
573
574 return Status;
575 }
576
577
578 /*
579 * @implemented
580 *
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.
597 */
598 NTSTATUS
599 NTAPI
600 RtlQueryAtomInAtomTable(
601 PRTL_ATOM_TABLE AtomTable,
602 RTL_ATOM Atom,
603 PULONG RefCount,
604 PULONG PinCount,
605 PWSTR AtomName,
606 PULONG NameLength)
607 {
608 ULONG Length;
609 BOOL Unlock = FALSE;
610
611 union
612 {
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];
617 } NumberEntry;
618 PRTL_ATOM_TABLE_ENTRY Entry;
619 NTSTATUS Status = STATUS_SUCCESS;
620
621 if (Atom < 0xC000)
622 {
623 /* Synthesize an entry */
624 NumberEntry.AtomTableEntry.Atom = Atom;
625 NumberEntry.AtomTableEntry.NameLength = swprintf(NumberEntry.AtomTableEntry.Name,
626 L"#%lu",
627 (ULONG)Atom);
628 NumberEntry.AtomTableEntry.ReferenceCount = 1;
629 NumberEntry.AtomTableEntry.Flags = RTL_ATOM_IS_PINNED;
630 Entry = &NumberEntry.AtomTableEntry;
631 }
632 else
633 {
634 RtlpLockAtomTable(AtomTable);
635 Unlock = TRUE;
636
637 Entry = RtlpGetAtomEntry(AtomTable, (ULONG)((USHORT)Atom - 0xC000));
638 }
639
640 if (Entry != NULL && Entry->Atom == (USHORT)Atom)
641 {
642 DPRINT("Atom name: %wZ\n", &Entry->Name);
643
644 if (RefCount != NULL)
645 {
646 *RefCount = Entry->ReferenceCount;
647 }
648
649 if (PinCount != NULL)
650 {
651 *PinCount = ((Entry->Flags & RTL_ATOM_IS_PINNED) != 0);
652 }
653
654 if (NULL != NameLength)
655 {
656 Length = Entry->NameLength * sizeof(WCHAR);
657 if (NULL != AtomName)
658 {
659 if (*NameLength < Length + sizeof(WCHAR))
660 {
661 if (*NameLength < 4)
662 {
663 *NameLength = Length;
664 Status = STATUS_BUFFER_TOO_SMALL;
665 }
666 else
667 {
668 Length = *NameLength - sizeof(WCHAR);
669 }
670 }
671 if (NT_SUCCESS(Status))
672 {
673 RtlCopyMemory(AtomName, Entry->Name, Length);
674 AtomName[Length / sizeof(WCHAR)] = L'\0';
675 *NameLength = Length;
676 }
677 }
678 else
679 {
680 *NameLength = Length;
681 }
682 }
683 else if (NULL != AtomName)
684 {
685 Status = STATUS_INVALID_PARAMETER;
686 }
687 }
688 else
689 {
690 Status = STATUS_INVALID_HANDLE;
691 }
692
693 if (Unlock) RtlpUnlockAtomTable(AtomTable);
694
695 return Status;
696 }
697
698
699 /*
700 * @private - only used by NtQueryInformationAtom
701 */
702 NTSTATUS
703 NTAPI
704 RtlQueryAtomListInAtomTable(
705 IN PRTL_ATOM_TABLE AtomTable,
706 IN ULONG MaxAtomCount,
707 OUT ULONG *AtomCount,
708 OUT RTL_ATOM *AtomList)
709 {
710 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket;
711 PRTL_ATOM_TABLE_ENTRY CurrentEntry;
712 ULONG Atoms = 0;
713 NTSTATUS Status = STATUS_SUCCESS;
714
715 RtlpLockAtomTable(AtomTable);
716
717 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets;
718 for (CurrentBucket = AtomTable->Buckets;
719 CurrentBucket != LastBucket;
720 CurrentBucket++)
721 {
722 CurrentEntry = *CurrentBucket;
723
724 while (CurrentEntry != NULL)
725 {
726 if (MaxAtomCount > 0)
727 {
728 *(AtomList++) = (RTL_ATOM)CurrentEntry->Atom;
729 MaxAtomCount--;
730 }
731 else
732 {
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;
736 }
737
738 Atoms++;
739 CurrentEntry = CurrentEntry->HashLink;
740 }
741 }
742
743 *AtomCount = Atoms;
744
745 RtlpUnlockAtomTable(AtomTable);
746
747 return Status;
748 }
749