Process Hacker
basesup.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * base support functions
4  *
5  * Copyright (C) 2009-2015 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * This file contains basic low-level code as well as general algorithms and data structures.
25  *
26  * Memory allocation. PhAllocate is a wrapper around RtlAllocateHeap, and always allocates
27  * from the phlib heap. PhAllocatePage is a wrapper around NtAllocateVirtualMemory and allocates
28  * pages.
29  *
30  * Null-terminated strings. The Ph*StringZ functions manipulate null-terminated strings. The
31  * copying functions provide a simple way to copy strings which may not be null-terminated, but
32  * have a specified limit.
33  *
34  * String. The design of the string object was chosen for maximum compatibility. As such each
35  * string buffer must be null-terminated, and each object contains an embedded PH_STRINGREF
36  * structure. Note that efficient sub-string creation (no copying, only references the parent
37  * string object) could not be implemented due to the mandatory null-termination. String objects
38  * must be regarded as immutable (for thread-safety reasons) unless the object has just been
39  * created and no references have been shared.
40  *
41  * String builder. This is a set of functions which allow for efficient modification of strings.
42  * For performance reasons, these functions modify string objects directly, even though they are
43  * normally immutable.
44  *
45  * List. A simple PVOID list that resizes itself when needed.
46  *
47  * Pointer list. Similar to the normal list object, but uses a free list in order to support
48  * constant time insertion and deletion. In order for the free list to work, normal entries
49  * have their lowest bit clear while free entries have their lowest bit set, with the index of
50  * the next free entry in the upper bits.
51  *
52  * Hashtable. A hashtable with power-of-two bucket sizes and with all entries stored in a
53  * single array. This improves locality but may be inefficient when resizing the hashtable. It
54  * is a good idea to store pointers to objects as entries, as opposed to the objects themselves.
55  *
56  * Simple hashtable. A wrapper around the normal hashtable, with PVOID keys and PVOID values.
57  *
58  * Free list. A thread-safe memory allocation method where freed blocks are stored in a S-list,
59  * and allocations are made from this list whenever possible.
60  *
61  * Callback. A thread-safe notification mechanism where clients can register callback functions
62  * which are then invoked by other code.
63  */
64 
65 #include <phbase.h>
66 #include <phintrnl.h>
67 #include <math.h>
68 
69 #define PH_VECTOR_LEVEL_NONE 0
70 #define PH_VECTOR_LEVEL_SSE2 1
71 #define PH_VECTOR_LEVEL_AVX 2
72 
73 typedef struct _PHP_BASE_THREAD_CONTEXT
74 {
75  PUSER_THREAD_START_ROUTINE StartAddress;
76  PVOID Parameter;
78 
80  _In_ PVOID Object,
81  _In_ ULONG Flags
82  );
83 
85  _In_ PVOID Object,
86  _In_ ULONG Flags
87  );
88 
90  _In_ PVOID Object,
91  _In_ ULONG Flags
92  );
93 
95  _In_ PVOID Object,
96  _In_ ULONG Flags
97  );
98 
99 // Types
100 
106 
107 // Misc.
108 
109 static BOOLEAN PhpVectorLevel = PH_VECTOR_LEVEL_NONE;
110 static PPH_STRING PhSharedEmptyString = NULL;
111 
112 // Threads
113 
114 static PH_FREE_LIST PhpBaseThreadContextFreeList;
115 #ifdef DEBUG
116 ULONG PhDbgThreadDbgTlsIndex;
117 LIST_ENTRY PhDbgThreadListHead;
118 PH_QUEUED_LOCK PhDbgThreadListLock = PH_QUEUED_LOCK_INIT;
119 #endif
120 
121 // Data
122 
123 static ULONG PhpPrimeNumbers[] =
124 {
125  0x3, 0x7, 0xb, 0x11, 0x17, 0x1d, 0x25, 0x2f, 0x3b, 0x47, 0x59, 0x6b, 0x83,
126  0xa3, 0xc5, 0xef, 0x125, 0x161, 0x1af, 0x209, 0x277, 0x2f9, 0x397, 0x44f,
127  0x52f, 0x63d, 0x78b, 0x91d, 0xaf1, 0xd2b, 0xfd1, 0x12fd, 0x16cf, 0x1b65,
128  0x20e3, 0x2777, 0x2f6f, 0x38ff, 0x446f, 0x521f, 0x628d, 0x7655, 0x8e01,
129  0xaa6b, 0xcc89, 0xf583, 0x126a7, 0x1619b, 0x1a857, 0x1fd3b, 0x26315, 0x2dd67,
130  0x3701b, 0x42023, 0x4f361, 0x5f0ed, 0x72125, 0x88e31, 0xa443b, 0xc51eb,
131  0xec8c1, 0x11bdbf, 0x154a3f, 0x198c4f, 0x1ea867, 0x24ca19, 0x2c25c1, 0x34fa1b,
132  0x3f928f, 0x4c4987, 0x5b8b6f, 0x6dda89
133 };
134 
139  _In_ ULONG Flags
140  )
141 {
142  PH_OBJECT_TYPE_PARAMETERS parameters;
143 
144  // The following relies on the (technically undefined) value of XState being zero before Windows 7 SP1.
145  // NOTE: This is unused for now.
146  /*if (USER_SHARED_DATA->XState.EnabledFeatures & XSTATE_MASK_AVX)
147  PhpVectorLevel = PH_VECTOR_LEVEL_AVX;
148  else*/ if (USER_SHARED_DATA->ProcessorFeatures[PF_XMMI64_INSTRUCTIONS_AVAILABLE])
149  PhpVectorLevel = PH_VECTOR_LEVEL_SSE2;
150 
151  PhStringType = PhCreateObjectType(L"String", 0, NULL);
152  PhBytesType = PhCreateObjectType(L"Bytes", 0, NULL);
153 
154  parameters.FreeListSize = sizeof(PH_LIST);
155  parameters.FreeListCount = 128;
156 
157  PhListType = PhCreateObjectTypeEx(L"List", PH_OBJECT_TYPE_USE_FREE_LIST, PhpListDeleteProcedure, &parameters);
158  PhPointerListType = PhCreateObjectType(L"PointerList", 0, PhpPointerListDeleteProcedure);
159 
160  parameters.FreeListSize = sizeof(PH_HASHTABLE);
161  parameters.FreeListCount = 64;
162 
163  PhHashtableType = PhCreateObjectTypeEx(L"Hashtable", PH_OBJECT_TYPE_USE_FREE_LIST, PhpHashtableDeleteProcedure, &parameters);
164 
165  PhInitializeFreeList(&PhpBaseThreadContextFreeList, sizeof(PHP_BASE_THREAD_CONTEXT), 16);
166 
167 #ifdef DEBUG
168  PhDbgThreadDbgTlsIndex = TlsAlloc();
169  InitializeListHead(&PhDbgThreadListHead);
170 #endif
171 
172  if (Flags & PHLIB_INIT_MODULE_WORK_QUEUE)
174  if (Flags & PHLIB_INIT_MODULE_HANDLE_TABLE)
176 
177  return TRUE;
178 }
179 
181  _In_ PVOID Parameter
182  )
183 {
184  NTSTATUS status;
185  HRESULT result;
186  PHP_BASE_THREAD_CONTEXT context;
187 #ifdef DEBUG
188  PHP_BASE_THREAD_DBG dbg;
189 #endif
190 
191  context = *(PPHP_BASE_THREAD_CONTEXT)Parameter;
192  PhFreeToFreeList(&PhpBaseThreadContextFreeList, Parameter);
193 
194 #ifdef DEBUG
195  dbg.ClientId = NtCurrentTeb()->ClientId;
196 
197  dbg.StartAddress = context.StartAddress;
198  dbg.Parameter = context.Parameter;
199  dbg.CurrentAutoPool = NULL;
200 
201  PhAcquireQueuedLockExclusive(&PhDbgThreadListLock);
202  InsertTailList(&PhDbgThreadListHead, &dbg.ListEntry);
203  PhReleaseQueuedLockExclusive(&PhDbgThreadListLock);
204 
205  TlsSetValue(PhDbgThreadDbgTlsIndex, &dbg);
206 #endif
207 
208  // Initialization code
209 
210  result = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
211 
212  // Call the user-supplied function.
213  status = context.StartAddress(context.Parameter);
214 
215  // De-initialization code
216 
217  if (result == S_OK || result == S_FALSE)
218  CoUninitialize();
219 
220 #ifdef DEBUG
221  PhAcquireQueuedLockExclusive(&PhDbgThreadListLock);
222  RemoveEntryList(&dbg.ListEntry);
223  PhReleaseQueuedLockExclusive(&PhDbgThreadListLock);
224 #endif
225 
226  return status;
227 }
228 
237  _In_opt_ SIZE_T StackSize,
238  _In_ PUSER_THREAD_START_ROUTINE StartAddress,
239  _In_opt_ PVOID Parameter
240  )
241 {
242  HANDLE threadHandle;
243  PPHP_BASE_THREAD_CONTEXT context;
244 
245  context = PhAllocateFromFreeList(&PhpBaseThreadContextFreeList);
246  context->StartAddress = StartAddress;
247  context->Parameter = Parameter;
248 
249  threadHandle = CreateThread(
250  NULL,
251  StackSize,
253  context,
254  0,
255  NULL
256  );
257 
258  if (threadHandle)
259  {
260  PHLIB_INC_STATISTIC(BaseThreadsCreated);
261  return threadHandle;
262  }
263  else
264  {
265  PHLIB_INC_STATISTIC(BaseThreadsCreateFailed);
266  PhFreeToFreeList(&PhpBaseThreadContextFreeList, context);
267  return NULL;
268  }
269 }
270 
278  _Out_ PLARGE_INTEGER SystemTime
279  )
280 {
281  do
282  {
283  SystemTime->HighPart = USER_SHARED_DATA->SystemTime.High1Time;
284  SystemTime->LowPart = USER_SHARED_DATA->SystemTime.LowPart;
285  } while (SystemTime->HighPart != USER_SHARED_DATA->SystemTime.High2Time);
286 }
287 
292  _Out_ PLARGE_INTEGER TimeZoneBias
293  )
294 {
295  do
296  {
297  TimeZoneBias->HighPart = USER_SHARED_DATA->TimeZoneBias.High1Time;
298  TimeZoneBias->LowPart = USER_SHARED_DATA->TimeZoneBias.LowPart;
299  } while (TimeZoneBias->HighPart != USER_SHARED_DATA->TimeZoneBias.High2Time);
300 }
301 
313  _In_ PLARGE_INTEGER SystemTime,
314  _Out_ PLARGE_INTEGER LocalTime
315  )
316 {
317  LARGE_INTEGER timeZoneBias;
318 
319  PhQueryTimeZoneBias(&timeZoneBias);
320  LocalTime->QuadPart = SystemTime->QuadPart - timeZoneBias.QuadPart;
321 }
322 
334  _In_ PLARGE_INTEGER LocalTime,
335  _Out_ PLARGE_INTEGER SystemTime
336  )
337 {
338  LARGE_INTEGER timeZoneBias;
339 
340  PhQueryTimeZoneBias(&timeZoneBias);
341  SystemTime->QuadPart = LocalTime->QuadPart + timeZoneBias.QuadPart;
342 }
343 
358 _Check_return_
359 _Ret_notnull_
361 PVOID PhAllocate(
362  _In_ SIZE_T Size
363  )
364 {
365  return RtlAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Size);
366 }
367 
377  _In_ SIZE_T Size
378  )
379 {
380  return RtlAllocateHeap(PhHeapHandle, 0, Size);
381 }
382 
393  _In_ SIZE_T Size,
394  _In_ ULONG Flags
395  )
396 {
397  return RtlAllocateHeap(PhHeapHandle, Flags, Size);
398 }
399 
407  _Frees_ptr_opt_ PVOID Memory
408  )
409 {
410  RtlFreeHeap(PhHeapHandle, 0, Memory);
411 }
412 
430 PVOID PhReAllocate(
431  _Frees_ptr_opt_ PVOID Memory,
432  _In_ SIZE_T Size
433  )
434 {
435  return RtlReAllocateHeap(PhHeapHandle, HEAP_GENERATE_EXCEPTIONS, Memory, Size);
436 }
437 
452  _In_ PVOID Memory,
453  _In_ SIZE_T Size
454  )
455 {
456  return RtlReAllocateHeap(PhHeapHandle, 0, Memory, Size);
457 }
458 
470 _Check_return_
471 _Ret_maybenull_
473  _In_ SIZE_T Size,
474  _Out_opt_ PSIZE_T NewSize
475  )
476 {
477  PVOID baseAddress;
478 
479  baseAddress = NULL;
480 
482  NtCurrentProcess(),
483  &baseAddress,
484  0,
485  &Size,
486  MEM_COMMIT,
487  PAGE_READWRITE
488  )))
489  {
490  if (NewSize)
491  *NewSize = Size;
492 
493  return baseAddress;
494  }
495  else
496  {
497  return NULL;
498  }
499 }
500 
507  _Frees_ptr_opt_ PVOID Memory
508  )
509 {
510  SIZE_T size;
511 
512  size = 0;
513 
515  NtCurrentProcess(),
516  &Memory,
517  &size,
518  MEM_RELEASE
519  );
520 }
521 
528  _In_ PWSTR String
529  )
530 {
531  if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2)
532  {
533  PWSTR p;
534  ULONG unaligned;
535  __m128i b;
536  __m128i z;
537  ULONG mask;
538  ULONG index;
539 
540  p = (PWSTR)((ULONG_PTR)String & ~0xe); // String should be 2 byte aligned
541  unaligned = (ULONG)String & 0xf;
542  z = _mm_setzero_si128();
543 
544  if (unaligned != 0)
545  {
546  b = _mm_load_si128((__m128i *)p);
547  b = _mm_cmpeq_epi16(b, z);
548  mask = _mm_movemask_epi8(b) >> unaligned;
549 
550  if (_BitScanForward(&index, mask))
551  return index / sizeof(WCHAR);
552 
553  p += 16 / sizeof(WCHAR);
554  }
555 
556  while (TRUE)
557  {
558  b = _mm_load_si128((__m128i *)p);
559  b = _mm_cmpeq_epi16(b, z);
560  mask = _mm_movemask_epi8(b);
561 
562  if (_BitScanForward(&index, mask))
563  return (SIZE_T)(p - String) + index / sizeof(WCHAR);
564 
565  p += 16 / sizeof(WCHAR);
566  }
567  }
568  else
569  {
570  return wcslen(String);
571  }
572 }
573 
582  _In_ PSTR String
583  )
584 {
585  PSTR newString;
586  SIZE_T length;
587 
588  length = strlen(String) + 1; // include the null terminator
589 
590  newString = PhAllocate(length);
591  memcpy(newString, String, length);
592 
593  return newString;
594 }
595 
605  _In_ PSTR String
606  )
607 {
608  PSTR newString;
609  SIZE_T length;
610 
611  length = strlen(String) + 1; // include the null terminator
612 
613  newString = PhAllocateSafe(length);
614 
615  if (!newString)
616  return NULL;
617 
618  memcpy(newString, String, length);
619 
620  return newString;
621 }
622 
631  _In_ PWSTR String
632  )
633 {
634  PWSTR newString;
635  SIZE_T length;
636 
637  length = (PhCountStringZ(String) + 1) * sizeof(WCHAR); // include the null terminator
638 
639  newString = PhAllocate(length);
640  memcpy(newString, String, length);
641 
642  return newString;
643 }
644 
671 BOOLEAN PhCopyBytesZ(
672  _In_ PSTR InputBuffer,
673  _In_ SIZE_T InputCount,
674  _Out_writes_opt_z_(OutputCount) PSTR OutputBuffer,
675  _In_ SIZE_T OutputCount,
676  _Out_opt_ PSIZE_T ReturnCount
677  )
678 {
679  SIZE_T i;
680  BOOLEAN copied;
681 
682  // Determine the length of the input string.
683 
684  if (InputCount != -1)
685  {
686  i = 0;
687 
688  while (i < InputCount && InputBuffer[i])
689  i++;
690  }
691  else
692  {
693  i = strlen(InputBuffer);
694  }
695 
696  // Copy the string if there is enough room.
697 
698  if (OutputBuffer && OutputCount >= i + 1) // need one character for null terminator
699  {
700  memcpy(OutputBuffer, InputBuffer, i);
701  OutputBuffer[i] = 0;
702  copied = TRUE;
703  }
704  else
705  {
706  copied = FALSE;
707  }
708 
709  if (ReturnCount)
710  *ReturnCount = i + 1;
711 
712  return copied;
713 }
714 
742  _In_ PWSTR InputBuffer,
743  _In_ SIZE_T InputCount,
744  _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer,
745  _In_ SIZE_T OutputCount,
746  _Out_opt_ PSIZE_T ReturnCount
747  )
748 {
749  SIZE_T i;
750  BOOLEAN copied;
751 
752  // Determine the length of the input string.
753 
754  if (InputCount != -1)
755  {
756  i = 0;
757 
758  while (i < InputCount && InputBuffer[i])
759  i++;
760  }
761  else
762  {
763  i = PhCountStringZ(InputBuffer);
764  }
765 
766  // Copy the string if there is enough room.
767 
768  if (OutputBuffer && OutputCount >= i + 1) // need one character for null terminator
769  {
770  memcpy(OutputBuffer, InputBuffer, i * sizeof(WCHAR));
771  OutputBuffer[i] = 0;
772  copied = TRUE;
773  }
774  else
775  {
776  copied = FALSE;
777  }
778 
779  if (ReturnCount)
780  *ReturnCount = i + 1;
781 
782  return copied;
783 }
784 
812  _In_ PSTR InputBuffer,
813  _In_ SIZE_T InputCount,
814  _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer,
815  _In_ SIZE_T OutputCount,
816  _Out_opt_ PSIZE_T ReturnCount
817  )
818 {
819  SIZE_T i;
820  BOOLEAN copied;
821 
822  // Determine the length of the input string.
823 
824  if (InputCount != -1)
825  {
826  i = 0;
827 
828  while (i < InputCount && InputBuffer[i])
829  i++;
830  }
831  else
832  {
833  i = strlen(InputBuffer);
834  }
835 
836  // Copy the string if there is enough room.
837 
838  if (OutputBuffer && OutputCount >= i + 1) // need one character for null terminator
839  {
840  PhZeroExtendToUtf16Buffer(InputBuffer, i, OutputBuffer);
841  OutputBuffer[i] = 0;
842  copied = TRUE;
843  }
844  else
845  {
846  copied = FALSE;
847  }
848 
849  if (ReturnCount)
850  *ReturnCount = i + 1;
851 
852  return copied;
853 }
854 
882  _In_ PSTR InputBuffer,
883  _In_ SIZE_T InputCount,
884  _Out_writes_opt_z_(OutputCount) PWSTR OutputBuffer,
885  _In_ SIZE_T OutputCount,
886  _Out_opt_ PSIZE_T ReturnCount
887  )
888 {
889  NTSTATUS status;
890  SIZE_T i;
891  ULONG unicodeBytes;
892  BOOLEAN copied;
893 
894  // Determine the length of the input string.
895 
896  if (InputCount != -1)
897  {
898  i = 0;
899 
900  while (i < InputCount && InputBuffer[i])
901  i++;
902  }
903  else
904  {
905  i = strlen(InputBuffer);
906  }
907 
908  // Determine the length of the output string.
909 
910  status = RtlMultiByteToUnicodeSize(
911  &unicodeBytes,
912  InputBuffer,
913  (ULONG)i
914  );
915 
916  if (!NT_SUCCESS(status))
917  {
918  if (ReturnCount)
919  *ReturnCount = -1;
920 
921  return FALSE;
922  }
923 
924  // Convert the string to Unicode if there is enough room.
925 
926  if (OutputBuffer && OutputCount >= unicodeBytes / sizeof(WCHAR) + 1)
927  {
928  status = RtlMultiByteToUnicodeN(
929  OutputBuffer,
930  unicodeBytes,
931  NULL,
932  InputBuffer,
933  (ULONG)i
934  );
935 
936  if (NT_SUCCESS(status))
937  {
938  // RtlMultiByteToUnicodeN doesn't null terminate the string.
939  *(PWCHAR)((PCHAR)OutputBuffer + unicodeBytes) = 0;
940  copied = TRUE;
941  }
942  else
943  {
944  copied = FALSE;
945  }
946  }
947  else
948  {
949  copied = FALSE;
950  }
951 
952  if (ReturnCount)
953  *ReturnCount = unicodeBytes / sizeof(WCHAR) + 1;
954 
955  return copied;
956 }
957 
959  _In_ PWSTR A,
960  _In_ PWSTR B
961  )
962 {
963  LONG bias = 0;
964 
965  for (; ; A++, B++)
966  {
967  if (!PhIsDigitCharacter(*A) && !PhIsDigitCharacter(*B))
968  {
969  return bias;
970  }
971  else if (!PhIsDigitCharacter(*A))
972  {
973  return -1;
974  }
975  else if (!PhIsDigitCharacter(*B))
976  {
977  return 1;
978  }
979  else if (*A < *B)
980  {
981  if (bias == 0)
982  bias = -1;
983  }
984  else if (*A > *B)
985  {
986  if (bias == 0)
987  bias = 1;
988  }
989  else if (!*A && !*B)
990  {
991  return bias;
992  }
993  }
994 
995  return 0;
996 }
997 
999  _In_ PWSTR A,
1000  _In_ PWSTR B
1001  )
1002 {
1003  for (; ; A++, B++)
1004  {
1005  if (!PhIsDigitCharacter(*A) && !PhIsDigitCharacter(*B))
1006  {
1007  return 0;
1008  }
1009  else if (!PhIsDigitCharacter(*A))
1010  {
1011  return -1;
1012  }
1013  else if (!PhIsDigitCharacter(*B))
1014  {
1015  return 1;
1016  }
1017  else if (*A < *B)
1018  {
1019  return -1;
1020  }
1021  else if (*A > *B)
1022  {
1023  return 1;
1024  }
1025  }
1026 
1027  return 0;
1028 }
1029 
1031  _In_ PWSTR A,
1032  _In_ PWSTR B,
1033  _In_ BOOLEAN IgnoreCase
1034  )
1035 {
1036  /* strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
1037  Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>
1038 
1039  This software is provided 'as-is', without any express or implied
1040  warranty. In no event will the authors be held liable for any damages
1041  arising from the use of this software.
1042 
1043  Permission is granted to anyone to use this software for any purpose,
1044  including commercial applications, and to alter it and redistribute it
1045  freely, subject to the following restrictions:
1046 
1047  1. The origin of this software must not be misrepresented; you must not
1048  claim that you wrote the original software. If you use this software
1049  in a product, an acknowledgment in the product documentation would be
1050  appreciated but is not required.
1051  2. Altered source versions must be plainly marked as such, and must not be
1052  misrepresented as being the original software.
1053  3. This notice may not be removed or altered from any source distribution.
1054 
1055  This code has been modified for Process Hacker.
1056  */
1057 
1058  ULONG ai, bi;
1059  WCHAR ca, cb;
1060  LONG result;
1061  BOOLEAN fractional;
1062 
1063  ai = 0;
1064  bi = 0;
1065 
1066  while (TRUE)
1067  {
1068  ca = A[ai];
1069  cb = B[bi];
1070 
1071  /* Skip over leading spaces or zeros. */
1072 
1073  while (ca == ' ')
1074  ca = A[++ai];
1075 
1076  while (cb == ' ')
1077  cb = B[++bi];
1078 
1079  /* Process run of digits. */
1080  if (PhIsDigitCharacter(ca) && PhIsDigitCharacter(cb))
1081  {
1082  fractional = (ca == '0' || cb == '0');
1083 
1084  if (fractional)
1085  {
1086  if ((result = PhpCompareLeftNatural(A + ai, B + bi)) != 0)
1087  return result;
1088  }
1089  else
1090  {
1091  if ((result = PhpCompareRightNatural(A + ai, B + bi)) != 0)
1092  return result;
1093  }
1094  }
1095 
1096  if (!ca && !cb)
1097  {
1098  /* Strings are considered the same. */
1099  return 0;
1100  }
1101 
1102  if (IgnoreCase)
1103  {
1104  ca = towupper(ca);
1105  cb = towupper(cb);
1106  }
1107 
1108  if (ca < cb)
1109  return -1;
1110  else if (ca > cb)
1111  return 1;
1112 
1113  ai++;
1114  bi++;
1115  }
1116 }
1117 
1126  _In_ PWSTR A,
1127  _In_ PWSTR B,
1128  _In_ BOOLEAN IgnoreCase
1129  )
1130 {
1131  if (!IgnoreCase)
1132  return PhpCompareStringZNatural(A, B, FALSE);
1133  else
1134  return PhpCompareStringZNatural(A, B, TRUE);
1135 }
1136 
1146  _In_ PPH_STRINGREF String1,
1147  _In_ PPH_STRINGREF String2,
1148  _In_ BOOLEAN IgnoreCase
1149  )
1150 {
1151  SIZE_T l1;
1152  SIZE_T l2;
1153  PWCHAR s1;
1154  PWCHAR s2;
1155  WCHAR c1;
1156  WCHAR c2;
1157  PWCHAR end;
1158 
1159  // Note: this function assumes that the difference between the lengths of
1160  // the two strings can fit inside a LONG.
1161 
1162  l1 = String1->Length;
1163  l2 = String2->Length;
1164  assert(!(l1 & 1));
1165  assert(!(l2 & 1));
1166  s1 = String1->Buffer;
1167  s2 = String2->Buffer;
1168 
1169  end = (PWCHAR)((PCHAR)s1 + (l1 <= l2 ? l1 : l2));
1170 
1171  if (!IgnoreCase)
1172  {
1173  while (s1 != end)
1174  {
1175  c1 = *s1;
1176  c2 = *s2;
1177 
1178  if (c1 != c2)
1179  return (LONG)c1 - (LONG)c2;
1180 
1181  s1++;
1182  s2++;
1183  }
1184  }
1185  else
1186  {
1187  while (s1 != end)
1188  {
1189  c1 = *s1;
1190  c2 = *s2;
1191 
1192  if (c1 != c2)
1193  {
1194  c1 = RtlUpcaseUnicodeChar(c1);
1195  c2 = RtlUpcaseUnicodeChar(c2);
1196 
1197  if (c1 != c2)
1198  return (LONG)c1 - (LONG)c2;
1199  }
1200 
1201  s1++;
1202  s2++;
1203  }
1204  }
1205 
1206  return (LONG)(l1 - l2);
1207 }
1208 
1218  _In_ PPH_STRINGREF String1,
1219  _In_ PPH_STRINGREF String2,
1220  _In_ BOOLEAN IgnoreCase
1221  )
1222 {
1223  SIZE_T l1;
1224  SIZE_T l2;
1225  PWSTR s1;
1226  PWSTR s2;
1227  WCHAR c1;
1228  WCHAR c2;
1229  SIZE_T length;
1230 
1231  l1 = String1->Length;
1232  l2 = String2->Length;
1233  assert(!(l1 & 1));
1234  assert(!(l2 & 1));
1235 
1236  if (l1 != l2)
1237  return FALSE;
1238 
1239  s1 = String1->Buffer;
1240  s2 = String2->Buffer;
1241 
1242  if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2)
1243  {
1244  length = l1 / 16;
1245 
1246  if (length != 0)
1247  {
1248  __m128i b1;
1249  __m128i b2;
1250 
1251  do
1252  {
1253  b1 = _mm_loadu_si128((__m128i *)s1);
1254  b2 = _mm_loadu_si128((__m128i *)s2);
1255  b1 = _mm_cmpeq_epi32(b1, b2);
1256 
1257  if (_mm_movemask_epi8(b1) != 0xffff)
1258  {
1259  if (!IgnoreCase)
1260  {
1261  return FALSE;
1262  }
1263  else
1264  {
1265  // Compare character-by-character to ignore case.
1266  l1 = length * 16 + (l1 & 15);
1267  l1 /= sizeof(WCHAR);
1268  goto CompareCharacters;
1269  }
1270  }
1271 
1272  s1 += 16 / sizeof(WCHAR);
1273  s2 += 16 / sizeof(WCHAR);
1274  } while (--length != 0);
1275  }
1276 
1277  // Compare character-by-character because we have no more 16-byte blocks
1278  // to compare.
1279  l1 = (l1 & 15) / sizeof(WCHAR);
1280  }
1281  else
1282  {
1283  length = l1 / sizeof(ULONG_PTR);
1284 
1285  if (length != 0)
1286  {
1287  do
1288  {
1289  if (*(PULONG_PTR)s1 != *(PULONG_PTR)s2)
1290  {
1291  if (!IgnoreCase)
1292  {
1293  return FALSE;
1294  }
1295  else
1296  {
1297  // Compare character-by-character to ignore case.
1298  l1 = length * sizeof(ULONG_PTR) + (l1 & (sizeof(ULONG_PTR) - 1));
1299  l1 /= sizeof(WCHAR);
1300  goto CompareCharacters;
1301  }
1302  }
1303 
1304  s1 += sizeof(ULONG_PTR) / sizeof(WCHAR);
1305  s2 += sizeof(ULONG_PTR) / sizeof(WCHAR);
1306  } while (--length != 0);
1307  }
1308 
1309  // Compare character-by-character because we have no more ULONG_PTR blocks
1310  // to compare.
1311  l1 = (l1 & (sizeof(ULONG_PTR) - 1)) / sizeof(WCHAR);
1312  }
1313 
1314 CompareCharacters:
1315  if (l1 != 0)
1316  {
1317  if (!IgnoreCase)
1318  {
1319  do
1320  {
1321  c1 = *s1;
1322  c2 = *s2;
1323 
1324  if (c1 != c2)
1325  return FALSE;
1326 
1327  s1++;
1328  s2++;
1329  } while (--l1 != 0);
1330  }
1331  else
1332  {
1333  do
1334  {
1335  c1 = *s1;
1336  c2 = *s2;
1337 
1338  if (c1 != c2)
1339  {
1340  c1 = RtlUpcaseUnicodeChar(c1);
1341  c2 = RtlUpcaseUnicodeChar(c2);
1342 
1343  if (c1 != c2)
1344  return FALSE;
1345  }
1346 
1347  s1++;
1348  s2++;
1349  } while (--l1 != 0);
1350  }
1351  }
1352 
1353  return TRUE;
1354 }
1355 
1367  _In_ PPH_STRINGREF String,
1368  _In_ WCHAR Character,
1369  _In_ BOOLEAN IgnoreCase
1370  )
1371 {
1372  PWSTR buffer;
1373  SIZE_T length;
1374 
1375  buffer = String->Buffer;
1376  length = String->Length / sizeof(WCHAR);
1377 
1378  if (!IgnoreCase)
1379  {
1380  if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2)
1381  {
1382  SIZE_T length16;
1383 
1384  length16 = String->Length / 16;
1385  length &= 7;
1386 
1387  if (length16 != 0)
1388  {
1389  __m128i pattern;
1390  __m128i block;
1391  ULONG mask;
1392  ULONG index;
1393 
1394  pattern = _mm_set1_epi16(Character);
1395 
1396  do
1397  {
1398  block = _mm_loadu_si128((__m128i *)buffer);
1399  block = _mm_cmpeq_epi16(block, pattern);
1400  mask = _mm_movemask_epi8(block);
1401 
1402  if (_BitScanForward(&index, mask))
1403  return (String->Length - length16 * 16) / sizeof(WCHAR) - length + index / 2;
1404 
1405  buffer += 16 / sizeof(WCHAR);
1406  } while (--length16 != 0);
1407  }
1408  }
1409 
1410  if (length != 0)
1411  {
1412  do
1413  {
1414  if (*buffer == Character)
1415  return String->Length / sizeof(WCHAR) - length;
1416 
1417  buffer++;
1418  } while (--length != 0);
1419  }
1420  }
1421  else
1422  {
1423  if (length != 0)
1424  {
1425  WCHAR c;
1426 
1427  c = RtlUpcaseUnicodeChar(Character);
1428 
1429  do
1430  {
1431  if (RtlUpcaseUnicodeChar(*buffer) == c)
1432  return String->Length / sizeof(WCHAR) - length;
1433 
1434  buffer++;
1435  } while (--length != 0);
1436  }
1437  }
1438 
1439  return -1;
1440 }
1441 
1453  _In_ PPH_STRINGREF String,
1454  _In_ WCHAR Character,
1455  _In_ BOOLEAN IgnoreCase
1456  )
1457 {
1458  PWCHAR buffer;
1459  SIZE_T length;
1460 
1461  buffer = (PWCHAR)((PCHAR)String->Buffer + String->Length);
1462  length = String->Length / sizeof(WCHAR);
1463 
1464  if (!IgnoreCase)
1465  {
1466  if (PhpVectorLevel >= PH_VECTOR_LEVEL_SSE2)
1467  {
1468  SIZE_T length16;
1469 
1470  length16 = String->Length / 16;
1471  length &= 7;
1472 
1473  if (length16 != 0)
1474  {
1475  __m128i pattern;
1476  __m128i block;
1477  ULONG mask;
1478  ULONG index;
1479 
1480  pattern = _mm_set1_epi16(Character);
1481  buffer -= 16 / sizeof(WCHAR);
1482 
1483  do
1484  {
1485  block = _mm_loadu_si128((__m128i *)buffer);
1486  block = _mm_cmpeq_epi16(block, pattern);
1487  mask = _mm_movemask_epi8(block);
1488 
1489  if (_BitScanReverse(&index, mask))
1490  return (length16 - 1) * 16 / sizeof(WCHAR) + length + index / 2;
1491 
1492  buffer -= 16 / sizeof(WCHAR);
1493  } while (--length16 != 0);
1494 
1495  buffer += 16 / sizeof(WCHAR);
1496  }
1497  }
1498 
1499  if (length != 0)
1500  {
1501  buffer--;
1502 
1503  do
1504  {
1505  if (*buffer == Character)
1506  return length - 1;
1507 
1508  buffer--;
1509  } while (--length != 0);
1510  }
1511  }
1512  else
1513  {
1514  if (length != 0)
1515  {
1516  WCHAR c;
1517 
1518  c = RtlUpcaseUnicodeChar(Character);
1519  buffer--;
1520 
1521  do
1522  {
1523  if (RtlUpcaseUnicodeChar(*buffer) == c)
1524  return length - 1;
1525 
1526  buffer--;
1527  } while (--length != 0);
1528  }
1529  }
1530 
1531  return -1;
1532 }
1533 
1546  _In_ PPH_STRINGREF String,
1547  _In_ PPH_STRINGREF SubString,
1548  _In_ BOOLEAN IgnoreCase
1549  )
1550 {
1551  SIZE_T length1;
1552  SIZE_T length2;
1553  PH_STRINGREF sr1;
1554  PH_STRINGREF sr2;
1555  WCHAR c;
1556  SIZE_T i;
1557 
1558  length1 = String->Length / sizeof(WCHAR);
1559  length2 = SubString->Length / sizeof(WCHAR);
1560 
1561  // Can't be a substring if it's bigger than the first string.
1562  if (length2 > length1)
1563  return -1;
1564  // We always get a match if the substring is zero-length.
1565  if (length2 == 0)
1566  return 0;
1567 
1568  sr1.Buffer = String->Buffer;
1569  sr1.Length = SubString->Length - sizeof(WCHAR);
1570  sr2.Buffer = SubString->Buffer;
1571  sr2.Length = SubString->Length - sizeof(WCHAR);
1572 
1573  if (!IgnoreCase)
1574  {
1575  c = *sr2.Buffer++;
1576 
1577  for (i = length1 - length2 + 1; i != 0; i--)
1578  {
1579  if (*sr1.Buffer++ == c && PhEqualStringRef(&sr1, &sr2, FALSE))
1580  {
1581  goto FoundUString;
1582  }
1583  }
1584  }
1585  else
1586  {
1587  c = RtlUpcaseUnicodeChar(*sr2.Buffer++);
1588 
1589  for (i = length1 - length2 + 1; i != 0; i--)
1590  {
1591  if (RtlUpcaseUnicodeChar(*sr1.Buffer++) == c && PhEqualStringRef(&sr1, &sr2, TRUE))
1592  {
1593  goto FoundUString;
1594  }
1595  }
1596  }
1597 
1598  return -1;
1599 FoundUString:
1600  return (ULONG_PTR)(sr1.Buffer - String->Buffer - 1);
1601 }
1602 
1620  _In_ PPH_STRINGREF Input,
1621  _In_ WCHAR Separator,
1622  _Out_ PPH_STRINGREF FirstPart,
1623  _Out_ PPH_STRINGREF SecondPart
1624  )
1625 {
1626  PH_STRINGREF input;
1627  ULONG_PTR index;
1628 
1629  input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input
1630  index = PhFindCharInStringRef(Input, Separator, FALSE);
1631 
1632  if (index == -1)
1633  {
1634  // The separator was not found.
1635 
1636  FirstPart->Buffer = Input->Buffer;
1637  FirstPart->Length = Input->Length;
1638  SecondPart->Buffer = NULL;
1639  SecondPart->Length = 0;
1640 
1641  return FALSE;
1642  }
1643 
1644  FirstPart->Buffer = input.Buffer;
1645  FirstPart->Length = index * sizeof(WCHAR);
1646  SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + sizeof(WCHAR));
1647  SecondPart->Length = input.Length - index * sizeof(WCHAR) - sizeof(WCHAR);
1648 
1649  return TRUE;
1650 }
1651 
1669  _In_ PPH_STRINGREF Input,
1670  _In_ WCHAR Separator,
1671  _Out_ PPH_STRINGREF FirstPart,
1672  _Out_ PPH_STRINGREF SecondPart
1673  )
1674 {
1675  PH_STRINGREF input;
1676  ULONG_PTR index;
1677 
1678  input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input
1679  index = PhFindLastCharInStringRef(Input, Separator, FALSE);
1680 
1681  if (index == -1)
1682  {
1683  // The separator was not found.
1684 
1685  FirstPart->Buffer = Input->Buffer;
1686  FirstPart->Length = Input->Length;
1687  SecondPart->Buffer = NULL;
1688  SecondPart->Length = 0;
1689 
1690  return FALSE;
1691  }
1692 
1693  FirstPart->Buffer = input.Buffer;
1694  FirstPart->Length = index * sizeof(WCHAR);
1695  SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + sizeof(WCHAR));
1696  SecondPart->Length = input.Length - index * sizeof(WCHAR) - sizeof(WCHAR);
1697 
1698  return TRUE;
1699 }
1700 
1720  _In_ PPH_STRINGREF Input,
1721  _In_ PPH_STRINGREF Separator,
1722  _In_ BOOLEAN IgnoreCase,
1723  _Out_ PPH_STRINGREF FirstPart,
1724  _Out_ PPH_STRINGREF SecondPart
1725  )
1726 {
1727  PH_STRINGREF input;
1728  ULONG_PTR index;
1729 
1730  input = *Input; // get a copy of the input because FirstPart/SecondPart may alias Input
1731  index = PhFindStringInStringRef(Input, Separator, IgnoreCase);
1732 
1733  if (index == -1)
1734  {
1735  // The separator was not found.
1736 
1737  FirstPart->Buffer = Input->Buffer;
1738  FirstPart->Length = Input->Length;
1739  SecondPart->Buffer = NULL;
1740  SecondPart->Length = 0;
1741 
1742  return FALSE;
1743  }
1744 
1745  FirstPart->Buffer = input.Buffer;
1746  FirstPart->Length = index * sizeof(WCHAR);
1747  SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + index * sizeof(WCHAR) + Separator->Length);
1748  SecondPart->Length = input.Length - index * sizeof(WCHAR) - Separator->Length;
1749 
1750  return TRUE;
1751 }
1752 
1790  _In_ PPH_STRINGREF Input,
1791  _In_ PPH_STRINGREF Separator,
1792  _In_ ULONG Flags,
1793  _Out_ PPH_STRINGREF FirstPart,
1794  _Out_ PPH_STRINGREF SecondPart,
1795  _Out_opt_ PPH_STRINGREF SeparatorPart
1796  )
1797 {
1798  PH_STRINGREF input;
1799  SIZE_T separatorIndex;
1800  SIZE_T separatorLength;
1801  PWCHAR charSet;
1802  SIZE_T charSetCount;
1803  BOOLEAN charSetTable[256];
1804  BOOLEAN charSetTableComplete;
1805  SIZE_T i;
1806  SIZE_T j;
1807  USHORT c;
1808  PWCHAR s;
1809  LONG_PTR direction;
1810 
1811  input = *Input; // get a copy of the input because FirstPart/SecondPart/SeparatorPart may alias Input
1812 
1813  if (Flags & PH_SPLIT_AT_RANGE)
1814  {
1815  separatorIndex = (SIZE_T)Separator->Buffer;
1816  separatorLength = Separator->Length;
1817 
1818  if (separatorIndex == -1)
1819  goto SeparatorNotFound;
1820 
1821  goto SeparatorFound;
1822  }
1823  else if (Flags & PH_SPLIT_AT_STRING)
1824  {
1825  if (Flags & PH_SPLIT_START_AT_END)
1826  {
1827  // not implemented
1828  goto SeparatorNotFound;
1829  }
1830 
1831  separatorIndex = PhFindStringInStringRef(Input, Separator, !!(Flags & PH_SPLIT_CASE_INSENSITIVE));
1832 
1833  if (separatorIndex == -1)
1834  goto SeparatorNotFound;
1835 
1836  separatorLength = Separator->Length;
1837  goto SeparatorFound;
1838  }
1839 
1840  // Special case for character sets with only one character.
1841  if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET) && Separator->Length == sizeof(WCHAR))
1842  {
1843  if (!(Flags & PH_SPLIT_START_AT_END))
1844  separatorIndex = PhFindCharInStringRef(Input, Separator->Buffer[0], !!(Flags & PH_SPLIT_CASE_INSENSITIVE));
1845  else
1846  separatorIndex = PhFindLastCharInStringRef(Input, Separator->Buffer[0], !!(Flags & PH_SPLIT_CASE_INSENSITIVE));
1847 
1848  if (separatorIndex == -1)
1849  goto SeparatorNotFound;
1850 
1851  separatorLength = sizeof(WCHAR);
1852  goto SeparatorFound;
1853  }
1854 
1855  if (input.Length == 0)
1856  goto SeparatorNotFound;
1857 
1858  // Build the character set lookup table.
1859 
1860  charSet = Separator->Buffer;
1861  charSetCount = Separator->Length / sizeof(WCHAR);
1862  memset(charSetTable, 0, sizeof(charSetTable));
1863  charSetTableComplete = TRUE;
1864 
1865  for (i = 0; i < charSetCount; i++)
1866  {
1867  c = charSet[i];
1868 
1869  if (Flags & PH_SPLIT_CASE_INSENSITIVE)
1870  c = RtlUpcaseUnicodeChar(c);
1871 
1872  charSetTable[c & 0xff] = TRUE;
1873 
1874  if (c >= 256)
1875  charSetTableComplete = FALSE;
1876  }
1877 
1878  // Perform the search.
1879 
1880  i = input.Length / sizeof(WCHAR);
1881  separatorLength = sizeof(WCHAR);
1882 
1883  if (!(Flags & PH_SPLIT_START_AT_END))
1884  {
1885  s = input.Buffer;
1886  direction = 1;
1887  }
1888  else
1889  {
1890  s = (PWCHAR)((PCHAR)input.Buffer + input.Length - sizeof(WCHAR));
1891  direction = -1;
1892  }
1893 
1894  do
1895  {
1896  c = *s;
1897 
1898  if (Flags & PH_SPLIT_CASE_INSENSITIVE)
1899  c = RtlUpcaseUnicodeChar(c);
1900 
1901  if (c < 256 && charSetTableComplete)
1902  {
1903  if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET))
1904  {
1905  if (charSetTable[c])
1906  goto CharFound;
1907  }
1908  else
1909  {
1910  if (!charSetTable[c])
1911  goto CharFound;
1912  }
1913  }
1914  else
1915  {
1916  if (!(Flags & PH_SPLIT_COMPLEMENT_CHAR_SET))
1917  {
1918  if (charSetTable[c & 0xff])
1919  {
1920  if (!(Flags & PH_SPLIT_CASE_INSENSITIVE) || (Flags & PH_SPLIT_CHAR_SET_IS_UPPERCASE))
1921  {
1922  for (j = 0; j < charSetCount; j++)
1923  {
1924  if (charSet[j] == c)
1925  goto CharFound;
1926  }
1927  }
1928  else
1929  {
1930  for (j = 0; j < charSetCount; j++)
1931  {
1932  if (RtlUpcaseUnicodeChar(charSet[j]) == c)
1933  goto CharFound;
1934  }
1935  }
1936  }
1937  }
1938  else
1939  {
1940  if (charSetTable[c & 0xff])
1941  {
1942  if (!(Flags & PH_SPLIT_CASE_INSENSITIVE) || (Flags & PH_SPLIT_CHAR_SET_IS_UPPERCASE))
1943  {
1944  for (j = 0; j < charSetCount; j++)
1945  {
1946  if (charSet[j] == c)
1947  break;
1948  }
1949  }
1950  else
1951  {
1952  for (j = 0; j < charSetCount; j++)
1953  {
1954  if (RtlUpcaseUnicodeChar(charSet[j]) == c)
1955  break;
1956  }
1957  }
1958 
1959  if (j == charSetCount)
1960  goto CharFound;
1961  }
1962  else
1963  {
1964  goto CharFound;
1965  }
1966  }
1967  }
1968 
1969  s += direction;
1970  } while (--i != 0);
1971 
1972  goto SeparatorNotFound;
1973 
1974 CharFound:
1975  separatorIndex = s - input.Buffer;
1976 
1977 SeparatorFound:
1978  FirstPart->Buffer = input.Buffer;
1979  FirstPart->Length = separatorIndex * sizeof(WCHAR);
1980  SecondPart->Buffer = (PWCHAR)((PCHAR)input.Buffer + separatorIndex * sizeof(WCHAR) + separatorLength);
1981  SecondPart->Length = input.Length - separatorIndex * sizeof(WCHAR) - separatorLength;
1982 
1983  if (SeparatorPart)
1984  {
1985  SeparatorPart->Buffer = input.Buffer + separatorIndex;
1986  SeparatorPart->Length = separatorLength;
1987  }
1988 
1989  return TRUE;
1990 
1991 SeparatorNotFound:
1992  FirstPart->Buffer = input.Buffer;
1993  FirstPart->Length = input.Length;
1994  SecondPart->Buffer = NULL;
1995  SecondPart->Length = 0;
1996 
1997  if (SeparatorPart)
1998  {
1999  SeparatorPart->Buffer = NULL;
2000  SeparatorPart->Length = 0;
2001  }
2002 
2003  return FALSE;
2004 }
2005 
2007  _Inout_ PPH_STRINGREF String,
2008  _In_ PPH_STRINGREF CharSet,
2009  _In_ ULONG Flags
2010  )
2011 {
2012  PWCHAR charSet;
2013  SIZE_T charSetCount;
2014  BOOLEAN charSetTable[256];
2015  BOOLEAN charSetTableComplete;
2016  SIZE_T i;
2017  SIZE_T j;
2018  USHORT c;
2019  SIZE_T trimCount;
2020  SIZE_T count;
2021  PWCHAR s;
2022 
2023  if (String->Length == 0 || CharSet->Length == 0)
2024  return;
2025 
2026  if (CharSet->Length == sizeof(WCHAR))
2027  {
2028  c = CharSet->Buffer[0];
2029 
2030  if (!(Flags & PH_TRIM_END_ONLY))
2031  {
2032  trimCount = 0;
2033  count = String->Length / sizeof(WCHAR);
2034  s = String->Buffer;
2035 
2036  while (count-- != 0)
2037  {
2038  if (*s++ != c)
2039  break;
2040 
2041  trimCount++;
2042  }
2043 
2044  PhSkipStringRef(String, trimCount * sizeof(WCHAR));
2045  }
2046 
2047  if (!(Flags & PH_TRIM_START_ONLY))
2048  {
2049  trimCount = 0;
2050  count = String->Length / sizeof(WCHAR);
2051  s = (PWCHAR)((PCHAR)String->Buffer + String->Length - sizeof(WCHAR));
2052 
2053  while (count-- != 0)
2054  {
2055  if (*s-- != c)
2056  break;
2057 
2058  trimCount++;
2059  }
2060 
2061  String->Length -= trimCount * sizeof(WCHAR);
2062  }
2063 
2064  return;
2065  }
2066 
2067  // Build the character set lookup table.
2068 
2069  charSet = CharSet->Buffer;
2070  charSetCount = CharSet->Length / sizeof(WCHAR);
2071  memset(charSetTable, 0, sizeof(charSetTable));
2072  charSetTableComplete = TRUE;
2073 
2074  for (i = 0; i < charSetCount; i++)
2075  {
2076  c = charSet[i];
2077  charSetTable[c & 0xff] = TRUE;
2078 
2079  if (c >= 256)
2080  charSetTableComplete = FALSE;
2081  }
2082 
2083  // Trim the string.
2084 
2085  if (!(Flags & PH_TRIM_END_ONLY))
2086  {
2087  trimCount = 0;
2088  count = String->Length / sizeof(WCHAR);
2089  s = String->Buffer;
2090 
2091  while (count-- != 0)
2092  {
2093  c = *s++;
2094 
2095  if (!charSetTable[c & 0xff])
2096  break;
2097 
2098  if (!charSetTableComplete)
2099  {
2100  for (j = 0; j < charSetCount; j++)
2101  {
2102  if (charSet[j] == c)
2103  goto CharFound;
2104  }
2105 
2106  break;
2107  }
2108 
2109 CharFound:
2110  trimCount++;
2111  }
2112 
2113  PhSkipStringRef(String, trimCount * sizeof(WCHAR));
2114  }
2115 
2116  if (!(Flags & PH_TRIM_START_ONLY))
2117  {
2118  trimCount = 0;
2119  count = String->Length / sizeof(WCHAR);
2120  s = (PWCHAR)((PCHAR)String->Buffer + String->Length - sizeof(WCHAR));
2121 
2122  while (count-- != 0)
2123  {
2124  c = *s--;
2125 
2126  if (!charSetTable[c & 0xff])
2127  break;
2128 
2129  if (!charSetTableComplete)
2130  {
2131  for (j = 0; j < charSetCount; j++)
2132  {
2133  if (charSet[j] == c)
2134  goto CharFound2;
2135  }
2136 
2137  break;
2138  }
2139 
2140 CharFound2:
2141  trimCount++;
2142  }
2143 
2144  String->Length -= trimCount * sizeof(WCHAR);
2145  }
2146 }
2147 
2155  _In_ PWSTR Buffer
2156  )
2157 {
2158  return PhCreateStringEx(Buffer, wcslen(Buffer) * sizeof(WCHAR));
2159 }
2160 
2168  _In_opt_ PWCHAR Buffer,
2169  _In_ SIZE_T Length
2170  )
2171 {
2172  PPH_STRING string;
2173 
2174  string = PhCreateObject(
2175  FIELD_OFFSET(PH_STRING, Data) + Length + sizeof(WCHAR), // Null terminator
2176  PhStringType
2177  );
2178 
2179  assert(!(Length & 1));
2180  string->Length = Length;
2181  string->Buffer = string->Data;
2182  *(PWCHAR)((PCHAR)string->Buffer + Length) = 0;
2183 
2184  if (Buffer)
2185  {
2186  memcpy(string->Buffer, Buffer, Length);
2187  }
2188 
2189  return string;
2190 }
2191 
2196  VOID
2197  )
2198 {
2199  PPH_STRING string;
2200  PPH_STRING newString;
2201 
2202  string = PhSharedEmptyString;
2203 
2204  if (!string)
2205  {
2206  newString = PhCreateStringEx(NULL, 0);
2207 
2209  &PhSharedEmptyString,
2210  newString,
2211  NULL
2212  );
2213 
2214  if (!string)
2215  {
2216  string = newString; // success
2217  }
2218  else
2219  {
2220  PhDereferenceObject(newString);
2221  }
2222  }
2223 
2224  return PhReferenceObject(string);
2225 }
2226 
2233  _In_ ULONG Count,
2234  ...
2235  )
2236 {
2237  va_list argptr;
2238 
2239  va_start(argptr, Count);
2240 
2241  return PhConcatStrings_V(Count, argptr);
2242 }
2243 
2251  _In_ ULONG Count,
2252  _In_ va_list ArgPtr
2253  )
2254 {
2255  va_list argptr;
2256  ULONG i;
2257  SIZE_T totalLength = 0;
2258  SIZE_T stringLength;
2259  SIZE_T cachedLengths[PH_CONCAT_STRINGS_LENGTH_CACHE_SIZE];
2260  PWSTR arg;
2261  PPH_STRING string;
2262 
2263  // Compute the total length, in bytes, of the strings.
2264 
2265  argptr = ArgPtr;
2266 
2267  for (i = 0; i < Count; i++)
2268  {
2269  arg = va_arg(argptr, PWSTR);
2270  stringLength = PhCountStringZ(arg) * sizeof(WCHAR);
2271  totalLength += stringLength;
2272 
2274  cachedLengths[i] = stringLength;
2275  }
2276 
2277  // Create the string.
2278 
2279  string = PhCreateStringEx(NULL, totalLength);
2280  totalLength = 0;
2281 
2282  // Append the strings one by one.
2283 
2284  argptr = ArgPtr;
2285 
2286  for (i = 0; i < Count; i++)
2287  {
2288  arg = va_arg(argptr, PWSTR);
2289 
2291  stringLength = cachedLengths[i];
2292  else
2293  stringLength = PhCountStringZ(arg) * sizeof(WCHAR);
2294 
2295  memcpy(
2296  (PCHAR)string->Buffer + totalLength,
2297  arg,
2298  stringLength
2299  );
2300  totalLength += stringLength;
2301  }
2302 
2303  return string;
2304 }
2305 
2313  _In_ PWSTR String1,
2314  _In_ PWSTR String2
2315  )
2316 {
2317  PPH_STRING string;
2318  SIZE_T length1;
2319  SIZE_T length2;
2320 
2321  length1 = PhCountStringZ(String1) * sizeof(WCHAR);
2322  length2 = PhCountStringZ(String2) * sizeof(WCHAR);
2323  string = PhCreateStringEx(NULL, length1 + length2);
2324  memcpy(
2325  string->Buffer,
2326  String1,
2327  length1
2328  );
2329  memcpy(
2330  (PCHAR)string->Buffer + length1,
2331  String2,
2332  length2
2333  );
2334 
2335  return string;
2336 }
2337 
2345  _In_ PPH_STRINGREF String1,
2346  _In_ PPH_STRINGREF String2
2347  )
2348 {
2349  PPH_STRING string;
2350 
2351  assert(!(String1->Length & 1));
2352  assert(!(String2->Length & 1));
2353 
2354  string = PhCreateStringEx(NULL, String1->Length + String2->Length);
2355  memcpy(string->Buffer, String1->Buffer, String1->Length);
2356  memcpy((PCHAR)string->Buffer + String1->Length, String2->Buffer, String2->Length);
2357 
2358  return string;
2359 }
2360 
2369  _In_ PPH_STRINGREF String1,
2370  _In_ PPH_STRINGREF String2,
2371  _In_ PPH_STRINGREF String3
2372  )
2373 {
2374  PPH_STRING string;
2375  PCHAR buffer;
2376 
2377  assert(!(String1->Length & 1));
2378  assert(!(String2->Length & 1));
2379  assert(!(String3->Length & 1));
2380 
2381  string = PhCreateStringEx(NULL, String1->Length + String2->Length + String3->Length);
2382 
2383  buffer = (PCHAR)string->Buffer;
2384  memcpy(buffer, String1->Buffer, String1->Length);
2385 
2386  buffer += String1->Length;
2387  memcpy(buffer, String2->Buffer, String2->Length);
2388 
2389  buffer += String2->Length;
2390  memcpy(buffer, String3->Buffer, String3->Length);
2391 
2392  return string;
2393 }
2394 
2401  _In_ _Printf_format_string_ PWSTR Format,
2402  ...
2403  )
2404 {
2405  va_list argptr;
2406 
2407  va_start(argptr, Format);
2408 
2409  return PhFormatString_V(Format, argptr);
2410 }
2411 
2419  _In_ _Printf_format_string_ PWSTR Format,
2420  _In_ va_list ArgPtr
2421  )
2422 {
2423  PPH_STRING string;
2424  int length;
2425 
2426  length = _vscwprintf(Format, ArgPtr);
2427 
2428  if (length == -1)
2429  return NULL;
2430 
2431  string = PhCreateStringEx(NULL, length * sizeof(WCHAR));
2432  _vsnwprintf(string->Buffer, length, Format, ArgPtr);
2433 
2434  return string;
2435 }
2436 
2444  _In_ PSTR Buffer
2445  )
2446 {
2447  return PhCreateBytesEx(Buffer, strlen(Buffer) * sizeof(CHAR));
2448 }
2449 
2457  _In_opt_ PCHAR Buffer,
2458  _In_ SIZE_T Length
2459  )
2460 {
2461  PPH_BYTES bytes;
2462 
2463  bytes = PhCreateObject(
2464  FIELD_OFFSET(PH_BYTES, Data) + Length + sizeof(CHAR), // Null terminator for compatibility
2465  PhBytesType
2466  );
2467 
2468  bytes->Length = Length;
2469  bytes->Buffer = bytes->Data;
2470  bytes->Buffer[Length] = 0;
2471 
2472  if (Buffer)
2473  {
2474  memcpy(bytes->Buffer, Buffer, Length);
2475  }
2476 
2477  return bytes;
2478 }
2479 
2481  _Inout_ PPH_UNICODE_DECODER Decoder,
2482  _In_ ULONG CodeUnit
2483  )
2484 {
2485  switch (Decoder->Encoding)
2486  {
2487  case PH_UNICODE_UTF8:
2488  if (Decoder->InputCount >= 4)
2489  return FALSE;
2490  Decoder->u.Utf8.Input[Decoder->InputCount] = (UCHAR)CodeUnit;
2491  Decoder->InputCount++;
2492  return TRUE;
2493  case PH_UNICODE_UTF16:
2494  if (Decoder->InputCount >= 2)
2495  return FALSE;
2496  Decoder->u.Utf16.Input[Decoder->InputCount] = (USHORT)CodeUnit;
2497  Decoder->InputCount++;
2498  return TRUE;
2499  case PH_UNICODE_UTF32:
2500  if (Decoder->InputCount >= 1)
2501  return FALSE;
2502  Decoder->u.Utf32.Input = CodeUnit;
2503  Decoder->InputCount = 1;
2504  return TRUE;
2505  default:
2506  PhRaiseStatus(STATUS_UNSUCCESSFUL);
2507  }
2508 }
2509 
2511  _Inout_ PPH_UNICODE_DECODER Decoder,
2512  _Out_ PULONG CodeUnit
2513  )
2514 {
2515  switch (Decoder->Encoding)
2516  {
2517  case PH_UNICODE_UTF8:
2518  if (Decoder->InputCount == 0)
2519  return FALSE;
2520  *CodeUnit = Decoder->u.Utf8.Input[0];
2521  Decoder->u.Utf8.Input[0] = Decoder->u.Utf8.Input[1];
2522  Decoder->u.Utf8.Input[1] = Decoder->u.Utf8.Input[2];
2523  Decoder->u.Utf8.Input[2] = Decoder->u.Utf8.Input[3];
2524  Decoder->InputCount--;
2525  return TRUE;
2526  case PH_UNICODE_UTF16:
2527  if (Decoder->InputCount == 0)
2528  return FALSE;
2529  *CodeUnit = Decoder->u.Utf16.Input[0];
2530  Decoder->u.Utf16.Input[0] = Decoder->u.Utf16.Input[1];
2531  Decoder->InputCount--;
2532  return TRUE;
2533  case PH_UNICODE_UTF32:
2534  if (Decoder->InputCount == 0)
2535  return FALSE;
2536  *CodeUnit = Decoder->u.Utf32.Input;
2537  Decoder->InputCount--;
2538  return TRUE;
2539  default:
2540  PhRaiseStatus(STATUS_UNSUCCESSFUL);
2541  }
2542 }
2543 
2545  _Inout_ PPH_UNICODE_DECODER Decoder,
2546  _In_ ULONG CodeUnit
2547  )
2548 {
2549  switch (Decoder->Encoding)
2550  {
2551  case PH_UNICODE_UTF8:
2552  if (Decoder->InputCount >= 4)
2553  PhRaiseStatus(STATUS_UNSUCCESSFUL);
2554  Decoder->u.Utf8.Input[3] = Decoder->u.Utf8.Input[2];
2555  Decoder->u.Utf8.Input[2] = Decoder->u.Utf8.Input[1];
2556  Decoder->u.Utf8.Input[1] = Decoder->u.Utf8.Input[0];
2557  Decoder->u.Utf8.Input[0] = (UCHAR)CodeUnit;
2558  Decoder->InputCount++;
2559  break;
2560  case PH_UNICODE_UTF16:
2561  if (Decoder->InputCount >= 2)
2562  PhRaiseStatus(STATUS_UNSUCCESSFUL);
2563  Decoder->u.Utf16.Input[1] = Decoder->u.Utf16.Input[0];
2564  Decoder->u.Utf16.Input[0] = (USHORT)CodeUnit;
2565  Decoder->InputCount++;
2566  break;
2567  case PH_UNICODE_UTF32:
2568  if (Decoder->InputCount >= 1)
2569  PhRaiseStatus(STATUS_UNSUCCESSFUL);
2570  Decoder->u.Utf32.Input = CodeUnit;
2571  Decoder->InputCount = 1;
2572  break;
2573  default:
2574  PhRaiseStatus(STATUS_UNSUCCESSFUL);
2575  }
2576 }
2577 
2579  _Inout_ PPH_UNICODE_DECODER Decoder,
2580  _Out_ PULONG CodePoint,
2581  _In_ ULONG Which
2582  )
2583 {
2584  if (Which >= 4)
2585  PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit4);
2586  if (Which >= 3)
2587  PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit3);
2588  if (Which >= 2)
2589  PhpUnreadUnicodeDecoder(Decoder, Decoder->u.Utf8.CodeUnit2);
2590 
2591  *CodePoint = (ULONG)Decoder->u.Utf8.CodeUnit1 + 0xdc00;
2592  Decoder->State = 0;
2593 
2594  return TRUE;
2595 }
2596 
2598  _Inout_ PPH_UNICODE_DECODER Decoder,
2599  _Out_ PULONG CodePoint
2600  )
2601 {
2602  ULONG codeUnit;
2603 
2604  while (TRUE)
2605  {
2606  switch (Decoder->Encoding)
2607  {
2608  case PH_UNICODE_UTF8:
2609  if (!PhpReadUnicodeDecoder(Decoder, &codeUnit))
2610  return FALSE;
2611 
2612  switch (Decoder->State)
2613  {
2614  case 0:
2615  Decoder->u.Utf8.CodeUnit1 = (UCHAR)codeUnit;
2616 
2617  if (codeUnit < 0x80)
2618  {
2619  *CodePoint = codeUnit;
2620  return TRUE;
2621  }
2622  else if (codeUnit < 0xc2)
2623  {
2624  return PhpDecodeUtf8Error(Decoder, CodePoint, 1);
2625  }
2626  else if (codeUnit < 0xe0)
2627  {
2628  Decoder->State = 1; // 2 byte sequence
2629  continue;
2630  }
2631  else if (codeUnit < 0xf0)
2632  {
2633  Decoder->State = 2; // 3 byte sequence
2634  continue;
2635  }
2636  else if (codeUnit < 0xf5)
2637  {
2638  Decoder->State = 3; // 4 byte sequence
2639  continue;
2640  }
2641  else
2642  {
2643  return PhpDecodeUtf8Error(Decoder, CodePoint, 1);
2644  }
2645 
2646  break;
2647  case 1: // 2 byte sequence
2648  Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit;
2649 
2650  if ((codeUnit & 0xc0) == 0x80)
2651  {
2652  *CodePoint = ((ULONG)Decoder->u.Utf8.CodeUnit1 << 6) + codeUnit - 0x3080;
2653  Decoder->State = 0;
2654  return TRUE;
2655  }
2656  else
2657  {
2658  return PhpDecodeUtf8Error(Decoder, CodePoint, 2);
2659  }
2660 
2661  break;
2662  case 2: // 3 byte sequence (1)
2663  Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit;
2664 
2665  if (((codeUnit & 0xc0) == 0x80) &&
2666  (Decoder->u.Utf8.CodeUnit1 != 0xe0 || codeUnit >= 0xa0))
2667  {
2668  Decoder->State = 4; // 3 byte sequence (2)
2669  continue;
2670  }
2671  else
2672  {
2673  return PhpDecodeUtf8Error(Decoder, CodePoint, 2);
2674  }
2675 
2676  break;
2677  case 3: // 4 byte sequence (1)
2678  Decoder->u.Utf8.CodeUnit2 = (UCHAR)codeUnit;
2679 
2680  if (((codeUnit & 0xc0) == 0x80) &&
2681  (Decoder->u.Utf8.CodeUnit1 != 0xf0 || codeUnit >= 0x90) &&
2682  (Decoder->u.Utf8.CodeUnit1 != 0xf4 || codeUnit < 0x90))
2683  {
2684  Decoder->State = 5; // 4 byte sequence (2)
2685  continue;
2686  }
2687  else
2688  {
2689  return PhpDecodeUtf8Error(Decoder, CodePoint, 2);
2690  }
2691 
2692  break;
2693  case 4: // 3 byte sequence (2)
2694  Decoder->u.Utf8.CodeUnit3 = (UCHAR)codeUnit;
2695 
2696  if ((codeUnit & 0xc0) == 0x80)
2697  {
2698  *CodePoint =
2699  ((ULONG)Decoder->u.Utf8.CodeUnit1 << 12) +
2700  ((ULONG)Decoder->u.Utf8.CodeUnit2 << 6) +
2701  codeUnit - 0xe2080;
2702  Decoder->State = 0;
2703  return TRUE;
2704  }
2705  else
2706  {
2707  return PhpDecodeUtf8Error(Decoder, CodePoint, 3);
2708  }
2709 
2710  break;
2711  case 5: // 4 byte sequence (2)
2712  Decoder->u.Utf8.CodeUnit3 = (UCHAR)codeUnit;
2713 
2714  if ((codeUnit & 0xc0) == 0x80)
2715  {
2716  Decoder->State = 6; // 4 byte sequence (3)
2717  continue;
2718  }
2719  else
2720  {
2721  return PhpDecodeUtf8Error(Decoder, CodePoint, 3);
2722  }
2723 
2724  break;
2725  case 6: // 4 byte sequence (3)
2726  Decoder->u.Utf8.CodeUnit4 = (UCHAR)codeUnit;
2727 
2728  if ((codeUnit & 0xc0) == 0x80)
2729  {
2730  *CodePoint =
2731  ((ULONG)Decoder->u.Utf8.CodeUnit1 << 18) +
2732  ((ULONG)Decoder->u.Utf8.CodeUnit2 << 12) +
2733  ((ULONG)Decoder->u.Utf8.CodeUnit3 << 6) +
2734  codeUnit - 0x3c82080;
2735  Decoder->State = 0;
2736  return TRUE;
2737  }
2738  else
2739  {
2740  return PhpDecodeUtf8Error(Decoder, CodePoint, 4);
2741  }
2742 
2743  break;
2744  }
2745 
2746  return FALSE;
2747  case PH_UNICODE_UTF16:
2748  if (!PhpReadUnicodeDecoder(Decoder, &codeUnit))
2749  return FALSE;
2750 
2751  switch (Decoder->State)
2752  {
2753  case 0:
2754  if (PH_UNICODE_UTF16_IS_HIGH_SURROGATE(codeUnit))
2755  {
2756  Decoder->u.Utf16.CodeUnit = (USHORT)codeUnit;
2757  Decoder->State = 1;
2758  continue;
2759  }
2760  else
2761  {
2762  *CodePoint = codeUnit;
2763  return TRUE;
2764  }
2765  break;
2766  case 1:
2767  if (PH_UNICODE_UTF16_IS_LOW_SURROGATE(codeUnit))
2768  {
2769  *CodePoint = PH_UNICODE_UTF16_TO_CODE_POINT(Decoder->u.Utf16.CodeUnit, codeUnit);
2770  Decoder->State = 0;
2771  return TRUE;
2772  }
2773  else
2774  {
2775  *CodePoint = Decoder->u.Utf16.CodeUnit;
2776  PhpUnreadUnicodeDecoder(Decoder, codeUnit);
2777  Decoder->State = 0;
2778  return TRUE;
2779  }
2780  break;
2781  }
2782 
2783  return FALSE;
2784  case PH_UNICODE_UTF32:
2785  if (PhpReadUnicodeDecoder(Decoder, CodePoint))
2786  return TRUE;
2787  return FALSE;
2788  default:
2789  return FALSE;
2790  }
2791  }
2792 }
2793 
2795  _In_ UCHAR Encoding,
2796  _In_ ULONG CodePoint,
2797  _Out_opt_ PVOID CodeUnits,
2798  _Out_ PULONG NumberOfCodeUnits
2799  )
2800 {
2801  switch (Encoding)
2802  {
2803  case PH_UNICODE_UTF8:
2804  {
2805  PUCHAR codeUnits = CodeUnits;
2806 
2807  if (CodePoint < 0x80)
2808  {
2809  *NumberOfCodeUnits = 1;
2810 
2811  if (codeUnits)
2812  codeUnits[0] = (UCHAR)CodePoint;
2813  }
2814  else if (CodePoint <= 0x7ff)
2815  {
2816  *NumberOfCodeUnits = 2;
2817 
2818  if (codeUnits)
2819  {
2820  codeUnits[0] = (UCHAR)(CodePoint >> 6) + 0xc0;
2821  codeUnits[1] = (UCHAR)(CodePoint & 0x3f) + 0x80;
2822  }
2823  }
2824  else if (CodePoint <= 0xffff)
2825  {
2826  *NumberOfCodeUnits = 3;
2827 
2828  if (codeUnits)
2829  {
2830  codeUnits[0] = (UCHAR)(CodePoint >> 12) + 0xe0;
2831  codeUnits[1] = (UCHAR)((CodePoint >> 6) & 0x3f) + 0x80;
2832  codeUnits[2] = (UCHAR)(CodePoint & 0x3f) + 0x80;
2833  }
2834  }
2835  else if (CodePoint <= PH_UNICODE_MAX_CODE_POINT)
2836  {
2837  *NumberOfCodeUnits = 4;
2838 
2839  if (codeUnits)
2840  {
2841  codeUnits[0] = (UCHAR)(CodePoint >> 18) + 0xf0;
2842  codeUnits[1] = (UCHAR)((CodePoint >> 12) & 0x3f) + 0x80;
2843  codeUnits[2] = (UCHAR)((CodePoint >> 6) & 0x3f) + 0x80;
2844  codeUnits[3] = (UCHAR)(CodePoint & 0x3f) + 0x80;
2845  }
2846  }
2847  else
2848  {
2849  return FALSE;
2850  }
2851  }
2852  return TRUE;
2853  case PH_UNICODE_UTF16:
2854  {
2855  PUSHORT codeUnits = CodeUnits;
2856 
2857  if (CodePoint < 0x10000)
2858  {
2859  *NumberOfCodeUnits = 1;
2860 
2861  if (codeUnits)
2862  codeUnits[0] = (USHORT)CodePoint;
2863  }
2864  else if (CodePoint <= PH_UNICODE_MAX_CODE_POINT)
2865  {
2866  *NumberOfCodeUnits = 2;
2867 
2868  if (codeUnits)
2869  {
2870  codeUnits[0] = PH_UNICODE_UTF16_TO_HIGH_SURROGATE(CodePoint);
2871  codeUnits[1] = PH_UNICODE_UTF16_TO_LOW_SURROGATE(CodePoint);
2872  }
2873  }
2874  else
2875  {
2876  return FALSE;
2877  }
2878  }
2879  return TRUE;
2880  case PH_UNICODE_UTF32:
2881  *NumberOfCodeUnits = 1;
2882  if (CodeUnits)
2883  *(PULONG)CodeUnits = CodePoint;
2884  return TRUE;
2885  default:
2886  return FALSE;
2887  }
2888 }
2889 
2898  _In_reads_bytes_(InputLength) PSTR Input,
2899  _In_ SIZE_T InputLength,
2900  _Out_writes_bytes_(InputLength * sizeof(WCHAR)) PWSTR Output
2901  )
2902 {
2903  SIZE_T inputLength;
2904 
2905  inputLength = InputLength & -4;
2906 
2907  if (inputLength)
2908  {
2909  do
2910  {
2911  Output[0] = C_1uTo2(Input[0]);
2912  Output[1] = C_1uTo2(Input[1]);
2913  Output[2] = C_1uTo2(Input[2]);
2914  Output[3] = C_1uTo2(Input[3]);
2915  Input += 4;
2916  Output += 4;
2917  inputLength -= 4;
2918  } while (inputLength != 0);
2919  }
2920 
2921  switch (InputLength & 3)
2922  {
2923  case 3:
2924  *Output++ = C_1uTo2(*Input++);
2925  case 2:
2926  *Output++ = C_1uTo2(*Input++);
2927  case 1:
2928  *Output++ = C_1uTo2(*Input++);
2929  }
2930 }
2931 
2933  _In_reads_bytes_(InputLength) PCH Input,
2934  _In_ SIZE_T InputLength
2935  )
2936 {
2937  PPH_STRING string;
2938 
2939  string = PhCreateStringEx(NULL, InputLength * sizeof(WCHAR));
2940  PhZeroExtendToUtf16Buffer(Input, InputLength, string->Buffer);
2941 
2942  return string;
2943 }
2944 
2946  _In_ PWCH Buffer,
2947  _In_ SIZE_T Length,
2948  _In_opt_ CHAR Replacement
2949  )
2950 {
2951  PPH_BYTES bytes;
2952  PH_UNICODE_DECODER decoder;
2953  PWCH in;
2954  SIZE_T inRemaining;
2955  PCH out;
2956  SIZE_T outLength;
2957  ULONG codePoint;
2958 
2959  bytes = PhCreateBytesEx(NULL, Length / sizeof(WCHAR));
2961  in = Buffer;
2962  inRemaining = Length / sizeof(WCHAR);
2963  out = bytes->Buffer;
2964  outLength = 0;
2965 
2966  while (inRemaining != 0)
2967  {
2968  PhWriteUnicodeDecoder(&decoder, (USHORT)*in);
2969  in++;
2970  inRemaining--;
2971 
2972  while (PhDecodeUnicodeDecoder(&decoder, &codePoint))
2973  {
2974  if (codePoint < 0x80)
2975  {
2976  *out++ = (CHAR)codePoint;
2977  outLength++;
2978  }
2979  else if (Replacement)
2980  {
2981  *out++ = Replacement;
2982  outLength++;
2983  }
2984  }
2985  }
2986 
2987  bytes->Length = outLength;
2988  bytes->Buffer[outLength] = 0;
2989 
2990  return bytes;
2991 }
2992 
2999  _In_ PSTR Buffer
3000  )
3001 {
3003  Buffer,
3004  strlen(Buffer)
3005  );
3006 }
3007 
3015  _In_ PCHAR Buffer,
3016  _In_ SIZE_T Length
3017  )
3018 {
3019  NTSTATUS status;
3020  PPH_STRING string;
3021  ULONG unicodeBytes;
3022 
3023  status = RtlMultiByteToUnicodeSize(
3024  &unicodeBytes,
3025  Buffer,
3026  (ULONG)Length
3027  );
3028 
3029  if (!NT_SUCCESS(status))
3030  return NULL;
3031 
3032  string = PhCreateStringEx(NULL, unicodeBytes);
3033  status = RtlMultiByteToUnicodeN(
3034  string->Buffer,
3035  (ULONG)string->Length,
3036  NULL,
3037  Buffer,
3038  (ULONG)Length
3039  );
3040 
3041  if (!NT_SUCCESS(status))
3042  {
3043  PhDereferenceObject(string);
3044  return NULL;
3045  }
3046 
3047  return string;
3048 }
3049 
3056  _In_ PWSTR Buffer
3057  )
3058 {
3060  Buffer,
3061  PhCountStringZ(Buffer) * sizeof(WCHAR)
3062  );
3063 }
3064 
3072  _In_ PWCHAR Buffer,
3073  _In_ SIZE_T Length
3074  )
3075 {
3076  NTSTATUS status;
3077  PPH_BYTES bytes;
3078  ULONG multiByteLength;
3079 
3080  status = RtlUnicodeToMultiByteSize(
3081  &multiByteLength,
3082  Buffer,
3083  (ULONG)Length
3084  );
3085 
3086  if (!NT_SUCCESS(status))
3087  return NULL;
3088 
3089  bytes = PhCreateBytesEx(NULL, multiByteLength);
3090  status = RtlUnicodeToMultiByteN(
3091  bytes->Buffer,
3092  (ULONG)bytes->Length,
3093  NULL,
3094  Buffer,
3095  (ULONG)Length
3096  );
3097 
3098  if (!NT_SUCCESS(status))
3099  {
3100  PhDereferenceObject(bytes);
3101  return NULL;
3102  }
3103 
3104  return bytes;
3105 }
3106 
3108  _Out_ PSIZE_T BytesInUtf16String,
3109  _In_reads_bytes_(BytesInUtf8String) PCH Utf8String,
3110  _In_ SIZE_T BytesInUtf8String
3111  )
3112 {
3113  BOOLEAN result;
3114  PH_UNICODE_DECODER decoder;
3115  PCH in;
3116  SIZE_T inRemaining;
3117  SIZE_T bytesInUtf16String;
3118  ULONG codePoint;
3119  ULONG numberOfCodeUnits;
3120 
3121  result = TRUE;
3123  in = Utf8String;
3124  inRemaining = BytesInUtf8String;
3125  bytesInUtf16String = 0;
3126 
3127  while (inRemaining != 0)
3128  {
3129  PhWriteUnicodeDecoder(&decoder, (UCHAR)*in);
3130  in++;
3131  inRemaining--;
3132 
3133  while (PhDecodeUnicodeDecoder(&decoder, &codePoint))
3134  {
3135  if (PhEncodeUnicode(PH_UNICODE_UTF16, codePoint, NULL, &numberOfCodeUnits))
3136  bytesInUtf16String += numberOfCodeUnits * sizeof(WCHAR);
3137  else
3138  result = FALSE;
3139  }
3140  }
3141 
3142  *BytesInUtf16String = bytesInUtf16String;
3143 
3144  return result;
3145 }
3146 
3148  _Out_writes_bytes_to_(MaxBytesInUtf16String, *BytesInUtf16String) PWCH Utf16String,
3149  _In_ SIZE_T MaxBytesInUtf16String,
3150  _Out_opt_ PSIZE_T BytesInUtf16String,
3151  _In_reads_bytes_(BytesInUtf8String) PCH Utf8String,
3152  _In_ SIZE_T BytesInUtf8String
3153  )
3154 {
3155  BOOLEAN result;
3156  PH_UNICODE_DECODER decoder;
3157  PCH in;
3158  SIZE_T inRemaining;
3159  PWCH out;
3160  SIZE_T outRemaining;
3161  SIZE_T bytesInUtf16String;
3162  ULONG codePoint;
3163  USHORT codeUnits[2];
3164  ULONG numberOfCodeUnits;
3165 
3166  result = TRUE;
3168  in = Utf8String;
3169  inRemaining = BytesInUtf8String;
3170  out = Utf16String;
3171  outRemaining = MaxBytesInUtf16String / sizeof(WCHAR);
3172  bytesInUtf16String = 0;
3173 
3174  while (inRemaining != 0)
3175  {
3176  PhWriteUnicodeDecoder(&decoder, (UCHAR)*in);
3177  in++;
3178  inRemaining--;
3179 
3180  while (PhDecodeUnicodeDecoder(&decoder, &codePoint))
3181  {
3182  if (PhEncodeUnicode(PH_UNICODE_UTF16, codePoint, codeUnits, &numberOfCodeUnits))
3183  {
3184  bytesInUtf16String += numberOfCodeUnits * sizeof(WCHAR);
3185 
3186  if (outRemaining >= numberOfCodeUnits)
3187  {
3188  *out++ = codeUnits[0];
3189 
3190  if (numberOfCodeUnits >= 2)
3191  *out++ = codeUnits[1];
3192 
3193  outRemaining -= numberOfCodeUnits;
3194  }
3195  else
3196  {
3197  result = FALSE;
3198  }
3199  }
3200  else
3201  {
3202  result = FALSE;
3203  }
3204  }
3205  }
3206 
3207  if (BytesInUtf16String)
3208  *BytesInUtf16String = bytesInUtf16String;
3209 
3210  return result;
3211 }
3212 
3214  _In_ PSTR Buffer
3215  )
3216 {
3217  return PhConvertUtf8ToUtf16Ex(
3218  Buffer,
3219  strlen(Buffer)
3220  );
3221 }
3222 
3224  _In_ PCHAR Buffer,
3225  _In_ SIZE_T Length
3226  )
3227 {
3228  PPH_STRING string;
3229  SIZE_T utf16Bytes;
3230 
3232  &utf16Bytes,
3233  Buffer,
3234  Length
3235  ))
3236  {
3237  return NULL;
3238  }
3239 
3240  string = PhCreateStringEx(NULL, utf16Bytes);
3241 
3243  string->Buffer,
3244  string->Length,
3245  NULL,
3246  Buffer,
3247  Length
3248  ))
3249  {
3250  PhDereferenceObject(string);
3251  return NULL;
3252  }
3253 
3254  return string;
3255 }
3256 
3258  _Out_ PSIZE_T BytesInUtf8String,
3259  _In_reads_bytes_(BytesInUtf16String) PWCH Utf16String,
3260  _In_ SIZE_T BytesInUtf16String
3261  )
3262 {
3263  BOOLEAN result;
3264  PH_UNICODE_DECODER decoder;
3265  PWCH in;
3266  SIZE_T inRemaining;
3267  SIZE_T bytesInUtf8String;
3268  ULONG codePoint;
3269  ULONG numberOfCodeUnits;
3270 
3271  result = TRUE;
3273  in = Utf16String;
3274  inRemaining = BytesInUtf16String / sizeof(WCHAR);
3275  bytesInUtf8String = 0;
3276 
3277  while (inRemaining != 0)
3278  {
3279  PhWriteUnicodeDecoder(&decoder, (USHORT)*in);
3280  in++;
3281  inRemaining--;
3282 
3283  while (PhDecodeUnicodeDecoder(&decoder, &codePoint))
3284  {
3285  if (PhEncodeUnicode(PH_UNICODE_UTF8, codePoint, NULL, &numberOfCodeUnits))
3286  bytesInUtf8String += numberOfCodeUnits;
3287  else
3288  result = FALSE;
3289  }
3290  }
3291 
3292  *BytesInUtf8String = bytesInUtf8String;
3293 
3294  return result;
3295 }
3296 
3298  _Out_writes_bytes_to_(MaxBytesInUtf8String, *BytesInUtf8String) PCH Utf8String,
3299  _In_ SIZE_T MaxBytesInUtf8String,
3300  _Out_opt_ PSIZE_T BytesInUtf8String,
3301  _In_reads_bytes_(BytesInUtf16String) PWCH Utf16String,
3302  _In_ SIZE_T BytesInUtf16String
3303  )
3304 {
3305  BOOLEAN result;
3306  PH_UNICODE_DECODER decoder;
3307  PWCH in;
3308  SIZE_T inRemaining;
3309  PCH out;
3310  SIZE_T outRemaining;
3311  SIZE_T bytesInUtf8String;
3312  ULONG codePoint;
3313  UCHAR codeUnits[4];
3314  ULONG numberOfCodeUnits;
3315 
3316  result = TRUE;
3318  in = Utf16String;
3319  inRemaining = BytesInUtf16String / sizeof(WCHAR);
3320  out = Utf8String;
3321  outRemaining = MaxBytesInUtf8String;
3322  bytesInUtf8String = 0;
3323 
3324  while (inRemaining != 0)
3325  {
3326  PhWriteUnicodeDecoder(&decoder, (USHORT)*in);
3327  in++;
3328  inRemaining--;
3329 
3330  while (PhDecodeUnicodeDecoder(&decoder, &codePoint))
3331  {
3332  if (PhEncodeUnicode(PH_UNICODE_UTF8, codePoint, codeUnits, &numberOfCodeUnits))
3333  {
3334  bytesInUtf8String += numberOfCodeUnits;
3335 
3336  if (outRemaining >= numberOfCodeUnits)
3337  {
3338  *out++ = codeUnits[0];
3339 
3340  if (numberOfCodeUnits >= 2)
3341  *out++ = codeUnits[1];
3342  if (numberOfCodeUnits >= 3)
3343  *out++ = codeUnits[2];
3344  if (numberOfCodeUnits >= 4)
3345  *out++ = codeUnits[3];
3346 
3347  outRemaining -= numberOfCodeUnits;
3348  }
3349  else
3350  {
3351  result = FALSE;
3352  }
3353  }
3354  else
3355  {
3356  result = FALSE;
3357  }
3358  }
3359  }
3360 
3361  if (BytesInUtf8String)
3362  *BytesInUtf8String = bytesInUtf8String;
3363 
3364  return result;
3365 }
3366 
3368  _In_ PWSTR Buffer
3369  )
3370 {
3371  return PhConvertUtf16ToUtf8Ex(
3372  Buffer,
3373  PhCountStringZ(Buffer) * sizeof(WCHAR)
3374  );
3375 }
3376 
3378  _In_ PWCHAR Buffer,
3379  _In_ SIZE_T Length
3380  )
3381 {
3382  PPH_BYTES bytes;
3383  SIZE_T utf8Bytes;
3384 
3386  &utf8Bytes,
3387  Buffer,
3388  Length
3389  ))
3390  {
3391  return NULL;
3392  }
3393 
3394  bytes = PhCreateBytesEx(NULL, utf8Bytes);
3395 
3397  bytes->Buffer,
3398  bytes->Length,
3399  NULL,
3400  Buffer,
3401  Length
3402  ))
3403  {
3404  PhDereferenceObject(bytes);
3405  return NULL;
3406  }
3407 
3408  return bytes;
3409 }
3410 
3419  _Out_ PPH_STRING_BUILDER StringBuilder,
3420  _In_ SIZE_T InitialCapacity
3421  )
3422 {
3423  // Make sure the initial capacity is even, as required for all string objects.
3424  if (InitialCapacity & 1)
3425  InitialCapacity++;
3426 
3427  StringBuilder->AllocatedLength = InitialCapacity;
3428 
3429  // Allocate a PH_STRING for the string builder.
3430  // We will dereference it and allocate a new one when we need to resize the string.
3431 
3432  StringBuilder->String = PhCreateStringEx(NULL, StringBuilder->AllocatedLength);
3433 
3434  // We will keep modifying the Length field of the string so that:
3435  // 1. We know how much of the string is used, and
3436  // 2. The user can simply get a reference to the string and use it as-is.
3437 
3438  StringBuilder->String->Length = 0;
3439 
3440  // Write the null terminator.
3441  StringBuilder->String->Buffer[0] = 0;
3442 
3443  PHLIB_INC_STATISTIC(BaseStringBuildersCreated);
3444 }
3445 
3452  _Inout_ PPH_STRING_BUILDER StringBuilder
3453  )
3454 {
3455  PhDereferenceObject(StringBuilder->String);
3456 }
3457 
3470  _Inout_ PPH_STRING_BUILDER StringBuilder
3471  )
3472 {
3473  return StringBuilder->String;
3474 }
3475 
3477  _In_ PPH_STRING_BUILDER StringBuilder,
3478  _In_ SIZE_T NewCapacity
3479  )
3480 {
3481  PPH_STRING newString;
3482 
3483  // Double the string size. If that still isn't enough room, just use the new length.
3484 
3485  StringBuilder->AllocatedLength *= 2;
3486 
3487  if (StringBuilder->AllocatedLength < NewCapacity)
3488  StringBuilder->AllocatedLength = NewCapacity;
3489 
3490  // Allocate a new string.
3491  newString = PhCreateStringEx(NULL, StringBuilder->AllocatedLength);
3492 
3493  // Copy the old string to the new string.
3494  memcpy(
3495  newString->Buffer,
3496  StringBuilder->String->Buffer,
3497  StringBuilder->String->Length + sizeof(WCHAR) // Include null terminator
3498  );
3499 
3500  // Copy the old string length.
3501  newString->Length = StringBuilder->String->Length;
3502 
3503  // Dereference the old string and replace it with the new string.
3504  PhMoveReference(&StringBuilder->String, newString);
3505 
3506  PHLIB_INC_STATISTIC(BaseStringBuildersResized);
3507 }
3508 
3510  _In_ PPH_STRING_BUILDER StringBuilder
3511  )
3512 {
3513  assert(!(StringBuilder->String->Length & 1));
3514  *(PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length) = 0;
3515 }
3516 
3524  _Inout_ PPH_STRING_BUILDER StringBuilder,
3525  _In_ PPH_STRINGREF String
3526  )
3527 {
3529  StringBuilder,
3530  String->Buffer,
3531  String->Length
3532  );
3533 }
3534 
3542  _Inout_ PPH_STRING_BUILDER StringBuilder,
3543  _In_ PWSTR String
3544  )
3545 {
3547  StringBuilder,
3548  String,
3549  PhCountStringZ(String) * sizeof(WCHAR)
3550  );
3551 }
3552 
3561  _Inout_ PPH_STRING_BUILDER StringBuilder,
3562  _In_opt_ PWCHAR String,
3563  _In_ SIZE_T Length
3564  )
3565 {
3566  if (Length == 0)
3567  return;
3568 
3569  // See if we need to re-allocate the string.
3570  if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Length)
3571  {
3572  PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Length);
3573  }
3574 
3575  // Copy the string, add the length, then write the null terminator.
3576 
3577  if (String)
3578  {
3579  memcpy(
3580  (PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length,
3581  String,
3582  Length
3583  );
3584  }
3585 
3586  StringBuilder->String->Length += Length;
3587  PhpWriteNullTerminatorStringBuilder(StringBuilder);
3588 }
3589 
3598  _Inout_ PPH_STRING_BUILDER StringBuilder,
3599  _In_ WCHAR Character
3600  )
3601 {
3602  if (StringBuilder->AllocatedLength < StringBuilder->String->Length + sizeof(WCHAR))
3603  {
3604  PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + sizeof(WCHAR));
3605  }
3606 
3607  *(PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length) = Character;
3608  StringBuilder->String->Length += sizeof(WCHAR);
3609  PhpWriteNullTerminatorStringBuilder(StringBuilder);
3610 }
3611 
3621  _Inout_ PPH_STRING_BUILDER StringBuilder,
3622  _In_ WCHAR Character,
3623  _In_ SIZE_T Count
3624  )
3625 {
3626  if (Count == 0)
3627  return;
3628 
3629  // See if we need to re-allocate the string.
3630  if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Count * sizeof(WCHAR))
3631  {
3632  PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Count * sizeof(WCHAR));
3633  }
3634 
3635  wmemset(
3636  (PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length),
3637  Character,
3638  Count
3639  );
3640 
3641  StringBuilder->String->Length += Count * sizeof(WCHAR);
3642  PhpWriteNullTerminatorStringBuilder(StringBuilder);
3643 }
3644 
3653  _Inout_ PPH_STRING_BUILDER StringBuilder,
3654  _In_ _Printf_format_string_ PWSTR Format,
3655  ...
3656  )
3657 {
3658  va_list argptr;
3659 
3660  va_start(argptr, Format);
3661  PhAppendFormatStringBuilder_V(StringBuilder, Format, argptr);
3662 }
3663 
3665  _Inout_ PPH_STRING_BUILDER StringBuilder,
3666  _In_ _Printf_format_string_ PWSTR Format,
3667  _In_ va_list ArgPtr
3668  )
3669 {
3670  int length;
3671  SIZE_T lengthInBytes;
3672 
3673  length = _vscwprintf(Format, ArgPtr);
3674 
3675  if (length == -1 || length == 0)
3676  return;
3677 
3678  lengthInBytes = length * sizeof(WCHAR);
3679 
3680  if (StringBuilder->AllocatedLength < StringBuilder->String->Length + lengthInBytes)
3681  PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + lengthInBytes);
3682 
3683  _vsnwprintf(
3684  (PWCHAR)((PCHAR)StringBuilder->String->Buffer + StringBuilder->String->Length),
3685  length,
3686  Format,
3687  ArgPtr
3688  );
3689 
3690  StringBuilder->String->Length += lengthInBytes;
3691  PhpWriteNullTerminatorStringBuilder(StringBuilder);
3692 }
3693 
3703  _Inout_ PPH_STRING_BUILDER StringBuilder,
3704  _In_ SIZE_T Index,
3705  _In_ PPH_STRINGREF String
3706  )
3707 {
3709  StringBuilder,
3710  Index,
3711  String->Buffer,
3712  String->Length
3713  );
3714 }
3715 
3725  _Inout_ PPH_STRING_BUILDER StringBuilder,
3726  _In_ SIZE_T Index,
3727  _In_ PWSTR String
3728  )
3729 {
3731  StringBuilder,
3732  Index,
3733  String,
3734  PhCountStringZ(String) * sizeof(WCHAR)
3735  );
3736 }
3737 
3749  _Inout_ PPH_STRING_BUILDER StringBuilder,
3750  _In_ SIZE_T Index,
3751  _In_opt_ PWCHAR String,
3752  _In_ SIZE_T Length
3753  )
3754 {
3755  if (Length == 0)
3756  return;
3757 
3758  // See if we need to re-allocate the string.
3759  if (StringBuilder->AllocatedLength < StringBuilder->String->Length + Length)
3760  {
3761  PhpResizeStringBuilder(StringBuilder, StringBuilder->String->Length + Length);
3762  }
3763 
3764  if (Index * sizeof(WCHAR) < StringBuilder->String->Length)
3765  {
3766  // Create some space for the string.
3767  memmove(
3768  &StringBuilder->String->Buffer[Index + Length / sizeof(WCHAR)],
3769  &StringBuilder->String->Buffer[Index],
3770  StringBuilder->String->Length - Index * sizeof(WCHAR)
3771  );
3772  }
3773 
3774  if (String)
3775  {
3776  // Copy the new string.
3777  memcpy(
3778  &StringBuilder->String->Buffer[Index],
3779  String,
3780  Length
3781  );
3782  }
3783 
3784  StringBuilder->String->Length += Length;
3785  PhpWriteNullTerminatorStringBuilder(StringBuilder);
3786 }
3787 
3797  _Inout_ PPH_STRING_BUILDER StringBuilder,
3798  _In_ SIZE_T StartIndex,
3799  _In_ SIZE_T Count
3800  )
3801 {
3802  // Overwrite the removed part with the part
3803  // behind it.
3804 
3805  memmove(
3806  &StringBuilder->String->Buffer[StartIndex],
3807  &StringBuilder->String->Buffer[StartIndex + Count],
3808  StringBuilder->String->Length - (Count + StartIndex) * sizeof(WCHAR)
3809  );
3810  StringBuilder->String->Length -= Count * sizeof(WCHAR);
3811  PhpWriteNullTerminatorStringBuilder(StringBuilder);
3812 }
3813 
3821  _Out_ PPH_BYTES_BUILDER BytesBuilder,
3822  _In_ SIZE_T InitialCapacity
3823  )
3824 {
3825  BytesBuilder->AllocatedLength = InitialCapacity;
3826  BytesBuilder->Bytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength);
3827  BytesBuilder->Bytes->Length = 0;
3828  BytesBuilder->Bytes->Buffer[0] = 0;
3829 }
3830 
3837  _Inout_ PPH_BYTES_BUILDER BytesBuilder
3838  )
3839 {
3840  PhDereferenceObject(BytesBuilder->Bytes);
3841 }
3842 
3855  _Inout_ PPH_BYTES_BUILDER BytesBuilder
3856  )
3857 {
3858  return BytesBuilder->Bytes;
3859 }
3860 
3862  _In_ PPH_BYTES_BUILDER BytesBuilder,
3863  _In_ SIZE_T NewCapacity
3864  )
3865 {
3866  PPH_BYTES newBytes;
3867 
3868  // Double the byte string size. If that still isn't enough room, just use the new length.
3869 
3870  BytesBuilder->AllocatedLength *= 2;
3871 
3872  if (BytesBuilder->AllocatedLength < NewCapacity)
3873  BytesBuilder->AllocatedLength = NewCapacity;
3874 
3875  // Allocate a new byte string.
3876  newBytes = PhCreateBytesEx(NULL, BytesBuilder->AllocatedLength);
3877 
3878  // Copy the old byte string to the new byte string.
3879  memcpy(
3880  newBytes->Buffer,
3881  BytesBuilder->Bytes->Buffer,
3882  BytesBuilder->Bytes->Length + sizeof(CHAR) // Include null terminator
3883  );
3884 
3885  // Copy the old byte string length.
3886  newBytes->Length = BytesBuilder->Bytes->Length;
3887 
3888  // Dereference the old byte string and replace it with the new byte string.
3889  PhMoveReference(&BytesBuilder->Bytes, newBytes);
3890 }
3891 
3893  _In_ PPH_BYTES_BUILDER BytesBuilder
3894  )
3895 {
3896  BytesBuilder->Bytes->Buffer[BytesBuilder->Bytes->Length] = 0;
3897 }
3898 
3906  _Inout_ PPH_BYTES_BUILDER BytesBuilder,
3907  _In_ PPH_BYTESREF Bytes
3908  )
3909 {
3911  BytesBuilder,
3912  Bytes->Buffer,
3913  Bytes->Length,
3914  0,
3915  NULL
3916  );
3917 }
3918 
3926  _Inout_ PPH_BYTES_BUILDER BytesBuilder,
3927  _In_ PCHAR Bytes
3928  )
3929 {
3931  BytesBuilder,
3932  Bytes,
3933  strlen(Bytes),
3934  0,
3935  NULL
3936  );
3937 }
3938 
3952  _Inout_ PPH_BYTES_BUILDER BytesBuilder,
3953  _In_opt_ PVOID Buffer,
3954  _In_ SIZE_T Length,
3955  _In_opt_ SIZE_T Alignment,
3956  _Out_opt_ PSIZE_T Offset
3957  )
3958 {
3959  SIZE_T currentLength;
3960 
3961  currentLength = BytesBuilder->Bytes->Length;
3962 
3963  if (Length == 0)
3964  goto Done;
3965 
3966  if (Alignment)
3967  currentLength = ALIGN_UP_BY(currentLength, Alignment);
3968 
3969  // See if we need to re-allocate the byte string.
3970  if (BytesBuilder->AllocatedLength < currentLength + Length)
3971  PhpResizeBytesBuilder(BytesBuilder, currentLength + Length);
3972 
3973  // Copy the byte string, add the length, then write the null terminator.
3974 
3975  if (Buffer)
3976  memcpy(BytesBuilder->Bytes->Buffer + currentLength, Buffer, Length);
3977 
3978  BytesBuilder->Bytes->Length = currentLength + Length;
3979  PhpWriteNullTerminatorBytesBuilder(BytesBuilder);
3980 
3981 Done:
3982  if (Offset)
3983  *Offset = currentLength;
3984 
3985  return BytesBuilder->Bytes->Buffer + currentLength;
3986 }
3987 
3995  _In_ ULONG InitialCapacity
3996  )
3997 {
3998  PPH_LIST list;
3999 
4000  list = PhCreateObject(sizeof(PH_LIST), PhListType);
4001 
4002  // Initial capacity of 0 is not allowed.
4003  if (InitialCapacity == 0)
4004  InitialCapacity = 1;
4005 
4006  list->Count = 0;
4007  list->AllocatedCount = InitialCapacity;
4008  list->Items = PhAllocate(list->AllocatedCount * sizeof(PVOID));
4009 
4010  return list;
4011 }
4012 
4014  _In_ PVOID Object,
4015  _In_ ULONG Flags
4016  )
4017 {
4018  PPH_LIST list = (PPH_LIST)Object;
4019 
4020  PhFree(list->Items);
4021 }
4022 
4032  _Inout_ PPH_LIST List,
4033  _In_ ULONG NewCapacity
4034  )
4035 {
4036  if (List->Count > NewCapacity)
4037  PhRaiseStatus(STATUS_INVALID_PARAMETER_2);
4038 
4039  List->AllocatedCount = NewCapacity;
4040  List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID));
4041 }
4042 
4050  _Inout_ PPH_LIST List,
4051  _In_ PVOID Item
4052  )
4053 {
4054  // See if we need to resize the list.
4055  if (List->Count == List->AllocatedCount)
4056  {
4057  List->AllocatedCount *= 2;
4058  List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID));
4059  }
4060 
4061  List->Items[List->Count++] = Item;
4062 }
4063 
4072  _Inout_ PPH_LIST List,
4073  _In_ PVOID *Items,
4074  _In_ ULONG Count
4075  )
4076 {
4077  // See if we need to resize the list.
4078  if (List->AllocatedCount < List->Count + Count)
4079  {
4080  List->AllocatedCount *= 2;
4081 
4082  if (List->AllocatedCount < List->Count + Count)
4083  List->AllocatedCount = List->Count + Count;
4084 
4085  List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID));
4086  }
4087 
4088  memcpy(
4089  &List->Items[List->Count],
4090  Items,
4091  Count * sizeof(PVOID)
4092  );
4093 
4094  List->Count += Count;
4095 }
4096 
4103  _Inout_ PPH_LIST List
4104  )
4105 {
4106  List->Count = 0;
4107 }
4108 
4109 _Success_(return != -1)
4119 ULONG PhFindItemList(
4120  _In_ PPH_LIST List,
4121  _In_ PVOID Item
4122  )
4123 {
4124  ULONG i;
4125 
4126  for (i = 0; i < List->Count; i++)
4127  {
4128  if (List->Items[i] == Item)
4129  return i;
4130  }
4131 
4132  return -1;
4133 }
4134 
4143  _Inout_ PPH_LIST List,
4144  _In_ ULONG Index,
4145  _In_ PVOID Item
4146  )
4147 {
4148  PhInsertItemsList(List, Index, &Item, 1);
4149 }
4150 
4160  _Inout_ PPH_LIST List,
4161  _In_ ULONG Index,
4162  _In_ PVOID *Items,
4163  _In_ ULONG Count
4164  )
4165 {
4166  // See if we need to resize the list.
4167  if (List->AllocatedCount < List->Count + Count)
4168  {
4169  List->AllocatedCount *= 2;
4170 
4171  if (List->AllocatedCount < List->Count + Count)
4172  List->AllocatedCount = List->Count + Count;
4173 
4174  List->Items = PhReAllocate(List->Items, List->AllocatedCount * sizeof(PVOID));
4175  }
4176 
4177  if (Index < List->Count)
4178  {
4179  // Shift the existing items backward.
4180  memmove(
4181  &List->Items[Index + Count],
4182  &List->Items[Index],
4183  (List->Count - Index) * sizeof(PVOID)
4184  );
4185  }
4186 
4187  // Copy the new items into the list.
4188  memcpy(
4189  &List->Items[Index],
4190  Items,
4191  Count * sizeof(PVOID)
4192  );
4193 
4194  List->Count += Count;
4195 }
4196 
4204  _Inout_ PPH_LIST List,
4205  _In_ ULONG Index
4206  )
4207 {
4208  PhRemoveItemsList(List, Index, 1);
4209 }
4210 
4220  _Inout_ PPH_LIST List,
4221  _In_ ULONG StartIndex,
4222  _In_ ULONG Count
4223  )
4224 {
4225  // Shift the items after the items forward.
4226  memmove(
4227  &List->Items[StartIndex],
4228  &List->Items[StartIndex + Count],
4229  (List->Count - StartIndex - Count) * sizeof(PVOID)
4230  );
4231 
4232  List->Count -= Count;
4233 }
4234 
4242  _In_ ULONG InitialCapacity
4243  )
4244 {
4245  PPH_POINTER_LIST pointerList;
4246 
4247  pointerList = PhCreateObject(sizeof(PH_POINTER_LIST), PhPointerListType);
4248 
4249  // Initial capacity of 0 is not allowed.
4250  if (InitialCapacity == 0)
4251  InitialCapacity = 1;
4252 
4253  pointerList->Count = 0;
4254  pointerList->AllocatedCount = InitialCapacity;
4255  pointerList->FreeEntry = -1;
4256  pointerList->NextEntry = 0;
4257  pointerList->Items = PhAllocate(pointerList->AllocatedCount * sizeof(PVOID));
4258 
4259  return pointerList;
4260 }
4261 
4263  _In_ PVOID Object,
4264  _In_ ULONG Flags
4265  )
4266 {
4267  PPH_POINTER_LIST pointerList = (PPH_POINTER_LIST)Object;
4268 
4269  PhFree(pointerList->Items);
4270 }
4271 
4275 FORCEINLINE ULONG PhpDecodePointerListIndex(
4276  _In_ PVOID Index
4277  )
4278 {
4279  // At least with Microsoft's compiler, shift right on
4280  // a signed value preserves the sign. This is important
4281  // because we want
4282  // decode(encode(-1)) = ((-1 << 1) | 1) >> 1 = -1.
4283  return (ULONG)((LONG_PTR)Index >> 1);
4284 }
4285 
4289 FORCEINLINE PVOID PhpEncodePointerListIndex(
4290  _In_ ULONG Index
4291  )
4292 {
4293  return (PVOID)(((ULONG_PTR)Index << 1) | 0x1);
4294 }
4295 
4296 FORCEINLINE HANDLE PhpPointerListIndexToHandle(
4297  _In_ ULONG Index
4298  )
4299 {
4300  // Add one to allow NULL handles to indicate
4301  // failure/an invalid index.
4302  return (HANDLE)(Index + 1);
4303 }
4304 
4306  _In_ HANDLE Handle
4307  )
4308 {
4309  return (ULONG)Handle - 1;
4310 }
4311 
4323  _Inout_ PPH_POINTER_LIST PointerList,
4324  _In_ PVOID Pointer
4325  )
4326 {
4327  ULONG index;
4328 
4329  assert(PH_IS_LIST_POINTER_VALID(Pointer));
4330 
4331  // Use a free entry if possible.
4332  if (PointerList->FreeEntry != -1)
4333  {
4334  PVOID oldPointer;
4335 
4336  index = PointerList->FreeEntry;
4337  oldPointer = PointerList->Items[index];
4338  PointerList->Items[index] = Pointer;
4339  PointerList->FreeEntry = PhpDecodePointerListIndex(oldPointer);
4340  }
4341  else
4342  {
4343  // Use the next entry.
4344  if (PointerList->NextEntry == PointerList->AllocatedCount)
4345  {
4346  PointerList->AllocatedCount *= 2;
4347  PointerList->Items = PhReAllocate(PointerList->Items, PointerList->AllocatedCount * sizeof(PVOID));
4348  }
4349 
4350  index = PointerList->NextEntry++;
4351  PointerList->Items[index] = Pointer;
4352  }
4353 
4354  PointerList->Count++;
4355 
4356  return PhpPointerListIndexToHandle(index);
4357 }
4358 
4360  _In_ PPH_POINTER_LIST PointerList,
4361  _Inout_ PULONG EnumerationKey,
4362  _Out_ PVOID *Pointer,
4363  _Out_ PHANDLE PointerHandle
4364  )
4365 {
4366  ULONG index;
4367 
4368  while ((index = *EnumerationKey) < PointerList->NextEntry)
4369  {
4370  PVOID pointer = PointerList->Items[index];
4371 
4372  (*EnumerationKey)++;
4373 
4374  if (PH_IS_LIST_POINTER_VALID(pointer))
4375  {
4376  *Pointer = pointer;
4377  *PointerHandle = PhpPointerListIndexToHandle(index);
4378 
4379  return TRUE;
4380  }
4381  }
4382 
4383  return FALSE;
4384 }
4385 
4399  _In_ PPH_POINTER_LIST PointerList,
4400  _In_ PVOID Pointer
4401  )
4402 {
4403  ULONG i;
4404 
4405  assert(PH_IS_LIST_POINTER_VALID(Pointer));
4406 
4407  for (i = 0; i < PointerList->NextEntry; i++)
4408  {
4409  if (PointerList->Items[i] == Pointer)
4410  return PhpPointerListIndexToHandle(i);
4411  }
4412 
4413  return NULL;
4414 }
4415 
4428  _Inout_ PPH_POINTER_LIST PointerList,
4429  _In_ HANDLE PointerHandle
4430  )
4431 {
4432  ULONG index;
4433 
4434  assert(PointerHandle);
4435 
4436  index = PhpPointerListHandleToIndex(PointerHandle);
4437 
4438  PointerList->Items[index] = PhpEncodePointerListIndex(PointerList->FreeEntry);
4439  PointerList->FreeEntry = index;
4440 
4441  PointerList->Count--;
4442 }
4443 
4444 FORCEINLINE ULONG PhpValidateHash(
4445  _In_ ULONG Hash
4446  )
4447 {
4448  // No point in using a full hash when we're going to
4449  // AND with size minus one anyway.
4450 #if defined(PH_HASHTABLE_FULL_HASH) && !defined(PH_HASHTABLE_POWER_OF_TWO_SIZE)
4451  if (Hash != -1)
4452  return Hash;
4453  else
4454  return 0;
4455 #else
4456  return Hash & MAXLONG;
4457 #endif
4458 }
4459 
4460 FORCEINLINE ULONG PhpIndexFromHash(
4461  _In_ PPH_HASHTABLE Hashtable,
4462  _In_ ULONG Hash
4463  )
4464 {
4465 #ifdef PH_HASHTABLE_POWER_OF_TWO_SIZE
4466  return Hash & (Hashtable->AllocatedBuckets - 1);
4467 #else
4468  return Hash % Hashtable->AllocatedBuckets;
4469 #endif
4470 }
4471 
4472 FORCEINLINE ULONG PhpGetNumberOfBuckets(
4473  _In_ ULONG Capacity
4474  )
4475 {
4476 #ifdef PH_HASHTABLE_POWER_OF_TWO_SIZE
4477  return PhRoundUpToPowerOfTwo(Capacity);
4478 #else
4479  return PhGetPrimeNumber(Capacity);
4480 #endif
4481 }
4482 
4496  _In_ ULONG EntrySize,
4497  _In_ PPH_HASHTABLE_COMPARE_FUNCTION CompareFunction,
4498  _In_ PPH_HASHTABLE_HASH_FUNCTION HashFunction,
4499  _In_ ULONG InitialCapacity
4500  )
4501 {
4502  PPH_HASHTABLE hashtable;
4503 
4504  hashtable = PhCreateObject(sizeof(PH_HASHTABLE), PhHashtableType);
4505 
4506  // Initial capacity of 0 is not allowed.
4507  if (InitialCapacity == 0)
4508  InitialCapacity = 1;
4509 
4510  hashtable->EntrySize = EntrySize;
4511  hashtable->CompareFunction = CompareFunction;
4512  hashtable->HashFunction = HashFunction;
4513 
4514  // Allocate the buckets.
4515  hashtable->AllocatedBuckets = PhpGetNumberOfBuckets(InitialCapacity);
4516  hashtable->Buckets = PhAllocate(sizeof(ULONG) * hashtable->AllocatedBuckets);
4517  // Set all bucket values to -1.
4518  memset(hashtable->Buckets, 0xff, sizeof(ULONG) * hashtable->AllocatedBuckets);
4519 
4520  // Allocate the entries.
4521  hashtable->AllocatedEntries = hashtable->AllocatedBuckets;
4522  hashtable->Entries = PhAllocate(PH_HASHTABLE_ENTRY_SIZE(EntrySize) * hashtable->AllocatedEntries);
4523 
4524  hashtable->Count = 0;
4525  hashtable->FreeEntry = -1;
4526  hashtable->NextEntry = 0;
4527 
4528  return hashtable;
4529 }
4530 
4532  _In_ PVOID Object,
4533  _In_ ULONG Flags
4534  )
4535 {
4536  PPH_HASHTABLE hashtable = (PPH_HASHTABLE)Object;
4537 
4538  PhFree(hashtable->Buckets);
4539  PhFree(hashtable->Entries);
4540 }
4541 
4543  _Inout_ PPH_HASHTABLE Hashtable,
4544  _In_ ULONG NewCapacity
4545  )
4546 {
4547  PPH_HASHTABLE_ENTRY entry;
4548  ULONG i;
4549 
4550  // Re-allocate the buckets. Note that we don't need to keep the
4551  // contents.
4552  Hashtable->AllocatedBuckets = PhpGetNumberOfBuckets(NewCapacity);
4553  PhFree(Hashtable->Buckets);
4554  Hashtable->Buckets = PhAllocate(sizeof(ULONG) * Hashtable->AllocatedBuckets);
4555  // Set all bucket values to -1.
4556  memset(Hashtable->Buckets, 0xff, sizeof(ULONG) * Hashtable->AllocatedBuckets);
4557 
4558  // Re-allocate the entries.
4559  Hashtable->AllocatedEntries = Hashtable->AllocatedBuckets;
4560  Hashtable->Entries = PhReAllocate(
4561  Hashtable->Entries,
4562  PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize) * Hashtable->AllocatedEntries
4563  );
4564 
4565  // Re-distribute the entries among the buckets.
4566 
4567  // PH_HASHTABLE_GET_ENTRY is quite slow (it involves a multiply), so we use a pointer here.
4568  entry = Hashtable->Entries;
4569 
4570  for (i = 0; i < Hashtable->NextEntry; i++)
4571  {
4572  if (entry->HashCode != -1)
4573  {
4574  ULONG index = PhpIndexFromHash(Hashtable, entry->HashCode);
4575 
4576  entry->Next = Hashtable->Buckets[index];
4577  Hashtable->Buckets[index] = i;
4578  }
4579 
4580  entry = (PPH_HASHTABLE_ENTRY)((ULONG_PTR)entry + PH_HASHTABLE_ENTRY_SIZE(Hashtable->EntrySize));
4581  }
4582 }
4583 
4584 FORCEINLINE PVOID PhpAddEntryHashtable(
4585  _Inout_ PPH_HASHTABLE Hashtable,
4586  _In_ PVOID Entry,
4587  _In_ BOOLEAN CheckForDuplicate,
4588  _Out_opt_ PBOOLEAN Added
4589  )
4590 {
4591  ULONG hashCode; // hash code of the new entry
4592  ULONG index; // bucket index of the new entry
4593  ULONG freeEntry; // index of new entry in entry array
4594  PPH_HASHTABLE_ENTRY entry; // pointer to new entry in entry array
4595 
4596  hashCode = PhpValidateHash(Hashtable->HashFunction(Entry));
4597  index = PhpIndexFromHash(Hashtable, hashCode);
4598 
4599  if (CheckForDuplicate)
4600  {
4601  ULONG i;
4602 
4603  for (i = Hashtable->Buckets[index]; i != -1; i = entry->Next)
4604  {
4605  entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i);
4606 
4607  if (entry->HashCode == hashCode && Hashtable->CompareFunction(&entry->Body, Entry))
4608  {
4609  if (Added)
4610  *Added = FALSE;
4611 
4612  return &entry->Body;
4613  }
4614  }
4615  }
4616 
4617  // Use a free entry if possible.
4618  if (Hashtable->FreeEntry != -1)
4619  {
4620  freeEntry = Hashtable->FreeEntry;
4621  entry = PH_HASHTABLE_GET_ENTRY(Hashtable, freeEntry);
4622  Hashtable->FreeEntry = entry->Next;
4623  }
4624  else
4625  {
4626  // Use the next entry in the entry array.
4627 
4628  if (Hashtable->NextEntry == Hashtable->AllocatedEntries)
4629  {
4630  // Resize the hashtable.
4631  PhpResizeHashtable(Hashtable, Hashtable->AllocatedBuckets * 2);
4632  index = PhpIndexFromHash(Hashtable, hashCode);
4633  }
4634 
4635  freeEntry = Hashtable->NextEntry++;
4636  entry = PH_HASHTABLE_GET_ENTRY(Hashtable, freeEntry);
4637  }
4638 
4639  // Initialize the entry.
4640  entry->HashCode = hashCode;
4641  entry->Next = Hashtable->Buckets[index];
4642  Hashtable->Buckets[index] = freeEntry;
4643  // Copy the user-supplied data to the entry.
4644  memcpy(&entry->Body, Entry, Hashtable->EntrySize);
4645 
4646  Hashtable->Count++;
4647 
4648  if (Added)
4649  *Added = TRUE;
4650 
4651  return &entry->Body;
4652 }
4653 
4669  _Inout_ PPH_HASHTABLE Hashtable,
4670  _In_ PVOID Entry
4671  )
4672 {
4673  PVOID entry;
4674  BOOLEAN added;
4675 
4676  entry = PhpAddEntryHashtable(Hashtable, Entry, TRUE, &added);
4677 
4678  if (added)
4679  return entry;
4680  else
4681  return NULL;
4682 }
4683 
4704  _Inout_ PPH_HASHTABLE Hashtable,
4705  _In_ PVOID Entry,
4706  _Out_opt_ PBOOLEAN Added
4707  )
4708 {
4709  return PhpAddEntryHashtable(Hashtable, Entry, TRUE, Added);
4710 }
4711 
4718  _Inout_ PPH_HASHTABLE Hashtable
4719  )
4720 {
4721  if (Hashtable->Count > 0)
4722  {
4723  memset(Hashtable->Buckets, 0xff, sizeof(ULONG) * Hashtable->AllocatedBuckets);
4724  Hashtable->Count = 0;
4725  Hashtable->FreeEntry = -1;
4726  Hashtable->NextEntry = 0;
4727  }
4728 }
4729 
4752  _In_ PPH_HASHTABLE Hashtable,
4753  _Out_ PVOID *Entry,
4754  _Inout_ PULONG EnumerationKey
4755  )
4756 {
4757  while (*EnumerationKey < Hashtable->NextEntry)
4758  {
4759  PPH_HASHTABLE_ENTRY entry = PH_HASHTABLE_GET_ENTRY(Hashtable, *EnumerationKey);
4760 
4761  (*EnumerationKey)++;
4762 
4763  if (entry->HashCode != -1)
4764  {
4765  *Entry = &entry->Body;
4766  return TRUE;
4767  }
4768  }
4769 
4770  return FALSE;
4771 }
4772 
4791  _In_ PPH_HASHTABLE Hashtable,
4792  _In_ PVOID Entry
4793  )
4794 {
4795  ULONG hashCode;
4796  ULONG index;
4797  ULONG i;
4798  PPH_HASHTABLE_ENTRY entry;
4799 
4800  hashCode = PhpValidateHash(Hashtable->HashFunction(Entry));
4801  index = PhpIndexFromHash(Hashtable, hashCode);
4802 
4803  for (i = Hashtable->Buckets[index]; i != -1; i = entry->Next)
4804  {
4805  entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i);
4806 
4807  if (entry->HashCode == hashCode && Hashtable->CompareFunction(&entry->Body, Entry))
4808  {
4809  return &entry->Body;
4810  }
4811  }
4812 
4813  return NULL;
4814 }
4815 
4831  _Inout_ PPH_HASHTABLE Hashtable,
4832  _In_ PVOID Entry
4833  )
4834 {
4835  ULONG hashCode;
4836  ULONG index;
4837  ULONG i;
4838  ULONG previousIndex;
4839  PPH_HASHTABLE_ENTRY entry;
4840 
4841  hashCode = PhpValidateHash(Hashtable->HashFunction(Entry));
4842  index = PhpIndexFromHash(Hashtable, hashCode);
4843  previousIndex = -1;
4844 
4845  for (i = Hashtable->Buckets[index]; i != -1; i = entry->Next)
4846  {
4847  entry = PH_HASHTABLE_GET_ENTRY(Hashtable, i);
4848 
4849  if (entry->HashCode == hashCode && Hashtable->CompareFunction(&entry->Body, Entry))
4850  {
4851  // Unlink the entry from the bucket.
4852  if (previousIndex == -1)
4853  {
4854  Hashtable->Buckets[index] = entry->Next;
4855  }
4856  else
4857  {
4858  PH_HASHTABLE_GET_ENTRY(Hashtable, previousIndex)->Next = entry->Next;
4859  }
4860 
4861  entry->HashCode = -1; // indicates the entry is not being used
4862  entry->Next = Hashtable->FreeEntry;
4863  Hashtable->FreeEntry = i;
4864 
4865  Hashtable->Count--;
4866 
4867  return TRUE;
4868  }
4869 
4870  previousIndex = i;
4871  }
4872 
4873  return FALSE;
4874 }
4875 
4883  _In_reads_(Length) PUCHAR Bytes,
4884  _In_ SIZE_T Length
4885  )
4886 {
4887  ULONG hash = 0;
4888 
4889  if (Length == 0)
4890  return hash;
4891 
4892  // FNV-1a algorithm: http://www.isthe.com/chongo/src/fnv/hash_32a.c
4893 
4894  do
4895  {
4896  hash ^= *Bytes++;
4897  hash *= 0x01000193;
4898  } while (--Length != 0);
4899 
4900  return hash;
4901 }
4902 
4910  _In_ PPH_STRINGREF String,
4911  _In_ BOOLEAN IgnoreCase
4912  )
4913 {
4914  ULONG hash = 0;
4915  SIZE_T count;
4916  PWCHAR p;
4917 
4918  if (String->Length == 0)
4919  return 0;
4920 
4921  count = String->Length / sizeof(WCHAR);
4922  p = String->Buffer;
4923 
4924  if (!IgnoreCase)
4925  {
4926  return PhHashBytes((PUCHAR)String->Buffer, String->Length);
4927  }
4928  else
4929  {
4930  do
4931  {
4932  hash ^= (USHORT)RtlUpcaseUnicodeChar(*p++);
4933  hash *= 0x01000193;
4934  } while (--count != 0);
4935  }
4936 
4937  return hash;
4938 }
4939 
4941  _In_ PVOID Entry1,
4942  _In_ PVOID Entry2
4943  )
4944 {
4945  PPH_KEY_VALUE_PAIR entry1 = Entry1;
4946  PPH_KEY_VALUE_PAIR entry2 = Entry2;
4947 
4948  return entry1->Key == entry2->Key;
4949 }
4950 
4952  _In_ PVOID Entry
4953  )
4954 {
4955  PPH_KEY_VALUE_PAIR entry = Entry;
4956 
4957  return PhHashIntPtr((ULONG_PTR)entry->Key);
4958 }
4959 
4961  _In_ ULONG InitialCapacity
4962  )
4963 {
4964  return PhCreateHashtable(
4965  sizeof(PH_KEY_VALUE_PAIR),
4968  InitialCapacity
4969  );
4970 }
4971 
4973  _Inout_ PPH_HASHTABLE SimpleHashtable,
4974  _In_opt_ PVOID Key,
4975  _In_opt_ PVOID Value
4976  )
4977 {
4978  PH_KEY_VALUE_PAIR entry;
4979 
4980  entry.Key = Key;
4981  entry.Value = Value;
4982 
4983  if (PhAddEntryHashtable(SimpleHashtable, &entry))
4984  return Value;
4985  else
4986  return NULL;
4987 }
4988 
4990  _In_ PPH_HASHTABLE SimpleHashtable,
4991  _In_opt_ PVOID Key
4992  )
4993 {
4994  PH_KEY_VALUE_PAIR lookupEntry;
4995  PPH_KEY_VALUE_PAIR entry;
4996 
4997  lookupEntry.Key = Key;
4998  entry = PhFindEntryHashtable(SimpleHashtable, &lookupEntry);
4999 
5000  if (entry)
5001  return &entry->Value;
5002  else
5003  return NULL;
5004 }
5005 
5007  _Inout_ PPH_HASHTABLE SimpleHashtable,
5008  _In_opt_ PVOID Key
5009  )
5010 {
5011  PH_KEY_VALUE_PAIR lookupEntry;
5012 
5013  lookupEntry.Key = Key;
5014 
5015  return PhRemoveEntryHashtable(SimpleHashtable, &lookupEntry);
5016 }
5017 
5027  _Out_ PPH_FREE_LIST FreeList,
5028  _In_ SIZE_T Size,
5029  _In_ ULONG MaximumCount
5030  )
5031 {
5032  RtlInitializeSListHead(&FreeList->ListHead);
5033  FreeList->Count = 0;
5034  FreeList->MaximumCount = MaximumCount;
5035  FreeList->Size = Size;
5036 }
5037 
5044  _Inout_ PPH_FREE_LIST FreeList
5045  )
5046 {
5047  PPH_FREE_LIST_ENTRY entry;
5048  PSLIST_ENTRY listEntry;
5049 
5050  listEntry = RtlInterlockedFlushSList(&FreeList->ListHead);
5051 
5052  while (listEntry)
5053  {
5054  entry = CONTAINING_RECORD(listEntry, PH_FREE_LIST_ENTRY, ListEntry);
5055  listEntry = listEntry->Next;
5056  PhFree(entry);
5057  }
5058 }
5059 
5071  _Inout_ PPH_FREE_LIST FreeList
5072  )
5073 {
5074  PPH_FREE_LIST_ENTRY entry;
5075  PSLIST_ENTRY listEntry;
5076 
5077  listEntry = RtlInterlockedPopEntrySList(&FreeList->ListHead);
5078 
5079  if (listEntry)
5080  {
5081  _InterlockedDecrement((PLONG)&FreeList->Count);
5082  entry = CONTAINING_RECORD(listEntry, PH_FREE_LIST_ENTRY, ListEntry);
5083  }
5084  else
5085  {
5086  entry = PhAllocate(FIELD_OFFSET(PH_FREE_LIST_ENTRY, Body) + FreeList->Size);
5087  }
5088 
5089  return &entry->Body;
5090 }
5091 
5099  _Inout_ PPH_FREE_LIST FreeList,
5100  _In_ PVOID Memory
5101  )
5102 {
5103  PPH_FREE_LIST_ENTRY entry;
5104 
5105  entry = CONTAINING_RECORD(Memory, PH_FREE_LIST_ENTRY, Body);
5106 
5107  // We don't enforce Count <= MaximumCount (that would require locking),
5108  // but we do check it.
5109  if (FreeList->Count < FreeList->MaximumCount)
5110  {
5111  RtlInterlockedPushEntrySList(&FreeList->ListHead, &entry->ListEntry);
5112  _InterlockedIncrement((PLONG)&FreeList->Count);
5113  }
5114  else
5115  {
5116  PhFree(entry);
5117  }
5118 }
5119 
5126  _Out_ PPH_CALLBACK Callback
5127  )
5128 {
5129  InitializeListHead(&Callback->ListHead);
5130  PhInitializeQueuedLock(&Callback->ListLock);
5131  PhInitializeQueuedLock(&Callback->BusyCondition);
5132 }
5133 
5140  _Inout_ PPH_CALLBACK Callback
5141  )
5142 {
5143  // Nothing for now
5144 }
5145 
5160  _Inout_ PPH_CALLBACK Callback,
5161  _In_ PPH_CALLBACK_FUNCTION Function,
5162  _In_opt_ PVOID Context,
5163  _Out_ PPH_CALLBACK_REGISTRATION Registration
5164  )
5165 {
5167  Callback,
5168  Function,
5169  Context,
5170  0,
5171  Registration
5172  );
5173 }
5174 
5191  _Inout_ PPH_CALLBACK Callback,
5192  _In_ PPH_CALLBACK_FUNCTION Function,
5193  _In_opt_ PVOID Context,
5194  _In_ USHORT Flags,
5195  _Out_ PPH_CALLBACK_REGISTRATION Registration
5196  )
5197 {
5198  Registration->Function = Function;
5199  Registration->Context = Context;
5200  Registration->Busy = 0;
5201  Registration->Unregistering = FALSE;
5202  Registration->Flags = Flags;
5203 
5204  PhAcquireQueuedLockExclusive(&Callback->ListLock);
5205  InsertTailList(&Callback->ListHead, &Registration->ListEntry);
5206  PhReleaseQueuedLockExclusive(&Callback->ListLock);
5207 }
5208 
5222  _Inout_ PPH_CALLBACK Callback,
5223  _Inout_ PPH_CALLBACK_REGISTRATION Registration
5224  )
5225 {
5226  Registration->Unregistering = TRUE;
5227 
5228  PhAcquireQueuedLockExclusive(&Callback->ListLock);
5229 
5230  // Wait for the callback to be unbusy.
5231  while (Registration->Busy)
5232  PhWaitForCondition(&Callback->BusyCondition, &Callback->ListLock, NULL);
5233 
5234  RemoveEntryList(&Registration->ListEntry);
5235 
5236  PhReleaseQueuedLockExclusive(&Callback->ListLock);
5237 }
5238 
5247  _In_ PPH_CALLBACK Callback,
5248  _In_opt_ PVOID Parameter
5249  )
5250 {
5251  PLIST_ENTRY listEntry;
5252 
5253  PhAcquireQueuedLockShared(&Callback->ListLock);
5254 
5255  listEntry = Callback->ListHead.Flink;
5256 
5257  while (listEntry != &Callback->ListHead)
5258  {
5259  PPH_CALLBACK_REGISTRATION registration;
5260  LONG busy;
5261 
5262  registration = CONTAINING_RECORD(listEntry, PH_CALLBACK_REGISTRATION, ListEntry);
5263 
5264  // Don't bother executing the callback function if
5265  // it is being unregistered.
5266  if (registration->Unregistering)
5267  continue;
5268 
5269  _InterlockedIncrement(&registration->Busy);
5270 
5271  // Execute the callback function.
5272 
5273  PhReleaseQueuedLockShared(&Callback->ListLock);
5274  registration->Function(
5275  Parameter,
5276  registration->Context
5277  );
5278  PhAcquireQueuedLockShared(&Callback->ListLock);
5279 
5280  busy = _InterlockedDecrement(&registration->Busy);
5281 
5282  if (registration->Unregistering && busy == 0)
5283  {
5284  // Someone started unregistering while the callback
5285  // function was executing, and we must wake them.
5286  PhPulseAllCondition(&Callback->BusyCondition);
5287  }
5288 
5289  listEntry = listEntry->Flink;
5290  }
5291 
5292  PhReleaseQueuedLockShared(&Callback->ListLock);
5293 }
5294 
5300  _In_ ULONG Minimum
5301  )
5302 {
5303  ULONG i, j;
5304 
5305  for (i = 0; i < sizeof(PhpPrimeNumbers) / sizeof(ULONG); i++)
5306  {
5307  if (PhpPrimeNumbers[i] >= Minimum)
5308  return PhpPrimeNumbers[i];
5309  }
5310 
5311  for (i = Minimum | 1; i < MAXLONG; i += 2)
5312  {
5313  ULONG sqrtI = (ULONG)sqrt(i);
5314 
5315  for (j = 3; j <= sqrtI; j += 2)
5316  {
5317  if (i % j == 0)
5318  {
5319  // Not a prime.
5320  goto NextPrime;
5321  }
5322  }
5323 
5324  // Success.
5325  return i;
5326 NextPrime:
5327  NOTHING;
5328  }
5329 
5330  return Minimum;
5331 }
5332 
5337  _In_ ULONG Number
5338  )
5339 {
5340  Number--;
5341  Number |= Number >> 1;
5342  Number |= Number >> 2;
5343  Number |= Number >> 4;
5344  Number |= Number >> 8;
5345  Number |= Number >> 16;
5346  Number++;
5347 
5348  return Number;
5349 }
5350 
5355  _In_ ULONG Base,
5356  _In_ ULONG Exponent
5357  )
5358 {
5359  ULONG result = 1;
5360 
5361  while (Exponent)
5362  {
5363  if (Exponent & 1)
5364  result *= Base;
5365 
5366  Exponent >>= 1;
5367  Base *= Base;
5368  }
5369 
5370  return result;
5371 }
5372 
5377  _In_ ULONG64 Base,
5378  _In_ ULONG Exponent
5379  )
5380 {
5381  ULONG64 result = 1;
5382 
5383  while (Exponent)
5384  {
5385  if (Exponent & 1)
5386  result *= Base;
5387 
5388  Exponent >>= 1;
5389  Base *= Base;
5390  }
5391 
5392  return result;
5393 }
5394 
5406  _In_ PPH_STRINGREF String,
5407  _Out_writes_bytes_(String->Length / sizeof(WCHAR) / 2) PUCHAR Buffer
5408  )
5409 {
5410  SIZE_T i;
5411  SIZE_T length;
5412 
5413  // The string must have an even length.
5414  if ((String->Length / sizeof(WCHAR)) & 1)
5415  return FALSE;
5416 
5417  length = String->Length / sizeof(WCHAR) / 2;
5418 
5419  for (i = 0; i < length; i++)
5420  {
5421  Buffer[i] =
5422  (UCHAR)(PhCharToInteger[(UCHAR)String->Buffer[i * 2]] << 4) +
5423  (UCHAR)PhCharToInteger[(UCHAR)String->Buffer[i * 2 + 1]];
5424  }
5425 
5426  return TRUE;
5427 }
5428 
5438  _In_reads_bytes_(Length) PUCHAR Buffer,
5439  _In_ ULONG Length
5440  )
5441 {
5442  return PhBufferToHexStringEx(Buffer, Length, FALSE);
5443 }
5444 
5455  _In_reads_bytes_(Length) PUCHAR Buffer,
5456  _In_ ULONG Length,
5457  _In_ BOOLEAN UpperCase
5458  )
5459 {
5460  PCHAR table;
5461  PPH_STRING string;
5462  ULONG i;
5463 
5464  if (UpperCase)
5465  table = PhIntegerToCharUpper;
5466  else
5467  table = PhIntegerToChar;
5468 
5469  string = PhCreateStringEx(NULL, Length * 2 * sizeof(WCHAR));
5470 
5471  for (i = 0; i < Length; i++)
5472  {
5473  string->Buffer[i * 2] = table[Buffer[i] >> 4];
5474  string->Buffer[i * 2 + 1] = table[Buffer[i] & 0xf];
5475  }
5476 
5477  return string;
5478 }
5479 
5490  _In_ PPH_STRINGREF String,
5491  _In_ ULONG Base,
5492  _Out_ PULONG64 Integer
5493  )
5494 {
5495  BOOLEAN valid = TRUE;
5496  ULONG64 result;
5497  SIZE_T length;
5498  SIZE_T i;
5499 
5500  length = String->Length / sizeof(WCHAR);
5501  result = 0;
5502 
5503  for (i = 0; i < length; i++)
5504  {
5505  ULONG value;
5506 
5507  value = PhCharToInteger[(UCHAR)String->Buffer[i]];
5508 
5509  if (value < Base)
5510  result = result * Base + value;
5511  else
5512  valid = FALSE;
5513  }
5514 
5515  *Integer = result;
5516 
5517  return valid;
5518 }
5519 
5545  _In_ PPH_STRINGREF String,
5546  _In_opt_ ULONG Base,
5547  _Out_opt_ PLONG64 Integer
5548  )
5549 {
5550  BOOLEAN valid;
5551  ULONG64 result;
5552  PH_STRINGREF string;
5553  BOOLEAN negative;
5554  ULONG base;
5555 
5556  if (Base > 69)
5557  return FALSE;
5558 
5559  string = *String;
5560  negative = FALSE;
5561 
5562  if (string.Length != 0 && (string.Buffer[0] == '-' || string.Buffer[0] == '+'))
5563  {
5564  if (string.Buffer[0] == '-')
5565  negative = TRUE;
5566 
5567  PhSkipStringRef(&string, sizeof(WCHAR));
5568  }
5569 
5570  // If the caller specified a base, don't perform any
5571  // additional processing.
5572 
5573  if (Base)
5574  {
5575  base = Base;
5576  }
5577  else
5578  {
5579  base = 10;
5580 
5581  if (string.Length >= 2 * sizeof(WCHAR) && string.Buffer[0] == '0')
5582  {
5583  switch (string.Buffer[1])
5584  {
5585  case 'x':
5586  case 'X':
5587  base = 16;
5588  break;
5589  case 'o':
5590  case 'O':
5591  base = 8;
5592  break;
5593  case 'b':
5594  case 'B':
5595  base = 2;
5596  break;
5597  case 't': // ternary
5598  case 'T':
5599  base = 3;
5600  break;
5601  case 'q': // quaternary
5602  case 'Q':
5603  base = 4;
5604  break;
5605  case 'w': // base 12
5606  case 'W':
5607  base = 12;
5608  break;
5609  case 'r': // base 32
5610  case 'R':
5611  base = 32;
5612  break;
5613  }
5614 
5615  if (base != 10)
5616  PhSkipStringRef(&string, 2 * sizeof(WCHAR));
5617  }
5618  }
5619 
5620  valid = PhpStringToInteger64(&string, base, &result);
5621 
5622  if (Integer)
5623  *Integer = negative ? -(LONG64)result : result;
5624 
5625  return valid;
5626 }
5627 
5643  _In_ LONG64 Integer,
5644  _In_opt_ ULONG Base,
5645  _In_ BOOLEAN Signed
5646  )
5647 {
5648  PH_FORMAT format;
5649 
5650  if (Base == 1 || Base > 69)
5651  return NULL;
5652 
5653  if (Signed)
5654  PhInitFormatI64D(&format, Integer);
5655  else
5656  PhInitFormatI64U(&format, Integer);
5657 
5658  if (Base != 0)
5659  {
5660  format.Type |= FormatUseRadix;
5661  format.Radix = (UCHAR)Base;
5662  }
5663 
5664  return PhFormat(&format, 1, 0);
5665 }
5666 
5668  _Out_writes_(PH_TIMESPAN_STR_LEN_1) PWSTR Destination,
5669  _In_ ULONG64 Ticks,
5670  _In_opt_ ULONG Mode
5671  )
5672 {
5673  switch (Mode)
5674  {
5675  case PH_TIMESPAN_HMSM:
5676  _snwprintf(
5677  Destination,
5679  L"%02I64u:%02I64u:%02I64u.%03I64u",
5680  PH_TICKS_PARTIAL_HOURS(Ticks),
5681  PH_TICKS_PARTIAL_MIN(Ticks),
5682  PH_TICKS_PARTIAL_SEC(Ticks),
5683  PH_TICKS_PARTIAL_MS(Ticks)
5684  );
5685  break;
5686  case PH_TIMESPAN_DHMS:
5687  _snwprintf(
5688  Destination,
5690  L"%I64u:%02I64u:%02I64u:%02I64u",
5691  PH_TICKS_PARTIAL_DAYS(Ticks),
5692  PH_TICKS_PARTIAL_HOURS(Ticks),
5693  PH_TICKS_PARTIAL_MIN(Ticks),
5694  PH_TICKS_PARTIAL_SEC(Ticks)
5695  );
5696  break;
5697  default:
5698  _snwprintf(
5699  Destination,
5701  L"%02I64u:%02I64u:%02I64u",
5702  PH_TICKS_PARTIAL_HOURS(Ticks),
5703  PH_TICKS_PARTIAL_MIN(Ticks),
5704  PH_TICKS_PARTIAL_SEC(Ticks)
5705  );
5706  break;
5707  }
5708 }
5709 
5718  _Inout_updates_(Count) _Needs_align_(4) PULONG Memory,
5719  _In_ ULONG Value,
5720  _In_ SIZE_T Count
5721  )
5722 {
5723  __m128i pattern;
5724  SIZE_T count;
5725 
5726  if (PhpVectorLevel < PH_VECTOR_LEVEL_SSE2)
5727  {
5728  if (Count != 0)
5729  {
5730  do
5731  {
5732  *Memory++ = Value;
5733  } while (--Count != 0);
5734  }
5735 
5736  return;
5737  }
5738 
5739  if ((ULONG_PTR)Memory & 0xf)
5740  {
5741  switch ((ULONG_PTR)Memory & 0xf)
5742  {
5743  case 0x4:
5744  if (Count >= 1)
5745  {
5746  *Memory++ = Value;
5747  Count--;
5748  }
5749  __fallthrough;
5750  case 0x8:
5751  if (Count >= 1)
5752  {
5753  *Memory++ = Value;
5754  Count--;
5755  }
5756  __fallthrough;
5757  case 0xc:
5758  if (Count >= 1)
5759  {
5760  *Memory++ = Value;
5761  Count--;
5762  }
5763  break;
5764  }
5765  }
5766 
5767  pattern = _mm_set1_epi32(Value);
5768  count = Count / 4;
5769 
5770  if (count != 0)
5771  {
5772  do
5773  {
5774  _mm_store_si128((__m128i *)Memory, pattern);
5775  Memory += 4;
5776  } while (--count != 0);
5777  }
5778 
5779  switch (Count & 0x3)
5780  {
5781  case 0x3:
5782  *Memory++ = Value;
5783  __fallthrough;
5784  case 0x2:
5785  *Memory++ = Value;
5786  __fallthrough;
5787  case 0x1:
5788  *Memory++ = Value;
5789  break;
5790  }
5791 }
5792 
5793 VOID FASTCALL PhxfFillMemoryUlong(PULONG Memory, ULONG Value, ULONG Count)
5794 {
5795  PhFillMemoryUlong(Memory, Value, Count);
5796 }
5797 
5806  _Inout_updates_(Count) PFLOAT A,
5807  _In_ FLOAT B,
5808  _In_ SIZE_T Count
5809  )
5810 {
5811  PFLOAT endA;
5812  __m128 b;
5813 
5814  if (PhpVectorLevel < PH_VECTOR_LEVEL_SSE2)
5815  {
5816  while (Count--)
5817  *A++ /= B;
5818 
5819  return;
5820  }
5821 
5822  if ((ULONG_PTR)A & 0xf)
5823  {
5824  switch ((ULONG_PTR)A & 0xf)
5825  {
5826  case 0x4:
5827  if (Count >= 1)
5828  {
5829  *A++ /= B;
5830  Count--;
5831  }
5832  __fallthrough;
5833  case 0x8:
5834  if (Count >= 1)
5835  {
5836  *A++ /= B;
5837  Count--;
5838  }
5839  __fallthrough;
5840  case 0xc:
5841  if (Count >= 1)
5842  {
5843  *A++ /= B;
5844  Count--;
5845  }
5846  else
5847  {
5848  return; // essential; A may not be aligned properly
5849  }
5850  break;
5851  }
5852  }
5853 
5854  endA = (PFLOAT)((ULONG_PTR)(A + Count) & ~0xf);
5855  b = _mm_load1_ps(&B);
5856 
5857  while (A != endA)
5858  {
5859  __m128 a;
5860 
5861  a = _mm_load_ps(A);
5862  a = _mm_div_ps(a, b);
5863  _mm_store_ps(A, a);
5864 
5865  A += 4;
5866  }
5867 
5868  switch (Count & 0x3)
5869  {
5870  case 0x3:
5871  *A++ /= B;
5872  __fallthrough;
5873  case 0x2:
5874  *A++ /= B;
5875  __fallthrough;
5876  case 0x1:
5877  *A++ /= B;
5878  break;
5879  }
5880 }
5881 
5882 VOID FASTCALL PhxfDivideSingle2U(PFLOAT A, FLOAT B, ULONG Count)
5883 {
5884  PhDivideSinglesBySingle(A, B, Count);
5885 }