Coverage Report

Created: 2023-09-25 06:56

/src/FreeRDP/winpr/libwinpr/utils/collections/Queue.c
Line
Count
Source (jump to first uncovered line)
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
  void** array;
39
  CRITICAL_SECTION lock;
40
  HANDLE event;
41
42
  wObject object;
43
  BOOL haveLock;
44
45
  BYTE padding2[4];
46
};
47
48
/**
49
 * C equivalent of the C# Queue Class:
50
 * http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
51
 */
52
53
/**
54
 * Properties
55
 */
56
57
/**
58
 * Gets the number of elements contained in the Queue.
59
 */
60
61
size_t Queue_Count(wQueue* queue)
62
0
{
63
0
  size_t ret;
64
65
0
  Queue_Lock(queue);
66
67
0
  ret = queue->size;
68
69
0
  Queue_Unlock(queue);
70
71
0
  return ret;
72
0
}
73
74
/**
75
 * Lock access to the ArrayList
76
 */
77
78
void Queue_Lock(wQueue* queue)
79
0
{
80
0
  WINPR_ASSERT(queue);
81
0
  if (queue->synchronized)
82
0
    EnterCriticalSection(&queue->lock);
83
0
}
84
85
/**
86
 * Unlock access to the ArrayList
87
 */
88
89
void Queue_Unlock(wQueue* queue)
90
0
{
91
0
  WINPR_ASSERT(queue);
92
0
  if (queue->synchronized)
93
0
    LeaveCriticalSection(&queue->lock);
94
0
}
95
96
/**
97
 * Gets an event which is set when the queue is non-empty
98
 */
99
100
HANDLE Queue_Event(wQueue* queue)
101
0
{
102
0
  WINPR_ASSERT(queue);
103
0
  return queue->event;
104
0
}
105
106
wObject* Queue_Object(wQueue* queue)
107
0
{
108
0
  WINPR_ASSERT(queue);
109
0
  return &queue->object;
110
0
}
111
112
/**
113
 * Methods
114
 */
115
116
/**
117
 * Removes all objects from the Queue.
118
 */
119
120
void Queue_Clear(wQueue* queue)
121
0
{
122
0
  size_t index;
123
124
0
  Queue_Lock(queue);
125
126
0
  for (index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity)
127
0
  {
128
0
    if (queue->object.fnObjectFree)
129
0
      queue->object.fnObjectFree(queue->array[index]);
130
131
0
    queue->array[index] = NULL;
132
0
  }
133
134
0
  queue->size = 0;
135
0
  queue->head = queue->tail = 0;
136
0
  ResetEvent(queue->event);
137
0
  Queue_Unlock(queue);
138
0
}
139
140
/**
141
 * Determines whether an element is in the Queue.
142
 */
143
144
BOOL Queue_Contains(wQueue* queue, const void* obj)
145
0
{
146
0
  size_t index;
147
0
  BOOL found = FALSE;
148
149
0
  Queue_Lock(queue);
150
151
0
  for (index = 0; index < queue->tail; index++)
152
0
  {
153
0
    if (queue->object.fnObjectEquals(queue->array[index], obj))
154
0
    {
155
0
      found = TRUE;
156
0
      break;
157
0
    }
158
0
  }
159
160
0
  Queue_Unlock(queue);
161
162
0
  return found;
163
0
}
164
165
static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count)
166
0
{
167
0
  WINPR_ASSERT(queue);
168
169
0
  if (queue->size + count >= queue->capacity)
170
0
  {
171
0
    const size_t old_capacity = queue->capacity;
172
0
    size_t new_capacity = queue->capacity * queue->growthFactor;
173
0
    void** newArray;
174
0
    if (new_capacity < queue->size + count)
175
0
      new_capacity = queue->size + count;
176
0
    newArray = (void**)realloc(queue->array, sizeof(void*) * new_capacity);
177
178
0
    if (!newArray)
179
0
      return FALSE;
180
181
0
    queue->capacity = new_capacity;
182
0
    queue->array = newArray;
183
0
    ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(void*));
184
185
    /* rearrange wrapped entries */
186
0
    if (queue->tail <= queue->head)
187
0
    {
188
0
      CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(void*));
189
0
      queue->tail += old_capacity;
190
0
    }
191
0
  }
192
0
  return TRUE;
