forked from openkylin/qpdf
151 lines
3.4 KiB
Plaintext
151 lines
3.4 KiB
Plaintext
|
|
#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__
|