/* CSL - Common Sound Layer
 * Copyright (C) 2000-2001 Stefan Westerfeld and Tim Janik
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef __ARTS_BIN_BUFFER_H__
#define __ARTS_BIN_BUFFER_H__


#include	"csldefs.h"
#include	<netinet/in.h>		/* ntohl(), htonl() */
#include	"cslutils.h"		/* csl_realloc() */


#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* --- structures --- */
typedef struct _ArtsBinBuffer ArtsBinBuffer;
struct _ArtsBinBuffer
{
  unsigned char   *bytes;
  unsigned char   *bound;
  union {
    unsigned char *cur;
    int		  *p_int32;
  } bp;
  int              error : 1;
  int		   need_free : 1;
};


/* --- prototypes --- */
ArtsBinBuffer*	_arts_bin_buffer_setup_readable (ArtsBinBuffer *buffer,
						unsigned int   n_bytes,
						void          *bytes);
ArtsBinBuffer*	_arts_bin_buffer_setup_writable (ArtsBinBuffer *buffer,
						unsigned int   prealloc);
void		_arts_bin_buffer_rewind		(ArtsBinBuffer *buffer);
void		_arts_bin_buffer_free_writable	(ArtsBinBuffer *buffer);
void		_arts_bin_buffer_put_hex	(ArtsBinBuffer *buffer,
						 const char    *hex_string);


/* --- transfer functions --- */
static inline void	_arts_bin_buffer_ensure_space	(ArtsBinBuffer	*buffer,
							 int             n_bytes);
static inline int	_arts_bin_buffer_check_space	(ArtsBinBuffer	*buffer,
							 int		 n_bytes);
static inline int	_arts_bin_buffer_get_byte	(ArtsBinBuffer	*buffer);
static inline void	_arts_bin_buffer_put_byte	(ArtsBinBuffer	*buffer,
							 int             value);
static inline int	_arts_bin_buffer_get_int	(ArtsBinBuffer	*buffer);
static inline void	_arts_bin_buffer_put_int	(ArtsBinBuffer	*buffer,
							 int             value);
static inline float	_arts_bin_buffer_get_float	(ArtsBinBuffer	*buffer);
static inline void	_arts_bin_buffer_put_float	(ArtsBinBuffer	*buffer,
							 float           value);
static inline void*	_arts_bin_buffer_get_bytes	(ArtsBinBuffer	*buffer,
							 unsigned int	*n_bytes_p);
static inline void	_arts_bin_buffer_put_bytes	(ArtsBinBuffer	*buffer,
							 unsigned int	 n_bytes,
							 void		*bytes);
static inline char*	_arts_bin_buffer_get_string	(ArtsBinBuffer	*buffer);
static inline void	_arts_bin_buffer_put_string	(ArtsBinBuffer	*buffer,
							 const char	*string);
static inline char**	_arts_bin_buffer_get_string_seq	(ArtsBinBuffer	*buffer,
							 int		*length_p);
static inline void	_arts_bin_buffer_put_string_seq	(ArtsBinBuffer	*buffer,
							 unsigned int	 n_strings,
							 char	       **strings);


/* --- implementations --- */
static inline void
_arts_bin_buffer_ensure_space (ArtsBinBuffer *buffer,
			     int           n_bytes)
{
  unsigned int offset = buffer->bp.cur - buffer->bytes;
  unsigned int length = buffer->bound - buffer->bytes;

  if (offset + n_bytes > length)
    {
      length += MAX (n_bytes, 1024);
      buffer->bytes = csl_realloc (buffer->bytes, length);
      buffer->bound = buffer->bytes + length;
      buffer->bp.cur = buffer->bytes + offset;
    }
}

static inline int
_arts_bin_buffer_check_space (ArtsBinBuffer *buffer,
			    int           n_bytes)
{
  buffer->error |= (buffer->bound - buffer->bp.cur) < n_bytes;
  return buffer->error;
}

