/* **********************************************************************
 * *                                                                    *
 * *        Compress / UnCompress Runs of Zero Words in an Array        *
 * *                                                                    *
 * **********************************************************************
 * File: libs/user/compress.c
 *
 *                        COPYRIGHT (c) 1990, 1998  BY
 *          SIGMET INCORPORATED, WESTFORD MASSACHUSETTS, U.S.A.
 *
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */

#include <stdio.h>
#include <string.h>
#include "sigtypes.h"
#include "user_lib.h"

/* Routines to perform zero compression/decompression on an array of 16-bit
 * words.  Compressed data consists of code words conveying the following:
 *
 *  MSB   Low Bits   Meaning
 *  ---   --------   -------
 *   0       0       <unused>
 *   0       1       End of Ray
 *   0       2       <unused>
 *   0    3-32767    3 to 32767 zeros skipped
 *   1       0       <unused>
 *   1    1-32767    1 to 32767 data words follow
 */

/* ======================================================================
 * Compress an array of words having a given length.  Return the compressed
 * array and compressed array length.
 */
void compress_words
 (const SINT2 inbuf_a[],       	/* Input array of data to be compressed */
  SINT4 inlen_a,		/* Length of INBUF (Positive or zero) */
  SINT2 ioutbuf_a[],		/* Output array to hold compressed data */
  SINT4 *ioutlen_a)		/* Number of words written to IOUTBUF */
{
  const SINT2 *in=inbuf_a, *inend ;
  SINT2 *io=ioutbuf_a, *ioa ;
  SINT4 ij, ik ;

  inend = &inbuf_a[inlen_a] ;

  while( in < inend ) {
    ij = ik = 0 ; ioa = io++ ;
    while( in < inend ) {
      ik++ ;
      if( (*io++ = *in++) != 0 ) ij=0 ; else if( ++ij == 3 ) break ;
    }
    if( ij == 3 ) {
      if( ik == 3 ) {
        io = ioa ;
      } else {
        io -= 3 ; *ioa = 0x7FFD + ik ;
      }
      ik = 3 ;
      while( in < inend ) { if( *in ) break ; ik++ ; in++ ; }
      *io++ = ik ;

    } else {
      *ioa = 0x8000 + ik ; break ;
    }
  }
  *io++ = 1 ; *ioutlen_a = (SINT4)(io - ioutbuf_a) ;
}

/* ======================================================================
 * UnCompress an array of words.  The input array is supplied directly.
 * Error signals are returned if either the input or output array bounds
 * are exceeded.
 */
#define URETURN( STATUS ) \
  { *inlen_a = in + 1 ; *ioutlen_a = iout ; return( STATUS ) ; }

MESSAGE uncompress_words
 (SINT2 inbuf_a[],		/* Input array of data to be uncompressed */
  SINT4 insize_a,               /* Maximum # usable words from input buffer */
  SINT4 *inlen_a,		/* Number of words read from INBUF */
  SINT2 ioutbuf_a[],		/* Output array to hold UnCompressed data */
  SINT4 ioutsize_a,             /* Maximum # usable words from output buffer */
  SINT4 *ioutlen_a)		/* Number of words written to IOUTBUF */
{
  SINT2 icode ; SINT4 in=0, iout=0 ;

  if( insize_a <= 0 ) URETURN( USER_UNCOMP_IN ) ;
  icode = inbuf_a[0] ;

  while( icode != 1 ) {
    in++ ;
    if( icode >= 0x0003 ) {
      if( (iout + (SINT4)icode) > ioutsize_a ) URETURN( USER_UNCOMP_OUT ) ;
      memset( &ioutbuf_a[iout], 0, 2*(SINT4)icode ) ; iout += (SINT4)icode ;

    } else {
      icode &= 0x7FFF ;
      if( icode >= 1 ) {
	if( (iout + (SINT4)icode) > ioutsize_a ) URETURN( USER_UNCOMP_OUT ) ;
	if( (  in + (SINT4)icode) >   insize_a ) URETURN( USER_UNCOMP_IN  ) ;
        memcpy( &ioutbuf_a[iout], &inbuf_a[in], 2*(SINT4)icode ) ;
        in += (SINT4)icode ; iout += (SINT4)icode ;
      }
    }
    if( (in + 1) > insize_a ) URETURN( USER_UNCOMP_IN ) ;
    icode = inbuf_a[in] ;
  }
  URETURN( SS_NORMAL ) ;
}

/* ======================================================================
 * Skip over the next item in a compressed word array.  This is like
 * UNCOMPRESS_WORDS except that no output array is produced.
 */
