Coverage Report

Created: 2024-09-08 06:20

/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
133k
{
50
133k
  WINPR_ASSERT(pool);
51
133k
  if (pool->synchronized)
52
133k
    EnterCriticalSection(&pool->lock);
53
133k
}
54
55
/**
56
 * Unlock the stream pool
57
 */
58
59
static INLINE void StreamPool_Unlock(wStreamPool* pool)
60
133k
{
61
133k
  WINPR_ASSERT(pool);
62
133k
  if (pool->synchronized)
63
133k
    LeaveCriticalSection(&pool->lock);
64
133k
}
65
66
static BOOL StreamPool_EnsureCapacity(wStreamPool* pool, size_t count, BOOL usedOrAvailable)
67
167k
{
68
167k
  size_t new_cap = 0;
69
167k
  size_t* cap = NULL;
70
167k
  size_t* size = NULL;
71
167k
  wStream*** array = NULL;
72
73
167k
  WINPR_ASSERT(pool);
74
75
167k
  cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity;
76
167k
  size = (usedOrAvailable) ? &pool->uSize : &pool->aSize;
77
167k
  array = (usedOrAvailable) ? &pool->uArray : &pool->aArray;
78
167k
  if (*cap == 0)
79
79.6k
    new_cap = *size + count;
80
88.2k
  else if (*size + count > *cap)
81
0
    new_cap = *cap * 2;
82
88.2k
  else if ((*size + count) < *cap / 3)
83
87.2k
    new_cap = *cap / 2;
84
85
167k
  if (new_cap > 0)
86
166k
  {
87
166k
    wStream** new_arr = NULL;
88
89
166k
    if (*cap < *size + count)
90
79.6k
      *cap += count;
91
92
166k
    new_arr = (wStream**)realloc(*array, sizeof(wStream*) * new_cap);
93
166k
    if (!new_arr)
94
0
      return FALSE;
95
166k
    *cap = new_cap;
96
166k
    *array = new_arr;
97
166k
  }
98
167k
  return TRUE;
99
167k
}
100
101
/**
102
 * Methods
103
 */
104
105
static void StreamPool_ShiftUsed(wStreamPool* pool, size_t index, INT64 count)
106
44.1k
{
107
44.1k
  WINPR_ASSERT(pool);
108
44.1k
  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
44.1k
  else if (count < 0)
118
44.1k
  {
119
44.1k
    const size_t pcount = (size_t)-count;
120
44.1k
    const size_t off = index + pcount;
121
44.1k
    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
44.1k
    pool->uSize -= pcount;
128
44.1k
  }
129
44.1k
}
130
131
/**
132
 * Adds a used stream to the pool.
133
 */
134
135
static void StreamPool_AddUsed(wStreamPool* pool, wStream* s)
136
44.1k
{
137
44.1k
  StreamPool_EnsureCapacity(pool, 1, TRUE);
138
44.1k
  pool->uArray[(pool->uSize)++] = s;
139
44.1k
}
140
141
/**
142
 * Removes a used stream from the pool.
143
 */
144
145
static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s)
146
44.1k
{
147
44.1k
  WINPR_ASSERT(pool);
148
48.4k
  for (size_t index = 0; index < pool->uSize; index++)
149
48.4k
  {
150
48.4k
    if (pool->uArray[index] == s)
151
44.1k
    {
152
44.1k
      StreamPool_ShiftUsed(pool, index, -1);
153
44.1k
      break;
154
44.1k
    }
155
48.4k
  }
156
44.1k
}
157
158
static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index, INT64 count)
159
395
{
160
395
  WINPR_ASSERT(pool);
161
395
  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
395
  else if (count < 0)
171
395
  {
172
395
    const size_t pcount = (size_t)-count;
173
395
    const size_t off = index + pcount;
174
395
    if (pool->aSize > off)
175
14
    {
176
14
      MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
177
14
                 (pool->aSize - index - pcount) * sizeof(wStream*));
178
14
    }
179
180
395
    pool->aSize -= pcount;
181
395
  }
182
395
}
183
184
/**
185
 * Gets a stream from the pool.
186
 */
