Fix #10490: Allow ships to exit depots if another is not moving at the exit point...
[openttd-github.git] / src / misc / endian_buffer.hpp
blob3229029c53569b04d653bf7548caf3efa3b8014b
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 */
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;
19 /**
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. */
28 Titer buffer;
30 public:
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); }
39 template <typename T>
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...>{});
46 return *this;
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());
56 } else {
57 this->Write(data);
59 return *this;
62 template <typename Tvalue, typename Tbuf = std::vector<byte>>
63 static Tbuf FromValue(const Tvalue &data)
65 Tbuf buffer;
66 EndianBufferWriter writer{ buffer };
67 writer << data;
68 return buffer;
71 private:
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) {
83 this->buffer++ = c;
85 this->buffer++ = '\0';
88 /** Fundamental write function. */
89 template <class T>
90 void Write(T value)
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);
107 } else {
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. */
123 size_t read_pos = 0;
125 public:
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...>{});
140 return *this;
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>();
150 } else {
151 data = this->Read<T>();
153 return *this;
156 template <typename Tvalue>
157 static Tvalue ToValue(std::span<const byte> buffer)
159 Tvalue result{};
160 EndianBufferReader reader{ buffer };
161 reader >> result;
162 return result;
165 private:
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()
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);
182 return str;
185 /** Fundamental read function. */
186 template <class T>
187 T Read()
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;
209 return value;
213 #endif /* ENDIAN_BUFFER_HPP */