/* * Copyright (c) 2021, kleines Filmröllchen . * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace AK { /// A stream wrapper class that allows you to read arbitrary amounts of bits /// in big-endian order from another stream. class BigEndianInputBitStream : public Stream { public: explicit BigEndianInputBitStream(MaybeOwned stream) : m_stream(move(stream)) { } // ^Stream virtual ErrorOr read_some(Bytes bytes) override { if (m_current_byte.has_value() && is_aligned_to_byte_boundary()) { bytes[0] = m_current_byte.release_value(); // FIXME: This accidentally slices off the first byte of the returned span. return m_stream->read_some(bytes.slice(1)); } align_to_byte_boundary(); return m_stream->read_some(bytes); } virtual ErrorOr write_some(ReadonlyBytes bytes) override { return m_stream->write_some(bytes); } virtual bool is_eof() const override { return m_stream->is_eof() && !m_current_byte.has_value(); } virtual bool is_open() const override { return m_stream->is_open(); } virtual void close() override { m_stream->close(); align_to_byte_boundary(); } ErrorOr read_bit() { return read_bits(1); } /// Depending on the number of bits to read, the return type can be chosen appropriately. /// This avoids a bunch of static_cast<>'s for the user. // TODO: Support u128, u256 etc. as well: The concepts would be quite complex. template ErrorOr read_bits(size_t count) { if constexpr (IsSame) { VERIFY(count == 1); } T result = 0; size_t nread = 0; while (nread < count) { if (m_current_byte.has_value()) { if constexpr (!IsSame && !IsSame) { // read as many bytes as possible directly if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) { // shift existing data over result <<= 8; result |= m_current_byte.value(); nread += 8; m_current_byte.clear(); } else { auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; result <<= 1; result |= bit; ++nread; if (m_bit_offset++ == 7) m_current_byte.clear(); } } else { // Always take this branch for booleans or u8: there's no purpose in reading more than a single bit auto const bit = (m_current_byte.value() >> (7 - m_bit_offset)) & 1; if constexpr (IsSame) result = bit; else { result <<= 1; result |= bit; } ++nread; if (m_bit_offset++ == 7) m_current_byte.clear(); } } else { m_current_byte = TRY(m_stream->read_value()); m_bit_offset = 0; } } return result; } /// Discards any sub-byte stream positioning the input stream may be keeping track of. /// Non-bitwise reads will implicitly call this. void align_to_byte_boundary() { m_current_byte.clear(); m_bit_offset = 0; } /// Whether we are (accidentally or intentionally) at a byte boundary right now. ALWAYS_INLINE bool is_aligned_to_byte_boundary() const { return m_bit_offset == 0; } private: Optional m_current_byte; size_t m_bit_offset { 0 }; MaybeOwned m_stream; }; /// A stream wrapper class that allows you to read arbitrary amounts of bits /// in little-endian order from another stream. class LittleEndianInputBitStream : public Stream { template static constexpr T lsb_mask(T bits) { constexpr auto max = NumericLimits::max(); constexpr auto digits = NumericLimits::digits(); return bits == 0 ? 0 : max >> (digits - bits); } public: explicit LittleEndianInputBitStream(MaybeOwned stream) : m_stream(move(stream)) { } // ^Stream virtual ErrorOr read_some(Bytes bytes) override { align_to_byte_boundary(); size_t bytes_read = 0; auto buffer = bytes; if (m_bit_count > 0) { auto bits_to_read = min(buffer.size() * bits_per_byte, m_bit_count); auto result = TRY(read_bits(bits_to_read)); bytes_read = bits_to_read / bits_per_byte; buffer.overwrite(0, &result, bytes_read); buffer = buffer.slice(bytes_read); } buffer = TRY(m_stream->read_some(buffer)); bytes_read += buffer.size(); return bytes.trim(bytes_read); } virtual ErrorOr write_some(ReadonlyBytes bytes) override { return m_stream->write_some(bytes); } virtual bool is_eof() const override { return m_stream->is_eof() && m_bit_count == 0; } virtual bool is_open() const override { return m_stream->is_open(); } virtual void close() override { m_stream->close(); align_to_byte_boundary(); } ErrorOr read_bit() { return read_bits(1); } /// Depending on the number of bits to read, the return type can be chosen appropriately. /// This avoids a bunch of static_cast<>'s for the user. // TODO: Support u128, u256 etc. as well: The concepts would be quite complex. template ErrorOr read_bits(size_t count) { auto result = TRY(peek_bits(count)); discard_previously_peeked_bits(count); return result; } template ErrorOr peek_bits(size_t count) { if (count > m_bit_count) TRY(refill_buffer_from_stream()); return lsb_aligned_buffer() & lsb_mask(min(count, m_bit_count)); } ALWAYS_INLINE void discard_previously_peeked_bits(u8 count) { m_bit_offset += count; m_bit_count -= count; } /// Discards any sub-byte stream positioning the input stream may be keeping track of. /// Non-bitwise reads will implicitly call this. u8 align_to_byte_boundary() { u8 remaining_bits = 0; m_bit_buffer = lsb_aligned_buffer(); m_bit_offset = 0; if (auto offset = m_bit_count % bits_per_byte; offset != 0) { remaining_bits = m_bit_buffer & lsb_mask(offset); discard_previously_peeked_bits(offset); } return remaining_bits; } /// Whether we are (accidentally or intentionally) at a byte boundary right now. ALWAYS_INLINE bool is_aligned_to_byte_boundary() const { return m_bit_count % bits_per_byte == 0; } private: using BufferType = u64; ALWAYS_INLINE BufferType lsb_aligned_buffer() const { return m_bit_offset == bit_buffer_size ? 0 : m_bit_buffer >> m_bit_offset; } ErrorOr refill_buffer_from_stream() { size_t bits_to_read = bit_buffer_size - m_bit_count; size_t bytes_to_read = bits_to_read / bits_per_byte; BufferType buffer = 0; auto bytes = TRY(m_stream->read_some({ &buffer, bytes_to_read })); m_bit_buffer = (buffer << m_bit_count) | lsb_aligned_buffer(); m_bit_count += bytes.size() * bits_per_byte; m_bit_offset = 0; return {}; } static constexpr size_t bits_per_byte = 8u; static constexpr size_t bit_buffer_size = sizeof(BufferType) * bits_per_byte; MaybeOwned m_stream; BufferType m_bit_buffer { 0 }; u8 m_bit_offset { 0 }; u8 m_bit_count { 0 }; }; /// A stream wrapper class that allows you to write arbitrary amounts of bits /// in big-endian order to another stream. class BigEndianOutputBitStream : public Stream { public: explicit BigEndianOutputBitStream(MaybeOwned stream) : m_stream(move(stream)) { } virtual ErrorOr read_some(Bytes) override { return Error::from_errno(EBADF); } virtual ErrorOr write_some(ReadonlyBytes bytes) override { VERIFY(m_bit_offset == 0); return m_stream->write_some(bytes); } template ErrorOr write_bits(T value, size_t bit_count) { VERIFY(m_bit_offset <= 7); while (bit_count > 0) { u8 next_bit = (value >> (bit_count - 1)) & 1; bit_count--; m_current_byte <<= 1; m_current_byte |= next_bit; m_bit_offset++; if (m_bit_offset > 7) { TRY(m_stream->write_value(m_current_byte)); m_bit_offset = 0; m_current_byte = 0; } } return {}; } virtual bool is_eof() const override { return true; } virtual bool is_open() const override { return m_stream->is_open(); } virtual void close() override { } size_t bit_offset() const { return m_bit_offset; } ErrorOr align_to_byte_boundary() { if (m_bit_offset == 0) return {}; TRY(write_bits(0u, 8 - m_bit_offset)); VERIFY(m_bit_offset == 0); return {}; } private: MaybeOwned m_stream; u8 m_current_byte { 0 }; size_t m_bit_offset { 0 }; }; /// A stream wrapper class that allows you to write arbitrary amounts of bits /// in little-endian order to another stream. class LittleEndianOutputBitStream : public Stream { public: explicit LittleEndianOutputBitStream(MaybeOwned stream) : m_stream(move(stream)) { } virtual ErrorOr read_some(Bytes) override { return Error::from_errno(EBADF); } virtual ErrorOr write_some(ReadonlyBytes bytes) override { VERIFY(m_bit_offset == 0); return m_stream->write_some(bytes); } template ErrorOr write_bits(T value, size_t bit_count) { VERIFY(m_bit_offset <= 7); size_t input_offset = 0; while (input_offset < bit_count) { u8 next_bit = (value >> input_offset) & 1; input_offset++; m_current_byte |= next_bit << m_bit_offset; m_bit_offset++; if (m_bit_offset > 7) { TRY(m_stream->write_value(m_current_byte)); m_bit_offset = 0; m_current_byte = 0; } } return {}; } virtual bool is_eof() const override { return true; } virtual bool is_open() const override { return m_stream->is_open(); } virtual void close() override { } size_t bit_offset() const { return m_bit_offset; } ErrorOr align_to_byte_boundary() { if (m_bit_offset == 0) return {}; TRY(write_bits(0u, 8 - m_bit_offset)); VERIFY(m_bit_offset == 0); return {}; } private: MaybeOwned m_stream; u8 m_current_byte { 0 }; size_t m_bit_offset { 0 }; }; }