/* ==========================================================================
 * libevnet/src/bufio/socket.c - Network server library for libevent.
 * --------------------------------------------------------------------------
 * Copyright (c) 2006  Barracuda Networks, Inc.
 * 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 <limits.h>	/* PIPE_BUF */

#include <errno.h>	/* EAGAIN EFAULT EPIPE ENOTSUP */

#include <assert.h>

#include <string.h>	/* memcpy(3) */

#include <sys/param.h>	/* MIN */

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

#include <sys/types.h>
#include <sys/time.h>	/* struct timeval timercmp(3) */

#if _WIN32
#include <winsock2.h>	/* send(2) recv(2) */
#else
#include <sys/socket.h>	/* recv(2) send(2) */
#endif

#include <windows.h>	/* GetLastError SetLastError */

#include <winsock2.h>	/* INVALID_SOCKET */

#include <event.h>	/* EV_READ EV_WRITE EV_TIMEOUT event_set(3)
			 * event_base_set(3) event_add(3) */

#include <arena/proto.h>

#include "tls.h"
#include "bufio.h"
#include "pagebuf.h"
#include "socket.h"

#include <stdio.h>
#define MARK (fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__))


/*
 * Extension of timercmp() which treats NULL pointers as "unset" timevals.
 */
static const struct timeval tv_zero;

#define timereq(a, b)	\
	timercmp(((a)? (a) : &tv_zero), ((b)? (b) : &tv_zero), ==)


/*
 * Stack frame handling code for optimized event management. We postpone
 * deleting an event so we can reuse it if an immediate poll is requested. 
 * And we need to keep state--about live call frames--in case our object(s)
 * become invalid during a callback.
 */
#define BUFIO_SOCKET_FRAME_PUSH(s, m, f) do {				\
	(f)->sp	= &(s);							\
	SLIST_INSERT_HEAD(&(s)->m.poll.stack, (f), sle);		\
} while(0)

#define BUFIO_SOCKET_FRAME_POP(s, m, f) do {				\
	if ((s) != 0) {							\
		assert((f) == SLIST_FIRST(&(s)->m.poll.stack));		\
		SLIST_REMOVE_HEAD(&(s)->m.poll.stack, sle);		\
	}								\
} while(0)

#define BUFIO_SOCKET_FRAME_OKAY(f)	(*(f)->sp != 0)


const struct bufio_socket bufio_socket_initializer = {
	.fd	= INVALID_SOCKET,
}; /* bufio_socket_initializer */


const struct bufio_socket_options bufio_socket_defaults = {
	.page_size	= 4096,
}; /* bufio_socket_defaults */


static void bufio_socket_source_poll_handler(int, short, void *);
static void bufio_socket_sink_poll_handler(int, short, void *);


static enum bufio_errno bufio_socket_tls_errno_tr(struct bufio_socket *s) {
	switch (tls_errno(s->tls)) {
	case TLS_ESUCCESS:
		return 0;
	case TLS_ESYSTEM:
		return BUFIO_ESYSTEM;
	case TLS_ETIMEDOUT:
		return BUFIO_ETIMEDOUT;
	case TLS_ECANCELLED:
		return BUFIO_ECANCELLED;
	default:
		SetLastError(EFAULT);

		return BUFIO_ESYSTEM;
	}

	/* NOT REACHED */	
} /* bufio_socket_tls_errno_tr() */


