From a9bcc81f821d7c66f623779fa5147e728eb3c388 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 03:24:41 +0200 Subject: Adding upstream version 3.3.0+dfsg1. Signed-off-by: Daniel Baumann --- winpr/libwinpr/utils/collections/ArrayList.c | 604 +++++++++++++++ winpr/libwinpr/utils/collections/BitStream.c | 176 +++++ winpr/libwinpr/utils/collections/BufferPool.c | 558 ++++++++++++++ winpr/libwinpr/utils/collections/CountdownEvent.c | 203 +++++ winpr/libwinpr/utils/collections/HashTable.c | 870 ++++++++++++++++++++++ winpr/libwinpr/utils/collections/LinkedList.c | 385 ++++++++++ winpr/libwinpr/utils/collections/ListDictionary.c | 562 ++++++++++++++ winpr/libwinpr/utils/collections/MessagePipe.c | 80 ++ winpr/libwinpr/utils/collections/MessageQueue.c | 313 ++++++++ winpr/libwinpr/utils/collections/Object.c | 42 ++ winpr/libwinpr/utils/collections/ObjectPool.c | 185 +++++ winpr/libwinpr/utils/collections/PubSub.c | 265 +++++++ winpr/libwinpr/utils/collections/Queue.c | 345 +++++++++ winpr/libwinpr/utils/collections/Stack.c | 252 +++++++ winpr/libwinpr/utils/collections/StreamPool.c | 407 ++++++++++ 15 files changed, 5247 insertions(+) create mode 100644 winpr/libwinpr/utils/collections/ArrayList.c create mode 100644 winpr/libwinpr/utils/collections/BitStream.c create mode 100644 winpr/libwinpr/utils/collections/BufferPool.c create mode 100644 winpr/libwinpr/utils/collections/CountdownEvent.c create mode 100644 winpr/libwinpr/utils/collections/HashTable.c create mode 100644 winpr/libwinpr/utils/collections/LinkedList.c create mode 100644 winpr/libwinpr/utils/collections/ListDictionary.c create mode 100644 winpr/libwinpr/utils/collections/MessagePipe.c create mode 100644 winpr/libwinpr/utils/collections/MessageQueue.c create mode 100644 winpr/libwinpr/utils/collections/Object.c create mode 100644 winpr/libwinpr/utils/collections/ObjectPool.c create mode 100644 winpr/libwinpr/utils/collections/PubSub.c create mode 100644 winpr/libwinpr/utils/collections/Queue.c create mode 100644 winpr/libwinpr/utils/collections/Stack.c create mode 100644 winpr/libwinpr/utils/collections/StreamPool.c (limited to 'winpr/libwinpr/utils/collections') 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 + * + * 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 + +#include + +#include +#include +#include + +#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 + * + * 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 +#include + +#include +#include + +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 + * + * 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 + +#include + +#include + +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 + * + * 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 +#include + +#include + +#include + +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 + * + * 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 + +#include +#include + +#include + +/** + * 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 + * + * Copyright 2013 Marc-Andre Moreau + * + * 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 + +#include +#include + +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 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 + * + * 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 +#include +#include + +#include + +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 + * + * 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 + +#include +#include + +#include + +/** + * 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 + * + * 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 + +#include +#include +#include + +#include + +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 + * 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 +#include + +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 + * + * 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 + +#include +#include + +#include + +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 + * + * 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 + +#include + +#include + +/** + * 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 + * + * 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 + +#include +#include + +#include + +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 + * + * 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 + +#include +#include + +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 + * + * 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 + +#include +#include + +#include + +#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; +} -- cgit v1.2.3