187
188
wStream* StreamPool_Take(wStreamPool* pool, size_t size)
189
44.1k
{
190
44.1k
  SSIZE_T foundIndex = -1;
191
44.1k
  wStream* s = NULL;
192
193
44.1k
  StreamPool_Lock(pool);
194
195
44.1k
  if (size == 0)
196
40.5k
    size = pool->defaultSize;
197
198
44.3k
  for (size_t index = 0; index < pool->aSize; index++)
199
572
  {
200
572
    s = pool->aArray[index];
201
202
572
    if (Stream_Capacity(s) >= size)
203
395
    {
204
395
      foundIndex = index;
205
395
      break;
206
395
    }
207
572
  }
208
209
44.1k
  if (foundIndex < 0)
210
43.7k
  {
211
43.7k
    s = Stream_New(NULL, size);
212
43.7k
    if (!s)
213
0
      goto out_fail;
214
43.7k
  }
215
395
  else if (s)
216
395
  {
217
395
    Stream_SetPosition(s, 0);
218
395
    Stream_SetLength(s, Stream_Capacity(s));
219
395
    StreamPool_ShiftAvailable(pool, foundIndex, -1);
220
395
  }
221
222
44.1k
  if (s)
223
44.1k
  {
224
44.1k
    s->pool = pool;
225
44.1k
    s->count = 1;
226
44.1k
    StreamPool_AddUsed(pool, s);
227
44.1k
  }
228
229
44.1k
out_fail:
230
44.1k
  StreamPool_Unlock(pool);
231
232
44.1k
  return s;
233
44.1k
}
234
235
/**
236
 * Returns an object to the pool.
237
 */
238
239
static void StreamPool_Remove(wStreamPool* pool, wStream* s)
240
44.1k
{
241
44.1k
  StreamPool_EnsureCapacity(pool, 1, FALSE);
242
44.1k
  Stream_EnsureValidity(s);
243
48.2k
  for (size_t x = 0; x < pool->aSize; x++)
244
4.12k
  {
245
4.12k
    wStream* cs = pool->aArray[x];
246
247
4.12k
    WINPR_ASSERT(cs != s);
248
4.12k
  }
249
44.1k
  pool->aArray[(pool->aSize)++] = s;
250
44.1k
  StreamPool_RemoveUsed(pool, s);
251
44.1k
}
252
253
static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
254
46.6k
{
255
46.6k
  StreamPool_Lock(pool);
256
46.6k
  if (s->count > 0)
257
46.6k
    s->count--;
258
46.6k
  if (s->count == 0)
259
44.1k
    StreamPool_Remove(pool, s);
260
46.6k
  StreamPool_Unlock(pool);
261
46.6k
}
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
2.64k
{
280
2.64k
  WINPR_ASSERT(s);
281
2.64k
  if (s->pool)
282
2.54k
  {
283
2.54k
    StreamPool_Lock(s->pool);
284
2.54k
    s->count++;
285
2.54k
    StreamPool_Unlock(s->pool);
286
2.54k
  }
287
2.64k
}
288
289
/**
290
 * Decrement stream reference count
291
 */
292
293
void Stream_Release(wStream* s)
294
46.7k
{
295
46.7k
  WINPR_ASSERT(s);
296
46.7k
  if (s->pool)
297
46.6k
    StreamPool_ReleaseOrReturn(s->pool, s);
298
46.7k
}
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
39.8k
{
333
39.8k
  StreamPool_Lock(pool);
334
335
83.5k
  while (pool->aSize > 0)
336
43.7k
  {
337
43.7k
    wStream* s = pool->aArray[--pool->aSize];
338
43.7k
    Stream_Free(s, s->isAllocatedStream);
339
43.7k
  }
340
341
39.8k
  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
39.8k
  StreamPool_Unlock(pool);
348
39.8k
}
349
350
/**
351
 * Construction, Destruction
352
 */
353
354
wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
355
39.8k
{
356
39.8k
  wStreamPool* pool = NULL;
357
358
39.8k
  pool = (wStreamPool*)calloc(1, sizeof(wStreamPool));
359
360
39.8k
  if (pool)
361
39.8k
  {
362
39.8k
    pool->synchronized = synchronized;
363
39.8k
    pool->defaultSize = defaultSize;
364
365
39.8k
    if (!StreamPool_EnsureCapacity(pool, 32, FALSE))
366
0
      goto fail;
367
39.8k
    if (!StreamPool_EnsureCapacity(pool, 32, TRUE))
368
0
      goto fail;
369
370
39.8k
    InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
371
39.8k
  }
372
373
39.8k
  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
39.8k
}
381
382
void StreamPool_Free(wStreamPool* pool)
383
39.8k
{
384
39.8k
  if (pool)
385
39.8k
  {
386
39.8k
    StreamPool_Clear(pool);
387
388
39.8k
    DeleteCriticalSection(&pool->lock);
389
390
39.8k
    free(pool->aArray);
391
39.8k
    free(pool->uArray);
392
393
39.8k
    free(pool);
394
39.8k
  }
395
39.8k
}
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
}