static size_t bufio_socket_read(struct bufio_socket *s, void *buf, size_t bufsiz, enum bufio_errno *e) {
	ssize_t n;

	if (s->tls) {
		if (-1 == (n = tls_read(s->tls, buf, bufsiz, 0, 0, 0))) {
			*e	= s->source.lasterr = bufio_socket_tls_errno_tr(s);
			n	= 0;

#ifdef WSAEWOULDBLOCK
			if (GetLastError() == WSAEWOULDBLOCK)
				SetLastError(EAGAIN);
#endif
		} else if (n == 0)
			*e	= s->source.lasterr = BUFIO_EEOF;
	} else {
		if (s->fd == BUFIO_SOCKET_FD_WAIT) {
			*e	= s->source.lasterr = BUFIO_ESYSTEM;

			SetLastError(EAGAIN);

			n	= 0;
		} else if (-1 == (n = recv(s->fd, buf, bufsiz, 0))) {
			*e	= s->source.lasterr = BUFIO_ESYSTEM;
			n	= 0;

#ifdef WSAEWOULDBLOCK
			if (GetLastError() == WSAEWOULDBLOCK)
				SetLastError(EAGAIN);
#endif
		} else if (n == 0)
			*e	= s->source.lasterr = BUFIO_EEOF;
	}

	if (n == 0 && *e == BUFIO_ESYSTEM && GetLastError() == EAGAIN)
		*e	= BUFIO_EAGAIN;

	return n;
} /* bufio_socket_read() */


static size_t bufio_socket_write(struct bufio_socket *s, void *buf, size_t bufsiz, enum bufio_errno *e) {
	ssize_t n;

	if (bufsiz == 0)
		return 0;

	if (s->tls) {
		if (-1 == (n = tls_write(s->tls, buf, bufsiz, 0, 0, 0))) {
			*e	= s->sink.lasterr = bufio_socket_tls_errno_tr(s);
			n	= 0;

#ifdef WSAEWOULDBLOCK
			if (GetLastError() == WSAEWOULDBLOCK)
				SetLastError(EAGAIN);
#endif
		} else if (n == 0) {
			*e	= s->sink.lasterr = BUFIO_ESYSTEM;

			SetLastError(EPIPE);
		}
	} else {
		if (s->fd == BUFIO_SOCKET_FD_WAIT) {
			*e	= s->sink.lasterr = BUFIO_ESYSTEM;

			SetLastError(EAGAIN);

			n	= 0;
		} else if (-1 == (n = send(s->fd, buf, bufsiz, 0))) {
			*e	= s->sink.lasterr = BUFIO_ESYSTEM;
			n	= 0;

#ifdef WSAEWOULDBLOCK
			if (GetLastError() == WSAEWOULDBLOCK)
				SetLastError(EAGAIN);
#endif
		} else if (n == 0) {
			*e	= s->sink.lasterr = BUFIO_ESYSTEM;

			SetLastError(EPIPE);
		}
	}

	if (n == 0 && *e == BUFIO_ESYSTEM) {
		if (GetLastError() == EAGAIN)
			*e	= BUFIO_EAGAIN;
		else if (GetLastError() == EPIPE)
			*e	= BUFIO_EPIPE;
	}

	return n;
} /* bufio_socket_write() */


static size_t bufio_socket_copyout(struct bufio_source *src, void *buf, size_t bufsiz, int flags, enum bufio_errno *e) {
	struct bufio_socket *s	= BUFIO_CAST_FROM(src, bufio_socket, bufio_source);

	if (flags & BUFIO_PEEK) {
		*e	= s->source.lasterr = BUFIO_ESYSTEM;

		SetLastError(ENOTSUP);

		return 0;
	}

	return bufio_socket_read(s, buf, bufsiz, e);
} /* bufio_socket_copyout() */


static size_t bufio_socket_copyto(struct bufio_source *src, struct bufio_sink *snk, int flags, enum bufio_errno *e) {
	struct bufio_socket *s	= BUFIO_CAST_FROM(src, bufio_socket, bufio_source);

	return snk->copyfrom(snk, src, flags, e);
} /* bufio_socket_copyto() */


static size_t bufio_socket_copyin(struct bufio_sink *snk, void *buf, size_t bufsiz, int flags, enum bufio_errno *e) {
	struct bufio_socket *s	= BUFIO_CAST_FROM(snk, bufio_socket, bufio_sink);

	return bufio_socket_write(s, buf, bufsiz, e);
} /* bufio_socket_copyin() */


