The aim of this task is to write functions (or create a class if your
language is Object Oriented and you prefer) for reading and writing sequences of
bits, most significant bit first. While the output of a asciiprint "STRING" is the ASCII byte sequence
"S", "T", "R", "I", "N", "G", the output of a "print" of the bits sequence
0101011101010 (13 bits) must be 0101011101010; real I/O is performed always
quantized by byte (avoiding endianness issues and relying on underlying
buffering for performance), therefore you must obtain as output the bytes
0101 0111 0101 0000 (bold bits are padding bits), i.e. in hexadecimal 57 50.

As test, you can implement a rough (e.g. don't care about error handling or
other issues) compression/decompression program for ASCII sequences
of bytes, i.e. bytes for which the most significant bit is always unused, so that you can write
seven bits instead of eight (each 8 bytes of input, we write 7 bytes of output).

These bit oriented I/O functions can be used to implement compressors and
decompressors; e.g. Dynamic and Static Huffman encodings use variable length
bits sequences, while LZW (see LZW compression) use fixed or variable words
nine (or more) bits long.

 Limits in the maximum number of bits that can be written/read in a single read/write operation are allowed.
 Errors handling is not mandatory


without js -- (file i/o)
enum FN, V, BITS -- fields of a bitwiseioreader/writer
 
function new_bitwiseio(string filename, mode)
    integer fn = open(filename,mode)
    return {fn,0,0}     -- ie {FN,V=0,BITS=0}
end function
 
function new_bitwiseiowriter(string filename)
    return new_bitwiseio(filename,"wb")
end function
 
function new_bitwiseioreader(string filename)
    return new_bitwiseio(filename,"rb")
end function
 
function write_bits(sequence writer, integer v, bits)
    integer {fn,wv,wb} = writer,
            p2 = power(2,bits), ch
    if v!=and_bits(v,p2*2-1) then ?9/0 end if
    wv = wv*p2+v
    wb += bits
    while wb>=8 do
        wb -= 8
        p2 = power(2,wb)
        ch = floor(wv/p2)
        puts(fn,ch)
        wv -= ch*p2
    end while
    if wv>=#100 then ?9/0 end if
    if wb>=8 then ?9/0 end if
    writer[V]= wv
    writer[BITS]= wb
    return writer
end function
 
function close_bitwiseiowriter(sequence writer)
    integer {fn,wv,wb} = writer
    if wb then
        if wb>=8 then ?9/0 end if -- sanity check
        writer = write_bits(writer,0,8-wb)
    end if
    if writer[V]!=0 then ?9/0 end if
    if writer[BITS]!=0 then ?9/0 end if
    close(fn)
    writer[FN]=-1
    return writer
end function
 
function read_bits(sequence reader, integer bits)
    integer {fn,rv,rb} = reader, ch, p2
    while bits>rb do
        ch = getc(fn)
        if ch=-1 then return {-1,reader} end if
        rv = rv*#100+ch
        rb += 8
    end while
    rb -= bits
    p2 = power(2,rb)
    ch = floor(rv/p2)
    rv -= ch*p2
    reader[V]= rv
    reader[BITS]= rb
    return {ch,reader}
end function
 
function as_hexb(string s, fmt="%02x ")
-- helper funtion, returns hex string, or binary if fmt="%08b "
    string res = ""
    for i=1 to length(s) do
        res &= sprintf(fmt,s[i])
    end for 
    return trim(res)
end function
 
constant test = "This is a test."
--constant test = "This is a test"
--constant test = "abcdefghijk"
--constant test = "STRING"
--constant test = "This is an ascii string that will be crunched, written, read and expanded."
printf(1,"\"%s\" as bytes: %s (length %d)\n",{test,as_hexb(test),length(test)})
printf(1,"    original bits: %s\n",{as_hexb(test,"%08b ")})
 
sequence writer = new_bitwiseiowriter("test.bin")
for i=1 to length(test) do
    writer = write_bits(writer,test[i],7)
end for
writer = close_bitwiseiowriter(writer)
 
integer fn = open("test.bin","rb")
string bytes = get_text(fn,GT_WHOLE_FILE)
printf(1,"Written bitstream: %s\n",{as_hexb(bytes,"%08b ")})
printf(1,"Written bytes: %s (length %d)\n",{as_hexb(bytes),length(bytes)})
close(fn)
 
sequence reader = new_bitwiseioreader("test.bin")
bytes = ""
integer ch
while true do
    {ch,reader} = read_bits(reader,7)
    if ch=-1 then exit end if
    bytes &= ch
end while
printf(1,"\"%s\" as bytes: %s (length %d)\n",{bytes,as_hexb(bytes),length(bytes)})
