/* ==========================================================================
 * libevnet/src/bufio-membuf.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 <errno.h>		/* ENOBUFS */

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

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

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

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

#include <arena/proto.h>	/* struct arena_prototype */

#include "bufio.h"
#include "pagebuf.h"
#include "membuf.h"


const struct bufio_membuf_options bufio_membuf_oneshot = {
#ifndef WIN32
	.oneshot	= 1,
#else
	1,
#endif
}; /* bufio_membuf_oneshot */


const struct bufio_membuf_options bufio_membuf_defaults;


static size_t bufio_membuf_copyfrom(struct bufio_sink *snk, struct bufio_source *src, int flags, enum bufio_errno *e) {
	struct bufio_membuf *m	= BUFIO_CAST_FROM(snk, bufio_membuf, bufio_sink);
	size_t n, nbytes	= 0;

	if (0 == bufio_page_nfree(&m->page)) {
		*e	= (m->opts.oneshot)? BUFIO_EPIPE : BUFIO_EAGAIN;

		return 0;
	}

	*e	= 0;

	do {
		n	= src->copyout(src, m->page.cursor.put, bufio_page_nfree(&m->page), flags, e);

		m->page.cursor.put	+= n;
		nbytes			+= n;
	} while (n > 0 && 0 < bufio_page_nfree(&m->page));

	return nbytes;
} /* bufio_membuf_copyfrom() */


static size_t bufio_membuf_copyin(struct bufio_sink *snk, void *srcbuf, size_t bufsiz, int flags, enum bufio_errno *e) {
	struct bufio_membuf *m	= BUFIO_CAST_FROM(snk, bufio_membuf, bufio_sink);
	size_t nbytes;

	if (0 == bufio_page_nfree(&m->page))
		goto wouldblock;

	*e	= 0;

	nbytes	= MIN(bufio_page_nfree(&m->page), bufsiz);

	if ((flags & BUFIO_NOFRAG) && nbytes != bufsiz)
		goto wouldblock;	

	(void)memcpy(m->page.cursor.put, srcbuf, nbytes);
	m->page.cursor.put	+= nbytes;

	return nbytes;
wouldblock:
	*e	= (m->opts.oneshot)? BUFIO_EPIPE : BUFIO_EAGAIN;

	return 0;
} /* bufio_membuf_copyin() */


static size_t bufio_membuf_sink_buffered(struct bufio_sink *snk) {
	return bufio_page_len(&BUFIO_CAST_FROM(snk, bufio_membuf, bufio_sink)->page);
} /* bufio_membuf_sink_buffered() */


static size_t bufio_membuf_copyto(struct bufio_source *src, struct bufio_sink *snk, int flags, enum bufio_errno *e) {
	struct bufio_membuf *m	= BUFIO_CAST_FROM(src, bufio_membuf, bufio_source);
	size_t n, nbytes		= 0;

	*e	= 0;

	if (0 == bufio_page_len(&m->page)) {
		*e	= (m->opts.oneshot)? BUFIO_EEOF : BUFIO_EAGAIN;

		return 0;
	}

	do {
		n	= snk->copyin(snk, m->page.cursor.get, bufio_page_len(&m->page), flags, e);

		m->page.cursor.get	+= n;
		nbytes			+= n;
	} while (n > 0 && 0 < bufio_page_len(&m->page));

	return nbytes;
} /* bufio_membuf_copyto() */


static size_t bufio_membuf_copyout(struct bufio_source *src, void *buf, size_t bufsiz, int flags, enum bufio_errno *e) {
	struct bufio_membuf *m	= BUFIO_CAST_FROM(src, bufio_membuf, bufio_source);
	size_t nbytes;

	*e	= 0;

	if (0 == bufio_page_len(&m->page))
		goto wouldblock;

	nbytes	= MIN(bufio_page_len(&m->page), bufsiz);

	if ((flags && BUFIO_NOFRAG) && nbytes != bufio_page_len(&m->page))
		goto wouldblock;

	(void)memcpy(buf, m->page.cursor.get, nbytes);

	if (!(flags & BUFIO_PEEK))
		m->page.cursor.get	+= nbytes;

	return nbytes;
wouldblock:
	*e	= (m->opts.oneshot)? BUFIO_EEOF : BUFIO_EAGAIN;

	return 0;
} /* bufio_membuf_copyout() */


static size_t bufio_membuf_source_buffered(struct bufio_source *src) {
	return bufio_page_len(&BUFIO_CAST_FROM(src, bufio_membuf, bufio_source)->page);
} /* bufio_membuf_source_buffered() */


struct bufio_membuf *bufio_membuf_init(struct bufio_membuf *m, const struct bufio_membuf_options *opts) {
	static const struct bufio_membuf bufio_membuf_initializer;
	struct bufio_source *src;
	struct bufio_sink *snk;

	*m	= bufio_membuf_initializer;
	m->opts	= *((opts)? opts : &bufio_membuf_defaults);

	src		= BUFIO_CAST_TO(m, bufio_membuf, bufio_source);
	*src		= BUFIO_SOURCE_INITIALIZER;
	src->copyto	= &bufio_membuf_copyto;
	src->copyout	= &bufio_membuf_copyout;
	src->buffered	= &bufio_membuf_source_buffered;

	snk		= BUFIO_CAST_TO(m, bufio_membuf, bufio_sink);
	*snk		= BUFIO_SINK_INITIALIZER;
	snk->copyfrom	= &bufio_membuf_copyfrom;
	snk->copyin	= &bufio_membuf_copyin;
	snk->buffered	= &bufio_membuf_sink_buffered;

	return m;
} /* bufio_membuf_options() */


struct bufio_source *bufio_membuf_init_source(struct bufio_membuf *m, const struct bufio_membuf_options *opts, const void *buf, size_t bufsiz) {
	bufio_membuf_init(m, opts);

	m->page.base	= m->page.cursor.get
			= (unsigned char *)buf;

	m->page.cursor.end	= m->page.cursor.put
				= m->page.cursor.get + bufsiz;

	return bufio_membuf_to_source(m);
} /* bufio_membuf_init_source() */


struct bufio_sink *bufio_membuf_init_sink(struct bufio_membuf *m, const struct bufio_membuf_options *opts, void *buf, size_t bufsiz) {
	bufio_membuf_init(m, opts);

	m->page.base	= m->page.cursor.get
			= m->page.cursor.put
			= (unsigned char *)buf;

	m->page.cursor.end	= m->page.cursor.get + bufsiz;

	return bufio_membuf_to_sink(m);
} /* bufio_membuf_init_sink() */


struct bufio_source *bufio_membuf_to_source(struct bufio_membuf *m) {
	return BUFIO_CAST_TO(m, bufio_membuf, bufio_source);
} /* bufio_membuf_to_source() */


struct bufio_sink *bufio_membuf_to_sink(struct bufio_membuf *m) {
	return BUFIO_CAST_TO(m, bufio_membuf, bufio_sink);
} /* bufio_membuf_to_sink() */



syntax highlighted by Code2HTML, v. 0.9.1