Coverage Report

Created: 2023-09-25 06:56

/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;
70
0
  size_t* size;
71
0
  wStream*** array;
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;
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
    if ((pool->uSize - index - pcount) > 0)
121
0
    {
122
0
      MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount],
123
0
                 (pool->uSize - index - pcount) * sizeof(wStream*));
124
0
    }
125
126
0
    pool->uSize -= pcount;
127
0
  }
128
0
}
129
130
/**
131
 * Adds a used stream to the pool.
132
 */
133
134
static void StreamPool_AddUsed(wStreamPool* pool, wStream* s)
135
0
{
136
0
  StreamPool_EnsureCapacity(pool, 1, TRUE);
137
0
  pool->uArray[(pool->uSize)++] = s;
138
0
}
139
140
/**
141
 * Removes a used stream from the pool.
142
 */
143
144
static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s)
145
0
{
146
0
  size_t index;
147
0
  BOOL found = FALSE;
148
149
0
  WINPR_ASSERT(pool);
150
0
  for (index = 0; index < pool->uSize; index++)
151
0
  {
152
0
    if (pool->uArray[index] == s)
153
0
    {
154
0
      found = TRUE;
155
0
      break;
156
0
    }
157
0
  }
158
159
0
  if (found)
160
0
    StreamPool_ShiftUsed(pool, index, -1);
161
0
}
162
163
static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index, INT64 count)
164
0
{
165
0
  WINPR_ASSERT(pool);
166
0
  if (count > 0)
167
0
  {
168
0
    const size_t pcount = (size_t)count;
169
170
0
    StreamPool_EnsureCapacity(pool, pcount, FALSE);
171
0
    MoveMemory(&pool->aArray[index + pcount], &pool->aArray[index],
172
0
               (pool->aSize - index) * sizeof(wStream*));
173
0
    pool->aSize += pcount;
174
0
  }
175
0
  else if (count < 0)
176
0
  {
177
0
    const size_t pcount = (size_t)-count;
178
179
0
    if ((pool->aSize - index - pcount) > 0)
180
0
    {
181
0
      MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
182
0
                 (pool->aSize - index - pcount) * sizeof(wStream*));
183
0
    }
184
185
0
    pool->aSize -= pcount;
186
0
  }
187
0
}
188
189
/**
190
 * Gets a stream from the pool.
191
 */
192
193
wStream* StreamPool_Take(wStreamPool* pool, size_t size)
194
0
{
195
0
  size_t index;
196
0
  SSIZE_T foundIndex;
197
0
  wStream* s = NULL;
198
199
0
  StreamPool_Lock(pool);
200
201
0
  if (size == 0)
202
0
    size = pool->defaultSize;
203
204
0
  foundIndex = -1;
205
206
0
  for (index = 0; index < pool->aSize; index++)
207
0
  {
208
0
    s = pool->aArray[index];
209
210
0
    if (Stream_Capacity(s) >= size)
211
0
    {
212
0
      foundIndex = index;
213
0
      break;
214
0
    }
215
0
  }
216
217
0
  if (foundIndex < 0)
218
0
  {
219
0
    s = Stream_New(NULL, size);
220
0
    if (!s)
221
0
      goto out_fail;
222
0
  }
223
0
  else
224
0
  {
225
0
    Stream_SetPosition(s, 0);
226
0
    Stream_SetLength(s, Stream_Capacity(s));
227
0
    StreamPool_ShiftAvailable(pool, foundIndex, -1);
228
0
  }
229
230
0
  if (s)
231
0
  {
232
0
    s->pool = pool;
233
0
    s->count = 1;
234
0
    StreamPool_AddUsed(pool, s);
235
0
  }
236
237
0
out_fail:
238
0
  StreamPool_Unlock(pool);
239
240
0
  return s;
241
0
}
242
243
/**
244
 * Returns an object to the pool.
245
 */
246
247
static void StreamPool_Remove(wStreamPool* pool, wStream* s)
248
0
{
249
0
  StreamPool_EnsureCapacity(pool, 1, FALSE);
250
0
  Stream_EnsureValidity(s);
251
0
  for (size_t x = 0; x < pool->aSize; x++)
252
0
  {
253
0
    wStream* cs = pool->aArray[x];
254
255
0
    WINPR_ASSERT(cs != s);
256
0
  }
257
0
  pool->aArray[(pool->aSize)++] = s;
258
0
  StreamPool_RemoveUsed(pool, s);
259
0
}
260
261
static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
262
0
{
263
0
  StreamPool_Lock(pool);
264
0
  if (s->count > 0)
265
0
    s->count--;
266
0
  if (s->count == 0)
267
0
    StreamPool_Remove(pool, s);
268
0
  StreamPool_Unlock(pool);
269
0
}
270
271
void StreamPool_Return(wStreamPool* pool, wStream* s)
272
0
{
273
0
  WINPR_ASSERT(pool);
274
0
  if (!s)
275
0
    return;
276
277
0
  StreamPool_Lock(pool);
278
0
  StreamPool_Remove(pool, s);
279
0
  StreamPool_Unlock(pool);
280
0
}
281
282
/**
283
 * Increment stream reference count
284
 */
