Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/winpr/libwinpr/utils/collections/BufferPool.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Buffer Pool
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
24
#include <winpr/collections.h>
25
26
typedef struct
27
{
28
  SSIZE_T size;
29
  void* buffer;
30
} wBufferPoolItem;
31
32
struct s_wBufferPool
33
{
34
  SSIZE_T fixedSize;
35
  DWORD alignment;
36
  BOOL synchronized;
37
  CRITICAL_SECTION lock;
38
39
  SSIZE_T size;
40
  SSIZE_T capacity;
41
  void** array;
42
43
  SSIZE_T aSize;
44
  SSIZE_T aCapacity;
45
  wBufferPoolItem* aArray;
46
47
  SSIZE_T uSize;
48
  SSIZE_T uCapacity;
49
  wBufferPoolItem* uArray;
50
};
51
52
static BOOL BufferPool_Lock(wBufferPool* pool)
53
17.1k
{
54
17.1k
  if (!pool)
55
0
    return FALSE;
56
57
17.1k
  if (pool->synchronized)
58
17.1k
    EnterCriticalSection(&pool->lock);
59
17.1k
  return TRUE;
60
17.1k
}
61
62
static BOOL BufferPool_Unlock(wBufferPool* pool)
63
17.1k
{
64
17.1k
  if (!pool)
65
0
    return FALSE;
66
67
17.1k
  if (pool->synchronized)
68
17.1k
    LeaveCriticalSection(&pool->lock);
69
17.1k
  return TRUE;
70
17.1k
}
71
72
/**
73
 * C equivalent of the C# BufferManager Class:
74
 * http://msdn.microsoft.com/en-us/library/ms405814.aspx
75
 */
76
77
/**
78
 * Methods
79
 */
