Mercurial > code
annotate C++/Pack.h @ 295:345aaeb5e0ba
Pack: fix constexpr can't be used on void return types
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 13 Nov 2014 21:07:48 +0100 |
parents | 7433ebe6a8b0 |
children | 24085fae3162 |
rev | line source |
---|---|
185 | 1 /* |
2 * Pack.h -- binary data serialization | |
3 * | |
4 * Copyright (c) 2013 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #ifndef _PACK_H_ | |
20 #define _PACK_H_ | |
21 | |
22 #include <cstdint> | |
23 #include <fstream> | |
290 | 24 #include <memory> |
25 #include <sstream> | |
185 | 26 #include <string> |
27 | |
28 /** | |
29 * @class Pack | |
30 * @brief Serialize binary data to files | |
31 * | |
32 * This class write and read binary data from files. It currently | |
33 * support: | |
34 * uint8_t, | |
35 * uint16_t, | |
36 * uint32_t, | |
37 * uint64_t | |
38 */ | |
39 class Pack { | |
290 | 40 private: |
41 template <typename T> | |
42 struct IsContainer | |
43 { | |
44 using Yes = char [1]; | |
45 using No = char [2]; | |
46 | |
47 template <typename U> | |
48 static Yes &test(typename U::value_type *); | |
49 | |
50 template <typename U> | |
51 static No &test(...); | |
52 | |
53 static constexpr const bool value = sizeof (test<T>(0)) == sizeof (Yes); | |
54 }; | |
55 | |
56 friend class PackWriter; | |
57 friend class PackReader; | |
58 | |
185 | 59 public: |
60 /** | |
61 * @enum Endian | |
62 * @brief Endian mode | |
63 */ | |
64 enum Endian { | |
65 Little, //! Little endian | |
66 Big //! Big endian | |
67 }; | |
68 | |
69 public: | |
219 | 70 /** |
71 * Host system endian mode. | |
72 */ | |
185 | 73 static const Endian mode; |
74 | |
75 /** | |
76 * @struct TypeInfo | |
77 * @brief Type information | |
78 * | |
79 * Used for conversions. | |
80 */ | |
81 template <typename T> | |
82 struct TypeInfo { | |
290 | 83 static constexpr const bool convertible{false}; |
84 static constexpr const bool serializable{false}; | |
85 }; | |
86 | |
87 /** | |
88 * Helper to mark a specialization convertible. | |
89 * | |
90 * Already done for: | |
91 * uint8_t | |
92 * uint16_t | |
93 * uint32_t | |
94 * uint64_t | |
95 * | |
96 * The specialization must have the following function: | |
97 * | |
98 * static constexpr void convert(T &value) noexcept | |
99 */ | |
100 struct Convertible { | |
101 static constexpr const bool convertible{true}; | |
102 }; | |
103 | |
104 /** | |
105 * Helper to mark a specialization serializable. | |
106 * | |
107 * The specialisation must have the following functions: | |
108 * | |
109 * static void serialize(PackWriter &writer, const T &) | |
110 * static void unserialize(PackReader &reader, T &) | |
111 */ | |
112 struct Serializable { | |
113 static constexpr const bool serializable{true}; | |
185 | 114 }; |
115 | |
116 /** | |
290 | 117 * Convert data inplace. |
185 | 118 * |
290 | 119 * @param value the value |
185 | 120 */ |
121 template <typename T> | |
290 | 122 static inline void convert(T &value) noexcept |
266 | 123 { |
290 | 124 static_assert(TypeInfo<T>::convertible, "unsupported type"); |
125 | |
126 TypeInfo<T>::convert(value); | |
127 } | |
128 }; | |
129 | |
130 /** | |
131 * @class PackReader | |
132 * @brief Base abstract reader class | |
133 */ | |
134 class PackReader { | |
135 protected: | |
136 Pack::Endian m_endian; | |
137 | |
138 PackReader(Pack::Endian endian); | |
139 | |
140 virtual std::istream &stream() = 0; | |
266 | 141 |
290 | 142 public: |
143 /** | |
144 * Default destructor. | |
145 */ | |
146 virtual ~PackReader() = default; | |
266 | 147 |
290 | 148 /** |
149 * Read a primitive convertible type. | |
150 * | |
151 * @param value the value destination | |
152 * @return *this | |
153 */ | |
154 template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> | |
155 PackReader &operator>>(T &value) | |
156 { | |
157 stream().read(reinterpret_cast<char *>(&value), sizeof (T)); | |
158 | |
159 if (m_endian != Pack::mode) | |
160 Pack::convert(value); | |
161 | |
162 return *this; | |
266 | 163 } |
164 | |
165 /** | |
290 | 166 * Read a serializable type. |
266 | 167 * |
290 | 168 * @param value the value destination |
169 * @return *this | |
266 | 170 */ |
290 | 171 template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> |
172 PackReader &operator>>(T &value) | |
266 | 173 { |
290 | 174 Pack::TypeInfo<T>::unserialize(*this, value); |
266 | 175 |
290 | 176 return *this; |
185 | 177 } |
178 | |
179 /** | |
290 | 180 * Read an array. |
181 * | |
182 * This operator is a little bit tricky because you don't know in | |
183 * advance how much data you want to read. Because of that, this | |
184 * function looks the capacity of the container and reads that number | |
185 * of data. | |
266 | 186 * |
290 | 187 * Because it looks for capacity, you can't use a container which |
188 * already have some data, they will be overriden. | |
189 * | |
190 * If this is a concern, you should roll your own loop to fill up | |
191 * your container. | |
192 * | |
193 * @param container the container (all previous data will be lost) | |
194 * @return *this | |
266 | 195 */ |
290 | 196 template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> |
197 PackReader &operator>>(T &container) | |
266 | 198 { |
290 | 199 typename T::value_type v; |
200 | |
201 T copy; | |
202 | |
203 for (size_t i = 0; i < container.capacity(); ++i) { | |
204 (*this) >> v; | |
205 copy.push_back(v); | |
206 } | |
207 | |
208 container = std::move(copy); | |
209 | |
210 return *this; | |
211 } | |
212 }; | |
213 | |
214 /** | |
215 * @class PackWriter | |
216 * @brief Base abstract writer class | |
217 */ | |
218 class PackWriter { | |
219 protected: | |
220 Pack::Endian m_endian; | |
221 | |
222 PackWriter(Pack::Endian endian); | |
223 | |
224 virtual std::ostream &stream() = 0; | |
225 | |
226 public: | |
227 /** | |
228 * Default destructor. | |
229 */ | |
230 virtual ~PackWriter() = default; | |
231 | |
232 /** | |
233 * Write a convertible type to the stream. | |
234 * | |
235 * @param value the value | |
236 * @return *this | |
237 */ | |
238 template <typename T, typename std::enable_if<Pack::TypeInfo<T>::convertible>::type * = nullptr> | |
239 PackWriter &operator<<(T value) | |
240 { | |
241 if (m_endian != Pack::mode) | |
242 Pack::convert(value); | |
243 | |
244 stream().write(reinterpret_cast<const char *>(&value), sizeof (T)); | |
245 | |
246 return *this; | |
247 } | |
248 | |
249 /** | |
250 * Write a serializable type to the stream. | |
251 * | |
252 * @param value the value | |
253 * @return *this | |
254 */ | |
255 template <typename T, typename std::enable_if<Pack::TypeInfo<T>::serializable>::type * = nullptr> | |
256 PackWriter &operator<<(const T &value) | |
257 { | |
258 Pack::TypeInfo<T>::serialize(*this, value); | |
259 | |
260 return *this; | |
266 | 261 } |
262 | |
263 /** | |
290 | 264 * Write a container to the stream. |
185 | 265 * |
290 | 266 * @param container the container |
267 * @return *this | |
268 */ | |
269 template <typename T, typename std::enable_if<Pack::IsContainer<T>::value>::type * = nullptr> | |
270 PackWriter &operator<<(const T &container) | |
271 { | |
272 for (const auto &v : container) | |
273 (*this) << v; | |
274 | |
275 return *this; | |
276 } | |
277 }; | |
278 | |
279 /** | |
280 * @class PackFileReader | |
281 * @brief Extract binary data from a file | |
282 */ | |
283 class PackFileReader : public PackReader { | |
284 private: | |
285 std::ifstream m_in; | |
286 | |
287 protected: | |
288 std::istream &stream() override; | |
289 | |
290 public: | |
291 /** | |
292 * Read a file. | |
293 * | |
294 * @param path the path | |
295 * @param endian the endian requested | |
185 | 296 */ |
290 | 297 PackFileReader(const std::string &path, Pack::Endian endian); |
298 }; | |
299 | |
300 /** | |
301 * @class PackStringReader | |
302 * @brief Extract binary data from a string | |
303 */ | |
304 class PackStringReader : public PackReader { | |
305 private: | |
306 std::istringstream m_in; | |
307 | |
308 std::istream &stream() override; | |
309 | |
310 public: | |
311 /** | |
312 * Read a string. | |
313 * | |
314 * @param input the input string | |
315 * @param endian the endian requested | |
316 */ | |
317 PackStringReader(std::string input, Pack::Endian endian); | |
318 }; | |
319 | |
320 /** | |
321 * @class PackFileWriter | |
322 * @brief Write binary data to a string | |
323 */ | |
324 class PackFileWriter : public PackWriter { | |
325 private: | |
326 std::ofstream m_out; | |
268 | 327 |
290 | 328 protected: |
329 std::ostream &stream() override; | |
330 | |
331 public: | |
332 /** | |
333 * Write to a file. | |
334 * | |
335 * @param path the path | |
336 * @param endian the endian requested | |
337 */ | |
338 PackFileWriter(const std::string &path, Pack::Endian endian); | |
339 }; | |
340 | |
341 /** | |
342 * @class PackStringWriter | |
343 * @brief Write binary data to a string | |
344 */ | |
345 class PackStringWriter : public PackWriter { | |
346 private: | |
347 std::ostringstream m_out; | |
348 | |
349 std::ostream &stream() override; | |
350 | |
351 public: | |
352 /** | |
353 * Write to a string | |
354 * | |
355 * @param endian the endian requested | |
356 */ | |
357 PackStringWriter(Pack::Endian endian); | |
185 | 358 |
268 | 359 /** |
290 | 360 * The current buffer. Returns a copy of the string. |
268 | 361 * |
290 | 362 * @return the string |
268 | 363 */ |
290 | 364 std::string buffer() const; |
365 }; | |
185 | 366 |
290 | 367 template <> |
368 struct Pack::TypeInfo<uint8_t> : public Pack::Convertible { | |
295
345aaeb5e0ba
Pack: fix constexpr can't be used on void return types
David Demelier <markand@malikania.fr>
parents:
290
diff
changeset
|
369 static inline void convert(uint8_t &) noexcept |
290 | 370 { |
371 // uint8_t are endian independent | |
185 | 372 } |
373 }; | |
374 | |
375 template <> | |
290 | 376 struct Pack::TypeInfo<uint16_t> : public Pack::Convertible { |
295
345aaeb5e0ba
Pack: fix constexpr can't be used on void return types
David Demelier <markand@malikania.fr>
parents:
290
diff
changeset
|
377 static inline void convert(uint16_t &v) |
266 | 378 { |
290 | 379 v = (((v >> 8) & 0x00FFL) | ((v << 8) & 0xFF00L)); |
266 | 380 } |
185 | 381 }; |
382 | |
383 template <> | |
290 | 384 struct Pack::TypeInfo<uint32_t> : public Pack::Convertible { |
295
345aaeb5e0ba
Pack: fix constexpr can't be used on void return types
David Demelier <markand@malikania.fr>
parents:
290
diff
changeset
|
385 static inline void convert(uint32_t &v) |
266 | 386 { |
290 | 387 v = ((((v) >> 24) & 0x000000FFL) |
266 | 388 | (((v) >> 8) & 0x0000FF00L) |
389 | (((v) << 8) & 0x00FF0000L) | |
390 | (((v) << 24) & 0xFF000000L)); | |
391 } | |
185 | 392 }; |
393 | |
394 template <> | |
290 | 395 struct Pack::TypeInfo<uint64_t> : public Pack::Convertible { |
295
345aaeb5e0ba
Pack: fix constexpr can't be used on void return types
David Demelier <markand@malikania.fr>
parents:
290
diff
changeset
|
396 static inline void convert(uint64_t &v) |
266 | 397 { |
290 | 398 v = ((((v) & 0xff00000000000000ull) >> 56) |
185 | 399 | (((v) & 0x00ff000000000000ull) >> 40) |
400 | (((v) & 0x0000ff0000000000ull) >> 24) | |
401 | (((v) & 0x000000ff00000000ull) >> 8 ) | |
402 | (((v) & 0x00000000ff000000ull) << 8 ) | |
403 | (((v) & 0x0000000000ff0000ull) << 24) | |
404 | (((v) & 0x000000000000ff00ull) << 40) | |
405 | (((v) & 0x00000000000000ffull) << 56)); | |
266 | 406 } |
407 }; | |
185 | 408 |
409 #endif // !_PACK_H_ |