/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* Coding Buffer Specifications */
#ifndef __ASN1BUF_H__
#define __ASN1BUF_H__

#include "k5-int.h"
#include "krbasn1.h"

typedef struct code_buffer_rep {
    char *base, *bound, *next;
} asn1buf;


/**************** Private Procedures ****************/

#if (__GNUC__ >= 2) && !defined(CONFIG_SMALL)
unsigned int asn1buf_free(const asn1buf *buf);
/*
 * requires  *buf is allocated
 * effects   Returns the number of unused, allocated octets in *buf.
 */
#define asn1buf_free(buf)                               \
    (((buf) == NULL || (buf)->base == NULL)             \
     ? 0U                                               \
     : (unsigned int)((buf)->bound - (buf)->next + 1))


asn1_error_code asn1buf_ensure_space(asn1buf *buf, const unsigned int amount);
/*
 * requires  *buf is allocated
 * modifies  *buf
 * effects  If buf has less than amount octets of free space, then it is
 *          expanded to have at least amount octets of free space.
 *          Returns ENOMEM memory is exhausted.
 */
#define asn1buf_ensure_space(buf,amount)                        \
    ((asn1buf_free(buf) < (amount))                             \
     ? (asn1buf_expand((buf), (amount)-asn1buf_free(buf)))      \
     : 0)

asn1_error_code asn1buf_expand(asn1buf *buf, unsigned int inc);
/*
 * requires  *buf is allocated
 * modifies  *buf
 * effects   Expands *buf by allocating space for inc more octets.
 *            Returns ENOMEM if memory is exhausted.
 */
#endif

int asn1buf_len(const asn1buf *buf);
/*
 * requires  *buf is allocated
 * effects   Returns the length of the encoding in *buf.
 */
#define asn1buf_len(buf)        ((buf)->next - (buf)->base)

/****** End of private procedures *****/

/*
 * Overview
 *
 *  The coding buffer is an array of char (to match a krb5_data structure)
 *   with 3 reference pointers:
 *   1) base - The bottom of the octet array.  Used for memory management
 *             operations on the array (e.g. alloc, realloc, free).
 *   2) next - Points to the next available octet position in the array.
 *             During encoding, this is the next free position, and it
 *               advances as octets are added to the array.
 *             During decoding, this is the next unread position, and it
 *               advances as octets are read from the array.
 *   3) bound - Points to the top of the array. Used for bounds-checking.
 *
 *  All pointers to encoding buffers should be initalized to NULL.
 *
 * Operations
 *
 *  asn1buf_create
 *  asn1buf_wrap_data
 *  asn1buf_destroy
 *  asn1buf_insert_octet
 *  asn1buf_insert_charstring
 *  asn1buf_remove_octet
 *  asn1buf_remove_charstring
 *  asn1buf_unparse
 *  asn1buf_hex_unparse
 *  asn12krb5_buf
 *  asn1buf_remains
 *
 *  (asn1buf_size)
 *  (asn1buf_free)
 *  (asn1buf_ensure_space)
 *  (asn1buf_expand)
 *  (asn1buf_len)
 */

asn1_error_code asn1buf_create(asn1buf **buf);
/*
 * effects   Creates a new encoding buffer pointed to by *buf.
 *           Returns ENOMEM if the buffer can't be created.
 */

asn1_error_code asn1buf_wrap_data(asn1buf *buf, const krb5_data *code);
/*
 * requires  *buf has already been allocated
 * effects   Turns *buf into a "wrapper" for *code.  i.e. *buf is set up
 *            such that its bottom is the beginning of *code, and its top
 *            is the top of *code.
 *           Returns ASN1_MISSING_FIELD if code is empty.
 */

asn1_error_code asn1buf_imbed(asn1buf *subbuf, const asn1buf *buf,
                              const unsigned int length,
                              const int indef);
/*
 * requires  *subbuf and *buf are allocated
 * effects   *subbuf becomes a sub-buffer of *buf.  *subbuf begins
 *            at *buf's current position and is length octets long.
 *            (Unless this would exceed the bounds of *buf -- in
 *            that case, ASN1_OVERRUN is returned)  *subbuf's current
 *            position starts at the beginning of *subbuf.
 */

