diff --git a/winpr/include/winpr/collections.h b/winpr/include/winpr/collections.h index 7d5edf1bf..3fc942f37 100644 --- a/winpr/include/winpr/collections.h +++ b/winpr/include/winpr/collections.h @@ -181,6 +181,14 @@ extern "C" WINPR_API void Queue_Free(wQueue* queue); /** @brief Creates a new queue + * + * @param synchronized If \b TRUE all functions are thread safe, if \b FALSE no synchronization + * is done. + * @param capacity The initial capacity of the queue. If \b 0 or \b -1 default settings are + * applied. + * @param growthFactor allocation behaviour when the queue capacity should be increased. Larger + * values increase the allocation contingent. \b 0 or \b -1 apply default settings. + * * * @return A newly allocated queue or \b NULL in case of failure */ diff --git a/winpr/libwinpr/utils/collections/Queue.c b/winpr/libwinpr/utils/collections/Queue.c index 44b9873e2..cad43f860 100644 --- a/winpr/libwinpr/utils/collections/Queue.c +++ b/winpr/libwinpr/utils/collections/Queue.c @@ -35,7 +35,7 @@ struct s_wQueue size_t head; size_t tail; size_t size; - void** array; + uintptr_t* array; CRITICAL_SECTION lock; HANDLE event; @@ -45,6 +45,16 @@ struct s_wQueue 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 @@ -124,9 +134,12 @@ void Queue_Clear(wQueue* queue) for (size_t index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity) { if (queue->object.fnObjectFree) - queue->object.fnObjectFree(queue->array[index]); + { + void* obj = uptr2void(queue->array[index]); + queue->object.fnObjectFree(obj); + } - queue->array[index] = NULL; + queue->array[index] = 0; } queue->size = 0; @@ -147,7 +160,8 @@ BOOL Queue_Contains(wQueue* queue, const void* obj) for (size_t index = 0; index < queue->tail; index++) { - if (queue->object.fnObjectEquals(queue->array[index], obj)) + void* ptr = uptr2void(queue->array[index]); + if (queue->object.fnObjectEquals(ptr, obj)) { found = TRUE; break; @@ -163,29 +177,52 @@ static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count) { WINPR_ASSERT(queue); - if (queue->size + count >= queue->capacity) + if (queue->size + count > queue->capacity) { + if (queue->growthFactor > SIZE_MAX / 32ull) + return FALSE; + if (queue->size > SIZE_MAX - count) + return FALSE; + + const size_t increment = 32ull * queue->growthFactor; const size_t old_capacity = queue->capacity; - size_t new_capacity = queue->capacity * queue->growthFactor; - void** newArray = NULL; - if (new_capacity < queue->size + count) - new_capacity = queue->size + count; - newArray = (void**)realloc((void*)queue->array, sizeof(void*) * new_capacity); + const size_t required = queue->size + count; + 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((void*)&(queue->array[old_capacity]), - (new_capacity - old_capacity) * sizeof(void*)); + ZeroMemory(&(queue->array[old_capacity]), + (new_capacity - old_capacity) * sizeof(uintptr_t)); /* rearrange wrapped entries */ if (queue->tail <= queue->head) { - CopyMemory((void*)&(queue->array[old_capacity]), (void*)queue->array, - queue->tail * sizeof(void*)); - queue->tail += old_capacity; + 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)); + ZeroMemory(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) + queue->tail += old_capacity; + else + { + const size_t movesize = (queue->tail - batch) * sizeof(uintptr_t); + memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch], + movesize); + ZeroMemory(&queue->array[batch], movesize); + queue->tail -= batch; + } } } return TRUE; @@ -205,17 +242,10 @@ BOOL Queue_Enqueue(wQueue* queue, const void* obj) goto out; if (queue->object.fnObjectNew) - queue->array[queue->tail] = queue->object.fnObjectNew(obj); + queue->array[queue->tail] = void2uptr(queue->object.fnObjectNew(obj)); else - { - union - { - const void* cv; - void* v; - } cnv; - cnv.cv = obj; - queue->array[queue->tail] = cnv.v; - } + queue->array[queue->tail] = void2uptr(obj); + queue->tail = (queue->tail + 1) % queue->capacity; { @@ -244,8 +274,8 @@ void* Queue_Dequeue(wQueue* queue) if (queue->size > 0) { - obj = queue->array[queue->head]; - queue->array[queue->head] = NULL; + obj = uptr2void(queue->array[queue->head]); + queue->array[queue->head] = 0; queue->head = (queue->head + 1) % queue->capacity; queue->size--; } @@ -265,11 +295,10 @@ void* Queue_Dequeue(wQueue* queue) void* Queue_Peek(wQueue* queue) { void* obj = NULL; - Queue_Lock(queue); if (queue->size > 0) - obj = queue->array[queue->head]; + obj = uptr2void(queue->array[queue->head]); Queue_Unlock(queue); @@ -299,9 +328,7 @@ static BOOL default_queue_equals(const void* obj1, const void* obj2) wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor) { - wObject* obj = NULL; - wQueue* queue = NULL; - queue = (wQueue*)calloc(1, sizeof(wQueue)); + wQueue* queue = (wQueue*)calloc(1, sizeof(wQueue)); if (!queue) return NULL; @@ -325,9 +352,10 @@ wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor) if (!queue->event) goto fail; - obj = Queue_Object(queue); - obj->fnObjectEquals = default_queue_equals; - + { + wObject* obj = Queue_Object(queue); + obj->fnObjectEquals = default_queue_equals; + } return queue; fail: WINPR_PRAGMA_DIAG_PUSH @@ -348,6 +376,6 @@ void Queue_Free(wQueue* queue) DeleteCriticalSection(&queue->lock); } (void)CloseHandle(queue->event); - free((void*)queue->array); + free(queue->array); free(queue); }