Coverage Report

Created: 2024-09-08 06:16

/src/FreeRDP/winpr/libwinpr/utils/collections/StreamPool.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Object 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
#include <winpr/wlog.h>
24
25
#include <winpr/collections.h>
26
27
#include "../stream.h"
28
29
struct s_wStreamPool
30
{
31
  size_t aSize;
32
  size_t aCapacity;
33
  wStream** aArray;
34
35
  size_t uSize;
36
  size_t uCapacity;
37
  wStream** uArray;
38
39
  CRITICAL_SECTION lock;
40
  BOOL synchronized;
41
  size_t defaultSize;
42
};
43
44
/**
45
 * Lock the stream pool
46
 */
47
48
static INLINE void StreamPool_Lock(wStreamPool* pool)
49
0
{
50
0
  WINPR_ASSERT(pool);
51
0
  if (pool->synchronized)
52
0
    EnterCriticalSection(&pool->lock);
53
0
}
54
55
/**
56
 * Unlock the stream pool
57
 */
58
59
static INLINE void StreamPool_Unlock(wStreamPool* pool)
60
0
{
61
0
  WINPR_ASSERT(pool);
62
0
  if (pool->synchronized)
63
0
    LeaveCriticalSection(&pool->lock);
64
0
}
65
66
static BOOL StreamPool_EnsureCapacity(wStreamPool* pool, size_t count, BOOL usedOrAvailable)
67
0
{
68
0
  size_t new_cap = 0;
69
0
  size_t* cap = NULL;
70
0
  size_t* size = NULL;
71
0
  wStream*** array = NULL;
72
73
0
  WINPR_ASSERT(pool);
74
75
0
  cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity;
76
0
  size = (usedOrAvailable) ? &pool->uSize : &pool->aSize;
77
0
  array = (usedOrAvailable) ? &pool->uArray : &pool->aArray;
78
0
  if (*cap == 0)
79
0
    new_cap = *size + count;
80
0
  else if (*size + count > *cap)
81
0
    new_cap = *cap * 2;
82
0
  else if ((*size + count) < *cap / 3)
83
0
    new_cap = *cap / 2;
84
85
0
  if (new_cap > 0)
86
0
  {
87
0
    wStream** new_arr = NULL;
88
89
0
    if (*cap < *size + count)
90
0
      *cap += count;
91
92
0
    new_arr = (wStream**)realloc(*array, sizeof(wStream*) * new_cap);
93
0
    if (!new_arr)
94
0
      return FALSE;
95
0
    *cap = new_cap;
96
0
    *array = new_arr;
97
0
  }
98
0
  return TRUE;
99
0
}
100
101
/**
102
 * Methods
103
 */
104
105
static void StreamPool_ShiftUsed(wStreamPool* pool, size_t index, INT64 count)
106
0
{
107
0
  WINPR_ASSERT(pool);
108
0
  if (count > 0)
109
0
  {
110
0
    const size_t pcount = (size_t)count;
111
0
    StreamPool_EnsureCapacity(pool, pcount, TRUE);
112
113
0
    MoveMemory(&pool->uArray[index + pcount], &pool->uArray[index],
114
0
               (pool->uSize - index) * sizeof(wStream*));
115
0
    pool->uSize += pcount;
116
0
  }
117
0
  else if (count < 0)
118
0
  {
119
0
    const size_t pcount = (size_t)-count;
120
0
    const size_t off = index + pcount;
121
0
    if (pool->uSize > off)
122
0
    {
123
0
      MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount],
124
0
                 (pool->uSize - index - pcount) * sizeof(wStream*));
125
0
    }
126
127
0
    pool->uSize -= pcount;
128
0
  }
129
0
}
130
131
/**
132
 * Adds a used stream to the pool.
133
 */
134
135
static void StreamPool_AddUsed(wStreamPool* pool, wStream* s)
136
0
{
137
0
  StreamPool_EnsureCapacity(pool, 1, TRUE);
138
0
  pool->uArray[(pool->uSize)++] = s;
139
0
}
140
141
/**
142
 * Removes a used stream from the pool.
143
 */
