/* ==========================================================================
 * libevnet/src/bufio-pipe.c - Network server library for libevent.
 * --------------------------------------------------------------------------
 * Copyright (c) 2006  William Ahern
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ==========================================================================
 */
#include <stddef.h>		/* size_t */

#include <assert.h>

#include <sys/queue.h>		/* SLIST */

#include <sys/time.h>		/* struct timeval */

#include "bufio.h"		/* struct bufio_sink struct bufio_source */
#include "pipe.h"

#ifndef NULL
#define NULL	0
#endif


#define BUFIO_PIPE_FRAME_PUSH(p, f) do {				\
	(f)->pp	= &(p);							\
	SLIST_INSERT_HEAD(&(p)->stack, (f), sle);			\
} while(0)

#define BUFIO_PIPE_FRAME_POP(p, f) do {					\
	if ((p) != 0) {							\
		assert((f) == SLIST_FIRST(&(p)->stack));		\
		SLIST_REMOVE_HEAD(&(p)->stack, sle);			\
	}								\
} while(0)

#define BUFIO_PIPE_FRAME_OKAY(f)	((f)->pp != 0)


const struct bufio_pipe_options bufio_pipe_defaults;


static size_t bufio_pipe_copyfrom(struct bufio_sink *snk, struct bufio_source *src, int flags, enum bufio_errno *ioe) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(snk, bufio_pipe, bufio_sink);

	if (0 == p->snk.next)
		return 0;

	return p->snk.next->copyfrom(p->snk.next, src, flags, ioe);
} /* bufio_pipe_copyfrom() */


static size_t bufio_pipe_copyin(struct bufio_sink *snk, void *srcbuf, size_t bufsiz, int flags, enum bufio_errno *ioe) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(snk, bufio_pipe, bufio_sink);

	if (0 == p->snk.next)
		return 0;

	return p->snk.next->copyin(p->snk.next, srcbuf, bufsiz, flags, ioe);
} /* bufio_pipe_copyin() */


static size_t bufio_pipe_sink_buffered(struct bufio_sink *snk) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(snk, bufio_pipe, bufio_sink);

	if (0 == p->snk.next)
		return 0;

	return p->snk.next->buffered(p->snk.next);
} /* bufio_pipe_sink_buffered() */


static enum bufio_errno bufio_pipe_sink_poll(struct bufio_sink *snk, bufio_sink_poll_cb fn, void *arg, struct timeval *timeout) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(snk, bufio_pipe, bufio_sink);

	assert(0 == p->snk.fn);

	p->snk.fn	= fn;
	p->snk.arg	= arg;

	/* Timeout not supported. */

	return 0;
} /* bufio_pipe_sink_poll() */


static enum bufio_errno bufio_pipe_sink_cancel(struct bufio_sink *snk, int notify) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(snk, bufio_pipe, bufio_sink);
	bufio_sink_poll_cb fn;

	if (0 != (fn = p->snk.fn)) {
		p->snk.fn	= 0;

		if (notify)
			fn(BUFIO_CAST_TO(p, bufio_pipe, bufio_sink), 0, p->snk.arg);
	}

	return 0;
} /* bufio_pipe_sink_cancel() */


static size_t bufio_pipe_copyto(struct bufio_source *src, struct bufio_sink *snk, int flags, enum bufio_errno *ioe) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(src, bufio_pipe, bufio_source);

	if (0 == p->src.next)
		return 0;

	return p->src.next->copyto(p->src.next, snk, flags, ioe);
} /* bufio_pipe_copyto() */


static size_t bufio_pipe_copyout(struct bufio_source *src, void *buf, size_t bufsiz, int flags, enum bufio_errno *ioe) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(src, bufio_pipe, bufio_source);

	if (0 == p->src.next)
		return 0;

	return p->src.next->copyout(p->src.next, buf, bufsiz, flags, ioe);
} /* bufio_pipe_copyout() */


static size_t bufio_pipe_source_buffered(struct bufio_source *src) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(src, bufio_pipe, bufio_source);

	if (0 == p->src.next)
		return 0;

	return p->src.next->buffered(p->src.next);
} /* bufio_pipe_source_buffered() */


static enum bufio_errno bufio_pipe_source_poll(struct bufio_source *src, bufio_source_poll_cb fn, void *arg, struct timeval *timeout) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(src, bufio_pipe, bufio_source);

	assert(0 == p->src.fn);

	p->src.fn	= fn;
	p->src.arg	= arg;

	/* Timeout not supported. */

	return 0;
} /* bufio_pipe_source_poll() */


