OpenTTD Source 20250205-master-gfd85ab1e2c
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
22template <typename Tcont = typename std::vector<uint8_t>, typename Titer = typename std::back_insert_iterator<Tcont>>
25 Titer buffer;
26
27public:
29 EndianBufferWriter(typename Titer::container_type &container) : buffer(std::back_inserter(container)) {}
30
31 EndianBufferWriter &operator <<(const std::string &data) { return *this << std::string_view{ data }; }
32 EndianBufferWriter &operator <<(const char *data) { return *this << std::string_view{ data }; }
33 EndianBufferWriter &operator <<(std::string_view data) { this->Write(data); return *this; }
34 EndianBufferWriter &operator <<(bool data) { return *this << static_cast<uint8_t>(data ? 1 : 0); }
35
36 template <typename... Targs>
37 EndianBufferWriter &operator <<(const std::tuple<Targs...> &data)
38 {
39 this->WriteTuple(data, std::index_sequence_for<Targs...>{});
40 return *this;
41 }
42
43 EndianBufferWriter &operator <<(const ConvertibleThroughBase auto data)
44 {
45 this->Write(data.base());
46 return *this;
47 }
48
49 template <class T> requires (!std::is_class_v<T>)
50 EndianBufferWriter &operator <<(const T data)
51 {
52 if constexpr (std::is_enum_v<T>) {
53 this->Write(to_underlying(data));
54 } else {
55 this->Write(data);
56 }
57 return *this;
58 }
59
60 template <typename Tvalue, typename Tbuf = std::vector<uint8_t>>
61 static Tbuf FromValue(const Tvalue &data)
62 {
63 Tbuf buffer;
64 EndianBufferWriter writer{ buffer };
65 writer << data;
66 return buffer;
67 }
68
69private:
71 template <class Ttuple, size_t... Tindices>
72 void WriteTuple(const Ttuple &values, std::index_sequence<Tindices...>)
73 {
74 ((*this << std::get<Tindices>(values)), ...);
75 }
76
78 void Write(std::string_view value)
79 {
80 for (auto c : value) {
81 this->buffer++ = c;
82 }
83 this->buffer++ = '\0';
84 }
85
87 template <class T>
88 void Write(T value)
89 {
90 static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
91
92 if constexpr (sizeof(T) > 1) {
93 this->buffer++ = GB(value, 0, 8);
94 this->buffer++ = GB(value, 8, 8);
95 if constexpr (sizeof(T) > 2) {
96 this->buffer++ = GB(value, 16, 8);
97 this->buffer++ = GB(value, 24, 8);
98 }
99 if constexpr (sizeof(T) > 4) {
100 this->buffer++ = GB(value, 32, 8);
101 this->buffer++ = GB(value, 40, 8);
102 this->buffer++ = GB(value, 48, 8);
103 this->buffer++ = GB(value, 56, 8);
104 }
105 } else {
106 this->buffer++ = value;
107 }
108 }
109};
110
119 std::span<const uint8_t> buffer;
121 size_t read_pos = 0;
122
123public:
124 EndianBufferReader(std::span<const uint8_t> buffer) : buffer(buffer) {}
125
126 void rewind() { this->read_pos = 0; }
127
128 EndianBufferReader &operator >>(std::string &data) { data = this->ReadStr(); return *this; }
129 EndianBufferReader &operator >>(bool &data) { data = this->Read<uint8_t>() != 0; return *this; }
130
131 template <typename... Targs>
132 EndianBufferReader &operator >>(std::tuple<Targs...> &data)
133 {
134 this->ReadTuple(data, std::index_sequence_for<Targs...>{});
135 return *this;
136 }
137
138 template <ConvertibleThroughBase T>
139 EndianBufferReader &operator >>(T &data)
140 {
141 data = T{this->Read<typename T::BaseType>()};
142 return *this;
143 }
144
145 template <class T> requires (!std::is_class_v<T>)
146 EndianBufferReader &operator >>(T &data)
147 {
148 if constexpr (std::is_enum_v<T>) {
149 data = static_cast<T>(this->Read<std::underlying_type_t<T>>());
150 } else {
151 data = this->Read<T>();
152 }
153 return *this;
154 }
155
156 template <typename Tvalue>
157 static Tvalue ToValue(std::span<const uint8_t> buffer)
158 {
159 Tvalue result{};
160 EndianBufferReader reader{ buffer };
161 reader >> result;
162 return result;
163 }
164
165private:
167 template <class Ttuple, size_t... Tindices>
168 void ReadTuple(Ttuple &values, std::index_sequence<Tindices...>)
169 {
170 ((*this >> std::get<Tindices>(values)), ...);
171 }
172
174 std::string ReadStr()
175 {
176 std::string str;
177 while (this->read_pos < this->buffer.size()) {
178 char ch = this->Read<char>();
179 if (ch == '\0') break;
180 str.push_back(ch);
181 }
182 return str;
183 }
184
186 template <class T>
188 {
189 static_assert(!std::is_const_v<T>, "Can't read into const variables");
190 static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
191
192 if (read_pos + sizeof(T) > this->buffer.size()) return {};
193
194 T value = static_cast<T>(this->buffer[this->read_pos++]);
195 if constexpr (sizeof(T) > 1) {
196 value += static_cast<T>(this->buffer[this->read_pos++]) << 8;
197 }
198 if constexpr (sizeof(T) > 2) {
199 value += static_cast<T>(this->buffer[this->read_pos++]) << 16;
200 value += static_cast<T>(this->buffer[this->read_pos++]) << 24;
201 }
202 if constexpr (sizeof(T) > 4) {
203 value += static_cast<T>(this->buffer[this->read_pos++]) << 32;
204 value += static_cast<T>(this->buffer[this->read_pos++]) << 40;
205 value += static_cast<T>(this->buffer[this->read_pos++]) << 48;
206 value += static_cast<T>(this->buffer[this->read_pos++]) << 56;
207 }
208
209 return value;
210 }
211};
212
213#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.
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:15