Coverage Report

Created: 2026-02-26 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/utils/collections/Queue.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * System.Collections.Queue
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <winpr/crt.h>
23
#include <winpr/assert.h>
24
25
#include <winpr/collections.h>
26
27
struct s_wQueue
28
{
29
  size_t capacity;
30
  size_t growthFactor;
31
  BOOL synchronized;
32
33
  BYTE padding[4];
34
35
  size_t head;
36
  size_t tail;
37
  size_t size;
38
  uintptr_t* array;
39
  CRITICAL_SECTION lock;
40
  HANDLE event;
41
42
  wObject object;
43
  BOOL haveLock;
44
45
  BYTE padding2[4];
46
};
47
48
static inline void* uptr2void(uintptr_t ptr)
49
0
{
50
0
  return (void*)ptr;
51
0
}
52
53
static inline uintptr_t void2uptr(const void* ptr)
54
0
{
55
0
  return (uintptr_t)ptr;
56
0
}
57
58
/**
59
 * C equivalent of the C# Queue Class:
60
 * http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
61
 */
62
63
/**
64
 * Properties
65
 */
66
67
/**
68
 * Gets the number of elements contained in the Queue.
69
 */
70
71
size_t Queue_Count(wQueue* queue)
72
0
{
73
0
  size_t ret = 0;
74
75
0
  Queue_Lock(queue);
76
77
0
  ret = queue->size;
78
79
0
  Queue_Unlock(queue);
80
81
0
  return ret;
82
0
}
83
84
size_t Queue_Capacity(wQueue* queue)
85
0
{
86
0
  WINPR_ASSERT(queue);
87
88
0
  Queue_Lock(queue);
89
90
0
  const size_t ret = queue->capacity;
91
92
0
  Queue_Unlock(queue);
93
94
0
  return ret;
95
0
}
96
97
/**
98
 * Lock access to the ArrayList
99
 */
100
101
void Queue_Lock(wQueue* queue)
102
0
{
103
0
  WINPR_ASSERT(queue);
104
0
  if (queue->synchronized)
105
0
    EnterCriticalSection(&queue->lock);
106
0
}
107
108
/**
109
 * Unlock access to the ArrayList
110
 */
111
112
void Queue_Unlock(wQueue* queue)
113
0
{
114
0
  WINPR_ASSERT(queue);
115
0
  if (queue->synchronized)
116
0
    LeaveCriticalSection(&queue->lock);
117
0
}
118
119
/**
120
 * Gets an event which is set when the queue is non-empty
121
 */
122
123
HANDLE Queue_Event(wQueue* queue)
124
0
{
125
0
  WINPR_ASSERT(queue);
126
0
  return queue->event;
127
0
}
128
129
wObject* Queue_Object(wQueue* queue)
130
0
{
131
0
  WINPR_ASSERT(queue);
132
0
  return &queue->object;
133
0
}
134
135
/**
136
 * Methods
137
 */
138
139
/**
140
 * Removes all objects from the Queue.
141
 */
142
143
void Queue_Clear(wQueue* queue)
144
0
{
145
0
  Queue_Lock(queue);
146
147
0
  for (size_t index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity)
148
0
  {
149
0
    if (queue->object.fnObjectFree)
150
0
    {
151
0
      void* obj = uptr2void(queue->array[index]);
152
0
      queue->object.fnObjectFree(obj);
153
0
    }
154
155
0
    queue->array[index] = 0;
156
0
  }
157
158
0
  queue->size = 0;
159
0
  queue->head = queue->tail = 0;
160
0
  (void)ResetEvent(queue->event);
161
0
  Queue_Unlock(queue);
162
0
}
163
164
/**
165
 * Determines whether an element is in the Queue.
166
 */
167
168
BOOL Queue_Contains(wQueue* queue, const void* obj)
169
0
{
170
0
  BOOL found = FALSE;
171
172
0
  Queue_Lock(queue);
173
174
0
  for (size_t index = 0; index < queue->tail; index++)
175
0
  {
176
0
    void* ptr = uptr2void(queue->array[index]);
177
0
    if (queue->object.fnObjectEquals(ptr, obj))
178
0
    {
179
0
      found = TRUE;
180
0
      break;
181
0
    }
182
0
  }
183
184
0
  Queue_Unlock(queue);
185
186
0
  return found;
187
0
}
188
189
static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count)
190
0
{
191
0
  const size_t blocksize = 32ull;
192
0
  WINPR_ASSERT(queue);
193
194
0
  if (queue->growthFactor > SIZE_MAX / blocksize)
195
0
    return FALSE;
196
197
0
  const size_t increment = blocksize * queue->growthFactor;
198
0
  if (queue->size > SIZE_MAX - count)
199
0
    return FALSE;
200
201
0
  const size_t required = queue->size + count;
202
0
  if (required > queue->capacity)
203
0
  {
204
0
    const size_t old_capacity = queue->capacity;
205
0
    if (required > SIZE_MAX - increment)
206
0
      return FALSE;
207
208
0
    const size_t new_capacity = required + increment - required % increment;
209
0
    if (new_capacity > SIZE_MAX / sizeof(BYTE*))
210
0
      return FALSE;
211
212
0
    uintptr_t* newArray = (uintptr_t*)realloc(queue->array, sizeof(uintptr_t) * new_capacity);
213
214
0
    if (!newArray)
215
0
      return FALSE;
216
217
0
    queue->capacity = new_capacity;
218
0
    queue->array = newArray;
219
0
    ZeroMemory(&(queue->array[old_capacity]),
220
0
               (new_capacity - old_capacity) * sizeof(uintptr_t));
221
222
    /* rearrange wrapped entries */
223
0
    if (queue->tail <= queue->head)
224
0
    {
225
0
      const size_t tocopy = queue->tail;
226
0
      const size_t slots = new_capacity - old_capacity;
227
0
      const size_t batch = (tocopy < slots) ? tocopy : slots;
228
229
0
      CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(uintptr_t));
230
231
      /* Tail is decremented. if the whole thing is appended
232
       * just move the existing tail by old_capacity */
233
0
      if (tocopy < slots)
234
0
      {
235
0
        ZeroMemory(queue->array, batch * sizeof(uintptr_t));
236
0
        queue->tail += old_capacity;
237
0
      }
238
0
      else
239
0
      {
240
0
        const size_t remain = queue->tail - batch;
241
0
        const size_t movesize = remain * sizeof(uintptr_t);
242
0
        memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch],
243
0
                  movesize);
244
245
0
        const size_t zerooffset = remain;
246
0
        const size_t zerosize = (queue->tail - remain) * sizeof(uintptr_t);
247
0
        ZeroMemory(&queue->array[zerooffset], zerosize);
248
0
        queue->tail -= batch;
249
0
      }
