Coverage Report

Created: 2026-03-04 06:14

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
  BOOL res = TRUE;
192
0
  const size_t blocksize = 32ull;
193
0
  WINPR_ASSERT(queue);
194
195
0
  if (queue->growthFactor > SIZE_MAX / blocksize)
196
0
    return FALSE;
197
198
0
  const size_t increment = blocksize * queue->growthFactor;
199
0
  if (queue->size > SIZE_MAX - count)
200
0
    return FALSE;
201
202
0
  const size_t required = queue->size + count;
203
0
  if (required > queue->capacity)
204
0
  {
205
0
    const size_t old_capacity = queue->capacity;
206
0
    if (required > SIZE_MAX - increment)
207
0
      return FALSE;
208
209
0
    const size_t new_capacity = required + increment - required % increment;
210
0
    if (new_capacity > SIZE_MAX / sizeof(BYTE*))
211
0
      return FALSE;
212
213
0
    uintptr_t* newArray = (uintptr_t*)realloc(queue->array, sizeof(uintptr_t) * new_capacity);
214
215
0
    if (!newArray)
216
0
      return FALSE;
217
218
0
    queue->capacity = new_capacity;
219
0
    queue->array = newArray;
220
0
    ZeroMemory(&(queue->array[old_capacity]),
221
0
               (new_capacity - old_capacity) * sizeof(uintptr_t));
222
223
    /* rearrange wrapped entries */
224
0
    if (queue->tail <= queue->head)
225
0
    {
226
0
      const size_t tocopy = queue->tail;
227
0
      const size_t slots = new_capacity - old_capacity;
228
0
      const size_t batch = (tocopy < slots) ? tocopy : slots;
229
230
0
      CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(uintptr_t));
231
232
      /* Tail is decremented. if the whole thing is appended
233
       * just move the existing tail by old_capacity */
234
0
      if (tocopy < slots)
235
0
      {
236
0
        ZeroMemory(queue->array, batch * sizeof(uintptr_t));
237
0
        queue->tail += old_capacity;
238
0
      }
239
0
      else
240
0
      {
241
0
        const size_t remain = queue->tail - batch;
242
0
        const size_t movesize = remain * sizeof(uintptr_t);
243
0
        res = memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch],
244
0
                        movesize) >= 0;
245
246
0
        const size_t zerooffset = remain;
247
0
        const size_t zerosize = (queue->tail - remain) * sizeof(uintptr_t);
248
0
        ZeroMemory(&queue->array[zerooffset], zerosize);
249
0
        queue->tail -= batch;
250
0
      }
251
0
    }
252
0
  }
253
0
  return res;
254
0
}
255
256
/**
257
 * Adds an object to the end of the Queue.
258
 */
259
260
BOOL Queue_Enqueue(wQueue* queue, const void* obj)
261
0
{
262
0
  BOOL ret = TRUE;
263
264
0
  Queue_Lock(queue);
265
266
0
  if (!Queue_EnsureCapacity(queue, 1))
267
0
    goto out;
268
269
0
  if (queue->object.fnObjectNew)
270
0
    queue->array[queue->tail] = void2uptr(queue->object.fnObjectNew(obj));
271
0
  else
272
0
    queue->array[queue->tail] = void2uptr(obj);
273
274
0
  queue->tail = (queue->tail + 1) % queue->capacity;
275
276
0
  {
277
0
    const BOOL signalSet = queue->size == 0;
278
0
    queue->size++;
279
280
0
    if (signalSet)
281
0
    {
282
0
      if (!SetEvent(queue->event))
283
0
        goto out;
284
0
    }
285
0
  }
286
0
out:
287
288
0
  Queue_Unlock(queue);
289
290
0
  return ret;
291
0
}
292
293
/**
294
 * Removes and returns the object at the beginning of the Queue.
295
 */
296
297
void* Queue_Dequeue(wQueue* queue)
298
0
{
299
0
  void* obj = nullptr;
300
301
0
  Queue_Lock(queue);
302
303
0
  if (queue->size > 0)
304
0
  {
305
0
    obj = uptr2void(queue->array[queue->head]);
306
0
    queue->array[queue->head] = 0;
307
0
    queue->head = (queue->head + 1) % queue->capacity;
308
0
    queue->size--;
309
0
  }
310
311
0
  if (queue->size < 1)
312
0
    (void)ResetEvent(queue->event);
313
314
0
  Queue_Unlock(queue);
315
316
0
  return obj;
317
0
}
318
319
/**
320
 * Returns the object at the beginning of the Queue without removing it.
321
 */
322
323
void* Queue_Peek(wQueue* queue)
324
0
{
325
0
  void* obj = nullptr;
326
0
  Queue_Lock(queue);
327
328
0
  if (queue->size > 0)
329
0
    obj = uptr2void(queue->array[queue->head]);
330
331
0
  Queue_Unlock(queue);
332
333
0
  return obj;
334
0
}
335
336
void Queue_Discard(wQueue* queue)
337
0
{
338
0
  void* obj = nullptr;
339
340
0
  Queue_Lock(queue);
341
0
  obj = Queue_Dequeue(queue);
342
343
0
  if (queue->object.fnObjectFree)
344
0
    queue->object.fnObjectFree(obj);
345
0
  Queue_Unlock(queue);
346
0
}
347
348
static BOOL default_queue_equals(const void* obj1, const void* obj2)
349
0
{
350
0
  return (obj1 == obj2);
351
0
}
352
353
/**
354
 * Construction, Destruction
355
 */
356
357
wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
358
0
{
359
0
  wQueue* queue = (wQueue*)calloc(1, sizeof(wQueue));
360
361
0
  if (!queue)
362
0
    return nullptr;
363
364
0
  queue->synchronized = synchronized;
365
366
0
  queue->growthFactor = 2;
367
0
  if (growthFactor > 0)
368
0
    queue->growthFactor = (size_t)growthFactor;
369
370
0
  if (capacity <= 0)
371
0
    capacity = 32;
372
0
  if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
373
0
    goto fail;
374
0
  queue->haveLock = TRUE;
375
0
  if (!Queue_EnsureCapacity(queue, (size_t)capacity))
376
0
    goto fail;
377
378
0
  queue->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
379
380
0
  if (!queue->event)
381
0
    goto fail;
382
383
0
  {
384
0
    wObject* obj = Queue_Object(queue);
385
0
    obj->fnObjectEquals = default_queue_equals;
386
0
  }
387
0
  return queue;
388
0
fail:
389
0
  WINPR_PRAGMA_DIAG_PUSH
390
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
391
0
  Queue_Free(queue);
392
0
  WINPR_PRAGMA_DIAG_POP
393
0
  return nullptr;
394
0
}
395
396
void Queue_Free(wQueue* queue)
397
0
{
398
0
  if (!queue)
399
0
    return;
400
401
0
  if (queue->haveLock)
402
0
  {
403
0
    Queue_Clear(queue);
404
0
    DeleteCriticalSection(&queue->lock);
405
0
  }
406
0
  (void)CloseHandle(queue->event);
407
0
  free(queue->array);
408
0
  free(queue);
409
0
}