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