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