Orrery
A GPU-accelerated N-body gravitational simulator
Loading...
Searching...
No Matches
binary_stream.hpp
Go to the documentation of this file.
1#pragma once
2
36
37#include <array>
38#include <bit>
39#include <cstddef>
40#include <cstdint>
41#include <istream>
42#include <limits>
43#include <ostream>
44#include <span>
45#include <string>
46#include <string_view>
47#include <type_traits>
48
49#include "orrery/core/types.hpp"
50
51namespace orrery::sim {
52
53static_assert(std::numeric_limits<core::Real>::is_iec559,
54 "The binary formats store the bit pattern of a Real, which assumes IEEE 754");
55
63using RealBits = std::conditional_t<core::kSinglePrecision, std::uint32_t, std::uint64_t>;
64
65static_assert(sizeof(RealBits) == sizeof(core::Real));
66
68inline constexpr std::size_t kRealBytes = sizeof(core::Real);
69
74inline constexpr std::uint64_t kChecksumBasis = 0xcbf29ce484222325ULL;
75inline constexpr std::uint64_t kChecksumPrime = 0x00000100000001b3ULL;
76
78[[nodiscard]] constexpr std::uint64_t checksum_byte(std::uint64_t checksum,
79 std::byte value) noexcept {
80 return (checksum ^ static_cast<std::uint64_t>(value)) * kChecksumPrime;
81}
82
90class BinaryWriter {
91public:
92 explicit BinaryWriter(std::ostream& out) noexcept : out_(&out) {}
93
94 void write_u8(std::uint8_t value) { write_integer(value, 1); }
95
96 void write_u32(std::uint32_t value) { write_integer(value, 4); }
97
98 void write_u64(std::uint64_t value) { write_integer(value, 8); }
99
106 void write_real(core::Real value) { write_integer(std::bit_cast<RealBits>(value), kRealBytes); }
107
110 void write_reals(std::span<const core::Real> values) {
111 for (const core::Real value : values) {
112 write_real(value);
113 }
114 }
115
121 void write_bytes(std::string_view text);
122
124 void write_string(std::string_view text) {
125 write_u32(static_cast<std::uint32_t>(text.size()));
126 write_bytes(text);
127 }
128
130 [[nodiscard]] std::uint64_t checksum() const noexcept { return checksum_; }
131
139 [[nodiscard]] bool ok() const noexcept { return out_->good(); }
140
141private:
149 void write_integer(std::uint64_t value, std::size_t width) {
150 std::array<char, 8> bytes{};
151 for (std::size_t index = 0; index < width; ++index) {
152 const auto octet = static_cast<unsigned char>((value >> (8 * index)) & 0xffU);
153 bytes[index] = static_cast<char>(octet);
154 checksum_ = checksum_byte(checksum_, static_cast<std::byte>(octet));
155 }
156 out_->write(bytes.data(), static_cast<std::streamsize>(width));
157 }
158
159 std::ostream* out_;
160 std::uint64_t checksum_{kChecksumBasis};
161};
162
170class BinaryReader {
171public:
172 explicit BinaryReader(std::istream& in) noexcept : in_(&in) {}
173
174 [[nodiscard]] std::uint8_t read_u8() { return static_cast<std::uint8_t>(read_integer(1)); }
175
176 [[nodiscard]] std::uint32_t read_u32() { return static_cast<std::uint32_t>(read_integer(4)); }
177
178 [[nodiscard]] std::uint64_t read_u64() { return read_integer(8); }
179
180 [[nodiscard]] core::Real read_real() {
181 return std::bit_cast<core::Real>(static_cast<RealBits>(read_integer(kRealBytes)));
182 }
183
185 void read_reals(std::span<core::Real> values) {
186 for (core::Real& value : values) {
187 value = read_real();
188 }
189 }
190
192 [[nodiscard]] std::string read_bytes(std::size_t length);
193
201 [[nodiscard]] std::string read_string(std::size_t limit);
202
205 [[nodiscard]] std::uint64_t checksum() const noexcept { return checksum_; }
206
208 [[nodiscard]] bool ok() const noexcept { return in_->good(); }
209
210private:
211 [[nodiscard]] std::uint64_t read_integer(std::size_t width);
212
213 std::istream* in_;
214 std::uint64_t checksum_{kChecksumBasis};
215};
216
217} // namespace orrery::sim
std::conditional_t< core::kSinglePrecision, std::uint32_t, std::uint64_t > RealBits
An unsigned integer the same width as Real, which is what a bit pattern is carried in between this fi...
Definition binary_stream.hpp:63
constexpr std::uint64_t checksum_byte(std::uint64_t checksum, std::byte value) noexcept
Fold one byte into a running FNV-1a checksum.
Definition binary_stream.hpp:78
constexpr std::uint64_t kChecksumBasis
The offset basis and prime of 64-bit FNV-1a.
Definition binary_stream.hpp:74
constexpr std::size_t kRealBytes
How many bytes a Real occupies in both formats.
Definition binary_stream.hpp:68
std::uint64_t checksum() const noexcept
The checksum of everything read so far, comparable against the one the writer recorded.
Definition binary_stream.hpp:205
void read_reals(std::span< core::Real > values)
Fill values with that many consecutive Real.
Definition binary_stream.hpp:185
bool ok() const noexcept
Whether every read so far found the bytes it wanted.
Definition binary_stream.hpp:208
std::string read_string(std::size_t limit)
Read a 32-bit length followed by that many bytes.
std::string read_bytes(std::size_t length)
Read exactly length bytes.
bool ok() const noexcept
Whether every write so far reached the stream.
Definition binary_stream.hpp:139
void write_bytes(std::string_view text)
Write the exact bytes of text, with no length prefix and no terminator.
void write_real(core::Real value)
Write a Real as its IEEE 754 bit pattern, four bytes or eight according to the precision this build w...
Definition binary_stream.hpp:106
void write_reals(std::span< const core::Real > values)
Write a contiguous run of Real, which is how both formats carry particle components.
Definition binary_stream.hpp:110
void write_string(std::string_view text)
Write a 32-bit length followed by that many bytes.
Definition binary_stream.hpp:124
std::uint64_t checksum() const noexcept
The checksum of everything written so far.
Definition binary_stream.hpp:130
The scalar and index types that every layer of the project agrees on.