Files

153 lines
6.2 KiB
C++

// ms-compress: implements Microsoft compression algorithms
// Copyright (C) 2012 Jeffrey Bush jeff@coderforlife.com
//
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
////////////////////////////// Bitstreams //////////////////////////////////////////////////////////
// A bitstream that allows either reading or writing, but not both at the same time.
// It reads uint16s for bits and 16 bits can be reliably read at a time.
// These are designed for speed and perform few checks. The burden of checking is on the caller.
// See the functions for assumptions they make that should be checked by the caller (asserts check
// these in the functions as well). Note that this->bits is >= 16 unless near the very end of the
// stream.
#ifndef MSCOMP_BITSTREAM_H
#define MSCOMP_BITSTREAM_H
#include "internal.h"
WARNINGS_PUSH()
WARNINGS_IGNORE_ASSIGNMENT_OPERATOR_NOT_GENERATED()
////////// Input Bitstream ////////////////////////////////////////////////////
class InputBitstream
{
private:
const_bytes in;
const const_bytes in_end;
uint32_t mask; // The next bits to be read/written in the bitstream
uint_fast8_t bits; // The number of bits in mask that are valid
public:
// Create an input bitstream
// Assumption: in != NULL && in_end - in >= 4
INLINE InputBitstream(const_bytes in, const const_bytes in_end) : in(in+4), in_end(in_end), mask((GET_UINT16(in) << 16) | GET_UINT16(in+2)), bits(32) { assert(in); assert(in_end - in >= 4); }
///// Basic Properties /////
FORCE_INLINE const_bytes RawStream() { return this->in; }
FORCE_INLINE uint_fast8_t AvailableBits() const { return this->bits; }
// Get the remaining number of raw bytes (disregards pre-read bits)
FORCE_INLINE size_t RemainingRawBytes() const { return this->in_end - this->in; }
///// Peeking Functions /////
// Peek at the next n bits of the stream
// Assumption: n <= 16 && n <= this->bits
FORCE_INLINE uint32_t Peek(const uint_fast8_t n) const { ASSERT_ALWAYS(n <= 16); assert(n <= this->bits); return (this->mask >> 16) >> (16 - n); } // we can't do a single shift because if n is 0 then a shift by 32 is undefined
// Check if all pre-read bits are 0, essentially Peek(AvailableBits()) == 0 (except that AvailableBits can be larger than 16, which Peek does not allow)
// If there are 0 pre-read bits, returns true
FORCE_INLINE bool MaskIsZero() const { return this->bits == 0 || (this->mask>>(32-this->bits)) == 0; }
///// Skipping Functions /////
// Skip the next n bits of the stream
// Assumption: n <= 16 && n <= this->bits
INLINE void Skip(const uint_fast8_t n)
{
ASSERT_ALWAYS(n <= 16); ASSERT_ALWAYS(n <= this->bits);
this->mask <<= n;
this->bits -= n;
if (this->bits < 16 && this->in + 2 <= this->in_end)
{
this->mask |= GET_UINT16(this->in) << (16 - this->bits);
this->bits |= 0x10; //this->bits += 16;
this->in += 2;
}
}
// Skip the next n bits of the stream without bounds checks
// Assumption: n <= 16
INLINE void Skip_Fast(const uint_fast8_t n)
{
ASSERT_ALWAYS(n <= 16);
this->mask <<= n;
this->bits -= n;
if (this->bits < 16)
{
this->mask |= GET_UINT16(this->in) << (16 - this->bits);
this->bits |= 0x10; //this->bits += 16;
this->in += 2;
}
}
///// Reading Functions /////
// Read the next n bits of the stream, where n <= 16 (essentially Peek(n); Skip(n))
// Assumption: n <= 16 && n <= this->bits
FORCE_INLINE uint32_t ReadBits(const uint_fast8_t n) { const uint32_t x = this->Peek(n); this->Skip(n); return x; }
///// Fast Reading Functions /////
// Equivalent to the reading functions but do not do bounds checks
// Read the next n bits of the stream, where n <= 16 (essentially Peek(n); Skip_Fast(n))
// Assumption: n <= 16
FORCE_INLINE uint32_t ReadBits_Fast(const uint_fast8_t n) { const uint32_t x = this->Peek(n); this->Skip_Fast(n); return x; }
///// Raw Reading Functions /////
// Get the next integer from the underlying stream, not the pre-read bits.
// These assume that this->RemainingRawBytes() >= sizeof(type)
FORCE_INLINE byte ReadRawByte() { assert(this->in + 1 <= this->in_end); return *this->in++; }
FORCE_INLINE uint16_t ReadRawUInt16() { assert(this->in + 2 <= this->in_end); const uint16_t x = GET_UINT16(this->in); this->in += 2; return x; }
FORCE_INLINE uint32_t ReadRawUInt32() { assert(this->in + 4 <= this->in_end); const uint32_t x = GET_UINT32(this->in); this->in += 4; return x; }
};
////////// Output Bitstream ///////////////////////////////////////////////////
class OutputBitstream
{
private:
bytes out;
uint16_t* pntr[2]; // the uint16's to write the data in mask to when there are enough bits
uint32_t mask; // The next bits to be read/written in the bitstream
uint_fast8_t bits; // The number of bits in mask that are valid
public:
INLINE OutputBitstream(bytes out) : out(out+4), mask(0), bits(0)
{
assert(out);
this->pntr[0] = (uint16_t*)(out);
this->pntr[1] = (uint16_t*)(out+2);
}
FORCE_INLINE bytes RawStream() { return this->out; }
INLINE void WriteBits(uint32_t b, uint_fast8_t n)
{
assert(n <= 16);
this->mask |= b << (32 - (this->bits += n));
if (this->bits > 16)
{
SET_UINT16(this->pntr[0], this->mask >> 16);
this->mask <<= 16;
this->bits &= 0xF; //this->bits -= 16;
this->pntr[0] = this->pntr[1];
this->pntr[1] = (uint16_t*)(this->out);
this->out += 2;
}
}
FORCE_INLINE void WriteRawByte(byte x) { *this->out++ = x; }
FORCE_INLINE void WriteRawUInt16(uint16_t x) { SET_UINT16(this->out, x); this->out += 2; }
FORCE_INLINE void WriteRawUInt32(uint32_t x) { SET_UINT32(this->out, x); this->out += 4; }
FORCE_INLINE void Finish()
{
SET_UINT16(this->pntr[0], this->mask >> 16); // if !bits then mask is 0 anyways
SET_UINT16_RAW(this->pntr[1], 0);
}
};
WARNINGS_POP()
#endif