############################################################################
#
#	File:     bitstrm.icn
#
#	Subject:  Procedures to read and write strings of bits in files
#
#	Author:   Robert J. Alexander
#
#	Date:     August 14, 1996
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  Procedures for reading and writing integer values made up of an
#  arbitrary number of bits, stored without regard to character
#  boundaries.
#
############################################################################
#
#  Usage of BitStreamWrite, by example:
#
#       record bit_value(value, nbits)
#       ...
#       BitStreamWrite()                        #initialize
#       while value := get_new_value() do       # loop to output values
#               BitStreamWrite(outfile, value.nbits, value.value)
#       BitStreamWrite(outfile)                 # output any buffered bits
#
#  Note the interesting effect that BitStreamWrite(outproc), as well as
#  outputting the complete string, pads the output to an even character
#  boundary.  This can be dune during construction of a bit string if
#  the effect is desired.
#
#  The "value" argument defaults to zero.
#
############################################################################
#
#  Usage of BitStreamRead, by example:
#
#       BitStreamRead()
#       while value := BitStreamRead(infile, nbits) do
#               # do something with value
#
#  BitStringRead fails when too few bits remain to satisfy a request.
#
############################################################################
#
#  See also: bitstr.icn
#
############################################################################

procedure BitStreamWrite(outfile,bits,value,outproc)
    local outvalue
    static buffer,bufferbits
    #
    #  Initialize.
    #
    initial {
	 buffer := bufferbits := 0
	    }
    /outproc := writes
    #
    #  If this is "close" call, flush buffer and reinitialize.
    #
    if /value then {
	 outvalue := &null
	 if bufferbits > 0 then
		  outproc(outfile,char(outvalue := ishift(buffer,8 - bufferbits)))
	 buffer := bufferbits := 0
	 return outvalue
	    }
    #
    #  Merge new value into buffer.
    #
    buffer := ior(ishift(buffer,bits),value)
    bufferbits +:= bits
    #
    #  Output bits.
    #
    while bufferbits >= 8 do {
	 outproc(outfile,char(outvalue := ishift(buffer,8 - bufferbits)))
	 buffer := ixor(buffer,ishift(outvalue,bufferbits - 8))
	 bufferbits -:= 8
	    }
    return outvalue
end


procedure BitStreamRead(infile,bits,inproc)
    local value
    static buffer,bufferbits
    #
    #  Initialize.
    #
    initial {
	 buffer := bufferbits := 0
	    }
    #
    #  Reinitialize if called with no arguments.
    #
    if /infile then {
	 buffer := bufferbits := 0
	 return
	    }
    #
    #  Read in more data if necessary.
    #
    /inproc := reads
    while bufferbits < bits do {
	 buffer := ior(ishift(buffer,8),ord(inproc(infile))) | fail
	 bufferbits +:= 8
	    }
    #
    #  Extract value from buffer and return.
    #
    value := ishift(buffer,bits - bufferbits)
    buffer := ixor(buffer,ishift(value,bufferbits - bits))
    bufferbits -:= bits
    return value
end
