OpenTTD Source 20260218-master-g2123fca5ea
endian_buffer.hpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#ifndef ENDIAN_BUFFER_HPP
11#define ENDIAN_BUFFER_HPP
12
13#include <string_view>
15#include "../strings_type.h"
16
23template <typename Tcont = typename std::vector<uint8_t>, typename Titer = typename std::back_insert_iterator<Tcont>>
24class EndianBufferWriter {
26 Titer buffer;
27
28public:
29 EndianBufferWriter(Titer buffer) : buffer(buffer) {}
30 EndianBufferWriter(typename Titer::container_type &container) : buffer(std::back_inserter(container)) {}
31
32 EndianBufferWriter &operator <<(const std::string &data) { return *this << std::string_view{ data }; }
33 EndianBufferWriter &operator <<(const EncodedString &data) { return *this << data.string; }
34 EndianBufferWriter &operator <<(std::string_view data) { this->Write(data); return *this; }
35 EndianBufferWriter &operator <<(bool data) { return *this << static_cast<uint8_t>(data ? 1 : 0); }
36
37 template <typename... Targs>
38 EndianBufferWriter &operator <<(const std::tuple<Targs...> &data)
39 {
40 this->WriteTuple(data, std::index_sequence_for<Targs...>{});
41 return *this;
42 }
43
44 template <typename... Targs>
45 EndianBufferWriter &operator <<(const std::variant<Targs...> &variant)
46 {
47 this->WriteVariant(variant);
48 return *this;
49 }
50
51 EndianBufferWriter &operator <<(const std::monostate &)
52 {
53 return *this;
54 }
55
56 EndianBufferWriter &operator <<(const ConvertibleThroughBase auto data)
57 {
58 this->Write(data.base());
59 return *this;
60 }
61
62 template <class T> requires (!std::is_class_v<T>)
63 EndianBufferWriter &operator <<(const T data)
64 {
65 if constexpr (std::is_enum_v<T>) {
66 this->Write(to_underlying(data));
67 } else {
68 this->Write(data);
69 }
70 return *this;
71 }
72
73 template <typename Tvalue, typename Tbuf = std::vector<uint8_t>>
74 static Tbuf FromValue(const Tvalue &data)
75 {
76 Tbuf buffer;
77 EndianBufferWriter writer{ buffer };
78 writer << data;
79 return buffer;
80 }
81
82private:
87 template <class Ttuple, size_t... Tindices>
88 void WriteTuple(const Ttuple &values, std::index_sequence<Tindices...>)
89 {
90 ((*this << std::get<Tindices>(values)), ...);
91 }
92
93 template <typename T, std::size_t I = 0>
94 void WriteVariant(const T &variant )
95 {
96 if constexpr (I < std::variant_size_v<T>) {
97 if (I == variant.index()) {
98 static_assert(std::variant_size_v<T> < std::numeric_limits<uint8_t>::max());
99 this->Write(static_cast<uint8_t>(variant.index()));
100 *this << std::get<I>(variant);
101 return;
102 }
103
104 WriteVariant<T, I + 1>(variant);
105 }
106 }
107
112 void Write(std::string_view value)
113 {
114 for (auto c : value) {
115 this->buffer++ = c;
116 }
117 this->buffer++ = '\0';
118 }
119
124 template <class T>
125 void Write(T value)
126 {
127 static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
128
129 if constexpr (sizeof(T) > 1) {
130 this->buffer++ = GB(value, 0, 8);
131 this->buffer++ = GB(value, 8, 8);
132 if constexpr (sizeof(T) > 2) {
133 this->buffer++ = GB(value, 16, 8);
134 this->buffer++ = GB(value, 24, 8);
135 }
136 if constexpr (sizeof(T) > 4) {
137 this->buffer++ = GB(value, 32, 8);
138 this->buffer++ = GB(value, 40, 8);
139 this->buffer++ = GB(value, 48, 8);
140 this->buffer++ = GB(value, 56, 8);
141 }
142 } else {
143 this->buffer++ = value;
144 }
145 }
146};
147
154class EndianBufferReader {
156 std::span<const uint8_t> buffer;
158 size_t read_pos = 0;
159
160public:
161 EndianBufferReader(std::span<const uint8_t> buffer) : buffer(buffer) {}
162
163 void rewind() { this->read_pos = 0; }
164
165 EndianBufferReader &operator >>(std::string &data) { data = this->ReadStr(); return *this; }
166 EndianBufferReader &operator >>(EncodedString &data) { data = EncodedString{this->ReadStr()}; return *this; }
167 EndianBufferReader &operator >>(bool &data) { data = this->Read<uint8_t>() != 0; return *this; }
168
169 template <typename... Targs>
170 EndianBufferReader &operator >>(std::tuple<Targs...> &data)
171 {
172 this->ReadTuple(data, std::index_sequence_for<Targs...>{});
173 return *this;
174 }
175
176 template <typename... Targs>
177 EndianBufferReader &operator >>(std::variant<Targs...> &variant)
178 {
179 this->ReadVariant(this->Read<uint8_t>(), variant);
180 return *this;
181 }
182
183 EndianBufferReader &operator >>(const std::monostate &)
184 {
185 return *this;
186 }
187
188 template <ConvertibleThroughBase T>
189 EndianBufferReader &operator >>(T &data)
190 {
191 data = T{this->Read<typename T::BaseType>()};
192 return *this;
193 }
194
195 template <class T> requires (!std::is_class_v<T>)
196 EndianBufferReader &operator >>(T &data)
197 {
198 if constexpr (std::is_enum_v<T>) {
199 data = static_cast<T>(this->Read<std::underlying_type_t<T>>());
200 } else {
201 data = this->Read<T>();
202 }
203 return *this;
204 }
205
206 template <typename Tvalue>
207 static Tvalue ToValue(std::span<const uint8_t> buffer)
208 {
209 Tvalue result{};
210 EndianBufferReader reader{ buffer };
211 reader >> result;
212 return result;
213 }
214
215private:
220 template <class Ttuple, size_t... Tindices>
221 void ReadTuple(Ttuple &values, std::index_sequence<Tindices...>)
222 {
223 ((*this >> std::get<Tindices>(values)), ...);
224 }
225
226 template <typename T, std::size_t I = 0>
227 void ReadVariant(uint8_t index, T &variant)
228 {
229 if constexpr (I < std::variant_size_v<T>) {
230 if (I != index) {
231 ReadVariant<T, I + 1>(index, variant);
232 return;
233 }
234
235 std::variant_alternative_t<I, T> data;
236 *this >> data;
237 variant = data;
238 }
239 }
240
245 std::string ReadStr()
246 {
247 std::string str;
248 while (this->read_pos < this->buffer.size()) {
249 char ch = this->Read<char>();
250 if (ch == '\0') break;
251 str.push_back(ch);
252 }
253 return str;
254 }
255
260 template <class T>
262 {
263 static_assert(!std::is_const_v<T>, "Can't read into const variables");
264 static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
265
266 if (read_pos + sizeof(T) > this->buffer.size()) return {};
267
268 T value = static_cast<T>(this->buffer[this->read_pos++]);
269 if constexpr (sizeof(T) > 1) {
270 value += static_cast<T>(this->buffer[this->read_pos++]) << 8;
271 }
272 if constexpr (sizeof(T) > 2) {
273 value += static_cast<T>(this->buffer[this->read_pos++]) << 16;
274 value += static_cast<T>(this->buffer[this->read_pos++]) << 24;
275 }
276 if constexpr (sizeof(T) > 4) {
277 value += static_cast<T>(this->buffer[this->read_pos++]) << 32;
278 value += static_cast<T>(this->buffer[this->read_pos++]) << 40;
279 value += static_cast<T>(this->buffer[this->read_pos++]) << 48;
280 value += static_cast<T>(this->buffer[this->read_pos++]) << 56;
281 }
282
283 return value;
284 }
285};
286
287#endif /* ENDIAN_BUFFER_HPP */
Functions related to bit mathematics.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
std::string string
The encoded string.
size_t read_pos
Current read position.
std::span< const uint8_t > buffer
Reference to storage buffer.
void ReadTuple(Ttuple &values, std::index_sequence< Tindices... >)
Helper function to read a tuple from the buffer.
T Read()
Fundamental read function.
std::string ReadStr()
Read overload for string data.
Endian-aware buffer adapter that always writes values in little endian order.
void WriteTuple(const Ttuple &values, std::index_sequence< Tindices... >)
Helper function to write a tuple to the buffer.
Titer buffer
Output iterator for the destination buffer.
void Write(T value)
Fundamental write function.
void Write(std::string_view value)
Write overload for string values.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Types related to strings.