
#ifndef __BITS_CC__
#define __BITS_CC__

#include <algorithm>
#include <stdexcept>
#include <qpdf/QTC.hh>
#include <qpdf/Pipeline.hh>

// These functions may be run at places where the function call
// overhead from test coverage testing would be too high.  Therefore,
// we make the test coverage cases conditional upon a preprocessor
// symbol.  BitStream.cc includes this file without defining the
// symbol, and the specially designed test code that fully exercises
// this code includes with the symbol defined.

#ifdef BITS_READ
static unsigned long long
read_bits(unsigned char const*& p, size_t& bit_offset,
	  size_t& bits_available, size_t bits_wanted)
{
    // View p as a stream of bits:

    // 76543210 76543210 ....

    // bit_offset is the bit number within the first byte that marks
    // the first bit that we would read.

    if (bits_wanted > bits_available)
    {
	throw std::length_error("overflow reading bit stream");
    }
    if (bits_wanted > 32)
    {
	throw std::out_of_range("read_bits: too many bits requested");
    }

    unsigned long result = 0;
#ifdef BITS_TESTING
    if (bits_wanted == 0)
    {
	QTC::TC("libtests", "bits zero bits wanted");
    }
#endif
    while (bits_wanted > 0)
    {
	// Grab bits from the first byte clearing anything before
	// bit_offset.
	unsigned char byte = static_cast<unsigned char>(
            *p & ((1U << (bit_offset + 1U)) - 1U));

	// There are bit_offset + 1 bits available in the first byte.
	size_t to_copy = std::min(bits_wanted, bit_offset + 1);
	size_t leftover = (bit_offset + 1) - to_copy;

#ifdef BITS_TESTING
	QTC::TC("libtests", "bits bit_offset",
		((bit_offset == 0) ? 0 :
		 (bit_offset == 7) ? 1 :
		 2));
	QTC::TC("libtests", "bits leftover", (leftover > 0) ? 1 : 0);
#endif

	// Right shift so that all the bits we want are right justified.
	byte = static_cast<unsigned char>(byte >> leftover);

	// Copy the bits into result
	result <<= to_copy;
	result |= byte;

	// Update pointers
	if (leftover)
	{
	    bit_offset = leftover - 1;
	}
	else
	{
	    bit_offset = 7;
	    ++p;
	}
	bits_wanted -= to_copy;
	bits_available -= to_copy;

#ifdef BITS_TESTING
	QTC::TC("libtests", "bits iterations",
		((bits_wanted > 8) ? 0 :
		 (bits_wanted > 0) ? 1 :
		 2));
#endif
    }

    return result;
}
#endif

#ifdef BITS_WRITE
static void
write_bits(unsigned char& ch, size_t& bit_offset,
	   unsigned long long val, size_t bits, Pipeline* pipeline)
{
    if (bits > 32)
    {
	throw std::out_of_range("write_bits: too many bits requested");
    }

    // bit_offset + 1 is the number of bits left in ch
#ifdef BITS_TESTING
    if (bits == 0)
    {
	QTC::TC("libtests", "bits write zero bits");
    }
#endif
    while (bits > 0)
    {
	size_t bits_to_write = std::min(bits, bit_offset + 1);
	unsigned char newval = static_cast<unsigned char>(
	    (val >> (bits - bits_to_write)) & ((1U << bits_to_write) - 1));
	size_t bits_left_in_ch = bit_offset + 1 - bits_to_write;
	newval = static_cast<unsigned char>(newval << bits_left_in_ch);
	ch |= newval;
	if (bits_left_in_ch == 0)
	{
#ifdef BITS_TESTING
	    QTC::TC("libtests", "bits write pipeline");
#endif
	    pipeline->write(&ch, 1);
	    bit_offset = 7;
	    ch = 0;
	}
	else
	{
#ifdef BITS_TESTING
	    QTC::TC("libtests", "bits write leftover");
#endif
	    bit_offset -= bits_to_write;
	}
	bits -= bits_to_write;
#ifdef BITS_TESTING
	QTC::TC("libtests", "bits write iterations",
		((bits > 8) ? 0 :
		 (bits > 0) ? 1 :
		 2));
#endif
    }

}
#endif


#endif // __BITS_CC__
