/*
 * $Id: udfifo_err.c,v 1.1 2000/08/07 23:15:04 emmerson Exp $
 */


#include "udposix.h"	/* for voidp */

#include <stddef.h>	/* for size_t */
#include <assert.h>
#include <stdio.h>	/* needed by some non-conforming <assert.h>s */
#include <stdlib.h>	/* for malloc() & memcpy() */

#include "udfifo.h"	/* for my public API */


/*
 * Destroy a FIFO.
 */
    int
udfifo_destroy(fifo)
    Udfifo	*fifo;
{
    int		status;

    if (fifo == NULL) {
	status	= UDFIFO_EINVAL;
    } else {
	if (fifo->buf == NULL) {
	    status	= UDFIFO_EINVAL;
	} else {
	    (void) free((voidp)fifo->buf);
	    fifo->buf	= NULL;
	    status	= UDFIFO_ESUCCESS;
	}
    }

    return status;
}


/*
 * Initiaize a FIFO.
 */
    int
udfifo_init(fifo, eltsize, numelts, mode)
    Udfifo	*fifo;
    size_t	eltsize;
    int		numelts;
    int		mode;
{
    int		status;

    if (fifo == NULL
	    || eltsize <= 0
	    || numelts <= 0
	    || (mode != 0 && mode != UDFIFO_FORCE)) {
	status	= UDFIFO_EINVAL;
    } else {
	fifo->buf	= malloc((size_t)(eltsize*numelts));
	if (fifo->buf == NULL) {
	    (void) udfifo_destroy(fifo);
	    status	= UDFIFO_ENOMEM;
	} else {
	    fifo->eltsize	= eltsize;
	    fifo->maxelts	= numelts;
	    fifo->count	= 0;
	    fifo->mode	= mode;
	    fifo->head	= fifo->buf;
	    fifo->tail	= fifo->buf;
	    fifo->end	= fifo->buf + (numelts-1)*eltsize;
	    status	= UDFIFO_ESUCCESS;
	}
    }

    return status;
}


/*
 * Add to a FIFO.
 */
    int
udfifo_put(fifo, new, old)
    Udfifo	*fifo;
    const void	*new;
    void	*old;
{
    int		status;

    if (fifo == NULL || new == NULL) {
	status	= UDFIFO_EINVAL;
    } else {
	status	= UDFIFO_ESUCCESS;

	if (fifo->count == fifo->maxelts) {
	    if (fifo->mode != UDFIFO_FORCE) {
		status	= UDFIFO_ENOSPC;
	    } else {
		(void) udfifo_get(fifo, old);
		status	= UDFIFO_EFORCED;
	    }
	}

	if (fifo->count < fifo->maxelts) {
	    (void) memcpy((voidp)fifo->tail, new, fifo->eltsize);

	    fifo->tail	= fifo->tail < fifo->end
			    ? fifo->tail + fifo->eltsize
			    : fifo->buf;

	    ++fifo->count;
	}
    }

    return status;
}


/*
 * Remove from a FIFO.
 */
    int
udfifo_get(fifo, old)
    Udfifo	*fifo;
    void	*old;
{
    int		status;

    if (fifo == NULL) {
	status	= UDFIFO_EINVAL;
    } else {
	if (fifo->count == 0) {
	    status	= UDFIFO_ENOENT;
	} else {
	    if (old != NULL)
		(void) memcpy(old, (voidp)fifo->head, fifo->eltsize);

	    fifo->head	= fifo->head < fifo->end
			    ? fifo->head + fifo->eltsize
			    : fifo->buf;

	    --fifo->count;
	    status	= UDFIFO_ESUCCESS;
	}
    }

    return status;
}


/*
 * Return the number of elements in a FIFO.
 */
    int
udfifo_count(fifo)
    const Udfifo	*fifo;
{
    return fifo->count == NULL
		? UDFIFO_EINVAL
		: fifo->count;
}


/*
 * Return the number of empty spaces in a FIFO in terms of elements.
 */
    int
udfifo_space(fifo)
    const Udfifo	*fifo;
{
    return fifo == NULL
		? UDFIFO_EINVAL
		: fifo->maxelts - fifo->count;
}
