OpenTTD Source 20250312-master-gcdcc6b491d
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 <http://www.gnu.org/licenses/>.
6 */
7
10#ifndef ENDIAN_BUFFER_HPP
11#define ENDIAN_BUFFER_HPP
12
13#include <string_view>
14#include "../core/bitmath_func.hpp"
15#include "../strings_type.h"
16
23template <typename Tcont = typename std::vector<uint8_t>, typename Titer = typename std::back_insert_iterator<Tcont>>
26 Titer buffer;
27
28public:
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 <<(const char *data) { return *this << std::string_view{ data }; }
35 EndianBufferWriter &operator <<(std::string_view data) { this->Write(data); return *this; }
36 EndianBufferWriter &operator <<(bool data) { return *this << static_cast<uint8_t>(data ? 1 : 0); }
37
38 template <typename... Targs>
39 EndianBufferWriter &operator <<(const std::tuple<Targs...> &data)
40 {
41 this->WriteTuple(data, std::index_sequence_for<Targs...>{});
42 return *this;
43 }
44
45 template <typename... Targs>
46 EndianBufferWriter &operator <<(const std::variant<Targs...> &variant)
47 {
48 this->WriteVariant(variant);
49 return *this;
50 }
51
52 EndianBufferWriter &operator <<(const std::monostate &)
53 {
54 return *this;
55 }
56
57 EndianBufferWriter &operator <<(const ConvertibleThroughBase auto data)
58 {
59 this->Write(data.base());
60 return *this;
61 }
62
63 template <class T> requires (!std::is_class_v<T>)
64 EndianBufferWriter &operator <<(const T data)
65 {
66 if constexpr (std::is_enum_v<T>) {
67 this->Write(to_underlying(data));
68 } else {
69 this->Write(data);
70 }
71 return *this;
72 }
73
74 template <typename Tvalue, typename Tbuf = std::vector<uint8_t>>
75 static Tbuf FromValue(const Tvalue &data)
76 {
77 Tbuf buffer;
78 EndianBufferWriter writer{ buffer };
79 writer << data;
80 return buffer;
81 }
82
83private:
85 template <class Ttuple, size_t... Tindices>
86 void WriteTuple(const Ttuple &values, std::index_sequence<Tindices...>)
87 {
88 ((*this << std::get<Tindices>(values)), ...);
89 }
90
91 template <typename T, std::size_t I = 0>
92 void WriteVariant(const T &variant )
93 {
94 if constexpr (I < std::variant_size_v<T>) {
95 if (I == variant.index()) {
96 static_assert(std::variant_size_v<T> < std::numeric_limits<uint8_t>::max());
97 this->Write(static_cast<uint8_t>(variant.index()));
98 *this << std::get<I>(variant);
99 return;
100 }
101
102 WriteVariant<T, I + 1>(variant);
103 }
104 }
105
107 void Write(std::string_view value)
108 {
109 for (auto c : value) {
110 this->buffer++ = c;
111 }
112 this->buffer++ = '\0';
113 }
114
116 template <class T>
117 void Write(T value)
118 {
119 static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
120
121 if constexpr (sizeof(T) > 1) {
122 this->buffer++ = GB(value, 0, 8);
123 this->buffer++ = GB(value, 8, 8);
124 if constexpr (sizeof(T) > 2) {
125 this->buffer++ = GB(value, 16, 8);
126 this->buffer++ = GB(value, 24, 8);
127 }
128 if constexpr (sizeof(T) > 4) {
129 this->buffer++ = GB(value, 32, 8);
130 this->buffer++ = GB(value, 40, 8);
131 this->buffer++ = GB(value, 48, 8);
132 this->buffer++ = GB(value, 56, 8);
133 }
134 } else {
135 this->buffer++ = value;
136 }
137 }
138};
139
148 std::span<const uint8_t> buffer;
150 size_t read_pos = 0;
151
152public:
153 EndianBufferReader(std::span<const uint8_t> buffer) : buffer(buffer) {}
154
155 void rewind() { this->read_pos = 0; }
156
157 EndianBufferReader &operator >>(std::string &data) { data = this->ReadStr(); return *this; }
158 EndianBufferReader &operator >>(EncodedString &data) { data = EncodedString{this->ReadStr()}; return *this; }
159 EndianBufferReader &operator >>(bool &data) { data = this->Read<uint8_t>() != 0; return *this; }
160
161 template <typename... Targs>
162 EndianBufferReader &operator >>(std::tuple<Targs...> &data)
163 {
164 this->ReadTuple(data, std::index_sequence_for<Targs...>{});
165 return *this;
166 }
167
168 template <typename... Targs>
169 EndianBufferReader &operator >>(std::variant<Targs...> &variant)
170 {
171 this->ReadVariant(this->Read<uint8_t>(), variant);
172 return *this;
173 }
174
175 EndianBufferReader &operator >>(const std::monostate &)
176 {
177 return *this;
178 }
179
180 template <ConvertibleThroughBase T>
181 EndianBufferReader &operator >>(T &data)
182 {
183 data = T{this->Read<typename T::BaseType>()};
184 return *this;
185 }
186
187 template <class T> requires (!std::is_class_v<T>)
188 EndianBufferReader &operator >>(T &data)
189 {
190 if constexpr (std::is_enum_v<T>) {
191 data = static_cast<T>(this->Read<std::underlying_type_t<T>>());
192 } else {
193 data = this->Read<T>();
194 }
195 return *this;
196 }
197
198 template <typename Tvalue>
199 static Tvalue ToValue(std::span<const uint8_t> buffer)
200 {
201 Tvalue result{};
202 EndianBufferReader reader{ buffer };
203 reader >> result;
204 return result;
205 }
206
207private:
209 template <class Ttuple, size_t... Tindices>
210 void ReadTuple(Ttuple &values, std::index_sequence<Tindices...>)
211 {
212 ((*this >> std::get<Tindices>(values)), ...);
213 }
214
215 template <typename T, std::size_t I = 0>
216 void ReadVariant(uint8_t index, T &variant)
217 {
218 if constexpr (I < std::variant_size_v<T>) {
219 if (I != index) {
220 ReadVariant<T, I + 1>(index, variant);
221 return;
222 }
223
224 std::variant_alternative_t<I, T> data;
225 *this >> data;
226 variant = data;
227 }
228 }
229
231 std::string ReadStr()
232 {
233 std::string str;
234 while (this->read_pos < this->buffer.size()) {
235 char ch = this->Read<char>();
236 if (ch == '\0') break;
237 str.push_back(ch);
238 }
239 return str;
240 }
241
243 template <class T>
245 {
246 static_assert(!std::is_const_v<T>, "Can't read into const variables");
247 static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
248
249 if (read_pos + sizeof(T) > this->buffer.size()) return {};
250
251 T value = static_cast<T>(this->buffer[this->read_pos++]);
252 if constexpr (sizeof(T) > 1) {
253 value += static_cast<T>(this->buffer[this->read_pos++]) << 8;
254 }
255 if constexpr (sizeof(T) > 2) {
256 value += static_cast<T>(this->buffer[this->read_pos++]) << 16;
257 value += static_cast<T>(this->buffer[this->read_pos++]) << 24;
258 }
259 if constexpr (sizeof(T) > 4) {
260 value += static_cast<T>(this->buffer[this->read_pos++]) << 32;
261 value += static_cast<T>(this->buffer[this->read_pos++]) << 40;
262 value += static_cast<T>(this->buffer[this->read_pos++]) << 48;
263 value += static_cast<T>(this->buffer[this->read_pos++]) << 56;
264 }
265
266 return value;
267 }
268};
269
270#endif /* ENDIAN_BUFFER_HPP */
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Container for an encoded string, created by GetEncodedString.
std::string string
The encoded string.
Endian-aware buffer adapter that always reads values in little endian order.
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.
A type is considered 'convertible through base()' when it has a 'base()' function that returns someth...
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:17