#ifndef __PACKED_LIST_H__ /* file wrapper */
#define __PACKED_LIST_H__
/*
 * Jeffrey Friedl
 * Omron Corporation			オムロン（株）
 * Nagaokakyoshi, Japan			〒617長岡京下海印寺
 *
 * jfriedl@nff.ncl.omron.co.jp
 *
 * This work is placed under the terms of the GNU General Purpose License
 * (the "GNU Copyleft").
 */


#define packed_list_version 200 /* 2.00 */

/*
 * Jeffrey Friedl, Omron Corp. (jfriedl@nff.ncl.omron.co.jp)
 * October 1993
 * 
 * Packed lists are sequences of unsigned values. Each value, which may
 * be as large as an unsigned long, is held in as few bytes as possible.
 * 
 * Each sequential set of value-indicating bytes is zero or more bytes
 * with the high bit set (to indicate that it's not the last byte in the
 * set) followed by one byte with the high bit clear. Each byte in the
 * set adds seven bits to the value. Bytes are given least-significant
 * first.
 * 
 * These are given as inlined functions (or local static ones if you don't
 * have gcc), as they're small.
 */
#include "MemItem.h"
#include "output.h"

#define DATA_BITS_IN_BYTE	  7
#define NON_LAST_BYTE_MARKER      (1<<DATA_BITS_IN_BYTE)
#define NON_LAST_BYTE_DATA_MASK   (NON_LAST_BYTE_MARKER - 1)

#if !defined(__GNUC__)
#  if !defined(__volatile__)
#    define __volatile__ /*nothing; for use with volatile functions */
#  endif
#  if !defined(__inline__)
#    define __inline__ /*nothing; for use with volatile functions */
#  endif
#endif

/*
 * write_packed_value(PTR, VALUE)
 *
 * The VALUE is placed onto the byte stream represented by (*PTR)
 * with a byproduct that (*PTR) is bumped up by the number of bytes
 * actually used.  Said number of bytes used is returned.
 */
static __inline__ unsigned
write_packed_value(unsigned char **ptr, unsigned long value)
{
    const unsigned char *optr = *ptr;
    while (value > NON_LAST_BYTE_DATA_MASK) {
	*(*ptr)++ = (value & NON_LAST_BYTE_DATA_MASK) | NON_LAST_BYTE_MARKER;
	value >>= DATA_BITS_IN_BYTE;
    }
    *(*ptr)++ = value;
    return *ptr - optr;
}

/*
 * VALUE = read_packed_value(PTR)
 *
 * The VALUE is read from the stream of bytes at (*PTR) and returned, with
 * the byproduct that (*PTR) is incremented by the number of bytes actually
 * used when getting VALUE.
 */
static __inline__ unsigned long
read_packed_value(const unsigned char **pptr)
{
    unsigned char c;
    unsigned long value;
    unsigned shift;

    if (c = *(*pptr)++, (c & NON_LAST_BYTE_MARKER) == 0)
	return c;

    value = c & NON_LAST_BYTE_DATA_MASK;
    shift = DATA_BITS_IN_BYTE;
    while (c = *(*pptr)++, c & NON_LAST_BYTE_MARKER) {
	value |= (c & NON_LAST_BYTE_DATA_MASK) << shift;
        shift += DATA_BITS_IN_BYTE;
    }
    value |= c << shift;

    return value;
}

#if 0
/*
 * Like access_index except that (*PTR) is not actually moved.
 */
static __inline__ unsigned long
peek_at_packed_value(const unsigned char **pptr)
{
    const unsigned char *ptr = *pptr;
    return read_packed_value(&ptr);
}
#endif

/*
 * VALUE = read_packed_value(PTR)
 *
 * The VALUE is read from the stream of bytes at (*PTR) and returned, with
 * the byproduct that (*PTR) is incremented by the number of bytes actually
 * used when getting VALUE.
 */
static __inline__ unsigned long
mem_read_packed_value(MemItem *mem)
{
    unsigned char c;
    unsigned long value;
    unsigned shift;

    if (c = GetMemByte(mem), (c & NON_LAST_BYTE_MARKER) == 0)
	return c;

    value = c & NON_LAST_BYTE_DATA_MASK;
    shift = DATA_BITS_IN_BYTE;
    while (c = GetMemByte(mem), c & NON_LAST_BYTE_MARKER) {
	value |= (c & NON_LAST_BYTE_DATA_MASK) << shift;
        shift += DATA_BITS_IN_BYTE;
    }
    value |= c << shift;

    return value;
}

#if 0
/*
 * Like access_index except that (*PTR) is not actually moved.
 */
static __inline__ unsigned long
mem_peek_at_packed_value(MemItem *mem)
{
    MemLoc loc = mem->loc;
    unsigned long val = mem_read_packed_value(mem);
    mem->loc = loc;
    return val;
}
#endif

/*
 * Returns the number of bytes required to store the given value.
 */
static __inline__ unsigned
bytes_required_for_packed_value(unsigned long value)
{
    unsigned bytes = 1;
    while (value > NON_LAST_BYTE_DATA_MASK) {
	bytes++;
        value >>= DATA_BITS_IN_BYTE;
    }
    return bytes;
}

#undef __inline__

#endif /* file wrapper */