144
145
static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s)
146
0
{
147
0
  WINPR_ASSERT(pool);
148
0
  for (size_t index = 0; index < pool->uSize; index++)
149
0
  {
150
0
    if (pool->uArray[index] == s)
151
0
    {
152
0
      StreamPool_ShiftUsed(pool, index, -1);
153
0
      break;
154
0
    }
155
0
  }
156
0
}
157
158
static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index, INT64 count)
159
0
{
160
0
  WINPR_ASSERT(pool);
161
0
  if (count > 0)
162
0
  {
163
0
    const size_t pcount = (size_t)count;
164
165
0
    StreamPool_EnsureCapacity(pool, pcount, FALSE);
166
0
    MoveMemory(&pool->aArray[index + pcount], &pool->aArray[index],
167
0
               (pool->aSize - index) * sizeof(wStream*));
168
0
    pool->aSize += pcount;
169
0
  }
170
0
  else if (count < 0)
171
0
  {
172
0
    const size_t pcount = (size_t)-count;
173
0
    const size_t off = index + pcount;
174
0
    if (pool->aSize > off)
175
0
    {
176
0
      MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
177
0
                 (pool->aSize - index - pcount) * sizeof(wStream*));
178
0
    }
179
180
0
    pool->aSize -= pcount;
181
0
  }
182
0
}
183
184
/**
185
 * Gets a stream from the pool.
186
 */
187
188
wStream* StreamPool_Take(wStreamPool* pool, size_t size)
189
0
{
190
0
  SSIZE_T foundIndex = -1;
191
0
  wStream* s = NULL;
192
193
0
  StreamPool_Lock(pool);
194
195
0
  if (size == 0)
196
0
    size = pool->defaultSize;
197
198
0
  for (size_t index = 0; index < pool->aSize; index++)
199
0
  {
200
0
    s = pool->aArray[index];
201
202
0
    if (Stream_Capacity(s) >= size)
203
0
    {
204
0
      foundIndex = index;
205
0
      break;
206
0
    }
207
0
  }
208
209
0
  if (foundIndex < 0)
210
0
  {
211
0
    s = Stream_New(NULL, size);
212
0
    if (!s)
213
0
      goto out_fail;
214
0
  }
215
0
  else if (s)
216
0
  {
217
0
    Stream_SetPosition(s, 0);
218
0
    Stream_SetLength(s, Stream_Capacity(s));
219
0
    StreamPool_ShiftAvailable(pool, foundIndex, -1);
220
0
  }
221
222
0
  if (s)
223
0
  {
224
0
    s->pool = pool;
225
0
    s->count = 1;
226
0
    StreamPool_AddUsed(pool, s);
227
0
  }
228
229
0
out_fail:
230
0
  StreamPool_Unlock(pool);
231
232
0
  return s;
233
0
}
234
235
/**
236
 * Returns an object to the pool.
237
 */
238
239
static void StreamPool_Remove(wStreamPool* pool, wStream* s)
240
0
{
241
0
  StreamPool_EnsureCapacity(pool, 1, FALSE);
242
0
  Stream_EnsureValidity(s);
243
0
  for (size_t x = 0; x < pool->aSize; x++)
244
0
  {
245
0
    wStream* cs = pool->aArray[x];
246
247
0
    WINPR_ASSERT(cs != s);
248
0
  }
249
0
  pool->aArray[(pool->aSize)++] = s;
250
0
  StreamPool_RemoveUsed(pool, s);
251
0
}
252
253
static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
254
0
{
255
0
  StreamPool_Lock(pool);
256
0
  if (s->count > 0)
257
0
    s->count--;
258
0
  if (s->count == 0)
259
0
    StreamPool_Remove(pool, s);
260
0
  StreamPool_Unlock(pool);
261
0
}
262
263
void StreamPool_Return(wStreamPool* pool, wStream* s)
264
0
{
265
0
  WINPR_ASSERT(pool);
266
0
  if (!s)
267
0
    return;
268
269
0
  StreamPool_Lock(pool);
270
0
  StreamPool_Remove(pool, s);
271
0
  StreamPool_Unlock(pool);
272
0
}
273
274
/**
275
 * Increment stream reference count
276
 */
