Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View 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);
}

View 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);
}

View 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);
}
}

View 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);
}

View 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;
}

View 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;
}

View 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);
}
}

View 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);
}
}

View 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;
}

View 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);
}

View 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);
}

View 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);
}
}

View 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);
}

View 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);
}
}

View 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;
}