Coverage Report

Created: 2026-10-15 06:27

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
0
{
58
0
  if (!pool)
59
0
    return FALSE;
60
61
0
  if (pool->synchronized)
62
0
    EnterCriticalSection(&pool->lock);
63
0
  return TRUE;
64
0
}
65
66
static BOOL BufferPool_Unlock(wBufferPool* pool)
67
0
{
68
0
  if (!pool)
69
0
    return FALSE;
70
71
0
  if (pool->synchronized)
72
0
    LeaveCriticalSection(&pool->lock);
73
0
  return TRUE;
74
0
}
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 = nullptr;
92
0
      SSIZE_T newCapacity = pool->aSize + count;
93
0
      newCapacity += (newCapacity + 2) / 2;
94
95
0
      newArray = (wBufferPoolItem*)winpr_aligned_recalloc(
96
0
          pool->aArray, sizeof(wBufferPoolItem),
97
0
          WINPR_ASSERTING_INT_CAST(size_t, newCapacity), pool->alignment);
98
0
      if (!newArray)
99
0
        return FALSE;
100
0
      pool->aArray = newArray;
101
0
      pool->aCapacity = newCapacity;
102
0
    }
103
104
0
    MoveMemory(
105
0
        &pool->aArray[index + WINPR_ASSERTING_INT_CAST(size_t, count)], &pool->aArray[index],
106
0
        (WINPR_ASSERTING_INT_CAST(size_t, pool->aSize) - index) * sizeof(wBufferPoolItem));
107
0
    pool->aSize += count;
108
0
  }
109
0
  else if (count < 0)
110
0
  {
111
0
    MoveMemory(
112
0
        &pool->aArray[index], &pool->aArray[index + WINPR_ASSERTING_INT_CAST(size_t, -count)],
113
0
        (WINPR_ASSERTING_INT_CAST(size_t, pool->aSize) - index) * sizeof(wBufferPoolItem));
114
0
    pool->aSize += count;
115
0
  }
116
0
  return TRUE;
117
0
}
118
119
static BOOL BufferPool_ShiftUsed(wBufferPool* pool, SSIZE_T index, SSIZE_T count)
120
0
{
121
0
  if (count > 0)
122
0
  {
123
0
    const SSIZE_T required = pool->uSize + count;
124
    // check for overflow
125
0
    if ((required < count) || (required < pool->uSize))
126
0
      return FALSE;
127
128
0
    if (required > pool->uCapacity)
129
0
    {
130
0
      SSIZE_T newUCapacity = pool->uCapacity;
131
0
      do
132
0
      {
133
0
        if (newUCapacity > SSIZE_MAX - 128ll)
134
0
          return FALSE;
135
0
        newUCapacity += 128ll;
136
0
      } while (newUCapacity <= required);
137
0
      wBufferPoolItem* newUArray = nullptr;
138
0
      newUArray = (wBufferPoolItem*)winpr_aligned_realloc(
139
0
          pool->uArray,
140
0
          sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newUCapacity),
141
0
          pool->alignment);
142
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 = nullptr;
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
      buffer = winpr_aligned_calloc(WINPR_ASSERTING_INT_CAST(size_t, pool->fixedSize), 1,
249
0
                                    pool->alignment);
250
0
    }
251
252
0
    if (!buffer)
253
0
      goto out_error;
254
0
  }
255
0
  else
