/**************************************************************************
 *
 * bitio_m_random.h -- Macros for bitio to a file (random access)
 * Copyright (C) 1994  Neil Sharman
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **************************************************************************
 *
 *  This file contains macros for doing bitwise input and output on an array 
 *  of chars. These routines are slower than the ones in "mem" files. but 
 *  with these routines you can mix reads and writes, or multiple writes,  on
 *  the array of chars at the same time and guarantee them to work, also you 
 *  can seek to a point and do a write. The decode and encode macros cannot 
 *  detect when the end off the character is reached and just continue 
 *  processing.
 *
 *
 **************************************************************************/

#ifndef H_BITIO_M_RANDOM
#define H_BITIO_M_RANDOM

typedef struct random_bitio_state
  {
    FILE *File;
    unsigned char *Buf;
    mg_u_long Base;
    mg_u_long Used;
    mg_u_long pos;
    mg_u_long len;
    mg_u_long sft;
  }
random_bitio_state;


#define ENCODE_START(f,l)						\
  {									\
    register FILE *__file = f;						\
    register unsigned char *__buf;					\
    register mg_u_long __pos = 0;					\
    register mg_u_long __base = 0;					\
    register mg_u_long __is_used = 0;					\
    register mg_u_long __len = (l)-1;				\
    register mg_u_long __sft = 0;					\
    while (__len) { ++__sft; __len >>=1; }				\
    __len = 1<<__sft;							\
    __buf = Xmalloc(__len);						\
    fseek(__file, 0, 0);						\
    fread(__buf, 1, __len, __file);

#define ENCODE_CONTINUE(b)						\
  {									\
    register FILE *__file = (b).File;					\
    register unsigned char *__buf = (b).Buf;				\
    register mg_u_long __pos = (b).pos;				\
    register mg_u_long __base = (b).Base;				\
    register mg_u_long __is_used = (b).Used;				\
    register mg_u_long __len = (b).len;				\
    register mg_u_long __sft = (b).sft;

#define SEEK fprintf(stderr, "Seek to %d\n",__base)
#define READ fprintf(stderr, "Read of %d\n",__len)
#define WRITE fprintf(stderr, "Write of %d\n",__is_used)

#define WRITE_READ 							\
	(__is_used ? (fseek(__file, __base, 0),				\
                   fwrite(__buf, 1, __len, __file)) : 0,		\
        __base += __len,						\
	fseek(__file, __base, 0),					\
        fread(__buf, 1, __len, __file),					\
	__pos = 0, __is_used = 0)

#define ENCODE_BIT(b)							\
  do {									\
    if (b)								\
      __buf[__pos>>3] |= 0x80 >> (__pos&7);				\
    else								\
      __buf[__pos>>3] &= 0xff7f >> (__pos&7);				\
    __is_used = 1;								\
    ++__pos;								\
    if ((__pos>>3) >= __len)						\
      (void)WRITE_READ;							\
  } while(0)

#define ENCODE_PAUSE(b)							\
    (b).File = __file;							\
    (b).Buf = __buf;							\
    (b).pos = __pos;							\
    (b).Base = __base;							\
    (b).Used = __is_used;							\
    (b).len = __len;							\
    (b).sft = __sft;							\
  }

#define ENCODE_FLUSH							\
  if (__is_used)								\
    {									\
      fseek(__file, __base, 0);						\
      fwrite(__buf, 1, __len, __file);					\
      __is_used = 0;							\
    }



#define ENCODE_DONE							\
    ENCODE_FLUSH;							\
    free(__buf);							\
  }


#define DECODE_START(f,l) ENCODE_START(f,l)

#define DECODE_CONTINUE(b) ENCODE_CONTINUE(b)

#define DECODE_BIT 							\
    (__pos++, (((__pos-1)>>3) >= __len ? WRITE_READ, __pos=1 : 0),	\
     ((__buf[(__pos-1)>>3] & (0x80 >> ((__pos-1)&7))) != 0))

#define DECODE_ADD_FF(b) ((b) += (b) + DECODE_BIT)

#define DECODE_ADD_00(b) DECODE_ADD_FF(b)

#define DECODE_DONE ENCODE_DONE

#define DECODE_PAUSE(b)	ENCODE_PAUSE(b)

#define ENCODE_SEEK(pos) 						\
  do {									\
    if ((((pos)>>3) >= __base) && ((((pos)+7)>>3) < (__base+__len)))   	\
      __pos = (pos)-(__base << 3); 					\
    else								\
      {									\
        ENCODE_FLUSH;							\
        __base = (pos) >> 3;						\
        __base = ((pos)  >> (__sft+3)) << __sft;			\
        fseek(__file, __base, 0);					\
        fread(__buf, 1, __len, __file);					\
        __pos = (pos)&7; 						\
        __pos = (pos)&((8 << __sft)-1);					\
      }									\
  } while(0)


#define DECODE_SEEK(pos) ENCODE_SEEK(pos)

#define ENCODE_TELL ((__base << 3) + __pos)

#define DECODE_TELL ENCODE_TELL




#endif