static enum bufio_errno bufio_pipe_source_cancel(struct bufio_source *src, int notify) {
	struct bufio_pipe *p	= BUFIO_CAST_FROM(src, bufio_pipe, bufio_source);
	bufio_source_poll_cb fn;

	if (0 != (fn = p->src.fn)) {
		p->src.fn	= 0;

		if (notify)
			fn(BUFIO_CAST_TO(p, bufio_pipe, bufio_source), 0, p->src.arg);
	}

	return 0;
} /* bufio_pipe_source_cancel() */


struct bufio_pipe *bufio_pipe_init(struct bufio_pipe *p, const struct bufio_pipe_options *opts) {
	static const struct bufio_pipe bufio_pipe_initializer;
	struct bufio_source *src;
	struct bufio_sink *snk;

	*p	= bufio_pipe_initializer;
	p->opts	= *((opts)? opts : &bufio_pipe_defaults);

	src		= BUFIO_CAST_TO(p, bufio_pipe, bufio_source);
	*src		= BUFIO_SOURCE_INITIALIZER;
	src->copyto	= &bufio_pipe_copyto;
	src->copyout	= &bufio_pipe_copyout;
	src->buffered	= &bufio_pipe_source_buffered;
	src->poll	= &bufio_pipe_source_poll;
	src->cancel	= &bufio_pipe_source_cancel;

	snk		= BUFIO_CAST_TO(p, bufio_pipe, bufio_sink);
	*snk		= BUFIO_SINK_INITIALIZER;
	snk->copyfrom	= &bufio_pipe_copyfrom;
	snk->copyin	= &bufio_pipe_copyin;
	snk->buffered	= &bufio_pipe_sink_buffered;
	snk->poll	= &bufio_pipe_sink_poll;
	snk->cancel	= &bufio_pipe_sink_cancel;

	SLIST_INIT(&p->stack);

	return p;
} /* bufio_pipe_init() */


void bufio_pipe_destroy(struct bufio_pipe *p) {
	struct bufio_pipe_frame *f;

	p->snk.fn	= 0;
	p->src.fn	= 0;

	SLIST_FOREACH(f, &p->stack, sle)
		*f->pp = 0, f->pp = 0;

	SLIST_INIT(&p->stack);

	return /* void */;	
} /* bufio_pipe_destroy() */


void bufio_pipe_set_source(struct bufio_pipe *p, struct bufio_source *src) {
	p->src.next	= src;

	return /* void */;
} /* bufio_pipe_set_source() */


void bufio_pipe_set_sink(struct bufio_pipe *p, struct bufio_sink *snk) {
	p->snk.next	= snk;

	return /* void */;
} /* bufio_pipe_set_sink() */


struct bufio_source *bufio_pipe_init_source(struct bufio_pipe *p, const struct bufio_pipe_options *opts) {
	bufio_pipe_init(p, opts);

	return bufio_pipe_to_source(p);
} /* bufio_pipe_init_source() */


struct bufio_sink *bufio_pipe_init_sink(struct bufio_pipe *p, const struct bufio_pipe_options *opts) {
	bufio_pipe_init(p, opts);

	return bufio_pipe_to_sink(p);
} /* bufio_pipe_init_sink() */


struct bufio_source *bufio_pipe_to_source(struct bufio_pipe *p) {
	return BUFIO_CAST_TO(p, bufio_pipe, bufio_source);
} /* bufio_pipe_to_source() */


struct bufio_sink *bufio_pipe_to_sink(struct bufio_pipe *p) {
	return BUFIO_CAST_TO(p, bufio_pipe, bufio_sink);
} /* bufio_pipe_to_sink() */


void bufio_pipe_notify(struct bufio_pipe *p) {
	struct bufio_pipe_frame f;
	bufio_source_poll_cb src_cb;
	bufio_sink_poll_cb snk_cb;

	BUFIO_PIPE_FRAME_PUSH(p, &f);

	if (0 != (snk_cb = p->snk.fn)) {
		p->snk.fn	= 0;

		snk_cb(BUFIO_CAST_TO(p, bufio_pipe, bufio_sink), 0, p->snk.arg);

		if (!BUFIO_PIPE_FRAME_OKAY(&f))
			return /* void */;
	}

	if (0 != (src_cb = p->src.fn)) {
		p->src.fn	= 0;

		src_cb(BUFIO_CAST_TO(p, bufio_pipe, bufio_source), 0, p->src.arg);

		if (!BUFIO_PIPE_FRAME_OKAY(&f))
			return /* void */;
	}

	return /* void */;
} /* bufio_pipe_notify() */



syntax highlighted by Code2HTML, v. 0.9.1