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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <winpr/crt.h>
25
26 #include <winpr/collections.h>
27
28 /**
29 * C equivalent of the C# BufferManager Class:
30 * http://msdn.microsoft.com/en-us/library/ms405814.aspx
31 */
32
33 /**
34 * Methods
35 */
36
BufferPool_ShiftAvailable(wBufferPool * pool,int index,int count)37 static BOOL BufferPool_ShiftAvailable(wBufferPool* pool, int index, int count)
38 {
39 if (count > 0)
40 {
41 if (pool->aSize + count > pool->aCapacity)
42 {
43 wBufferPoolItem* newArray;
44 int newCapacity = pool->aCapacity * 2;
45
46 newArray =
47 (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity);
48 if (!newArray)
49 return FALSE;
50 pool->aArray = newArray;
51 pool->aCapacity = newCapacity;
52 }
53
54 MoveMemory(&pool->aArray[index + count], &pool->aArray[index],
55 (pool->aSize - index) * sizeof(wBufferPoolItem));
56 pool->aSize += count;
57 }
58 else if (count < 0)
59 {
60 MoveMemory(&pool->aArray[index], &pool->aArray[index - count],
61 (pool->aSize - index) * sizeof(wBufferPoolItem));
62 pool->aSize += count;
63 }
64 return TRUE;
65 }
66
BufferPool_ShiftUsed(wBufferPool * pool,int index,int count)67 static BOOL BufferPool_ShiftUsed(wBufferPool* pool, int index, int count)
68 {
69 if (count > 0)
70 {
71 if (pool->uSize + count > pool->uCapacity)
72 {
73 int newUCapacity = pool->uCapacity * 2;
74 wBufferPoolItem* newUArray =
75 (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
76 if (!newUArray)
77 return FALSE;
78 pool->uCapacity = newUCapacity;
79 pool->uArray = newUArray;
80 }
81
82 MoveMemory(&pool->uArray[index + count], &pool->uArray[index],
83 (pool->uSize - index) * sizeof(wBufferPoolItem));
84 pool->uSize += count;
85 }
86 else if (count < 0)
87 {
88 MoveMemory(&pool->uArray[index], &pool->uArray[index - count],
89 (pool->uSize - index) * sizeof(wBufferPoolItem));
90 pool->uSize += count;
91 }
92 return TRUE;
93 }
94
95 /**
96 * Get the buffer pool size
97 */
98
BufferPool_GetPoolSize(wBufferPool * pool)99 int BufferPool_GetPoolSize(wBufferPool* pool)
100 {
101 int size;
102
103 if (pool->synchronized)
104 EnterCriticalSection(&pool->lock);
105
106 if (pool->fixedSize)
107 {
108 /* fixed size buffers */
109 size = pool->size;
110 }
111 else
112 {
113 /* variable size buffers */
114 size = pool->uSize;
115 }
116
117 if (pool->synchronized)
118 LeaveCriticalSection(&pool->lock);
119
120 return size;
121 }
122
123 /**
124 * Get the size of a pooled buffer
125 */
126
BufferPool_GetBufferSize(wBufferPool * pool,void * buffer)127 int BufferPool_GetBufferSize(wBufferPool* pool, void* buffer)
128 {
129 int size = 0;
130 int index = 0;
131 BOOL found = FALSE;
132
133 if (pool->synchronized)
134 EnterCriticalSection(&pool->lock);
135
136 if (pool->fixedSize)
137 {
138 /* fixed size buffers */
139 size = pool->fixedSize;
140 found = TRUE;
141 }
142 else
143 {
144 /* variable size buffers */
145
146 for (index = 0; index < pool->uSize; index++)
147 {
148 if (pool->uArray[index].buffer == buffer)
149 {
150 size = pool->uArray[index].size;
151 found = TRUE;
152 break;
153 }
154 }
155 }
156
157 if (pool->synchronized)
158 LeaveCriticalSection(&pool->lock);
159
160 return (found) ? size : -1;
161 }
162
163 /**
164 * Gets a buffer of at least the specified size from the pool.
165 */
166
BufferPool_Take(wBufferPool * pool,int size)167 void* BufferPool_Take(wBufferPool* pool, int size)
168 {
169 int index;
170 int maxSize;
171 int maxIndex;
172 int foundIndex;
173 BOOL found = FALSE;
174 void* buffer = NULL;
175
176 if (pool->synchronized)
177 EnterCriticalSection(&pool->lock);
178
179 if (pool->fixedSize)
180 {
181 /* fixed size buffers */
182
183 if (pool->size > 0)
184 buffer = pool->array[--(pool->size)];
185
186 if (!buffer)
187 {
188 if (pool->alignment)
189 buffer = _aligned_malloc(pool->fixedSize, pool->alignment);
190 else
191 buffer = malloc(pool->fixedSize);
192 }
193
194 if (!buffer)
195 goto out_error;
196 }
197 else
198 {
199 /* variable size buffers */
200
201 maxSize = 0;
202 maxIndex = 0;
203
204 if (size < 1)
205 size = pool->fixedSize;
206
207 for (index = 0; index < pool->aSize; index++)
208 {
209 if (pool->aArray[index].size > maxSize)
210 {
211 maxIndex = index;
212 maxSize = pool->aArray[index].size;
213 }
214
215 if (pool->aArray[index].size >= size)
216 {
217 foundIndex = index;
218 found = TRUE;
219 break;
220 }
221 }
222
223 if (!found && maxSize)
224 {
225 foundIndex = maxIndex;
226 found = TRUE;
227 }
228
229 if (!found)
230 {
231 if (!size)
232 buffer = NULL;
233 else
234 {
235 if (pool->alignment)
236 buffer = _aligned_malloc(size, pool->alignment);
237 else
238 buffer = malloc(size);
239
240 if (!buffer)
241 goto out_error;
242 }
243 }
244 else
245 {
246 buffer = pool->aArray[foundIndex].buffer;
247
248 if (maxSize < size)
249 {
250 void* newBuffer;
251 if (pool->alignment)
252 newBuffer = _aligned_realloc(buffer, size, pool->alignment);
253 else
254 newBuffer = realloc(buffer, size);
255
256 if (!newBuffer)
257 goto out_error_no_free;
258
259 buffer = newBuffer;
260 }
261
262 if (!BufferPool_ShiftAvailable(pool, foundIndex, -1))
263 goto out_error;
264 }
265
266 if (!buffer)
267 goto out_error;
268
269 if (pool->uSize + 1 > pool->uCapacity)
270 {
271 int newUCapacity = pool->uCapacity * 2;
272 wBufferPoolItem* newUArray =
273 (wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
274 if (!newUArray)
275 goto out_error;
276
277 pool->uCapacity = newUCapacity;
278 pool->uArray = newUArray;
279 }
280
281 pool->uArray[pool->uSize].buffer = buffer;
282 pool->uArray[pool->uSize].size = size;
283 (pool->uSize)++;
284 }
285
286 if (pool->synchronized)
287 LeaveCriticalSection(&pool->lock);
288
289 return buffer;
290
291 out_error:
292 if (pool->alignment)
293 _aligned_free(buffer);
294 else
295 free(buffer);
296 out_error_no_free:
297 if (pool->synchronized)
298 LeaveCriticalSection(&pool->lock);
299 return NULL;
300 }
301
302 /**
303 * Returns a buffer to the pool.
304 */
305
BufferPool_Return(wBufferPool * pool,void * buffer)306 BOOL BufferPool_Return(wBufferPool* pool, void* buffer)
307 {
308 int size = 0;
309 int index = 0;
310 BOOL found = FALSE;
311
312 if (pool->synchronized)
313 EnterCriticalSection(&pool->lock);
314
315 if (pool->fixedSize)
316 {
317 /* fixed size buffers */
318
319 if ((pool->size + 1) >= pool->capacity)
320 {
321 int newCapacity = pool->capacity * 2;
322 void** newArray = (void**)realloc(pool->array, sizeof(void*) * newCapacity);
323 if (!newArray)
324 goto out_error;
325
326 pool->capacity = newCapacity;
327 pool->array = newArray;
328 }
329
330 pool->array[(pool->size)++] = buffer;
331 }
332 else
333 {
334 /* variable size buffers */
335
336 for (index = 0; index < pool->uSize; index++)
337 {
338 if (pool->uArray[index].buffer == buffer)
339 {
340 found = TRUE;
341 break;
342 }
343 }
344
345 if (found)
346 {
347 size = pool->uArray[index].size;
348 if (!BufferPool_ShiftUsed(pool, index, -1))
349 goto out_error;
350 }
351
352 if (size)
353 {
354 if ((pool->aSize + 1) >= pool->aCapacity)
355 {
356 int newCapacity = pool->aCapacity * 2;
357 wBufferPoolItem* newArray =
358 (wBufferPoolItem*)realloc(pool->aArray, sizeof(wBufferPoolItem) * newCapacity);
359 if (!newArray)
360 goto out_error;
361
362 pool->aCapacity = newCapacity;
363 pool->aArray = newArray;
364 }
365
366 pool->aArray[pool->aSize].buffer = buffer;
367 pool->aArray[pool->aSize].size = size;
368 (pool->aSize)++;
369 }
370 }
371
372 if (pool->synchronized)
373 LeaveCriticalSection(&pool->lock);
374 return TRUE;
375
376 out_error:
377 if (pool->synchronized)
378 LeaveCriticalSection(&pool->lock);
379 return FALSE;
380 }
381
382 /**
383 * Releases the buffers currently cached in the pool.
384 */
385
BufferPool_Clear(wBufferPool * pool)386 void BufferPool_Clear(wBufferPool* pool)
387 {
388 if (pool->synchronized)
389 EnterCriticalSection(&pool->lock);
390
391 if (pool->fixedSize)
392 {
393 /* fixed size buffers */
394
395 while (pool->size > 0)
396 {
397 (pool->size)--;
398
399 if (pool->alignment)
400 _aligned_free(pool->array[pool->size]);
401 else
402 free(pool->array[pool->size]);
403 }
404 }
405 else
406 {
407 /* variable size buffers */
408
409 while (pool->aSize > 0)
410 {
411 (pool->aSize)--;
412
413 if (pool->alignment)
414 _aligned_free(pool->aArray[pool->aSize].buffer);
415 else
416 free(pool->aArray[pool->aSize].buffer);
417 }
418
419 while (pool->uSize > 0)
420 {
421 (pool->uSize)--;
422
423 if (pool->alignment)
424 _aligned_free(pool->uArray[pool->uSize].buffer);
425 else
426 free(pool->uArray[pool->uSize].buffer);
427 }
428 }
429
430 if (pool->synchronized)
431 LeaveCriticalSection(&pool->lock);
432 }
433
434 /**
435 * Construction, Destruction
436 */
437
BufferPool_New(BOOL synchronized,int fixedSize,DWORD alignment)438 wBufferPool* BufferPool_New(BOOL synchronized, int fixedSize, DWORD alignment)
439 {
440 wBufferPool* pool = NULL;
441
442 pool = (wBufferPool*)malloc(sizeof(wBufferPool));
443
444 if (pool)
445 {
446 pool->fixedSize = fixedSize;
447
448 if (pool->fixedSize < 0)
449 pool->fixedSize = 0;
450
451 pool->alignment = alignment;
452 pool->synchronized = synchronized;
453
454 if (pool->synchronized)
455 InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
456
457 if (pool->fixedSize)
458 {
459 /* fixed size buffers */
460
461 pool->size = 0;
462 pool->capacity = 32;
463 pool->array = (void**)calloc(pool->capacity, sizeof(void*));
464 if (!pool->array)
465 goto out_error;
466 }
467 else
468 {
469 /* variable size buffers */
470
471 pool->aSize = 0;
472 pool->aCapacity = 32;
473 pool->aArray = (wBufferPoolItem*)calloc(pool->aCapacity, sizeof(wBufferPoolItem));
474 if (!pool->aArray)
475 goto out_error;
476
477 pool->uSize = 0;
478 pool->uCapacity = 32;
479 pool->uArray = (wBufferPoolItem*)calloc(pool->uCapacity, sizeof(wBufferPoolItem));
480 if (!pool->uArray)
481 {
482 free(pool->aArray);
483 goto out_error;
484 }
485 }
486 }
487
488 return pool;
489
490 out_error:
491 if (pool->synchronized)
492 DeleteCriticalSection(&pool->lock);
493 free(pool);
494 return NULL;
495 }
496
BufferPool_Free(wBufferPool * pool)497 void BufferPool_Free(wBufferPool* pool)
498 {
499 if (pool)
500 {
501 BufferPool_Clear(pool);
502
503 if (pool->synchronized)
504 DeleteCriticalSection(&pool->lock);
505
506 if (pool->fixedSize)
507 {
508 /* fixed size buffers */
509
510 free(pool->array);
511 }
512 else
513 {
514 /* variable size buffers */
515
516 free(pool->aArray);
517 free(pool->uArray);
518 }
519
520 free(pool);
521 }
522 }
523