285
286
void Stream_AddRef(wStream* s)
287
0
{
288
0
  WINPR_ASSERT(s);
289
0
  if (s->pool)
290
0
  {
291
0
    StreamPool_Lock(s->pool);
292
0
    s->count++;
293
0
    StreamPool_Unlock(s->pool);
294
0
  }
295
0
}
296
297
/**
298
 * Decrement stream reference count
299
 */
300
301
void Stream_Release(wStream* s)
302
0
{
303
0
  WINPR_ASSERT(s);
304
0
  if (s->pool)
305
0
    StreamPool_ReleaseOrReturn(s->pool, s);
306
0
}
307
308
/**
309
 * Find stream in pool using pointer inside buffer
310
 */
311
312
wStream* StreamPool_Find(wStreamPool* pool, BYTE* ptr)
313
0
{
314
0
  size_t index;
315
0
  wStream* s = NULL;
316
0
  BOOL found = FALSE;
317
318
0
  StreamPool_Lock(pool);
319
320
0
  for (index = 0; index < pool->uSize; index++)
321
0
  {
322
0
    s = pool->uArray[index];
323
324
0
    if ((ptr >= Stream_Buffer(s)) && (ptr < (Stream_Buffer(s) + Stream_Capacity(s))))
325
0
    {
326
0
      found = TRUE;
327
0
      break;
328
0
    }
329
0
  }
330
331
0
  StreamPool_Unlock(pool);
332
333
0
  return (found) ? s : NULL;
334
0
}
335
336
/**
337
 * Releases the streams currently cached in the pool.
338
 */
339
340
void StreamPool_Clear(wStreamPool* pool)
341
0
{
342
0
  StreamPool_Lock(pool);
343
344
0
  while (pool->aSize > 0)
345
0
  {
346
0
    wStream* s = pool->aArray[--pool->aSize];
347
0
    Stream_Free(s, s->isAllocatedStream);
348
0
  }
349
350
0
  while (pool->uSize > 0)
351
0
  {
352
0
    wStream* s = pool->uArray[--pool->uSize];
353
0
    Stream_Free(s, s->isAllocatedStream);
354
0
  }
355
356
0
  StreamPool_Unlock(pool);
357
0
}
358
359
/**
360
 * Construction, Destruction
361
 */
362
363
wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
364
0
{
365
0
  wStreamPool* pool = NULL;
366
367
0
  pool = (wStreamPool*)calloc(1, sizeof(wStreamPool));
368
369
0
  if (pool)
370
0
  {
371
0
    pool->synchronized = synchronized;
372
0
    pool->defaultSize = defaultSize;
373
374
0
    if (!StreamPool_EnsureCapacity(pool, 32, FALSE))
375
0
      goto fail;
376
0
    if (!StreamPool_EnsureCapacity(pool, 32, TRUE))
377
0
      goto fail;
378
379
0
    InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
380
0
  }
381
382
0
  return pool;
383
0
fail:
384
0
  StreamPool_Free(pool);
385
0
  return NULL;
386
0
}
387
388
void StreamPool_Free(wStreamPool* pool)
389
0
{
390
0
  if (pool)
391
0
  {
392
0
    StreamPool_Clear(pool);
393
394
0
    DeleteCriticalSection(&pool->lock);
395
396
0
    free(pool->aArray);
397
0
    free(pool->uArray);
398
399
0
    free(pool);
400
0
  }
401
0
}
402
403
char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size)
404
0
{
405
0
  WINPR_ASSERT(pool);
406
407
0
  if (!buffer || (size < 1))
408
0
    return NULL;
409
0
  _snprintf(buffer, size - 1,
410
0
            "aSize    =%" PRIuz ", uSize    =%" PRIuz "aCapacity=%" PRIuz ", uCapacity=%" PRIuz,
411
0
            pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity);
412
0
  buffer[size - 1] = '\0';
413
0
  return buffer;
414
0
}