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_splice(
79 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
80 sjme_attrOutNotNull
const sjme_circleBuffer_slice
* circleSl
,
81 sjme_attrOutNotNull
const sjme_circleBuffer_slice
* externSl
,
82 sjme_attrOutNotNull
const sjme_circleBuffer_slice
* srcSl
,
83 sjme_attrOutNotNull
const sjme_circleBuffer_slice
* destSl
,
84 sjme_attrOutNotNull sjme_circleBuffer_calcResult
* result
)
86 sjme_circleBuffer_slice
* circleAl
;
87 sjme_circleBuffer_slice
* externAl
;
88 sjme_circleBuffer_slice
* circleBl
;
89 sjme_circleBuffer_slice
* externBl
;
90 sjme_circleBuffer_slice
* tempXl
;
91 sjme_jint clipped
, len
, circleBase
, externBase
, limit
;
93 if (buffer
== NULL
|| circleSl
== NULL
|| externSl
== NULL
||
94 srcSl
== NULL
|| destSl
== NULL
|| result
== NULL
)
95 return SJME_ERROR_NULL_ARGUMENTS
;
97 /* Common parameters used in the clipping algorithm. */
98 circleBase
= circleSl
->base
;
99 externBase
= externSl
->base
;
104 sjme_message("len: %d", len
);
107 /* Completely an inside slice? */
108 if (circleBase
>= 0 && circleBase
< buffer
->size
&&
109 circleBase
+ len
<= buffer
->size
)
111 result
->src
[0] = *srcSl
;
112 result
->dest
[0] = *destSl
;
114 return SJME_ERROR_NONE
;
117 /* Determine source and destination A and B sides to the slice. */
118 if (srcSl
== circleSl
)
120 circleAl
= &result
->src
[0];
121 externAl
= &result
->dest
[0];
122 circleBl
= &result
->src
[1];
123 externBl
= &result
->dest
[1];
127 circleAl
= &result
->dest
[0];
128 externAl
= &result
->src
[0];
129 circleBl
= &result
->dest
[1];
130 externBl
= &result
->src
[1];
133 /* External in opposite order? */
134 if (circleBase
< 0 && circleBase
+ len
> 0)
141 /* Left side slice? */
144 clipped
= 0 - circleBase
;
147 circleAl
->len
= len
- clipped
;
149 externAl
->len
= len
- clipped
;
151 circleBl
->base
= buffer
->size
- clipped
;
152 circleBl
->len
= clipped
;
153 externBl
->base
= len
- clipped
;
154 externBl
->len
= clipped
;
157 /* Right side slice? */
160 clipped
= (circleBase
+ len
) - buffer
->size
;
161 limit
= buffer
->size
- circleBase
;
163 circleAl
->base
= circleBase
;
164 circleAl
->len
= limit
;
166 externAl
->len
= limit
;
169 circleBl
->len
= clipped
;
170 externBl
->base
= externBase
+ limit
;
171 externBl
->len
= clipped
;
174 /* Propagate buffers. */
175 externAl
->externalBuf
= externSl
->externalBuf
;
176 externBl
->externalBuf
= externSl
->externalBuf
;
179 return SJME_ERROR_NONE
;
182 static sjme_errorCode
sjme_circleBuffer_calc(
183 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
184 sjme_attrOutNotNull sjme_circleBuffer_calcResult
* result
,
185 sjme_attrInValue sjme_circleBuffer_operation operation
,
186 sjme_attrInNotNullBuf(length
) sjme_pointer opData
,
187 sjme_attrInPositiveNonZero sjme_jint length
,
188 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
,
189 sjme_attrInPositiveNonZero sjme_jint seekPos
)
191 sjme_circleBuffer_slice circleSl
;
192 sjme_circleBuffer_slice externSl
;
193 sjme_circleBuffer_slice
* srcSl
;
194 sjme_circleBuffer_slice
* destSl
;
196 if (buffer
== NULL
|| result
== NULL
|| opData
== NULL
)
197 return SJME_ERROR_NULL_ARGUMENTS
;
199 if (seekType
!= SJME_CIRCLE_BUFFER_HEAD
&&
200 seekType
!= SJME_CIRCLE_BUFFER_TAIL
)
201 return SJME_ERROR_INVALID_ARGUMENT
;
203 if (operation
!= SJME_CIRCLE_BUFFER_GET
&& seekPos
!= 0)
204 return SJME_ERROR_INVALID_ARGUMENT
;
206 if (seekPos
< 0 || length
< 0 || (seekPos
+ length
) < 0)
207 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
209 /* Clear result first. */
210 memset(result
, 0, sizeof(*result
));
211 memset(&circleSl
, 0, sizeof(circleSl
));
212 memset(&externSl
, 0, sizeof(externSl
));
214 /* Default result parameters. */
215 result
->newReady
= buffer
->ready
;
216 result
->newReadHead
= buffer
->readHead
;
217 result
->newWriteHead
= buffer
->writeHead
;
219 /* External slice is always set to the passed buffer. */
220 externSl
.externalBuf
= opData
;
222 externSl
.len
= length
;
224 /* Circle slice is always the same length. */
225 circleSl
.len
= length
;
227 /* The window buffer can be pushed the same as a queue as long as it */
228 /* does not exceed the buffer size. */
229 if (operation
== SJME_CIRCLE_BUFFER_PUSH_WINDOW
)
230 if (buffer
->ready
+ length
<= buffer
->size
)
231 operation
= SJME_CIRCLE_BUFFER_PUSH_QUEUE
;
233 /* Pushing as a queue? */
234 if (operation
== SJME_CIRCLE_BUFFER_PUSH_QUEUE
)
236 /* External to circle. */
240 /* Calculate new current data in the buffer. */
241 result
->newReady
= buffer
->ready
+ length
;
242 if (result
->newReady
> buffer
->size
)
243 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
245 /* Writing at end? */
246 if (seekType
== SJME_CIRCLE_BUFFER_LAST
)
248 result
->newWriteHead
= buffer
->writeHead
+ length
;
249 circleSl
.base
= buffer
->writeHead
;
252 /* Writing at start? */
255 result
->newReadHead
= buffer
->readHead
- length
;
256 circleSl
.base
= result
->newReadHead
;
260 /* Pushing as a window? */
261 else if (operation
== SJME_CIRCLE_BUFFER_PUSH_WINDOW
)
263 /* External to circle. */
268 return sjme_error_notImplemented(0);
272 else if (operation
== SJME_CIRCLE_BUFFER_POP
)
274 /* Circle to external. */
278 /* Calculate new size. */
279 result
->newReady
= buffer
->ready
- length
;
280 if (result
->newReady
< 0)
281 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
283 /* Removing from end? */
284 if (seekType
== SJME_CIRCLE_BUFFER_LAST
)
286 result
->newWriteHead
= buffer
->writeHead
- length
;
287 circleSl
.base
= result
->newWriteHead
;
290 /* Removing from start? */
293 result
->newReadHead
= buffer
->readHead
+ length
;
294 circleSl
.base
= buffer
->readHead
;
299 else if (operation
== SJME_CIRCLE_BUFFER_GET
)
301 /* Circle to external. */
305 /* The positions in the circle buffer are relative to the heads. */
306 if (seekType
== SJME_CIRCLE_BUFFER_LAST
)
308 /* Cannot get more data than what exists in the buffer. */
309 if ((buffer
->ready
- seekPos
) + length
> buffer
->ready
)
310 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
312 circleSl
.base
= buffer
->writeHead
- seekPos
;
316 /* Cannot get more data than what exists in the buffer. */
317 if (seekPos
+ length
> buffer
->ready
)
318 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
320 circleSl
.base
= buffer
->readHead
+ seekPos
;
326 return SJME_ERROR_INVALID_ARGUMENT
;
328 /* Make sure the read/write heads are in range. */
329 while (result
->newReadHead
< 0)
330 result
->newReadHead
+= buffer
->size
;
331 while (result
->newReadHead
>= buffer
->size
)
332 result
->newReadHead
-= buffer
->size
;
333 while (result
->newWriteHead
< 0)
334 result
->newWriteHead
+= buffer
->size
;
335 while (result
->newWriteHead
>= buffer
->size
)
336 result
->newWriteHead
-= buffer
->size
;
338 /* Perform splice clipping. */
339 return sjme_circleBuffer_splice(buffer
, &circleSl
, &externSl
,
340 srcSl
, destSl
, result
);
343 static sjme_errorCode
sjme_circleBuffer_operate(
344 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
345 sjme_attrInNotNull sjme_circleBuffer_calcResult
* result
)
348 sjme_circleBuffer_slice
* src
;
349 sjme_circleBuffer_slice
* dest
;
351 if (buffer
== NULL
|| result
== NULL
)
352 return SJME_ERROR_NULL_ARGUMENTS
;
354 /* These must be in range. */
355 if (result
->newReady
< 0 || result
->newReady
> buffer
->size
||
356 result
->newReadHead
< 0 || result
->newReadHead
> buffer
->size
||
357 result
->newWriteHead
< 0 || result
->newWriteHead
> buffer
->size
)
358 return SJME_ERROR_ILLEGAL_STATE
;
360 /* Copy each slice, if valid. */
361 for (i
= 0; i
< 2; i
++)
363 src
= &result
->src
[i
];
364 dest
= &result
->dest
[i
];
366 /* Skip if nothing here. */
367 if (src
->len
== 0 && dest
->len
== 0)
370 /* Length cannot be negative! */
371 if (src
->len
< 0 || dest
->len
< 0)
372 return SJME_ERROR_ILLEGAL_STATE
;
374 /* Fail if both external or both internal. */
375 if ((src
->externalBuf
== NULL
) == (dest
->externalBuf
== NULL
))
376 return SJME_ERROR_ILLEGAL_STATE
;
378 /* These must always be the same. */
379 if (src
->len
!= dest
->len
)
380 return SJME_ERROR_ILLEGAL_STATE
;
384 sjme_message("Slice %d [%p %d %d] <- [%p %d %d]",
385 i
, dest
->externalBuf
, dest
->base
, dest
->len
,
386 src
->externalBuf
, src
->base
, src
->len
);
389 /* External to internal. */
390 if (src
->externalBuf
!= NULL
&& dest
->externalBuf
== NULL
)
391 memmove(&buffer
->buffer
[dest
->base
],
392 SJME_POINTER_OFFSET(src
->externalBuf
, src
->base
),
395 /* Internal to external. */
396 else if (src
->externalBuf
== NULL
&& dest
->externalBuf
!= NULL
)
397 memmove(SJME_POINTER_OFFSET(dest
->externalBuf
, dest
->base
),
398 &buffer
->buffer
[src
->base
],
401 /* Should not occur. */
403 return SJME_ERROR_ILLEGAL_STATE
;
407 buffer
->ready
= result
->newReady
;
408 buffer
->readHead
= result
->newReadHead
;
409 buffer
->writeHead
= result
->newWriteHead
;
412 return SJME_ERROR_NONE
;
415 sjme_errorCode
sjme_circleBuffer_available(
416 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
417 sjme_attrOutNotNull sjme_jint
* outAvailable
)
419 if (buffer
== NULL
|| outAvailable
== NULL
)
420 return SJME_ERROR_NULL_ARGUMENTS
;
422 *outAvailable
= buffer
->size
- buffer
->ready
;
423 return SJME_ERROR_NONE
;
426 sjme_errorCode
sjme_circleBuffer_destroy(
427 sjme_attrInNotNull sjme_circleBuffer
* buffer
)
429 sjme_errorCode error
;
432 return SJME_ERROR_NULL_ARGUMENTS
;
434 /* Free storage first. */
435 if (buffer
->buffer
!= NULL
)
437 if (sjme_error_is(error
= sjme_alloc_free(buffer
->buffer
)))
438 return sjme_error_default(error
);
439 buffer
->buffer
= NULL
;
442 /* Then the buffer info. */
443 return sjme_alloc_free(buffer
);
446 sjme_errorCode
sjme_circleBuffer_get(
447 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
448 sjme_attrOutNotNullBuf(outDataLen
) sjme_pointer outData
,
449 sjme_attrInPositiveNonZero sjme_jint length
,
450 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
,
451 sjme_attrInPositiveNonZero sjme_jint seekPos
)
453 sjme_errorCode error
;
454 sjme_circleBuffer_calcResult result
;
457 return SJME_ERROR_NULL_ARGUMENTS
;
460 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
462 /* Calculate result. */
463 memset(&result
, 0, sizeof(result
));
464 if (sjme_error_is(error
= sjme_circleBuffer_calc(
466 SJME_CIRCLE_BUFFER_GET
,
467 outData
, length
, seekType
, seekPos
)))
468 return sjme_error_default(error
);
471 return sjme_circleBuffer_operate(buffer
, &result
);
474 sjme_errorCode
sjme_circleBuffer_new(
475 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
476 sjme_attrOutNotNull sjme_circleBuffer
** outBuffer
,
477 sjme_attrInValue sjme_circleBuffer_mode inMode
,
478 sjme_attrInPositiveNonZero sjme_jint length
)
480 sjme_errorCode error
;
481 sjme_pointer storage
;
482 sjme_circleBuffer
* result
;
484 if (inPool
== NULL
|| outBuffer
== NULL
)
485 return SJME_ERROR_NULL_ARGUMENTS
;
487 if (inMode
!= SJME_CIRCLE_BUFFER_QUEUE
&&
488 inMode
!= SJME_CIRCLE_BUFFER_WINDOW
)
489 return SJME_ERROR_INVALID_ARGUMENT
;
492 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
494 /* Make sure we can allocate the buffer first. */
496 if (sjme_error_is(error
= sjme_alloc(inPool
,
497 length
, (sjme_pointer
*)&storage
)) || storage
== NULL
)
498 goto fail_allocStorage
;
500 /* Then the actual buffer info. */
502 if (sjme_error_is(error
= sjme_alloc(inPool
,
503 sizeof(*result
), (sjme_pointer
*)&result
)) || result
== NULL
)
504 goto fail_allocResult
;
506 /* Setup information. */
507 result
->size
= length
;
508 result
->mode
= inMode
;
509 result
->buffer
= storage
;
513 return SJME_ERROR_NONE
;
517 sjme_alloc_free(result
);
520 sjme_alloc_free(storage
);
522 return sjme_error_default(error
);
525 sjme_errorCode
sjme_circleBuffer_pop(
526 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
527 sjme_attrOutNotNullBuf(outDataLen
) sjme_pointer outData
,
528 sjme_attrInPositiveNonZero sjme_jint length
,
529 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
)
531 sjme_errorCode error
;
532 sjme_circleBuffer_calcResult result
;
535 return SJME_ERROR_NULL_ARGUMENTS
;
538 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
540 /* Calculate result. */
541 memset(&result
, 0, sizeof(result
));
542 if (sjme_error_is(error
= sjme_circleBuffer_calc(
544 SJME_CIRCLE_BUFFER_POP
,
545 outData
, length
, seekType
, 0)))
546 return sjme_error_default(error
);
549 return sjme_circleBuffer_operate(buffer
, &result
);
552 sjme_errorCode
sjme_circleBuffer_push(
553 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
554 sjme_attrInNotNullBuf(outDataLen
) sjme_cpointer inData
,
555 sjme_attrInPositiveNonZero sjme_jint length
,
556 sjme_attrInValue sjme_circleBuffer_seekEnd seekType
)
558 sjme_errorCode error
;
559 sjme_circleBuffer_calcResult result
;
562 return SJME_ERROR_NULL_ARGUMENTS
;
565 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
567 /* Calculate result. */
568 memset(&result
, 0, sizeof(result
));
569 if (sjme_error_is(error
= sjme_circleBuffer_calc(
571 (buffer
->mode
== SJME_CIRCLE_BUFFER_WINDOW
?
572 SJME_CIRCLE_BUFFER_PUSH_WINDOW
: SJME_CIRCLE_BUFFER_PUSH_QUEUE
),
573 inData
, length
, seekType
, 0)))
574 return sjme_error_default(error
);
577 return sjme_circleBuffer_operate(buffer
, &result
);
580 sjme_errorCode
sjme_circleBuffer_stored(
581 sjme_attrInNotNull sjme_circleBuffer
* buffer
,
582 sjme_attrOutNotNull sjme_jint
* outStored
)
584 if (buffer
== NULL
|| outStored
== NULL
)
585 return SJME_ERROR_NULL_ARGUMENTS
;
587 *outStored
= buffer
->ready
;
588 return SJME_ERROR_NONE
;