#ifndef COMMON_SDR_H
#define COMMON_SDR_H

/*
 * Copyright (c) 2002, The EROS Group, LLC and Johns Hopkins
 * University. All rights reserved.
 * 
 * This software was developed to support the EROS secure operating
 * system project (http://www.eros-os.org). The latest version of
 * the OpenCM software can be found at http://www.opencm.org.
 * 
 * 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.
 * 
 * 3. Neither the name of the The EROS Group, LLC nor the name of
 *    Johns Hopkins University, nor the names of its contributors
 *    may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
 */

/* Serialization logic. This generates the XDR format (or it should),
 * but uses dynamic memory allocation
 */

#define SDR_AUTO   0		/* try to autodetect stream type */
#define SDR_RAW    1		/* not self identifying! */
#define SDR_BINARY 2
#define SDR_TEXTY  3
#define SDR_DTEXTY 4

#define STREAM_FILE    1
#define STREAM_MEM     2
#define STREAM_BUFFER  3

#define NEW_SDR

#ifdef NEW_SDR
#define NEW_FILE_STREAM
#define NEW_BUFFERED_STREAM
#define NEW_MEMORY_STREAM
#endif

typedef struct SDR_stream SDR_stream;
struct SDR_stream {
  const char *name;		/* name of the source, for exceptions */
  uint32_t type;
  uint32_t format;
  uint32_t indent;	/* used for TEXTY output */
  uint32_t mode;

#ifndef NEW_SDR
  void    *data;
#endif
  void    *state;		/* private internal state */
  ocmoff_t len;
#ifndef NEW_SDR
  ocmoff_t bound;
#endif
  ocmoff_t pos;

#ifdef NEW_SDR
  uint8_t *rwbuf;		/* read/write buffer */
  uint8_t *rwptr;		/* current position pointer into rwbuf */
  uint8_t *rwlim;		/* rw buffer limit */

  OC_bool  (*wflush)(SDR_stream *);
  OC_bool  (*rfill)(SDR_stream *);
#else
  int      (*strm_putc)(SDR_stream *, int c);
  ocmoff_t (*strm_puts)(SDR_stream *, const void *v, ocmoff_t len);
  int      (*strm_getc)(SDR_stream *);
  int      (*strm_peekc)(SDR_stream *);
  ocmoff_t (*strm_gets)(SDR_stream *, void *v, ocmoff_t len);
#endif

  Buffer * (*asBuffer)(SDR_stream *);

  void     (*reread)(SDR_stream *);
  void     (*close)(SDR_stream *);
};

SDR_stream*    stream_createfile(const char *fileName, unsigned format);

SDR_stream*    stream_createMemory(uint32_t format);
SDR_stream*    stream_createBuffer(uint32_t format);

SDR_stream*    stream_create_sha1(void);

SDR_stream*    stream_fromfile(const char *fileName, uint32_t format);
SDR_stream*    stream_fromMemory(const char *buffer, uint32_t len,
                                 uint32_t format);
SDR_stream*    stream_fromBuffer(Buffer *buf, uint32_t format);

#define stream_wdrain(s) (void)( ((s)->rwptr != (s)->rwbuf) && (s)->wflush(s) )
#define stream_wflush(s) (void)( ((s)->rwptr == (s)->rwlim) && (s)->wflush(s) )
#define stream_rfill(s)  (void)( ((s)->rwptr == (s)->rwlim) && (s)->rfill(s) )

#ifdef NEW_SDR
#define stream_length(s)   ( stream_wdrain(s), s->len )
#else
#define old_stream_length(s) (s)->len
#define new_stream_length(s)   ( stream_wdrain(s), s->len )
#define stream_length(s) (s->rwbuf ? new_stream_length(s) : old_stream_length(s))
#endif

#ifdef NEW_SDR
#define stream_position(s) ( s->pos + ((s)->rwptr - (s)->rwbuf) )
#else
#define old_stream_position(s) (s)->pos
#define new_stream_position(s) ( s->pos + ((s)->rwptr - (s)->rwbuf) )
#define stream_position(s) (s->rwbuf ? new_stream_position(s) : old_stream_position(s))
#endif

#define stream_asBuffer(s) (s)->asBuffer(s)

#define stream_format(s) (s)->format

#define stream_reread(s) (s)->reread(s)
#define stream_close(s)  (s)->close(s)