250
0
    }
251
0
  }
252
0
  return TRUE;
253
0
}
254
255
/**
256
 * Adds an object to the end of the Queue.
257
 */
258
259
BOOL Queue_Enqueue(wQueue* queue, const void* obj)
260
0
{
261
0
  BOOL ret = TRUE;
262
263
0
  Queue_Lock(queue);
264
265
0
  if (!Queue_EnsureCapacity(queue, 1))
266
0
    goto out;
267
268
0
  if (queue->object.fnObjectNew)
269
0
    queue->array[queue->tail] = void2uptr(queue->object.fnObjectNew(obj));
270
0
  else
271
0
    queue->array[queue->tail] = void2uptr(obj);
272
273
0
  queue->tail = (queue->tail + 1) % queue->capacity;
274
275
0
  {
276
0
    const BOOL signalSet = queue->size == 0;
277
0
    queue->size++;
278
279
0
    if (signalSet)
280
0
      (void)SetEvent(queue->event);
281
0
  }
282
0
out:
283
284
0
  Queue_Unlock(queue);
285
286
0
  return ret;
287
0
}
288
289
/**
290
 * Removes and returns the object at the beginning of the Queue.
291
 */
292
293
void* Queue_Dequeue(wQueue* queue)
294
0
{
295
0
  void* obj = NULL;
296
297
0
  Queue_Lock(queue);
298
299
0
  if (queue->size > 0)
300
0
  {
301
0
    obj = uptr2void(queue->array[queue->head]);
302
0
    queue->array[queue->head] = 0;
303
0
    queue->head = (queue->head + 1) % queue->capacity;
304
0
    queue->size--;
305
0
  }
306
307
0
  if (queue->size < 1)
308
0
    (void)ResetEvent(queue->event);
309
310
0
  Queue_Unlock(queue);
311
312
0
  return obj;
313
0
}
314
315
/**
316
 * Returns the object at the beginning of the Queue without removing it.
317
 */
318
319
void* Queue_Peek(wQueue* queue)
320
0
{
321
0
  void* obj = NULL;
322
0
  Queue_Lock(queue);
323
324
0
  if (queue->size > 0)
325
0
    obj = uptr2void(queue->array[queue->head]);
326
327
0
  Queue_Unlock(queue);
328
329
0
  return obj;
330
0
}
331
332
void Queue_Discard(wQueue* queue)
333
0
{
334
0
  void* obj = NULL;
335
336
0
  Queue_Lock(queue);
337
0
  obj = Queue_Dequeue(queue);
338
339
0
  if (queue->object.fnObjectFree)
340
0
    queue->object.fnObjectFree(obj);
341
0
  Queue_Unlock(queue);
342
0
}
343
344
static BOOL default_queue_equals(const void* obj1, const void* obj2)
345
0
{
346
0
  return (obj1 == obj2);
347
0
}
348
349
/**
350
 * Construction, Destruction
351
 */
352
353
wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
354
0
{
355
0
  wQueue* queue = (wQueue*)calloc(1, sizeof(wQueue));
356
357
0
  if (!queue)
358
0
    return NULL;
359
360
0
  queue->synchronized = synchronized;
361
362
0
  queue->growthFactor = 2;
363
0
  if (growthFactor > 0)
364
0
    queue->growthFactor = (size_t)growthFactor;
365
366
0
  if (capacity <= 0)
367
0
    capacity = 32;
368
0
  if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
369
0
    goto fail;
370
0
  queue->haveLock = TRUE;
371
0
  if (!Queue_EnsureCapacity(queue, (size_t)capacity))
372
0
    goto fail;
373
374
0
  queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
375
376
0
  if (!queue->event)
377
0
    goto fail;
378
379
0
  {
380
0
    wObject* obj = Queue_Object(queue);
381
0
    obj->fnObjectEquals = default_queue_equals;
382
0
  }
383
0
  return queue;
384
0
fail:
385
0
  WINPR_PRAGMA_DIAG_PUSH
386
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
387
0
  Queue_Free(queue);
388
0
  WINPR_PRAGMA_DIAG_POP
389
0
  return NULL;
390
0
}
391
392
void Queue_Free(wQueue* queue)
393
0
{
394
0
  if (!queue)
395
0
    return;
396
397
0
  if (queue->haveLock)
398
0
  {
399
0
    Queue_Clear(queue);
400
0
    DeleteCriticalSection(&queue->lock);
401
0
  }
402
0
  (void)CloseHandle(queue->event);
403
0
  free(queue->array);
404
0
  free(queue);
405
0
}