static size_t bufio_socket_copyfrom(struct bufio_sink *snk, struct bufio_source *src, int flags, enum bufio_errno *e) {
	struct bufio_socket *s	= BUFIO_CAST_FROM(snk, bufio_socket, bufio_sink);

	return src->copyto(src, snk, flags, e);
} /* bufio_socket_copyfrom() */


static short bufio_socket_event_tr(struct bufio_socket *s, short want) {
	if (!s->tls)
		return want;

	switch ((TLS_S_NEED_READ | TLS_S_NEED_WRITE) & tls_state(s->tls)) {
	case TLS_S_NEED_READ:
		return EV_READ;
	case TLS_S_NEED_WRITE:
		return EV_WRITE;
	case TLS_S_NEED_READ|TLS_S_NEED_WRITE:
		return EV_READ|EV_WRITE;
	default:
		return want;
	}

	/* NOT REACHED */
} /* bufio_socket_event_tr() */


static void bufio_socket_event_timer_wake(int fd, short events, void *arg) {
	struct bufio_socket_event *e	= arg;
	struct bufio_socket *s;

	timerclear(&e->timer.timeout);	/* Timers are always one-shot. */

	if (e->notify && e->want && (s = e->timer.ev_so)) {
		e->timer.ev_so	= 0;

		e->notify(s->fd, EV_TIMEOUT, s);
	} /* else log some information about this odd condition. */

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


static void bufio_socket_event_timer_stop(struct bufio_socket *s, struct bufio_socket_event *e) {
	if (!timerisset(&e->timer.timeout))
		return /* void */;

	evtimer_del(&e->timer.ev_op);

	e->timer.ev_so	= 0;

	timerclear(&e->timer.timeout);

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


static void bufio_socket_event_timer_start(struct bufio_socket *s, struct bufio_socket_event *e, struct timeval *tv) {
	if (timerisset(&e->timer.timeout))
		bufio_socket_event_timer_stop(s, e);

	if (tv == 0 || !timerisset(tv))
		return /* void */;

	evtimer_set(&e->timer.ev_op, &bufio_socket_event_timer_wake, e);

	if (s->ev_base)
		event_base_set(s->ev_base, &e->timer.ev_op);

	evtimer_add(&e->timer.ev_op, tv);

	e->timer.ev_so		= s;
	e->timer.timeout	= *tv;

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


static void bufio_socket_event_run(int fd, short events, void *arg) {
	struct bufio_socket *s	= arg;
	/*
	 * We fire the sink event right off the bat. Since the source event
	 * comes second, we need to make sure the state hasn't changed so
	 * that we can mimic as best as possible the behavior the
	 * application would see if libevent was directly firing the second
	 * event. Checking the number of calls to the source poller
	 * guarantees that if there's an intervening cancel and then a
	 * subsequent poll (all from the sink callback), the event won't
	 * fire til another roundtrip through the event loop.
	 */
	unsigned int ncalls	= s->source.poll.ncalls;
	struct bufio_socket_frame f;

	BUFIO_SOCKET_FRAME_PUSH(s, source, &f);

	if (s->sink.event.want & events)
		s->sink.event.notify(fd, events, s);

	if (!BUFIO_SOCKET_FRAME_OKAY(&f))
		return /* void */;

	if (ncalls == s->source.poll.ncalls && (s->source.event.want & events))
		s->source.event.notify(fd, events, s);

	BUFIO_SOCKET_FRAME_POP(s, source, &f);

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


static enum bufio_errno bufio_socket_event_mod(struct bufio_socket *s) {
	short want;

	if (s->ev_pending == (want = (s->source.event.want | s->sink.event.want)))
		return 0;

	if (s->ev_pending)
		(void)event_del(&s->ev_op), s->ev_pending = 0;

	/*
	 * If we have shim descriptor, just fake the event enqueue. When
	 * bufio_socket_set_fd() gives a valid descriptor it will manually
	 * re-issue this routine.
	 */
	if (want == 0 || s->fd == BUFIO_SOCKET_FD_WAIT)
		return 0;

	event_set(&s->ev_op, s->fd, want | EV_PERSIST, &bufio_socket_event_run, s);

	if (s->ev_base)
		event_base_set(s->ev_base, &s->ev_op);

	if (0 != event_add(&s->ev_op, 0))
		return BUFIO_ESYSTEM;

	s->ev_pending	= want;

	return 0;
} /* bufio_socket_event_mod() */


static enum bufio_errno bufio_socket_event_del(struct bufio_socket *s, struct bufio_socket_event *e) {
	e->want		= 0;
	e->notify	= 0;

	bufio_socket_event_timer_stop(s, e);

	return bufio_socket_event_mod(s);
} /* bufio_socket_event_del() */


static enum bufio_errno bufio_socket_event_add(struct bufio_socket *s, struct bufio_socket_event *e, short events, void (*fn)(int, short, void *), struct timeval *tv) {
	e->want		= events;
	e->notify	= fn;

	bufio_socket_event_timer_start(s, e, tv); /* Begin the stopwatch. */

	return bufio_socket_event_mod(s);
} /* bufio_socket_event_add() */


static void bufio_socket_source_poll_handler(int fd, short events, void *arg) {
	struct bufio_socket *s		= arg;
	unsigned int ncalls		= s->source.poll.ncalls;
	bufio_source_poll_cb fn;
	enum bufio_errno retval;
	struct bufio_socket_frame f;

	BUFIO_SOCKET_FRAME_PUSH(s, source, &f);

	s->source.event.want	= 0;

	bufio_socket_event_timer_stop(s, &s->source.event);

	fn		= s->source.cb.fn;
	arg		= s->source.cb.arg;
	retval		= s->source.cb.retval;

	s->source.cb.fn		= 0;
	s->source.cb.arg	= 0;
	s->source.cb.retval	= 0;

	if (events & EV_TIMEOUT)
		retval	= s->source.lasterr = BUFIO_ETIMEDOUT;

	fn(BUFIO_CAST_TO(s, bufio_socket, bufio_source), retval, arg);

	if (!BUFIO_SOCKET_FRAME_OKAY(&f))
		return /* void */;

	if (s->source.poll.ncalls == ncalls)
		(void)bufio_socket_event_del(s, &s->source.event);

	BUFIO_SOCKET_FRAME_POP(s, source, &f);

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


static enum bufio_errno bufio_socket_source_poll(struct bufio_source *src, bufio_source_poll_cb cb, void *arg, struct timeval *timeout) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(src, bufio_socket, bufio_source);
	short want			= bufio_socket_event_tr(s, EV_READ);
	enum bufio_errno our_errno;

	++s->source.poll.ncalls;

	s->source.cb.fn		= cb;
	s->source.cb.arg	= arg;
	s->source.cb.retval	= 0;

	if (0 != (our_errno = bufio_socket_event_add(s, &s->source.event, want, &bufio_socket_source_poll_handler, timeout)))
		return s->source.lasterr = our_errno;

	return 0;	
} /* bufio_socket_source_poll() */


static enum bufio_errno bufio_socket_source_cancel(struct bufio_source *src, int notify) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(src, bufio_socket, bufio_source);
	bufio_source_poll_cb fn;
	void *arg;

	(void)bufio_socket_event_del(s, &s->source.event);

	if (!s->source.cb.fn)
		return BUFIO_ENOTPOLLING;

	fn	= s->source.cb.fn;
	arg	= s->source.cb.arg;

	s->source.cb.fn		= 0;
	s->source.cb.arg	= 0;
	s->source.cb.retval	= 0;

	if (notify)
		fn(BUFIO_CAST_TO(s, bufio_socket, bufio_source), BUFIO_ECANCELLED, arg);

	return 0;
} /* bufio_socket_source_cancel() */


static enum bufio_errno bufio_socket_source_lasterr(struct bufio_source *src) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(src, bufio_socket, bufio_source);

	return s->source.lasterr;
} /* bufio_socket_source_lasterr() */


static void bufio_socket_source_clearerr(struct bufio_source *src) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(src, bufio_socket, bufio_source);

	s->source.lasterr	= 0;

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


static void bufio_socket_sink_poll_handler(int fd, short events, void *arg) {
	struct bufio_socket *s		= arg;
	unsigned int ncalls		= s->sink.poll.ncalls;
	bufio_sink_poll_cb fn;
	enum bufio_errno retval;
	struct bufio_socket_frame f;

	BUFIO_SOCKET_FRAME_PUSH(s, sink, &f);

	s->sink.event.want	= 0;

	bufio_socket_event_timer_stop(s, &s->sink.event);

	fn		= s->sink.cb.fn;
	arg		= s->sink.cb.arg;
	retval		= s->sink.cb.retval;

	s->sink.cb.fn		= 0;
	s->sink.cb.arg		= 0;
	s->sink.cb.retval	= 0;

	if (events & EV_TIMEOUT)
		retval	= s->sink.lasterr = BUFIO_ETIMEDOUT;

	fn(BUFIO_CAST_TO(s, bufio_socket, bufio_sink), retval, arg);

	if (!BUFIO_SOCKET_FRAME_OKAY(&f))
		return /* void */;

	if (s->sink.poll.ncalls == ncalls)
		(void)bufio_socket_event_del(s, &s->sink.event);

	BUFIO_SOCKET_FRAME_POP(s, sink, &f);

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


static enum bufio_errno bufio_socket_sink_poll(struct bufio_sink *src, bufio_sink_poll_cb cb, void *arg, struct timeval *timeout) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(src, bufio_socket, bufio_sink);
	short want			= bufio_socket_event_tr(s, EV_WRITE);
	enum bufio_errno our_errno;

	++s->sink.poll.ncalls;

	s->sink.cb.fn		= cb;
	s->sink.cb.arg		= arg;
	s->sink.cb.retval	= 0;

	if (0 != (our_errno = bufio_socket_event_add(s, &s->sink.event, want, &bufio_socket_sink_poll_handler, timeout)))
		return s->sink.lasterr = our_errno;

	return 0;	
} /* bufio_socket_sink_poll() */


static enum bufio_errno bufio_socket_sink_cancel(struct bufio_sink *snk, int notify) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(snk, bufio_socket, bufio_sink);
	bufio_sink_poll_cb fn;
	void *arg;

	(void)bufio_socket_event_del(s, &s->sink.event);

	if (!s->sink.cb.fn)
		return BUFIO_ENOTPOLLING;

	fn	= s->sink.cb.fn;
	arg	= s->sink.cb.arg;

	s->sink.cb.fn		= 0;
	s->sink.cb.arg		= 0;
	s->sink.cb.retval	= 0;

	if (notify)
		fn(BUFIO_CAST_TO(s, bufio_socket, bufio_sink), BUFIO_ECANCELLED, arg);

	return 0;
} /* bufio_socket_sink_cancel() */


static enum bufio_errno bufio_socket_sink_lasterr(struct bufio_sink *snk) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(snk, bufio_socket, bufio_sink);

	return s->sink.lasterr;
} /* bufio_socket_sink_lasterr() */

