/** @file runtime/codecfcn.h
 * Definitions of encoding and decoding functions and data structures
 */

/* Copyright © 2000-2002 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA 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 2, or (at your option)
   any later version.

   MARIA 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.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifndef FCN
# error "FCN not defined!"
#endif /* FCN */

/** encoding buffer */
static word_t* ENCVAR (buf) = 0;
/** current length of the buffer, in bits */
static unsigned ENCVAR (bits) = 0;
/** allocated length of the buffer, in words */
static unsigned ENCVAR (length) = 0;

/** Append data to the buffer
 * @param data	Data to be appended
 * @param bits	Length of data in bits
 */
static void
FCN (enc) (card_t data, unsigned bits)
{
  unsigned wordpos, bitpos;

  if (ENCVAR (bits) + bits > ENCVAR (length) * WORD_T_BIT) {
    /* enlarge the buffer */
    wordpos = NUM_WORDS (ENCVAR (bits));
    ENCVAR (length) = NUM_WORDS (ENCVAR (bits) + bits);
    if (ENCVAR (buf))
      ENCVAR (buf) = realloc (ENCVAR (buf), ENCVAR (length) * sizeof (word_t));
    else
      ENCVAR (buf) = malloc (ENCVAR (length) * sizeof (word_t));
    /* clear the new words */
    memset (ENCVAR (buf) + wordpos, 0,
	    (ENCVAR (length) - wordpos) * sizeof (word_t));
  }

  wordpos = ENCVAR (bits) / WORD_T_BIT, bitpos = ENCVAR (bits) % WORD_T_BIT;
  ENCVAR (bits) += bits;
  ENCVAR (buf) [wordpos] |= ((word_t) data) << bitpos;
  if (bitpos + bits > WORD_T_BIT) {
    bitpos = WORD_T_BIT - bitpos;
    ENCVAR (buf) [++wordpos] = data >>= bitpos;
# if !(BYTE_ORDER == BIG_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN)
    for (bits -= bitpos; bits > WORD_T_BIT; bits -= WORD_T_BIT)
      ENCVAR (buf) [++wordpos] = data >>= WORD_T_BIT;
# endif
  }
}

/** Remove data from the buffer
 * @param bits	Number of bits to remove
 */
static void
FCN (del) (unsigned bits)
{
  unsigned wordpos, bitpos;

  ENCVAR (bits) -= bits;
  wordpos = ENCVAR (bits) / WORD_T_BIT, bitpos = ENCVAR (bits) % WORD_T_BIT;
  if (bitpos)
    ENCVAR (buf) [wordpos] &= (((word_t) 1) << bitpos) - 1;
  memset (ENCVAR (buf) + wordpos, 0,
	  ((ENCVAR (bits) + bits) / WORD_T_BIT - wordpos) * sizeof (word_t));
}

/** clear the encoding buffer */
static void
FCN (clear) (void)
{
  memset (ENCVAR (buf), 0, NUM_WORDS (ENCVAR (bits)) * sizeof (word_t));
  ENCVAR (bits) = ENCVAR (length) = 0;
}

/** clean up the encoding buffer */
static void
FCN (cleanup) (void)
{
  free (ENCVAR (buf)), ENCVAR (buf) = 0;
  ENCVAR (bits) = ENCVAR (length) = 0;
}

/** deflate the encoding buffer */
static void
FCN (deflate) (void)
{
#if BYTE_ORDER == BIG_ENDIAN
  if (ENCVAR (bits) % WORD_T_BIT)
    ENCVAR (buf) [ENCVAR (bits) / WORD_T_BIT] <<=
      CHAR_BIT * (((word_t) -NUM_BYTES (ENCVAR (bits))) % sizeof (word_t));
#endif
}

/** decoding buffer */
static word_t* DECVAR (buf) = 0;
/** bit offset to the buffer */
static unsigned DECVAR (bits) = 0;

/** Extract data from the buffer
 * @param bits	number of bits to extract
 * @return	the data
 */
static card_t
FCN (dec) (unsigned bits)
{
  unsigned wordpos, bitpos;
  card_t mask, data;
  wordpos = DECVAR (bits) / WORD_T_BIT, bitpos = DECVAR (bits) % WORD_T_BIT;
  DECVAR (bits) += bits;
  mask = bits < CARD_T_BIT ? (((card_t) 1) << bits) - 1 : ~0;
  data = (DECVAR (buf) [wordpos] >> bitpos) & mask;
  if (bitpos + bits > WORD_T_BIT) {
    bitpos = WORD_T_BIT - bitpos;
    data |= (((card_t) DECVAR (buf) [++wordpos]) << bitpos) & mask;
# if !(BYTE_ORDER == BIG_ENDIAN || BYTE_ORDER == LITTLE_ENDIAN)
    for (bits -= bitpos, bitpos = WORD_T_BIT;
	 bits > WORD_T_BIT;
	 bits -= WORD_T_BIT, bitpos += WORD_T_BIT)
      data |= (((card_t) DECVAR (buf) [++wordpos]) << bitpos) & mask;
# endif
  }
  return data;
}

/** inflate the decoding buffer
 * @param buf	the buffer
 * @param bytes	length of the buffer
 */
static void
FCN (inflate) (word_t* buf, size_t bytes)
{
  DECVAR (buf) = buf;
  DECVAR (bits) = 0;
#if BYTE_ORDER == BIG_ENDIAN
  if (bytes % sizeof (word_t))
    DECVAR (buf) [NUM_WORD_B (bytes) - 1] >>=
      CHAR_BIT * (((word_t) -bytes) % sizeof (word_t));
#elif BYTE_ORDER == LITTLE_ENDIAN
  if (bytes % sizeof (word_t))
    DECVAR (buf) [NUM_WORD_B (bytes) - 1] &=
      (((word_t) 1) << (WORD_T_BIT - CHAR_BIT *
			(((word_t) -bytes) % sizeof (word_t)))) - 1;
#endif
}
