123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- /*
- * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #pragma once
- #include <AK/ByteBuffer.h>
- #include <AK/Concepts.h>
- #include <AK/Error.h>
- #include <AK/NonnullOwnPtr.h>
- #include <AK/NonnullRefPtr.h>
- #include <AK/OwnPtr.h>
- #include <AK/Span.h>
- #include <AK/StdLibExtraDetails.h>
- #include <AK/Types.h>
- #include <LibCore/Stream.h>
- namespace Core::Stream {
- /// 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:
- static ErrorOr<NonnullOwnPtr<BigEndianInputBitStream>> construct(Handle<Stream> stream)
- {
- return adopt_nonnull_own_or_enomem<BigEndianInputBitStream>(new BigEndianInputBitStream(move(stream)));
- }
- // ^Stream
- virtual ErrorOr<Bytes> read(Bytes bytes) override
- {
- if (m_current_byte.has_value() && is_aligned_to_byte_boundary()) {
- bytes[0] = m_current_byte.release_value();
- return m_stream->read(bytes.slice(1));
- }
- align_to_byte_boundary();
- return m_stream->read(bytes);
- }
- virtual ErrorOr<size_t> write(ReadonlyBytes bytes) override { return m_stream->write(bytes); }
- virtual ErrorOr<void> write_entire_buffer(ReadonlyBytes bytes) override { return m_stream->write_entire_buffer(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<bool> read_bit()
- {
- return read_bits<bool>(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<Unsigned T = u64>
- ErrorOr<T> read_bits(size_t count)
- {
- if constexpr (IsSame<bool, T>) {
- VERIFY(count == 1);
- }
- T result = 0;
- size_t nread = 0;
- while (nread < count) {
- if (m_current_byte.has_value()) {
- if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) {
- // 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<bool, T>)
- result = bit;
- else {
- result <<= 1;
- result |= bit;
- }
- ++nread;
- if (m_bit_offset++ == 7)
- m_current_byte.clear();
- }
- } else {
- auto temp_buffer = TRY(ByteBuffer::create_uninitialized(1));
- TRY(m_stream->read(temp_buffer.bytes()));
- m_current_byte = temp_buffer[0];
- 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:
- BigEndianInputBitStream(Handle<Stream> stream)
- : m_stream(move(stream))
- {
- }
- Optional<u8> m_current_byte;
- size_t m_bit_offset { 0 };
- Handle<Stream> 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 {
- public:
- static ErrorOr<NonnullOwnPtr<LittleEndianInputBitStream>> construct(Handle<Stream> stream)
- {
- return adopt_nonnull_own_or_enomem<LittleEndianInputBitStream>(new LittleEndianInputBitStream(move(stream)));
- }
- LittleEndianInputBitStream(Handle<Stream> stream)
- : m_stream(move(stream))
- {
- }
- // ^Stream
- virtual ErrorOr<Bytes> read(Bytes bytes) override
- {
- if (m_current_byte.has_value() && is_aligned_to_byte_boundary()) {
- bytes[0] = m_current_byte.release_value();
- return m_stream->read(bytes.slice(1));
- }
- align_to_byte_boundary();
- return m_stream->read(bytes);
- }
- virtual ErrorOr<size_t> write(ReadonlyBytes bytes) override { return m_stream->write(bytes); }
- virtual ErrorOr<void> write_entire_buffer(ReadonlyBytes bytes) override { return m_stream->write_entire_buffer(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<bool> read_bit()
- {
- return read_bits<bool>(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<Unsigned T = u64>
- ErrorOr<T> read_bits(size_t count)
- {
- if constexpr (IsSame<bool, T>) {
- VERIFY(count == 1);
- }
- T result = 0;
- size_t nread = 0;
- while (nread < count) {
- if (m_current_byte.has_value()) {
- if constexpr (!IsSame<bool, T> && !IsSame<u8, T>) {
- // read as many bytes as possible directly
- if (((count - nread) >= 8) && is_aligned_to_byte_boundary()) {
- // shift existing data over
- result |= (m_current_byte.value() << nread);
- nread += 8;
- m_current_byte.clear();
- } else {
- auto const bit = (m_current_byte.value() >> m_bit_offset) & 1;
- result |= (bit << nread);
- ++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() >> m_bit_offset) & 1;
- if constexpr (IsSame<bool, T>)
- result = bit;
- else
- result |= (bit << nread);
- ++nread;
- if (m_bit_offset++ == 7)
- m_current_byte.clear();
- }
- } else {
- auto temp_buffer = TRY(ByteBuffer::create_uninitialized(1));
- auto read_bytes = TRY(m_stream->read(temp_buffer.bytes()));
- if (read_bytes.is_empty())
- return Error::from_string_literal("eof");
- m_current_byte = temp_buffer[0];
- 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.
- u8 align_to_byte_boundary()
- {
- u8 remaining_bits = m_current_byte.value_or(0) >> m_bit_offset;
- m_current_byte.clear();
- m_bit_offset = 0;
- 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_offset == 0; }
- private:
- Optional<u8> m_current_byte;
- size_t m_bit_offset { 0 };
- Handle<Stream> m_stream;
- };
- }
|