256
0
  {
257
    /* variable size buffers */
258
259
0
    maxSize = 0;
260
0
    maxIndex = 0;
261
262
0
    if (size < 1)
263
0
      size = pool->fixedSize;
264
265
0
    for (SSIZE_T index = 0; index < pool->aSize; index++)
266
0
    {
267
0
      if (pool->aArray[index].size > maxSize)
268
0
      {
269
0
        maxIndex = index;
270
0
        maxSize = pool->aArray[index].size;
271
0
      }
272
273
0
      if (pool->aArray[index].size >= size)
274
0
      {
275
0
        foundIndex = index;
276
0
        found = TRUE;
277
0
        break;
278
0
      }
279
0
    }
280
281
0
    if (!found && maxSize)
282
0
    {
283
0
      foundIndex = maxIndex;
284
0
      found = TRUE;
285
0
    }
286
287
0
    if (!found)
288
0
    {
289
0
      if (!size)
290
0
        buffer = nullptr;
291
0
      else
292
0
      {
293
0
        buffer = winpr_aligned_calloc(WINPR_ASSERTING_INT_CAST(size_t, size), 1,
294
0
                                      pool->alignment);
295
296
0
        if (!buffer)
297
0
          goto out_error;
298
0
      }
299
0
    }
300
0
    else
301
0
    {
302
0
      buffer = pool->aArray[foundIndex].buffer;
303
304
0
      if (maxSize < size)
305
0
      {
306
0
        void* newBuffer = nullptr;
307
0
        newBuffer = winpr_aligned_realloc(buffer, WINPR_ASSERTING_INT_CAST(size_t, size),
308
0
                                          pool->alignment);
309
310
0
        if (!newBuffer)
311
0
          goto out_error_no_free;
312
313
0
        buffer = newBuffer;
314
0
      }
315
316
0
      if (!BufferPool_ShiftAvailable(pool, WINPR_ASSERTING_INT_CAST(size_t, foundIndex), -1))
317
0
        goto out_error;
318
0
    }
319
320
0
    if (!buffer)
321
0
      goto out_error;
322
323
0
    if (pool->uSize + 1 > pool->uCapacity)
324
0
    {
325
0
      size_t newUCapacity = WINPR_ASSERTING_INT_CAST(size_t, pool->uCapacity);
326
0
      newUCapacity += (newUCapacity + 2) / 2;
327
0
      if (newUCapacity > SSIZE_MAX)
328
0
        goto out_error;
329
0
      wBufferPoolItem* newUArray =
330
0
          (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
331
0
      if (!newUArray)
332
0
        goto out_error;
333
334
0
      pool->uCapacity = (SSIZE_T)newUCapacity;
335
0
      pool->uArray = newUArray;
336
0
    }
337
338
0
    pool->uArray[pool->uSize].buffer = buffer;
339
0
    pool->uArray[pool->uSize].size = size;
340
0
    (pool->uSize)++;
341
0
  }
342
343
0
  BufferPool_Unlock(pool);
344
345
0
  return buffer;
346
347
0
out_error:
348
0
  winpr_aligned_free(buffer);
349
0
out_error_no_free:
350
0
  BufferPool_Unlock(pool);
351
0
  return nullptr;
352
0
}
353
354
/**
355
 * Returns a buffer to the pool.
356
 */
357
358
BOOL BufferPool_Return(wBufferPool* pool, void* buffer)
359
0
{
360
0
  BOOL rc = FALSE;
361
0
  SSIZE_T size = 0;
362
0
  BOOL found = FALSE;
363
364
0
  BufferPool_Lock(pool);
365
366
0
  if (pool->fixedSize)
367
0
  {
368
    /* fixed size buffers */
369
370
0
    if ((pool->size + 1) >= pool->capacity)
371
0
    {
372
0
      SSIZE_T newCapacity = MAX(2, pool->size + (pool->size + 2) / 2 + 1);
373
0
      void** newArray = (void**)realloc(
374
0
          (void*)pool->array, sizeof(void*) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity));
375
0
      if (!newArray)
376
0
        goto out_error;
377
378
0
      pool->capacity = newCapacity;
379
0
      pool->array = newArray;
380
0
    }
381
382
0
    pool->array[(pool->size)++] = buffer;
383
0
  }
384
0
  else
385
0
  {
386
    /* variable size buffers */
387
388
0
    SSIZE_T index = 0;
389
0
    for (; index < pool->uSize; index++)
390
0
    {
391
0
      if (pool->uArray[index].buffer == buffer)
392
0
      {
393
0
        found = TRUE;
394
0
        break;
395
0
      }
396
0
    }
397
398
0
    if (found)
399
0
    {
400
0
      size = pool->uArray[index].size;
401
0
      if (!BufferPool_ShiftUsed(pool, index, -1))
402
0
        goto out_error;
403
0
    }
404
405
0
    if (size)
406
0
    {
407
0
      if ((pool->aSize + 1) >= pool->aCapacity)
408
0
      {
409
0
        SSIZE_T newCapacity = MAX(2, pool->aSize + (pool->aSize + 2) / 2 + 1);
410
0
        wBufferPoolItem* newArray = (wBufferPoolItem*)realloc(
411
0
            pool->aArray,
412
0
            sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity));
413
0
        if (!newArray)
414
0
          goto out_error;
415
416
0
        pool->aCapacity = newCapacity;
417
0
        pool->aArray = newArray;
418
0
      }
419
420
0
      pool->aArray[pool->aSize].buffer = buffer;
421
0
      pool->aArray[pool->aSize].size = size;
422
0
      (pool->aSize)++;
423
0
    }
