Coverage Report

Created: 2025-08-26 06:33

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