############################################################################
#
#	File:     outbits.icn
#
#	Subject:  Procedure to write variable-length characters
#
#	Author:   Richard L. Goerwitz
#
#	Date:     November 3, 1991
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#	Version:  1.5
#
############################################################################
#
#  In any number of instances (e.g. when outputting variable-length
#  characters or fixed-length encoded strings), the programmer must
#  fit variable and/or non-byte-sized blocks into standard 8-bit
#  bytes.  Outbits() performs this task.
#
#  Pass to outbits(i, len) an integer i, and a length parameter (len),
#  and outbits will suspend byte-sized chunks of i converted to
#  characters (most significant bits first) until there is not enough
#  left of i to fill up an 8-bit character.  The remaining portion is
#  stored in a buffer until outbits() is called again, at which point
#  the buffer is combined with the new i and then output in the same
#  manner as before.  The buffer is flushed by calling outbits() with
#  a null i argument.  Note that len gives the number of bits there
#  are in i (or at least the number of bits you want preserved; those
#  that are discarded are the most significant ones). 
#
#  A trivial example of how outbits() might be used:
#
#      outtext := open("some.file.name","w")
#      l := [1,2,3,4]
#      every writes(outtext, outbits(!l,3))
#      writes(outtext, outbits(&null,3))           # flush buffer
#
#  List l may be reconstructed with inbits() (see inbits.icn):
#
#      intext := open("some.file.name")
#      l := []
#      while put(l, inbits(intext, 3))
#
#  Note that outbits() is a generator, while inbits() is not.
#
############################################################################
#
#  See also: inbits.icn
#
############################################################################

procedure outbits(i, len)

    local old_part, new_part, window, old_byte_mask
    static old_i, old_len, byte_length, byte_mask
    initial {
	old_i := old_len := 0
	byte_length := 8
	byte_mask := (2^byte_length)-1
    }

    old_byte_mask := (0 < 2^old_len - 1) | 0
    window := byte_length - old_len
    old_part := ishift(iand(old_i, old_byte_mask), window)

    # If we have a no-arg invocation, then flush buffer (old_i).
    if /i then {
	if old_len > 0 then {
	    old_i := old_len := 0
	    return char(old_part)
	} else {
	    old_i := old_len := 0
	    fail
	}
    } else {
	new_part := ishift(i, window-len)
	len -:= (len >= window) | {
	    old_len +:= len
	    old_i := ior(ishift(old_part, len-window), i)
	    fail
	}
#	For debugging purposes.
#	write("old_byte_mask = ", old_byte_mask)
#	write("window = ", image(window))
#	write("old_part = ", image(old_part))
#	write("new_part = ", image(new_part))
#	write("outputting ", image(ior(old_part, new_part)))
	suspend char(ior(old_part, new_part))
    }

    until len < byte_length do {
	suspend char(iand(ishift(i, byte_length-len), byte_mask))
	len -:= byte_length
    }

    old_len := len
    old_i := i
    fail

end
