OpenTTD Source  20241121-master-g67a0fccfad
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 "../core/overflowsafe_type.hpp"
16 
17 struct StrongTypedefBase;
18 
25 template <typename Tcont = typename std::vector<uint8_t>, typename Titer = typename std::back_insert_iterator<Tcont>>
28  Titer buffer;
29 
30 public:
32  EndianBufferWriter(typename Titer::container_type &container) : buffer(std::back_inserter(container)) {}
33 
34  EndianBufferWriter &operator <<(const std::string &data) { return *this << std::string_view{ data }; }
35  EndianBufferWriter &operator <<(const char *data) { return *this << std::string_view{ data }; }
36  EndianBufferWriter &operator <<(std::string_view data) { this->Write(data); return *this; }
37  EndianBufferWriter &operator <<(bool data) { return *this << static_cast<uint8_t>(data ? 1 : 0); }
38 
39  template <typename T>
40  EndianBufferWriter &operator <<(const OverflowSafeInt<T> &data) { return *this << static_cast<T>(data); };
41 
42  template <typename... Targs>
43  EndianBufferWriter &operator <<(const std::tuple<Targs...> &data)
44  {
45  this->WriteTuple(data, std::index_sequence_for<Targs...>{});
46  return *this;
47  }
48 
49  template <class T, std::enable_if_t<std::disjunction_v<std::negation<std::is_class<T>>, std::is_base_of<StrongTypedefBase, T>>, int> = 0>
50  EndianBufferWriter &operator <<(const T data)
51  {
52  if constexpr (std::is_enum_v<T>) {
53  this->Write(to_underlying(data));
54  } else if constexpr (std::is_base_of_v<StrongTypedefBase, T>) {
55  this->Write(data.base());
56  } else {
57  this->Write(data);
58  }
59  return *this;
60  }
61 
62  template <typename Tvalue, typename Tbuf = std::vector<uint8_t>>
63  static Tbuf FromValue(const Tvalue &data)
64  {
65  Tbuf buffer;
66  EndianBufferWriter writer{ buffer };
67  writer << data;
68  return buffer;
69  }
70 
71 private:
73  template<class Ttuple, size_t... Tindices>
74  void WriteTuple(const Ttuple &values, std::index_sequence<Tindices...>)
75  {
76  ((*this << std::get<Tindices>(values)), ...);
77  }
78 
80  void Write(std::string_view value)
81  {
82  for (auto c : value) {
83  this->buffer++ = c;
84  }
85  this->buffer++ = '\0';
86  }
87 
89  template <class T>
90  void Write(T value)
91  {
92  static_assert(sizeof(T) <= 8, "Value can't be larger than 8 bytes");
93 
94  if constexpr (sizeof(T) > 1) {
95  this->buffer++ = GB(value, 0, 8);
96  this->buffer++ = GB(value, 8, 8);
97  if constexpr (sizeof(T) > 2) {
98  this->buffer++ = GB(value, 16, 8);
99  this->buffer++ = GB(value, 24, 8);
100  }
101  if constexpr (sizeof(T) > 4) {
102  this->buffer++ = GB(value, 32, 8);
103  this->buffer++ = GB(value, 40, 8);
104  this->buffer++ = GB(value, 48, 8);
105  this->buffer++ = GB(value, 56, 8);
106  }
107  } else {
108  this->buffer++ = value;
109  }
110  }
111 };
112 
121  std::span<const uint8_t> buffer;
123  size_t read_pos = 0;
124 
125 public:
126  EndianBufferReader(std::span<const uint8_t> buffer) : buffer(buffer) {}
127 
128  void rewind() { this->read_pos = 0; }
129 
130  EndianBufferReader &operator >>(std::string &data) { data = this->ReadStr(); return *this; }
131  EndianBufferReader &operator >>(bool &data) { data = this->Read<uint8_t>() != 0; return *this; }
132 
133  template <typename T>
134  EndianBufferReader &operator >>(OverflowSafeInt<T> &data) { data = this->Read<T>(); return *this; };
135 
136  template <typename... Targs>
137  EndianBufferReader &operator >>(std::tuple<Targs...> &data)
138  {
139  this->ReadTuple(data, std::index_sequence_for<Targs...>{});
140  return *this;
141  }
142 
143  template <class T, std::enable_if_t<std::disjunction_v<std::negation<std::is_class<T>>, std::is_base_of<StrongTypedefBase, T>>, int> = 0>
144  EndianBufferReader &operator >>(T &data)
145  {
146  if constexpr (std::is_enum_v<T>) {
147  data = static_cast<T>(this->Read<std::underlying_type_t<T>>());
148  } else if constexpr (std::is_base_of_v<StrongTypedefBase, T>) {
149  data = this->Read<typename T::BaseType>();
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 
165 private:
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>
187  T Read()
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 */
constexpr static debug_inline 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.
Overflow safe template for integers, i.e.
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
Non-templated base for StrongType::Typedef for use with type trait queries.