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