1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
12 #include "sjme/circleBuffer.h"
13 #include "sjme/debug.h"
16 * Generic operation for circle buffers.
20 typedef enum sjme_circleBuffer_operation
22 /** Push operation, queue. */
23 SJME_CIRCLE_BUFFER_PUSH_QUEUE
,
25 /** Push operation, window. */
26 SJME_CIRCLE_BUFFER_PUSH_WINDOW
,
29 SJME_CIRCLE_BUFFER_POP
,
32 SJME_CIRCLE_BUFFER_GET
,
34 /** The number of operations that can be performed. */
35 SJME_CIRCLE_BUFFER_NUM_OPERATIONS
36 } sjme_circleBuffer_operation
;
39 * A slice for accessing a buffer.
43 typedef struct sjme_circleBuffer_slice
45 /** Non-circle buffer pointer? */
46 sjme_pointer externalBuf
;
48 /** The base position. */
53 } sjme_circleBuffer_slice
;
56 * Stores the calculated result of the operation.
60 typedef struct sjme_circleBuffer_calcResult
65 /** The new write head. */
66 sjme_jint newWriteHead
;
68 /** The new read head. */
69 sjme_jint newReadHead
;
71 /** The source data slice. */
72 sjme_circleBuffer_slice src
[2];
74 /** The destination data slice. */
75 sjme_circleBuffer_slice dest
[2];
76 } sjme_circleBuffer_calcResult
;
78 static sjme_errorCode
sjme_circleBuffer_calc(
79 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
80 sjme_attrOutNotNull sjme_circleBuffer_calcResult
* result
,
81 sjme_attrInValue sjme_circleBuffer_operation operation
,
82 sjme_attrInNotNullBuf(length
) sjme_pointer opData
,
83 sjme_attrInPositiveNonZero sjme_jint length
,
84 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
,
85 sjme_attrInPositiveNonZero sjme_jint seekPos
)
87 sjme_circleBuffer_slice (*circleSl
)[2];
88 sjme_circleBuffer_slice (*externSl
)[2];
89 sjme_jint cutLen
, cutPos
;
91 if (buffer
== NULL
|| result
== NULL
|| opData
== NULL
)
92 return SJME_ERROR_NULL_ARGUMENTS
;
94 if (seekType
!= SJME_CIRCLE_BUFFER_HEAD
&&
95 seekType
!= SJME_CIRCLE_BUFFER_TAIL
)
96 return SJME_ERROR_INVALID_ARGUMENT
;
98 if (operation
!= SJME_CIRCLE_BUFFER_GET
&& seekPos
!= 0)
99 return SJME_ERROR_INVALID_ARGUMENT
;
101 if (seekPos
< 0 || length
< 0 || (seekPos
+ length
) < 0)
102 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
104 /* Clear result first. */
105 memset(result
, 0, sizeof(*result
));
108 if (operation
== SJME_CIRCLE_BUFFER_PUSH_QUEUE
||
109 operation
== SJME_CIRCLE_BUFFER_PUSH_WINDOW
)
111 /* Source slice is the data we want to put in. */
112 result
->src
[0].externalBuf
= opData
;
113 result
->src
[0].base
= 0;
114 result
->src
[0].len
= length
;
116 /* Should always be the same. */
117 result
->dest
[0].len
= length
;
119 /* Calculate new current data in the buffer. */
120 result
->newReady
= buffer
->ready
+ length
;
121 if (result
->newReady
> buffer
->size
)
123 if (operation
!= SJME_CIRCLE_BUFFER_PUSH_WINDOW
)
124 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
126 /* Cannot add more than what the buffer can contain. */
127 if (length
> buffer
->size
)
128 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
130 /* Clip back into bounds. */
131 result
->newReady
= buffer
->size
;
133 /* Completely erasing the entire buffer? */
134 if (length
== buffer
->size
)
136 /* Write over everything. */
137 result
->dest
[0].base
= 0;
138 result
->dest
[0].len
= length
;
140 /* Set new details. */
141 result
->newReadHead
= 0;
142 result
->newWriteHead
= 0;
145 return SJME_ERROR_NONE
;
150 if (seekType
== SJME_CIRCLE_BUFFER_LAST
)
152 result
->newWriteHead
= buffer
->writeHead
+ length
;
153 result
->dest
[0].base
= buffer
->writeHead
;
156 /* Writing at start */
159 result
->newReadHead
= buffer
->readHead
- length
;
160 result
->dest
[0].base
= result
->newReadHead
;
165 else if (operation
== SJME_CIRCLE_BUFFER_POP
)
168 return sjme_error_notImplemented(0);
172 else if (operation
== SJME_CIRCLE_BUFFER_GET
)
174 /* Cannot get more data than exists in the buffer. */
175 if (seekPos
+ length
< 0 ||
176 seekPos
+ length
> buffer
->ready
)
177 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
179 /* We want to copy to the destination output buffer. */
180 result
->dest
[0].externalBuf
= opData
;
181 result
->dest
[0].base
= 0;
182 result
->dest
[0].len
= length
;
184 /* The positions in the circle buffer are relative to the heads. */
185 result
->src
[0].len
= length
;
186 if (seekType
== SJME_CIRCLE_BUFFER_LAST
)
187 result
->src
[0].base
= buffer
->writeHead
- seekPos
;
189 result
->src
[0].base
= buffer
->readHead
+ seekPos
;
194 return SJME_ERROR_INVALID_ARGUMENT
;
196 /* Get appropriate slice for clipping. */
197 if (result
->src
[0].externalBuf
)
199 externSl
= &result
->src
;
200 circleSl
= &result
->dest
;
204 externSl
= &result
->dest
;
205 circleSl
= &result
->src
;
208 /* Slice lower region? */
209 if ((*circleSl
)[0].base
< 0)
211 /* How much to cut? */
212 cutLen
= -((*circleSl
)[0].base
);
214 /* Second slice gets what was trimmed off. */
215 (*circleSl
)[1].base
= buffer
->size
- cutLen
;
216 (*circleSl
)[1].len
= cutLen
;
217 (*externSl
)[1].base
= length
- cutLen
;
218 (*externSl
)[1].len
= cutLen
;
220 /* Make sure second slice gets the buffer! */
221 (*externSl
)[1].externalBuf
= (*externSl
)[0].externalBuf
;
223 /* First slice is adjusted accordingly. */
224 (*circleSl
)[0].base
= 0;
225 (*circleSl
)[0].len
= (*circleSl
)[0].len
- cutLen
;
226 (*externSl
)[0].base
= 0;
227 (*externSl
)[0].len
= (*externSl
)[1].base
;
230 /* Slice higher region? */
231 else if ((*circleSl
)[0].base
+ (*circleSl
)[0].len
> buffer
->size
)
233 /* How much to cut? */
234 cutLen
= buffer
->size
- ((*circleSl
)[0].base
);
235 cutPos
= length
- cutLen
;
237 /* Second slice gets what was trimmed off. */
238 (*circleSl
)[1].base
= 0;
239 (*circleSl
)[1].len
= cutLen
;
240 (*externSl
)[1].base
= cutPos
;
241 (*externSl
)[1].len
= cutLen
;
243 /* Make sure second slice gets the buffer! */
244 (*externSl
)[1].externalBuf
= (*externSl
)[0].externalBuf
;
246 /* First slice is adjusted accordingly. */
247 (*circleSl
)[0].base
= (*circleSl
)[0].base
;
248 (*circleSl
)[0].len
= buffer
->size
- cutLen
;
249 (*externSl
)[0].base
= (*externSl
)[0].base
;
250 (*externSl
)[0].len
= cutPos
;
253 /* Make sure read/write heads are wrapped properly. */
254 /* From negative position. */
255 while (result
->newReadHead
< 0)
256 result
->newReadHead
+= buffer
->size
;
257 while (result
->newWriteHead
< 0)
258 result
->newWriteHead
+= buffer
->size
;
260 /* From positive position. */
261 while (result
->newReadHead
>= buffer
->size
)
262 result
->newReadHead
-= buffer
->size
;
263 while (result
->newWriteHead
>= buffer
->size
)
264 result
->newWriteHead
-= buffer
->size
;
267 return SJME_ERROR_NONE
;
270 static sjme_errorCode
sjme_circleBuffer_operate(
271 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
272 sjme_attrInNotNull sjme_circleBuffer_calcResult
* result
)
275 sjme_circleBuffer_slice
* src
;
276 sjme_circleBuffer_slice
* dest
;
278 if (buffer
== NULL
|| result
== NULL
)
279 return SJME_ERROR_NULL_ARGUMENTS
;
281 /* These must be in range. */
282 if (result
->newReady
< 0 || result
->newReady
> buffer
->size
||
283 result
->newReadHead
< 0 || result
->newReadHead
> buffer
->size
||
284 result
->newWriteHead
< 0 || result
->newWriteHead
> buffer
->size
)
285 return SJME_ERROR_ILLEGAL_STATE
;
287 /* Copy each slice, if valid. */
288 for (i
= 0; i
< 2; i
++)
290 src
= &result
->src
[i
];
291 dest
= &result
->dest
[i
];
293 /* Skip if nothing here. */
294 if (src
->len
== 0 && dest
->len
== 0)
297 /* Fail if both external or both internal. */
298 if ((src
->externalBuf
== NULL
) == (dest
->externalBuf
== NULL
))
299 return SJME_ERROR_ILLEGAL_STATE
;
301 /* These must always be the same. */
302 if (src
->len
!= dest
->len
)
303 return SJME_ERROR_ILLEGAL_STATE
;
305 /* External to internal. */
306 if (src
->externalBuf
!= NULL
&& dest
->externalBuf
== NULL
)
307 memmove(&buffer
->buffer
[dest
->base
],
308 SJME_POINTER_OFFSET(src
->externalBuf
, src
->base
),
311 /* Internal to external. */
312 else if (src
->externalBuf
== NULL
&& dest
->externalBuf
!= NULL
)
313 memmove(SJME_POINTER_OFFSET(dest
->externalBuf
, dest
->base
),
314 &buffer
->buffer
[src
->base
],
317 /* Should not occur. */
319 return SJME_ERROR_ILLEGAL_STATE
;
323 buffer
->ready
= result
->newReady
;
324 buffer
->readHead
= result
->newReadHead
;
325 buffer
->writeHead
= result
->newWriteHead
;
328 return SJME_ERROR_NONE
;
331 sjme_errorCode
sjme_circleBuffer_available(
332 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
333 sjme_attrOutNotNull sjme_jint
* outAvailable
)
335 if (buffer
== NULL
|| outAvailable
== NULL
)
336 return SJME_ERROR_NULL_ARGUMENTS
;
338 *outAvailable
= buffer
->size
- buffer
->ready
;
339 return SJME_ERROR_NONE
;
342 sjme_errorCode
sjme_circleBuffer_destroy(
343 sjme_attrInNotNull sjme_circleBuffer
* buffer
)
345 sjme_errorCode error
;
348 return SJME_ERROR_NULL_ARGUMENTS
;
350 /* Free storage first. */
351 if (buffer
->buffer
!= NULL
)
353 if (sjme_error_is(error
= sjme_alloc_free(buffer
->buffer
)))
354 return sjme_error_default(error
);
355 buffer
->buffer
= NULL
;
358 /* Then the buffer info. */
359 return sjme_alloc_free(buffer
);
362 sjme_errorCode
sjme_circleBuffer_get(
363 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
364 sjme_attrOutNotNullBuf(outDataLen
) sjme_pointer outData
,
365 sjme_attrInPositiveNonZero sjme_jint length
,
366 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
,
367 sjme_attrInPositiveNonZero sjme_jint seekPos
)
369 sjme_errorCode error
;
370 sjme_circleBuffer_calcResult result
;
373 return SJME_ERROR_NULL_ARGUMENTS
;
375 /* Calculate result. */
376 memset(&result
, 0, sizeof(result
));
377 if (sjme_error_is(error
= sjme_circleBuffer_calc(
379 SJME_CIRCLE_BUFFER_GET
,
380 outData
, length
, seekType
, seekPos
)))
381 return sjme_error_default(error
);
384 return sjme_circleBuffer_operate(buffer
, &result
);
387 sjme_errorCode
sjme_circleBuffer_new(
388 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
389 sjme_attrOutNotNull sjme_circleBuffer
** outBuffer
,
390 sjme_attrInValue sjme_circleBuffer_mode inMode
,
391 sjme_attrInPositiveNonZero sjme_jint length
)
393 sjme_errorCode error
;
394 sjme_pointer storage
;
395 sjme_circleBuffer
* result
;
397 if (inPool
== NULL
|| outBuffer
== NULL
)
398 return SJME_ERROR_NULL_ARGUMENTS
;
400 if (inMode
!= SJME_CIRCLE_BUFFER_QUEUE
&&
401 inMode
!= SJME_CIRCLE_BUFFER_WINDOW
)
402 return SJME_ERROR_INVALID_ARGUMENT
;
405 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
407 /* Make sure we can allocate the buffer first. */
409 if (sjme_error_is(error
= sjme_alloc(inPool
,
410 length
, (sjme_pointer
*)&storage
)) || storage
== NULL
)
411 goto fail_allocStorage
;
413 /* Then the actual buffer info. */
415 if (sjme_error_is(error
= sjme_alloc(inPool
,
416 sizeof(*result
), (sjme_pointer
*)&result
)) || result
== NULL
)
417 goto fail_allocResult
;
419 /* Setup information. */
420 result
->size
= length
;
421 result
->mode
= inMode
;
422 result
->buffer
= storage
;
426 return SJME_ERROR_NONE
;
430 sjme_alloc_free(result
);
433 sjme_alloc_free(storage
);
435 return sjme_error_default(error
);
438 sjme_errorCode
sjme_circleBuffer_pop(
439 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
440 sjme_attrOutNotNullBuf(outDataLen
) sjme_pointer outData
,
441 sjme_attrInPositiveNonZero sjme_jint length
,
442 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
)
444 sjme_errorCode error
;
445 sjme_circleBuffer_calcResult result
;
448 return SJME_ERROR_NULL_ARGUMENTS
;
450 /* Calculate result. */
451 memset(&result
, 0, sizeof(result
));
452 if (sjme_error_is(error
= sjme_circleBuffer_calc(
454 SJME_CIRCLE_BUFFER_POP
,
455 outData
, length
, seekType
, 0)))
456 return sjme_error_default(error
);
459 return sjme_circleBuffer_operate(buffer
, &result
);
462 sjme_errorCode
sjme_circleBuffer_push(
463 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
464 sjme_attrInNotNullBuf(outDataLen
) sjme_cpointer inData
,
465 sjme_attrInPositiveNonZero sjme_jint length
,
466 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
)
468 sjme_errorCode error
;
469 sjme_circleBuffer_calcResult result
;
472 return SJME_ERROR_NULL_ARGUMENTS
;
474 /* Calculate result. */
475 memset(&result
, 0, sizeof(result
));
476 if (sjme_error_is(error
= sjme_circleBuffer_calc(
478 (buffer
->mode
== SJME_CIRCLE_BUFFER_WINDOW
?
479 SJME_CIRCLE_BUFFER_PUSH_WINDOW
: SJME_CIRCLE_BUFFER_PUSH_QUEUE
),
480 inData
, length
, seekType
, 0)))
481 return sjme_error_default(error
);
484 return sjme_circleBuffer_operate(buffer
, &result
);
487 sjme_errorCode
sjme_circleBuffer_stored(
488 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
489 sjme_attrOutNotNull sjme_jint
* outStored
)
491 if (buffer
== NULL
|| outStored
== NULL
)
492 return SJME_ERROR_NULL_ARGUMENTS
;
494 *outStored
= buffer
->ready
;
495 return SJME_ERROR_NONE
;