asn1_error_code asn1buf_sync(asn1buf *buf, asn1buf *subbuf, asn1_class Class,
                             asn1_tagnum lasttag,
                             unsigned int length, int indef,
                             int seqindef);
/*
 * requires  *subbuf is a sub-buffer of *buf, as created by asn1buf_imbed.
 *           lasttag is the last tagnumber read.
 * effects   Synchronizes *buf's current position to match that of *subbuf.
 */

asn1_error_code asn1buf_skiptail(asn1buf *buf, const unsigned int length,
                                 const int indef);
/*
 * requires  *buf is a subbuffer used in a decoding of a
 *           constructed indefinite sequence.
 * effects   skips trailing fields.
 */

void asn1buf_destroy(asn1buf **buf);
/* effects   Deallocates **buf, sets *buf to NULL. */

asn1_error_code asn1buf_insert_octet(asn1buf *buf, const int o);
/*
 * requires  *buf is allocated
 * effects   Inserts o into the buffer *buf, expanding the buffer if
 *           necessary.  Returns ENOMEM memory is exhausted.
 */
#if ((__GNUC__ >= 2) && !defined(ASN1BUF_OMIT_INLINE_FUNCS)) && !defined(CONFIG_SMALL)
extern __inline__ asn1_error_code asn1buf_insert_octet(asn1buf *buf, const int o)
{
    asn1_error_code retval;

    retval = asn1buf_ensure_space(buf,1U);
    if (retval) return retval;
    *(buf->next) = (char)o;
    (buf->next)++;
    return 0;
}
#endif

asn1_error_code
asn1buf_insert_bytestring(
    asn1buf *buf,
    const unsigned int len,
    const void *s);
/*
 * requires  *buf is allocated
 * modifies  *buf
 * effects   Inserts the contents of s (an array of length len)
 *            into the buffer *buf, expanding the buffer if necessary.
 *           Returns ENOMEM if memory is exhausted.
 */

#define asn1buf_insert_octetstring asn1buf_insert_bytestring
#define asn1buf_insert_charstring  asn1buf_insert_bytestring

asn1_error_code asn1buf_remove_octet(asn1buf *buf, asn1_octet *o);
/*
 * requires  *buf is allocated
 * effects   Returns *buf's current octet in *o and advances to
 *            the next octet.
 *           Returns ASN1_OVERRUN if *buf has already been exhausted.
 */
#define asn1buf_remove_octet(buf,o)                     \
    (((buf)->next > (buf)->bound)                       \
     ? ASN1_OVERRUN                                     \
     : ((*(o) = (asn1_octet)(*(((buf)->next)++))),0))

asn1_error_code
asn1buf_remove_octetstring(
    asn1buf *buf,
    const unsigned int len,
    asn1_octet **s);
/*
 * requires  *buf is allocated
 * effects   Removes the next len octets of *buf and returns them in **s.
 *           Returns ASN1_OVERRUN if there are fewer than len unread octets
 *            left in *buf.
 *           Returns ENOMEM if *s could not be allocated.
 */

asn1_error_code
asn1buf_remove_charstring(asn1buf *buf, const unsigned int len, char **s);
/*
 * requires  *buf is allocated
 * effects   Removes the next len octets of *buf and returns them in **s.
 *           Returns ASN1_OVERRUN if there are fewer than len unread octets
 *            left in *buf.
 *           Returns ENOMEM if *s could not be allocated.
 */

asn1_error_code asn1buf_unparse(const asn1buf *buf, char **s);
/*
 * modifies  *s
 * effects   Returns a human-readable representation of *buf in *s,
 *           where each octet in *buf is represented by a character in *s.
 */

asn1_error_code asn1buf_hex_unparse(const asn1buf *buf, char **s);
/*
 * modifies  *s
 * effects   Returns a human-readable representation of *buf in *s,
 *           where each octet in *buf is represented by a 2-digit
 *           hexadecimal number in *s.
 */

asn1_error_code asn12krb5_buf(const asn1buf *buf, krb5_data **code);
/*
 * modifies  *code
 * effects   Instantiates **code with the krb5_data representation of **buf.
 */

int asn1buf_remains(asn1buf *buf, int indef);
/*
 * requires  *buf is a buffer containing an asn.1 structure or array
 * modifies  *buf
 * effects   Returns the number of unprocessed octets remaining in *buf.
 */

#endif
