summaryrefslogtreecommitdiff
path: root/AK/BitStream.h
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2021-03-01 17:43:54 +0200
committerAndreas Kling <kling@serenityos.org>2021-03-13 20:07:25 +0100
commit0ddc0e45aec1f71f6c4604ad67cf36cda7181e2b (patch)
tree4e425961cfeeffdbf54b7a045a0969347046d1ee /AK/BitStream.h
parent5a0ebdb1096a2320fffdfdd46641600928238d78 (diff)
downloadserenity-0ddc0e45aec1f71f6c4604ad67cf36cda7181e2b.zip
AK: Add OutputBitStream class
This will be used in the deflate compressor.
Diffstat (limited to 'AK/BitStream.h')
-rw-r--r--AK/BitStream.h80
1 files changed, 80 insertions, 0 deletions
diff --git a/AK/BitStream.h b/AK/BitStream.h
index fbb26d2b5c..8e509dd5fe 100644
--- a/AK/BitStream.h
+++ b/AK/BitStream.h
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
+ * Copyright (c) 2021, Idan Horowitz <idan.horowitz@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -121,6 +122,85 @@ private:
InputStream& m_stream;
};
+class OutputBitStream final : public OutputStream {
+public:
+ explicit OutputBitStream(OutputStream& stream)
+ : m_stream(stream)
+ {
+ }
+
+ // WARNING: write aligns to the next byte boundary before writing, if unaligned writes are needed this should be rewritten
+ size_t write(ReadonlyBytes bytes) override
+ {
+ if (has_any_error())
+ return 0;
+ align_to_byte_boundary();
+ if (has_fatal_error()) // if align_to_byte_boundary failed
+ return 0;
+ return m_stream.write(bytes);
+ }
+
+ bool write_or_error(ReadonlyBytes bytes) override
+ {
+ if (write(bytes) < bytes.size()) {
+ set_fatal_error();
+ return false;
+ }
+ return true;
+ }
+
+ void write_bits(u32 bits, size_t count)
+ {
+ VERIFY(count <= 32);
+ size_t n_written = 0;
+ while (n_written < count) {
+ if (m_stream.has_any_error()) {
+ set_fatal_error();
+ return;
+ }
+
+ if (m_next_byte.has_value()) {
+ m_next_byte.value() |= ((bits >> n_written) & 1) << m_bit_offset;
+ ++n_written;
+
+ if (m_bit_offset++ == 7) {
+ m_stream << m_next_byte.value();
+ m_next_byte.clear();
+ }
+ } else {
+ m_bit_offset = 0;
+ m_next_byte = 0;
+ }
+ }
+ }
+
+ void write_bit(bool bit)
+ {
+ write_bits(bit, 1);
+ }
+
+ void align_to_byte_boundary()
+ {
+ if (m_next_byte.has_value()) {
+ if (!m_stream.write_or_error(ReadonlyBytes { &m_next_byte.value(), 1 })) {
+ set_fatal_error();
+ }
+ m_next_byte.clear();
+ }
+ }
+
+ size_t bit_offset() const
+ {
+ return m_bit_offset;
+ }
+
+private:
+ Optional<u8> m_next_byte;
+ size_t m_bit_offset { 0 };
+ OutputStream& m_stream;
+};
+
}
using AK::InputBitStream;
+using AK::OutputBitStream;