/* This file is part of GNU Pies.
   Copyright (C) 2019-2022 Sergey Poznyakoff

   GNU Pies is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GNU Pies is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Pies.  If not, see <http://www.gnu.org/licenses/>. */

#define IO_BUF_SIZE 512

struct iobuf
{
  char buf[IO_BUF_SIZE];    /* Initial line buffer */
  size_t start;             /* Start of data */
  size_t avail;             /* Start of available space */
};

static inline void
iobuf_reset (struct iobuf *bp)
{
  bp->start = bp->avail = 0;
}

/* Return the count of data bytes in the buffer */
static inline size_t
iobuf_data_size (struct iobuf *bp)
{
  return bp->avail - bp->start;
}

static inline char *
iobuf_data_ptr (struct iobuf *bp)
{
  return bp->buf + bp->start;
}

static inline void
iobuf_data_advance (struct iobuf *bp, size_t n)
{
  bp->start += n;
  if (bp->start == bp->avail)
    iobuf_reset (bp);
}

static inline int
iobuf_putback (struct iobuf *bp)
{
  return bp->buf[--bp->start-1];
}

static inline int
iobuf_getc (struct iobuf *bp)
{
  int c = -1;
  if (iobuf_data_size (bp) > 0)
    {
      c = *iobuf_data_ptr (bp);
      iobuf_data_advance(bp, 1);
    }
  return c;
}

/* Return the count of available bytes in the buffer */
static inline size_t
iobuf_avail_size (struct iobuf *bp)
{
  return sizeof (bp->buf) - bp->avail;
}

static inline char *
iobuf_avail_ptr (struct iobuf *bp)
{
  return bp->buf + bp->avail;
}

static inline void
iobuf_avail_advance (struct iobuf *bp, size_t n)
{
  bp->avail += n;
  if (iobuf_avail_size (bp) == 0 && iobuf_data_size (bp) == 0)
    iobuf_reset (bp);
}

static inline int
iobuf_putc (struct iobuf *bp, int c)
{
  if (iobuf_avail_size (bp) > 0)
    {
      *iobuf_avail_ptr (bp) = c;
      iobuf_avail_advance (bp, 1);
      return c;
    }
  return -1;
}

static inline ssize_t
iobuf_fill (struct iobuf *bp, int fd)
{
  ssize_t n = read (fd, iobuf_avail_ptr (bp), iobuf_avail_size (bp));
  if (n >= 0)
    iobuf_avail_advance (bp, n);
  return n;
}

static inline ssize_t
iobuf_flush (struct iobuf *bp, int fd)
{
  ssize_t n = write (fd, iobuf_data_ptr (bp), iobuf_data_size (bp));
  if (n >= 0)
    iobuf_data_advance (bp, n);
  return n;
}

static inline ssize_t
iobuf_copy (struct iobuf *dst, struct iobuf *src)
{
  ssize_t n = iobuf_data_size (src);
  if (n > 0)
    {
      ssize_t avail = iobuf_avail_size (dst);
      if (avail < n)
	n = avail;
      if (n)
	{
	  memcpy (iobuf_avail_ptr (dst), iobuf_data_ptr (src), n);
	  iobuf_avail_advance (dst, n);
	  iobuf_data_advance (src, n);
	}
    }
  return n;
}


