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