summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/utils/collections
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/utils/collections
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/utils/collections')
-rw-r--r--winpr/libwinpr/utils/collections/ArrayList.c604
-rw-r--r--winpr/libwinpr/utils/collections/BitStream.c176
-rw-r--r--winpr/libwinpr/utils/collections/BufferPool.c558
-rw-r--r--winpr/libwinpr/utils/collections/CountdownEvent.c203
-rw-r--r--winpr/libwinpr/utils/collections/HashTable.c870
-rw-r--r--winpr/libwinpr/utils/collections/LinkedList.c385
-rw-r--r--winpr/libwinpr/utils/collections/ListDictionary.c562
-rw-r--r--winpr/libwinpr/utils/collections/MessagePipe.c80
-rw-r--r--winpr/libwinpr/utils/collections/MessageQueue.c313
-rw-r--r--winpr/libwinpr/utils/collections/Object.c42
-rw-r--r--winpr/libwinpr/utils/collections/ObjectPool.c185
-rw-r--r--winpr/libwinpr/utils/collections/PubSub.c265
-rw-r--r--winpr/libwinpr/utils/collections/Queue.c345
-rw-r--r--winpr/libwinpr/utils/collections/Stack.c252
-rw-r--r--winpr/libwinpr/utils/collections/StreamPool.c407
15 files changed, 5247 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/collections/ArrayList.c b/winpr/libwinpr/utils/collections/ArrayList.c
new file mode 100644
index 0000000..5595edb
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/ArrayList.c
@@ -0,0 +1,604 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System.Collections.ArrayList
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <stdarg.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/collections.h>
+
+#if defined(_WIN32) && (_MSC_VER < 1800) && !defined(__MINGW32__)
+#define va_copy(dest, src) (dest = src)
+#endif
+
+struct s_wArrayList
+{
+ size_t capacity;
+ size_t growthFactor;
+ BOOL synchronized;
+
+ size_t size;
+ void** array;
+ CRITICAL_SECTION lock;
+
+ wObject object;
+};
+
+/**
+ * C equivalent of the C# ArrayList Class:
+ * http://msdn.microsoft.com/en-us/library/system.collections.arraylist.aspx
+ */
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets or sets the number of elements that the ArrayList can contain.
+ */
+
+size_t ArrayList_Capacity(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ return arrayList->capacity;
+}
+
+/**
+ * Gets the number of elements actually contained in the ArrayList.
+ */
+
+size_t ArrayList_Count(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ return arrayList->size;
+}
+
+/**
+ * Gets the internal list of items contained in the ArrayList.
+ */
+
+size_t ArrayList_Items(wArrayList* arrayList, ULONG_PTR** ppItems)
+{
+ WINPR_ASSERT(arrayList);
+ *ppItems = (ULONG_PTR*)arrayList->array;
+ return arrayList->size;
+}
+
+/**
+ * Gets a value indicating whether the ArrayList has a fixed size.
+ */
+
+BOOL ArrayList_IsFixedSized(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ return FALSE;
+}
+
+/**
+ * Gets a value indicating whether the ArrayList is read-only.
+ */
+
+BOOL ArrayList_IsReadOnly(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ return FALSE;
+}
+
+/**
+ * Gets a value indicating whether access to the ArrayList is synchronized (thread safe).
+ */
+
+BOOL ArrayList_IsSynchronized(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ return arrayList->synchronized;
+}
+
+/**
+ * Lock access to the ArrayList
+ */
+
+static void ArrayList_Lock_Conditional(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ if (arrayList->synchronized)
+ EnterCriticalSection(&arrayList->lock);
+}
+
+void ArrayList_Lock(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ EnterCriticalSection(&arrayList->lock);
+}
+
+/**
+ * Unlock access to the ArrayList
+ */
+
+static void ArrayList_Unlock_Conditional(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ if (arrayList->synchronized)
+ LeaveCriticalSection(&arrayList->lock);
+}
+
+void ArrayList_Unlock(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ LeaveCriticalSection(&arrayList->lock);
+}
+
+/**
+ * Gets the element at the specified index.
+ */
+
+void* ArrayList_GetItem(wArrayList* arrayList, size_t index)
+{
+ void* obj = NULL;
+
+ WINPR_ASSERT(arrayList);
+ if (index < arrayList->size)
+ {
+ obj = arrayList->array[index];
+ }
+
+ return obj;
+}
+
+/**
+ * Sets the element at the specified index.
+ */
+
+BOOL ArrayList_SetItem(wArrayList* arrayList, size_t index, const void* obj)
+{
+ WINPR_ASSERT(arrayList);
+ if (index >= arrayList->size)
+ return FALSE;
+
+ if (arrayList->object.fnObjectNew)
+ {
+ arrayList->array[index] = arrayList->object.fnObjectNew(obj);
+ if (obj && !arrayList->array[index])
+ return FALSE;
+ }
+ else
+ {
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ cnv.cpv = obj;
+ arrayList->array[index] = cnv.pv;
+ }
+ return TRUE;
+}
+
+/**
+ * Methods
+ */
+static BOOL ArrayList_EnsureCapacity(wArrayList* arrayList, size_t count)
+{
+ WINPR_ASSERT(arrayList);
+ WINPR_ASSERT(count > 0);
+
+ if (arrayList->size + count > arrayList->capacity)
+ {
+ void** newArray = NULL;
+ size_t newCapacity = arrayList->capacity * arrayList->growthFactor;
+ if (newCapacity < arrayList->size + count)
+ newCapacity = arrayList->size + count;
+
+ newArray = (void**)realloc(arrayList->array, sizeof(void*) * newCapacity);
+
+ if (!newArray)
+ return FALSE;
+
+ arrayList->array = newArray;
+ arrayList->capacity = newCapacity;
+ }
+
+ return TRUE;
+}
+/**
+ * Shift a section of the list.
+ */
+
+static BOOL ArrayList_Shift(wArrayList* arrayList, size_t index, SSIZE_T count)
+{
+ WINPR_ASSERT(arrayList);
+ if (count > 0)
+ {
+ if (!ArrayList_EnsureCapacity(arrayList, count))
+ return FALSE;
+
+ MoveMemory(&arrayList->array[index + count], &arrayList->array[index],
+ (arrayList->size - index) * sizeof(void*));
+ arrayList->size += count;
+ }
+ else if (count < 0)
+ {
+ INT64 chunk = arrayList->size - index + count;
+
+ if (chunk > 0)
+ MoveMemory(&arrayList->array[index], &arrayList->array[index - count],
+ (size_t)chunk * sizeof(void*));
+
+ arrayList->size += count;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Removes all elements from the ArrayList.
+ */
+
+void ArrayList_Clear(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ for (size_t index = 0; index < arrayList->size; index++)
+ {
+ if (arrayList->object.fnObjectFree)
+ arrayList->object.fnObjectFree(arrayList->array[index]);
+
+ arrayList->array[index] = NULL;
+ }
+
+ arrayList->size = 0;
+
+ ArrayList_Unlock_Conditional(arrayList);
+}
+
+/**
+ * Determines whether an element is in the ArrayList.
+ */
+
+BOOL ArrayList_Contains(wArrayList* arrayList, const void* obj)
+{
+ BOOL rc = FALSE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ for (size_t index = 0; index < arrayList->size; index++)
+ {
+ rc = arrayList->object.fnObjectEquals(arrayList->array[index], obj);
+
+ if (rc)
+ break;
+ }
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return rc;
+}
+
+#if defined(WITH_WINPR_DEPRECATED)
+int ArrayList_Add(wArrayList* arrayList, const void* obj)
+{
+ WINPR_ASSERT(arrayList);
+ if (!ArrayList_Append(arrayList, obj))
+ return -1;
+ return (int)ArrayList_Count(arrayList) - 1;
+}
+#endif
+
+/**
+ * Adds an object to the end of the ArrayList.
+ */
+
+BOOL ArrayList_Append(wArrayList* arrayList, const void* obj)
+{
+ size_t index = 0;
+ BOOL rc = FALSE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ if (!ArrayList_EnsureCapacity(arrayList, 1))
+ goto out;
+
+ index = arrayList->size++;
+ rc = ArrayList_SetItem(arrayList, index, obj);
+out:
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return rc;
+}
+
+/*
+ * Inserts an element into the ArrayList at the specified index.
+ */
+
+BOOL ArrayList_Insert(wArrayList* arrayList, size_t index, const void* obj)
+{
+ BOOL ret = TRUE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ if (index < arrayList->size)
+ {
+ if (!ArrayList_Shift(arrayList, index, 1))
+ {
+ ret = FALSE;
+ }
+ else
+ {
+ ArrayList_SetItem(arrayList, index, obj);
+ }
+ }
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return ret;
+}
+
+/**
+ * Removes the first occurrence of a specific object from the ArrayList.
+ */
+
+BOOL ArrayList_Remove(wArrayList* arrayList, const void* obj)
+{
+ BOOL found = FALSE;
+ BOOL ret = TRUE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ size_t index = 0;
+ for (; index < arrayList->size; index++)
+ {
+ if (arrayList->object.fnObjectEquals(arrayList->array[index], obj))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ if (arrayList->object.fnObjectFree)
+ arrayList->object.fnObjectFree(arrayList->array[index]);
+
+ ret = ArrayList_Shift(arrayList, index, -1);
+ }
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return ret;
+}
+
+/**
+ * Removes the element at the specified index of the ArrayList.
+ */
+
+BOOL ArrayList_RemoveAt(wArrayList* arrayList, size_t index)
+{
+ BOOL ret = TRUE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ if (index < arrayList->size)
+ {
+ if (arrayList->object.fnObjectFree)
+ arrayList->object.fnObjectFree(arrayList->array[index]);
+
+ ret = ArrayList_Shift(arrayList, index, -1);
+ }
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return ret;
+}
+
+/**
+ * Searches for the specified Object and returns the zero-based index of the first occurrence within
+ * the entire ArrayList.
+ *
+ * Searches for the specified Object and returns the zero-based index of the last occurrence within
+ * the range of elements in the ArrayList that extends from the first element to the specified
+ * index.
+ *
+ * Searches for the specified Object and returns the zero-based index of the last occurrence within
+ * the range of elements in the ArrayList that contains the specified number of elements and ends at
+ * the specified index.
+ */
+
+SSIZE_T ArrayList_IndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex, SSIZE_T count)
+{
+ SSIZE_T sindex = 0;
+ SSIZE_T cindex = 0;
+ BOOL found = FALSE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ sindex = (size_t)startIndex;
+ if (startIndex < 0)
+ sindex = 0;
+
+ cindex = (size_t)count;
+ if (count < 0)
+ cindex = arrayList->size;
+
+ SSIZE_T index = sindex;
+ for (; index < sindex + cindex; index++)
+ {
+ if (arrayList->object.fnObjectEquals(arrayList->array[index], obj))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ index = -1;
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return index;
+}
+
+/**
+ * Searches for the specified Object and returns the zero-based index of the last occurrence within
+ * the entire ArrayList.
+ *
+ * Searches for the specified Object and returns the zero-based index of the last occurrence within
+ * the range of elements in the ArrayList that extends from the first element to the specified
+ * index.
+ *
+ * Searches for the specified Object and returns the zero-based index of the last occurrence within
+ * the range of elements in the ArrayList that contains the specified number of elements and ends at
+ * the specified index.
+ */
+
+SSIZE_T ArrayList_LastIndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex,
+ SSIZE_T count)
+{
+ SSIZE_T sindex = 0;
+ SSIZE_T cindex = 0;
+ BOOL found = FALSE;
+
+ WINPR_ASSERT(arrayList);
+ ArrayList_Lock_Conditional(arrayList);
+
+ sindex = (size_t)startIndex;
+ if (startIndex < 0)
+ sindex = 0;
+
+ cindex = (size_t)count;
+ if (count < 0)
+ cindex = arrayList->size;
+
+ SSIZE_T index = sindex + cindex;
+ for (; index > sindex; index--)
+ {
+ if (arrayList->object.fnObjectEquals(arrayList->array[index - 1], obj))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found)
+ index = -1;
+
+ ArrayList_Unlock_Conditional(arrayList);
+
+ return index;
+}
+
+static BOOL ArrayList_DefaultCompare(const void* objA, const void* objB)
+{
+ return objA == objB ? TRUE : FALSE;
+}
+
+wObject* ArrayList_Object(wArrayList* arrayList)
+{
+ WINPR_ASSERT(arrayList);
+ return &arrayList->object;
+}
+
+BOOL ArrayList_ForEach(wArrayList* arrayList, ArrayList_ForEachFkt fkt, ...)
+{
+ BOOL rc = 0;
+ va_list ap;
+ va_start(ap, fkt);
+ rc = ArrayList_ForEachAP(arrayList, fkt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+BOOL ArrayList_ForEachAP(wArrayList* arrayList, ArrayList_ForEachFkt fkt, va_list ap)
+{
+ BOOL rc = FALSE;
+ va_list cap;
+
+ WINPR_ASSERT(arrayList);
+ WINPR_ASSERT(fkt);
+
+ ArrayList_Lock_Conditional(arrayList);
+ size_t count = ArrayList_Count(arrayList);
+ for (size_t index = 0; index < count; index++)
+ {
+ BOOL rs = 0;
+ void* obj = ArrayList_GetItem(arrayList, index);
+ va_copy(cap, ap);
+ rs = fkt(obj, index, cap);
+ va_end(cap);
+ if (!rs)
+ goto fail;
+ }
+ rc = TRUE;
+fail:
+ ArrayList_Unlock_Conditional(arrayList);
+ return rc;
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wArrayList* ArrayList_New(BOOL synchronized)
+{
+ wObject* obj = NULL;
+ wArrayList* arrayList = NULL;
+ arrayList = (wArrayList*)calloc(1, sizeof(wArrayList));
+
+ if (!arrayList)
+ return NULL;
+
+ arrayList->synchronized = synchronized;
+ arrayList->growthFactor = 2;
+ obj = ArrayList_Object(arrayList);
+ if (!obj)
+ goto fail;
+ obj->fnObjectEquals = ArrayList_DefaultCompare;
+ if (!ArrayList_EnsureCapacity(arrayList, 32))
+ goto fail;
+
+ InitializeCriticalSectionAndSpinCount(&arrayList->lock, 4000);
+ return arrayList;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ ArrayList_Free(arrayList);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void ArrayList_Free(wArrayList* arrayList)
+{
+ if (!arrayList)
+ return;
+
+ ArrayList_Clear(arrayList);
+ DeleteCriticalSection(&arrayList->lock);
+ free(arrayList->array);
+ free(arrayList);
+}
diff --git a/winpr/libwinpr/utils/collections/BitStream.c b/winpr/libwinpr/utils/collections/BitStream.c
new file mode 100644
index 0000000..e57af94
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/BitStream.c
@@ -0,0 +1,176 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * BitStream
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/assert.h>
+#include <winpr/config.h>
+
+#include <winpr/print.h>
+#include <winpr/bitstream.h>
+
+static const char* BYTE_BIT_STRINGS_LSB[256] = {
+ "00000000", "00000001", "00000010", "00000011", "00000100", "00000101", "00000110", "00000111",
+ "00001000", "00001001", "00001010", "00001011", "00001100", "00001101", "00001110", "00001111",
+ "00010000", "00010001", "00010010", "00010011", "00010100", "00010101", "00010110", "00010111",
+ "00011000", "00011001", "00011010", "00011011", "00011100", "00011101", "00011110", "00011111",
+ "00100000", "00100001", "00100010", "00100011", "00100100", "00100101", "00100110", "00100111",
+ "00101000", "00101001", "00101010", "00101011", "00101100", "00101101", "00101110", "00101111",
+ "00110000", "00110001", "00110010", "00110011", "00110100", "00110101", "00110110", "00110111",
+ "00111000", "00111001", "00111010", "00111011", "00111100", "00111101", "00111110", "00111111",
+ "01000000", "01000001", "01000010", "01000011", "01000100", "01000101", "01000110", "01000111",
+ "01001000", "01001001", "01001010", "01001011", "01001100", "01001101", "01001110", "01001111",
+ "01010000", "01010001", "01010010", "01010011", "01010100", "01010101", "01010110", "01010111",
+ "01011000", "01011001", "01011010", "01011011", "01011100", "01011101", "01011110", "01011111",
+ "01100000", "01100001", "01100010", "01100011", "01100100", "01100101", "01100110", "01100111",
+ "01101000", "01101001", "01101010", "01101011", "01101100", "01101101", "01101110", "01101111",
+ "01110000", "01110001", "01110010", "01110011", "01110100", "01110101", "01110110", "01110111",
+ "01111000", "01111001", "01111010", "01111011", "01111100", "01111101", "01111110", "01111111",
+ "10000000", "10000001", "10000010", "10000011", "10000100", "10000101", "10000110", "10000111",
+ "10001000", "10001001", "10001010", "10001011", "10001100", "10001101", "10001110", "10001111",
+ "10010000", "10010001", "10010010", "10010011", "10010100", "10010101", "10010110", "10010111",
+ "10011000", "10011001", "10011010", "10011011", "10011100", "10011101", "10011110", "10011111",
+ "10100000", "10100001", "10100010", "10100011", "10100100", "10100101", "10100110", "10100111",
+ "10101000", "10101001", "10101010", "10101011", "10101100", "10101101", "10101110", "10101111",
+ "10110000", "10110001", "10110010", "10110011", "10110100", "10110101", "10110110", "10110111",
+ "10111000", "10111001", "10111010", "10111011", "10111100", "10111101", "10111110", "10111111",
+ "11000000", "11000001", "11000010", "11000011", "11000100", "11000101", "11000110", "11000111",
+ "11001000", "11001001", "11001010", "11001011", "11001100", "11001101", "11001110", "11001111",
+ "11010000", "11010001", "11010010", "11010011", "11010100", "11010101", "11010110", "11010111",
+ "11011000", "11011001", "11011010", "11011011", "11011100", "11011101", "11011110", "11011111",
+ "11100000", "11100001", "11100010", "11100011", "11100100", "11100101", "11100110", "11100111",
+ "11101000", "11101001", "11101010", "11101011", "11101100", "11101101", "11101110", "11101111",
+ "11110000", "11110001", "11110010", "11110011", "11110100", "11110101", "11110110", "11110111",
+ "11111000", "11111001", "11111010", "11111011", "11111100", "11111101", "11111110", "11111111"
+};
+
+static const char* BYTE_BIT_STRINGS_MSB[256] = {
+ "00000000", "10000000", "01000000", "11000000", "00100000", "10100000", "01100000", "11100000",
+ "00010000", "10010000", "01010000", "11010000", "00110000", "10110000", "01110000", "11110000",
+ "00001000", "10001000", "01001000", "11001000", "00101000", "10101000", "01101000", "11101000",
+ "00011000", "10011000", "01011000", "11011000", "00111000", "10111000", "01111000", "11111000",
+ "00000100", "10000100", "01000100", "11000100", "00100100", "10100100", "01100100", "11100100",
+ "00010100", "10010100", "01010100", "11010100", "00110100", "10110100", "01110100", "11110100",
+ "00001100", "10001100", "01001100", "11001100", "00101100", "10101100", "01101100", "11101100",
+ "00011100", "10011100", "01011100", "11011100", "00111100", "10111100", "01111100", "11111100",
+ "00000010", "10000010", "01000010", "11000010", "00100010", "10100010", "01100010", "11100010",
+ "00010010", "10010010", "01010010", "11010010", "00110010", "10110010", "01110010", "11110010",
+ "00001010", "10001010", "01001010", "11001010", "00101010", "10101010", "01101010", "11101010",
+ "00011010", "10011010", "01011010", "11011010", "00111010", "10111010", "01111010", "11111010",
+ "00000110", "10000110", "01000110", "11000110", "00100110", "10100110", "01100110", "11100110",
+ "00010110", "10010110", "01010110", "11010110", "00110110", "10110110", "01110110", "11110110",
+ "00001110", "10001110", "01001110", "11001110", "00101110", "10101110", "01101110", "11101110",
+ "00011110", "10011110", "01011110", "11011110", "00111110", "10111110", "01111110", "11111110",
+ "00000001", "10000001", "01000001", "11000001", "00100001", "10100001", "01100001", "11100001",
+ "00010001", "10010001", "01010001", "11010001", "00110001", "10110001", "01110001", "11110001",
+ "00001001", "10001001", "01001001", "11001001", "00101001", "10101001", "01101001", "11101001",
+ "00011001", "10011001", "01011001", "11011001", "00111001", "10111001", "01111001", "11111001",
+ "00000101", "10000101", "01000101", "11000101", "00100101", "10100101", "01100101", "11100101",
+ "00010101", "10010101", "01010101", "11010101", "00110101", "10110101", "01110101", "11110101",
+ "00001101", "10001101", "01001101", "11001101", "00101101", "10101101", "01101101", "11101101",
+ "00011101", "10011101", "01011101", "11011101", "00111101", "10111101", "01111101", "11111101",
+ "00000011", "10000011", "01000011", "11000011", "00100011", "10100011", "01100011", "11100011",
+ "00010011", "10010011", "01010011", "11010011", "00110011", "10110011", "01110011", "11110011",
+ "00001011", "10001011", "01001011", "11001011", "00101011", "10101011", "01101011", "11101011",
+ "00011011", "10011011", "01011011", "11011011", "00111011", "10111011", "01111011", "11111011",
+ "00000111", "10000111", "01000111", "11000111", "00100111", "10100111", "01100111", "11100111",
+ "00010111", "10010111", "01010111", "11010111", "00110111", "10110111", "01110111", "11110111",
+ "00001111", "10001111", "01001111", "11001111", "00101111", "10101111", "01101111", "11101111",
+ "00011111", "10011111", "01011111", "11011111", "00111111", "10111111", "01111111", "11111111"
+};
+
+void BitDump(const char* tag, UINT32 level, const BYTE* buffer, UINT32 length, UINT32 flags)
+{
+ const char** strs = (flags & BITDUMP_MSB_FIRST) ? BYTE_BIT_STRINGS_MSB : BYTE_BIT_STRINGS_LSB;
+ char pbuffer[64 * 8 + 1] = { 0 };
+ size_t pos = 0;
+
+ WINPR_ASSERT(tag);
+ WINPR_ASSERT(buffer || (length == 0));
+
+ DWORD i = 0;
+ for (; i < length; i += 8)
+ {
+ const char* str = strs[buffer[i / 8]];
+ const int nbits = (length - i) > 8 ? 8 : (length - i);
+ const int rc = _snprintf(&pbuffer[pos], length - pos, "%.*s ", nbits, str);
+ if (rc < 0)
+ return;
+
+ pos += (size_t)rc;
+ if ((i % 64) == 0)
+ {
+ pos = 0;
+ WLog_LVL(tag, level, "%s", pbuffer);
+ }
+ }
+
+ if (i)
+ WLog_LVL(tag, level, "%s ", pbuffer);
+}
+
+UINT32 ReverseBits32(UINT32 bits, UINT32 nbits)
+{
+ UINT32 rbits = 0;
+
+ do
+ {
+ rbits = (rbits | (bits & 1)) << 1;
+ bits >>= 1;
+ nbits--;
+ } while (nbits > 0);
+
+ rbits >>= 1;
+ return rbits;
+}
+
+void BitStream_Attach(wBitStream* bs, const BYTE* buffer, UINT32 capacity)
+{
+ union
+ {
+ const BYTE* cpv;
+ BYTE* pv;
+ } cnv;
+
+ WINPR_ASSERT(bs);
+ WINPR_ASSERT(buffer);
+
+ cnv.cpv = buffer;
+
+ bs->position = 0;
+ bs->buffer = cnv.pv;
+ bs->offset = 0;
+ bs->accumulator = 0;
+ bs->pointer = cnv.pv;
+ bs->capacity = capacity;
+ bs->length = bs->capacity * 8;
+}
+
+wBitStream* BitStream_New(void)
+{
+ wBitStream* bs = (wBitStream*)calloc(1, sizeof(wBitStream));
+
+ return bs;
+}
+
+void BitStream_Free(wBitStream* bs)
+{
+ if (!bs)
+ return;
+
+ free(bs);
+}
diff --git a/winpr/libwinpr/utils/collections/BufferPool.c b/winpr/libwinpr/utils/collections/BufferPool.c
new file mode 100644
index 0000000..ebdf4f1
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/BufferPool.c
@@ -0,0 +1,558 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Buffer Pool
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+
+#include <winpr/collections.h>
+
+typedef struct
+{
+ SSIZE_T size;
+ void* buffer;
+} wBufferPoolItem;
+
+struct s_wBufferPool
+{
+ SSIZE_T fixedSize;
+ DWORD alignment;
+ BOOL synchronized;
+ CRITICAL_SECTION lock;
+
+ SSIZE_T size;
+ SSIZE_T capacity;
+ void** array;
+
+ SSIZE_T aSize;
+ SSIZE_T aCapacity;
+ wBufferPoolItem* aArray;
+
+ SSIZE_T uSize;
+ SSIZE_T uCapacity;
+ wBufferPoolItem* uArray;
+};
+
+static BOOL BufferPool_Lock(wBufferPool* pool)
+{
+ if (!pool)
+ return FALSE;
+
+ if (pool->synchronized)
+ EnterCriticalSection(&pool->lock);
+ return TRUE;
+}
+
+static BOOL BufferPool_Unlock(wBufferPool* pool)
+{
+ if (!pool)
+ return FALSE;
+
+ if (pool->synchronized)
+ LeaveCriticalSection(&pool->lock);
+ return TRUE;
+}
+
+/**
+ * C equivalent of the C# BufferManager Class:
+ * http://msdn.microsoft.com/en-us/library/ms405814.aspx
+ */
+
+/**
+ * Methods
+ */
+
+static BOOL BufferPool_ShiftAvailable(wBufferPool* pool, size_t index, int count)
+{
+ if (count > 0)
+ {
+ if (pool->aSize + count > pool->aCapacity)
+ {
+ wBufferPoolItem* newArray = NULL;
+ SSIZE_T newCapacity = pool->aCapacity * 2;
+
+ if (pool->alignment > 0)
+ newArray = (wBufferPoolItem*)winpr_aligned_realloc(
+ pool->aArray, sizeof(wBufferPoolItem) * newCapacity, pool->alignment);
+ else
+ newArray =
+ (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity);
+ if (!newArray)
+ return FALSE;
+ pool->aArray = newArray;
+ pool->aCapacity = newCapacity;
+ }
+
+ MoveMemory(&pool->aArray[index + count], &pool->aArray[index],
+ (pool->aSize - index) * sizeof(wBufferPoolItem));
+ pool->aSize += count;
+ }
+ else if (count < 0)
+ {
+ MoveMemory(&pool->aArray[index], &pool->aArray[index - count],
+ (pool->aSize - index) * sizeof(wBufferPoolItem));
+ pool->aSize += count;
+ }
+ return TRUE;
+}
+
+static BOOL BufferPool_ShiftUsed(wBufferPool* pool, SSIZE_T index, SSIZE_T count)
+{
+ if (count > 0)
+ {
+ if (pool->uSize + count > pool->uCapacity)
+ {
+ SSIZE_T newUCapacity = pool->uCapacity * 2;
+ wBufferPoolItem* newUArray = NULL;
+ if (pool->alignment > 0)
+ newUArray = (wBufferPoolItem*)winpr_aligned_realloc(
+ pool->uArray, sizeof(wBufferPoolItem) * newUCapacity, pool->alignment);
+ else
+ newUArray =
+ (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
+ if (!newUArray)
+ return FALSE;
+ pool->uCapacity = newUCapacity;
+ pool->uArray = newUArray;
+ }
+
+ MoveMemory(&pool->uArray[index + count], &pool->uArray[index],
+ (pool->uSize - index) * sizeof(wBufferPoolItem));
+ pool->uSize += count;
+ }
+ else if (count < 0)
+ {
+ MoveMemory(&pool->uArray[index], &pool->uArray[index - count],
+ (pool->uSize - index) * sizeof(wBufferPoolItem));
+ pool->uSize += count;
+ }
+ return TRUE;
+}
+
+/**
+ * Get the buffer pool size
+ */
+
+SSIZE_T BufferPool_GetPoolSize(wBufferPool* pool)
+{
+ SSIZE_T size = 0;
+
+ BufferPool_Lock(pool);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+ size = pool->size;
+ }
+ else
+ {
+ /* variable size buffers */
+ size = pool->uSize;
+ }
+
+ BufferPool_Unlock(pool);
+
+ return size;
+}
+
+/**
+ * Get the size of a pooled buffer
+ */
+
+SSIZE_T BufferPool_GetBufferSize(wBufferPool* pool, const void* buffer)
+{
+ SSIZE_T size = 0;
+ BOOL found = FALSE;
+
+ BufferPool_Lock(pool);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+ size = pool->fixedSize;
+ found = TRUE;
+ }
+ else
+ {
+ /* variable size buffers */
+
+ for (SSIZE_T index = 0; index < pool->uSize; index++)
+ {
+ if (pool->uArray[index].buffer == buffer)
+ {
+ size = pool->uArray[index].size;
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ BufferPool_Unlock(pool);
+
+ return (found) ? size : -1;
+}
+
+/**
+ * Gets a buffer of at least the specified size from the pool.
+ */
+
+void* BufferPool_Take(wBufferPool* pool, SSIZE_T size)
+{
+ SSIZE_T maxSize = 0;
+ SSIZE_T maxIndex = 0;
+ SSIZE_T foundIndex = -1;
+ BOOL found = FALSE;
+ void* buffer = NULL;
+
+ BufferPool_Lock(pool);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+
+ if (pool->size > 0)
+ buffer = pool->array[--(pool->size)];
+
+ if (!buffer)
+ {
+ if (pool->alignment)
+ buffer = winpr_aligned_malloc(pool->fixedSize, pool->alignment);
+ else
+ buffer = malloc(pool->fixedSize);
+ }
+
+ if (!buffer)
+ goto out_error;
+ }
+ else
+ {
+ /* variable size buffers */
+
+ maxSize = 0;
+ maxIndex = 0;
+
+ if (size < 1)
+ size = pool->fixedSize;
+
+ for (SSIZE_T index = 0; index < pool->aSize; index++)
+ {
+ if (pool->aArray[index].size > maxSize)
+ {
+ maxIndex = index;
+ maxSize = pool->aArray[index].size;
+ }
+
+ if (pool->aArray[index].size >= size)
+ {
+ foundIndex = index;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found && maxSize)
+ {
+ foundIndex = maxIndex;
+ found = TRUE;
+ }
+
+ if (!found)
+ {
+ if (!size)
+ buffer = NULL;
+ else
+ {
+ if (pool->alignment)
+ buffer = winpr_aligned_malloc(size, pool->alignment);
+ else
+ buffer = malloc(size);
+
+ if (!buffer)
+ goto out_error;
+ }
+ }
+ else
+ {
+ buffer = pool->aArray[foundIndex].buffer;
+
+ if (maxSize < size)
+ {
+ void* newBuffer = NULL;
+ if (pool->alignment)
+ newBuffer = winpr_aligned_realloc(buffer, size, pool->alignment);
+ else
+ newBuffer = realloc(buffer, size);
+
+ if (!newBuffer)
+ goto out_error_no_free;
+
+ buffer = newBuffer;
+ }
+
+ if (!BufferPool_ShiftAvailable(pool, foundIndex, -1))
+ goto out_error;
+ }
+
+ if (!buffer)
+ goto out_error;
+
+ if (pool->uSize + 1 > pool->uCapacity)
+ {
+ size_t newUCapacity = pool->uCapacity * 2;
+ wBufferPoolItem* newUArray =
+ (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
+ if (!newUArray)
+ goto out_error;
+
+ pool->uCapacity = newUCapacity;
+ pool->uArray = newUArray;
+ }
+
+ pool->uArray[pool->uSize].buffer = buffer;
+ pool->uArray[pool->uSize].size = size;
+ (pool->uSize)++;
+ }
+
+ BufferPool_Unlock(pool);
+
+ return buffer;
+
+out_error:
+ if (pool->alignment)
+ winpr_aligned_free(buffer);
+ else
+ free(buffer);
+out_error_no_free:
+ BufferPool_Unlock(pool);
+ return NULL;
+}
+
+/**
+ * Returns a buffer to the pool.
+ */
+
+BOOL BufferPool_Return(wBufferPool* pool, void* buffer)
+{
+ BOOL rc = FALSE;
+ SSIZE_T size = 0;
+ BOOL found = FALSE;
+
+ BufferPool_Lock(pool);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+
+ if ((pool->size + 1) >= pool->capacity)
+ {
+ SSIZE_T newCapacity = pool->capacity * 2;
+ void** newArray = (void**)realloc(pool->array, sizeof(void*) * newCapacity);
+ if (!newArray)
+ goto out_error;
+
+ pool->capacity = newCapacity;
+ pool->array = newArray;
+ }
+
+ pool->array[(pool->size)++] = buffer;
+ }
+ else
+ {
+ /* variable size buffers */
+
+ SSIZE_T index = 0;
+ for (; index < pool->uSize; index++)
+ {
+ if (pool->uArray[index].buffer == buffer)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ size = pool->uArray[index].size;
+ if (!BufferPool_ShiftUsed(pool, index, -1))
+ goto out_error;
+ }
+
+ if (size)
+ {
+ if ((pool->aSize + 1) >= pool->aCapacity)
+ {
+ SSIZE_T newCapacity = pool->aCapacity * 2;
+ wBufferPoolItem* newArray =
+ (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity);
+ if (!newArray)
+ goto out_error;
+
+ pool->aCapacity = newCapacity;
+ pool->aArray = newArray;
+ }
+
+ pool->aArray[pool->aSize].buffer = buffer;
+ pool->aArray[pool->aSize].size = size;
+ (pool->aSize)++;
+ }
+ }
+
+ rc = TRUE;
+out_error:
+ BufferPool_Unlock(pool);
+ return rc;
+}
+
+/**
+ * Releases the buffers currently cached in the pool.
+ */
+
+void BufferPool_Clear(wBufferPool* pool)
+{
+ BufferPool_Lock(pool);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+
+ while (pool->size > 0)
+ {
+ (pool->size)--;
+
+ if (pool->alignment)
+ winpr_aligned_free(pool->array[pool->size]);
+ else
+ free(pool->array[pool->size]);
+ }
+ }
+ else
+ {
+ /* variable size buffers */
+
+ while (pool->aSize > 0)
+ {
+ (pool->aSize)--;
+
+ if (pool->alignment)
+ winpr_aligned_free(pool->aArray[pool->aSize].buffer);
+ else
+ free(pool->aArray[pool->aSize].buffer);
+ }
+
+ while (pool->uSize > 0)
+ {
+ (pool->uSize)--;
+
+ if (pool->alignment)
+ winpr_aligned_free(pool->uArray[pool->uSize].buffer);
+ else
+ free(pool->uArray[pool->uSize].buffer);
+ }
+ }
+
+ BufferPool_Unlock(pool);
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wBufferPool* BufferPool_New(BOOL synchronized, SSIZE_T fixedSize, DWORD alignment)
+{
+ wBufferPool* pool = NULL;
+
+ pool = (wBufferPool*)calloc(1, sizeof(wBufferPool));
+
+ if (pool)
+ {
+ pool->fixedSize = fixedSize;
+
+ if (pool->fixedSize < 0)
+ pool->fixedSize = 0;
+
+ pool->alignment = alignment;
+ pool->synchronized = synchronized;
+
+ if (pool->synchronized)
+ InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+
+ pool->size = 0;
+ pool->capacity = 32;
+ pool->array = (void**)calloc(pool->capacity, sizeof(void*));
+ if (!pool->array)
+ goto out_error;
+ }
+ else
+ {
+ /* variable size buffers */
+
+ pool->aSize = 0;
+ pool->aCapacity = 32;
+ pool->aArray = (wBufferPoolItem*)calloc(pool->aCapacity, sizeof(wBufferPoolItem));
+ if (!pool->aArray)
+ goto out_error;
+
+ pool->uSize = 0;
+ pool->uCapacity = 32;
+ pool->uArray = (wBufferPoolItem*)calloc(pool->uCapacity, sizeof(wBufferPoolItem));
+ if (!pool->uArray)
+ goto out_error;
+ }
+ }
+
+ return pool;
+
+out_error:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ BufferPool_Free(pool);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void BufferPool_Free(wBufferPool* pool)
+{
+ if (pool)
+ {
+ BufferPool_Clear(pool);
+
+ if (pool->synchronized)
+ DeleteCriticalSection(&pool->lock);
+
+ if (pool->fixedSize)
+ {
+ /* fixed size buffers */
+
+ free(pool->array);
+ }
+ else
+ {
+ /* variable size buffers */
+
+ free(pool->aArray);
+ free(pool->uArray);
+ }
+
+ free(pool);
+ }
+}
diff --git a/winpr/libwinpr/utils/collections/CountdownEvent.c b/winpr/libwinpr/utils/collections/CountdownEvent.c
new file mode 100644
index 0000000..fd23e0c
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/CountdownEvent.c
@@ -0,0 +1,203 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Countdown Event
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+
+#include <winpr/crt.h>
+
+#include <winpr/collections.h>
+
+struct CountdownEvent
+{
+ size_t count;
+ CRITICAL_SECTION lock;
+ HANDLE event;
+ size_t initialCount;
+};
+
+/**
+ * C equivalent of the C# CountdownEvent Class
+ * http://msdn.microsoft.com/en-us/library/dd235708/
+ */
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets the number of remaining signals required to set the event.
+ */
+
+size_t CountdownEvent_CurrentCount(wCountdownEvent* countdown)
+{
+ WINPR_ASSERT(countdown);
+ return countdown->count;
+}
+
+/**
+ * Gets the numbers of signals initially required to set the event.
+ */
+
+size_t CountdownEvent_InitialCount(wCountdownEvent* countdown)
+{
+ WINPR_ASSERT(countdown);
+ return countdown->initialCount;
+}
+
+/**
+ * Determines whether the event is set.
+ */
+
+BOOL CountdownEvent_IsSet(wCountdownEvent* countdown)
+{
+ BOOL status = FALSE;
+
+ WINPR_ASSERT(countdown);
+ if (WaitForSingleObject(countdown->event, 0) == WAIT_OBJECT_0)
+ status = TRUE;
+
+ return status;
+}
+
+/**
+ * Gets a WaitHandle that is used to wait for the event to be set.
+ */
+
+HANDLE CountdownEvent_WaitHandle(wCountdownEvent* countdown)
+{
+ WINPR_ASSERT(countdown);
+ return countdown->event;
+}
+
+/**
+ * Methods
+ */
+
+/**
+ * Increments the CountdownEvent's current count by a specified value.
+ */
+
+void CountdownEvent_AddCount(wCountdownEvent* countdown, size_t signalCount)
+{
+ WINPR_ASSERT(countdown);
+ EnterCriticalSection(&countdown->lock);
+
+ countdown->count += signalCount;
+
+ if (countdown->count > 0)
+ ResetEvent(countdown->event);
+
+ LeaveCriticalSection(&countdown->lock);
+}
+
+/**
+ * Registers multiple signals with the CountdownEvent, decrementing the value of CurrentCount by the
+ * specified amount.
+ */
+
+BOOL CountdownEvent_Signal(wCountdownEvent* countdown, size_t signalCount)
+{
+ BOOL status = FALSE;
+ BOOL newStatus = FALSE;
+ BOOL oldStatus = FALSE;
+
+ WINPR_ASSERT(countdown);
+
+ EnterCriticalSection(&countdown->lock);
+
+ if (WaitForSingleObject(countdown->event, 0) == WAIT_OBJECT_0)
+ oldStatus = TRUE;
+
+ if (signalCount <= countdown->count)
+ countdown->count -= signalCount;
+ else
+ countdown->count = 0;
+
+ if (countdown->count == 0)
+ newStatus = TRUE;
+
+ if (newStatus && (!oldStatus))
+ {
+ SetEvent(countdown->event);
+ status = TRUE;
+ }
+
+ LeaveCriticalSection(&countdown->lock);
+
+ return status;
+}
+
+/**
+ * Resets the InitialCount property to a specified value.
+ */
+
+void CountdownEvent_Reset(wCountdownEvent* countdown, size_t count)
+{
+ WINPR_ASSERT(countdown);
+ countdown->initialCount = count;
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wCountdownEvent* CountdownEvent_New(size_t initialCount)
+{
+ wCountdownEvent* countdown = (wCountdownEvent*)calloc(1, sizeof(wCountdownEvent));
+
+ if (!countdown)
+ return NULL;
+
+ countdown->count = initialCount;
+ countdown->initialCount = initialCount;
+
+ if (!InitializeCriticalSectionAndSpinCount(&countdown->lock, 4000))
+ goto fail;
+
+ countdown->event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!countdown->event)
+ goto fail;
+
+ if (countdown->count == 0)
+ {
+ if (!SetEvent(countdown->event))
+ goto fail;
+ }
+
+ return countdown;
+
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ CountdownEvent_Free(countdown);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void CountdownEvent_Free(wCountdownEvent* countdown)
+{
+ if (!countdown)
+ return;
+
+ DeleteCriticalSection(&countdown->lock);
+ CloseHandle(countdown->event);
+
+ free(countdown);
+}
diff --git a/winpr/libwinpr/utils/collections/HashTable.c b/winpr/libwinpr/utils/collections/HashTable.c
new file mode 100644
index 0000000..7782b2b
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/HashTable.c
@@ -0,0 +1,870 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System.Collections.Hashtable
+ *
+ * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+
+#include <winpr/collections.h>
+
+/**
+ * This implementation is based on the public domain
+ * hash table implementation made by Keith Pomakis:
+ *
+ * http://www.pomakis.com/hashtable/hashtable.c
+ * http://www.pomakis.com/hashtable/hashtable.h
+ */
+
+typedef struct s_wKeyValuePair wKeyValuePair;
+
+struct s_wKeyValuePair
+{
+ void* key;
+ void* value;
+
+ wKeyValuePair* next;
+ BOOL markedForRemove;
+};
+
+struct s_wHashTable
+{
+ BOOL synchronized;
+ CRITICAL_SECTION lock;
+
+ size_t numOfBuckets;
+ size_t numOfElements;
+ float idealRatio;
+ float lowerRehashThreshold;
+ float upperRehashThreshold;
+ wKeyValuePair** bucketArray;
+
+ HASH_TABLE_HASH_FN hash;
+ wObject key;
+ wObject value;
+
+ DWORD foreachRecursionLevel;
+ DWORD pendingRemoves;
+};
+
+BOOL HashTable_PointerCompare(const void* pointer1, const void* pointer2)
+{
+ return (pointer1 == pointer2);
+}
+
+UINT32 HashTable_PointerHash(const void* pointer)
+{
+ return ((UINT32)(UINT_PTR)pointer) >> 4;
+}
+
+BOOL HashTable_StringCompare(const void* string1, const void* string2)
+{
+ if (!string1 || !string2)
+ return (string1 == string2);
+
+ return (strcmp((const char*)string1, (const char*)string2) == 0);
+}
+
+UINT32 HashTable_StringHash(const void* key)
+{
+ UINT32 c = 0;
+ UINT32 hash = 5381;
+ const BYTE* str = (const BYTE*)key;
+
+ /* djb2 algorithm */
+ while ((c = *str++) != '\0')
+ hash = (hash * 33) + c;
+
+ return hash;
+}
+
+void* HashTable_StringClone(const void* str)
+{
+ return winpr_ObjectStringClone(str);
+}
+
+void HashTable_StringFree(void* str)
+{
+ winpr_ObjectStringFree(str);
+}
+
+static INLINE BOOL HashTable_IsProbablePrime(size_t oddNumber)
+{
+ for (size_t i = 3; i < 51; i += 2)
+ {
+ if (oddNumber == i)
+ return TRUE;
+ else if (oddNumber % i == 0)
+ return FALSE;
+ }
+
+ return TRUE; /* maybe */
+}
+
+static INLINE size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
+{
+ WINPR_ASSERT(table);
+
+ const float tmp = (table->numOfElements / table->idealRatio);
+ size_t idealNumOfBuckets = (size_t)tmp;
+
+ if (idealNumOfBuckets < 5)
+ idealNumOfBuckets = 5;
+ else
+ idealNumOfBuckets |= 0x01;
+
+ while (!HashTable_IsProbablePrime(idealNumOfBuckets))
+ idealNumOfBuckets += 2;
+
+ return idealNumOfBuckets;
+}
+
+static INLINE void HashTable_Rehash(wHashTable* table, size_t numOfBuckets)
+{
+ UINT32 hashValue = 0;
+ wKeyValuePair* nextPair = NULL;
+ wKeyValuePair** newBucketArray = NULL;
+
+ WINPR_ASSERT(table);
+ if (numOfBuckets == 0)
+ numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table);
+
+ if (numOfBuckets == table->numOfBuckets)
+ return; /* already the right size! */
+
+ newBucketArray = (wKeyValuePair**)calloc(numOfBuckets, sizeof(wKeyValuePair*));
+
+ if (!newBucketArray)
+ {
+ /*
+ * Couldn't allocate memory for the new array.
+ * This isn't a fatal error; we just can't perform the rehash.
+ */
+ return;
+ }
+
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ wKeyValuePair* pair = table->bucketArray[index];
+
+ while (pair)
+ {
+ nextPair = pair->next;
+ hashValue = table->hash(pair->key) % numOfBuckets;
+ pair->next = newBucketArray[hashValue];
+ newBucketArray[hashValue] = pair;
+ pair = nextPair;
+ }
+ }
+
+ free(table->bucketArray);
+ table->bucketArray = newBucketArray;
+ table->numOfBuckets = numOfBuckets;
+}
+
+static INLINE BOOL HashTable_Equals(wHashTable* table, const wKeyValuePair* pair, const void* key)
+{
+ WINPR_ASSERT(table);
+ WINPR_ASSERT(pair);
+ WINPR_ASSERT(key);
+ return table->key.fnObjectEquals(key, pair->key);
+}
+
+static INLINE wKeyValuePair* HashTable_Get(wHashTable* table, const void* key)
+{
+ UINT32 hashValue = 0;
+ wKeyValuePair* pair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key)
+ return NULL;
+
+ hashValue = table->hash(key) % table->numOfBuckets;
+ pair = table->bucketArray[hashValue];
+
+ while (pair && !HashTable_Equals(table, pair, key))
+ pair = pair->next;
+
+ return pair;
+}
+
+static INLINE void disposeKey(wHashTable* table, void* key)
+{
+ WINPR_ASSERT(table);
+ if (table->key.fnObjectFree)
+ table->key.fnObjectFree(key);
+}
+
+static INLINE void disposeValue(wHashTable* table, void* value)
+{
+ WINPR_ASSERT(table);
+ if (table->value.fnObjectFree)
+ table->value.fnObjectFree(value);
+}
+
+static INLINE void disposePair(wHashTable* table, wKeyValuePair* pair)
+{
+ WINPR_ASSERT(table);
+ if (!pair)
+ return;
+ disposeKey(table, pair->key);
+ disposeValue(table, pair->value);
+ free(pair);
+}
+
+static INLINE void setKey(wHashTable* table, wKeyValuePair* pair, const void* key)
+{
+ WINPR_ASSERT(table);
+ if (!pair)
+ return;
+ disposeKey(table, pair->key);
+ if (table->key.fnObjectNew)
+ pair->key = table->key.fnObjectNew(key);
+ else
+ {
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ cnv.cpv = key;
+ pair->key = cnv.pv;
+ }
+}
+
+static INLINE void setValue(wHashTable* table, wKeyValuePair* pair, const void* value)
+{
+ WINPR_ASSERT(table);
+ if (!pair)
+ return;
+ disposeValue(table, pair->value);
+ if (table->value.fnObjectNew)
+ pair->value = table->value.fnObjectNew(value);
+ else
+ {
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ cnv.cpv = value;
+ pair->value = cnv.pv;
+ }
+}
+
+/**
+ * C equivalent of the C# Hashtable Class:
+ * http://msdn.microsoft.com/en-us/library/system.collections.hashtable.aspx
+ */
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets the number of key/value pairs contained in the HashTable.
+ */
+
+size_t HashTable_Count(wHashTable* table)
+{
+ WINPR_ASSERT(table);
+ return table->numOfElements;
+}
+
+/**
+ * Methods
+ */
+
+/**
+ * Adds an element with the specified key and value into the HashTable.
+ */
+#if defined(WITH_WINPR_DEPRECATED)
+int HashTable_Add(wHashTable* table, const void* key, const void* value)
+{
+ if (!HashTable_Insert(table, key, value))
+ return -1;
+ return 0;
+}
+#endif
+
+BOOL HashTable_Insert(wHashTable* table, const void* key, const void* value)
+{
+ BOOL rc = FALSE;
+ UINT32 hashValue = 0;
+ wKeyValuePair* pair = NULL;
+ wKeyValuePair* newPair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key || !value)
+ return FALSE;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ hashValue = table->hash(key) % table->numOfBuckets;
+ pair = table->bucketArray[hashValue];
+
+ while (pair && !HashTable_Equals(table, pair, key))
+ pair = pair->next;
+
+ if (pair)
+ {
+ if (pair->markedForRemove)
+ {
+ /* this entry was set to be removed but will be recycled instead */
+ table->pendingRemoves--;
+ pair->markedForRemove = FALSE;
+ table->numOfElements++;
+ }
+
+ if (pair->key != key)
+ {
+ setKey(table, pair, key);
+ }
+
+ if (pair->value != value)
+ {
+ setValue(table, pair, value);
+ }
+ rc = TRUE;
+ }
+ else
+ {
+ newPair = (wKeyValuePair*)calloc(1, sizeof(wKeyValuePair));
+
+ if (newPair)
+ {
+ setKey(table, newPair, key);
+ setValue(table, newPair, value);
+ newPair->next = table->bucketArray[hashValue];
+ newPair->markedForRemove = FALSE;
+ table->bucketArray[hashValue] = newPair;
+ table->numOfElements++;
+
+ if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio)
+ {
+ float elementToBucketRatio =
+ (float)table->numOfElements / (float)table->numOfBuckets;
+
+ if (elementToBucketRatio > table->upperRehashThreshold)
+ HashTable_Rehash(table, 0);
+ }
+ rc = TRUE;
+ }
+ }
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return rc;
+}
+
+/**
+ * Removes the element with the specified key from the HashTable.
+ */
+
+BOOL HashTable_Remove(wHashTable* table, const void* key)
+{
+ UINT32 hashValue = 0;
+ BOOL status = TRUE;
+ wKeyValuePair* pair = NULL;
+ wKeyValuePair* previousPair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key)
+ return FALSE;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ hashValue = table->hash(key) % table->numOfBuckets;
+ pair = table->bucketArray[hashValue];
+
+ while (pair && !HashTable_Equals(table, pair, key))
+ {
+ previousPair = pair;
+ pair = pair->next;
+ }
+
+ if (!pair)
+ {
+ status = FALSE;
+ goto out;
+ }
+
+ if (table->foreachRecursionLevel)
+ {
+ /* if we are running a HashTable_Foreach, just mark the entry for removal */
+ pair->markedForRemove = TRUE;
+ table->pendingRemoves++;
+ table->numOfElements--;
+ goto out;
+ }
+
+ if (previousPair)
+ previousPair->next = pair->next;
+ else
+ table->bucketArray[hashValue] = pair->next;
+
+ disposePair(table, pair);
+ table->numOfElements--;
+
+ if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f)
+ {
+ float elementToBucketRatio = (float)table->numOfElements / (float)table->numOfBuckets;
+
+ if (elementToBucketRatio < table->lowerRehashThreshold)
+ HashTable_Rehash(table, 0);
+ }
+
+out:
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return status;
+}
+
+/**
+ * Get an item value using key
+ */
+
+void* HashTable_GetItemValue(wHashTable* table, const void* key)
+{
+ void* value = NULL;
+ wKeyValuePair* pair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key)
+ return NULL;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ pair = HashTable_Get(table, key);
+
+ if (pair && !pair->markedForRemove)
+ value = pair->value;
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return value;
+}
+
+/**
+ * Set an item value using key
+ */
+
+BOOL HashTable_SetItemValue(wHashTable* table, const void* key, const void* value)
+{
+ BOOL status = TRUE;
+ wKeyValuePair* pair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key)
+ return FALSE;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ pair = HashTable_Get(table, key);
+
+ if (!pair || pair->markedForRemove)
+ status = FALSE;
+ else
+ {
+ setValue(table, pair, value);
+ }
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return status;
+}
+
+/**
+ * Removes all elements from the HashTable.
+ */
+
+void HashTable_Clear(wHashTable* table)
+{
+ wKeyValuePair* nextPair = NULL;
+
+ WINPR_ASSERT(table);
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ wKeyValuePair* pair = table->bucketArray[index];
+
+ while (pair)
+ {
+ nextPair = pair->next;
+
+ if (table->foreachRecursionLevel)
+ {
+ /* if we're in a foreach we just mark the entry for removal */
+ pair->markedForRemove = TRUE;
+ table->pendingRemoves++;
+ }
+ else
+ {
+ disposePair(table, pair);
+ pair = nextPair;
+ }
+ }
+
+ table->bucketArray[index] = NULL;
+ }
+
+ table->numOfElements = 0;
+ if (table->foreachRecursionLevel == 0)
+ HashTable_Rehash(table, 5);
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+}
+
+/**
+ * Gets the list of keys as an array
+ */
+
+size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
+{
+ size_t iKey = 0;
+ size_t count = 0;
+ ULONG_PTR* pKeys = NULL;
+ wKeyValuePair* nextPair = NULL;
+
+ WINPR_ASSERT(table);
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ iKey = 0;
+ count = table->numOfElements;
+ *ppKeys = NULL;
+
+ if (count < 1)
+ {
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return 0;
+ }
+
+ pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR));
+
+ if (!pKeys)
+ {
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return 0;
+ }
+
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ wKeyValuePair* pair = table->bucketArray[index];
+
+ while (pair)
+ {
+ nextPair = pair->next;
+ if (!pair->markedForRemove)
+ pKeys[iKey++] = (ULONG_PTR)pair->key;
+ pair = nextPair;
+ }
+ }
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ if (ppKeys)
+ *ppKeys = pKeys;
+ else
+ free(pKeys);
+ return count;
+}
+
+BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg)
+{
+ BOOL ret = TRUE;
+
+ WINPR_ASSERT(table);
+ WINPR_ASSERT(fn);
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ table->foreachRecursionLevel++;
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next)
+ {
+ if (!pair->markedForRemove && !fn(pair->key, pair->value, arg))
+ {
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+ table->foreachRecursionLevel--;
+
+ if (!table->foreachRecursionLevel && table->pendingRemoves)
+ {
+ /* if we're the last recursive foreach call, let's do the cleanup if needed */
+ wKeyValuePair** prevPtr = NULL;
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ wKeyValuePair* nextPair = NULL;
+ prevPtr = &table->bucketArray[index];
+ for (wKeyValuePair* pair = table->bucketArray[index]; pair;)
+ {
+ nextPair = pair->next;
+
+ if (pair->markedForRemove)
+ {
+ disposePair(table, pair);
+ *prevPtr = nextPair;
+ }
+ else
+ {
+ prevPtr = &pair->next;
+ }
+ pair = nextPair;
+ }
+ }
+ table->pendingRemoves = 0;
+ }
+
+out:
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+ return ret;
+}
+
+/**
+ * Determines whether the HashTable contains a specific key.
+ */
+
+BOOL HashTable_Contains(wHashTable* table, const void* key)
+{
+ BOOL status = 0;
+ wKeyValuePair* pair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key)
+ return FALSE;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ pair = HashTable_Get(table, key);
+ status = (pair && !pair->markedForRemove);
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return status;
+}
+
+/**
+ * Determines whether the HashTable contains a specific key.
+ */
+
+BOOL HashTable_ContainsKey(wHashTable* table, const void* key)
+{
+ BOOL status = 0;
+ wKeyValuePair* pair = NULL;
+
+ WINPR_ASSERT(table);
+ if (!key)
+ return FALSE;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ pair = HashTable_Get(table, key);
+ status = (pair && !pair->markedForRemove);
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return status;
+}
+
+/**
+ * Determines whether the HashTable contains a specific value.
+ */
+
+BOOL HashTable_ContainsValue(wHashTable* table, const void* value)
+{
+ BOOL status = FALSE;
+
+ WINPR_ASSERT(table);
+ if (!value)
+ return FALSE;
+
+ if (table->synchronized)
+ EnterCriticalSection(&table->lock);
+
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ wKeyValuePair* pair = table->bucketArray[index];
+
+ while (pair)
+ {
+ if (!pair->markedForRemove && HashTable_Equals(table, pair, value))
+ {
+ status = TRUE;
+ break;
+ }
+
+ pair = pair->next;
+ }
+
+ if (status)
+ break;
+ }
+
+ if (table->synchronized)
+ LeaveCriticalSection(&table->lock);
+
+ return status;
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wHashTable* HashTable_New(BOOL synchronized)
+{
+ wHashTable* table = (wHashTable*)calloc(1, sizeof(wHashTable));
+
+ if (!table)
+ goto fail;
+
+ table->synchronized = synchronized;
+ InitializeCriticalSectionAndSpinCount(&(table->lock), 4000);
+ table->numOfBuckets = 64;
+ table->numOfElements = 0;
+ table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets, sizeof(wKeyValuePair*));
+
+ if (!table->bucketArray)
+ goto fail;
+
+ table->idealRatio = 3.0;
+ table->lowerRehashThreshold = 0.0;
+ table->upperRehashThreshold = 15.0;
+ table->hash = HashTable_PointerHash;
+ table->key.fnObjectEquals = HashTable_PointerCompare;
+ table->value.fnObjectEquals = HashTable_PointerCompare;
+
+ return table;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ HashTable_Free(table);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void HashTable_Free(wHashTable* table)
+{
+ wKeyValuePair* pair = NULL;
+ wKeyValuePair* nextPair = NULL;
+
+ if (!table)
+ return;
+
+ if (table->bucketArray)
+ {
+ for (size_t index = 0; index < table->numOfBuckets; index++)
+ {
+ pair = table->bucketArray[index];
+
+ while (pair)
+ {
+ nextPair = pair->next;
+
+ disposePair(table, pair);
+ pair = nextPair;
+ }
+ }
+ free(table->bucketArray);
+ }
+ DeleteCriticalSection(&(table->lock));
+
+ free(table);
+}
+
+void HashTable_Lock(wHashTable* table)
+{
+ WINPR_ASSERT(table);
+ EnterCriticalSection(&table->lock);
+}
+
+void HashTable_Unlock(wHashTable* table)
+{
+ WINPR_ASSERT(table);
+ LeaveCriticalSection(&table->lock);
+}
+
+wObject* HashTable_KeyObject(wHashTable* table)
+{
+ WINPR_ASSERT(table);
+ return &table->key;
+}
+
+wObject* HashTable_ValueObject(wHashTable* table)
+{
+ WINPR_ASSERT(table);
+ return &table->value;
+}
+
+BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn)
+{
+ WINPR_ASSERT(table);
+ table->hash = fn;
+ return fn != NULL;
+}
+
+BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
+{
+ wObject* obj = NULL;
+
+ if (!HashTable_SetHashFunction(table, HashTable_StringHash))
+ return FALSE;
+
+ obj = HashTable_KeyObject(table);
+ obj->fnObjectEquals = HashTable_StringCompare;
+ obj->fnObjectNew = HashTable_StringClone;
+ obj->fnObjectFree = HashTable_StringFree;
+
+ if (stringValues)
+ {
+ obj = HashTable_ValueObject(table);
+ obj->fnObjectEquals = HashTable_StringCompare;
+ obj->fnObjectNew = HashTable_StringClone;
+ obj->fnObjectFree = HashTable_StringFree;
+ }
+ return TRUE;
+}
diff --git a/winpr/libwinpr/utils/collections/LinkedList.c b/winpr/libwinpr/utils/collections/LinkedList.c
new file mode 100644
index 0000000..48d64d9
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/LinkedList.c
@@ -0,0 +1,385 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System.Collections.Generic.LinkedList<T>
+ *
+ * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/collections.h>
+#include <winpr/assert.h>
+
+typedef struct s_wLinkedListItem wLinkedListNode;
+
+struct s_wLinkedListItem
+{
+ void* value;
+ wLinkedListNode* prev;
+ wLinkedListNode* next;
+};
+
+struct s_wLinkedList
+{
+ size_t count;
+ int initial;
+ wLinkedListNode* head;
+ wLinkedListNode* tail;
+ wLinkedListNode* current;
+ wObject object;
+};
+
+/**
+ * C equivalent of the C# LinkedList<T> Class:
+ * http://msdn.microsoft.com/en-us/library/he2s3bh7.aspx
+ *
+ * Internal implementation uses a doubly-linked list
+ */
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets the number of nodes actually contained in the LinkedList.
+ */
+
+size_t LinkedList_Count(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ return list->count;
+}
+
+/**
+ * Gets the first node of the LinkedList.
+ */
+
+void* LinkedList_First(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ if (list->head)
+ return list->head->value;
+ else
+ return NULL;
+}
+
+/**
+ * Gets the last node of the LinkedList.
+ */
+
+void* LinkedList_Last(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ if (list->tail)
+ return list->tail->value;
+ else
+ return NULL;
+}
+
+/**
+ * Methods
+ */
+
+/**
+ * Determines whether the LinkedList contains a specific value.
+ */
+
+BOOL LinkedList_Contains(wLinkedList* list, const void* value)
+{
+ wLinkedListNode* item = NULL;
+ OBJECT_EQUALS_FN keyEquals = NULL;
+
+ WINPR_ASSERT(list);
+ if (!list->head)
+ return FALSE;
+
+ item = list->head;
+ keyEquals = list->object.fnObjectEquals;
+
+ while (item)
+ {
+ if (keyEquals(item->value, value))
+ break;
+
+ item = item->next;
+ }
+
+ return (item) ? TRUE : FALSE;
+}
+
+static wLinkedListNode* LinkedList_FreeNode(wLinkedList* list, wLinkedListNode* node)
+{
+ wLinkedListNode* next = NULL;
+ wLinkedListNode* prev = NULL;
+
+ WINPR_ASSERT(list);
+ WINPR_ASSERT(node);
+
+ next = node->next;
+ prev = node->prev;
+ if (prev)
+ prev->next = next;
+
+ if (next)
+ next->prev = prev;
+
+ if (node == list->head)
+ list->head = node->next;
+
+ if (node == list->tail)
+ list->tail = node->prev;
+
+ if (list->object.fnObjectUninit)
+ list->object.fnObjectUninit(node);
+
+ if (list->object.fnObjectFree)
+ list->object.fnObjectFree(node);
+
+ free(node);
+ list->count--;
+ return next;
+}
+
+/**
+ * Removes all entries from the LinkedList.
+ */
+
+void LinkedList_Clear(wLinkedList* list)
+{
+ wLinkedListNode* node = NULL;
+ WINPR_ASSERT(list);
+ if (!list->head)
+ return;
+
+ node = list->head;
+
+ while (node)
+ node = LinkedList_FreeNode(list, node);
+
+ list->head = list->tail = NULL;
+ list->count = 0;
+}
+
+static wLinkedListNode* LinkedList_Create(wLinkedList* list, const void* value)
+{
+ wLinkedListNode* node = NULL;
+
+ WINPR_ASSERT(list);
+ node = (wLinkedListNode*)calloc(1, sizeof(wLinkedListNode));
+
+ if (!node)
+ return NULL;
+
+ if (list->object.fnObjectNew)
+ node->value = list->object.fnObjectNew(value);
+ else
+ {
+ union
+ {
+ const void* cpv;
+ void* pv;
+ } cnv;
+ cnv.cpv = value;
+ node->value = cnv.pv;
+ }
+
+ if (list->object.fnObjectInit)
+ list->object.fnObjectInit(node);
+
+ return node;
+}
+/**
+ * Adds a new node containing the specified value at the start of the LinkedList.
+ */
+
+BOOL LinkedList_AddFirst(wLinkedList* list, const void* value)
+{
+ wLinkedListNode* node = LinkedList_Create(list, value);
+
+ if (!node)
+ return FALSE;
+
+ if (!list->head)
+ {
+ list->tail = list->head = node;
+ }
+ else
+ {
+ list->head->prev = node;
+ node->next = list->head;
+ list->head = node;
+ }
+
+ list->count++;
+ return TRUE;
+}
+
+/**
+ * Adds a new node containing the specified value at the end of the LinkedList.
+ */
+
+BOOL LinkedList_AddLast(wLinkedList* list, const void* value)
+{
+ wLinkedListNode* node = LinkedList_Create(list, value);
+
+ if (!node)
+ return FALSE;
+
+ if (!list->tail)
+ {
+ list->head = list->tail = node;
+ }
+ else
+ {
+ list->tail->next = node;
+ node->prev = list->tail;
+ list->tail = node;
+ }
+
+ list->count++;
+ return TRUE;
+}
+
+/**
+ * Removes the first occurrence of the specified value from the LinkedList.
+ */
+
+BOOL LinkedList_Remove(wLinkedList* list, const void* value)
+{
+ wLinkedListNode* node = NULL;
+ OBJECT_EQUALS_FN keyEquals = NULL;
+ WINPR_ASSERT(list);
+
+ keyEquals = list->object.fnObjectEquals;
+ node = list->head;
+
+ while (node)
+ {
+ if (keyEquals(node->value, value))
+ {
+ LinkedList_FreeNode(list, node);
+ return TRUE;
+ }
+
+ node = node->next;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Removes the node at the start of the LinkedList.
+ */
+
+void LinkedList_RemoveFirst(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ if (list->head)
+ LinkedList_FreeNode(list, list->head);
+}
+
+/**
+ * Removes the node at the end of the LinkedList.
+ */
+
+void LinkedList_RemoveLast(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ if (list->tail)
+ LinkedList_FreeNode(list, list->tail);
+}
+
+/**
+ * Sets the enumerator to its initial position, which is before the first element in the collection.
+ */
+
+void LinkedList_Enumerator_Reset(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ list->initial = 1;
+ list->current = list->head;
+}
+
+/*
+ * Gets the element at the current position of the enumerator.
+ */
+
+void* LinkedList_Enumerator_Current(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ if (list->initial)
+ return NULL;
+
+ if (list->current)
+ return list->current->value;
+ else
+ return NULL;
+}
+
+/*
+ * Advances the enumerator to the next element of the LinkedList.
+ */
+
+BOOL LinkedList_Enumerator_MoveNext(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+ if (list->initial)
+ list->initial = 0;
+ else if (list->current)
+ list->current = list->current->next;
+
+ if (!list->current)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL default_equal_function(const void* objA, const void* objB)
+{
+ return objA == objB;
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wLinkedList* LinkedList_New(void)
+{
+ wLinkedList* list = NULL;
+ list = (wLinkedList*)calloc(1, sizeof(wLinkedList));
+
+ if (list)
+ {
+ list->object.fnObjectEquals = default_equal_function;
+ }
+
+ return list;
+}
+
+void LinkedList_Free(wLinkedList* list)
+{
+ if (list)
+ {
+ LinkedList_Clear(list);
+ free(list);
+ }
+}
+
+wObject* LinkedList_Object(wLinkedList* list)
+{
+ WINPR_ASSERT(list);
+
+ return &list->object;
+}
diff --git a/winpr/libwinpr/utils/collections/ListDictionary.c b/winpr/libwinpr/utils/collections/ListDictionary.c
new file mode 100644
index 0000000..cf238a9
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/ListDictionary.c
@@ -0,0 +1,562 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System.Collections.Specialized.ListDictionary
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+#include <winpr/assert.h>
+#include <winpr/crt.h>
+
+#include <winpr/collections.h>
+
+typedef struct s_wListDictionaryItem wListDictionaryItem;
+
+struct s_wListDictionaryItem
+{
+ void* key;
+ void* value;
+
+ wListDictionaryItem* next;
+};
+
+struct s_wListDictionary
+{
+ BOOL synchronized;
+ CRITICAL_SECTION lock;
+
+ wListDictionaryItem* head;
+ wObject objectKey;
+ wObject objectValue;
+};
+
+/**
+ * C equivalent of the C# ListDictionary Class:
+ * http://msdn.microsoft.com/en-us/library/system.collections.specialized.listdictionary.aspx
+ *
+ * Internal implementation uses a singly-linked list
+ */
+
+WINPR_API wObject* ListDictionary_KeyObject(wListDictionary* _dictionary)
+{
+ WINPR_ASSERT(_dictionary);
+ return &_dictionary->objectKey;
+}
+
+WINPR_API wObject* ListDictionary_ValueObject(wListDictionary* _dictionary)
+{
+ WINPR_ASSERT(_dictionary);
+ return &_dictionary->objectValue;
+}
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets the number of key/value pairs contained in the ListDictionary.
+ */
+
+size_t ListDictionary_Count(wListDictionary* listDictionary)
+{
+ size_t count = 0;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ if (listDictionary->head)
+ {
+ wListDictionaryItem* item = listDictionary->head;
+
+ while (item)
+ {
+ count++;
+ item = item->next;
+ }
+ }
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return count;
+}
+
+/**
+ * Lock access to the ListDictionary
+ */
+
+void ListDictionary_Lock(wListDictionary* listDictionary)
+{
+ WINPR_ASSERT(listDictionary);
+
+ EnterCriticalSection(&listDictionary->lock);
+}
+
+/**
+ * Unlock access to the ListDictionary
+ */
+
+void ListDictionary_Unlock(wListDictionary* listDictionary)
+{
+ WINPR_ASSERT(listDictionary);
+
+ LeaveCriticalSection(&listDictionary->lock);
+}
+
+/**
+ * Methods
+ */
+
+/**
+ * Gets the list of keys as an array
+ */
+
+size_t ListDictionary_GetKeys(wListDictionary* listDictionary, ULONG_PTR** ppKeys)
+{
+ ULONG_PTR* pKeys = NULL;
+
+ WINPR_ASSERT(listDictionary);
+ if (!ppKeys)
+ return 0;
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ size_t count = 0;
+
+ if (listDictionary->head)
+ {
+ wListDictionaryItem* item = listDictionary->head;
+
+ while (item)
+ {
+ count++;
+ item = item->next;
+ }
+ }
+
+ if (count > 0)
+ {
+ pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR));
+
+ if (!pKeys)
+ {
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return -1;
+ }
+ }
+
+ size_t index = 0;
+
+ if (listDictionary->head)
+ {
+ wListDictionaryItem* item = listDictionary->head;
+
+ while (item)
+ {
+ pKeys[index++] = (ULONG_PTR)item->key;
+ item = item->next;
+ }
+ }
+
+ *ppKeys = pKeys;
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return count;
+}
+
+static void item_free(wListDictionary* listDictionary, wListDictionaryItem* item)
+{
+ WINPR_ASSERT(listDictionary);
+
+ if (item)
+ {
+ if (listDictionary->objectKey.fnObjectFree)
+ listDictionary->objectKey.fnObjectFree(item->key);
+ if (listDictionary->objectValue.fnObjectFree)
+ listDictionary->objectValue.fnObjectFree(item->value);
+ }
+ free(item);
+}
+
+static void item_set(wListDictionary* listDictionary, wListDictionaryItem* item, const void* value)
+{
+ WINPR_ASSERT(listDictionary);
+ WINPR_ASSERT(item);
+
+ if (listDictionary->objectValue.fnObjectFree)
+ listDictionary->objectValue.fnObjectFree(item->value);
+
+ if (listDictionary->objectValue.fnObjectNew)
+ item->value = listDictionary->objectValue.fnObjectNew(value);
+ else
+ item->value = (void*)(uintptr_t)value;
+}
+
+static wListDictionaryItem* new_item(wListDictionary* listDictionary, const void* key,
+ const void* value)
+{
+ wListDictionaryItem* item = (wListDictionaryItem*)calloc(1, sizeof(wListDictionaryItem));
+ if (!item)
+ return NULL;
+
+ if (listDictionary->objectKey.fnObjectNew)
+ item->key = listDictionary->objectKey.fnObjectNew(key);
+ else
+ item->key = (void*)(uintptr_t)key;
+ if (!item->key)
+ goto fail;
+
+ item_set(listDictionary, item, value);
+ if (value && !item->value)
+ goto fail;
+
+ return item;
+
+fail:
+ item_free(listDictionary, item);
+ return NULL;
+}
+
+/**
+ * Adds an entry with the specified key and value into the ListDictionary.
+ */
+
+BOOL ListDictionary_Add(wListDictionary* listDictionary, const void* key, const void* value)
+{
+ BOOL ret = FALSE;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ wListDictionaryItem* item = new_item(listDictionary, key, value);
+
+ if (!item)
+ goto out_error;
+
+ if (!listDictionary->head)
+ {
+ listDictionary->head = item;
+ }
+ else
+ {
+ wListDictionaryItem* lastItem = listDictionary->head;
+
+ while (lastItem->next)
+ lastItem = lastItem->next;
+
+ lastItem->next = item;
+ }
+
+ ret = TRUE;
+out_error:
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return ret;
+}
+
+/**
+ * Removes all entries from the ListDictionary.
+ */
+
+void ListDictionary_Clear(wListDictionary* listDictionary)
+{
+ wListDictionaryItem* item = NULL;
+ wListDictionaryItem* nextItem = NULL;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ if (listDictionary->head)
+ {
+ item = listDictionary->head;
+
+ while (item)
+ {
+ nextItem = item->next;
+
+ item_free(listDictionary, item);
+ item = nextItem;
+ }
+
+ listDictionary->head = NULL;
+ }
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+}
+
+/**
+ * Determines whether the ListDictionary contains a specific key.
+ */
+
+BOOL ListDictionary_Contains(wListDictionary* listDictionary, const void* key)
+{
+ wListDictionaryItem* item = NULL;
+ OBJECT_EQUALS_FN keyEquals = NULL;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&(listDictionary->lock));
+
+ keyEquals = listDictionary->objectKey.fnObjectEquals;
+ item = listDictionary->head;
+
+ while (item)
+ {
+ if (keyEquals(item->key, key))
+ break;
+
+ item = item->next;
+ }
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&(listDictionary->lock));
+
+ return (item) ? TRUE : FALSE;
+}
+
+/**
+ * Removes the entry with the specified key from the ListDictionary.
+ */
+
+static void* ListDictionary_RemoveOrTake(wListDictionary* listDictionary, const void* key,
+ BOOL take)
+{
+ void* value = NULL;
+ wListDictionaryItem* item = NULL;
+ wListDictionaryItem* prevItem = NULL;
+ OBJECT_EQUALS_FN keyEquals = NULL;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ keyEquals = listDictionary->objectKey.fnObjectEquals;
+ item = listDictionary->head;
+ prevItem = NULL;
+
+ while (item)
+ {
+ if (keyEquals(item->key, key))
+ {
+ if (!prevItem)
+ listDictionary->head = item->next;
+ else
+ prevItem->next = item->next;
+
+ if (take)
+ {
+ value = item->value;
+ item->value = NULL;
+ }
+ item_free(listDictionary, item);
+ break;
+ }
+
+ prevItem = item;
+ item = item->next;
+ }
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return value;
+}
+
+void ListDictionary_Remove(wListDictionary* listDictionary, const void* key)
+{
+ ListDictionary_RemoveOrTake(listDictionary, key, FALSE);
+}
+
+void* ListDictionary_Take(wListDictionary* listDictionary, const void* key)
+{
+ return ListDictionary_RemoveOrTake(listDictionary, key, TRUE);
+}
+
+/**
+ * Removes the first (head) entry from the list
+ */
+
+static void* ListDictionary_Remove_Or_Take_Head(wListDictionary* listDictionary, BOOL take)
+{
+ wListDictionaryItem* item = NULL;
+ void* value = NULL;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ if (listDictionary->head)
+ {
+ item = listDictionary->head;
+ listDictionary->head = listDictionary->head->next;
+ if (take)
+ {
+ value = item->value;
+ item->value = NULL;
+ }
+ item_free(listDictionary, item);
+ }
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return value;
+}
+
+void ListDictionary_Remove_Head(wListDictionary* listDictionary)
+{
+ ListDictionary_Remove_Or_Take_Head(listDictionary, FALSE);
+}
+
+void* ListDictionary_Take_Head(wListDictionary* listDictionary)
+{
+ return ListDictionary_Remove_Or_Take_Head(listDictionary, TRUE);
+}
+
+/**
+ * Get an item value using key
+ */
+
+void* ListDictionary_GetItemValue(wListDictionary* listDictionary, const void* key)
+{
+ void* value = NULL;
+ wListDictionaryItem* item = NULL;
+ OBJECT_EQUALS_FN keyEquals = NULL;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ keyEquals = listDictionary->objectKey.fnObjectEquals;
+
+ if (listDictionary->head)
+ {
+ item = listDictionary->head;
+
+ while (item)
+ {
+ if (keyEquals(item->key, key))
+ break;
+
+ item = item->next;
+ }
+ }
+
+ value = (item) ? item->value : NULL;
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return value;
+}
+
+/**
+ * Set an item value using key
+ */
+
+BOOL ListDictionary_SetItemValue(wListDictionary* listDictionary, const void* key,
+ const void* value)
+{
+ BOOL status = FALSE;
+ wListDictionaryItem* item = NULL;
+ OBJECT_EQUALS_FN keyEquals = NULL;
+
+ WINPR_ASSERT(listDictionary);
+
+ if (listDictionary->synchronized)
+ EnterCriticalSection(&listDictionary->lock);
+
+ keyEquals = listDictionary->objectKey.fnObjectEquals;
+
+ if (listDictionary->head)
+ {
+ item = listDictionary->head;
+
+ while (item)
+ {
+ if (keyEquals(item->key, key))
+ break;
+
+ item = item->next;
+ }
+
+ if (item)
+ item_set(listDictionary, item, value);
+
+ status = (item) ? TRUE : FALSE;
+ }
+
+ if (listDictionary->synchronized)
+ LeaveCriticalSection(&listDictionary->lock);
+
+ return status;
+}
+
+static BOOL default_equal_function(const void* obj1, const void* obj2)
+{
+ return (obj1 == obj2);
+}
+/**
+ * Construction, Destruction
+ */
+
+wListDictionary* ListDictionary_New(BOOL synchronized)
+{
+ wListDictionary* listDictionary = (wListDictionary*)calloc(1, sizeof(wListDictionary));
+
+ if (!listDictionary)
+ return NULL;
+
+ listDictionary->synchronized = synchronized;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(listDictionary->lock), 4000))
+ {
+ free(listDictionary);
+ return NULL;
+ }
+
+ listDictionary->objectKey.fnObjectEquals = default_equal_function;
+ listDictionary->objectValue.fnObjectEquals = default_equal_function;
+ return listDictionary;
+}
+
+void ListDictionary_Free(wListDictionary* listDictionary)
+{
+ if (listDictionary)
+ {
+ ListDictionary_Clear(listDictionary);
+ DeleteCriticalSection(&listDictionary->lock);
+ free(listDictionary);
+ }
+}
diff --git a/winpr/libwinpr/utils/collections/MessagePipe.c b/winpr/libwinpr/utils/collections/MessagePipe.c
new file mode 100644
index 0000000..98adfe7
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/MessagePipe.c
@@ -0,0 +1,80 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Message Pipe
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+
+#include <winpr/collections.h>
+
+/**
+ * Properties
+ */
+
+/**
+ * Methods
+ */
+
+void MessagePipe_PostQuit(wMessagePipe* pipe, int nExitCode)
+{
+ MessageQueue_PostQuit(pipe->In, nExitCode);
+ MessageQueue_PostQuit(pipe->Out, nExitCode);
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wMessagePipe* MessagePipe_New(void)
+{
+ wMessagePipe* pipe = NULL;
+
+ pipe = (wMessagePipe*)malloc(sizeof(wMessagePipe));
+
+ if (!pipe)
+ return NULL;
+
+ pipe->In = MessageQueue_New(NULL);
+ if (!pipe->In)
+ goto error_in;
+
+ pipe->Out = MessageQueue_New(NULL);
+ if (!pipe->Out)
+ goto error_out;
+
+ return pipe;
+
+error_out:
+ MessageQueue_Free(pipe->In);
+error_in:
+ free(pipe);
+ return NULL;
+}
+
+void MessagePipe_Free(wMessagePipe* pipe)
+{
+ if (pipe)
+ {
+ MessageQueue_Free(pipe->In);
+ MessageQueue_Free(pipe->Out);
+
+ free(pipe);
+ }
+}
diff --git a/winpr/libwinpr/utils/collections/MessageQueue.c b/winpr/libwinpr/utils/collections/MessageQueue.c
new file mode 100644
index 0000000..5481bd4
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/MessageQueue.c
@@ -0,0 +1,313 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Message Queue
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/sysinfo.h>
+#include <winpr/assert.h>
+
+#include <winpr/collections.h>
+
+struct s_wMessageQueue
+{
+ size_t head;
+ size_t tail;
+ size_t size;
+ size_t capacity;
+ BOOL closed;
+ wMessage* array;
+ CRITICAL_SECTION lock;
+ HANDLE event;
+
+ wObject object;
+};
+
+/**
+ * Message Queue inspired from Windows:
+ * http://msdn.microsoft.com/en-us/library/ms632590/
+ */
+
+/**
+ * Properties
+ */
+
+wObject* MessageQueue_Object(wMessageQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ return &queue->object;
+}
+
+/**
+ * Gets an event which is set when the queue is non-empty
+ */
+
+HANDLE MessageQueue_Event(wMessageQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ return queue->event;
+}
+
+/**
+ * Gets the queue size
+ */
+
+size_t MessageQueue_Size(wMessageQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ return queue->size;
+}
+
+/**
+ * Methods
+ */
+
+BOOL MessageQueue_Wait(wMessageQueue* queue)
+{
+ BOOL status = FALSE;
+
+ WINPR_ASSERT(queue);
+ if (WaitForSingleObject(queue->event, INFINITE) == WAIT_OBJECT_0)
+ status = TRUE;
+
+ return status;
+}
+
+static BOOL MessageQueue_EnsureCapacity(wMessageQueue* queue, size_t count)
+{
+ WINPR_ASSERT(queue);
+
+ if (queue->size + count >= queue->capacity)
+ {
+ wMessage* new_arr = NULL;
+ size_t old_capacity = queue->capacity;
+ size_t new_capacity = queue->capacity * 2;
+
+ if (new_capacity < queue->size + count)
+ new_capacity = queue->size + count;
+
+ new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity);
+ if (!new_arr)
+ return FALSE;
+ queue->array = new_arr;
+ queue->capacity = new_capacity;
+ ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(wMessage));
+
+ /* rearrange wrapped entries */
+ if (queue->tail <= queue->head)
+ {
+ CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(wMessage));
+ queue->tail += old_capacity;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL MessageQueue_Dispatch(wMessageQueue* queue, const wMessage* message)
+{
+ wMessage* dst = NULL;
+ BOOL ret = FALSE;
+ WINPR_ASSERT(queue);
+
+ if (!message)
+ return FALSE;
+
+ WINPR_ASSERT(queue);
+ EnterCriticalSection(&queue->lock);
+
+ if (queue->closed)
+ goto out;
+
+ if (!MessageQueue_EnsureCapacity(queue, 1))
+ goto out;
+
+ dst = &(queue->array[queue->tail]);
+ *dst = *message;
+ dst->time = GetTickCount64();
+
+ queue->tail = (queue->tail + 1) % queue->capacity;
+ queue->size++;
+
+ if (queue->size > 0)
+ SetEvent(queue->event);
+
+ if (message->id == WMQ_QUIT)
+ queue->closed = TRUE;
+
+ ret = TRUE;
+out:
+ LeaveCriticalSection(&queue->lock);
+ return ret;
+}
+
+BOOL MessageQueue_Post(wMessageQueue* queue, void* context, UINT32 type, void* wParam, void* lParam)
+{
+ wMessage message = { 0 };
+
+ message.context = context;
+ message.id = type;
+ message.wParam = wParam;
+ message.lParam = lParam;
+ message.Free = NULL;
+
+ return MessageQueue_Dispatch(queue, &message);
+}
+
+BOOL MessageQueue_PostQuit(wMessageQueue* queue, int nExitCode)
+{
+ return MessageQueue_Post(queue, NULL, WMQ_QUIT, (void*)(size_t)nExitCode, NULL);
+}
+
+int MessageQueue_Get(wMessageQueue* queue, wMessage* message)
+{
+ int status = -1;
+
+ if (!MessageQueue_Wait(queue))
+ return status;
+
+ EnterCriticalSection(&queue->lock);
+
+ if (queue->size > 0)
+ {
+ CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage));
+ ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage));
+ queue->head = (queue->head + 1) % queue->capacity;
+ queue->size--;
+
+ if (queue->size < 1)
+ ResetEvent(queue->event);
+
+ status = (message->id != WMQ_QUIT) ? 1 : 0;
+ }
+
+ LeaveCriticalSection(&queue->lock);
+
+ return status;
+}
+
+int MessageQueue_Peek(wMessageQueue* queue, wMessage* message, BOOL remove)
+{
+ int status = 0;
+
+ WINPR_ASSERT(queue);
+ EnterCriticalSection(&queue->lock);
+
+ if (queue->size > 0)
+ {
+ CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage));
+ status = 1;
+
+ if (remove)
+ {
+ ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage));
+ queue->head = (queue->head + 1) % queue->capacity;
+ queue->size--;
+
+ if (queue->size < 1)
+ ResetEvent(queue->event);
+ }
+ }
+
+ LeaveCriticalSection(&queue->lock);
+
+ return status;
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wMessageQueue* MessageQueue_New(const wObject* callback)
+{
+ wMessageQueue* queue = NULL;
+
+ queue = (wMessageQueue*)calloc(1, sizeof(wMessageQueue));
+ if (!queue)
+ return NULL;
+
+ if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
+ goto fail;
+
+ if (!MessageQueue_EnsureCapacity(queue, 32))
+ goto fail;
+
+ queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!queue->event)
+ goto fail;
+
+ if (callback)
+ queue->object = *callback;
+
+ return queue;
+
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ MessageQueue_Free(queue);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void MessageQueue_Free(wMessageQueue* queue)
+{
+ if (!queue)
+ return;
+
+ if (queue->event)
+ MessageQueue_Clear(queue);
+
+ CloseHandle(queue->event);
+ DeleteCriticalSection(&queue->lock);
+
+ free(queue->array);
+ free(queue);
+}
+
+int MessageQueue_Clear(wMessageQueue* queue)
+{
+ int status = 0;
+
+ WINPR_ASSERT(queue);
+ WINPR_ASSERT(queue->event);
+
+ EnterCriticalSection(&queue->lock);
+
+ while (queue->size > 0)
+ {
+ wMessage* msg = &(queue->array[queue->head]);
+
+ /* Free resources of message. */
+ if (queue->object.fnObjectUninit)
+ queue->object.fnObjectUninit(msg);
+ if (queue->object.fnObjectFree)
+ queue->object.fnObjectFree(msg);
+
+ ZeroMemory(msg, sizeof(wMessage));
+
+ queue->head = (queue->head + 1) % queue->capacity;
+ queue->size--;
+ }
+ ResetEvent(queue->event);
+ queue->closed = FALSE;
+
+ LeaveCriticalSection(&queue->lock);
+
+ return status;
+}
diff --git a/winpr/libwinpr/utils/collections/Object.c b/winpr/libwinpr/utils/collections/Object.c
new file mode 100644
index 0000000..8bee86c
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/Object.c
@@ -0,0 +1,42 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Collections
+ *
+ * Copyright 2024 Armin Novak <anovak@thincast.com>
+ * Copyright 2024 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <winpr/string.h>
+#include <winpr/collections.h>
+
+void* winpr_ObjectStringClone(const void* pvstr)
+{
+ const char* str = pvstr;
+ if (!str)
+ return NULL;
+ return _strdup(str);
+}
+
+void* winpr_ObjectWStringClone(const void* pvstr)
+{
+ const WCHAR* str = pvstr;
+ if (!str)
+ return NULL;
+ return _wcsdup(str);
+}
+
+void winpr_ObjectStringFree(void* pvstr)
+{
+ free(pvstr);
+}
diff --git a/winpr/libwinpr/utils/collections/ObjectPool.c b/winpr/libwinpr/utils/collections/ObjectPool.c
new file mode 100644
index 0000000..d7bb623
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/ObjectPool.c
@@ -0,0 +1,185 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Object Pool
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+
+#include <winpr/collections.h>
+
+struct s_wObjectPool
+{
+ size_t size;
+ size_t capacity;
+ void** array;
+ CRITICAL_SECTION lock;
+ wObject object;
+ BOOL synchronized;
+};
+
+/**
+ * C Object Pool similar to C# BufferManager Class:
+ * http://msdn.microsoft.com/en-us/library/ms405814.aspx
+ */
+
+/**
+ * Methods
+ */
+
+static void ObjectPool_Lock(wObjectPool* pool)
+{
+ WINPR_ASSERT(pool);
+ if (pool->synchronized)
+ EnterCriticalSection(&pool->lock);
+}
+
+static void ObjectPool_Unlock(wObjectPool* pool)
+{
+ WINPR_ASSERT(pool);
+ if (pool->synchronized)
+ LeaveCriticalSection(&pool->lock);
+}
+
+/**
+ * Gets an object from the pool.
+ */
+
+void* ObjectPool_Take(wObjectPool* pool)
+{
+ void* obj = NULL;
+
+ ObjectPool_Lock(pool);
+
+ if (pool->size > 0)
+ obj = pool->array[--(pool->size)];
+
+ if (!obj)
+ {
+ if (pool->object.fnObjectNew)
+ obj = pool->object.fnObjectNew(NULL);
+ }
+
+ if (pool->object.fnObjectInit)
+ pool->object.fnObjectInit(obj);
+
+ ObjectPool_Unlock(pool);
+
+ return obj;
+}
+
+/**
+ * Returns an object to the pool.
+ */
+
+void ObjectPool_Return(wObjectPool* pool, void* obj)
+{
+ ObjectPool_Lock(pool);
+
+ if ((pool->size + 1) >= pool->capacity)
+ {
+ size_t new_cap = 0;
+ void** new_arr = NULL;
+
+ new_cap = pool->capacity * 2;
+ new_arr = (void**)realloc(pool->array, sizeof(void*) * new_cap);
+ if (!new_arr)
+ goto out;
+
+ pool->array = new_arr;
+ pool->capacity = new_cap;
+ }
+
+ pool->array[(pool->size)++] = obj;
+
+ if (pool->object.fnObjectUninit)
+ pool->object.fnObjectUninit(obj);
+
+out:
+ ObjectPool_Unlock(pool);
+}
+
+wObject* ObjectPool_Object(wObjectPool* pool)
+{
+ WINPR_ASSERT(pool);
+ return &pool->object;
+}
+
+/**
+ * Releases the buffers currently cached in the pool.
+ */
+
+void ObjectPool_Clear(wObjectPool* pool)
+{
+ ObjectPool_Lock(pool);
+
+ while (pool->size > 0)
+ {
+ (pool->size)--;
+
+ if (pool->object.fnObjectFree)
+ pool->object.fnObjectFree(pool->array[pool->size]);
+ }
+
+ ObjectPool_Unlock(pool);
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wObjectPool* ObjectPool_New(BOOL synchronized)
+{
+ wObjectPool* pool = NULL;
+
+ pool = (wObjectPool*)calloc(1, sizeof(wObjectPool));
+
+ if (pool)
+ {
+ pool->capacity = 32;
+ pool->size = 0;
+ pool->array = (void**)calloc(pool->capacity, sizeof(void*));
+ if (!pool->array)
+ {
+ free(pool);
+ return NULL;
+ }
+ pool->synchronized = synchronized;
+
+ if (pool->synchronized)
+ InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
+ }
+
+ return pool;
+}
+
+void ObjectPool_Free(wObjectPool* pool)
+{
+ if (pool)
+ {
+ ObjectPool_Clear(pool);
+
+ if (pool->synchronized)
+ DeleteCriticalSection(&pool->lock);
+
+ free(pool->array);
+
+ free(pool);
+ }
+}
diff --git a/winpr/libwinpr/utils/collections/PubSub.c b/winpr/libwinpr/utils/collections/PubSub.c
new file mode 100644
index 0000000..0efffb7
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/PubSub.c
@@ -0,0 +1,265 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Publisher/Subscriber Pattern
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+
+#include <winpr/collections.h>
+
+/**
+ * Events (C# Programming Guide)
+ * http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
+ */
+
+struct s_wPubSub
+{
+ CRITICAL_SECTION lock;
+ BOOL synchronized;
+
+ size_t size;
+ size_t count;
+ wEventType* events;
+};
+
+/**
+ * Properties
+ */
+
+wEventType* PubSub_GetEventTypes(wPubSub* pubSub, size_t* count)
+{
+ WINPR_ASSERT(pubSub);
+ if (count)
+ *count = pubSub->count;
+
+ return pubSub->events;
+}
+
+/**
+ * Methods
+ */
+
+void PubSub_Lock(wPubSub* pubSub)
+{
+ WINPR_ASSERT(pubSub);
+ if (pubSub->synchronized)
+ EnterCriticalSection(&pubSub->lock);
+}
+
+void PubSub_Unlock(wPubSub* pubSub)
+{
+ WINPR_ASSERT(pubSub);
+ if (pubSub->synchronized)
+ LeaveCriticalSection(&pubSub->lock);
+}
+
+wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName)
+{
+ wEventType* event = NULL;
+
+ WINPR_ASSERT(pubSub);
+ WINPR_ASSERT(EventName);
+ for (size_t index = 0; index < pubSub->count; index++)
+ {
+ if (strcmp(pubSub->events[index].EventName, EventName) == 0)
+ {
+ event = &(pubSub->events[index]);
+ break;
+ }
+ }
+
+ return event;
+}
+
+void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, size_t count)
+{
+ WINPR_ASSERT(pubSub);
+ WINPR_ASSERT(events || (count == 0));
+ if (pubSub->synchronized)
+ PubSub_Lock(pubSub);
+
+ while (pubSub->count + count >= pubSub->size)
+ {
+ size_t new_size = 0;
+ wEventType* new_event = NULL;
+
+ new_size = pubSub->size * 2;
+ new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType));
+ if (!new_event)
+ return;
+ pubSub->size = new_size;
+ pubSub->events = new_event;
+ }
+
+ CopyMemory(&pubSub->events[pubSub->count], events, count * sizeof(wEventType));
+ pubSub->count += count;
+
+ if (pubSub->synchronized)
+ PubSub_Unlock(pubSub);
+}
+
+int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, ...)
+{
+ wEventType* event = NULL;
+ int status = -1;
+ WINPR_ASSERT(pubSub);
+
+ va_list ap;
+ va_start(ap, EventName);
+ pEventHandler EventHandler = va_arg(ap, pEventHandler);
+
+ if (pubSub->synchronized)
+ PubSub_Lock(pubSub);
+
+ event = PubSub_FindEventType(pubSub, EventName);
+
+ if (event)
+ {
+ status = 0;
+
+ if (event->EventHandlerCount < MAX_EVENT_HANDLERS)
+ event->EventHandlers[event->EventHandlerCount++] = EventHandler;
+ else
+ status = -1;
+ }
+
+ if (pubSub->synchronized)
+ PubSub_Unlock(pubSub);
+
+ va_end(ap);
+ return status;
+}
+
+int PubSub_Unsubscribe(wPubSub* pubSub, const char* EventName, ...)
+{
+ wEventType* event = NULL;
+ int status = -1;
+ WINPR_ASSERT(pubSub);
+ WINPR_ASSERT(EventName);
+
+ va_list ap;
+ va_start(ap, EventName);
+ pEventHandler EventHandler = va_arg(ap, pEventHandler);
+
+ if (pubSub->synchronized)
+ PubSub_Lock(pubSub);
+
+ event = PubSub_FindEventType(pubSub, EventName);
+
+ if (event)
+ {
+ status = 0;
+
+ for (size_t index = 0; index < event->EventHandlerCount; index++)
+ {
+ if (event->EventHandlers[index] == EventHandler)
+ {
+ event->EventHandlers[index] = NULL;
+ event->EventHandlerCount--;
+ MoveMemory(&event->EventHandlers[index], &event->EventHandlers[index + 1],
+ (MAX_EVENT_HANDLERS - index - 1) * sizeof(pEventHandler));
+ status = 1;
+ }
+ }
+ }
+
+ if (pubSub->synchronized)
+ PubSub_Unlock(pubSub);
+
+ va_end(ap);
+ return status;
+}
+
+int PubSub_OnEvent(wPubSub* pubSub, const char* EventName, void* context, const wEventArgs* e)
+{
+ wEventType* event = NULL;
+ int status = -1;
+
+ if (!pubSub)
+ return -1;
+ WINPR_ASSERT(e);
+
+ if (pubSub->synchronized)
+ PubSub_Lock(pubSub);
+
+ event = PubSub_FindEventType(pubSub, EventName);
+
+ if (pubSub->synchronized)
+ PubSub_Unlock(pubSub);
+
+ if (event)
+ {
+ status = 0;
+
+ for (size_t index = 0; index < event->EventHandlerCount; index++)
+ {
+ if (event->EventHandlers[index])
+ {
+ event->EventHandlers[index](context, e);
+ status++;
+ }
+ }
+ }
+
+ return status;
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wPubSub* PubSub_New(BOOL synchronized)
+{
+ wPubSub* pubSub = (wPubSub*)calloc(1, sizeof(wPubSub));
+
+ if (!pubSub)
+ return NULL;
+
+ pubSub->synchronized = synchronized;
+
+ if (pubSub->synchronized && !InitializeCriticalSectionAndSpinCount(&pubSub->lock, 4000))
+ goto fail;
+
+ pubSub->count = 0;
+ pubSub->size = 64;
+
+ pubSub->events = (wEventType*)calloc(pubSub->size, sizeof(wEventType));
+ if (!pubSub->events)
+ goto fail;
+
+ return pubSub;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ PubSub_Free(pubSub);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void PubSub_Free(wPubSub* pubSub)
+{
+ if (pubSub)
+ {
+ if (pubSub->synchronized)
+ DeleteCriticalSection(&pubSub->lock);
+
+ free(pubSub->events);
+ free(pubSub);
+ }
+}
diff --git a/winpr/libwinpr/utils/collections/Queue.c b/winpr/libwinpr/utils/collections/Queue.c
new file mode 100644
index 0000000..d24e1dd
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/Queue.c
@@ -0,0 +1,345 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System.Collections.Queue
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+
+#include <winpr/collections.h>
+
+struct s_wQueue
+{
+ size_t capacity;
+ size_t growthFactor;
+ BOOL synchronized;
+
+ BYTE padding[4];
+
+ size_t head;
+ size_t tail;
+ size_t size;
+ void** array;
+ CRITICAL_SECTION lock;
+ HANDLE event;
+
+ wObject object;
+ BOOL haveLock;
+
+ BYTE padding2[4];
+};
+
+/**
+ * C equivalent of the C# Queue Class:
+ * http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
+ */
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets the number of elements contained in the Queue.
+ */
+
+size_t Queue_Count(wQueue* queue)
+{
+ size_t ret = 0;
+
+ Queue_Lock(queue);
+
+ ret = queue->size;
+
+ Queue_Unlock(queue);
+
+ return ret;
+}
+
+/**
+ * Lock access to the ArrayList
+ */
+
+void Queue_Lock(wQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ if (queue->synchronized)
+ EnterCriticalSection(&queue->lock);
+}
+
+/**
+ * Unlock access to the ArrayList
+ */
+
+void Queue_Unlock(wQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ if (queue->synchronized)
+ LeaveCriticalSection(&queue->lock);
+}
+
+/**
+ * Gets an event which is set when the queue is non-empty
+ */
+
+HANDLE Queue_Event(wQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ return queue->event;
+}
+
+wObject* Queue_Object(wQueue* queue)
+{
+ WINPR_ASSERT(queue);
+ return &queue->object;
+}
+
+/**
+ * Methods
+ */
+
+/**
+ * Removes all objects from the Queue.
+ */
+
+void Queue_Clear(wQueue* queue)
+{
+ Queue_Lock(queue);
+
+ for (size_t index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity)
+ {
+ if (queue->object.fnObjectFree)
+ queue->object.fnObjectFree(queue->array[index]);
+
+ queue->array[index] = NULL;
+ }
+
+ queue->size = 0;
+ queue->head = queue->tail = 0;
+ ResetEvent(queue->event);
+ Queue_Unlock(queue);
+}
+
+/**
+ * Determines whether an element is in the Queue.
+ */
+
+BOOL Queue_Contains(wQueue* queue, const void* obj)
+{
+ BOOL found = FALSE;
+
+ Queue_Lock(queue);
+
+ for (size_t index = 0; index < queue->tail; index++)
+ {
+ if (queue->object.fnObjectEquals(queue->array[index], obj))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ Queue_Unlock(queue);
+
+ return found;
+}
+
+static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count)
+{
+ WINPR_ASSERT(queue);
+
+ if (queue->size + count >= queue->capacity)
+ {
+ const size_t old_capacity = queue->capacity;
+ size_t new_capacity = queue->capacity * queue->growthFactor;
+ void** newArray = NULL;
+ if (new_capacity < queue->size + count)
+ new_capacity = queue->size + count;
+ newArray = (void**)realloc(queue->array, sizeof(void*) * new_capacity);
+
+ if (!newArray)
+ return FALSE;
+
+ queue->capacity = new_capacity;
+ queue->array = newArray;
+ ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(void*));
+
+ /* rearrange wrapped entries */
+ if (queue->tail <= queue->head)
+ {
+ CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(void*));
+ queue->tail += old_capacity;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Adds an object to the end of the Queue.
+ */
+
+BOOL Queue_Enqueue(wQueue* queue, const void* obj)
+{
+ BOOL ret = TRUE;
+
+ Queue_Lock(queue);
+
+ if (!Queue_EnsureCapacity(queue, 1))
+ goto out;
+
+ if (queue->object.fnObjectNew)
+ queue->array[queue->tail] = queue->object.fnObjectNew(obj);
+ else
+ {
+ union
+ {
+ const void* cv;
+ void* v;
+ } cnv;
+ cnv.cv = obj;
+ queue->array[queue->tail] = cnv.v;
+ }
+ queue->tail = (queue->tail + 1) % queue->capacity;
+ queue->size++;
+ SetEvent(queue->event);
+out:
+
+ Queue_Unlock(queue);
+
+ return ret;
+}
+
+/**
+ * Removes and returns the object at the beginning of the Queue.
+ */
+
+void* Queue_Dequeue(wQueue* queue)
+{
+ void* obj = NULL;
+
+ Queue_Lock(queue);
+
+ if (queue->size > 0)
+ {
+ obj = queue->array[queue->head];
+ queue->array[queue->head] = NULL;
+ queue->head = (queue->head + 1) % queue->capacity;
+ queue->size--;
+ }
+
+ if (queue->size < 1)
+ ResetEvent(queue->event);
+
+ Queue_Unlock(queue);
+
+ return obj;
+}
+
+/**
+ * Returns the object at the beginning of the Queue without removing it.
+ */
+
+void* Queue_Peek(wQueue* queue)
+{
+ void* obj = NULL;
+
+ Queue_Lock(queue);
+
+ if (queue->size > 0)
+ obj = queue->array[queue->head];
+
+ Queue_Unlock(queue);
+
+ return obj;
+}
+
+void Queue_Discard(wQueue* queue)
+{
+ void* obj = NULL;
+
+ Queue_Lock(queue);
+ obj = Queue_Dequeue(queue);
+
+ if (queue->object.fnObjectFree)
+ queue->object.fnObjectFree(obj);
+ Queue_Unlock(queue);
+}
+
+static BOOL default_queue_equals(const void* obj1, const void* obj2)
+{
+ return (obj1 == obj2);
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
+{
+ wObject* obj = NULL;
+ wQueue* queue = NULL;
+ queue = (wQueue*)calloc(1, sizeof(wQueue));
+
+ if (!queue)
+ return NULL;
+
+ queue->synchronized = synchronized;
+
+ queue->growthFactor = 2;
+ if (growthFactor > 0)
+ queue->growthFactor = (size_t)growthFactor;
+
+ if (capacity <= 0)
+ capacity = 32;
+ if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
+ goto fail;
+ queue->haveLock = TRUE;
+ if (!Queue_EnsureCapacity(queue, (size_t)capacity))
+ goto fail;
+
+ queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (!queue->event)
+ goto fail;
+
+ obj = Queue_Object(queue);
+ obj->fnObjectEquals = default_queue_equals;
+
+ return queue;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ Queue_Free(queue);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void Queue_Free(wQueue* queue)
+{
+ if (!queue)
+ return;
+
+ if (queue->haveLock)
+ {
+ Queue_Clear(queue);
+ DeleteCriticalSection(&queue->lock);
+ }
+ CloseHandle(queue->event);
+ free(queue->array);
+ free(queue);
+}
diff --git a/winpr/libwinpr/utils/collections/Stack.c b/winpr/libwinpr/utils/collections/Stack.c
new file mode 100644
index 0000000..f8b5c5e
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/Stack.c
@@ -0,0 +1,252 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * System.Collections.Stack
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/collections.h>
+#include <winpr/assert.h>
+
+struct s_wStack
+{
+ size_t size;
+ size_t capacity;
+ void** array;
+ CRITICAL_SECTION lock;
+ BOOL synchronized;
+ wObject object;
+};
+
+/**
+ * C equivalent of the C# Stack Class:
+ * http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx
+ */
+
+/**
+ * Properties
+ */
+
+/**
+ * Gets the number of elements contained in the Stack.
+ */
+
+size_t Stack_Count(wStack* stack)
+{
+ size_t ret = 0;
+ WINPR_ASSERT(stack);
+ if (stack->synchronized)
+ EnterCriticalSection(&stack->lock);
+
+ ret = stack->size;
+
+ if (stack->synchronized)
+ LeaveCriticalSection(&stack->lock);
+
+ return ret;
+}
+
+/**
+ * Gets a value indicating whether access to the Stack is synchronized (thread safe).
+ */
+
+BOOL Stack_IsSynchronized(wStack* stack)
+{
+ WINPR_ASSERT(stack);
+ return stack->synchronized;
+}
+
+wObject* Stack_Object(wStack* stack)
+{
+ WINPR_ASSERT(stack);
+ return &stack->object;
+}
+
+/**
+ * Methods
+ */
+
+/**
+ * Removes all objects from the Stack.
+ */
+
+void Stack_Clear(wStack* stack)
+{
+ WINPR_ASSERT(stack);
+ if (stack->synchronized)
+ EnterCriticalSection(&stack->lock);
+
+ for (size_t index = 0; index < stack->size; index++)
+ {
+ if (stack->object.fnObjectFree)
+ stack->object.fnObjectFree(stack->array[index]);
+
+ stack->array[index] = NULL;
+ }
+
+ stack->size = 0;
+
+ if (stack->synchronized)
+ LeaveCriticalSection(&stack->lock);
+}
+
+/**
+ * Determines whether an element is in the Stack.
+ */
+
+BOOL Stack_Contains(wStack* stack, const void* obj)
+{
+ BOOL found = FALSE;
+
+ WINPR_ASSERT(stack);
+ if (stack->synchronized)
+ EnterCriticalSection(&stack->lock);
+
+ for (size_t i = 0; i < stack->size; i++)
+ {
+ if (stack->object.fnObjectEquals(stack->array[i], obj))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (stack->synchronized)
+ LeaveCriticalSection(&stack->lock);
+
+ return found;
+}
+
+/**
+ * Inserts an object at the top of the Stack.
+ */
+
+void Stack_Push(wStack* stack, void* obj)
+{
+ WINPR_ASSERT(stack);
+ if (stack->synchronized)
+ EnterCriticalSection(&stack->lock);
+
+ if ((stack->size + 1) >= stack->capacity)
+ {
+ const size_t new_cap = stack->capacity * 2;
+ void** new_arr = (void**)realloc(stack->array, sizeof(void*) * new_cap);
+
+ if (!new_arr)
+ goto end;
+
+ stack->array = new_arr;
+ stack->capacity = new_cap;
+ }
+
+ stack->array[(stack->size)++] = obj;
+
+end:
+ if (stack->synchronized)
+ LeaveCriticalSection(&stack->lock);
+}
+
+/**
+ * Removes and returns the object at the top of the Stack.
+ */
+
+void* Stack_Pop(wStack* stack)
+{
+ void* obj = NULL;
+
+ WINPR_ASSERT(stack);
+ if (stack->synchronized)
+ EnterCriticalSection(&stack->lock);
+
+ if (stack->size > 0)
+ obj = stack->array[--(stack->size)];
+
+ if (stack->synchronized)
+ LeaveCriticalSection(&stack->lock);
+
+ return obj;
+}
+
+/**
+ * Returns the object at the top of the Stack without removing it.
+ */
+
+void* Stack_Peek(wStack* stack)
+{
+ void* obj = NULL;
+
+ WINPR_ASSERT(stack);
+ if (stack->synchronized)
+ EnterCriticalSection(&stack->lock);
+
+ if (stack->size > 0)
+ obj = stack->array[stack->size - 1];
+
+ if (stack->synchronized)
+ LeaveCriticalSection(&stack->lock);
+
+ return obj;
+}
+
+static BOOL default_stack_equals(const void* obj1, const void* obj2)
+{
+ return (obj1 == obj2);
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wStack* Stack_New(BOOL synchronized)
+{
+ wStack* stack = NULL;
+ stack = (wStack*)calloc(1, sizeof(wStack));
+
+ if (!stack)
+ return NULL;
+
+ stack->object.fnObjectEquals = default_stack_equals;
+ stack->synchronized = synchronized;
+ stack->capacity = 32;
+ stack->array = (void**)calloc(stack->capacity, sizeof(void*));
+
+ if (!stack->array)
+ goto out_free;
+
+ if (stack->synchronized && !InitializeCriticalSectionAndSpinCount(&stack->lock, 4000))
+ goto out_free;
+
+ return stack;
+out_free:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ Stack_Free(stack);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void Stack_Free(wStack* stack)
+{
+ if (stack)
+ {
+ if (stack->synchronized)
+ DeleteCriticalSection(&stack->lock);
+
+ free(stack->array);
+ free(stack);
+ }
+}
diff --git a/winpr/libwinpr/utils/collections/StreamPool.c b/winpr/libwinpr/utils/collections/StreamPool.c
new file mode 100644
index 0000000..910309f
--- /dev/null
+++ b/winpr/libwinpr/utils/collections/StreamPool.c
@@ -0,0 +1,407 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Object Pool
+ *
+ * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/wlog.h>
+
+#include <winpr/collections.h>
+
+#include "../stream.h"
+
+struct s_wStreamPool
+{
+ size_t aSize;
+ size_t aCapacity;
+ wStream** aArray;
+
+ size_t uSize;
+ size_t uCapacity;
+ wStream** uArray;
+
+ CRITICAL_SECTION lock;
+ BOOL synchronized;
+ size_t defaultSize;
+};
+
+/**
+ * Lock the stream pool
+ */
+
+static INLINE void StreamPool_Lock(wStreamPool* pool)
+{
+ WINPR_ASSERT(pool);
+ if (pool->synchronized)
+ EnterCriticalSection(&pool->lock);
+}
+
+/**
+ * Unlock the stream pool
+ */
+
+static INLINE void StreamPool_Unlock(wStreamPool* pool)
+{
+ WINPR_ASSERT(pool);
+ if (pool->synchronized)
+ LeaveCriticalSection(&pool->lock);
+}
+
+static BOOL StreamPool_EnsureCapacity(wStreamPool* pool, size_t count, BOOL usedOrAvailable)
+{
+ size_t new_cap = 0;
+ size_t* cap = NULL;
+ size_t* size = NULL;
+ wStream*** array = NULL;
+
+ WINPR_ASSERT(pool);
+
+ cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity;
+ size = (usedOrAvailable) ? &pool->uSize : &pool->aSize;
+ array = (usedOrAvailable) ? &pool->uArray : &pool->aArray;
+ if (*cap == 0)
+ new_cap = *size + count;
+ else if (*size + count > *cap)
+ new_cap = *cap * 2;
+ else if ((*size + count) < *cap / 3)
+ new_cap = *cap / 2;
+
+ if (new_cap > 0)
+ {
+ wStream** new_arr = NULL;
+
+ if (*cap < *size + count)
+ *cap += count;
+
+ new_arr = (wStream**)realloc(*array, sizeof(wStream*) * new_cap);
+ if (!new_arr)
+ return FALSE;
+ *cap = new_cap;
+ *array = new_arr;
+ }
+ return TRUE;
+}
+
+/**
+ * Methods
+ */
+
+static void StreamPool_ShiftUsed(wStreamPool* pool, size_t index, INT64 count)
+{
+ WINPR_ASSERT(pool);
+ if (count > 0)
+ {
+ const size_t pcount = (size_t)count;
+ StreamPool_EnsureCapacity(pool, pcount, TRUE);
+
+ MoveMemory(&pool->uArray[index + pcount], &pool->uArray[index],
+ (pool->uSize - index) * sizeof(wStream*));
+ pool->uSize += pcount;
+ }
+ else if (count < 0)
+ {
+ const size_t pcount = (size_t)-count;
+ if ((pool->uSize - index - pcount) > 0)
+ {
+ MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount],
+ (pool->uSize - index - pcount) * sizeof(wStream*));
+ }
+
+ pool->uSize -= pcount;
+ }
+}
+
+/**
+ * Adds a used stream to the pool.
+ */
+
+static void StreamPool_AddUsed(wStreamPool* pool, wStream* s)
+{
+ StreamPool_EnsureCapacity(pool, 1, TRUE);
+ pool->uArray[(pool->uSize)++] = s;
+}
+
+/**
+ * Removes a used stream from the pool.
+ */
+
+static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s)
+{
+ WINPR_ASSERT(pool);
+ for (size_t index = 0; index < pool->uSize; index++)
+ {
+ if (pool->uArray[index] == s)
+ {
+ StreamPool_ShiftUsed(pool, index, -1);
+ break;
+ }
+ }
+}
+
+static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index, INT64 count)
+{
+ WINPR_ASSERT(pool);
+ if (count > 0)
+ {
+ const size_t pcount = (size_t)count;
+
+ StreamPool_EnsureCapacity(pool, pcount, FALSE);
+ MoveMemory(&pool->aArray[index + pcount], &pool->aArray[index],
+ (pool->aSize - index) * sizeof(wStream*));
+ pool->aSize += pcount;
+ }
+ else if (count < 0)
+ {
+ const size_t pcount = (size_t)-count;
+
+ if ((pool->aSize - index - pcount) > 0)
+ {
+ MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
+ (pool->aSize - index - pcount) * sizeof(wStream*));
+ }
+
+ pool->aSize -= pcount;
+ }
+}
+
+/**
+ * Gets a stream from the pool.
+ */
+
+wStream* StreamPool_Take(wStreamPool* pool, size_t size)
+{
+ SSIZE_T foundIndex = -1;
+ wStream* s = NULL;
+
+ StreamPool_Lock(pool);
+
+ if (size == 0)
+ size = pool->defaultSize;
+
+ for (size_t index = 0; index < pool->aSize; index++)
+ {
+ s = pool->aArray[index];
+
+ if (Stream_Capacity(s) >= size)
+ {
+ foundIndex = index;
+ break;
+ }
+ }
+
+ if (foundIndex < 0)
+ {
+ s = Stream_New(NULL, size);
+ if (!s)
+ goto out_fail;
+ }
+ else
+ {
+ Stream_SetPosition(s, 0);
+ Stream_SetLength(s, Stream_Capacity(s));
+ StreamPool_ShiftAvailable(pool, foundIndex, -1);
+ }
+
+ if (s)
+ {
+ s->pool = pool;
+ s->count = 1;
+ StreamPool_AddUsed(pool, s);
+ }
+
+out_fail:
+ StreamPool_Unlock(pool);
+
+ return s;
+}
+
+/**
+ * Returns an object to the pool.
+ */
+
+static void StreamPool_Remove(wStreamPool* pool, wStream* s)
+{
+ StreamPool_EnsureCapacity(pool, 1, FALSE);
+ Stream_EnsureValidity(s);
+ for (size_t x = 0; x < pool->aSize; x++)
+ {
+ wStream* cs = pool->aArray[x];
+
+ WINPR_ASSERT(cs != s);
+ }
+ pool->aArray[(pool->aSize)++] = s;
+ StreamPool_RemoveUsed(pool, s);
+}
+
+static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
+{
+ StreamPool_Lock(pool);
+ if (s->count > 0)
+ s->count--;
+ if (s->count == 0)
+ StreamPool_Remove(pool, s);
+ StreamPool_Unlock(pool);
+}
+
+void StreamPool_Return(wStreamPool* pool, wStream* s)
+{
+ WINPR_ASSERT(pool);
+ if (!s)
+ return;
+
+ StreamPool_Lock(pool);
+ StreamPool_Remove(pool, s);
+ StreamPool_Unlock(pool);
+}
+
+/**
+ * Increment stream reference count
+ */
+
+void Stream_AddRef(wStream* s)
+{
+ WINPR_ASSERT(s);
+ if (s->pool)
+ {
+ StreamPool_Lock(s->pool);
+ s->count++;
+ StreamPool_Unlock(s->pool);
+ }
+}
+
+/**
+ * Decrement stream reference count
+ */
+
+void Stream_Release(wStream* s)
+{
+ WINPR_ASSERT(s);
+ if (s->pool)
+ StreamPool_ReleaseOrReturn(s->pool, s);
+}
+
+/**
+ * Find stream in pool using pointer inside buffer
+ */
+
+wStream* StreamPool_Find(wStreamPool* pool, BYTE* ptr)
+{
+ wStream* s = NULL;
+ BOOL found = FALSE;
+
+ StreamPool_Lock(pool);
+
+ for (size_t index = 0; index < pool->uSize; index++)
+ {
+ s = pool->uArray[index];
+
+ if ((ptr >= Stream_Buffer(s)) && (ptr < (Stream_Buffer(s) + Stream_Capacity(s))))
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ StreamPool_Unlock(pool);
+
+ return (found) ? s : NULL;
+}
+
+/**
+ * Releases the streams currently cached in the pool.
+ */
+
+void StreamPool_Clear(wStreamPool* pool)
+{
+ StreamPool_Lock(pool);
+
+ while (pool->aSize > 0)
+ {
+ wStream* s = pool->aArray[--pool->aSize];
+ Stream_Free(s, s->isAllocatedStream);
+ }
+
+ while (pool->uSize > 0)
+ {
+ wStream* s = pool->uArray[--pool->uSize];
+ Stream_Free(s, s->isAllocatedStream);
+ }
+
+ StreamPool_Unlock(pool);
+}
+
+/**
+ * Construction, Destruction
+ */
+
+wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
+{
+ wStreamPool* pool = NULL;
+
+ pool = (wStreamPool*)calloc(1, sizeof(wStreamPool));
+
+ if (pool)
+ {
+ pool->synchronized = synchronized;
+ pool->defaultSize = defaultSize;
+
+ if (!StreamPool_EnsureCapacity(pool, 32, FALSE))
+ goto fail;
+ if (!StreamPool_EnsureCapacity(pool, 32, TRUE))
+ goto fail;
+
+ InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
+ }
+
+ return pool;
+fail:
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
+ StreamPool_Free(pool);
+ WINPR_PRAGMA_DIAG_POP
+ return NULL;
+}
+
+void StreamPool_Free(wStreamPool* pool)
+{
+ if (pool)
+ {
+ StreamPool_Clear(pool);
+
+ DeleteCriticalSection(&pool->lock);
+
+ free(pool->aArray);
+ free(pool->uArray);
+
+ free(pool);
+ }
+}
+
+char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size)
+{
+ WINPR_ASSERT(pool);
+
+ if (!buffer || (size < 1))
+ return NULL;
+ _snprintf(buffer, size - 1,
+ "aSize =%" PRIuz ", uSize =%" PRIuz "aCapacity=%" PRIuz ", uCapacity=%" PRIuz,
+ pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity);
+ buffer[size - 1] = '\0';
+ return buffer;
+}