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/>.
8 /** @file endian_buffer.hpp Endian-aware buffer. */
10 #ifndef ENDIAN_BUFFER_HPP
11 #define ENDIAN_BUFFER_HPP
13 #include <string_view>
14 #include "../core/bitmath_func.hpp"
15 #include "../core/overflowsafe_type.hpp"
17 struct StrongTypedefBase
;
20 * Endian-aware buffer adapter that always writes values in little endian order.
21 * @note This class uses operator overloading (<<, just like streams) for writing
22 * as this allows providing custom operator overloads for more complex types
23 * like e.g. structs without needing to modify this class.
25 template <typename Tcont
= typename
std::vector
<byte
>, typename Titer
= typename
std::back_insert_iterator
<Tcont
>>
26 class EndianBufferWriter
{
27 /** Output iterator for the destination buffer. */
31 EndianBufferWriter(Titer buffer
) : buffer(buffer
) {}
32 EndianBufferWriter(typename
Titer::container_type
&container
) : buffer(std::back_inserter(container
)) {}
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<byte
>(data
? 1 : 0); }
40 EndianBufferWriter
&operator <<(const OverflowSafeInt
<T
> &data
) { return *this << static_cast<T
>(data
); };
42 template <typename
... Targs
>
43 EndianBufferWriter
&operator <<(const std::tuple
<Targs
...> &data
)
45 this->WriteTuple(data
, std::index_sequence_for
<Targs
...>{});
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
)
52 if constexpr (std::is_enum_v
<T
>) {
53 this->Write(static_cast<std::underlying_type_t
<const T
>>(data
));
54 } else if constexpr (std::is_base_of_v
<StrongTypedefBase
, T
>) {
55 this->Write(data
.base());
62 template <typename Tvalue
, typename Tbuf
= std::vector
<byte
>>
63 static Tbuf
FromValue(const Tvalue
&data
)
66 EndianBufferWriter writer
{ buffer
};
72 /** Helper function to write a tuple to the buffer. */
73 template<class Ttuple
, size_t... Tindices
>
74 void WriteTuple(const Ttuple
&values
, std::index_sequence
<Tindices
...>)
76 ((*this << std::get
<Tindices
>(values
)), ...);
79 /** Write overload for string values. */
80 void Write(std::string_view value
)
82 for (auto c
: value
) {
85 this->buffer
++ = '\0';
88 /** Fundamental write function. */
92 static_assert(sizeof(T
) <= 8, "Value can't be larger than 8 bytes");
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);
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);
108 this->buffer
++ = value
;
114 * Endian-aware buffer adapter that always reads values in little endian order.
115 * @note This class uses operator overloading (>>, just like streams) for reading
116 * as this allows providing custom operator overloads for more complex types
117 * like e.g. structs without needing to modify this class.
119 class EndianBufferReader
{
120 /** Reference to storage buffer. */
121 std::span
<const byte
> buffer
;
122 /** Current read position. */
126 EndianBufferReader(std::span
<const byte
> buffer
) : buffer(buffer
) {}
128 void rewind() { this->read_pos
= 0; }
130 EndianBufferReader
&operator >>(std::string
&data
) { data
= this->ReadStr(); return *this; }
131 EndianBufferReader
&operator >>(bool &data
) { data
= this->Read
<byte
>() != 0; return *this; }
133 template <typename T
>
134 EndianBufferReader
&operator >>(OverflowSafeInt
<T
> &data
) { data
= this->Read
<T
>(); return *this; };
136 template <typename
... Targs
>
137 EndianBufferReader
&operator >>(std::tuple
<Targs
...> &data
)
139 this->ReadTuple(data
, std::index_sequence_for
<Targs
...>{});
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
)
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
>();
151 data
= this->Read
<T
>();
156 template <typename Tvalue
>
157 static Tvalue
ToValue(std::span
<const byte
> buffer
)
160 EndianBufferReader reader
{ buffer
};
166 /** Helper function to read a tuple from the buffer. */
167 template<class Ttuple
, size_t... Tindices
>
168 void ReadTuple(Ttuple
&values
, std::index_sequence
<Tindices
...>)
170 ((*this >> std::get
<Tindices
>(values
)), ...);
173 /** Read overload for string data. */
174 std::string
ReadStr()
177 while (this->read_pos
< this->buffer
.size()) {
178 char ch
= this->Read
<char>();
179 if (ch
== '\0') break;
185 /** Fundamental read function. */
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");
192 if (read_pos
+ sizeof(T
) > this->buffer
.size()) return {};
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;
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;
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;
213 #endif /* ENDIAN_BUFFER_HPP */