/* rb.c - ring buffer routines	*/

#ifdef	DOCUMENTATION
title	rb	Ring Buffers
index	RingBuffers
index	rb
index	rbin()
index	rbout()
index	struct rb
usage
	 #include <rb.h>
	 struct rb r;
	 BOOL ok;	/* true if done successfully */
	 #define ITMSIZ (...)	/* # chars in item */
	 #define NUM (...)	/* # items in buf */
	 char it[ITMSIZ];	/* an item */
	 char mybuffer[ITMSIZ*NUM];	/* ringbuf */
	 
	 rbnew(&r,mybuffer,ITMSIZ,NUM); /* init */
	 ok = rbin(&r,it);	/* false: no room */
	 ok = rbout(&r,it);	/* false: empty */
	 
description

	Two routines, and a struct, implement the basic idea of
	a ring buffer. Items put into/outof the buffer are fixed
	length. The ring buffer is controlled by a header (struct rb)
	that contains details like buffer element size, where the
	buffer is etc.
	The header need not be near the buffer.
	For an example of how to set up a buffer header, see RBTEST.C.

	Rbin(r,it) will either (a) add elemnet 'it' to the buffer, returning
	TRUE, or (b) determine the buffer is full, not touch the buffer,
	and return FALSE.

	Rbout(r,it) will either (a) remove the next element from the
	buffer, copying it to 'it', returning TRUE, or (b) determine
	the ringbuffer is empty, preserve both the buffer and 'it',
	and return FALSE.

	The header is seperate from the buffer. This allows eg the
	buffer to be mapped very differently from the header.

	All buffer knowledge is in the header. This allows two different
	buffers to have different element sizes,
	or other details different, so you may use
	these routines to maintain all your fixed-element-size ring
	buffers.

	Rbnew() is used to initialise a struct rb.
bugs
internal
	Test with rbtest.c .

	A n-slot ring buffer has n*n+1 states: EMPTY, and n data lengths
	starting at n positions. Since rb_in and rb_out are normally pointing
	at one of the n buffer slots we can't represent the EMPTY case.
	So we define rb_in==NULL to mean empty buffer.
#endif

/*
 *		E D I T   H I S T O R Y
 *
 *  3aug85 DLE	Create.
 *
 */


#include <rb.h>
#include <algol68.h>

VOID rbnew(r,buf,siz,num)		/* set up struct rb		*/
struct rb *	r;
char *		buf;			/* where the ring buffer starts	*/
unsigned	siz;			/* length (chars) of item	*/
unsigned	num;			/* number of items in ring	*/
BEGIN
	r -> rb_in	=	NULL;
	r -> rb_len	=	siz;
	r -> rb_beg	=	buf;
	r -> rb_end	=	buf + num*siz;
END

BOOL rbin(r,it)				/* TRUE if was done OK		*/
struct rb *	r;			/* the ring buffer		*/
char *		it;			/* thing to add to ring buffer	*/
BEGIN
register BOOL		retval;		/* TRUE if was done		*/
					/* FALSE if no room		*/
register char *		i;		/* next slot to input to	*/
register char *		o;		/* next slot to output from	*/
register unsigned	len;		/* length of a unit (chars)	*/
register char *		beg;		/* start of buffer ring		*/

	beg	= r -> rb_beg;
	o	= r -> rb_out;
	IF	( (i=r->rb_in) )
	THEN				/* at least something in ring	*/
		retval = (i!=o);	/* FALSE if ring is full, i==o	*/
	ELSE				/* empty ring			*/
		r -> rb_out = i = o = beg;
		retval = TRUE;
	FI
	IF	(retval)
	THEN	copy(i,it,len=r->rb_len);
		i += len;
		IF	(i>=r->rb_end)
		THEN	i = beg;
		FI
		r -> rb_in = i;
	FI
	return(retval);
END

BOOL rbout(r,it)			/* TRUE if was done OK		*/
struct rb *	r;			/* the ring buffer		*/
char *		it;			/* where to copy data to	*/
BEGIN
register unsigned	len;		/* length of object		*/
register BOOL		retval;		/* true if was done OK		*/
					/* FALSE if noting in buffer to	*/
					/* return via it		*/
register char *		i;		/* address of next input slot	*/
register char *		o;		/* address of next output slot	*/

	IF	(retval = i = r->rb_in)
	THEN				/* buffer has at least one item	*/
		copy(it,o=r->rb_out,len=r->rb_len);	/* output item	*/
		o += len;		/* advance to next output slot	*/
		IF	(o >= r->rb_end)
		THEN	o = r->rb_beg;	/* wrap output slot		*/
		FI
		IF	(o==i)
		THEN			/* we just emptied last item	*/
			r -> rb_in = NULL;	/* rb_out random	*/
		ELSE	r -> rb_out = o;
		FI
	END
	return(retval);

END

/* end: rb.c */
