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