NGen
Loading...
Searching...
No Matches
byte_io.hpp
1/*
2Author: Nels Frazier
3Copyright (C) 2025 Lynker
4------------------------------------------------------------------------
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16------------------------------------------------------------------------
17INTERNAL — implementation detail of wire_format::RecordPrefix.
18
19Names the byte-level primitives `wire_format` uses, in one place:
20
21 * Buffer-level: `store_little_uXX` / `load_little_uXX` (and the
22 signed `sXX` counterparts) act on `uint8_t*` buffers and compute
23 values arithmetically from byte sequences.
24 This ensures the wire-format's cross-host portability: same bytes on disk from any
25 host, correct numeric values when read on any host.
26
27 * `store_little_u8` / `load_little_u8` are single-byte trivials defined here.
28 These are just for consistency.
29
30 * Stream adapters (`write_uXX_le(ostream, v)`,
31 `read_uXX_le(istream, v_out)`) wrap the buffer primitives for
32 callers that work with `std::ostream` / `std::istream`.
33 These encapsulate the stack-buffer-then-cast-then-write pattern
34 so the `reinterpret_cast<char*>(uint8_t*)` bridge between the
35 primitives' byte type and the iostream `char*` API is in one
36 place.
37
38Signed-integer encoding inherits boost::endian's two's-complement
39assumption ([1], [2]). C++20 mandates two's-complement, so the
40run-host canary tests in byte_io_Test.cpp are formalities on any
41conforming toolchain — kept as a safeguard against nonconforming
42toolchains and possible ports to older standards.
43
44 [1] versioned (matches the Boost we link against):
45 https://www.boost.org/doc/libs/1_86_0/libs/endian/doc/html/endian.html
46 [2] latest release (in case the versioned page diverges):
47 https://www.boost.org/doc/libs/release/libs/endian/doc/html/endian.html
48
49Visibility is enforced by include-path discipline: this header is
50only on the PRIVATE include path of the `ngen_bmi_protocols` library's
51own translation units and of the `test_bmi_protocols` test target. It
52is never exported via `target_include_directories(PUBLIC ...)`.
53*/
54#pragma once
55
56#include <boost/endian/conversion.hpp>
57
58#include <cstdint>
59#include <istream>
60#include <ostream>
61#include <type_traits>
62
63namespace models {
64namespace bmi {
65namespace protocols {
66namespace serialization {
67namespace byte_io {
68
69// Compile-time guard for the `reinterpret_cast<char*>(uint8_t*)`
70// bridge in the stream adapters below. The standard does not
71// mandate that `std::uint8_t` alias `unsigned char` — it only
72// requires that the type exist, be unsigned, and be exactly 8
73// bits. In most scenarios the two are aliases, and the
74// reinterpret_cast is well-defined via the standard's
75// `char`/`unsigned char` aliasing rules. The assertion below
76// catches any future target where the alias does not hold.
77static_assert(
78 std::is_same<std::uint8_t, unsigned char>::value,
79 "byte_io: std::uint8_t must alias unsigned char on this "
80 "target — the stream adapters' reinterpret_cast<char*> "
81 "bridge depends on it. See the comment immediately above "
82 "this assertion for the rationale."
83);
84
85// ============================================================
86// Buffer encode/decode primitives needed from boost::endian
87// Using the convience wrappers around their template load/store.
88// ============================================================
89
90using boost::endian::load_little_s32;
91using boost::endian::load_little_s64;
92using boost::endian::load_little_u16;
93using boost::endian::load_little_u32;
94using boost::endian::load_little_u64;
95using boost::endian::store_little_s32;
96using boost::endian::store_little_s64;
97using boost::endian::store_little_u16;
98using boost::endian::store_little_u32;
99using boost::endian::store_little_u64;
100
101// Added for consistency, these are essentially noops
102inline void store_little_u8(uint8_t* p, uint8_t v) {
103 p[0] = v;
104}
105
106inline uint8_t load_little_u8(const uint8_t* p) {
107 return p[0];
108}
109
110// ============================================================
111// Stream adapters — wrap the buffer primitives for callers that
112// work with iostream rather than raw byte buffers.
113//
114// Each adapter contains the `reinterpret_cast<char*>(uint8_t*)`
115// that bridges the buffer primitives' byte type to the iostream
116// API. The cast is guarded by the static_assert at the top of
117// this header.
118// ============================================================
119
120inline void write_u8(std::ostream& out, uint8_t v) {
121 uint8_t buf[1];
122 store_little_u8(buf, v);
123 out.write(reinterpret_cast<const char*>(buf), 1);
124}
125
126inline bool read_u8(std::istream& in, uint8_t& v) {
127 uint8_t buf[1];
128 if (!in.read(reinterpret_cast<char*>(buf), 1)) return false;
129 if (in.gcount() != 1) return false;
130 v = load_little_u8(buf);
131 return true;
132}
133
134inline void write_u16_le(std::ostream& out, uint16_t v) {
135 uint8_t buf[2];
136 store_little_u16(buf, v);
137 out.write(reinterpret_cast<const char*>(buf), 2);
138}
139
140inline bool read_u16_le(std::istream& in, uint16_t& v) {
141 uint8_t buf[2];
142 if (!in.read(reinterpret_cast<char*>(buf), 2)) return false;
143 if (in.gcount() != 2) return false;
144 v = load_little_u16(buf);
145 return true;
146}
147
148inline void write_u32_le(std::ostream& out, uint32_t v) {
149 uint8_t buf[4];
150 store_little_u32(buf, v);
151 out.write(reinterpret_cast<const char*>(buf), 4);
152}
153
154inline bool read_u32_le(std::istream& in, uint32_t& v) {
155 uint8_t buf[4];
156 if (!in.read(reinterpret_cast<char*>(buf), 4)) return false;
157 if (in.gcount() != 4) return false;
158 v = load_little_u32(buf);
159 return true;
160}
161
162inline void write_u64_le(std::ostream& out, uint64_t v) {
163 uint8_t buf[8];
164 store_little_u64(buf, v);
165 out.write(reinterpret_cast<const char*>(buf), 8);
166}
167
168inline bool read_u64_le(std::istream& in, uint64_t& v) {
169 uint8_t buf[8];
170 if (!in.read(reinterpret_cast<char*>(buf), 8)) return false;
171 if (in.gcount() != 8) return false;
172 v = load_little_u64(buf);
173 return true;
174}
175
176inline void write_i32_le(std::ostream& out, int32_t v) {
177 uint8_t buf[4];
178 store_little_s32(buf, v);
179 out.write(reinterpret_cast<const char*>(buf), 4);
180}
181
182inline bool read_i32_le(std::istream& in, int32_t& v) {
183 uint8_t buf[4];
184 if (!in.read(reinterpret_cast<char*>(buf), 4)) return false;
185 if (in.gcount() != 4) return false;
186 v = load_little_s32(buf);
187 return true;
188}
189
190inline void write_i64_le(std::ostream& out, int64_t v) {
191 uint8_t buf[8];
192 store_little_s64(buf, v);
193 out.write(reinterpret_cast<const char*>(buf), 8);
194}
195
196inline bool read_i64_le(std::istream& in, int64_t& v) {
197 uint8_t buf[8];
198 if (!in.read(reinterpret_cast<char*>(buf), 8)) return false;
199 if (in.gcount() != 8) return false;
200 v = load_little_s64(buf);
201 return true;
202}
203
204} // namespace byte_io
205} // namespace serialization
206} // namespace protocols
207} // namespace bmi
208} // namespace models
bool read_u32_le(std::istream &in, uint32_t &v)
Definition byte_io.hpp:154
bool read_u64_le(std::istream &in, uint64_t &v)
Definition byte_io.hpp:168
bool read_i64_le(std::istream &in, int64_t &v)
Definition byte_io.hpp:196
bool read_i32_le(std::istream &in, int32_t &v)
Definition byte_io.hpp:182
void write_i64_le(std::ostream &out, int64_t v)
Definition byte_io.hpp:190
void write_u32_le(std::ostream &out, uint32_t v)
Definition byte_io.hpp:148
uint8_t load_little_u8(const uint8_t *p)
Definition byte_io.hpp:106
void store_little_u8(uint8_t *p, uint8_t v)
Definition byte_io.hpp:102
void write_u8(std::ostream &out, uint8_t v)
Definition byte_io.hpp:120
void write_u16_le(std::ostream &out, uint16_t v)
Definition byte_io.hpp:134
bool read_u8(std::istream &in, uint8_t &v)
Definition byte_io.hpp:126
void write_u64_le(std::ostream &out, uint64_t v)
Definition byte_io.hpp:162
bool read_u16_le(std::istream &in, uint16_t &v)
Definition byte_io.hpp:140
void write_i32_le(std::ostream &out, int32_t v)
Definition byte_io.hpp:176
Definition deserialization.hpp:40
Definition AbstractCLibBmiAdapter.hpp:6