Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
614
third_party/FreeRDP/winpr/libwinpr/utils/collections/ArrayList.c
vendored
Normal file
614
third_party/FreeRDP/winpr/libwinpr/utils/collections/ArrayList.c
vendored
Normal file
@@ -0,0 +1,614 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* System.Collections.ArrayList
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#if defined(_WIN32) && (_MSC_VER < 1800) && !defined(__MINGW32__)
|
||||
#define va_copy(dest, src) (dest = src)
|
||||
#endif
|
||||
|
||||
struct s_wArrayList
|
||||
{
|
||||
size_t capacity;
|
||||
size_t growthFactor;
|
||||
BOOL synchronized;
|
||||
|
||||
size_t size;
|
||||
void** array;
|
||||
CRITICAL_SECTION lock;
|
||||
|
||||
wObject object;
|
||||
};
|
||||
|
||||
/**
|
||||
* C equivalent of the C# ArrayList Class:
|
||||
* http://msdn.microsoft.com/en-us/library/system.collections.arraylist.aspx
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets or sets the number of elements that the ArrayList can contain.
|
||||
*/
|
||||
|
||||
size_t ArrayList_Capacity(wArrayList* arrayList)
|
||||
{
|
||||
WINPR_ASSERT(arrayList);
|
||||
return arrayList->capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of elements actually contained in the ArrayList.
|
||||
*/
|
||||
|
||||
size_t ArrayList_Count(wArrayList* arrayList)
|
||||
{
|
||||
WINPR_ASSERT(arrayList);
|
||||
return arrayList->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal list of items contained in the ArrayList.
|
||||
*/
|
||||
|
||||
size_t ArrayList_Items(wArrayList* arrayList, ULONG_PTR** ppItems)
|
||||
{
|
||||
WINPR_ASSERT(arrayList);
|
||||
*ppItems = (ULONG_PTR*)arrayList->array;
|
||||
return arrayList->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the ArrayList has a fixed size.
|
||||
*/
|
||||
|
||||
BOOL ArrayList_IsFixedSized(WINPR_ATTR_UNUSED wArrayList* arrayList)
|
||||
{
|
||||
WINPR_ASSERT(arrayList);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether the ArrayList is read-only.
|
||||
*/
|
||||
|
||||
BOOL ArrayList_IsReadOnly(WINPR_ATTR_UNUSED 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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
size_t newCapacity = arrayList->capacity * arrayList->growthFactor;
|
||||
if (newCapacity < arrayList->size + count)
|
||||
newCapacity = arrayList->size + count;
|
||||
|
||||
newArray = (void**)realloc((void*)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, (size_t)count))
|
||||
return FALSE;
|
||||
|
||||
MoveMemory((void*)&arrayList->array[index + (size_t)count], (void*)&arrayList->array[index],
|
||||
(arrayList->size - index) * sizeof(void*));
|
||||
arrayList->size += (size_t)count;
|
||||
}
|
||||
else if (count < 0)
|
||||
{
|
||||
const size_t scount = WINPR_ASSERTING_INT_CAST(size_t, -count);
|
||||
const size_t off = index + scount;
|
||||
if (off < arrayList->size)
|
||||
{
|
||||
const size_t chunk = arrayList->size - off;
|
||||
MoveMemory((void*)&arrayList->array[index], (void*)&arrayList->array[off],
|
||||
chunk * sizeof(void*));
|
||||
}
|
||||
|
||||
arrayList->size -= scount;
|
||||
}
|
||||
|
||||
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] = nullptr;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
BOOL found = FALSE;
|
||||
|
||||
WINPR_ASSERT(arrayList);
|
||||
ArrayList_Lock_Conditional(arrayList);
|
||||
|
||||
SSIZE_T sindex = startIndex;
|
||||
if (startIndex < 0)
|
||||
sindex = 0;
|
||||
|
||||
SSIZE_T index = sindex;
|
||||
SSIZE_T cindex = count;
|
||||
if (count < 0)
|
||||
{
|
||||
if (arrayList->size > SSIZE_MAX)
|
||||
goto fail;
|
||||
cindex = (SSIZE_T)arrayList->size;
|
||||
}
|
||||
|
||||
for (; index < sindex + cindex; index++)
|
||||
{
|
||||
if (arrayList->object.fnObjectEquals(arrayList->array[index], obj))
|
||||
{
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
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 = startIndex;
|
||||
if (startIndex < 0)
|
||||
sindex = 0;
|
||||
|
||||
cindex = count;
|
||||
if (count < 0)
|
||||
{
|
||||
WINPR_ASSERT(arrayList->size <= SSIZE_MAX);
|
||||
cindex = (SSIZE_T)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);
|
||||
}
|
||||
|
||||
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 = WINPR_C_ARRAY_INIT;
|
||||
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_C_ARRAY_INIT;
|
||||
|
||||
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 = nullptr;
|
||||
wArrayList* arrayList = nullptr;
|
||||
arrayList = (wArrayList*)calloc(1, sizeof(wArrayList));
|
||||
|
||||
if (!arrayList)
|
||||
return nullptr;
|
||||
|
||||
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;
|
||||
|
||||
if (!InitializeCriticalSectionAndSpinCount(&arrayList->lock, 4000))
|
||||
goto fail;
|
||||
return arrayList;
|
||||
fail:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
ArrayList_Free(arrayList);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ArrayList_Free(wArrayList* arrayList)
|
||||
{
|
||||
if (!arrayList)
|
||||
return;
|
||||
|
||||
ArrayList_Clear(arrayList);
|
||||
DeleteCriticalSection(&arrayList->lock);
|
||||
free((void*)arrayList->array);
|
||||
free(arrayList);
|
||||
}
|
||||
177
third_party/FreeRDP/winpr/libwinpr/utils/collections/BitStream.c
vendored
Normal file
177
third_party/FreeRDP/winpr/libwinpr/utils/collections/BitStream.c
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* BitStream
|
||||
*
|
||||
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/bitstream.h>
|
||||
|
||||
static const char* BYTE_BIT_STRINGS_LSB[256] = {
|
||||
"00000000", "00000001", "00000010", "00000011", "00000100", "00000101", "00000110", "00000111",
|
||||
"00001000", "00001001", "00001010", "00001011", "00001100", "00001101", "00001110", "00001111",
|
||||
"00010000", "00010001", "00010010", "00010011", "00010100", "00010101", "00010110", "00010111",
|
||||
"00011000", "00011001", "00011010", "00011011", "00011100", "00011101", "00011110", "00011111",
|
||||
"00100000", "00100001", "00100010", "00100011", "00100100", "00100101", "00100110", "00100111",
|
||||
"00101000", "00101001", "00101010", "00101011", "00101100", "00101101", "00101110", "00101111",
|
||||
"00110000", "00110001", "00110010", "00110011", "00110100", "00110101", "00110110", "00110111",
|
||||
"00111000", "00111001", "00111010", "00111011", "00111100", "00111101", "00111110", "00111111",
|
||||
"01000000", "01000001", "01000010", "01000011", "01000100", "01000101", "01000110", "01000111",
|
||||
"01001000", "01001001", "01001010", "01001011", "01001100", "01001101", "01001110", "01001111",
|
||||
"01010000", "01010001", "01010010", "01010011", "01010100", "01010101", "01010110", "01010111",
|
||||
"01011000", "01011001", "01011010", "01011011", "01011100", "01011101", "01011110", "01011111",
|
||||
"01100000", "01100001", "01100010", "01100011", "01100100", "01100101", "01100110", "01100111",
|
||||
"01101000", "01101001", "01101010", "01101011", "01101100", "01101101", "01101110", "01101111",
|
||||
"01110000", "01110001", "01110010", "01110011", "01110100", "01110101", "01110110", "01110111",
|
||||
"01111000", "01111001", "01111010", "01111011", "01111100", "01111101", "01111110", "01111111",
|
||||
"10000000", "10000001", "10000010", "10000011", "10000100", "10000101", "10000110", "10000111",
|
||||
"10001000", "10001001", "10001010", "10001011", "10001100", "10001101", "10001110", "10001111",
|
||||
"10010000", "10010001", "10010010", "10010011", "10010100", "10010101", "10010110", "10010111",
|
||||
"10011000", "10011001", "10011010", "10011011", "10011100", "10011101", "10011110", "10011111",
|
||||
"10100000", "10100001", "10100010", "10100011", "10100100", "10100101", "10100110", "10100111",
|
||||
"10101000", "10101001", "10101010", "10101011", "10101100", "10101101", "10101110", "10101111",
|
||||
"10110000", "10110001", "10110010", "10110011", "10110100", "10110101", "10110110", "10110111",
|
||||
"10111000", "10111001", "10111010", "10111011", "10111100", "10111101", "10111110", "10111111",
|
||||
"11000000", "11000001", "11000010", "11000011", "11000100", "11000101", "11000110", "11000111",
|
||||
"11001000", "11001001", "11001010", "11001011", "11001100", "11001101", "11001110", "11001111",
|
||||
"11010000", "11010001", "11010010", "11010011", "11010100", "11010101", "11010110", "11010111",
|
||||
"11011000", "11011001", "11011010", "11011011", "11011100", "11011101", "11011110", "11011111",
|
||||
"11100000", "11100001", "11100010", "11100011", "11100100", "11100101", "11100110", "11100111",
|
||||
"11101000", "11101001", "11101010", "11101011", "11101100", "11101101", "11101110", "11101111",
|
||||
"11110000", "11110001", "11110010", "11110011", "11110100", "11110101", "11110110", "11110111",
|
||||
"11111000", "11111001", "11111010", "11111011", "11111100", "11111101", "11111110", "11111111"
|
||||
};
|
||||
|
||||
static const char* BYTE_BIT_STRINGS_MSB[256] = {
|
||||
"00000000", "10000000", "01000000", "11000000", "00100000", "10100000", "01100000", "11100000",
|
||||
"00010000", "10010000", "01010000", "11010000", "00110000", "10110000", "01110000", "11110000",
|
||||
"00001000", "10001000", "01001000", "11001000", "00101000", "10101000", "01101000", "11101000",
|
||||
"00011000", "10011000", "01011000", "11011000", "00111000", "10111000", "01111000", "11111000",
|
||||
"00000100", "10000100", "01000100", "11000100", "00100100", "10100100", "01100100", "11100100",
|
||||
"00010100", "10010100", "01010100", "11010100", "00110100", "10110100", "01110100", "11110100",
|
||||
"00001100", "10001100", "01001100", "11001100", "00101100", "10101100", "01101100", "11101100",
|
||||
"00011100", "10011100", "01011100", "11011100", "00111100", "10111100", "01111100", "11111100",
|
||||
"00000010", "10000010", "01000010", "11000010", "00100010", "10100010", "01100010", "11100010",
|
||||
"00010010", "10010010", "01010010", "11010010", "00110010", "10110010", "01110010", "11110010",
|
||||
"00001010", "10001010", "01001010", "11001010", "00101010", "10101010", "01101010", "11101010",
|
||||
"00011010", "10011010", "01011010", "11011010", "00111010", "10111010", "01111010", "11111010",
|
||||
"00000110", "10000110", "01000110", "11000110", "00100110", "10100110", "01100110", "11100110",
|
||||
"00010110", "10010110", "01010110", "11010110", "00110110", "10110110", "01110110", "11110110",
|
||||
"00001110", "10001110", "01001110", "11001110", "00101110", "10101110", "01101110", "11101110",
|
||||
"00011110", "10011110", "01011110", "11011110", "00111110", "10111110", "01111110", "11111110",
|
||||
"00000001", "10000001", "01000001", "11000001", "00100001", "10100001", "01100001", "11100001",
|
||||
"00010001", "10010001", "01010001", "11010001", "00110001", "10110001", "01110001", "11110001",
|
||||
"00001001", "10001001", "01001001", "11001001", "00101001", "10101001", "01101001", "11101001",
|
||||
"00011001", "10011001", "01011001", "11011001", "00111001", "10111001", "01111001", "11111001",
|
||||
"00000101", "10000101", "01000101", "11000101", "00100101", "10100101", "01100101", "11100101",
|
||||
"00010101", "10010101", "01010101", "11010101", "00110101", "10110101", "01110101", "11110101",
|
||||
"00001101", "10001101", "01001101", "11001101", "00101101", "10101101", "01101101", "11101101",
|
||||
"00011101", "10011101", "01011101", "11011101", "00111101", "10111101", "01111101", "11111101",
|
||||
"00000011", "10000011", "01000011", "11000011", "00100011", "10100011", "01100011", "11100011",
|
||||
"00010011", "10010011", "01010011", "11010011", "00110011", "10110011", "01110011", "11110011",
|
||||
"00001011", "10001011", "01001011", "11001011", "00101011", "10101011", "01101011", "11101011",
|
||||
"00011011", "10011011", "01011011", "11011011", "00111011", "10111011", "01111011", "11111011",
|
||||
"00000111", "10000111", "01000111", "11000111", "00100111", "10100111", "01100111", "11100111",
|
||||
"00010111", "10010111", "01010111", "11010111", "00110111", "10110111", "01110111", "11110111",
|
||||
"00001111", "10001111", "01001111", "11001111", "00101111", "10101111", "01101111", "11101111",
|
||||
"00011111", "10011111", "01011111", "11011111", "00111111", "10111111", "01111111", "11111111"
|
||||
};
|
||||
|
||||
void BitDump(const char* tag, UINT32 level, const BYTE* buffer, UINT32 length, UINT32 flags)
|
||||
{
|
||||
const char** strs = (flags & BITDUMP_MSB_FIRST) ? BYTE_BIT_STRINGS_MSB : BYTE_BIT_STRINGS_LSB;
|
||||
char pbuffer[64 * 8 + 1] = WINPR_C_ARRAY_INIT;
|
||||
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 DWORD nbits = (length - i) > 8 ? 8 : (length - i);
|
||||
WINPR_ASSERT(nbits <= INT32_MAX);
|
||||
const int rc = _snprintf(&pbuffer[pos], length - pos, "%.*s ", (int)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);
|
||||
}
|
||||
597
third_party/FreeRDP/winpr/libwinpr/utils/collections/BufferPool.c
vendored
Normal file
597
third_party/FreeRDP/winpr/libwinpr/utils/collections/BufferPool.c
vendored
Normal file
@@ -0,0 +1,597 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Buffer Pool
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
|
||||
#endif
|
||||
|
||||
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 = nullptr;
|
||||
SSIZE_T newCapacity = pool->aSize + count;
|
||||
newCapacity += (newCapacity + 2) / 2;
|
||||
|
||||
WINPR_ASSERT(newCapacity > 0);
|
||||
if (pool->alignment > 0)
|
||||
newArray = (wBufferPoolItem*)winpr_aligned_realloc(
|
||||
pool->aArray,
|
||||
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity),
|
||||
pool->alignment);
|
||||
else
|
||||
newArray = (wBufferPoolItem*)realloc(
|
||||
pool->aArray,
|
||||
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity));
|
||||
if (!newArray)
|
||||
return FALSE;
|
||||
pool->aArray = newArray;
|
||||
pool->aCapacity = newCapacity;
|
||||
}
|
||||
|
||||
MoveMemory(
|
||||
&pool->aArray[index + WINPR_ASSERTING_INT_CAST(size_t, count)], &pool->aArray[index],
|
||||
(WINPR_ASSERTING_INT_CAST(size_t, pool->aSize) - index) * sizeof(wBufferPoolItem));
|
||||
pool->aSize += count;
|
||||
}
|
||||
else if (count < 0)
|
||||
{
|
||||
MoveMemory(
|
||||
&pool->aArray[index], &pool->aArray[index + WINPR_ASSERTING_INT_CAST(size_t, -count)],
|
||||
(WINPR_ASSERTING_INT_CAST(size_t, 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)
|
||||
{
|
||||
const SSIZE_T required = pool->uSize + count;
|
||||
// check for overflow
|
||||
if ((required < count) || (required < pool->uSize))
|
||||
return FALSE;
|
||||
|
||||
if (required > pool->uCapacity)
|
||||
{
|
||||
SSIZE_T newUCapacity = pool->uCapacity;
|
||||
do
|
||||
{
|
||||
if (newUCapacity > SSIZE_MAX - 128ll)
|
||||
return FALSE;
|
||||
newUCapacity += 128ll;
|
||||
} while (newUCapacity <= required);
|
||||
wBufferPoolItem* newUArray = nullptr;
|
||||
if (pool->alignment > 0)
|
||||
newUArray = (wBufferPoolItem*)winpr_aligned_realloc(
|
||||
pool->uArray,
|
||||
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newUCapacity),
|
||||
pool->alignment);
|
||||
else
|
||||
newUArray = (wBufferPoolItem*)realloc(
|
||||
pool->uArray,
|
||||
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newUCapacity));
|
||||
if (!newUArray)
|
||||
return FALSE;
|
||||
pool->uCapacity = newUCapacity;
|
||||
pool->uArray = newUArray;
|
||||
}
|
||||
|
||||
MoveMemory(&pool->uArray[index + count], &pool->uArray[index],
|
||||
WINPR_ASSERTING_INT_CAST(size_t, pool->uSize - index) * sizeof(wBufferPoolItem));
|
||||
pool->uSize += count;
|
||||
}
|
||||
else if (count < 0)
|
||||
{
|
||||
MoveMemory(&pool->uArray[index], &pool->uArray[index - count],
|
||||
WINPR_ASSERTING_INT_CAST(size_t, 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 = nullptr;
|
||||
|
||||
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(WINPR_ASSERTING_INT_CAST(size_t, pool->fixedSize),
|
||||
pool->alignment);
|
||||
else
|
||||
buffer = malloc(WINPR_ASSERTING_INT_CAST(size_t, 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 = nullptr;
|
||||
else
|
||||
{
|
||||
if (pool->alignment)
|
||||
buffer = winpr_aligned_malloc(WINPR_ASSERTING_INT_CAST(size_t, size),
|
||||
pool->alignment);
|
||||
else
|
||||
buffer = malloc(WINPR_ASSERTING_INT_CAST(size_t, size));
|
||||
|
||||
if (!buffer)
|
||||
goto out_error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = pool->aArray[foundIndex].buffer;
|
||||
|
||||
if (maxSize < size)
|
||||
{
|
||||
void* newBuffer = nullptr;
|
||||
if (pool->alignment)
|
||||
newBuffer = winpr_aligned_realloc(
|
||||
buffer, WINPR_ASSERTING_INT_CAST(size_t, size), pool->alignment);
|
||||
else
|
||||
newBuffer = realloc(buffer, WINPR_ASSERTING_INT_CAST(size_t, size));
|
||||
|
||||
if (!newBuffer)
|
||||
goto out_error_no_free;
|
||||
|
||||
buffer = newBuffer;
|
||||
}
|
||||
|
||||
if (!BufferPool_ShiftAvailable(pool, WINPR_ASSERTING_INT_CAST(size_t, foundIndex), -1))
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
goto out_error;
|
||||
|
||||
if (pool->uSize + 1 > pool->uCapacity)
|
||||
{
|
||||
size_t newUCapacity = WINPR_ASSERTING_INT_CAST(size_t, pool->uCapacity);
|
||||
newUCapacity += (newUCapacity + 2) / 2;
|
||||
if (newUCapacity > SSIZE_MAX)
|
||||
goto out_error;
|
||||
wBufferPoolItem* newUArray =
|
||||
(wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
|
||||
if (!newUArray)
|
||||
goto out_error;
|
||||
|
||||
pool->uCapacity = (SSIZE_T)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 nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = MAX(2, pool->size + (pool->size + 2) / 2 + 1);
|
||||
void** newArray = (void**)realloc(
|
||||
(void*)pool->array, sizeof(void*) * WINPR_ASSERTING_INT_CAST(size_t, 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 = MAX(2, pool->aSize + (pool->aSize + 2) / 2 + 1);
|
||||
wBufferPoolItem* newArray = (wBufferPoolItem*)realloc(
|
||||
pool->aArray,
|
||||
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, 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 = nullptr;
|
||||
|
||||
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)
|
||||
{
|
||||
if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (pool->fixedSize)
|
||||
{
|
||||
/* fixed size buffers */
|
||||
|
||||
pool->size = 0;
|
||||
pool->capacity = 32;
|
||||
pool->array =
|
||||
(void**)calloc(WINPR_ASSERTING_INT_CAST(size_t, pool->capacity), sizeof(void*));
|
||||
if (!pool->array)
|
||||
goto out_error;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* variable size buffers */
|
||||
|
||||
pool->aSize = 0;
|
||||
pool->aCapacity = 32;
|
||||
pool->aArray = (wBufferPoolItem*)calloc(
|
||||
WINPR_ASSERTING_INT_CAST(size_t, pool->aCapacity), sizeof(wBufferPoolItem));
|
||||
if (!pool->aArray)
|
||||
goto out_error;
|
||||
|
||||
pool->uSize = 0;
|
||||
pool->uCapacity = 32;
|
||||
pool->uArray = (wBufferPoolItem*)calloc(
|
||||
WINPR_ASSERTING_INT_CAST(size_t, 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 nullptr;
|
||||
}
|
||||
|
||||
void BufferPool_Free(wBufferPool* pool)
|
||||
{
|
||||
if (pool)
|
||||
{
|
||||
BufferPool_Clear(pool);
|
||||
|
||||
if (pool->synchronized)
|
||||
DeleteCriticalSection(&pool->lock);
|
||||
|
||||
if (pool->fixedSize)
|
||||
{
|
||||
/* fixed size buffers */
|
||||
|
||||
free((void*)pool->array);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* variable size buffers */
|
||||
|
||||
free(pool->aArray);
|
||||
free(pool->uArray);
|
||||
}
|
||||
|
||||
free(pool);
|
||||
}
|
||||
}
|
||||
210
third_party/FreeRDP/winpr/libwinpr/utils/collections/CountdownEvent.c
vendored
Normal file
210
third_party/FreeRDP/winpr/libwinpr/utils/collections/CountdownEvent.c
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Countdown Event
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
struct CountdownEvent
|
||||
{
|
||||
size_t count;
|
||||
CRITICAL_SECTION lock;
|
||||
HANDLE event;
|
||||
size_t initialCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* C equivalent of the C# CountdownEvent Class
|
||||
* http://msdn.microsoft.com/en-us/library/dd235708/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the number of remaining signals required to set the event.
|
||||
*/
|
||||
|
||||
size_t CountdownEvent_CurrentCount(wCountdownEvent* countdown)
|
||||
{
|
||||
WINPR_ASSERT(countdown);
|
||||
EnterCriticalSection(&countdown->lock);
|
||||
const size_t rc = countdown->count;
|
||||
LeaveCriticalSection(&countdown->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the numbers of signals initially required to set the event.
|
||||
*/
|
||||
|
||||
size_t CountdownEvent_InitialCount(wCountdownEvent* countdown)
|
||||
{
|
||||
WINPR_ASSERT(countdown);
|
||||
EnterCriticalSection(&countdown->lock);
|
||||
const size_t rc = countdown->initialCount;
|
||||
LeaveCriticalSection(&countdown->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
const BOOL signalSet = countdown->count == 0;
|
||||
countdown->count += signalCount;
|
||||
|
||||
if (signalSet)
|
||||
(void)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))
|
||||
{
|
||||
(void)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 nullptr;
|
||||
|
||||
countdown->count = initialCount;
|
||||
countdown->initialCount = initialCount;
|
||||
|
||||
if (!InitializeCriticalSectionAndSpinCount(&countdown->lock, 4000))
|
||||
goto fail;
|
||||
|
||||
countdown->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
void CountdownEvent_Free(wCountdownEvent* countdown)
|
||||
{
|
||||
if (!countdown)
|
||||
return;
|
||||
|
||||
DeleteCriticalSection(&countdown->lock);
|
||||
(void)CloseHandle(countdown->event);
|
||||
|
||||
free(countdown);
|
||||
}
|
||||
873
third_party/FreeRDP/winpr/libwinpr/utils/collections/HashTable.c
vendored
Normal file
873
third_party/FreeRDP/winpr/libwinpr/utils/collections/HashTable.c
vendored
Normal file
@@ -0,0 +1,873 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* System.Collections.Hashtable
|
||||
*
|
||||
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
/**
|
||||
* This implementation is based on the public domain
|
||||
* hash table implementation made by Keith Pomakis:
|
||||
*
|
||||
* http://www.pomakis.com/hashtable/hashtable.c
|
||||
* http://www.pomakis.com/hashtable/hashtable.h
|
||||
*/
|
||||
|
||||
typedef struct s_wKeyValuePair wKeyValuePair;
|
||||
|
||||
struct s_wKeyValuePair
|
||||
{
|
||||
void* key;
|
||||
void* value;
|
||||
|
||||
wKeyValuePair* next;
|
||||
BOOL markedForRemove;
|
||||
};
|
||||
|
||||
struct s_wHashTable
|
||||
{
|
||||
BOOL synchronized;
|
||||
CRITICAL_SECTION lock;
|
||||
|
||||
size_t numOfBuckets;
|
||||
size_t numOfElements;
|
||||
float idealRatio;
|
||||
float lowerRehashThreshold;
|
||||
float upperRehashThreshold;
|
||||
wKeyValuePair** bucketArray;
|
||||
|
||||
HASH_TABLE_HASH_FN hash;
|
||||
wObject key;
|
||||
wObject value;
|
||||
|
||||
DWORD foreachRecursionLevel;
|
||||
DWORD pendingRemoves;
|
||||
};
|
||||
|
||||
BOOL HashTable_PointerCompare(const void* pointer1, const void* pointer2)
|
||||
{
|
||||
return (pointer1 == pointer2);
|
||||
}
|
||||
|
||||
UINT32 HashTable_PointerHash(const void* pointer)
|
||||
{
|
||||
return ((UINT32)(UINT_PTR)pointer) >> 4;
|
||||
}
|
||||
|
||||
BOOL HashTable_StringCompare(const void* string1, const void* string2)
|
||||
{
|
||||
if (!string1 || !string2)
|
||||
return (string1 == string2);
|
||||
|
||||
return (strcmp((const char*)string1, (const char*)string2) == 0);
|
||||
}
|
||||
|
||||
UINT32 HashTable_StringHash(const void* key)
|
||||
{
|
||||
UINT32 c = 0;
|
||||
UINT32 hash = 5381;
|
||||
const BYTE* str = (const BYTE*)key;
|
||||
|
||||
/* djb2 algorithm */
|
||||
while ((c = *str++) != '\0')
|
||||
hash = (hash * 33) + c;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
void* HashTable_StringClone(const void* str)
|
||||
{
|
||||
return winpr_ObjectStringClone(str);
|
||||
}
|
||||
|
||||
void HashTable_StringFree(void* str)
|
||||
{
|
||||
winpr_ObjectStringFree(str);
|
||||
}
|
||||
|
||||
static inline BOOL HashTable_IsProbablePrime(size_t oddNumber)
|
||||
{
|
||||
for (size_t i = 3; i < 51; i += 2)
|
||||
{
|
||||
if (oddNumber == i)
|
||||
return TRUE;
|
||||
else if (oddNumber % i == 0)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE; /* maybe */
|
||||
}
|
||||
|
||||
static inline size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
|
||||
{
|
||||
WINPR_ASSERT(table);
|
||||
|
||||
const float numOfElements = (float)table->numOfElements;
|
||||
const float tmp = (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 = nullptr;
|
||||
wKeyValuePair** newBucketArray = nullptr;
|
||||
|
||||
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((void*)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 = nullptr;
|
||||
|
||||
WINPR_ASSERT(table);
|
||||
if (!key)
|
||||
return nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
wKeyValuePair* newPair = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
wKeyValuePair* previousPair = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
wKeyValuePair* pair = nullptr;
|
||||
|
||||
WINPR_ASSERT(table);
|
||||
if (!key)
|
||||
return nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
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] = nullptr;
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
wKeyValuePair* nextPair = nullptr;
|
||||
|
||||
WINPR_ASSERT(table);
|
||||
|
||||
if (table->synchronized)
|
||||
EnterCriticalSection(&table->lock);
|
||||
|
||||
iKey = 0;
|
||||
count = table->numOfElements;
|
||||
if (ppKeys)
|
||||
*ppKeys = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
for (size_t index = 0; index < table->numOfBuckets; index++)
|
||||
{
|
||||
wKeyValuePair* nextPair = nullptr;
|
||||
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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
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;
|
||||
if (!InitializeCriticalSectionAndSpinCount(&(table->lock), 4000))
|
||||
goto fail;
|
||||
table->numOfBuckets = 64;
|
||||
table->numOfElements = 0;
|
||||
table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets, sizeof(wKeyValuePair*));
|
||||
|
||||
if (!table->bucketArray)
|
||||
goto fail;
|
||||
|
||||
table->idealRatio = 3.0f;
|
||||
table->lowerRehashThreshold = 0.0f;
|
||||
table->upperRehashThreshold = 15.0f;
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
void HashTable_Free(wHashTable* table)
|
||||
{
|
||||
wKeyValuePair* pair = nullptr;
|
||||
wKeyValuePair* nextPair = nullptr;
|
||||
|
||||
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((void*)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 != nullptr;
|
||||
}
|
||||
|
||||
BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
|
||||
{
|
||||
wObject* obj = nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
385
third_party/FreeRDP/winpr/libwinpr/utils/collections/LinkedList.c
vendored
Normal file
385
third_party/FreeRDP/winpr/libwinpr/utils/collections/LinkedList.c
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* System.Collections.Generic.LinkedList<T>
|
||||
*
|
||||
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
typedef struct s_wLinkedListItem wLinkedListNode;
|
||||
|
||||
struct s_wLinkedListItem
|
||||
{
|
||||
void* value;
|
||||
wLinkedListNode* prev;
|
||||
wLinkedListNode* next;
|
||||
};
|
||||
|
||||
struct s_wLinkedList
|
||||
{
|
||||
size_t count;
|
||||
int initial;
|
||||
wLinkedListNode* head;
|
||||
wLinkedListNode* tail;
|
||||
wLinkedListNode* current;
|
||||
wObject object;
|
||||
};
|
||||
|
||||
/**
|
||||
* C equivalent of the C# LinkedList<T> Class:
|
||||
* http://msdn.microsoft.com/en-us/library/he2s3bh7.aspx
|
||||
*
|
||||
* Internal implementation uses a doubly-linked list
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the number of nodes actually contained in the LinkedList.
|
||||
*/
|
||||
|
||||
size_t LinkedList_Count(wLinkedList* list)
|
||||
{
|
||||
WINPR_ASSERT(list);
|
||||
return list->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first node of the LinkedList.
|
||||
*/
|
||||
|
||||
void* LinkedList_First(wLinkedList* list)
|
||||
{
|
||||
WINPR_ASSERT(list);
|
||||
if (list->head)
|
||||
return list->head->value;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last node of the LinkedList.
|
||||
*/
|
||||
|
||||
void* LinkedList_Last(wLinkedList* list)
|
||||
{
|
||||
WINPR_ASSERT(list);
|
||||
if (list->tail)
|
||||
return list->tail->value;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determines whether the LinkedList contains a specific value.
|
||||
*/
|
||||
|
||||
BOOL LinkedList_Contains(wLinkedList* list, const void* value)
|
||||
{
|
||||
wLinkedListNode* item = nullptr;
|
||||
OBJECT_EQUALS_FN keyEquals = nullptr;
|
||||
|
||||
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 != nullptr);
|
||||
}
|
||||
|
||||
static wLinkedListNode* LinkedList_FreeNode(wLinkedList* list, wLinkedListNode* node)
|
||||
{
|
||||
wLinkedListNode* next = nullptr;
|
||||
wLinkedListNode* prev = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
WINPR_ASSERT(list);
|
||||
if (!list->head)
|
||||
return;
|
||||
|
||||
node = list->head;
|
||||
|
||||
while (node)
|
||||
node = LinkedList_FreeNode(list, node);
|
||||
|
||||
list->head = list->tail = nullptr;
|
||||
list->count = 0;
|
||||
}
|
||||
|
||||
static wLinkedListNode* LinkedList_Create(wLinkedList* list, const void* value)
|
||||
{
|
||||
wLinkedListNode* node = nullptr;
|
||||
|
||||
WINPR_ASSERT(list);
|
||||
node = (wLinkedListNode*)calloc(1, sizeof(wLinkedListNode));
|
||||
|
||||
if (!node)
|
||||
return nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
OBJECT_EQUALS_FN keyEquals = nullptr;
|
||||
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 nullptr;
|
||||
|
||||
if (list->current)
|
||||
return list->current->value;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = nullptr;
|
||||
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;
|
||||
}
|
||||
562
third_party/FreeRDP/winpr/libwinpr/utils/collections/ListDictionary.c
vendored
Normal file
562
third_party/FreeRDP/winpr/libwinpr/utils/collections/ListDictionary.c
vendored
Normal file
@@ -0,0 +1,562 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* System.Collections.Specialized.ListDictionary
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
typedef struct s_wListDictionaryItem wListDictionaryItem;
|
||||
|
||||
struct s_wListDictionaryItem
|
||||
{
|
||||
void* key;
|
||||
void* value;
|
||||
|
||||
wListDictionaryItem* next;
|
||||
};
|
||||
|
||||
struct s_wListDictionary
|
||||
{
|
||||
BOOL synchronized;
|
||||
CRITICAL_SECTION lock;
|
||||
|
||||
wListDictionaryItem* head;
|
||||
wObject objectKey;
|
||||
wObject objectValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* C equivalent of the C# ListDictionary Class:
|
||||
* http://msdn.microsoft.com/en-us/library/system.collections.specialized.listdictionary.aspx
|
||||
*
|
||||
* Internal implementation uses a singly-linked list
|
||||
*/
|
||||
|
||||
WINPR_API wObject* ListDictionary_KeyObject(wListDictionary* listDictionary)
|
||||
{
|
||||
WINPR_ASSERT(listDictionary);
|
||||
return &listDictionary->objectKey;
|
||||
}
|
||||
|
||||
WINPR_API wObject* ListDictionary_ValueObject(wListDictionary* listDictionary)
|
||||
{
|
||||
WINPR_ASSERT(listDictionary);
|
||||
return &listDictionary->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)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
if (listDictionary->head)
|
||||
{
|
||||
wListDictionaryItem* item = listDictionary->head;
|
||||
|
||||
while (item)
|
||||
{
|
||||
count++;
|
||||
item = item->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
if (!ppKeys)
|
||||
return 0;
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
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)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
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 nullptr;
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
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)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entries from the ListDictionary.
|
||||
*/
|
||||
|
||||
void ListDictionary_Clear(wListDictionary* listDictionary)
|
||||
{
|
||||
wListDictionaryItem* item = nullptr;
|
||||
wListDictionaryItem* nextItem = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
if (listDictionary->head)
|
||||
{
|
||||
item = listDictionary->head;
|
||||
|
||||
while (item)
|
||||
{
|
||||
nextItem = item->next;
|
||||
|
||||
item_free(listDictionary, item);
|
||||
item = nextItem;
|
||||
}
|
||||
|
||||
listDictionary->head = nullptr;
|
||||
}
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the ListDictionary contains a specific key.
|
||||
*/
|
||||
|
||||
BOOL ListDictionary_Contains(wListDictionary* listDictionary, const void* key)
|
||||
{
|
||||
wListDictionaryItem* item = nullptr;
|
||||
OBJECT_EQUALS_FN keyEquals = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
keyEquals = listDictionary->objectKey.fnObjectEquals;
|
||||
item = listDictionary->head;
|
||||
|
||||
while (item)
|
||||
{
|
||||
if (keyEquals(item->key, key))
|
||||
break;
|
||||
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
return (item != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the entry with the specified key from the ListDictionary.
|
||||
*/
|
||||
|
||||
static void* ListDictionary_RemoveOrTake(wListDictionary* listDictionary, const void* key,
|
||||
BOOL take)
|
||||
{
|
||||
void* value = nullptr;
|
||||
wListDictionaryItem* item = nullptr;
|
||||
wListDictionaryItem* prevItem = nullptr;
|
||||
OBJECT_EQUALS_FN keyEquals = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
keyEquals = listDictionary->objectKey.fnObjectEquals;
|
||||
item = listDictionary->head;
|
||||
prevItem = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
}
|
||||
item_free(listDictionary, item);
|
||||
break;
|
||||
}
|
||||
|
||||
prevItem = item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
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 = nullptr;
|
||||
void* value = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
if (listDictionary->head)
|
||||
{
|
||||
item = listDictionary->head;
|
||||
listDictionary->head = listDictionary->head->next;
|
||||
if (take)
|
||||
{
|
||||
value = item->value;
|
||||
item->value = nullptr;
|
||||
}
|
||||
item_free(listDictionary, item);
|
||||
}
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
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 = nullptr;
|
||||
wListDictionaryItem* item = nullptr;
|
||||
OBJECT_EQUALS_FN keyEquals = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
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 : nullptr;
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an item value using key
|
||||
*/
|
||||
|
||||
BOOL ListDictionary_SetItemValue(wListDictionary* listDictionary, const void* key,
|
||||
const void* value)
|
||||
{
|
||||
BOOL status = FALSE;
|
||||
wListDictionaryItem* item = nullptr;
|
||||
OBJECT_EQUALS_FN keyEquals = nullptr;
|
||||
|
||||
WINPR_ASSERT(listDictionary);
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Lock(listDictionary);
|
||||
|
||||
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 != nullptr);
|
||||
}
|
||||
|
||||
if (listDictionary->synchronized)
|
||||
ListDictionary_Unlock(listDictionary);
|
||||
|
||||
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 nullptr;
|
||||
|
||||
listDictionary->synchronized = synchronized;
|
||||
|
||||
if (!InitializeCriticalSectionAndSpinCount(&(listDictionary->lock), 4000))
|
||||
{
|
||||
free(listDictionary);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
80
third_party/FreeRDP/winpr/libwinpr/utils/collections/MessagePipe.c
vendored
Normal file
80
third_party/FreeRDP/winpr/libwinpr/utils/collections/MessagePipe.c
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Message Pipe
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
|
||||
void MessagePipe_PostQuit(wMessagePipe* pipe, int nExitCode)
|
||||
{
|
||||
MessageQueue_PostQuit(pipe->In, nExitCode);
|
||||
MessageQueue_PostQuit(pipe->Out, nExitCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construction, Destruction
|
||||
*/
|
||||
|
||||
wMessagePipe* MessagePipe_New(void)
|
||||
{
|
||||
wMessagePipe* pipe = nullptr;
|
||||
|
||||
pipe = (wMessagePipe*)malloc(sizeof(wMessagePipe));
|
||||
|
||||
if (!pipe)
|
||||
return nullptr;
|
||||
|
||||
pipe->In = MessageQueue_New(nullptr);
|
||||
if (!pipe->In)
|
||||
goto error_in;
|
||||
|
||||
pipe->Out = MessageQueue_New(nullptr);
|
||||
if (!pipe->Out)
|
||||
goto error_out;
|
||||
|
||||
return pipe;
|
||||
|
||||
error_out:
|
||||
MessageQueue_Free(pipe->In);
|
||||
error_in:
|
||||
free(pipe);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MessagePipe_Free(wMessagePipe* pipe)
|
||||
{
|
||||
if (pipe)
|
||||
{
|
||||
MessageQueue_Free(pipe->In);
|
||||
MessageQueue_Free(pipe->Out);
|
||||
|
||||
free(pipe);
|
||||
}
|
||||
}
|
||||
354
third_party/FreeRDP/winpr/libwinpr/utils/collections/MessageQueue.c
vendored
Normal file
354
third_party/FreeRDP/winpr/libwinpr/utils/collections/MessageQueue.c
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Message Queue
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
struct s_wMessageQueue
|
||||
{
|
||||
size_t head;
|
||||
size_t tail;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
BOOL closed;
|
||||
wMessage* array;
|
||||
CRITICAL_SECTION lock;
|
||||
HANDLE event;
|
||||
|
||||
wObject object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Message Queue inspired from Windows:
|
||||
* http://msdn.microsoft.com/en-us/library/ms632590/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
wObject* MessageQueue_Object(wMessageQueue* queue)
|
||||
{
|
||||
WINPR_ASSERT(queue);
|
||||
return &queue->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an event which is set when the queue is non-empty
|
||||
*/
|
||||
|
||||
HANDLE MessageQueue_Event(wMessageQueue* queue)
|
||||
{
|
||||
WINPR_ASSERT(queue);
|
||||
return queue->event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the queue size
|
||||
*/
|
||||
|
||||
size_t MessageQueue_Size(wMessageQueue* queue)
|
||||
{
|
||||
WINPR_ASSERT(queue);
|
||||
EnterCriticalSection(&queue->lock);
|
||||
const size_t ret = queue->size;
|
||||
LeaveCriticalSection(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t MessageQueue_Capacity(wMessageQueue* queue)
|
||||
{
|
||||
WINPR_ASSERT(queue);
|
||||
EnterCriticalSection(&queue->lock);
|
||||
const size_t ret = queue->capacity;
|
||||
LeaveCriticalSection(&queue->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
const size_t increment = 128;
|
||||
WINPR_ASSERT(queue);
|
||||
|
||||
const size_t required = queue->size + count;
|
||||
// check for overflow
|
||||
if ((required < queue->size) || (required < count) ||
|
||||
(required > (SIZE_MAX - increment) / sizeof(wMessage)))
|
||||
return FALSE;
|
||||
|
||||
if (required > queue->capacity)
|
||||
{
|
||||
const size_t old_capacity = queue->capacity;
|
||||
const size_t new_capacity = required + increment;
|
||||
|
||||
wMessage* 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:
|
||||
* fill up the newly available space and move tail
|
||||
* back by the amount of elements that have been moved to the newly
|
||||
* allocated space.
|
||||
*/
|
||||
if (queue->tail <= queue->head)
|
||||
{
|
||||
size_t tocopy = queue->tail;
|
||||
size_t slots = new_capacity - old_capacity;
|
||||
const size_t batch = (tocopy < slots) ? tocopy : slots;
|
||||
CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(wMessage));
|
||||
|
||||
/* Tail is decremented. if the whole thing is appended
|
||||
* just move the existing tail by old_capacity */
|
||||
if (tocopy < slots)
|
||||
{
|
||||
ZeroMemory(queue->array, batch * sizeof(wMessage));
|
||||
queue->tail += old_capacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t remain = queue->tail - batch;
|
||||
const size_t movesize = remain * sizeof(wMessage);
|
||||
memmove_s(queue->array, queue->tail * sizeof(wMessage), &queue->array[batch],
|
||||
movesize);
|
||||
|
||||
const size_t zerooffset = remain;
|
||||
const size_t zerosize = (queue->tail - remain) * sizeof(wMessage);
|
||||
ZeroMemory(&queue->array[zerooffset], zerosize);
|
||||
queue->tail -= batch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL MessageQueue_Dispatch(wMessageQueue* queue, const wMessage* message)
|
||||
{
|
||||
wMessage* dst = nullptr;
|
||||
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)
|
||||
(void)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 = WINPR_C_ARRAY_INIT;
|
||||
|
||||
message.context = context;
|
||||
message.id = type;
|
||||
message.wParam = wParam;
|
||||
message.lParam = lParam;
|
||||
message.Free = nullptr;
|
||||
|
||||
return MessageQueue_Dispatch(queue, &message);
|
||||
}
|
||||
|
||||
BOOL MessageQueue_PostQuit(wMessageQueue* queue, int nExitCode)
|
||||
{
|
||||
return MessageQueue_Post(queue, nullptr, WMQ_QUIT, (void*)(size_t)nExitCode, nullptr);
|
||||
}
|
||||
|
||||
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)
|
||||
(void)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)
|
||||
(void)ResetEvent(queue->event);
|
||||
}
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&queue->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construction, Destruction
|
||||
*/
|
||||
|
||||
wMessageQueue* MessageQueue_New(const wObject* callback)
|
||||
{
|
||||
wMessageQueue* queue = nullptr;
|
||||
|
||||
queue = (wMessageQueue*)calloc(1, sizeof(wMessageQueue));
|
||||
if (!queue)
|
||||
return nullptr;
|
||||
|
||||
if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
|
||||
goto fail;
|
||||
|
||||
if (!MessageQueue_EnsureCapacity(queue, 32))
|
||||
goto fail;
|
||||
|
||||
queue->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
void MessageQueue_Free(wMessageQueue* queue)
|
||||
{
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
if (queue->event)
|
||||
MessageQueue_Clear(queue);
|
||||
|
||||
(void)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--;
|
||||
}
|
||||
(void)ResetEvent(queue->event);
|
||||
queue->closed = FALSE;
|
||||
|
||||
LeaveCriticalSection(&queue->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
42
third_party/FreeRDP/winpr/libwinpr/utils/collections/Object.c
vendored
Normal file
42
third_party/FreeRDP/winpr/libwinpr/utils/collections/Object.c
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Collections
|
||||
*
|
||||
* Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2024 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
void* winpr_ObjectStringClone(const void* pvstr)
|
||||
{
|
||||
const char* str = pvstr;
|
||||
if (!str)
|
||||
return nullptr;
|
||||
return _strdup(str);
|
||||
}
|
||||
|
||||
void* winpr_ObjectWStringClone(const void* pvstr)
|
||||
{
|
||||
const WCHAR* str = pvstr;
|
||||
if (!str)
|
||||
return nullptr;
|
||||
return _wcsdup(str);
|
||||
}
|
||||
|
||||
void winpr_ObjectStringFree(void* pvstr)
|
||||
{
|
||||
free(pvstr);
|
||||
}
|
||||
194
third_party/FreeRDP/winpr/libwinpr/utils/collections/ObjectPool.c
vendored
Normal file
194
third_party/FreeRDP/winpr/libwinpr/utils/collections/ObjectPool.c
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Object Pool
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
struct s_wObjectPool
|
||||
{
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
void** array;
|
||||
CRITICAL_SECTION lock;
|
||||
wObject object;
|
||||
BOOL synchronized;
|
||||
};
|
||||
|
||||
/**
|
||||
* C Object Pool similar to C# BufferManager Class:
|
||||
* http://msdn.microsoft.com/en-us/library/ms405814.aspx
|
||||
*/
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
|
||||
static void ObjectPool_Lock(wObjectPool* pool)
|
||||
{
|
||||
WINPR_ASSERT(pool);
|
||||
if (pool->synchronized)
|
||||
EnterCriticalSection(&pool->lock);
|
||||
}
|
||||
|
||||
static void ObjectPool_Unlock(wObjectPool* pool)
|
||||
{
|
||||
WINPR_ASSERT(pool);
|
||||
if (pool->synchronized)
|
||||
LeaveCriticalSection(&pool->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an object from the pool.
|
||||
*/
|
||||
|
||||
void* ObjectPool_Take(wObjectPool* pool)
|
||||
{
|
||||
void* obj = nullptr;
|
||||
|
||||
ObjectPool_Lock(pool);
|
||||
|
||||
if (pool->size > 0)
|
||||
obj = pool->array[--(pool->size)];
|
||||
|
||||
if (!obj)
|
||||
{
|
||||
if (pool->object.fnObjectNew)
|
||||
obj = pool->object.fnObjectNew(nullptr);
|
||||
}
|
||||
|
||||
if (pool->object.fnObjectInit)
|
||||
pool->object.fnObjectInit(obj);
|
||||
|
||||
ObjectPool_Unlock(pool);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
static BOOL ObjectPool_EnsureCapacity(wObjectPool* pool, size_t add)
|
||||
{
|
||||
WINPR_ASSERT(pool->size < SIZE_MAX - add);
|
||||
|
||||
const size_t blocksize = 128ull;
|
||||
const size_t required = pool->size + add;
|
||||
if (required >= pool->capacity)
|
||||
{
|
||||
const size_t new_cap = required + blocksize - required % blocksize;
|
||||
|
||||
void** new_arr = (void**)realloc((void*)pool->array, sizeof(void*) * new_cap);
|
||||
if (!new_arr)
|
||||
return FALSE;
|
||||
|
||||
pool->array = new_arr;
|
||||
pool->capacity = new_cap;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object to the pool.
|
||||
*/
|
||||
|
||||
void ObjectPool_Return(wObjectPool* pool, void* obj)
|
||||
{
|
||||
ObjectPool_Lock(pool);
|
||||
|
||||
if (!ObjectPool_EnsureCapacity(pool, 1))
|
||||
goto out;
|
||||
|
||||
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 = (wObjectPool*)calloc(1, sizeof(wObjectPool));
|
||||
|
||||
if (!pool)
|
||||
goto fail;
|
||||
|
||||
pool->synchronized = synchronized;
|
||||
|
||||
if (pool->synchronized)
|
||||
{
|
||||
if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!ObjectPool_EnsureCapacity(pool, 32))
|
||||
goto fail;
|
||||
|
||||
return pool;
|
||||
|
||||
fail:
|
||||
ObjectPool_Free(pool);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjectPool_Free(wObjectPool* pool)
|
||||
{
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
ObjectPool_Clear(pool);
|
||||
|
||||
if (pool->synchronized)
|
||||
DeleteCriticalSection(&pool->lock);
|
||||
|
||||
free((void*)pool->array);
|
||||
|
||||
free(pool);
|
||||
}
|
||||
273
third_party/FreeRDP/winpr/libwinpr/utils/collections/PubSub.c
vendored
Normal file
273
third_party/FreeRDP/winpr/libwinpr/utils/collections/PubSub.c
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Publisher/Subscriber Pattern
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
/**
|
||||
* Events (C# Programming Guide)
|
||||
* http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
|
||||
*/
|
||||
|
||||
struct s_wPubSub
|
||||
{
|
||||
CRITICAL_SECTION lock;
|
||||
BOOL synchronized;
|
||||
|
||||
size_t size;
|
||||
size_t count;
|
||||
wEventType* events;
|
||||
};
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
wEventType* PubSub_GetEventTypes(wPubSub* pubSub, size_t* count)
|
||||
{
|
||||
WINPR_ASSERT(pubSub);
|
||||
if (count)
|
||||
*count = pubSub->count;
|
||||
|
||||
return pubSub->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
|
||||
void PubSub_Lock(wPubSub* pubSub)
|
||||
{
|
||||
WINPR_ASSERT(pubSub);
|
||||
if (pubSub->synchronized)
|
||||
EnterCriticalSection(&pubSub->lock);
|
||||
}
|
||||
|
||||
void PubSub_Unlock(wPubSub* pubSub)
|
||||
{
|
||||
WINPR_ASSERT(pubSub);
|
||||
if (pubSub->synchronized)
|
||||
LeaveCriticalSection(&pubSub->lock);
|
||||
}
|
||||
|
||||
wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName)
|
||||
{
|
||||
wEventType* event = nullptr;
|
||||
|
||||
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);
|
||||
|
||||
const size_t required = pubSub->count + count;
|
||||
WINPR_ASSERT((required >= pubSub->count) && (required >= count));
|
||||
|
||||
if (required >= pubSub->size)
|
||||
{
|
||||
size_t new_size = pubSub->size;
|
||||
do
|
||||
{
|
||||
WINPR_ASSERT(new_size <= SIZE_MAX - 128ull);
|
||||
new_size += 128ull;
|
||||
} while (new_size <= required);
|
||||
|
||||
wEventType* new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType));
|
||||
if (!new_event)
|
||||
goto fail;
|
||||
pubSub->size = new_size;
|
||||
pubSub->events = new_event;
|
||||
}
|
||||
|
||||
CopyMemory(&pubSub->events[pubSub->count], events, count * sizeof(wEventType));
|
||||
pubSub->count += count;
|
||||
|
||||
fail:
|
||||
if (pubSub->synchronized)
|
||||
PubSub_Unlock(pubSub);
|
||||
}
|
||||
|
||||
int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, ...)
|
||||
{
|
||||
wEventType* event = nullptr;
|
||||
int status = -1;
|
||||
WINPR_ASSERT(pubSub);
|
||||
|
||||
va_list ap = WINPR_C_ARRAY_INIT;
|
||||
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 = nullptr;
|
||||
int status = -1;
|
||||
WINPR_ASSERT(pubSub);
|
||||
WINPR_ASSERT(EventName);
|
||||
|
||||
va_list ap = WINPR_C_ARRAY_INIT;
|
||||
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] = nullptr;
|
||||
event->EventHandlerCount--;
|
||||
MoveMemory((void*)&event->EventHandlers[index],
|
||||
(void*)&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 = nullptr;
|
||||
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 nullptr;
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
void PubSub_Free(wPubSub* pubSub)
|
||||
{
|
||||
if (pubSub)
|
||||
{
|
||||
if (pubSub->synchronized)
|
||||
DeleteCriticalSection(&pubSub->lock);
|
||||
|
||||
free(pubSub->events);
|
||||
free(pubSub);
|
||||
}
|
||||
}
|
||||
405
third_party/FreeRDP/winpr/libwinpr/utils/collections/Queue.c
vendored
Normal file
405
third_party/FreeRDP/winpr/libwinpr/utils/collections/Queue.c
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* System.Collections.Queue
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
struct s_wQueue
|
||||
{
|
||||
size_t capacity;
|
||||
size_t growthFactor;
|
||||
BOOL synchronized;
|
||||
|
||||
BYTE padding[4];
|
||||
|
||||
size_t head;
|
||||
size_t tail;
|
||||
size_t size;
|
||||
uintptr_t* array;
|
||||
CRITICAL_SECTION lock;
|
||||
HANDLE event;
|
||||
|
||||
wObject object;
|
||||
BOOL haveLock;
|
||||
|
||||
BYTE padding2[4];
|
||||
};
|
||||
|
||||
static inline void* uptr2void(uintptr_t ptr)
|
||||
{
|
||||
return (void*)ptr;
|
||||
}
|
||||
|
||||
static inline uintptr_t void2uptr(const void* ptr)
|
||||
{
|
||||
return (uintptr_t)ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
size_t Queue_Capacity(wQueue* queue)
|
||||
{
|
||||
WINPR_ASSERT(queue);
|
||||
|
||||
Queue_Lock(queue);
|
||||
|
||||
const size_t ret = queue->capacity;
|
||||
|
||||
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)
|
||||
{
|
||||
void* obj = uptr2void(queue->array[index]);
|
||||
queue->object.fnObjectFree(obj);
|
||||
}
|
||||
|
||||
queue->array[index] = 0;
|
||||
}
|
||||
|
||||
queue->size = 0;
|
||||
queue->head = queue->tail = 0;
|
||||
(void)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++)
|
||||
{
|
||||
void* ptr = uptr2void(queue->array[index]);
|
||||
if (queue->object.fnObjectEquals(ptr, obj))
|
||||
{
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Queue_Unlock(queue);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count)
|
||||
{
|
||||
const size_t blocksize = 32ull;
|
||||
WINPR_ASSERT(queue);
|
||||
|
||||
if (queue->growthFactor > SIZE_MAX / blocksize)
|
||||
return FALSE;
|
||||
|
||||
const size_t increment = blocksize * queue->growthFactor;
|
||||
if (queue->size > SIZE_MAX - count)
|
||||
return FALSE;
|
||||
|
||||
const size_t required = queue->size + count;
|
||||
if (required > queue->capacity)
|
||||
{
|
||||
const size_t old_capacity = queue->capacity;
|
||||
if (required > SIZE_MAX - increment)
|
||||
return FALSE;
|
||||
|
||||
const size_t new_capacity = required + increment - required % increment;
|
||||
if (new_capacity > SIZE_MAX / sizeof(BYTE*))
|
||||
return FALSE;
|
||||
|
||||
uintptr_t* newArray = (uintptr_t*)realloc(queue->array, sizeof(uintptr_t) * new_capacity);
|
||||
|
||||
if (!newArray)
|
||||
return FALSE;
|
||||
|
||||
queue->capacity = new_capacity;
|
||||
queue->array = newArray;
|
||||
ZeroMemory(&(queue->array[old_capacity]),
|
||||
(new_capacity - old_capacity) * sizeof(uintptr_t));
|
||||
|
||||
/* rearrange wrapped entries */
|
||||
if (queue->tail <= queue->head)
|
||||
{
|
||||
const size_t tocopy = queue->tail;
|
||||
const size_t slots = new_capacity - old_capacity;
|
||||
const size_t batch = (tocopy < slots) ? tocopy : slots;
|
||||
|
||||
CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(uintptr_t));
|
||||
|
||||
/* Tail is decremented. if the whole thing is appended
|
||||
* just move the existing tail by old_capacity */
|
||||
if (tocopy < slots)
|
||||
{
|
||||
ZeroMemory(queue->array, batch * sizeof(uintptr_t));
|
||||
queue->tail += old_capacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
const size_t remain = queue->tail - batch;
|
||||
const size_t movesize = remain * sizeof(uintptr_t);
|
||||
memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch],
|
||||
movesize);
|
||||
|
||||
const size_t zerooffset = remain;
|
||||
const size_t zerosize = (queue->tail - remain) * sizeof(uintptr_t);
|
||||
ZeroMemory(&queue->array[zerooffset], zerosize);
|
||||
queue->tail -= batch;
|
||||
}
|
||||
}
|
||||
}
|
||||
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] = void2uptr(queue->object.fnObjectNew(obj));
|
||||
else
|
||||
queue->array[queue->tail] = void2uptr(obj);
|
||||
|
||||
queue->tail = (queue->tail + 1) % queue->capacity;
|
||||
|
||||
{
|
||||
const BOOL signalSet = queue->size == 0;
|
||||
queue->size++;
|
||||
|
||||
if (signalSet)
|
||||
(void)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 = nullptr;
|
||||
|
||||
Queue_Lock(queue);
|
||||
|
||||
if (queue->size > 0)
|
||||
{
|
||||
obj = uptr2void(queue->array[queue->head]);
|
||||
queue->array[queue->head] = 0;
|
||||
queue->head = (queue->head + 1) % queue->capacity;
|
||||
queue->size--;
|
||||
}
|
||||
|
||||
if (queue->size < 1)
|
||||
(void)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 = nullptr;
|
||||
Queue_Lock(queue);
|
||||
|
||||
if (queue->size > 0)
|
||||
obj = uptr2void(queue->array[queue->head]);
|
||||
|
||||
Queue_Unlock(queue);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void Queue_Discard(wQueue* queue)
|
||||
{
|
||||
void* obj = nullptr;
|
||||
|
||||
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)
|
||||
{
|
||||
wQueue* queue = (wQueue*)calloc(1, sizeof(wQueue));
|
||||
|
||||
if (!queue)
|
||||
return nullptr;
|
||||
|
||||
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(nullptr, TRUE, FALSE, nullptr);
|
||||
|
||||
if (!queue->event)
|
||||
goto fail;
|
||||
|
||||
{
|
||||
wObject* 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 nullptr;
|
||||
}
|
||||
|
||||
void Queue_Free(wQueue* queue)
|
||||
{
|
||||
if (!queue)
|
||||
return;
|
||||
|
||||
if (queue->haveLock)
|
||||
{
|
||||
Queue_Clear(queue);
|
||||
DeleteCriticalSection(&queue->lock);
|
||||
}
|
||||
(void)CloseHandle(queue->event);
|
||||
free(queue->array);
|
||||
free(queue);
|
||||
}
|
||||
258
third_party/FreeRDP/winpr/libwinpr/utils/collections/Stack.c
vendored
Normal file
258
third_party/FreeRDP/winpr/libwinpr/utils/collections/Stack.c
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* System.Collections.Stack
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
struct s_wStack
|
||||
{
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
void** array;
|
||||
CRITICAL_SECTION lock;
|
||||
BOOL synchronized;
|
||||
wObject object;
|
||||
};
|
||||
|
||||
/**
|
||||
* C equivalent of the C# Stack Class:
|
||||
* http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx
|
||||
*/
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the number of elements contained in the Stack.
|
||||
*/
|
||||
|
||||
size_t Stack_Count(wStack* stack)
|
||||
{
|
||||
size_t ret = 0;
|
||||
WINPR_ASSERT(stack);
|
||||
if (stack->synchronized)
|
||||
EnterCriticalSection(&stack->lock);
|
||||
|
||||
ret = stack->size;
|
||||
|
||||
if (stack->synchronized)
|
||||
LeaveCriticalSection(&stack->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value indicating whether access to the Stack is synchronized (thread safe).
|
||||
*/
|
||||
|
||||
BOOL Stack_IsSynchronized(wStack* stack)
|
||||
{
|
||||
WINPR_ASSERT(stack);
|
||||
return stack->synchronized;
|
||||
}
|
||||
|
||||
wObject* Stack_Object(wStack* stack)
|
||||
{
|
||||
WINPR_ASSERT(stack);
|
||||
return &stack->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removes all objects from the Stack.
|
||||
*/
|
||||
|
||||
void Stack_Clear(wStack* stack)
|
||||
{
|
||||
WINPR_ASSERT(stack);
|
||||
if (stack->synchronized)
|
||||
EnterCriticalSection(&stack->lock);
|
||||
|
||||
for (size_t index = 0; index < stack->size; index++)
|
||||
{
|
||||
if (stack->object.fnObjectFree)
|
||||
stack->object.fnObjectFree(stack->array[index]);
|
||||
|
||||
stack->array[index] = nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
WINPR_ASSERT(stack->size < SIZE_MAX);
|
||||
if ((stack->size + 1ull) >= stack->capacity)
|
||||
{
|
||||
size_t new_cap = stack->capacity;
|
||||
do
|
||||
{
|
||||
WINPR_ASSERT(new_cap <= SIZE_MAX - 128ull);
|
||||
new_cap += 128ull;
|
||||
} while (new_cap <= stack->size + 1ull);
|
||||
void** new_arr = (void**)realloc((void*)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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
stack = (wStack*)calloc(1, sizeof(wStack));
|
||||
|
||||
if (!stack)
|
||||
return nullptr;
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
void Stack_Free(wStack* stack)
|
||||
{
|
||||
if (stack)
|
||||
{
|
||||
if (stack->synchronized)
|
||||
DeleteCriticalSection(&stack->lock);
|
||||
|
||||
free((void*)stack->array);
|
||||
free(stack);
|
||||
}
|
||||
}
|
||||
531
third_party/FreeRDP/winpr/libwinpr/utils/collections/StreamPool.c
vendored
Normal file
531
third_party/FreeRDP/winpr/libwinpr/utils/collections/StreamPool.c
vendored
Normal file
@@ -0,0 +1,531 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Object Pool
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include "../stream.h"
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("utils.streampool")
|
||||
|
||||
struct s_StreamPoolEntry
|
||||
{
|
||||
#if defined(WITH_STREAMPOOL_DEBUG)
|
||||
char** msg;
|
||||
size_t lines;
|
||||
#endif
|
||||
wStream* s;
|
||||
};
|
||||
|
||||
struct s_wStreamPool
|
||||
{
|
||||
size_t aSize;
|
||||
size_t aCapacity;
|
||||
struct s_StreamPoolEntry* aArray;
|
||||
|
||||
size_t uSize;
|
||||
size_t uCapacity;
|
||||
struct s_StreamPoolEntry* uArray;
|
||||
|
||||
CRITICAL_SECTION lock;
|
||||
BOOL synchronized;
|
||||
size_t defaultSize;
|
||||
};
|
||||
|
||||
static void discard_entry(struct s_StreamPoolEntry* entry, BOOL discardStream)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
#if defined(WITH_STREAMPOOL_DEBUG)
|
||||
free((void*)entry->msg);
|
||||
#endif
|
||||
|
||||
if (discardStream && entry->s)
|
||||
Stream_Free(entry->s, entry->s->isAllocatedStream);
|
||||
|
||||
const struct s_StreamPoolEntry empty = WINPR_C_ARRAY_INIT;
|
||||
*entry = empty;
|
||||
}
|
||||
|
||||
static struct s_StreamPoolEntry add_entry(wStream* s)
|
||||
{
|
||||
struct s_StreamPoolEntry entry = WINPR_C_ARRAY_INIT;
|
||||
|
||||
#if defined(WITH_STREAMPOOL_DEBUG)
|
||||
void* stack = winpr_backtrace(20);
|
||||
if (stack)
|
||||
entry.msg = winpr_backtrace_symbols(stack, &entry.lines);
|
||||
winpr_backtrace_free(stack);
|
||||
#endif
|
||||
|
||||
entry.s = s;
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
WINPR_ASSERT(pool);
|
||||
|
||||
size_t* cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity;
|
||||
size_t* size = (usedOrAvailable) ? &pool->uSize : &pool->aSize;
|
||||
struct s_StreamPoolEntry** array = (usedOrAvailable) ? &pool->uArray : &pool->aArray;
|
||||
|
||||
size_t new_cap = 0;
|
||||
if (*cap == 0)
|
||||
new_cap = *size + count;
|
||||
else if (*size + count > *cap)
|
||||
new_cap = (*size + count + 2) / 2 * 3;
|
||||
else if ((*size + count) < *cap / 3)
|
||||
new_cap = *cap / 2;
|
||||
|
||||
if (new_cap > 0)
|
||||
{
|
||||
struct s_StreamPoolEntry* new_arr = nullptr;
|
||||
|
||||
if (*cap < *size + count)
|
||||
*cap += count;
|
||||
|
||||
new_arr =
|
||||
(struct s_StreamPoolEntry*)realloc(*array, sizeof(struct s_StreamPoolEntry) * 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)
|
||||
{
|
||||
WINPR_ASSERT(pool);
|
||||
|
||||
const size_t pcount = 1;
|
||||
const size_t off = index + pcount;
|
||||
if (pool->uSize >= off)
|
||||
{
|
||||
for (size_t x = 0; x < pcount; x++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->uArray[index + x];
|
||||
discard_entry(cur, FALSE);
|
||||
}
|
||||
MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount],
|
||||
(pool->uSize - index - pcount) * sizeof(struct s_StreamPoolEntry));
|
||||
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] = add_entry(s);
|
||||
pool->uSize++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->uArray[index];
|
||||
if (cur->s == s)
|
||||
{
|
||||
StreamPool_ShiftUsed(pool, index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index)
|
||||
{
|
||||
WINPR_ASSERT(pool);
|
||||
|
||||
const size_t pcount = 1;
|
||||
const size_t off = index + pcount;
|
||||
if (pool->aSize >= off)
|
||||
{
|
||||
for (size_t x = 0; x < pcount; x++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->aArray[index + x];
|
||||
discard_entry(cur, FALSE);
|
||||
}
|
||||
|
||||
MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
|
||||
(pool->aSize - index - pcount) * sizeof(struct s_StreamPoolEntry));
|
||||
pool->aSize -= pcount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream from the pool.
|
||||
*/
|
||||
|
||||
wStream* StreamPool_Take(wStreamPool* pool, size_t size)
|
||||
{
|
||||
BOOL found = FALSE;
|
||||
size_t foundIndex = 0;
|
||||
wStream* s = nullptr;
|
||||
|
||||
StreamPool_Lock(pool);
|
||||
|
||||
if (size == 0)
|
||||
size = pool->defaultSize;
|
||||
|
||||
for (size_t index = 0; index < pool->aSize; index++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->aArray[index];
|
||||
s = cur->s;
|
||||
|
||||
if (Stream_Capacity(s) >= size)
|
||||
{
|
||||
found = TRUE;
|
||||
foundIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
s = Stream_New(nullptr, size);
|
||||
if (!s)
|
||||
goto out_fail;
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
Stream_ResetPosition(s);
|
||||
Stream_SetLength(s, Stream_Capacity(s));
|
||||
StreamPool_ShiftAvailable(pool, foundIndex);
|
||||
}
|
||||
|
||||
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].s;
|
||||
if (cs == s)
|
||||
return;
|
||||
}
|
||||
pool->aArray[(pool->aSize)++] = add_entry(s);
|
||||
StreamPool_RemoveUsed(pool, s);
|
||||
}
|
||||
|
||||
static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
|
||||
{
|
||||
StreamPool_Lock(pool);
|
||||
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);
|
||||
s->count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement stream reference count
|
||||
*/
|
||||
|
||||
void Stream_Release(wStream* s)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
|
||||
if (s->count > 0)
|
||||
s->count--;
|
||||
if (s->count == 0)
|
||||
{
|
||||
if (s->pool)
|
||||
StreamPool_ReleaseOrReturn(s->pool, s);
|
||||
else
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find stream in pool using pointer inside buffer
|
||||
*/
|
||||
|
||||
wStream* StreamPool_Find(wStreamPool* pool, const BYTE* ptr)
|
||||
{
|
||||
wStream* s = nullptr;
|
||||
|
||||
StreamPool_Lock(pool);
|
||||
|
||||
for (size_t index = 0; index < pool->uSize; index++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->uArray[index];
|
||||
|
||||
if ((ptr >= Stream_Buffer(cur->s)) &&
|
||||
(ptr < (Stream_Buffer(cur->s) + Stream_Capacity(cur->s))))
|
||||
{
|
||||
s = cur->s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StreamPool_Unlock(pool);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the streams currently cached in the pool.
|
||||
*/
|
||||
|
||||
void StreamPool_Clear(wStreamPool* pool)
|
||||
{
|
||||
StreamPool_Lock(pool);
|
||||
|
||||
for (size_t x = 0; x < pool->aSize; x++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->aArray[x];
|
||||
discard_entry(cur, TRUE);
|
||||
}
|
||||
pool->aSize = 0;
|
||||
|
||||
if (pool->uSize > 0)
|
||||
{
|
||||
WLog_WARN(TAG, "Clearing StreamPool, but there are %" PRIuz " streams currently in use",
|
||||
pool->uSize);
|
||||
for (size_t x = 0; x < pool->uSize; x++)
|
||||
{
|
||||
struct s_StreamPoolEntry* cur = &pool->uArray[x];
|
||||
discard_entry(cur, TRUE);
|
||||
}
|
||||
pool->uSize = 0;
|
||||
}
|
||||
|
||||
StreamPool_Unlock(pool);
|
||||
}
|
||||
|
||||
size_t StreamPool_UsedCount(wStreamPool* pool)
|
||||
{
|
||||
StreamPool_Lock(pool);
|
||||
size_t usize = pool->uSize;
|
||||
StreamPool_Unlock(pool);
|
||||
return usize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construction, Destruction
|
||||
*/
|
||||
|
||||
wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
|
||||
{
|
||||
wStreamPool* pool = nullptr;
|
||||
|
||||
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;
|
||||
|
||||
if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return pool;
|
||||
fail:
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
|
||||
StreamPool_Free(pool);
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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 nullptr;
|
||||
|
||||
size_t used = 0;
|
||||
int offset = _snprintf(buffer, size - 1,
|
||||
"aSize =%" PRIuz ", uSize =%" PRIuz ", aCapacity=%" PRIuz
|
||||
", uCapacity=%" PRIuz,
|
||||
pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity);
|
||||
if ((offset > 0) && ((size_t)offset < size))
|
||||
used += (size_t)offset;
|
||||
|
||||
#if defined(WITH_STREAMPOOL_DEBUG)
|
||||
StreamPool_Lock(pool);
|
||||
|
||||
offset = _snprintf(&buffer[used], size - 1 - used, "\n-- dump used array take locations --\n");
|
||||
if ((offset > 0) && ((size_t)offset < size - used))
|
||||
used += (size_t)offset;
|
||||
for (size_t x = 0; x < pool->uSize; x++)
|
||||
{
|
||||
const struct s_StreamPoolEntry* cur = &pool->uArray[x];
|
||||
WINPR_ASSERT(cur->msg || (cur->lines == 0));
|
||||
|
||||
for (size_t y = 0; y < cur->lines; y++)
|
||||
{
|
||||
offset = _snprintf(&buffer[used], size - 1 - used, "[%" PRIuz " | %" PRIuz "]: %s\n", x,
|
||||
y, cur->msg[y]);
|
||||
if ((offset > 0) && ((size_t)offset < size - used))
|
||||
used += (size_t)offset;
|
||||
}
|
||||
}
|
||||
|
||||
offset = _snprintf(&buffer[used], size - 1 - used, "\n-- statistics called from --\n");
|
||||
if ((offset > 0) && ((size_t)offset < size - used))
|
||||
used += (size_t)offset;
|
||||
|
||||
struct s_StreamPoolEntry entry = WINPR_C_ARRAY_INIT;
|
||||
void* stack = winpr_backtrace(20);
|
||||
if (stack)
|
||||
entry.msg = winpr_backtrace_symbols(stack, &entry.lines);
|
||||
winpr_backtrace_free(stack);
|
||||
|
||||
for (size_t x = 0; x < entry.lines; x++)
|
||||
{
|
||||
const char* msg = entry.msg[x];
|
||||
offset = _snprintf(&buffer[used], size - 1 - used, "[%" PRIuz "]: %s\n", x, msg);
|
||||
if ((offset > 0) && ((size_t)offset < size - used))
|
||||
used += (size_t)offset;
|
||||
}
|
||||
free((void*)entry.msg);
|
||||
StreamPool_Unlock(pool);
|
||||
#endif
|
||||
buffer[used] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
BOOL StreamPool_WaitForReturn(wStreamPool* pool, UINT32 timeoutMS)
|
||||
{
|
||||
wLog* log = WLog_Get(TAG);
|
||||
|
||||
/* HACK: We disconnected the transport above, now wait without a read or write lock until all
|
||||
* streams in use have been returned to the pool. */
|
||||
while (timeoutMS > 0)
|
||||
{
|
||||
const size_t used = StreamPool_UsedCount(pool);
|
||||
if (used == 0)
|
||||
return TRUE;
|
||||
WLog_Print(log, WLOG_DEBUG, "%" PRIuz " streams still in use, sleeping...", used);
|
||||
|
||||
char buffer[4096] = WINPR_C_ARRAY_INIT;
|
||||
StreamPool_GetStatistics(pool, buffer, sizeof(buffer));
|
||||
WLog_Print(log, WLOG_TRACE, "Pool statistics: %s", buffer);
|
||||
|
||||
UINT32 diff = 10;
|
||||
if (timeoutMS != INFINITE)
|
||||
{
|
||||
diff = timeoutMS > 10 ? 10 : timeoutMS;
|
||||
timeoutMS -= diff;
|
||||
}
|
||||
Sleep(diff);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
Reference in New Issue
Block a user