Coverage Report

Created: 2023-09-25 06:56

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