#ifdef NEW_SDR
/** putc(): flush if necessary, then append to the rwbuffer: */
#define stream_putc(s, c)  ( stream_wflush(s), *(s)->rwptr++ = (c) )
/** getc(): fill if necessary, then take from the rwbuffer: */
#define stream_getc(s)     ( stream_rfill(s), *(s)->rwptr++ )
/** ungetc(): flush, then make rwbuffer hold only one char */
#define stream_peekc(s)    ( stream_rfill(s), *(s)->rwptr )

#else
int            old_stream_putc(SDR_stream*, int c);
int            old_stream_getc(SDR_stream*);
int            old_stream_peekc(SDR_stream*);

/** putc(): flush if necessary, then append to the rwbuffer: */
#define new_stream_putc(s, c)  ( stream_wflush(s), *(s)->rwptr++ = (c) )
/** getc(): fill if necessary, then take from the rwbuffer: */
#define new_stream_getc(s)     ( stream_rfill(s), *(s)->rwptr++ )
/** ungetc(): flush, then make rwbuffer hold only one char */
#define new_stream_peekc(s)    ( stream_rfill(s), *(s)->rwptr )

#define stream_putc(s, c) ((s)->rwbuf ? new_stream_putc(s,c) : old_stream_putc(s, c))
#define stream_getc(s)    ((s)->rwbuf ? new_stream_getc(s) : old_stream_getc(s))
#define stream_peekc(s)   ((s)->rwbuf ? new_stream_peekc(s) : old_stream_peekc(s))
#endif

ocmoff_t       do_stream_write(SDR_stream*, const void *, ocmoff_t len);
ocmoff_t       do_stream_read(SDR_stream*, void *, ocmoff_t len);

#define stream_read(s, vp, len) \
  (( (len)<= ( (s)->rwlim - (s)->rwptr ) ) ? \
   ( memcpy(vp, (s)->rwptr, (len)), (s)->rwptr += (len), (len) ) : \
   do_stream_read(s, vp, len) )
#define stream_write(s, vp, len) \
  (( (len)<= ( (s)->rwlim - (s)->rwptr ) ) ? \
   ( memcpy((s)->rwptr, vp, (len)), (s)->rwptr += (len), (len) ) : \
   do_stream_write(s, vp, len) )

Buffer *       stream_read_buffer(SDR_stream*, ocmoff_t len);

void           stream_write_buffer(SDR_stream *, const Buffer *);
void           stream_write_partial_buffer(SDR_stream *, const Buffer *, 
					   ocmoff_t start, ocmoff_t len);

size_t         stream_vprintf(SDR_stream*, const char *fmt, va_list);
size_t         stream_printf(SDR_stream*, const char *fmt, ...);
size_t         stream_scanf(SDR_stream*, const char *fmt, ...);

void           sdr_w_u8(const char *elem, SDR_stream *, uint8_t);
uint8_t        sdr_r_u8(const char *elem, SDR_stream *);

void           sdr_w_u16(const char *elem, SDR_stream *, uint16_t);
uint16_t       sdr_r_u16(const char *elem, SDR_stream *);

void           sdr_w_u32(const char *elem, SDR_stream *, uint32_t);
uint32_t       sdr_r_u32(const char *elem, SDR_stream *);

void           sdr_w_u64(const char *elem, SDR_stream *, uint64_t);
uint64_t       sdr_r_u64(const char *elem, SDR_stream *);

void           sdr_w_bytes(const char *elem, SDR_stream *, uint32_t len,
                           const void *);
void *         sdr_r_bytes(const char *elem, SDR_stream *, uint32_t len);

void           sdr_w_buffer(const char *elem, SDR_stream *, const Buffer*);
Buffer *       sdr_r_buffer(const char *elem, SDR_stream *, ocmoff_t len);

void           sdr_w_string(const char *elem, SDR_stream *, const char *);
char *         sdr_r_string(const char *elem, SDR_stream *);

void           sdr_w_obname(const char *elem, SDR_stream *, const char *);
char *         sdr_r_obname(const char *elem, SDR_stream *);

/* Interface for reading/writing serializable objects. Write is
   non-const because serializer may canonicalize. */
void           sdr_write(const char *elem, SDR_stream *, const void *);
void *         sdr_read(const char *elem, SDR_stream *);

void           sdr_show(const void *);
void           sdr_nshow(const void *);

/* This isn't really an SDR function, but it is handled in parallel
   with the serialize() and deserialize() routine, so it is easier to
   declare it here than anywhere else. */
OC_bool        sdr_check(const void *elem);

/* Format the print-style arguments into a dynamically allocated
   string. This is a wrapper for stream_vprintf(), intended for use
   with the exceptions package to format the explanation string. */
const char *format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));

#endif /* COMMON_SDR_H */
