1 /**
2  * WinPR: Windows Portable Runtime
3  * BipBuffer
4  *
5  * Copyright 2014 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 <limits.h>
25 #include <winpr/crt.h>
26 #include <winpr/sysinfo.h>
27 
28 #include <winpr/collections.h>
29 
30 /**
31  * The Bip Buffer - The Circular Buffer with a Twist:
32  * http://www.codeproject.com/Articles/3479/The-Bip-Buffer-The-Circular-Buffer-with-a-Twist
33  */
34 
35 #define BipBlock_Clear(_bbl) _bbl.index = _bbl.size = 0
36 
37 #define BipBlock_Copy(_dst, _src) \
38 	_dst.index = _src.index;      \
39 	_dst.size = _src.size
40 
BipBuffer_Clear(wBipBuffer * bb)41 void BipBuffer_Clear(wBipBuffer* bb)
42 {
43 	BipBlock_Clear(bb->blockA);
44 	BipBlock_Clear(bb->blockB);
45 	BipBlock_Clear(bb->readR);
46 	BipBlock_Clear(bb->writeR);
47 }
48 
BipBuffer_AllocBuffer(wBipBuffer * bb,size_t size)49 static BOOL BipBuffer_AllocBuffer(wBipBuffer* bb, size_t size)
50 {
51 	if (size < 1)
52 		return FALSE;
53 
54 	size += size % bb->pageSize;
55 	bb->buffer = (BYTE*)malloc(size);
56 
57 	if (!bb->buffer)
58 		return FALSE;
59 
60 	bb->size = size;
61 	return TRUE;
62 }
63 
BipBuffer_Grow(wBipBuffer * bb,size_t size)64 BOOL BipBuffer_Grow(wBipBuffer* bb, size_t size)
65 {
66 	BYTE* block;
67 	BYTE* buffer;
68 	size_t blockSize = 0;
69 	size_t commitSize = 0;
70 	size += size % bb->pageSize;
71 
72 	if (size <= bb->size)
73 		return TRUE;
74 
75 	buffer = (BYTE*)malloc(size);
76 
77 	if (!buffer)
78 		return FALSE;
79 
80 	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
81 
82 	if (block)
83 	{
84 		CopyMemory(&buffer[commitSize], block, blockSize);
85 		BipBuffer_ReadCommit(bb, blockSize);
86 		commitSize += blockSize;
87 	}
88 
89 	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
90 
91 	if (block)
92 	{
93 		CopyMemory(&buffer[commitSize], block, blockSize);
94 		BipBuffer_ReadCommit(bb, blockSize);
95 		commitSize += blockSize;
96 	}
97 
98 	BipBuffer_Clear(bb);
99 	free(bb->buffer);
100 	bb->buffer = buffer;
101 	bb->size = size;
102 	bb->blockA.index = 0;
103 	bb->blockA.size = commitSize;
104 	return TRUE;
105 }
106 
BipBuffer_FreeBuffer(wBipBuffer * bb)107 static void BipBuffer_FreeBuffer(wBipBuffer* bb)
108 {
109 	if (bb->buffer)
110 	{
111 		free(bb->buffer);
112 		bb->buffer = NULL;
113 	}
114 
115 	BipBuffer_Clear(bb);
116 }
117 
BipBuffer_UsedSize(wBipBuffer * bb)118 size_t BipBuffer_UsedSize(wBipBuffer* bb)
119 {
120 	return bb->blockA.size + bb->blockB.size;
121 }
122 
BipBuffer_BufferSize(wBipBuffer * bb)123 size_t BipBuffer_BufferSize(wBipBuffer* bb)
124 {
125 	return bb->size;
126 }
127 
BipBuffer_WriteTryReserve(wBipBuffer * bb,size_t size,size_t * reserved)128 BYTE* BipBuffer_WriteTryReserve(wBipBuffer* bb, size_t size, size_t* reserved)
129 {
130 	size_t reservable;
131 
132 	if (!reserved)
133 		return NULL;
134 
135 	if (!bb->blockB.size)
136 	{
137 		/* block B does not exist */
138 		reservable = bb->size - bb->blockA.index - bb->blockA.size; /* space after block A */
139 
140 		if (reservable >= bb->blockA.index)
141 		{
142 			if (reservable == 0)
143 				return NULL;
144 
145 			if (size < reservable)
146 				reservable = size;
147 
148 			bb->writeR.size = reservable;
149 			*reserved = reservable;
150 			bb->writeR.index = bb->blockA.index + bb->blockA.size;
151 			return &bb->buffer[bb->writeR.index];
152 		}
153 
154 		if (bb->blockA.index == 0)
155 			return NULL;
156 
157 		if (bb->blockA.index < size)
158 			size = bb->blockA.index;
159 
160 		bb->writeR.size = size;
161 		*reserved = size;
162 		bb->writeR.index = 0;
163 		return bb->buffer;
164 	}
165 
166 	/* block B exists */
167 	reservable = bb->blockA.index - bb->blockB.index - bb->blockB.size; /* space after block B */
168 
169 	if (size < reservable)
170 		reservable = size;
171 
172 	if (reservable == 0)
173 		return NULL;
174 
175 	bb->writeR.size = reservable;
176 	*reserved = reservable;
177 	bb->writeR.index = bb->blockB.index + bb->blockB.size;
178 	return &bb->buffer[bb->writeR.index];
179 }
180 
BipBuffer_WriteReserve(wBipBuffer * bb,size_t size)181 BYTE* BipBuffer_WriteReserve(wBipBuffer* bb, size_t size)
182 {
183 	BYTE* block = NULL;
184 	size_t reserved = 0;
185 	block = BipBuffer_WriteTryReserve(bb, size, &reserved);
186 
187 	if (reserved == size)
188 		return block;
189 
190 	if (!BipBuffer_Grow(bb, size))
191 		return NULL;
192 
193 	block = BipBuffer_WriteTryReserve(bb, size, &reserved);
194 	return block;
195 }
196 
BipBuffer_WriteCommit(wBipBuffer * bb,size_t size)197 void BipBuffer_WriteCommit(wBipBuffer* bb, size_t size)
198 {
199 	if (size == 0)
200 	{
201 		BipBlock_Clear(bb->writeR);
202 		return;
203 	}
204 
205 	if (size > bb->writeR.size)
206 		size = bb->writeR.size;
207 
208 	if ((bb->blockA.size == 0) && (bb->blockB.size == 0))
209 	{
210 		bb->blockA.index = bb->writeR.index;
211 		bb->blockA.size = size;
212 		BipBlock_Clear(bb->writeR);
213 		return;
214 	}
215 
216 	if (bb->writeR.index == (bb->blockA.size + bb->blockA.index))
217 		bb->blockA.size += size;
218 	else
219 		bb->blockB.size += size;
220 
221 	BipBlock_Clear(bb->writeR);
222 }
223 
BipBuffer_Write(wBipBuffer * bb,const BYTE * data,size_t size)224 SSIZE_T BipBuffer_Write(wBipBuffer* bb, const BYTE* data, size_t size)
225 {
226 	size_t status = 0;
227 	BYTE* block = NULL;
228 	size_t writeSize = 0;
229 	size_t blockSize = 0;
230 
231 	if (size == 0)
232 		return 0;
233 
234 	if (!bb || !data)
235 		return -1;
236 
237 	if (size > SSIZE_MAX)
238 		size = SSIZE_MAX;
239 
240 	block = BipBuffer_WriteReserve(bb, size);
241 
242 	if (!block)
243 		return -1;
244 
245 	block = BipBuffer_WriteTryReserve(bb, size - status, &blockSize);
246 
247 	if (block)
248 	{
249 		writeSize = size - status;
250 
251 		if (writeSize > blockSize)
252 			writeSize = blockSize;
253 
254 		CopyMemory(block, &data[status], writeSize);
255 		BipBuffer_WriteCommit(bb, writeSize);
256 		status += writeSize;
257 
258 		if ((status == size) || (writeSize < blockSize))
259 			return (SSIZE_T)status;
260 	}
261 
262 	block = BipBuffer_WriteTryReserve(bb, size - status, &blockSize);
263 
264 	if (block)
265 	{
266 		writeSize = size - status;
267 
268 		if (writeSize > blockSize)
269 			writeSize = blockSize;
270 
271 		CopyMemory(block, &data[status], writeSize);
272 		BipBuffer_WriteCommit(bb, writeSize);
273 		status += writeSize;
274 
275 		if ((status == size) || (writeSize < blockSize))
276 			return (SSIZE_T)status;
277 	}
278 
279 	return (SSIZE_T)status;
280 }
281 
BipBuffer_ReadTryReserve(wBipBuffer * bb,size_t size,size_t * reserved)282 BYTE* BipBuffer_ReadTryReserve(wBipBuffer* bb, size_t size, size_t* reserved)
283 {
284 	size_t reservable = 0;
285 
286 	if (!reserved)
287 		return NULL;
288 
289 	if (bb->blockA.size == 0)
290 	{
291 		*reserved = 0;
292 		return NULL;
293 	}
294 
295 	reservable = bb->blockA.size;
296 
297 	if (size && (reservable > size))
298 		reservable = size;
299 
300 	*reserved = reservable;
301 	return &bb->buffer[bb->blockA.index];
302 }
303 
BipBuffer_ReadReserve(wBipBuffer * bb,size_t size)304 BYTE* BipBuffer_ReadReserve(wBipBuffer* bb, size_t size)
305 {
306 	BYTE* block = NULL;
307 	size_t reserved = 0;
308 
309 	if (BipBuffer_UsedSize(bb) < size)
310 		return NULL;
311 
312 	block = BipBuffer_ReadTryReserve(bb, size, &reserved);
313 
314 	if (reserved == size)
315 		return block;
316 
317 	if (!BipBuffer_Grow(bb, bb->size + 1))
318 		return NULL;
319 
320 	block = BipBuffer_ReadTryReserve(bb, size, &reserved);
321 
322 	if (reserved != size)
323 		return NULL;
324 
325 	return block;
326 }
327 
BipBuffer_ReadCommit(wBipBuffer * bb,size_t size)328 void BipBuffer_ReadCommit(wBipBuffer* bb, size_t size)
329 {
330 	if (!bb)
331 		return;
332 
333 	if (size >= bb->blockA.size)
334 	{
335 		BipBlock_Copy(bb->blockA, bb->blockB);
336 		BipBlock_Clear(bb->blockB);
337 	}
338 	else
339 	{
340 		bb->blockA.size -= size;
341 		bb->blockA.index += size;
342 	}
343 }
344 
BipBuffer_Read(wBipBuffer * bb,BYTE * data,size_t size)345 SSIZE_T BipBuffer_Read(wBipBuffer* bb, BYTE* data, size_t size)
346 {
347 	size_t status = 0;
348 	BYTE* block = NULL;
349 	size_t readSize = 0;
350 	size_t blockSize = 0;
351 
352 	if (size == 0)
353 		return 0;
354 
355 	if (!bb || !data)
356 		return -1;
357 
358 	if (size > SSIZE_MAX)
359 		size = SSIZE_MAX;
360 
361 	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
362 
363 	if (block)
364 	{
365 		readSize = size - status;
366 
367 		if (readSize > blockSize)
368 			readSize = blockSize;
369 
370 		CopyMemory(&data[status], block, readSize);
371 		BipBuffer_ReadCommit(bb, readSize);
372 		status += readSize;
373 
374 		if ((status == size) || (readSize < blockSize))
375 			return (SSIZE_T)status;
376 	}
377 
378 	block = BipBuffer_ReadTryReserve(bb, 0, &blockSize);
379 
380 	if (block)
381 	{
382 		readSize = size - status;
383 
384 		if (readSize > blockSize)
385 			readSize = blockSize;
386 
387 		CopyMemory(&data[status], block, readSize);
388 		BipBuffer_ReadCommit(bb, readSize);
389 		status += readSize;
390 
391 		if ((status == size) || (readSize < blockSize))
392 			return (SSIZE_T)status;
393 	}
394 
395 	return (SSIZE_T)status;
396 }
397 
398 /**
399  * Construction, Destruction
400  */
401 
BipBuffer_New(size_t size)402 wBipBuffer* BipBuffer_New(size_t size)
403 {
404 	wBipBuffer* bb;
405 	bb = (wBipBuffer*)calloc(1, sizeof(wBipBuffer));
406 
407 	if (bb)
408 	{
409 		SYSTEM_INFO si;
410 		GetSystemInfo(&si);
411 		bb->pageSize = (size_t)si.dwPageSize;
412 
413 		if (bb->pageSize < 4096)
414 			bb->pageSize = 4096;
415 
416 		if (!BipBuffer_AllocBuffer(bb, size))
417 		{
418 			free(bb);
419 			return NULL;
420 		}
421 	}
422 
423 	return bb;
424 }
425 
BipBuffer_Free(wBipBuffer * bb)426 void BipBuffer_Free(wBipBuffer* bb)
427 {
428 	if (!bb)
429 		return;
430 
431 	BipBuffer_FreeBuffer(bb);
432 	free(bb);
433 }
434