424
0
  }
425
426
0
  rc = TRUE;
427
0
out_error:
428
0
  BufferPool_Unlock(pool);
429
0
  return rc;
430
0
}
431
432
/**
433
 * Releases the buffers currently cached in the pool.
434
 */
435
436
void BufferPool_Clear(wBufferPool* pool)
437
0
{
438
0
  BufferPool_Lock(pool);
439
440
0
  if (pool->fixedSize)
441
0
  {
442
    /* fixed size buffers */
443
444
0
    while (pool->size > 0)
445
0
    {
446
0
      (pool->size)--;
447
448
0
      winpr_aligned_free(pool->array[pool->size]);
449
0
    }
450
0
  }
451
0
  else
452
0
  {
453
    /* variable size buffers */
454
455
0
    while (pool->aSize > 0)
456
0
    {
457
0
      (pool->aSize)--;
458
459
0
      winpr_aligned_free(pool->aArray[pool->aSize].buffer);
460
0
    }
461
462
0
    while (pool->uSize > 0)
463
0
    {
464
0
      (pool->uSize)--;
465
466
0
      winpr_aligned_free(pool->uArray[pool->uSize].buffer);
467
0
    }
468
0
  }
469
470
0
  BufferPool_Unlock(pool);
471
0
}
472
473
/**
474
 * Construction, Destruction
475
 */
476
477
wBufferPool* BufferPool_New(BOOL synchronized, SSIZE_T fixedSize, DWORD alignment)
478
0
{
479
0
  wBufferPool* pool = nullptr;
480
481
0
  pool = (wBufferPool*)calloc(1, sizeof(wBufferPool));
482
483
0
  if (pool)
484
0
  {
485
0
    pool->fixedSize = fixedSize;
486
487
0
    if (pool->fixedSize < 0)
488
0
      pool->fixedSize = 0;
489
490
0
    pool->alignment = alignment;
491
0
    pool->synchronized = synchronized;
492
493
0
    if (pool->synchronized)
494
0
    {
495
0
      if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
496
0
        goto out_error;
497
0
    }
498
499
0
    if (pool->fixedSize)
500
0
    {
501
      /* fixed size buffers */
502
503
0
      pool->size = 0;
504
0
      pool->capacity = 32;
505
0
      pool->array =
506
0
          (void**)calloc(WINPR_ASSERTING_INT_CAST(size_t, pool->capacity), sizeof(void*));
507
0
      if (!pool->array)
508
0
        goto out_error;
509
0
    }
510
0
    else
511
0
    {
512
      /* variable size buffers */
513
514
0
      pool->aSize = 0;
515
0
      pool->aCapacity = 32;
516
0
      pool->aArray = (wBufferPoolItem*)calloc(
517
0
          WINPR_ASSERTING_INT_CAST(size_t, pool->aCapacity), sizeof(wBufferPoolItem));
518
0
      if (!pool->aArray)
519
0
        goto out_error;
520
521
0
      pool->uSize = 0;
522
0
      pool->uCapacity = 32;
523
0
      pool->uArray = (wBufferPoolItem*)calloc(
524
0
          WINPR_ASSERTING_INT_CAST(size_t, pool->uCapacity), sizeof(wBufferPoolItem));
525
0
      if (!pool->uArray)
526
0
        goto out_error;
527
0
    }
528
0
  }
529
530
0
  return pool;
531
532
0
out_error:
533
0
  WINPR_PRAGMA_DIAG_PUSH
534
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
535
0
  BufferPool_Free(pool);
536
0
  WINPR_PRAGMA_DIAG_POP
537
0
  return nullptr;
538
0
}
539
540
void BufferPool_Free(wBufferPool* pool)
541
0
{
542
0
  if (pool)
543
0
  {
544
0
    BufferPool_Clear(pool);
545
546
0
    if (pool->synchronized)
547
0
      DeleteCriticalSection(&pool->lock);
548
549
0
    if (pool->fixedSize)
550
0
    {
551
      /* fixed size buffers */
552
553
0
      free((void*)pool->array);
554
0
    }
555
0
    else
556
0
    {
557
      /* variable size buffers */
558
559
0
      free(pool->aArray);
560
0
      free(pool->uArray);
561
0
    }
562
563
0
    free(pool);
564
0
  }
565
0
}