Coverage Report

Created: 2025-07-01 06:46

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