static void bufio_socket_sink_clearerr(struct bufio_sink *snk) {
	struct bufio_socket *s		= BUFIO_CAST_FROM(snk, bufio_socket, bufio_sink);

	s->sink.lasterr	= 0;

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


struct bufio_socket *bufio_socket_init(struct bufio_socket *s, int fd, const struct bufio_socket_options *opts, struct event_base *evb, const struct arena_prototype *ap, enum bufio_errno *e) {
	static const struct bufio_socket bufio_socket_initializer;
	struct bufio_source *src;
	struct bufio_sink *snk;

	*s		= bufio_socket_initializer;
	s->fd		= fd;
	s->opts		= *((opts != 0)? opts : &bufio_socket_defaults);
	s->ev_base	= evb;
	s->ap		= ap;

	src		= BUFIO_CAST_TO(s, bufio_socket, bufio_source);
	*src		= BUFIO_SOURCE_INITIALIZER;
	src->copyto	= &bufio_socket_copyto;
	src->copyout	= &bufio_socket_copyout;
	src->poll	= &bufio_socket_source_poll;
	src->cancel	= &bufio_socket_source_cancel;
	src->lasterr	= &bufio_socket_source_lasterr;
	src->clearerr	= &bufio_socket_source_clearerr;

	snk		= BUFIO_CAST_TO(s, bufio_socket, bufio_sink);
	*snk		= BUFIO_SINK_INITIALIZER;
	snk->copyfrom	= &bufio_socket_copyfrom;
	snk->copyin	= &bufio_socket_copyin;
	snk->poll	= &bufio_socket_sink_poll;
	snk->cancel	= &bufio_socket_sink_cancel;
	snk->lasterr	= &bufio_socket_sink_lasterr;
	snk->clearerr	= &bufio_socket_sink_clearerr;

	SLIST_INIT(&s->source.poll.stack);
	SLIST_INIT(&s->sink.poll.stack);

	return s;
} /* bufio_socket_init() */


void bufio_socket_destroy(struct bufio_socket *s) {
	struct bufio_socket_frame *fp;

	if (!s)
		return /* void */;

	SLIST_FOREACH(fp, &s->source.poll.stack, sle)
		*fp->sp	= 0;

	SLIST_FOREACH(fp, &s->sink.poll.stack, sle)
		*fp->sp	= 0;

	SLIST_INIT(&s->source.poll.stack);
	SLIST_INIT(&s->sink.poll.stack);

	s->source.event.want	= 0;
	s->sink.event.want	= 0;

	(void)bufio_socket_event_del(s, &s->source.event);
	(void)bufio_socket_event_del(s, &s->sink.event);

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


struct bufio_socket *bufio_socket_open(int fd, const struct bufio_socket_options *opts, struct event_base *evb, const struct arena_prototype *ap, enum bufio_errno *e) {
	struct bufio_socket *s	= 0;
	int sys_errno;

	if (!(s = ap->malloc(ap, sizeof *s, 0)))
		goto sysfail;

	return bufio_socket_init(s, fd, opts, evb, ap, e);
sysfail:
	*e	= BUFIO_ESYSTEM;

	sys_errno	= GetLastError();

	bufio_socket_destroy(s);

	SetLastError(sys_errno);

	return 0;
} /* bufio_socket_open() */


void bufio_socket_close(struct bufio_socket *s) {
	if (s == 0)
		return /* void */;

	bufio_socket_destroy(s);

	s->ap->free(s->ap, s);

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


void bufio_socket_switch_tls(struct bufio_socket *s, struct tls *tls) {
	s->tls	= tls;

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


void bufio_socket_set_fd(struct bufio_socket *s, int fd) {
	/*
	 * Must remove any libevent handler so we don't get confused over
	 * which descriptor is actually registered for polling.
	 */
	if (s->ev_pending)
		(void)event_del(&s->ev_op), s->ev_pending = 0;

	s->fd	= fd;

	(void)bufio_socket_event_mod(s);

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


struct bufio_source *bufio_socket_to_source(struct bufio_socket *s) {
	return BUFIO_CAST_TO(s, bufio_socket, bufio_source);
} /* bufio_socket_to_source() */


struct bufio_sink *bufio_socket_to_sink(struct bufio_socket *s) {
	return BUFIO_CAST_TO(s, bufio_socket, bufio_sink);
} /* bufio_socket_to_sink() */



syntax highlighted by Code2HTML, v. 0.9.1