summaryrefslogtreecommitdiffstats
path: root/lib/vio_fifo.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vio_fifo.c')
-rw-r--r--lib/vio_fifo.c1825
1 files changed, 1084 insertions, 741 deletions
diff --git a/lib/vio_fifo.c b/lib/vio_fifo.c
index 685f33ec..0612c465 100644
--- a/lib/vio_fifo.c
+++ b/lib/vio_fifo.c
@@ -19,15 +19,14 @@
* Boston, MA 02111-1307, USA.
*/
-#include <stddef.h>
-#include <string.h>
+#include "misc.h"
+#include <stdio.h>
#include "vio_fifo.h"
+#include "qfstring.h"
#include "network.h"
-#include "list_util.h"
#include "memory.h"
-#include "zassert.h"
/*==============================================================================
* VTY I/O FIFO manages an arbitrary length byte-wise FIFO buffer.
@@ -39,503 +38,558 @@
* ever needed.
*
* When releasing lumps, keeps one lump "spare", to be reused as necessary.
- * This is used in ... TODO <<<< And is released...
*
*------------------------------------------------------------------------------
* Implementation notes:
*
- * The FIFO is initialised with all pointers NULL -- so with no lumps at all.
+ * The FIFO is initialised with one lump in it (the "own_lump", which is
+ * embedded in the FIFO structure). There is always at least one lump in
+ * the FIFO.
*
- * Once a lump has been allocated there is always one lump in the FIFO.
+ * The hold_ptr allows the get_ptr to move forward, but retaining the data in
+ * the FIFO until the hold_ptr is cleared. Can move the get_ptr back to the
+ * hold_ptr to reread the data.
+ *
+ * The end_ptr allows put_ptr to move forward, but the new data cannot be got
+ * from the FIFO until the end_ptr is cleared. Can discard the new data
+ * and move the put_ptr back to the end_ptr.
+ *
+ * There are four lumps of interest:
+ *
+ * * head -- where the hold_mark is, if there is one.
+ *
+ * * get_lump -- where the get_ptr is.
+ * Same as head when no hold_mark.
+ *
+ * * end_lump -- where the end_mark is, if there is one.
+ * Same as tail when no end mark.
+ *
+ * * tail -- where the put_ptr is.
+ *
+ * Some or all of those may be the same, depending on how big the FIFO is.
*
* The following are expected to be true:
*
- * * put_ptr == get_ptr => FIFO empty
+ * * p_start == &get_ptr => no hold mark
+ * &hold_ptr => hold mark is set
+ *
+ * * put_ptr == get_ptr => FIFO empty -- unless *p_start != get_ptr.
*
- * * put_ptr == tail->end -- at all times (NULL when no lumps)
+ * * put_end == tail->end -- at all times
*
* put_ptr >= tail->data ) otherwise something is broken
* put_ptr <= tail->end )
*
- * * get_ptr == head->end -- when there is more than one lump
- * get_ptr <= put_ptr -- when there is only one lump
+ * * p_get_end == &get_lump->end -- when get_lump != end_lump
+ * == &end_ptr -- when get_lump == end_lump & end mark set
+ * == &put_ptr -- when get_lump == end_lump & no end mark
*
- * get_ptr >= head->data ) otherwise something is broken
- * get_ptr <= head->end )
+ * get_ptr >= get_lump->data ) otherwise something is broken
+ * get_ptr <= get_lump->end )
*
* * put_ptr == put_end => tail lump is full
* put_ptr < put_end => space exists in the tail lump
* put_ptr > put_end => broken
*
- * * get_ptr == get_end => head lump is empty
- * BUT if there is only one lump, make sure that
- * get_end == put_ptr.
- * get_ptr < get_end => data exists in the head lump
- * get_ptr > get_end => broken
+ * * get_ptr == *p_get_end => nothing to get -- get_lump == end_lump
+ * get_ptr < *p_get_end => data exists in the current get_lump
+ * get_ptr > *p_get_end => broken
+ *
+ * * p_end == &put_ptr => no end mark
+ * == &end_ptr => end mark is set
*
* Note that:
*
- * * when the get_ptr reaches the put_ptr the pointers are reset to the
- * start of the one and only lump.
+ * * while get_ptr < *p_get_end can get stuff without worrying about other
+ * pointers or moving between lumps etc.
+ *
+ * When get_ptr reaches *p_get_end, however, must move to the next lump,
+ * if possible, or collapse the pointers if have hit the put_ptr. Keeping
+ * the get_ptr honest in this way: (a) ensures that will always get from
+ * the beginning of a lump if possible; (b) simplifies the handling of
+ * hold_ptr et al (because get_ptr is never in the ambiguous position
+ * at the end of one lump, which is the same as the start of the next).
+ *
+ * Similarly, while put_ptr < put_end, can put stuff without worrying
+ * about other pointers or moving between lumps etc. Will leave the
+ * put_ptr at the very end of the current lump if just fills it.
+ *
+ * * the value of p_get_end depends on whether get_lump == end_lump, and
+ * then whether there is an end_ptr. But at any time, points to the end
+ * of what can be read by get_ptr without stepping between lumps etc.
+ *
+ * Note that get_ptr == p_get_end <=> is at the current end of the FIFO,
+ * because after any get operation, will advance to the next lump. If
+ * FIFO is empty after advancing the get_ptr, will reset the pointers
+ * back to the start of the then current (and only) lump.
+ *
+ * * some care must be taken to ensure that if the fifo is empty, the
+ * pointers will be at the start of one empty lump.
+ *
+ * In this context, empty means nothing between *p_start and put_ptr.
+ * Noting that *p_start is &hold_ptr or &get_ptr, depending on whether
+ * there is a hold mark or not. (If *p_start == put_ptr, there may be
+ * an end mark, but it must be end_ptr == put_ptr !)
+ *
+ * - get_ptr -- as above, if this hits *p_get_end, must:
*
- * Everywhere that the get_ptr is moved, must check for meeting the
- * put_ptr and reset pointers. At the same time, when reaches the end of
- * a lump, gets rid of it.
+ * * step to the next lump, if there is one, and,
+ * unless something is held behind the get_ptr,
+ * release the lump just stepped from.
*
- * * when advancing the put_ptr does not check for advancing the get_end.
+ * * if has hit put_ptr, reset pointers -- unless there
+ * is something held behind the get_ptr.
*
- * The one exception to this, is that when the put_ptr advances to a new
- * block, if there was one lump, sets the get_end to the end of that block.
+ * - put_ptr -- is always somewhere in the tail lump.
*
- * Everywhere that the get_end is used, must check for there being one
- * lump and the possibility that put_ptr has changed.
+ * - end_ptr -- when a new lump is added, if the end_ptr is at the
+ * end of the last lump, it is moved to the start of the
+ * new last lump (along with the put_ptr).
+ *
+ * If the end_ptr is equal to the put_ptr and the get_ptr
+ * hits it, if the pointers are reset, the end_ptr will be
+ * reset along with the put_ptr.
+ *
+ * If the put_ptr is reset back to the end_ptr, need to
+ * see if the FIFO is empty, and reset pointers if it is.
+ *
+ * - hold_ptr -- because the pointers are reset whenever the FIFO
+ * becomes empty, when hold_ptr is set, it will be at
+ * the start of an empty FIFO, or somewhere in a not-
+ * empty one. When a hold_ptr is cleared, the get_ptr
+ * may be equal to the put_ptr, and pointers must be
+ * reset.
*/
+inline static void vio_fifo_set_get_ptr(vio_fifo vff, vio_fifo_lump lump,
+ char* ptr) ;
+inline static void vio_fifo_set_get_end(vio_fifo vff) ;
+inline static void vio_fifo_release_up_to(vio_fifo vff, vio_fifo_lump upto) ;
+static void vio_fifo_release_lump(vio_fifo vff, vio_fifo_lump lump) ;
-/*==============================================================================
- * Initialisation, allocation and freeing of FIFO and lumps thereof.
+/*------------------------------------------------------------------------------
+ * Test whether there is a hold mark.
*/
-
-/* Return default size, or given size rounded up to 16 byte boundary */
-static size_t
-vio_fifo_size(size_t size)
+inline static bool
+vio_fifo_have_hold_mark(vio_fifo vff)
{
- if (size == 0)
- return 4096 ;
- else
- return ((size + 16 - 1) / 16) * 16 ;
+ return vff->p_start == &vff->hold_ptr ;
} ;
-/*==============================================================================
- * Initialise VTY I/O FIFO -- allocating if required.
+/*------------------------------------------------------------------------------
+ * Test whether there is an end mark.
*/
-extern vio_fifo
-vio_fifo_init_new(vio_fifo vf, size_t size)
+inline static bool
+vio_fifo_have_end_mark(vio_fifo vff)
{
- if (vf == NULL)
- vf = XCALLOC(MTYPE_VIO_FIFO, sizeof(vio_fifo_t)) ;
- else
- memset(vf, 0, sizeof(vio_fifo_t)) ;
-
- /* Zeroising the the vio_fifo_t has set:
- *
- * lump -- base pair, both pointers NULL => list is empty
- *
- * put_ptr -- NULL ) no lump to put anything into
- * put_end -- NULL ) put_ptr == put_end => no room in current lump
- *
- * get_ptr -- NULL ) no lump to get anything from
- * get_end -- NULL ) get_ptr -- get_end => nothing left in current lump
- *
- * rdr_lump -- NULL ) no rdr_lump
- * rdr_ptr -- NULL
- *
- * spare -- NULL no spare lump
- *
- * ALSO put_ptr == get_ptr => FIFO is empty !
- */
-
- vf->size = vio_fifo_size(size) ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- return vf ;
-}
+ return vff->p_end == &vff->end_ptr ;
+} ;
/*------------------------------------------------------------------------------
- * Free contents of given FIFO, and free FIFO structure as well, if required.
+ * The FIFO is empty, with one lump -- reset all pointers.
*
- * Does nothing if given a NULL pointer -- must already have been freed !
+ * Preserves and hold mark or end mark -- so no need to change p_start or p_end.
*
- * If does not free the FIFO structure, resets it all empty.
+ * Resets the hold_ptr and the end_ptr whether there is a hold and/or end mark
+ * or not -- saves testing for whether to do it or not. These values are
+ * only significant if p_start or p_end/p_get_end point at them.
*
- * Frees *all* FIFO lumps.
+ * HOWEVER: does not set p_get_end -- so if get_lump or end_lump or p_end have
+ * changed, then must also call vio_fifo_set_get_end().
*
- * See also: vio_fifo_reset_keep(vio_fifo)
- * vio_fifo_reset_free(vio_fifo)
+ * ALSO: does not set put_end -- so if the tail lumps has changed, that must
+ * be updated.
*/
-extern vio_fifo
-vio_fifo_reset(vio_fifo vf, int free_structure)
+inline static void
+vio_fifo_reset_ptrs(vio_fifo vff)
{
- vio_fifo_lump lump ;
+ char* ptr = ddl_tail(vff->base)->data ;
- if (vf == NULL)
- return NULL ;
+ if (vio_fifo_debug)
+ assert(ddl_head(vff->base) == ddl_tail(vff->base)) ;
- while (ddl_pop(&lump, vf->base, list) != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+ vff->hold_ptr = ptr ;
+ vff->get_ptr = ptr ;
+ vff->end_ptr = ptr ;
+ vff->put_ptr = ptr ;
+} ;
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ;
+/*------------------------------------------------------------------------------
+ * This is called *iff* get_ptr >= *p_get_end -- and preferably only after
+ * it has been adjusted forwards by at least 1 (but that is not required).
+ *
+ * If there is anything available to be got, adjust get_ptr and/or get_end
+ * in order to be able to get it -- discarding lumps as required.
+ */
+Private void
+vio_fifo_sync_get(vio_fifo vff)
+{
+ if (vio_fifo_debug)
+ assert(vff->get_ptr == *vff->p_get_end) ;
+
+ if (vff->get_lump == vff->end_lump)
+ {
+ if (vio_fifo_debug)
+ assert(vff->get_ptr == *vff->p_end) ;
- if (free_structure)
- XFREE(MTYPE_VIO_FIFO, vf) ; /* sets vf = NULL */
+ /* We are in the end_lump, and there is nothing more to be read.
+ *
+ * If have reached the put_ptr, then unless there is something held
+ * behind the get_ptr, the fifo is completely empty, and pointers can
+ * be reset to the start of the end_lump (which is the only lump).
+ *
+ * p_start == &hold_ptr or &get_ptr, so can check for empty in one test.
+ */
+ if (*vff->p_start == vff->put_ptr)
+ vio_fifo_reset_ptrs(vff) ;
+ }
else
- vio_fifo_init_new(vf, vf->size) ;
+ {
+ /* Good news, can advance the get_ptr
+ *
+ * Step the get_ptr to the start of the next lump, and if no hold mark,
+ * discard any lumps which precede the new get_lump.
+ */
+ vio_fifo_lump get_lump ;
+
+ if (vio_fifo_debug)
+ {
+ assert(vff->get_lump != vff->end_lump) ;
+ assert(vff->get_ptr == vff->get_lump->end) ;
+ } ;
- return vf ;
+ get_lump = ddl_next(vff->get_lump, list) ;
+ vio_fifo_set_get_ptr(vff, get_lump, get_lump->data) ;
+
+ if (!vio_fifo_have_hold_mark(vff))
+ vio_fifo_release_up_to(vff, get_lump) ;
+ } ;
} ;
/*------------------------------------------------------------------------------
- * The FIFO is empty, with one lump -- reset all pointers.
+ * Set get_lump/get_ptr and p_get_end to suit.
*/
inline static void
-vio_fifo_ptr_reset(vio_fifo vf, vio_fifo_lump lump)
+vio_fifo_set_get_ptr(vio_fifo vff, vio_fifo_lump lump, char* ptr)
{
- if (vf->rdr_lump != NULL)
- {
- assert((lump == vf->rdr_lump) && (vf->rdr_ptr == vf->get_ptr)) ;
- vf->rdr_ptr = lump->data ;
- } ;
+ vff->get_lump = lump ;
+ vff->get_ptr = ptr ;
- /* Note that sets the lump->end to the true lump->end */
- vf->get_ptr = vf->get_end = vf->put_ptr = lump->data ;
- vf->put_end = lump->end = lump->data + lump->size ;
+ vio_fifo_set_get_end(vff) ;
} ;
/*------------------------------------------------------------------------------
- * The FIFO is utterly empty, with ZERO lumps -- unset all pointers.
+ * Set the p_get_end depending on whether the get_lump == end_lump, or not.
+ *
+ * This must be called if the get_lump or the end_lump are changed, or if
+ * p_end changes.
*/
inline static void
-vio_fifo_ptr_unset(vio_fifo vf)
+vio_fifo_set_get_end(vio_fifo vff)
{
- assert((ddl_head(vf->base) == NULL) && (ddl_tail(vf->base) == NULL)) ;
-
- vf->one = false ;
-
- vf->put_ptr = NULL ;
- vf->put_end = NULL ;
- vf->get_ptr = NULL ;
- vf->get_end = NULL ;
-
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
+ vff->p_get_end = (vff->get_lump == vff->end_lump) ? vff->p_end
+ : &vff->get_lump->end ;
} ;
/*------------------------------------------------------------------------------
- * Clear out contents of FIFO -- will continue to use the FIFO.
+ * Release all lumps up to (but excluding) the given lump.
*
- * Keeps one FIFO lump. (Frees everything else, including any spare.)
+ * NB: takes no notice of hold_ptr or anything else.
*/
-extern void
-vio_fifo_clear(vio_fifo vf)
+inline static void
+vio_fifo_release_up_to(vio_fifo vff, vio_fifo_lump to)
{
- vio_fifo_lump tail ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- assert(vf != NULL) ;
-
- tail = ddl_tail(vf->base) ;
-
- if (tail != NULL)
- {
- while (ddl_head(vf->base) != tail)
- {
- vio_fifo_lump lump ;
- ddl_pop(&lump, vf->base, list) ;
- XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
- } ;
-
- vf->rdr_lump = NULL ; /* clear rdr */
- vf->rdr_ptr = NULL ;
-
- vf->one = true ;
- vio_fifo_ptr_reset(vf, tail) ;
- }
- else
- vio_fifo_ptr_unset(vf) ;
-
- if (vf->spare != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, vf->spare) ; /* sets vf->spare = NULL */
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vio_fifo_lump lump ;
+ while (ddl_head(vff->base) != to)
+ vio_fifo_release_lump(vff, ddl_pop(&lump, vff->base, list)) ;
} ;
/*------------------------------------------------------------------------------
- * See how much room there is in the FIFO.
- *
- * If no lumps have been allocated, returns the size of the lump that would
- * allocate.
+ * Release all lumps back to (but excluding) the given lump.
*
- * Otherwise, returns the amount of space available *without* allocating any
- * further lumps.
+ * Reset vff->put_end to be the end of the to->lump.
*
- * Returns: room available as described
+ * NB: takes no notice of hold_ptr or anything else.
*/
-extern size_t
-vio_fifo_room(vio_fifo vf)
+inline static void
+vio_fifo_release_back_to(vio_fifo vff, vio_fifo_lump to)
{
- if (vf->put_ptr != NULL)
- return vf->put_end - vf->put_ptr ;
- else
- return vf->size ;
+ vio_fifo_lump lump ;
+ do
+ vio_fifo_release_lump(vff, ddl_crop(&lump, vff->base, list)) ;
+ while (to != ddl_tail(vff->base)) ;
+
+ vff->put_end = to->end ;
} ;
+/*==============================================================================
+ * Initialisation, allocation and freeing of FIFO and lumps thereof.
+ */
+
/*------------------------------------------------------------------------------
- * Allocate another lump for putting into.
+ * Allocate and initialise a new FIFO.
*
- * Call when (vf->put_ptr >= vf->put_end) -- asserts that they are equal.
+ * The size given is the size for all lumps in the FIFO. 0 => default size.
*
- * Set the put_ptr/put_end pointers to point at the new lump.
+ * Size is rounded up to a 128 byte boundary.
*
- * If this is the first lump allocated, set the get_ptr/get_end pointers too.
+ * Once allocated and initialised, the FIFO contains one lump, and if it
+ * grows to more than one, will retain a spare lump once it shrinks again.
*
- * If have just filled the first lump on the list, update the get_end pointer
- * to reflect the fact that the out lump is now full.
+ * Keeping a pair of lumps allows the get_ptr to lag behind the put_ptr by
+ * about a lump full, without requiring repeated memory allocation. Also,
+ * vio_fifo_write_nb() can be asked to write only lumps -- so if called
+ * regularly while putting stuff to a FIFO, will write entire lumps at once.
*/
-extern void
-vio_fifo_lump_new(vio_fifo vf, size_t size)
+extern vio_fifo
+vio_fifo_new(ulen size)
{
- vio_fifo_lump lump ;
- int first_alloc ;
+ vio_fifo vff ;
+ ulen total_size ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (size == 0)
+ size = VIO_FIFO_DEFAULT_LUMP_SIZE ;
- passert(vf->put_ptr == vf->put_end) ; /* must be end of tail lump */
+ size = ((size + 128 - 1) / 128) * 128 ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* update get_end */
+ if (vio_fifo_debug)
+ size = 29 ;
- lump = ddl_tail(vf->base) ;
+ total_size = offsetof(struct vio_fifo, own_lump[0].data[size]) ;
- first_alloc = (lump == NULL) ; /* extra initialisation needed */
+ vff = XCALLOC(MTYPE_VIO_FIFO, total_size) ;
- if (first_alloc)
- assert(vf->put_ptr == NULL) ; /* must all be NULL together */
- else
- assert(vf->put_ptr == lump->end) ; /* must be end of tail lump */
+ /* Zeroising the the vio_fifo_t has set:
+ *
+ * base -- base pair, both pointers NULL => list is empty
+ *
+ * p_start -- X -- see vio_fifo_ptr_set()
+ *
+ * hold_ptr -- NULL -- not relevant until hold mark is set
+ *
+ * get_lump -- X )
+ * get_ptr -- X ) -- see vio_fifo_ptr_set()
+ * p_get_end -- X )
+ *
+ * end_lump -- X -- see vio_fifo_ptr_set()
+ *
+ * end_ptr -- NULL -- not relevant until hold mark is set
+ *
+ * put_ptr -- X )
+ * put_end -- X ) -- see vio_fifo_ptr_set()
+ * p_end -- X )
+ *
+ * size -- X -- set below
+ *
+ * spare -- NULL -- no spare lump
+ *
+ * own_lump -- all zeros: list -- pointers NULL, set below
+ * end -- set below
+ */
+ vff->size = size ;
+ vff->own_lump->end = vff->own_lump->data + vff->size ;
- size = vio_fifo_size(size) ;
+ if (vio_fifo_debug)
+ assert(vff->own_lump->end == ((char*)vff + total_size)) ;
- if ((vf->spare != NULL) && (vf->spare->size >= size))
- {
- lump = vf->spare ;
- vf->spare = NULL ;
- }
- else
- {
- lump = XMALLOC(MTYPE_VIO_FIFO_LUMP,
- offsetof(vio_fifo_lump_t, data[size])) ;
- lump->size = size ;
- } ;
+ ddl_append(vff->base, vff->own_lump, list) ;
- lump->end = lump->data + lump->size ;
+ vff->p_start = &vff->get_ptr ;
- ddl_append(vf->base, lump, list) ;
+ vff->get_lump = vff->own_lump ;
+ vff->get_ptr = vff->own_lump->data ;
+ vff->p_get_end = &vff->put_ptr ;
- vf->one = first_alloc ;
+ vff->end_lump = vff->own_lump ;
- vf->put_ptr = lump->data ;
- vf->put_end = lump->end ;
+ vff->put_ptr = vff->own_lump->data ;
+ vff->put_end = vff->own_lump->end ;
- if (first_alloc)
- {
- vf->get_ptr = vf->put_ptr ; /* get_ptr == put_ptr => empty */
- vf->get_end = vf->put_ptr ;
- } ;
+ vff->p_end = &vff->put_ptr ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ return vff ;
} ;
/*------------------------------------------------------------------------------
- * Release lump, head or tail (or both) and update pointers.
+ * Free contents of given FIFO and the FIFO structure as well.
*
- * Note that this does release the lump if it is the only lump.
- *
- * Do nothing if nothing is yet allocated.
- *
- * If releasing the only lump in the FIFO, resets all pointers to NULL.
- *
- * If releasing the head lump:
- *
- * * the lump MUST be finished with -- so vf->get_ptr must be at the end
- *
- * If releasing the only lump, the FIFO MUST be empty.
- *
- * * if the lump is the current vf->rdr_lump, the reader must be at the
- * end too -- ie it must be the same as the vf->get_ptr !
- *
- * If releasing the tail lump:
- *
- * * the lump MUST be empty
- *
- * If releasing the only lump, the FIFO MUST be empty.
+ * Does nothing if given a NULL pointer -- must already have been freed !
*
- * * if the lump is the current vf->rdr_lump, the reader must be at the
- * end too -- ie it must be the same as the vf->get_ptr !
+ * Returns: NULL
*/
-static void
-vio_fifo_lump_release(vio_fifo vf, vio_fifo_lump lump)
+extern vio_fifo
+vio_fifo_free(vio_fifo vff)
{
- vio_fifo_lump head ;
- vio_fifo_lump tail ;
- vio_fifo_lump free ;
- bool release_head ;
- bool release_tail ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- /* Prepare and check whether removing head or tail (or both) */
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
-
- release_head = (lump == head) ;
- release_tail = (lump == tail) ;
-
- assert(release_head || release_tail) ;
-
- /* Unless nothing ever allocated -- release the lump. */
- free = lump ; /* expect to free the lump */
- if (lump != NULL)
+ if (vff != NULL)
{
- vio_fifo_lump keep ;
+ vio_fifo_lump lump ;
+
+ lump = vff->spare ;
+ vff->spare = NULL ;
- /* Consistency checks */
- if (release_head)
+ do
{
- if (release_tail)
- assert(vf->get_ptr == vf->put_ptr) ;
- else
- assert(vf->get_ptr == lump->end) ;
+ if (lump != vff->own_lump)
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ; /* accepts lump == NULL */
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->get_ptr) ;
+ ddl_pop(&lump, vff->base, list) ;
}
- else if (release_tail)
- {
- assert(vf->put_ptr == lump->data) ;
+ while (lump != NULL) ;
- if (vf->rdr_lump == lump)
- assert(vf->rdr_ptr == vf->put_ptr) ;
- } ;
+ XFREE(MTYPE_VIO_FIFO, vff) ;
+ } ;
- /* Remove lump from FIFO and decide whether to keep as spare, or
- * which of spare and this to free.
- */
- ddl_del(vf->base, lump, list) ;
+ return NULL ;
+} ;
- keep = vf->spare ; /* expect to keep current spare */
+/*------------------------------------------------------------------------------
+ * Clear out contents of FIFO -- will continue to use the FIFO.
+ *
+ * If required, clears any hold mark and/or end mark.
+ *
+ * Keeps one spare lump.
+ *
+ * Does nothing if there is no FIFO !
+ */
+extern void
+vio_fifo_clear(vio_fifo vff, bool clear_marks)
+{
+ vio_fifo_lump lump ;
- if ((keep == NULL) || (keep->size < lump->size))
- {
- keep = lump ;
- free = vf->spare ;
- } ;
+ if (vff == NULL)
+ return ;
- vf->spare = keep ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- head = ddl_head(vf->base) ; /* changed if released head */
- tail = ddl_tail(vf->base) ; /* changed if released tail */
- } ;
+ lump = ddl_tail(vff->base) ;
- /* Now update pointers... depending on what was released and what have
- * left.
- */
- if (head == NULL)
- {
- /* Deal with FIFO that now has no lumps or had none to start with */
- if (lump != NULL)
- assert(vf->one) ;
+ vff->get_lump = lump ; /* before releasing */
+ vff->end_lump = lump ;
- vio_fifo_ptr_unset(vf) ;
- }
- else
- {
- /* Have at least one lump left -- so must have had at least two ! */
- assert(!vf->one) ;
+ vio_fifo_release_up_to(vff, lump) ;
- vf->one = (head == tail) ; /* update */
+ vio_fifo_reset_ptrs(vff) ;
- if (release_head)
- {
- /* Released the head.
- *
- * Update the vf->get_ptr and the vf->get_end.
- */
- vf->get_ptr = head->data ;
- if (vf->one)
- vf->get_end = vf->put_ptr ;
- else
- vf->get_end = head->end ;
-
- /* Update vf->rdr_ptr and vf->rdr_lump. */
- if (vf->rdr_lump == lump)
- {
- vf->rdr_lump = head ;
- vf->rdr_ptr = head->data ;
- } ;
- }
- else
- {
- /* Released the tail.
- * Update the vf->put_ptr and vf->put_end
- */
- vf->put_ptr = vf->put_end = tail->end ;
-
- /* Update vf->rdr_ptr and vf->rdr_lump. */
- if (vf->rdr_lump == lump)
- {
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->end ;
- } ;
- } ;
+ if (clear_marks)
+ {
+ vff->p_start = &vff->get_ptr ;
+ vff->p_end = &vff->put_ptr ;
} ;
- /* Finally, free any lump that is actually to be freed */
-
- if (free != NULL)
- XFREE(MTYPE_VIO_FIFO_LUMP, free) ;
+ vff->p_get_end = vff->p_end ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Re-allocate lump for putting into.
+ * Add a new lump to put stuff into -- work-horse for putting to the FIFO.
*
- * Call when vf->put_ptr == start of last lump, and that lump is not big
- * enough !
+ * Call when (vff->put_ptr >= vff->put_end) -- asserts that they are equal.
*
- * There must be at least one lump.
+ * The FIFO cannot be empty -- if it were, the pointers would have been reset,
+ * and could not be vff->put_ptr >= vff->put_end !!
*
- * Updates put_ptr/put_end pointers to point at the new lump.
+ * Allocates a new lump (or reuses the spare) and updates the put_ptr.
*
- * Updates get_ptr/get_end pointers if required.
- *
- * Updates rdr_ptr if required.
+ * If the end_ptr and the put_ptr were equal, then advances that too, which
+ * ensures that the end_ptr is not ambiguous.
+
+ * If the get_ptr and the put_ptr were equal, then advances that too, which
+ * ensures that the get_ptr is not ambiguous. This can be the case if there
+ * is a hold_ptr.
*/
-static void
-vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
+Private void
+vio_fifo_add_lump(vio_fifo vff)
{
- bool rdr_set ;
+ vio_fifo_lump lump ;
+
+ assert(vff->put_ptr == vff->put_end) ; /* must be end of tail lump */
+ assert(vff->put_ptr != *vff->p_start) ; /* cannot be empty ! */
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- /* FIFO may not be completely empty.
- * This must be the last lump.
- * The last lump must be empty.
+ /* If we can use the spare, do so, otherwise make a new one and
+ * add to the end of the FIFO.
*/
- assert((lump != NULL) && (lump == ddl_tail(vf->base))) ;
+ lump = vff->spare ;
+ vff->spare = NULL ;
+
+ if (lump == NULL)
+ {
+ ulen lump_size = offsetof(vio_fifo_lump_t, data[vff->size]) ;
- /* Remove the last, *empty* lump, and update all pointers to suit. */
- rdr_set = (vf->rdr_lump == lump) ;
+ lump = XMALLOC(MTYPE_VIO_FIFO_LUMP, lump_size) ;
+ lump->end = (char*)lump + lump_size ;
- vio_fifo_lump_release(vf, lump) ;
+ if (vio_fifo_debug)
+ assert(lump->end == (lump->data + vff->size)) ;
+ } ;
- /* Now allocate a new lump with the required size */
- vio_fifo_lump_new(vf, size) ;
+ ddl_append(vff->base, lump, list) ;
- /* Restore the rdr_ptr, if required */
- if (rdr_set)
+ /* Allocated new lump on the end of FIFO.
+ *
+ * If the get_ptr == put_ptr, advance the get_ptr. If there is an end_ptr,
+ * it must be == put_ptr, and is about to advance too.
+ *
+ * If put_ptr == *p_end, advance the end_lump and the end_ptr. If there is
+ * no end_mark, then p_end == &put_ptr, and the end_lump must follow the
+ * put_ptr. If there is an end_mark, then p_end == &end_ptr, and that must
+ * follow the put_ptr if they are equal.
+ *
+ * The get_lump may or may not have been the end_lump, and that may or may
+ * not have changed. Simplest thing is to set p_get_end to what it should
+ * be now.
+ */
+ if (vff->get_ptr == vff->put_ptr)
{
- vio_fifo_lump tail ;
+ if (vio_fifo_debug)
+ assert(vio_fifo_have_hold_mark(vff)) ;
- tail = ddl_tail(vf->base) ;
+ vff->get_lump = lump ;
+ vff->get_ptr = lump->data ;
+ } ;
- vf->rdr_lump = tail ;
- vf->rdr_ptr = tail->data ;
+ if (vff->put_ptr == *vff->p_end)
+ {
+ vff->end_lump = lump ;
+ vff->end_ptr = lump->data ; /* no effect if no end_mark */
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->put_ptr = lump->data ;
+ vff->put_end = lump->end ;
+
+ vio_fifo_set_get_end(vff) ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Release the given lump, provided it is neither get_lump nor end_lump.
+ *
+ * If don't have a spare lump, keep this one.
+ * If do have a spare lump, discard this one, unless it is "own_lump".
+ */
+static void
+vio_fifo_release_lump(vio_fifo vff, vio_fifo_lump lump)
+{
+ assert(lump != NULL) ;
+ assert(lump != vff->get_lump) ;
+ assert(lump != vff->end_lump) ;
+
+ if (vff->spare == NULL)
+ vff->spare = lump ;
+ else
+ {
+ if (lump == vff->own_lump)
+ {
+ lump = vff->spare ; /* free the spare instead */
+ vff->spare = vff->own_lump ;
+ } ;
+
+ XFREE(MTYPE_VIO_FIFO_LUMP, lump) ;
+ } ;
} ;
/*==============================================================================
@@ -543,623 +597,912 @@ vio_fifo_lump_renew(vio_fifo vf, vio_fifo_lump lump, size_t size)
*/
/*------------------------------------------------------------------------------
- * Store 'n' bytes -- allocate new lump if current is exhausted.
+ * Put 'n' bytes -- allocating as required.
*/
extern void
-vio_fifo_put(vio_fifo vf, const char* src, size_t n)
+vio_fifo_put_bytes(vio_fifo vff, const char* src, ulen n)
{
- size_t take ;
-
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
while (n > 0)
{
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, vf->size) ; /* traps put_ptr > put_end */
+ ulen take ;
+
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_add_lump(vff) ; /* traps put_ptr > put_end */
- take = (vf->put_end - vf->put_ptr) ;
+ take = (vff->put_end - vff->put_ptr) ;
if (take > n)
take = n ;
- memcpy(vf->put_ptr, src, take) ;
- vf->put_ptr += take ;
+ memcpy(vff->put_ptr, src, take) ;
+ vff->put_ptr += take ;
src += take ;
n -= take ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to fifo -- cf printf()
+ * Formatted print to FIFO -- cf printf()
*
* Returns: >= 0 -- number of bytes written
* < 0 -- failed (unlikely though that is)
*/
extern int
-vio_fifo_printf(vio_fifo vf, const char* format, ...)
+vio_fifo_printf(vio_fifo vff, const char* format, ...)
{
va_list args;
int len ;
va_start (args, format);
- len = vio_fifo_vprintf(vf, format, args);
+ len = vio_fifo_vprintf(vff, format, args);
va_end (args);
return len;
} ;
/*------------------------------------------------------------------------------
- * Formatted print to fifo -- cf vprintf()
+ * Formatted print to FIFO -- cf vprintf()
+ *
+ * Does nothing if vff is NULL !
*
* Returns: >= 0 -- number of bytes written
* < 0 -- failed (unlikely though that is)
+ *
+ * NB: uses qfs_vprintf(qfs, format, va), which allows the result to be
+ * collected a section at a time, if required. With reasonable size
+ * lumps, expect to need no more than two sections, and then only
+ * occasionally.
*/
extern int
-vio_fifo_vprintf(vio_fifo vf, const char *format, va_list args)
+vio_fifo_vprintf(vio_fifo vff, const char *format, va_list va)
{
- va_list ac ;
- int len ;
- int have ;
- size_t size ;
- vio_fifo_lump lump ;
+ qf_str_t qfs ;
+ ulen done ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if (vff == NULL)
+ return 0 ;
- size = vf->size ; /* standard allocation size */
- while (1)
- {
- /* Find what space is left in the tail lump, and allocate a new,
- * empty lump if required.
- */
- if (vf->put_ptr >= vf->put_end)
- vio_fifo_lump_new(vf, size) ; /* traps put_ptr > put_end */
-
- have = vf->put_end - vf->put_ptr ;
- assert(have > 0) ;
-
- /* Note that vsnprintf() returns the length of what it would like to
- * have produced, if it had the space. That length does not include
- * the trailing '\0'.
- */
- va_copy(ac, args) ;
- len = vsnprintf(vf->put_ptr, have, format, ac) ;
- va_end(ac) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- if (len < have)
- {
- if (len < 0)
- break ; /* quit if failed */
+ done = 0 ;
+ do
+ {
+ ulen did ;
- vf->put_ptr += len ;
- break ; /* done */
- } ;
+ if (vff->put_ptr >= vff->put_end)
+ vio_fifo_add_lump(vff) ; /* traps put_ptr > put_end */
- /* Not able to complete the operation in the current buffer.
- *
- * If the required space (len + 1) is greater than the standard
- * allocation, then need to increase the allocation for the next lump.
- *
- * If the current lump is empty, need to renew it with a fresh lump of
- * the now known required size.
- *
- * If the current lump is not empty, need to cut the end off and then
- * allocate a fresh lump (of the standard or now known required size).
- */
- if (len >= (int)size)
- size = len + 1 ; /* need a non-standard size */
+ qfs_init_offset(qfs, vff->put_ptr, vff->put_end - vff->put_ptr, done) ;
- lump = ddl_tail(vf->base) ;
+ did = qfs_vprintf(qfs, format, va) ;
- if (vf->put_ptr == lump->data)
- /* Need to replace the last, empty, lump with another empty lump, but
- * big enough.
- */
- vio_fifo_lump_renew(vf, lump, size) ;
- else
- /* Need to cut this lump short, and allocate new lump at top of loop.
- */
- lump->end = vf->put_end = vf->put_ptr ;
- } ;
+ done += did ;
+ vff->put_ptr += did ;
+ }
+ while (qfs_overflow(qfs) != 0) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- return len ;
+ return done ;
} ;
-/*==============================================================================
- * Get data from the FIFO.
- */
-
-static bool vio_fifo_get_next_lump(vio_fifo vf) ;
-
/*------------------------------------------------------------------------------
- * Get ready to read something out of the FIFO.
+ * Read part of file into FIFO -- assuming non-blocking file
+ *
+ * Will read up to the end of the current lump, then will read as may whole
+ * lumps as are requested -- request of 0 reads up to the end of the current
+ * lump (at least 1 byte). Will stop if would block.
*
- * Makes sure vf->get_end is up to date (if required) and if the FIFO is not
- * empty, makes sure vf->get_ptr points at the next byte to be read.
+ * Except where blocking intervenes, this reads in units of the lump size.
*
- * Returns: true <=> there is something in the FIFO.
+ * Returns: 0..n -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met immediately
+ *
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here with
+ * something, error or EOF.
*/
-static inline bool
-vio_fifo_get_ready(vio_fifo vf)
+extern int
+vio_fifo_read_nb(vio_fifo vff, int fd, ulen request)
{
- assert(vf->rdr_lump == NULL) ;
+ ulen total ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ total = 0 ;
+
+ do
+ {
+ int got ;
+
+ if (vff->put_ptr >= vff->put_end)
+ {
+ vio_fifo_add_lump(vff) ; /* traps put_ptr > put_end */
+
+ if (request > 0)
+ --request ;
+ } ;
- if (vf->one)
- vf->get_end = vf->put_ptr ; /* make sure have everything */
+ got = read_nb(fd, vff->put_ptr, vff->put_end - vff->put_ptr) ;
- if (vf->get_ptr >= vf->get_end)
- if (!vio_fifo_get_next_lump(vf))
- return 0 ; /* quit now if nothing there */
+ if (got <= 0)
+ {
+ if (got == -2) /* EOF met */
+ return (total > 0) ? (int)total : got ;
+ else
+ return (got == 0) ? (int)total : got ;
+ } ;
+
+ vff->put_ptr += got ;
+ total += got ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ } while (request > 0) ;
- return 1 ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ return total ;
} ;
/*------------------------------------------------------------------------------
- * Get upto 'n' bytes.
+ * Strip trailing whitespace and, if required, insert '\n' if result is not
+ * empty and does not now end in '\n'
*
- * Returns: number of bytes got -- may be zero.
+ * Strips anything 0x00..0x20 except for '\n'.
+ *
+ * Returns: 0..n -- number of bytes read
+ * -1 => failed -- see errno
+ * -2 => EOF met immediately
*/
-extern size_t
-vio_fifo_get(vio_fifo vf, void* dst, size_t n)
+extern void
+vio_fifo_trim(vio_fifo vff, bool term)
{
- size_t have ;
- void* dst_in ;
+ vio_fifo_lump lump ;
+ char* p ;
+ char* s ;
+ char* end_ptr ;
+ bool end_ptr_passed ;
+ char ch ;
+
+ /* Position at end of fifo, and establish how far back in current lump
+ * can discard whitespace.
+ */
+ lump = ddl_tail(vff->base) ;
+ p = vff->put_ptr ;
+ s = (lump == vff->get_lump) ? vff->get_ptr : lump->data ;
- if (!vio_fifo_get_ready(vf))
- return 0 ; /* quit now if nothing there */
+ if (vio_fifo_have_end_mark(vff))
+ end_ptr = vff->end_ptr ;
+ else
+ end_ptr = NULL ;
+ end_ptr_passed = false ;
- dst_in = dst ;
- while (n > 0)
+ /* Track backwards, until reach get_ptr or hit '\n' or something which is
+ * not 0x00..0x20.
+ */
+ ch = '\0' ;
+ while (1)
{
- have = vf->get_end - vf->get_ptr ;
+ if (s == p)
+ {
+ /* At the start of the current lump and/or at the get_ptr.
+ *
+ * If at the get_ptr, then cannot track any further back, but if
+ * there is a hold mark, there may be something before the get_ptr.
+ */
+ if (lump == vff->get_lump)
+ {
+ qassert(p == vff->get_ptr) ; /* hit get_ptr */
+
+ ch = '\0' ;
+ if (p == *vff->p_start)
+ break ; /* hit start of fifo */
+
+ if (p != lump->data)
+ qassert(p > lump->data) ;
+ else
+ {
+ qassert(lump != ddl_head(vff->base)) ;
+ s = ddl_prev(lump, list)->end ;
+ } ;
+
+ ch = *(s - 1) ;
+
+ if (ch != '\n')
+ ch = '\xFF' ; /* anything but '\0' */
+
+ break ;
+ } ;
+
+ lump = ddl_prev(lump, list) ;
+
+ p = lump->end ;
+ s = (lump == vff->get_lump) ? vff->get_ptr : lump->data ;
- if (have > n)
- have = n ;
+ continue ;
+ } ;
+
+ qassert(p > s) ;
- memcpy(dst, vf->get_ptr, have) ;
- vf->get_ptr += have ;
- dst = (char*)dst + have ;
+ ch = *(p-1) ;
- if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
- if (!vio_fifo_get_next_lump(vf))
- break ; /* quit if nothing more to come */
+ if ((ch > 0x20) || (ch == '\n'))
+ break ;
- n -= have ;
+ if (p == end_ptr)
+ end_ptr_passed = true ; /* stepped past the end mark */
+
+ --p ;
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ /* Decide what to do now.
+ *
+ * ch == '\0' <=> p points at end of empty fifo !
+ * ch == '\n' <=> p points after a '\n'.
+ * otherwise <=> p points after non-whitespace, or is at get_ptr and
+ * something other than '\n' precedes it.
+ *
+ * lump points at the lump that 'p' is in.
+ *
+ * If stepped back past the end_ptr, now is the time to set the end_ptr
+ * to the current position.
+ */
+ if (end_ptr_passed)
+ {
+ qassert(end_ptr != NULL) ;
- return (char*)dst - (char*)dst_in ;
+ vff->end_lump = lump ;
+ vff->end_ptr = p ;
+ } ;
+
+ /* If have stepped back across one or more lumps, trim now excess lumps off
+ * the end.
+ *
+ * Note, it is (just) possible that in inserting a '\n', that this will
+ * trim off a lump that needs to be re-instated -- but it is more
+ * straightforward to do this, and the odds are pretty slim.
+ */
+ if (lump != ddl_tail(vff->base))
+ vio_fifo_release_back_to(vff, lump) ;
+
+ /* set the (new) put_ptr.
+ */
+ vff->put_ptr = p ;
+
+ /* If we need to add a terminator, do that now.
+ */
+ if (term && (ch != '\n') && (ch != '\0'))
+ vio_fifo_put_byte(vff, '\n') ;
} ;
+/*==============================================================================
+ * Copy operations -- from one FIFO to another.
+ */
+
/*------------------------------------------------------------------------------
- * Get byte -- the long winded way.
+ * Copy src FIFO (everything from get_ptr to end mark or put_ptr) to dst FIFO.
*
- * See the inline vio_fifo_get_byte().
+ * Create a dst FIFO if there isn't one. There must be a src FIFO.
*
- * The version is used when the get_ptr is at or just before the end of the
- * current lump. Looks after all the necessary pointer updates associated with
- * hitting end of lump, or hitting end of FIFO.
+ * Appends to the dst FIFO.
*
- * Returns: 0x00..0xFF -- byte value (as an int)
- * -1 => FIFO is empty.
+ * Does not change the src FIFO in any way.
*/
-
-extern int
-vio_fifo_get_next_byte(vio_fifo vf)
+extern vio_fifo
+vio_fifo_copy(vio_fifo dst, vio_fifo src)
{
- unsigned char u ;
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
- if (!vio_fifo_get_ready(vf))
- return -1 ; /* quit now if nothing there */
+ if (dst == NULL)
+ dst = vio_fifo_new(src->size) ;
- u = *vf->get_ptr++ ;
+ VIO_FIFO_DEBUG_VERIFY(src) ;
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
- /* As soon as reach the end want either to discard empty lump, or reset
- * the pointers.
- */
- if (vf->get_ptr >= vf->get_end) /* deal with exhausted lump */
- vio_fifo_get_next_lump(vf) ;
+ src_lump = src->get_lump ;
+ src_ptr = src->get_ptr ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ while (1)
+ {
+ char* src_end ;
+
+ if (src_lump != src->end_lump)
+ src_end = src_lump->end ; /* end of not end_lump */
+ else
+ src_end = *src->p_end ; /* end of end_lump */
+
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
+
+ if (src_lump == src->end_lump)
+ break ;
+
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
- return u ;
+ return dst ;
} ;
/*------------------------------------------------------------------------------
- * Get pointer to a lump of bytes.
+ * Copy tail of src FIFO (everything from end mark to put_ptr) to dst FIFO.
*
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
+ * Create a dst FIFO if there isn't one. There must be a src FIFO.
*
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Appends to the dst FIFO.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * Does not change the src FIFO in any way.
*/
-extern void*
-vio_fifo_get_lump(vio_fifo vf, size_t* p_have)
+extern vio_fifo
+vio_fifo_copy_tail(vio_fifo dst, vio_fifo src)
{
- if (!vio_fifo_get_ready(vf))
+ vio_fifo_lump src_lump ;
+ char* src_ptr ;
+ vio_fifo_lump tail ;
+
+ if (dst == NULL)
+ dst = vio_fifo_new(src->size) ;
+
+ VIO_FIFO_DEBUG_VERIFY(src) ;
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
+
+ if (!vio_fifo_have_end_mark(src))
+ return dst ;
+
+ src_lump = src->end_lump ;
+ src_ptr = src->end_ptr ;
+ tail = ddl_tail(src->base) ;
+
+ while (1)
{
- *p_have = 0 ;
- return NULL ;
+ char* src_end ;
+
+ if (src_lump != tail)
+ src_end = src_lump->end ;
+ else
+ src_end = src->put_ptr ;
+
+ vio_fifo_put_bytes(dst, src_ptr, src_end - src_ptr) ;
+
+ if (src_lump == tail)
+ break ;
+
+ src_lump = ddl_next(src_lump, list) ;
+ src_ptr = src_lump->data ;
} ;
- *p_have = (vf->get_end - vf->get_ptr) ;
- return vf->get_ptr ;
+ VIO_FIFO_DEBUG_VERIFY(dst) ;
+
+ return dst ;
} ;
+/*==============================================================================
+ * End Mark Operations.
+ *
+ * Set/Clear end mark is pretty straightforward:
+ *
+ * * if there was an end_ptr before and the put_ptr is ahead of it:
+ *
+ * this adds one or more bytes between the get_ptr and the (new) end.
+ *
+ * * if there was no end_ptr, or it is the same as the put_ptr:
+ *
+ * setting/clearing the end_ptr makes no difference, because whenever
+ * the get_ptr reaches the put_ptr, the pointers are reset if they can
+ * be -- so need not worry about that here.
+ *
+ * Set the p_end and p_get_end to the new reality.
+ *
+ * The put_ptr stays in its current lump, so no need to worry about put_end.
+ */
+
/*------------------------------------------------------------------------------
- * Advance FIFO to position reached.
+ * Set end_mark at the current put position.
+ *
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
*
- * Having done vio_fifo_get_lump(), can take any number of bytes (up to the
- * number that "have"), then call this function to advance the pointers.
+ * If the put_ptr is at the end of the last lump, then the end_ptr will follow
+ * it if another lump is added to the FIFO.
*
- * The "here" argument must the the address returned by vio_fifo_get_lump()
- * plus the number of bytes taken.
+ * Set the p_end and p_get_end to the new reality.
*/
extern void
-vio_fifo_got_upto(vio_fifo vf, void* here)
+vio_fifo_set_end_mark(vio_fifo vff)
{
- vf->get_ptr = here ;
+ vff->p_end = &vff->end_ptr ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ vff->end_ptr = vff->put_ptr ;
+ vff->end_lump = ddl_tail(vff->base) ;
- if (vf->get_ptr >= vf->get_end)
- vio_fifo_get_next_lump(vf) ;
+ vio_fifo_set_get_end(vff) ; /* in case end_lump or p_end changed */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Write contents of FIFO -- assuming non-blocking file
+ * If there is an end mark, advance it to the put_ptr.
*
- * Will write all of FIFO, or upto but excluding the last lump.
- *
- * Returns: > 0 => blocked
- * 0 => all gone (up to last lump if !all)
- * < 0 => failed -- see errno
+ * If there was an end_mark before, move it (forward) to the current put_ptr,
+ * which keeps everything in between in the FIFO.
*
- * Note: will work perfectly well for a non-blocking file -- which should
- * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
+ * If there was no end mark before, do nothing.
*/
-extern int
-vio_fifo_write_nb(vio_fifo vf, int fd, bool all)
+extern void
+vio_fifo_step_end_mark(vio_fifo vff)
{
- char* src ;
- size_t have ;
- int done ;
-
- while ((src = vio_fifo_get_lump(vf, &have)) != NULL)
+ if (vio_fifo_have_end_mark(vff))
{
- if (!all && vf->one)
- break ; /* don't write last lump */
-
- done = write_nb(fd, src, have) ;
-
- if (done < 0)
- return -1 ; /* failed */
+ vff->end_ptr = vff->put_ptr ;
+ vff->end_lump = ddl_tail(vff->base) ;
- vio_fifo_got_upto(vf, src + done) ;
+ vio_fifo_set_get_end(vff) ; /* in case end_lump changed */
- if (done < (int)have)
- return 1 ; /* blocked */
- } ;
-
- return 0 ; /* all gone */
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+ }
} ;
/*------------------------------------------------------------------------------
- * Get the current rdr_end value.
+ * If there is an end_ptr, clear it -- everything between end_ptr and the
+ * current put_ptr is kept in the FIFO.
*
- * Unlike get_end, do not have a field for this, but find it each time.
+ * Set the p_end and p_get_end to the new reality.
*/
-inline static char*
-vio_fifo_rdr_end(vio_fifo vf)
+extern void
+vio_fifo_clear_end_mark(vio_fifo vff)
{
- if (vf->rdr_lump == ddl_tail(vf->base))
- return vf->put_ptr ;
- else
- return vf->rdr_lump->end ;
+ vff->p_end = &vff->put_ptr ;
+
+ vff->end_lump = ddl_tail(vff->base) ;
+
+ vio_fifo_set_get_end(vff) ; /* in case end_lump or p_end changed */
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*------------------------------------------------------------------------------
- * Get the current rdr position -- sets it up if not currently set.
+ * Move put_ptr back to the end mark, if any, and discard data.
*
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
+ * If there is an end_mark, keep it if required.
*
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * If there is no end mark, do nothing.
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * If there is an end mark but it is the same as the put_ptr, then need do
+ * nothing at all.
*
- * NB: unless returns FIFO is empty, it is a mistake to now do any "get"
- * operation other than vio_fifo_step_rdr(), until do vio_fifo_sync_rdr()
- * or vio_fifo_drop_rdr.
+ * Note that: if there is an end mark, then p_end == &end_ptr, so if
+ * *p_end == put_ptr, then end_ptr == put_ptr ; if there is no end mark,
+ * then p_end == &put_ptr, so *p_end == put_ptr !!
*/
-extern void*
-vio_fifo_get_rdr(vio_fifo vf, size_t* p_have)
+extern void
+vio_fifo_back_to_end_mark(vio_fifo vff, bool keep)
{
- if (!vio_fifo_get_ready(vf))
+ if (*vff->p_end != vff->put_ptr) /* test for not-empty end mark */
{
- *p_have = 0 ;
- return NULL ;
+ if (vio_fifo_debug)
+ assert(vio_fifo_have_end_mark(vff)) ;
+
+ if (vff->end_lump != ddl_tail(vff->base))
+ vio_fifo_release_back_to(vff, vff->end_lump) ;
+
+ if (*vff->p_start == vff->end_ptr)
+ vio_fifo_reset_ptrs(vff) ;
+ else
+ vff->put_ptr = vff->end_ptr ;
} ;
- if (vf->rdr_lump == NULL) /* set up new rdr if required */
+ if (!keep)
{
- vf->rdr_lump = ddl_head(vf->base) ;
- vf->rdr_ptr = vf->get_ptr ;
+ vff->p_end = &vff->put_ptr ;
+ vio_fifo_set_get_end(vff) ; /* in case get_lump == end_lump */
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
-
- *p_have = vio_fifo_rdr_end(vf) - vf->rdr_ptr ;
- return vf->rdr_ptr ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
+/*==============================================================================
+ * Get data from the FIFO.
+ */
+
/*------------------------------------------------------------------------------
- * Step the rdr forward by the given number of bytes.
+ * Get upto 'n' bytes -- steps past the bytes fetched.
*
- * Returns: address of next byte to get, *p_have = number of bytes available
- * or: NULL => FIFO is empty, *p_have = 0
+ * Stops at current end of FIFO (and not before).
*
- * If the FIFO is not empty, will return pointer to at least one byte.
+ * Returns: number of bytes got -- may be zero.
+ */
+extern ulen
+vio_fifo_get_bytes(vio_fifo vff, void* dst, ulen n)
+{
+ void* dst_start ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ dst_start = dst ;
+ while (n > 0)
+ {
+ ulen take ;
+
+ take = *vff->p_get_end - vff->get_ptr ;
+
+ if (take > n)
+ take = n ;
+ else if (take == 0)
+ break ;
+
+ memcpy(dst, vff->get_ptr, take) ;
+ dst = (char*)dst + take ;
+
+ n -= take ;
+
+ vio_fifo_step(vff, take) ;
+ } ;
+
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+
+ return (char*)dst - (char*)dst_start ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Write contents of FIFO -- assuming non-blocking file
*
- * Returns number of bytes to the end of the current lump. There may be
- * further lumps beyond the current one.
+ * Will write all of FIFO up to end mark or put_ptr, or upto but excluding
+ * the end_lump.
*
- * NB: this does not change the get pointers, so all the data being stepped
- * over is preserved in the FIFO, until vio_fifo_sync_rdr().
+ * Returns: > 0 => blocked
+ * 0 => all gone (up to last lump if !all)
+ * < 0 => failed -- see errno
*
- * NB: the step may NOT exceed the last reported "have".
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
*/
-extern void*
-vio_fifo_step_rdr(vio_fifo vf, size_t* p_have, size_t step)
+extern int
+vio_fifo_write_nb(vio_fifo vff, int fd, bool all)
{
- char* rdr_end ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- assert(vf->rdr_lump != NULL) ;
+ while (1)
+ {
+ ulen have ;
+ int done ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ if ((vff->get_lump == vff->end_lump) && !all)
+ break ; /* don't write last lump */
- rdr_end = vio_fifo_rdr_end(vf) ;
- vf->rdr_ptr += step ;
+ have = vio_fifo_get(vff) ;
- if (vf->rdr_ptr >= rdr_end)
- {
- assert(vf->rdr_ptr == rdr_end) ;
+ if (have == 0)
+ break ;
- if (vf->rdr_lump != ddl_tail(vf->base))
- {
- vf->rdr_lump = ddl_next(vf->rdr_lump, list) ;
- vf->rdr_ptr = vf->rdr_lump->data ;
+ done = write_nb(fd, vio_fifo_get_ptr(vff), have) ;
- rdr_end = vio_fifo_rdr_end(vf) ;
- } ;
+ if (done < 0)
+ return -1 ; /* failed */
+
+ vio_fifo_step(vff, done) ;
+
+ if (done < (int)have)
+ return 1 ; /* blocked */
} ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- *p_have = (rdr_end - vf->rdr_ptr) ;
- return (*p_have > 0) ? vf->rdr_ptr : NULL ;
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Move FIFO get position to the rdr position, if any.
+ * Write contents of FIFO -- assuming blocking file
+ *
+ * Will write all of FIFO up to end mark or put_ptr.
+ *
+ * Returns: 0 => all gone
+ * < 0 => failed -- see errno
*
- * This clears the rdr position, and removes all data between the current and
- * new get positions from the FIFO.
+ * Note: will work perfectly well for a non-blocking file -- which should
+ * never return EAGAIN/EWOULDBLOCK, so will return from here "all gone".
*/
-extern void
-vio_fifo_sync_rdr(vio_fifo vf)
+extern int
+vio_fifo_fwrite(vio_fifo vff, FILE* file)
{
- vio_fifo_lump head ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ while (1)
+ {
+ int done ;
+ ulen have ;
- if (vf->rdr_lump == NULL)
- return ;
+ have = vio_fifo_get(vff) ;
+ if (have == 0)
+ break ;
- while ((head = ddl_head(vf->base)) != vf->rdr_lump)
- {
- vf->get_ptr = vf->get_end ; /* jump to end of lump */
- vio_fifo_lump_release(vf, head) ;
- } ;
+ done = fwrite(vio_fifo_get_ptr(vff), have, 1, file) ;
- vf->get_ptr = vf->rdr_ptr ; /* jump to rdr_ptr */
+ if (done < 1)
+ return -1 ; /* failed */
- vf->rdr_lump = NULL ; /* clear the rdr */
- vf->rdr_ptr = NULL ;
+ vio_fifo_step(vff, have) ;
+ } ;
- if (vf->one)
- {
- if (vf->put_ptr == vf->get_ptr) /* reset pointers if FIFO empty */
- vio_fifo_ptr_reset(vf, head) ;
- else
- vf->get_end = vf->put_ptr ;
- }
- else
- vf->get_end = head->end ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ return 0 ; /* all gone */
} ;
/*------------------------------------------------------------------------------
- * Drop the rdr position (if any).
+ * Skip get_ptr to the current end -- which may be the current end mark.
*
- * This clears the rdr position leaving the get position and FIFO unchanged.
+ * Does not clear any hold_ptr or end_ptr.
*/
extern void
-vio_fifo_drop_rdr(vio_fifo vf)
+vio_fifo_skip_to_end(vio_fifo vff)
{
- VIO_FIFO_DEBUG_VERIFY(vf) ;
+ /* Advance directly to the current end and then synchronise get_ptr
+ */
+ vff->get_lump = vff->end_lump ;
+ vff->get_ptr = *vff->p_end ;
+ vff->p_get_end = vff->p_end ;
- vf->rdr_lump = NULL ;
- vf->rdr_ptr = NULL ;
+ vio_fifo_sync_get(vff) ;
} ;
-/*------------------------------------------------------------------------------
- * Move on to next lump to get stuff from.
- *
- * Advance pointers etc. so that have at least one byte available, unless
- * the FIFO is entirely empty.
- *
- * This should be called if (vf->get_ptr >= vf->get_end) -- asserts that
- * these are equal !
+/*==============================================================================
+ * Hold Mark Operations.
*
- * NB: when there is only one block, it may be that get_end is out of date,
- * and should be advanced to the current put_ptr position.
+ * Set or clear hold_ptr.
*
- * That is done here, but may be worth updating get_end before testing
- * against get_ptr.
+ * The get_ptr is unambiguous -- so the hold_ptr is, because it is only ever
+ * set equal to the get_ptr !
*
- * Returns: true <=> at least one byte in FIFO.
+ * The put_ptr stays in its current lump, so no need to worry about put_end.
+ */
+
+/*------------------------------------------------------------------------------
+ * Set hold mark -- clearing existing one, if any.
*
- * NB: if finds that the FIFO is empty, resets the pointers to the start
- * of the last lump -- does not release the last lump.
+ * Discard all contents up to the current get_ptr (easy if no hold mark), then
+ * set hold mark at get_ptr.
*/
-static bool
-vio_fifo_get_next_lump(vio_fifo vf)
+extern void
+vio_fifo_set_hold_mark(vio_fifo vff)
{
- vio_fifo_lump head ;
+ vio_fifo_release_up_to(vff, vff->get_lump) ;
- VIO_FIFO_DEBUG_VERIFY(vf) ;
- assert(vf->get_ptr == vf->get_end) ;
+ if (vff->get_ptr == vff->put_ptr)
+ vio_fifo_reset_ptrs(vff) ;
+ else
+ vff->hold_ptr = vff->get_ptr ;
- head = ddl_head(vf->base) ; /* current lump for get */
+ vff->p_start = &vff->hold_ptr ;
- /* Deal with the simple case of one lump, first.
- *
- * To save work when putting data into the FIFO (particularly when putting
- * a byte at a time) does not keep the vf->get_end up to date (when there is
- * only one lump).
- *
- * If the FIFO is empty, reset pointers and return empty.
- */
- if (vf->one)
- {
- assert( (head != NULL) && (head == ddl_tail(vf->base)) ) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
- if (vf->get_ptr < vf->put_ptr)
- {
- /* Had an out of date vf->get_end */
- vf->get_end = vf->put_ptr ;
+/*------------------------------------------------------------------------------
+ * Clear hold mark -- if any.
+ *
+ * Discard all contents up to the current get_ptr (easy if no hold mark), then
+ * clear hold mark (no effect if not set).
+ *
+ * Note that clearing a hold_ptr in an empty FIFO resets all the pointers. To
+ * avoid that could test for an empty hold mark (*p_start == get_ptr), but the
+ * extra step in the majority case seems worse than the extra work in the
+ * minority one.
+ */
+extern void
+vio_fifo_clear_hold_mark(vio_fifo vff)
+{
+ vio_fifo_release_up_to(vff, vff->get_lump) ;
- return true ; /* FIFO not empty */
- } ;
+ if (vff->get_ptr == vff->put_ptr)
+ vio_fifo_reset_ptrs(vff) ;
- assert(vf->get_ptr == vf->put_ptr) ;
+ vff->p_start = &vff->get_ptr ;
- /* FIFO is empty -- reset pointers and exit */
- vio_fifo_ptr_reset(vf, head) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
+} ;
- return false ; /* FIFO empty */
- } ;
+/*------------------------------------------------------------------------------
+ * If there is an hold_mark, reset get_ptr *back* to it, and leave the mark
+ * set or clear.
+ *
+ * If there is no hold mark, set one at the current position, if required.
+ *
+ * Setting the get_ptr back to the hold_ptr sets it to an unambiguous
+ * position. If the get_ptr == hold_ptr then if the FIFO is empty, the
+ * pointers will have been reset.
+ */
+extern void
+vio_fifo_back_to_hold_mark(vio_fifo vff, on_off_b set)
+{
+ if (vio_fifo_have_hold_mark(vff))
+ vio_fifo_set_get_ptr(vff, ddl_head(vff->base), vff->hold_ptr) ;
+
+ vff->p_start = (set) ? &vff->hold_ptr : &vff->get_ptr ;
- /* Release the head and update pointers
- *
- * Deals with possibility that nothing has yet been allocated
- */
- vio_fifo_lump_release(vf, head) ;
- return (vf->get_ptr < vf->get_end) ;
+ VIO_FIFO_DEBUG_VERIFY(vff) ;
} ;
/*==============================================================================
* For debug purposes -- verify the state of the given FIFO
*/
Private void
-vio_fifo_verify(vio_fifo vf)
+vio_fifo_verify(vio_fifo vff)
{
vio_fifo_lump head ;
vio_fifo_lump lump ;
vio_fifo_lump tail ;
+ bool own_seen ;
- head = ddl_head(vf->base) ;
- tail = ddl_tail(vf->base) ;
+ head = ddl_head(vff->base) ;
+ tail = ddl_tail(vff->base) ;
- /* If nothing allocated, should all be NULL & !vf->one */
- /* If something allocated, tail must not be NULL */
+ /* FIFO always has at least one lump. */
if (head == NULL)
+ zabort("head is NULL") ;
+ if (tail == NULL)
+ zabort("tail is NULL") ;
+
+ /* Make sure that the lump pointers all work
+ *
+ * When finished, know that head <= get_lump <= end_lump <= tail.
+ */
+ own_seen = false ;
+
+ lump = head ;
+ while (1)
+ {
+ if (lump == vff->own_lump)
+ own_seen = true ;
+
+ if (lump == vff->get_lump)
+ break ;
+
+ lump = ddl_next(lump, list) ;
+ if (lump == NULL)
+ zabort("ran out of lumps looking for get_lump") ;
+ } ;
+
+ while (lump != vff->end_lump)
+ {
+ lump = ddl_next(lump, list) ;
+
+ if (lump == NULL)
+ zabort("ran out of lumps looking for end_lump") ;
+
+ if (lump == vff->own_lump)
+ own_seen = true ;
+ } ;
+
+ while (lump != tail)
+ {
+ lump = ddl_next(lump, list) ;
+
+ if (lump == NULL)
+ zabort("ran out of lumps looking for tail") ;
+
+ if (lump == vff->own_lump)
+ own_seen = true ;
+ } ;
+
+ if (vff->spare == vff->own_lump)
{
- if ( (tail != NULL)
- || (vf->put_ptr != NULL)
- || (vf->put_end != NULL)
- || (vf->get_ptr != NULL)
- || (vf->rdr_lump != NULL)
- || (vf->rdr_ptr != NULL)
- || (vf->one) )
- zabort("nothing allocated, but not all NULL") ;
- return ;
+ if (own_seen)
+ zabort("own seen in FIFO, but is also spare") ;
}
else
{
- if (tail == NULL)
- zabort("head pointer not NULL, but tail pointer is") ;
+ if (!own_seen)
+ zabort("not found own lump in the FIFO") ;
+ } ;
+
+ /* Check that the p_start, p_get_end and p_end are valid
+ */
+ if ((vff->p_start != &vff->hold_ptr) && (vff->p_start != &vff->get_ptr))
+ zabort("p_start is neither &get_ptr nor &hold_ptr") ;
+
+ if ((vff->p_end != &vff->end_ptr) && (vff->p_end != &vff->put_ptr))
+ zabort("p_end is neither &put_ptr nor &end_ptr") ;
+
+ if (vff->get_lump == vff->end_lump)
+ {
+ if (vff->p_get_end != vff->p_end)
+ zabort("p_get_end not equal to p_end and is in end_lump") ;
+ }
+ else
+ {
+ if (vff->p_get_end != &vff->get_lump->end)
+ zabort("p_get_end not equal to get_lump->end and is not in end_lump") ;
} ;
/* Check that all the pointers are within respective lumps
*
* Know that put_end is always tail->end, but get_end need not be.
+ *
+ * When finished, know that:
+ *
+ * - get_lump == head if !hold_mark
+ * - end_lump == tail if !end_mark
+ * - that all pointers are within their respective lumps
+ * - all ptr are <= their respective ends
+ * - if hold_mark: hold_ptr <= get_ptr or head != get_lump
+ * - if end_mark: end_ptr <= put_ptr or tail != end_lump
*/
- if ( (tail->data > vf->put_ptr)
- || (vf->put_ptr > vf->put_end)
- || (vf->put_end != tail->end) )
- zabort("put pointers outside the tail lump") ;
+ if (vio_fifo_have_hold_mark(vff))
+ {
+ if ( (head->data > vff->hold_ptr)
+ || (vff->hold_ptr > head->end) )
+ zabort("hold_ptr outside the head lump") ;
- if ( (head->data > vf->get_ptr)
- || (vf->get_ptr > vf->get_end)
- || (vf->get_end > head->end) )
- zabort("get pointers outside the head lump") ;
+ if ((vff->get_lump == head) && (vff->hold_ptr > vff->get_ptr))
+ zabort("hold_ptr greater than get_ptr") ;
+ }
+ else
+ {
+ if (vff->get_lump != head)
+ zabort("no hold_ptr, but get_lump is not head") ;
+ } ;
+
+ if ( (vff->get_lump->data > vff->get_ptr)
+ || (vff->get_ptr > *vff->p_get_end)
+ || (*vff->p_get_end > vff->get_lump->end))
+ zabort("get pointers outside the get lump") ;
- /* If head == tail, should be vf->one, etc. */
- if (head == tail)
+ if (vio_fifo_have_end_mark(vff))
{
- if (!vf->one)
- zabort("have one lump, but !vf->one") ;
+ if ( (vff->end_lump->data > vff->end_ptr)
+ || (vff->end_ptr > vff->end_lump->end) )
+ zabort("end pointer outside the end lump") ;
- if (vf->get_end > vf->put_ptr)
- zabort("get_end is greater than put_ptr when vf->one") ;
+ if ((vff->end_lump == tail) && (vff->end_ptr > vff->put_ptr))
+ zabort("end pointer greater than put pointer") ;
}
else
{
- if (vf->one)
- zabort("have two or more lumps, but vf->one is true") ;
-
- if (vf->get_end != head->end)
- zabort("get_end is not head->end when !vf->one") ;
+ if (vff->end_lump != tail)
+ zabort("no end_ptr, but end_lump is not tail") ;
} ;
- /* If have an rdr_lump -- make sure everything else is valid */
- if (vf->rdr_lump != NULL)
+ if ( (tail->data > vff->put_ptr)
+ || (vff->put_ptr > vff->put_end)
+ || (vff->put_end != tail->end) )
+ zabort("put pointers outside the tail lump") ;
+
+ /* Check that if get_ptr == p_get_end, that it is empty, or there is some
+ * not-empty hold or end mark.
+ *
+ * The point is to trap any failure to reset pointers or advance the get_ptr
+ * when it hits *p_get_end.
+ */
+ if (vff->get_ptr == *vff->p_get_end)
{
- lump = head ;
- while (lump != vf->rdr_lump)
+ if (*vff->p_start != vff->put_ptr)
{
- if (lump == tail)
- zabort("rdr_lump is not part of FIFO") ;
- lump = ddl_next(lump, list) ;
+ /* Not empty -- so must have a hold and/or end */
+ if (!(vio_fifo_have_hold_mark(vff) || vio_fifo_have_end_mark(vff)))
+ zabort("get_ptr is at get_end, is not empty by no marks set") ;
} ;
+ } ;
- if ( (lump->data > vf->rdr_ptr)
- || (vf->rdr_ptr > lump->end) )
- zabort("rdr_ptr outside its lump") ;
-
- if ( (lump == head) && (vf->rdr_ptr < vf->get_ptr))
- zabort("rdr_ptr is less than get_ptr in first lump") ;
-
- if ( (lump == tail) && (vf->rdr_ptr > vf->put_ptr))
- zabort("rdr_ptr is greater than put_ptr in last lump") ;
- }
- else
+ /* Check that if is empty, the pointers are reset.
+ */
+ if (*vff->p_start == vff->put_ptr)
{
- if (vf->rdr_ptr != NULL)
- zabort("rdr_ptr not NULL when rdr_lump is") ;
- }
+ if ( (tail != head)
+ || (vff->get_lump != head)
+ || (vff->end_lump != head)
+ || (vff->get_ptr != head->data)
+ || (vff->put_ptr != head->data)
+ || (vff->put_end != head->end)
+ || !( (vff->p_start != &vff->hold_ptr) || (vff->hold_ptr == head->data) )
+ || !( (vff->p_end != &vff->end_ptr) || (vff->end_ptr == head->data) )
+ )
+ zabort("pointers not valid for empty fifo") ;
+ } ;
} ;