277
278
void Stream_AddRef(wStream* s)
279
0
{
280
0
  WINPR_ASSERT(s);
281
0
  if (s->pool)
282
0
  {
283
0
    StreamPool_Lock(s->pool);
284
0
    s->count++;
285
0
    StreamPool_Unlock(s->pool);
286
0
  }
287
0
}
288
289
/**
290
 * Decrement stream reference count
291
 */
292
293
void Stream_Release(wStream* s)
294
0
{
295
0
  WINPR_ASSERT(s);
296
0
  if (s->pool)
297
0
    StreamPool_ReleaseOrReturn(s->pool, s);
298
0
}
299
300
/**
301
 * Find stream in pool using pointer inside buffer
302
 */
303
304
wStream* StreamPool_Find(wStreamPool* pool, BYTE* ptr)
305
0
{
306
0
  wStream* s = NULL;
307
0
  BOOL found = FALSE;
308
309
0
  StreamPool_Lock(pool);
310
311
0
  for (size_t index = 0; index < pool->uSize; index++)
312
0
  {
313
0
    s = pool->uArray[index];
314
315
0
    if ((ptr >= Stream_Buffer(s)) && (ptr < (Stream_Buffer(s) + Stream_Capacity(s))))
316
0
    {
317
0
      found = TRUE;
318
0
      break;
319
0
    }
320
0
  }
321
322
0
  StreamPool_Unlock(pool);
323
324
0
  return (found) ? s : NULL;
325
0
}
326
327
/**
328
 * Releases the streams currently cached in the pool.
329
 */
330
331
void StreamPool_Clear(wStreamPool* pool)
332
0
{
333
0
  StreamPool_Lock(pool);
334
335
0
  while (pool->aSize > 0)
336
0
  {
337
0
    wStream* s = pool->aArray[--pool->aSize];
338
0
    Stream_Free(s, s->isAllocatedStream);
339
0
  }
340
341
0
  while (pool->uSize > 0)
342
0
  {
343
0
    wStream* s = pool->uArray[--pool->uSize];
344
0
    Stream_Free(s, s->isAllocatedStream);
345
0
  }
346
347
0
  StreamPool_Unlock(pool);
348
0
}
349
350
/**
351
 * Construction, Destruction
352
 */
353
354
wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
355
0
{
356
0
  wStreamPool* pool = NULL;
357
358
0
  pool = (wStreamPool*)calloc(1, sizeof(wStreamPool));
359
360
0
  if (pool)
361
0
  {
362
0
    pool->synchronized = synchronized;
363
0
    pool->defaultSize = defaultSize;
364
365
0
    if (!StreamPool_EnsureCapacity(pool, 32, FALSE))
366
0
      goto fail;
367
0
    if (!StreamPool_EnsureCapacity(pool, 32, TRUE))
368
0
      goto fail;
369
370
0
    InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
371
0
  }
372
373
0
  return pool;
374
0
fail:
375
0
  WINPR_PRAGMA_DIAG_PUSH
376
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
377
0
  StreamPool_Free(pool);
378
0
  WINPR_PRAGMA_DIAG_POP
379
0
  return NULL;
380
0
}
381
382
void StreamPool_Free(wStreamPool* pool)
383
0
{
384
0
  if (pool)
385
0
  {
386
0
    StreamPool_Clear(pool);
387
388
0
    DeleteCriticalSection(&pool->lock);
389
390
0
    free(pool->aArray);
391
0
    free(pool->uArray);
392
393
0
    free(pool);
394
0
  }
395
0
}
396
397
char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size)
398
0
{
399
0
  WINPR_ASSERT(pool);
400
401
0
  if (!buffer || (size < 1))
402
0
    return NULL;
403
0
  (void)_snprintf(buffer, size - 1,
404
0
                  "aSize    =%" PRIuz ", uSize    =%" PRIuz "aCapacity=%" PRIuz
405
0
                  ", uCapacity=%" PRIuz,
406
0
                  pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity);
407
0
  buffer[size - 1] = '\0';
408
0
  return buffer;
409
0
}