static inline int
_arts_bin_buffer_get_byte (ArtsBinBuffer *buffer)
{
  _arts_bin_buffer_check_space (buffer, 1);
  return buffer->error ? 0 : *buffer->bp.cur++;
}

static inline void
_arts_bin_buffer_put_byte (ArtsBinBuffer *buffer,
			 int           value)
{
  _arts_bin_buffer_ensure_space (buffer, 1);
  *buffer->bp.cur++ = value;
}

static inline int
_arts_bin_buffer_get_int	(ArtsBinBuffer *buffer)
{
  _arts_bin_buffer_check_space (buffer, 4);
  return buffer->error ? 0 : ntohl (*buffer->bp.p_int32++);
}

static inline void
_arts_bin_buffer_put_int	(ArtsBinBuffer *buffer,
			 int           value)
{
  _arts_bin_buffer_ensure_space (buffer, 4);
  *buffer->bp.p_int32++ = htonl (value);
}

static inline float
_arts_bin_buffer_get_float (ArtsBinBuffer *buffer)
{
  union { float v_float; int v_int; } cval;
  
  _arts_bin_buffer_check_space (buffer, 4);
  cval.v_int = ntohl (*buffer->bp.p_int32++);
  return buffer->error ? 0 : cval.v_float;
}

static inline void
_arts_bin_buffer_put_float (ArtsBinBuffer *buffer,
			  float         value)
{
  union { float v_float; int v_int; } cval;
  
  _arts_bin_buffer_ensure_space (buffer, 4);
  cval.v_float = value;
  *buffer->bp.p_int32++ = htonl (cval.v_int);
}

static inline void*
_arts_bin_buffer_get_bytes (ArtsBinBuffer *buffer,
			  unsigned int *n_bytes_p)
{
  unsigned int length;
  char *bytes = NULL;

  length = _arts_bin_buffer_get_int (buffer);
  *n_bytes_p = length;
  _arts_bin_buffer_check_space (buffer, length);
  if (!buffer->error)
    {
      bytes = csl_malloc (length + 1);
      memcpy (bytes, buffer->bp.cur, length);
      buffer->bp.cur += length;
      bytes[length] = 0;
    }
  
  return bytes;
}

static inline void
_arts_bin_buffer_put_bytes (ArtsBinBuffer *buffer,
			  unsigned int  n_bytes,
			  void         *bytes)
{
  _arts_bin_buffer_ensure_space (buffer, 4 + n_bytes);
  _arts_bin_buffer_put_int (buffer, n_bytes);
  memcpy (buffer->bp.cur, bytes, n_bytes);
  buffer->bp.cur += n_bytes;
}

static inline char*
_arts_bin_buffer_get_string (ArtsBinBuffer *buffer)
{
  int length;

  return _arts_bin_buffer_get_bytes (buffer, &length);
}

static inline void
_arts_bin_buffer_put_string (ArtsBinBuffer *buffer,
			   const char   *string)
{
  int length;

  string = string ? string : "";
  length = strlen (string);
  _arts_bin_buffer_put_bytes (buffer, length + 1, (void*) string);
}

static inline char**
_arts_bin_buffer_get_string_seq (ArtsBinBuffer *buffer,
				int           *length_p)
{
  int i, l;
  char **p;

  l = _arts_bin_buffer_get_int (buffer);
  p = csl_malloc (l * sizeof (char*));

  for (i = 0; i < l; i++)
    p[i] = _arts_bin_buffer_get_string (buffer);

  if (!buffer->error)
    {
      *length_p = l;
      return p;
    }
  else
    {
      csl_strfreevn (l, p);
      *length_p = 0;
      return NULL;
    }
}

static inline void
_arts_bin_buffer_put_string_seq (ArtsBinBuffer *buffer,
				unsigned int   n_strings,
				char         **strings)
{
  unsigned int i;

  _arts_bin_buffer_put_int (buffer, n_strings);
  for (i = 0; i < n_strings; i++)
    _arts_bin_buffer_put_string (buffer, strings[i]);
}



#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __ARTS_BIN_BUFFER_H__ */
