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