MESSAGE uncompress_words_skip
 (SINT2 inbuf_a[],		/* Input array of compressed data */
  SINT4 insize_a,               /* Maximum # usable words from input buffer */
  SINT4 *inlen_a,		/* Number of words read from INBUF */
  SINT4 *ioutlen_a)		/* Number of words of output that would have
				   resulted, had the data been UnCompressed. */
{
  SINT2 icode ; SINT4 in=0, iout=0 ;

  if( insize_a <= 0 ) URETURN( USER_UNCOMP_IN ) ;

  icode = inbuf_a[0] ;
  while( icode != 1 ) {
    in++ ;
    if( icode >= 0x0003 ) {
      iout += (SINT4)icode ;

    } else {
      icode &= 0x7FFF ;
      if( icode >= 1 ) {
	if( (in + (SINT4)icode) > insize_a ) URETURN( USER_UNCOMP_IN ) ;
        in += (SINT4)icode ; iout += (SINT4)icode ;
      }
    }
    if( (in + 1) > insize_a ) URETURN( USER_UNCOMP_IN ) ;
    icode = inbuf_a[in] ;
  }
  URETURN( SS_NORMAL ) ;
}

/* ======================================================================
 * UnCompress an array of words.  Like the above routine, except assumes the use
 * of a co-routine GET_A(N,BUF) which supplies the next N bytes from the input
 * array into the buffer BUF.  GET_A must have internal state, such that each
 * time it is called we get the next words from the input string, not the same
 * words each time.
 */
MESSAGE uncompress_cowords
 (void get_a( SINT2 *buf_a, SINT4 cnt_a), /* Input coroutine */
  SINT4 insize_a,               /* Maximum # usable words from input buffer */
  SINT4 *inlen_a,		/* Number of words read from INBUF */
  SINT2 ioutbuf_a[],		/* Output array to hold UnCompressed data */
  SINT4 ioutsize_a,             /* Maximum # usable words from output buffer */
  SINT4 *ioutlen_a)		/* Number of words written to IOUTBUF */
{
  SINT2 icode ; SINT4 in=0, iout=0 ;

  if( insize_a <= 0 ) URETURN( USER_UNCOMP_IN ) ;

  get_a( &icode, 2 ) ;
  while( icode != 1 ) {
    in++ ;
    if( icode >= 0x0003 ) {
      if( (iout + (SINT4)icode) > ioutsize_a ) URETURN( USER_UNCOMP_OUT ) ;
      memset( &ioutbuf_a[iout], 0, 2*(SINT4)icode ) ; iout += (SINT4)icode ;

    } else {
      icode &= 0x7FFF ;
      if( icode >= 1 ) {
	if( (iout + (SINT4)icode) > ioutsize_a ) URETURN( USER_UNCOMP_OUT ) ;
	if( (  in + (SINT4)icode) >   insize_a ) URETURN( USER_UNCOMP_IN  ) ;
	get_a( &ioutbuf_a[iout], 2*(SINT4)icode ) ;
        in += (SINT4)icode ; iout += (SINT4)icode ;
      }
    }
    if( (in + 1) > insize_a ) URETURN( USER_UNCOMP_IN ) ;
    get_a( &icode, 2 ) ;
  }
  URETURN( SS_NORMAL ) ;
}

/* ======================================================================
 * Skip over the next item in a compressed word array.  This is like
 * UNCOMPRESS_COWORDS except that no output array is produced.
 */
MESSAGE uncompress_cowords_skip
 (void get_a( SINT2 *buf_a, SINT4 cnt_a), /* Input coroutine */
  SINT4 insize_a,               /* Maximum # usable words from input buffer */
  SINT4 *inlen_a,		/* Number of words read from INBUF */
  SINT4 *ioutlen_a)		/* Number of words written to IOUTBUF */
{
  SINT2 icode ; SINT4 in=0, iout=0 ;

  if( insize_a <= 0 ) URETURN( USER_UNCOMP_IN ) ;

  get_a( &icode, 2 ) ;
  while( icode != 1 ) {
    in++ ;
    if( icode >= 0x0003 ) {
      iout += (SINT4)icode ;

    } else {
      icode &= 0x7FFF ;
      if( icode >= 1 ) {
	if( (in + (SINT4)icode) > insize_a ) URETURN( USER_UNCOMP_IN  ) ;
        in += (SINT4)icode ; iout += (SINT4)icode ;
	while( icode > 0 ) {
	  SINT2 trashbuf[500], iread = MIN( 500, icode ) ;
	  get_a( trashbuf, 2*(SINT4)iread ) ; icode -= iread ;
	}
      }
    }
    if( (in + 1) > insize_a ) URETURN( USER_UNCOMP_IN ) ;
    get_a( &icode, 2 ) ;
  }
  URETURN( SS_NORMAL ) ;
}
