Coverage Report

Created: 2024-05-20 06:11

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