text: /*
   * mbuf.c
   *		Memory buffer operations.
   *
   * Copyright (c) 2005 Marko Kreen
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *	  notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *	  notice, this list of conditions and the following disclaimer in the
   *	  documentation and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   *
   * contrib/pgcrypto/mbuf.c
   */
  
  #include "postgres.h"
  
  #include "px.h"
  #include "mbuf.h"
  
  #define STEP  (16*1024)
  
  struct MBuf
  {
  	uint8	   *data;
  	uint8	   *data_end;
  	uint8	   *read_pos;
  	uint8	   *buf_end;
  	bool		no_write;
  	bool		own_data;
  };
  
  int
  mbuf_avail(MBuf *mbuf)
  {
  	return mbuf->data_end - mbuf->read_pos;
  }
  
  int
  mbuf_size(MBuf *mbuf)
  {
  	return mbuf->data_end - mbuf->data;
  }
  
  int
  mbuf_tell(MBuf *mbuf)
  {
  	return mbuf->read_pos - mbuf->data;
  }
  
  int
  mbuf_free(MBuf *mbuf)
  {
  	if (mbuf->own_data)
  	{
  		px_memset(mbuf->data, 0, mbuf->buf_end - mbuf->data);
  		px_free(mbuf->data);
  	}
  	px_free(mbuf);
  	return 0;
  }
  
  static void
  prepare_room(MBuf *mbuf, int block_len)
  {
  	uint8	   *newbuf;
  	unsigned	newlen;
  
  	if (mbuf->data_end + block_len <= mbuf->buf_end)
  		return;
  
  	newlen = (mbuf->buf_end - mbuf->data)
  		+ ((block_len + STEP + STEP - 1) & -STEP);
  
  	newbuf = px_realloc(mbuf->data, newlen);
  
  	mbuf->buf_end = newbuf + newlen;
  	mbuf->data_end = newbuf + (mbuf->data_end - mbuf->data);
  	mbuf->read_pos = newbuf + (mbuf->read_pos - mbuf->data);
  	mbuf->data = newbuf;
  
  	return;
  }
  
  int
  mbuf_append(MBuf *dst, const uint8 *buf, int len)
  {
  	if (dst->no_write)
  	{
  		px_debug("mbuf_append: no_write");
  		return PXE_BUG;
  	}
  
  	prepare_room(dst, len);
  
  	memcpy(dst->data_end, buf, len);
  	dst->data_end += len;
  
  	return 0;
  }
  
  MBuf *
  mbuf_create(int len)
  {
  	MBuf	   *mbuf;
  
  	if (!len)
  		len = 8192;
  
  	mbuf = px_alloc(sizeof *mbuf);
  	mbuf->data = px_alloc(len);
  	mbuf->buf_end = mbuf->data + len;
  	mbuf->data_end = mbuf->data;
  	mbuf->read_pos = mbuf->data;
  
  	mbuf->no_write = false;
  	mbuf->own_data = true;
  
  	return mbuf;
  }
  
  MBuf *
  mbuf_create_from_data(uint8 *data, int len)
  {
  	MBuf	   *mbuf;
  
  	mbuf = px_alloc(sizeof *mbuf);
  	mbuf->data = (uint8 *) data;
  	mbuf->buf_end = mbuf->data + len;
  	mbuf->data_end = mbuf->data + len;
  	mbuf->read_pos = mbuf->data;
  
  	mbuf->no_write = true;
  	mbuf->own_data = false;
  
  	return mbuf;
  }
  
  
  int
  mbuf_grab(MBuf *mbuf, int len, uint8 **data_p)
  {
  	if (len > mbuf_avail(mbuf))
  		len = mbuf_avail(mbuf);
  
  	mbuf->no_write = true;
  
  	*data_p = mbuf->read_pos;
  	mbuf->read_pos += len;
  	return len;
  }
  
  int
  mbuf_rewind(MBuf *mbuf)
  {
  	mbuf->read_pos = mbuf->data;
  	return 0;
  }
  
  int
  mbuf_steal_data(MBuf *mbuf, uint8 **data_p)
  {
  	int			len = mbuf_size(mbuf);
  
  	mbuf->no_write = true;
  	mbuf->own_data = false;
  
  	*data_p = mbuf->data;
  
  	mbuf->data = mbuf->data_end = mbuf->read_pos = mbuf->buf_end = NULL;
  
  	return len;
  }
  
  /*
   * PullFilter
   */
  
  struct PullFilter
  {
  	PullFilter *src;
  	const PullFilterOps *op;
  	int			buflen;
  	uint8	   *buf;
  	int			pos;
  	void	   *priv;
  };
  
  int
  pullf_create(PullFilter **pf_p, const PullFilterOps *op, void *init_arg, PullFilter *src)
  {
  	PullFilter *pf;
  	void	   *priv;
  	int			res;
  
  	if (op->init != NULL)
  	{
  		res = op->init(&priv, init_arg, src);
  		if (res < 0)
  			return res;
  	}
  	else
  	{
  		priv = init_arg;
  		res = 0;
  	}
  
  	pf = px_alloc(sizeof(*pf));
  	memset(pf, 0, sizeof(*pf));
  	pf->buflen = res;
  	pf->op = op;
  	pf->priv = priv;
  	pf->src = src;
  	if (pf->buflen > 0)
  	{
  		pf->buf = px_alloc(pf->buflen);
  		pf->pos = 0;
  	}
  	else
  	{
  		pf->buf = NULL;
  		pf->pos = 0;
  	}
  	*pf_p = pf;
  	return 0;
  }
  
  void
  pullf_free(PullFilter *pf)
  {
  	if (pf->op->free)
  		pf->op->free(pf->priv);
  
  	if (pf->buf)
  	{
  		px_memset(pf->buf, 0, pf->buflen);
  		px_free(pf->buf);
  	}
  
  	px_memset(pf, 0, sizeof(*pf));
  	px_free(pf);
  }
  
  /* may return less data than asked, 0 means eof */
  int
  pullf_read(PullFilter *pf, int len, uint8 **data_p)
  {
  	int			res;
  
  	if (pf->op->pull)
  	{
  		if (pf->buflen && len > pf->buflen)
  			len = pf->buflen;
  		res = pf->op->pull(pf->priv, pf->src, len, data_p,
  						   pf->buf, pf->buflen);
  	}
  	else
  		res = pullf_read(pf->src, len, data_p);
  	return res;
  }
  
  int
  pullf_read_max(PullFilter *pf, int len, uint8 **data_p, uint8 *tmpbuf)
  {
  	int			res,
  				total;
  	uint8	   *tmp;
  
  	res = pullf_read(pf, len, data_p);
  	if (res <= 0 || res == len)
  		return res;
  
  	/* read was shorter, use tmpbuf */
  	memcpy(tmpbuf, *data_p, res);
  	*data_p = tmpbuf;
  	len -= res;
  	total = res;
  
  	while (len > 0)
  	{
  		res = pullf_read(pf, len, &tmp);
  		if (res < 0)
  		{
  			/* so the caller must clear only on success */
  			px_memset(tmpbuf, 0, total);
  			return res;
  		}
  		if (res == 0)
  			break;
  		memcpy(tmpbuf + total, tmp, res);
  		total += res;
  		len -= res;
  	}
  	return total;
  }
  
  /*
   * caller wants exatly len bytes and dont bother with references
   */
  int
  pullf_read_fixed(PullFilter *src, int len, uint8 *dst)
  {
  	int			res;
  	uint8	   *p;
  
  	res = pullf_read_max(src, len, &p, dst);
  	if (res < 0)
  		return res;
  	if (res != len)
  	{
  		px_debug("pullf_read_fixed: need=%d got=%d", len, res);
  		return PXE_PGP_CORRUPT_DATA;
  	}
  	if (p != dst)
  		memcpy(dst, p, len);
  	return 0;
  }
  
  /*
   * read from MBuf
   */
  static int
  pull_from_mbuf(void *arg, PullFilter *src, int len,
  			   uint8 **data_p, uint8 *buf, int buflen)
  {
  	MBuf	   *mbuf = arg;
  
  	return mbuf_grab(mbuf, len, data_p);
  }
  
  static const struct PullFilterOps mbuf_reader = {
  	NULL, pull_from_mbuf, NULL
  };
  
  int
  pullf_create_mbuf_reader(PullFilter **mp_p, MBuf *src)
  {
  	return pullf_create(mp_p, &mbuf_reader, src, NULL);
  }
  
  
  /*
   * PushFilter
   */
  
  struct PushFilter
  {
  	PushFilter *next;
  	const PushFilterOps *op;
  	int			block_size;
  	uint8	   *buf;
  	int			pos;
  	void	   *priv;
  };
  
  int
  pushf_create(PushFilter **mp_p, const PushFilterOps *op, void *init_arg, PushFilter *next)
  {
  	PushFilter *mp;
  	void	   *priv;
  	int			res;
  
  	if (op->init != NULL)
  	{
  		res = op->init(next, init_arg, &priv);
  		if (res < 0)
  			return res;
  	}
  	else
  	{
  		priv = init_arg;
  		res = 0;
  	}
  
  	mp = px_alloc(sizeof(*mp));
  	memset(mp, 0, sizeof(*mp));
  	mp->block_size = res;
  	mp->op = op;
  	mp->priv = priv;
  	mp->next = next;
  	if (mp->block_size > 0)
  	{
  		mp->buf = px_alloc(mp->block_size);
  		mp->pos = 0;
  	}
  	else
  	{
  		mp->buf = NULL;
  		mp->pos = 0;
  	}
  	*mp_p = mp;
  	return 0;
  }
  
  void
  pushf_free(PushFilter *mp)
  {
  	if (mp->op->free)
  		mp->op->free(mp->priv);
  
  	if (mp->buf)
  	{
  		px_memset(mp->buf, 0, mp->block_size);
  		px_free(mp->buf);
  	}
  
  	px_memset(mp, 0, sizeof(*mp));
  	px_free(mp);
  }
  
  void
  pushf_free_all(PushFilter *mp)
  {
  	PushFilter *tmp;
  
  	while (mp)
  	{
  		tmp = mp->next;
  		pushf_free(mp);
  		mp = tmp;
  	}
  }
  
  static int
  wrap_process(PushFilter *mp, const uint8 *data, int len)
  {
  	int			res;
  
  	if (mp->op->push != NULL)
  		res = mp->op->push(mp->next, mp->priv, data, len);
  	else
  		res = pushf_write(mp->next, data, len);
  	if (res > 0)
  		return PXE_BUG;
  	return res;
  }
  
  /* consumes all data, returns len on success */
  int
  pushf_write(PushFilter *mp, const uint8 *data, int len)
  {
  	int			need,
  				res;
  
  	/*
  	 * no buffering
  	 */
  	if (mp->block_size <= 0)
  		return wrap_process(mp, data, len);
  
  	/*
  	 * try to empty buffer
  	 */
  	need = mp->block_size - mp->pos;
  	if (need > 0)
  	{
  		if (len < need)
  		{
  			memcpy(mp->buf + mp->pos, data, len);
  			mp->pos += len;
  			return 0;
  		}
  		memcpy(mp->buf + mp->pos, data, need);
  		len -= need;
  		data += need;
  	}
  
  	/*
  	 * buffer full, process
  	 */
  	res = wrap_process(mp, mp->buf, mp->block_size);
  	if (res < 0)
  		return res;
  	mp->pos = 0;
  
  	/*
  	 * now process directly from data
  	 */
  	while (len > 0)
  	{
  		if (len > mp->block_size)
  		{
  			res = wrap_process(mp, data, mp->block_size);
  			if (res < 0)
  				return res;
  			data += mp->block_size;
  			len -= mp->block_size;
  		}
  		else
  		{
  			memcpy(mp->buf, data, len);
  			mp->pos += len;
  			break;
  		}
  	}
  	return 0;
  }
  
  int
  pushf_flush(PushFilter *mp)
  {
  	int			res;
  
  	while (mp)
  	{
  		if (mp->block_size > 0)
  		{
  			res = wrap_process(mp, mp->buf, mp->pos);
  			if (res < 0)
  				return res;
  		}
  
  		if (mp->op->flush)
  		{
  			res = mp->op->flush(mp->next, mp->priv);
  			if (res < 0)
  				return res;
  		}
  
  		mp = mp->next;
  	}
  	return 0;
  }
  
  
  /*
   * write to MBuf
   */
  static int
  push_into_mbuf(PushFilter *next, void *arg, const uint8 *data, int len)
  {
  	int			res = 0;
  	MBuf	   *mbuf = arg;
  
  	if (len > 0)
  		res = mbuf_append(mbuf, data, len);
  	return res < 0 ? res : 0;
  }
  
  static const struct PushFilterOps mbuf_filter = {
  	NULL, push_into_mbuf, NULL, NULL
  };
  
  int
  pushf_create_mbuf_writer(PushFilter **res, MBuf *dst)
  {
  	return pushf_create(res, &mbuf_filter, dst, NULL);
  }
  