80
81
static BOOL BufferPool_ShiftAvailable(wBufferPool* pool, size_t index, int count)
82
0
{
83
0
  if (count > 0)
84
0
  {
85
0
    if (pool->aSize + count > pool->aCapacity)
86
0
    {
87
0
      wBufferPoolItem* newArray = NULL;
88
0
      SSIZE_T newCapacity = pool->aCapacity * 2;
89
90
0
      if (pool->alignment > 0)
91
0
        newArray = (wBufferPoolItem*)winpr_aligned_realloc(
92
0
            pool->aArray, sizeof(wBufferPoolItem) * newCapacity, pool->alignment);
93
0
      else
94
0
        newArray =
95
0
            (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity);
96
0
      if (!newArray)
97
0
        return FALSE;
98
0
      pool->aArray = newArray;
99
0
      pool->aCapacity = newCapacity;
100
0
    }
101
102
0
    MoveMemory(&pool->aArray[index + count], &pool->aArray[index],
103
0
               (pool->aSize - index) * sizeof(wBufferPoolItem));
104
0
    pool->aSize += count;
105
0
  }
106
0
  else if (count < 0)
107
0
  {
108
0
    MoveMemory(&pool->aArray[index], &pool->aArray[index - count],
109
0
               (pool->aSize - index) * sizeof(wBufferPoolItem));
110
0
    pool->aSize += count;
111
0
  }
112
0
  return TRUE;
113
0
}
114
115
static BOOL BufferPool_ShiftUsed(wBufferPool* pool, SSIZE_T index, SSIZE_T count)
116
0
{
117
0
  if (count > 0)
118
0
  {
119
0
    if (pool->uSize + count > pool->uCapacity)
120
0
    {
121
0
      SSIZE_T newUCapacity = pool->uCapacity * 2;
122
0
      wBufferPoolItem* newUArray = NULL;
123
0
      if (pool->alignment > 0)
124
0
        newUArray = (wBufferPoolItem*)winpr_aligned_realloc(
125
0
            pool->uArray, sizeof(wBufferPoolItem) * newUCapacity, pool->alignment);
126
0
      else
127
0
        newUArray =
128
0
            (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
129
0
      if (!newUArray)
130
0
        return FALSE;
131
0
      pool->uCapacity = newUCapacity;
132
0
      pool->uArray = newUArray;
133
0
    }
134
135
0
    MoveMemory(&pool->uArray[index + count], &pool->uArray[index],
136
0
               (pool->uSize - index) * sizeof(wBufferPoolItem));
137
0
    pool->uSize += count;
138
0
  }
139
0
  else if (count < 0)
140
0
  {
141
0
    MoveMemory(&pool->uArray[index], &pool->uArray[index - count],
142
0
               (pool->uSize - index) * sizeof(wBufferPoolItem));
143
0
    pool->uSize += count;
144
0
  }
145
0
  return TRUE;
146
0
}
147
148
/**
149
 * Get the buffer pool size
150
 */
151
152
SSIZE_T BufferPool_GetPoolSize(wBufferPool* pool)
153
0
{
154
0
  SSIZE_T size = 0;
155
156
0
  BufferPool_Lock(pool);
157
158
0
  if (pool->fixedSize)
159
0
  {
160
    /* fixed size buffers */
161
0
    size = pool->size;
162
0
  }
163
0
  else
164
0
  {
165
    /* variable size buffers */
166
0
    size = pool->uSize;
167
0
  }
168
169
0
  BufferPool_Unlock(pool);
170
171
0
  return size;
172
0
}
173
174
/**
175
 * Get the size of a pooled buffer
176
 */
177
178
SSIZE_T BufferPool_GetBufferSize(wBufferPool* pool, const void* buffer)
179
0
{
180
0
  SSIZE_T size = 0;
181
0
  BOOL found = FALSE;
182
183
0
  BufferPool_Lock(pool);
184
185
0
  if (pool->fixedSize)
186
0
  {
187
    /* fixed size buffers */
188
0
    size = pool->fixedSize;
189
0
    found = TRUE;
190
0
  }
191
0
  else
192
0
  {
193
    /* variable size buffers */
194
195
0
    for (SSIZE_T index = 0; index < pool->uSize; index++)
196
0
    {
197
0
      if (pool->uArray[index].buffer == buffer)
198
0
      {
199
0
        size = pool->uArray[index].size;
200
0
        found = TRUE;
201
0
        break;
202
0
      }
203
0
    }
204
0
  }
205
206
0
  BufferPool_Unlock(pool);
207
208
0
  return (found) ? size : -1;
209
0
}
210
211
/**
212
 * Gets a buffer of at least the specified size from the pool.
213
 */
214
215
void* BufferPool_Take(wBufferPool* pool, SSIZE_T size)
216
0
{
217
0
  SSIZE_T maxSize = 0;
218
0
  SSIZE_T maxIndex = 0;
219
0
  SSIZE_T foundIndex = -1;
220
0
  BOOL found = FALSE;
221
0
  void* buffer = NULL;
222
223
0
  BufferPool_Lock(pool);
224
225
0
  if (pool->fixedSize)
226
0
  {
227
    /* fixed size buffers */
228
229
0
    if (pool->size > 0)
230
0
      buffer = pool->array[--(pool->size)];
231
232
0
    if (!buffer)
233
0
    {
234
0
      if (pool->alignment)
235
0
        buffer = winpr_aligned_malloc(pool->fixedSize, pool->alignment);
236
0
      else
237
0
        buffer = malloc(pool->fixedSize);
238
0
    }
239
240
0
    if (!buffer)
241
0
      goto out_error;
242
0
  }
243
0
  else
244
0
  {
245
    /* variable size buffers */
246
247
0
    maxSize = 0;
248
0
    maxIndex = 0;
249
250
0
    if (size < 1)
251
0
      size = pool->fixedSize;
252
253
0
    for (SSIZE_T index = 0; index < pool->aSize; index++)
254
0
    {
255
0
      if (pool->aArray[index].size > maxSize)
256
0
      {
257
0
        maxIndex = index;
258
0
        maxSize = pool->aArray[index].size;
259
0
      }
260
261
0
      if (pool->aArray[index].size >= size)
262
0
      {
263
0
        foundIndex = index;
264
0
        found = TRUE;
265
0
        break;
266
0
      }
267
0
    }
268
269
0
    if (!found && maxSize)
270
0
    {
271
0
      foundIndex = maxIndex;
272
0
      found = TRUE;
273
0
    }
274
275
0
    if (!found)
276
0
    {
277
0
      if (!size)
278
0
        buffer = NULL;
279
0
      else
280
0
      {
281
0
        if (pool->alignment)
282
0
          buffer = winpr_aligned_malloc(size, pool->alignment);
283
0
        else
284
0
          buffer = malloc(size);
285
286
0
        if (!buffer)
287
0
          goto out_error;
288
0
      }
289
0
    }
290
0
    else
291
0
    {
292
0
      buffer = pool->aArray[foundIndex].buffer;
293
294
0
      if (maxSize < size)
295
0
      {
296
0
        void* newBuffer = NULL;
297
0
        if (pool->alignment)
298
0
          newBuffer = winpr_aligned_realloc(buffer, size, pool->alignment);
299
0
        else
300
0
          newBuffer = realloc(buffer, size);
301
302
0
        if (!newBuffer)
303
0
          goto out_error_no_free;
304
305
0
        buffer = newBuffer;
306
0
      }
307
308
0
      if (!BufferPool_ShiftAvailable(pool, foundIndex, -1))
309
0
        goto out_error;
310
0
    }
311
312
0
    if (!buffer)
313
0
      goto out_error;
314
315
0
    if (pool->uSize + 1 > pool->uCapacity)
316
0
    {
317
0
      size_t newUCapacity = pool->uCapacity * 2;
318
0
      wBufferPoolItem* newUArray =
319
0
          (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
320
0
      if (!newUArray)
321
0
        goto out_error;
322
323
0
      pool->uCapacity = newUCapacity;
324
0
      pool->uArray = newUArray;
325
0
    }
326
327
0
    pool->uArray[pool->uSize].buffer = buffer;
328
0
    pool->uArray[pool->uSize].size = size;
329
0
    (pool->uSize)++;
330
0
  }
331
332
0
  BufferPool_Unlock(pool);
333
334
0
  return buffer;
335
336
0
out_error:
337
0
  if (pool->alignment)
338
0
    winpr_aligned_free(buffer);
339
0
  else
340
0
    free(buffer);
341
0
out_error_no_free:
342
0
  BufferPool_Unlock(pool);
343
0
  return NULL;
344
0
}
345
346
/**
347
 * Returns a buffer to the pool.
348
 */
349
350
BOOL BufferPool_Return(wBufferPool* pool, void* buffer)
351
0
{
352
0
  BOOL rc = FALSE;
353
0
  SSIZE_T size = 0;
354
0
  BOOL found = FALSE;
355
356
0
  BufferPool_Lock(pool);
357
358
0
  if (pool->fixedSize)
359
0
  {
360
    /* fixed size buffers */
361
362
0
    if ((pool->size + 1) >= pool->capacity)
363
0
    {
364
0
      SSIZE_T newCapacity = pool->capacity * 2;
365
0
      void** newArray = (void**)realloc(pool->array, sizeof(void*) * newCapacity);
366
0
      if (!newArray)
367
0
        goto out_error;
368
369
0
      pool->capacity = newCapacity;
370
0
      pool->array = newArray;
371
0
    }
372
373
0
    pool->array[(pool->size)++] = buffer;
374
0
  }
375
0
  else
376
0
  {
377
    /* variable size buffers */
378
379
0
    SSIZE_T index = 0;
380
0
    for (; index < pool->uSize; index++)
381
0
    {
382
0
      if (pool->uArray[index].buffer == buffer)
383
0
      {
384
0
        found = TRUE;
385
0
        break;
386
0
      }
387
0
    }
388
389
0
    if (found)
390
0
    {
391
0
      size = pool->uArray[index].size;
392
0
      if (!BufferPool_ShiftUsed(pool, index, -1))
393
0
        goto out_error;
394
0
    }
395
396
0
    if (size)
397
0
    {
398
0
      if ((pool->aSize + 1) >= pool->aCapacity)
399
0
      {
400
0
        SSIZE_T newCapacity = pool->aCapacity * 2;
401
0
        wBufferPoolItem* newArray =
402
0
            (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity);
403
0
        if (!newArray)
404
0
          goto out_error;
405
406
0
        pool->aCapacity = newCapacity;
407
0
        pool->aArray = newArray;
408
0
      }
409
410
0
      pool->aArray[pool->aSize].buffer = buffer;
411
0
      pool->aArray[pool->aSize].size = size;
412
0
      (pool->aSize)++;
413
0
    }
414
0
  }
415
416
0
  rc = TRUE;
417
0
out_error:
418
0
  BufferPool_Unlock(pool);
419
0
  return rc;
420
0
}
421
422
/**
423
 * Releases the buffers currently cached in the pool.
424
 */
425
426
void BufferPool_Clear(wBufferPool* pool)
427
17.1k
{
428
17.1k
  BufferPool_Lock(pool);
429
430
17.1k
  if (pool->fixedSize)
431
17.1k
  {
432
    /* fixed size buffers */
433
434
17.1k
    while (pool->size > 0)
435
0
    {
436
0
      (pool->size)--;
437
438
0
      if (pool->alignment)
439
0
        winpr_aligned_free(pool->array[pool->size]);
440
0
      else
441
0
        free(pool->array[pool->size]);
442
0
    }
443
17.1k
  }
444
0
  else
445
0
  {
446
    /* variable size buffers */
447
448
0
    while (pool->aSize > 0)
449
0
    {
450
0
      (pool->aSize)--;
451
452
0
      if (pool->alignment)
453
0
        winpr_aligned_free(pool->aArray[pool->aSize].buffer);
454
0
      else
455
0
        free(pool->aArray[pool->aSize].buffer);
456
0
    }
457
458
0
    while (pool->uSize > 0)
459
0
    {
460
0
      (pool->uSize)--;
461
462
0
      if (pool->alignment)
463
0
        winpr_aligned_free(pool->uArray[pool->uSize].buffer);
464
0
      else
465
0
        free(pool->uArray[pool->uSize].buffer);
466
0
    }
467
0
  }
468
469
17.1k
  BufferPool_Unlock(pool);
470
17.1k
}
471
472
/**
473
 * Construction, Destruction
474
 */
475
476
wBufferPool* BufferPool_New(BOOL synchronized, SSIZE_T fixedSize, DWORD alignment)
477
17.1k
{
478
17.1k
  wBufferPool* pool = NULL;
479
480
17.1k
  pool = (wBufferPool*)calloc(1, sizeof(wBufferPool));
481
482
17.1k
  if (pool)
483
17.1k
  {
484
17.1k
    pool->fixedSize = fixedSize;
485
486
17.1k
    if (pool->fixedSize < 0)
487
0
      pool->fixedSize = 0;
488
489
17.1k
    pool->alignment = alignment;
490
17.1k
    pool->synchronized = synchronized;
491
492
17.1k
    if (pool->synchronized)
493
17.1k
      InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
494
495
17.1k
    if (pool->fixedSize)
496
17.1k
    {
497
      /* fixed size buffers */
498
499
17.1k
      pool->size = 0;
500
17.1k
      pool->capacity = 32;
501
17.1k
      pool->array = (void**)calloc(pool->capacity, sizeof(void*));
502
17.1k
      if (!pool->array)
503
0
        goto out_error;
504
17.1k
    }
505
0
    else
506
0
    {
507
      /* variable size buffers */
508
509
0
      pool->aSize = 0;
510
0
      pool->aCapacity = 32;
511
0
      pool->aArray = (wBufferPoolItem*)calloc(pool->aCapacity, sizeof(wBufferPoolItem));
512
0
      if (!pool->aArray)
513
0
        goto out_error;
514
515
0
      pool->uSize = 0;
516
0
      pool->uCapacity = 32;
517
0
      pool->uArray = (wBufferPoolItem*)calloc(pool->uCapacity, sizeof(wBufferPoolItem));
518
0
      if (!pool->uArray)
519
0
        goto out_error;
520
0
    }
521
17.1k
  }
522
523
17.1k
  return pool;
524
525
0
out_error:
526
0
  WINPR_PRAGMA_DIAG_PUSH
527
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
528
0
  BufferPool_Free(pool);
529
0
  WINPR_PRAGMA_DIAG_POP
530
0
  return NULL;
531
17.1k
}
532
533
void BufferPool_Free(wBufferPool* pool)
534
17.1k
{
535
17.1k
  if (pool)
536
17.1k
  {
537
17.1k
    BufferPool_Clear(pool);
538
539
17.1k
    if (pool->synchronized)
540
17.1k
      DeleteCriticalSection(&pool->lock);
541
542
17.1k
    if (pool->fixedSize)
543
17.1k
    {
544
      /* fixed size buffers */
545
546
17.1k
      free(pool->array);
547
17.1k
    }
548
0
    else
549
0
    {
550
      /* variable size buffers */
551
552
0
      free(pool->aArray);
553
0
      free(pool->uArray);
554
0
    }
555
556
17.1k
    free(pool);
557
17.1k
  }
558
17.1k
}