193
0
}
194
195
/**
196
 * Adds an object to the end of the Queue.
197
 */
198
199
BOOL Queue_Enqueue(wQueue* queue, const void* obj)
200
0
{
201
0
  BOOL ret = TRUE;
202
203
0
  Queue_Lock(queue);
204
205
0
  if (!Queue_EnsureCapacity(queue, 1))
206
0
    goto out;
207
208
0
  if (queue->object.fnObjectNew)
209
0
    queue->array[queue->tail] = queue->object.fnObjectNew(obj);
210
0
  else
211
0
  {
212
0
    union
213
0
    {
214
0
      const void* cv;
215
0
      void* v;
216
0
    } cnv;
217
0
    cnv.cv = obj;
218
0
    queue->array[queue->tail] = cnv.v;
219
0
  }
220
0
  queue->tail = (queue->tail + 1) % queue->capacity;
221
0
  queue->size++;
222
0
  SetEvent(queue->event);
223
0
out:
224
225
0
  Queue_Unlock(queue);
226
227
0
  return ret;
228
0
}
229
230
/**
231
 * Removes and returns the object at the beginning of the Queue.
232
 */
233
234
void* Queue_Dequeue(wQueue* queue)
235
0
{
236
0
  void* obj = NULL;
237
238
0
  Queue_Lock(queue);
239
240
0
  if (queue->size > 0)
241
0
  {
242
0
    obj = queue->array[queue->head];
243
0
    queue->array[queue->head] = NULL;
244
0
    queue->head = (queue->head + 1) % queue->capacity;
245
0
    queue->size--;
246
0
  }
247
248
0
  if (queue->size < 1)
249
0
    ResetEvent(queue->event);
250
251
0
  Queue_Unlock(queue);
252
253
0
  return obj;
254
0
}
255
256
/**
257
 * Returns the object at the beginning of the Queue without removing it.
258
 */
259
260
void* Queue_Peek(wQueue* queue)
261
0
{
262
0
  void* obj = NULL;
263
264
0
  Queue_Lock(queue);
265
266
0
  if (queue->size > 0)
267
0
    obj = queue->array[queue->head];
268
269
0
  Queue_Unlock(queue);
270
271
0
  return obj;
272
0
}
273
274
void Queue_Discard(wQueue* queue)
275
0
{
276
0
  void* obj;
277
278
0
  Queue_Lock(queue);
279
0
  obj = Queue_Dequeue(queue);
280
281
0
  if (queue->object.fnObjectFree)
282
0
    queue->object.fnObjectFree(obj);
283
0
  Queue_Unlock(queue);
284
0
}
285
286
static BOOL default_queue_equals(const void* obj1, const void* obj2)
287
0
{
288
0
  return (obj1 == obj2);
289
0
}
290
291
/**
292
 * Construction, Destruction
293
 */
294
295
wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
296
0
{
297
0
  wObject* obj;
298
0
  wQueue* queue = NULL;
299
0
  queue = (wQueue*)calloc(1, sizeof(wQueue));
300
301
0
  if (!queue)
302
0
    return NULL;
303
304
0
  queue->synchronized = synchronized;
305
306
0
  queue->growthFactor = 2;
307
0
  if (growthFactor > 0)
308
0
    queue->growthFactor = (size_t)growthFactor;
309
310
0
  if (capacity <= 0)
311
0
    capacity = 32;
312
0
  if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
313
0
    goto fail;
314
0
  queue->haveLock = TRUE;
315
0
  if (!Queue_EnsureCapacity(queue, (size_t)capacity))
316
0
    goto fail;
317
318
0
  queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
319
320
0
  if (!queue->event)
321
0
    goto fail;
322
323
0
  obj = Queue_Object(queue);
324
0
  obj->fnObjectEquals = default_queue_equals;
325
326
0
  return queue;
327
0
fail:
328
0
  Queue_Free(queue);
329
0
  return NULL;
330
0
}
331
332
void Queue_Free(wQueue* queue)
333
0
{
334
0
  if (!queue)
335
0
    return;
336
337
0
  if (queue->haveLock)
338
0
  {
339
0
    Queue_Clear(queue);
340
0
    DeleteCriticalSection(&queue->lock);
341
0
  }
342
0
  CloseHandle(queue->event);
343
0
  free(queue->array);
344
0
  free(queue);
345
0
}