RPNX::Compress
Self-contained C++20 compression and ZIP library
 
Loading...
Searching...
No Matches
io.hpp
Go to the documentation of this file.
1#ifndef RPNX_COMPRESSION_IMPLEMENTATION_IO_HPP
2#define RPNX_COMPRESSION_IMPLEMENTATION_IO_HPP
3
4#include <cstddef>
5#include <cstdint>
6#include <iterator>
7#include <optional>
8#include <type_traits>
9#include <utility>
10
11/**
12 * @file
13 * @brief Shared iterator, byte-conversion, and checksum primitives.
14 */
15
16/** @brief Internal primitives shared by the format-specific codecs. */
18{
19
20 /**
21 * @brief Converts one supported iterator value to std::byte.
22 * @tparam value_type std::byte or a one-byte integral type.
23 * @param value Value to convert without changing its bit pattern.
24 * @return The corresponding byte value.
25 */
26 template < typename value_type >
27 [[nodiscard]] constexpr std::byte to_byte(value_type value) noexcept
28 {
29 using plain_type = std::remove_cv_t< value_type >;
30 static_assert(std::is_same_v< plain_type, std::byte > || (std::is_integral_v< plain_type > && sizeof(plain_type) == 1U), "compression iterators must contain byte-sized values");
31 if constexpr (std::is_same_v< plain_type, std::byte >)
32 {
33 return value;
34 }
35 else
36 {
37 return static_cast< std::byte >(static_cast< std::uint8_t >(value));
38 }
39 }
40
41 /**
42 * @brief Writes one byte through an output iterator and advances it.
43 * @tparam output_iterator Iterator accepting std::byte or std::uint8_t.
44 * @param output Iterator to update in place.
45 * @param value Byte to write.
46 */
47 template < typename output_iterator >
48 void write_byte(output_iterator& output, std::byte value)
49 {
50 if constexpr (requires { *output = value; })
51 {
52 *output = value;
53 }
54 else
55 {
56 *output = std::to_integer< std::uint8_t >(value);
57 }
58 ++output;
59 }
60
61 /**
62 * @brief Single-pass byte reader over an input iterator and sentinel.
63 * @tparam input_iterator Input iterator yielding std::byte or one-byte integral values.
64 * @tparam sentinel Sentinel type for the input iterator.
65 *
66 * One byte of lookahead supports format-boundary checks without requiring a
67 * forward iterator. The consumed count excludes a byte held by peek().
68 */
69 template < std::input_iterator input_iterator, std::sentinel_for< input_iterator > sentinel >
71 {
72 public:
73 /**
74 * @brief Constructs a reader over an iterator range.
75 * @param first Iterator to the first byte.
76 * @param last Sentinel past the final byte.
77 */
78 byte_reader(input_iterator first, sentinel last) : m_current(std::move(first)), m_last(std::move(last))
79 {
80 }
81
82 /**
83 * @brief Tests whether no unread byte remains.
84 * @return true if neither lookahead nor the source contains another byte.
85 */
86 [[nodiscard]] bool empty() const
87 {
88 return !m_lookahead.has_value() && m_current == m_last;
89 }
90
91 /**
92 * @brief Inspects the next byte without consuming it.
93 * @param value Receives the next byte on success and is unchanged at end of input.
94 * @return true if a byte was available; false at end of input.
95 */
96 [[nodiscard]] bool peek(std::byte& value)
97 {
98 if (!m_lookahead.has_value())
99 {
100 if (m_current == m_last)
101 {
102 return false;
103 }
104 m_lookahead = to_byte(*m_current);
105 ++m_current;
106 }
107 value = *m_lookahead;
108 return true;
109 }
110
111 /**
112 * @brief Reads one byte.
113 * @param value Receives the byte on success and is unchanged at end of input.
114 * @return true if a byte was consumed; false at end of input.
115 */
116 [[nodiscard]] bool read(std::byte& value)
117 {
118 if (m_lookahead.has_value())
119 {
120 value = *m_lookahead;
121 m_lookahead.reset();
122 ++m_consumed;
123 return true;
124 }
125 if (m_current == m_last)
126 {
127 return false;
128 }
129 value = to_byte(*m_current);
130 ++m_current;
131 ++m_consumed;
132 return true;
133 }
134
135 /**
136 * @brief Reads one byte as an unsigned integer.
137 * @param value Receives the byte on success and is unchanged at end of input.
138 * @return true if a byte was consumed; false at end of input.
139 */
140 [[nodiscard]] bool read(std::uint8_t& value)
141 {
142 std::byte byte{};
143 if (!read(byte))
144 {
145 return false;
146 }
147 value = std::to_integer< std::uint8_t >(byte);
148 return true;
149 }
150
151 /**
152 * @brief Returns the number of bytes consumed from the source.
153 * @return Successful read count; a byte retained by peek() is not included.
154 */
155 [[nodiscard]] std::size_t consumed() const noexcept
156 {
157 return m_consumed;
158 }
159
160 private:
161 input_iterator m_current;
162 sentinel m_last;
163 std::optional< std::byte > m_lookahead;
164 std::size_t m_consumed = 0U;
165 };
166
167 /** @brief Incremental reflected IEEE CRC-32 accumulator. */
169 {
170 public:
171 /**
172 * @brief Includes one byte in the checksum.
173 * @param value Next byte in stream order.
174 */
175 void update(std::byte value) noexcept
176 {
177 m_checksum ^= std::to_integer< std::uint8_t >(value);
178 for (std::uint8_t bit = 0U; bit < 8U; ++bit)
179 {
180 std::uint32_t const mask = 0U - (m_checksum & 1U);
181 m_checksum = (m_checksum >> 1U) ^ (0xedb88320U & mask);
182 }
183 }
184
185 /**
186 * @brief Returns the checksum for all bytes supplied so far.
187 * @return Finalized reflected IEEE CRC-32 value.
188 */
189 [[nodiscard]] std::uint32_t value() const noexcept
190 {
191 return ~m_checksum;
192 }
193
194 private:
195 std::uint32_t m_checksum = 0xffffffffU;
196 };
197
198 /** @brief Incremental Adler-32 accumulator. */
200 {
201 public:
202 /**
203 * @brief Includes one byte in the checksum.
204 * @param value Next byte in stream order.
205 */
206 void update(std::byte value) noexcept
207 {
208 constexpr std::uint32_t modulus = 65521U;
209 m_first = (m_first + std::to_integer< std::uint8_t >(value)) % modulus;
210 m_second = (m_second + m_first) % modulus;
211 }
212
213 /**
214 * @brief Returns the checksum for all bytes supplied so far.
215 * @return Adler-32 value with the second sum in the high 16 bits.
216 */
217 [[nodiscard]] std::uint32_t value() const noexcept
218 {
219 return (m_second << 16U) | m_first;
220 }
221
222 private:
223 std::uint32_t m_first = 1U;
224 std::uint32_t m_second = 0U;
225 };
226
227} // namespace rpnx::compression::implementation
228
229#endif
Incremental Adler-32 accumulator.
Definition io.hpp:200
std::uint32_t value() const noexcept
Returns the checksum for all bytes supplied so far.
Definition io.hpp:217
void update(std::byte value) noexcept
Includes one byte in the checksum.
Definition io.hpp:206
bool peek(std::byte &value)
Inspects the next byte without consuming it.
Definition io.hpp:96
std::size_t consumed() const noexcept
Returns the number of bytes consumed from the source.
Definition io.hpp:155
byte_reader(input_iterator first, sentinel last)
Constructs a reader over an iterator range.
Definition io.hpp:78
bool read(std::uint8_t &value)
Reads one byte as an unsigned integer.
Definition io.hpp:140
bool empty() const
Tests whether no unread byte remains.
Definition io.hpp:86
bool read(std::byte &value)
Reads one byte.
Definition io.hpp:116
Incremental reflected IEEE CRC-32 accumulator.
Definition io.hpp:169
std::uint32_t value() const noexcept
Returns the checksum for all bytes supplied so far.
Definition io.hpp:189
void update(std::byte value) noexcept
Includes one byte in the checksum.
Definition io.hpp:175
Internal primitives shared by the format-specific codecs.
Definition io.hpp:18
void write_byte(output_iterator &output, std::byte value)
Writes one byte through an output iterator and advances it.
Definition io.hpp:48
constexpr std::byte to_byte(value_type value) noexcept
Converts one supported iterator value to std::byte.
Definition io.hpp:27