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 { |
|
369 static constexpr void convert(uint8_t &) noexcept |
|
370 { |
|
371 // uint8_t are endian independent |
185
|
372 } |
|
373 }; |
|
374 |
|
375 template <> |
290
|
376 struct Pack::TypeInfo<uint16_t> : public Pack::Convertible { |
|
377 static constexpr 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 { |
|
385 static constexpr 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 { |
|
396 static constexpr 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_ |