RavEngine
Loading...
Searching...
No Matches
archive.h
1//----------------------------------------------------------------------------//
2// //
3// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
4// and distributed under the MIT License (MIT). //
5// //
6// Copyright (c) Guillaume Blanc //
7// //
8// Permission is hereby granted, free of charge, to any person obtaining a //
9// copy of this software and associated documentation files (the "Software"), //
10// to deal in the Software without restriction, including without limitation //
11// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
12// and/or sell copies of the Software, and to permit persons to whom the //
13// Software is furnished to do so, subject to the following conditions: //
14// //
15// The above copyright notice and this permission notice shall be included in //
16// all copies or substantial portions of the Software. //
17// //
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
21// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
23// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
24// DEALINGS IN THE SOFTWARE. //
25// //
26//----------------------------------------------------------------------------//
27
28#ifndef OZZ_OZZ_BASE_IO_ARCHIVE_H_
29#define OZZ_OZZ_BASE_IO_ARCHIVE_H_
30
31// Provides input (IArchive) and output (OArchive) serialization containers.
32// Archive are similar to c++ iostream. Data can be saved to a OArchive with
33// the << operator, or loaded from a IArchive with the >> operator.
34// Primitive data types are simply saved/loaded to/from archives, while struct
35// and class are saved/loaded through Save/Load intrusive or non-intrusive
36// functions:
37// - The intrusive function prototypes are "void Save(ozz::io::OArchive*) const"
38// and "void Load(ozz::io::IArchive*)".
39// - The non-intrusive functions allow to work on arrays of objects. They must
40// be implemented in ozz::io namespace, by specializing the following template
41// struct:
42// template <typename _Ty>
43// struct Extern {
44// static void Save(OArchive& _archive, const _Ty* _ty, size_t _count);
45// static void Load(IArchive& _archive, _Ty* _ty, size_t _count,
46// uint32_t _version);
47// };
48//
49// Arrays of struct/class or primitive types can be saved/loaded with the
50// helper function ozz::io::MakeArray() that is then streamed in or out using
51// << and >> archive operators: archive << ozz::io::MakeArray(my_array, count);
52//
53// Versioning can be done using OZZ_IO_TYPE_VERSION macros. Type version
54// is saved in the OArchive, and is given back to Load functions to allow to
55// manually handle version modifications. Versioning can be disabled using
56// OZZ_IO_TYPE_NOT_VERSIONABLE like macros. It can not be re-enabled afterward.
57//
58// Objects can be assigned a tag using OZZ_IO_TYPE_TAG macros. A tag allows to
59// check the type of the next object to read from an archive. An automatic
60// assertion check is performed for each object that has a tag. It can also be
61// done manually to ensure an archive has the expected content.
62//
63// Endianness (big-endian or little-endian) can be specified while constructing
64// an output archive (ozz::io::OArchive). Input archives automatically handle
65// endianness conversion if the native platform endian mode differs from the
66// archive one.
67//
68// IArchive and OArchive expect valid streams as argument, respectively opened
69// for reading and writing. Archives do NOT perform error detection while
70// reading or writing. All errors are considered programming errors. This leads
71// to the following assertions on the user side:
72// - When writing: the stream must be big (or grow-able) enough to support the
73// data being written.
74// - When reading: Stream's tell (position in the stream) must match the object
75// being read. To help with this requirement, archives provide a tag mechanism
76// that allows to check the tag (ie the type) of the next object to read. Stream
77// integrity, like data corruption or file truncation, must also be validated on
78// the user side.
79
80#include <stdint.h>
81
82#include <cassert>
83
84#include "ozz/base/endianness.h"
85#include "ozz/base/io/archive_traits.h"
86#include "ozz/base/io/stream.h"
87#include "ozz/base/platform.h"
88#include "ozz/base/span.h"
89
90namespace ozz {
91namespace io {
92namespace internal {
93// Defines Tagger helper object struct.
94// The boolean template argument is used to automatically select a template
95// specialization, whether _Ty has a tag or not.
96template <typename _Ty,
97 bool _HasTag = internal::Tag<const _Ty>::kTagLength != 0>
98struct Tagger;
99} // namespace internal
100
101// Implements output archive concept used to save/serialize data from a Stream.
102// The output endianness mode is set at construction time. It is written to the
103// stream to allow the IArchive to perform the required conversion to the native
104// endianness mode while reading.
105class OZZ_BASE_DLL OArchive {
106 public:
107 // Constructs an output archive from the Stream _stream that must be valid
108 // and opened for writing.
109 explicit OArchive(Stream* _stream,
110 Endianness _endianness = GetNativeEndianness());
111
112 // Returns true if an endian swap is required while writing.
113 bool endian_swap() const { return endian_swap_; }
114
115 // Saves _size bytes of binary data from _data.
116 size_t SaveBinary(const void* _data, size_t _size) {
117 return stream_->Write(_data, _size);
118 }
119
120 // Class type saving.
121 template <typename _Ty>
122 void operator<<(const _Ty& _ty) {
124 SaveVersion<_Ty>();
125 Extern<_Ty>::Save(*this, &_ty, 1);
126 }
127
128// Primitive type saving.
129#define OZZ_IO_PRIMITIVE_TYPE(_type) \
130 void operator<<(_type _v) { \
131 _type v = endian_swap_ ? EndianSwapper<_type>::Swap(_v) : _v; \
132 OZZ_IF_DEBUG(size_t size =) stream_->Write(&v, sizeof(v)); \
133 assert(size == sizeof(v)); \
134 }
135
136 OZZ_IO_PRIMITIVE_TYPE(char)
137 OZZ_IO_PRIMITIVE_TYPE(int8_t)
138 OZZ_IO_PRIMITIVE_TYPE(uint8_t)
139 OZZ_IO_PRIMITIVE_TYPE(int16_t)
140 OZZ_IO_PRIMITIVE_TYPE(uint16_t)
141 OZZ_IO_PRIMITIVE_TYPE(int32_t)
142 OZZ_IO_PRIMITIVE_TYPE(uint32_t)
143 OZZ_IO_PRIMITIVE_TYPE(int64_t)
144 OZZ_IO_PRIMITIVE_TYPE(uint64_t)
145 OZZ_IO_PRIMITIVE_TYPE(bool)
146 OZZ_IO_PRIMITIVE_TYPE(float)
147#undef OZZ_IO_PRIMITIVE_TYPE
148
149 // Returns output stream.
150 Stream* stream() const { return stream_; }
151
152 private:
153 template <typename _Ty>
154 void SaveVersion() {
155 // Compilation could fail here if the version is not defined for _Ty, or if
156 // the .h file containing its definition is not included by the caller of
157 // this function.
158 if (void(0), internal::Version<const _Ty>::kValue != 0) {
159 uint32_t version = internal::Version<const _Ty>::kValue;
160 *this << version;
161 }
162 }
163
164 // The output stream.
165 Stream* stream_;
166
167 // Endian swap state, true if a conversion is required while writing.
168 bool endian_swap_;
169};
170
171// Implements input archive concept used to load/de-serialize data to a Stream.
172// Endianness conversions are automatically performed according to the Archive
173// and the native formats.
174class OZZ_BASE_DLL IArchive {
175 public:
176 // Constructs an input archive from the Stream _stream that must be opened for
177 // reading, at the same tell (position in the stream) as when it was passed to
178 // the OArchive.
179 explicit IArchive(Stream* _stream);
180
181 // Returns true if an endian swap is required while reading.
182 bool endian_swap() const { return endian_swap_; }
183
184 // Loads _size bytes of binary data to _data.
185 size_t LoadBinary(void* _data, size_t _size) {
186 return stream_->Read(_data, _size);
187 }
188
189 // Class type loading.
190 template <typename _Ty>
191 void operator>>(_Ty& _ty) {
192 // Only uses tag validation for assertions, as reading cannot fail.
193 OZZ_IF_DEBUG(bool valid =) internal::Tagger<const _Ty>::Validate(*this);
194 assert(valid && "Type tag does not match archive content.");
195
196 // Loads instance.
197 uint32_t version = LoadVersion<_Ty>();
198 Extern<_Ty>::Load(*this, &_ty, 1, version);
199 }
200
201// Primitive type loading.
202#define OZZ_IO_PRIMITIVE_TYPE(_type) \
203 void operator>>(_type& _v) { \
204 _type v; \
205 OZZ_IF_DEBUG(size_t size =) stream_->Read(&v, sizeof(v)); \
206 assert(size == sizeof(v)); \
207 _v = endian_swap_ ? EndianSwapper<_type>::Swap(v) : v; \
208 }
209
210 OZZ_IO_PRIMITIVE_TYPE(char)
211 OZZ_IO_PRIMITIVE_TYPE(int8_t)
212 OZZ_IO_PRIMITIVE_TYPE(uint8_t)
213 OZZ_IO_PRIMITIVE_TYPE(int16_t)
214 OZZ_IO_PRIMITIVE_TYPE(uint16_t)
215 OZZ_IO_PRIMITIVE_TYPE(int32_t)
216 OZZ_IO_PRIMITIVE_TYPE(uint32_t)
217 OZZ_IO_PRIMITIVE_TYPE(int64_t)
218 OZZ_IO_PRIMITIVE_TYPE(uint64_t)
219 OZZ_IO_PRIMITIVE_TYPE(bool)
220 OZZ_IO_PRIMITIVE_TYPE(float)
221#undef OZZ_IO_PRIMITIVE_TYPE
222
223 template <typename _Ty>
224 bool TestTag() {
225 // Only tagged types can be tested. If compilations fails here, it can
226 // mean the file containing tag declaration is not included.
227 static_assert(internal::Tag<const _Ty>::kTagLength != 0,
228 "Tag unknown for type.");
229
230 const int tell = stream_->Tell();
231 bool valid = internal::Tagger<const _Ty>::Validate(*this);
232 stream_->Seek(tell, Stream::kSet); // Rewinds before the tag test.
233 return valid;
234 }
235
236 // Returns input stream.
237 Stream* stream() const { return stream_; }
238
239 private:
240 template <typename _Ty>
241 uint32_t LoadVersion() {
242 uint32_t version = 0;
243 if (void(0), internal::Version<const _Ty>::kValue != 0) {
244 *this >> version;
245 }
246 return version;
247 }
248
249 // The input stream.
250 Stream* stream_;
251
252 // Endian swap state, true if a conversion is required while reading.
253 bool endian_swap_;
254};
255
256// Primitive type are not versionable.
257OZZ_IO_TYPE_NOT_VERSIONABLE(char)
258OZZ_IO_TYPE_NOT_VERSIONABLE(int8_t)
259OZZ_IO_TYPE_NOT_VERSIONABLE(uint8_t)
260OZZ_IO_TYPE_NOT_VERSIONABLE(int16_t)
261OZZ_IO_TYPE_NOT_VERSIONABLE(uint16_t)
262OZZ_IO_TYPE_NOT_VERSIONABLE(int32_t)
263OZZ_IO_TYPE_NOT_VERSIONABLE(uint32_t)
264OZZ_IO_TYPE_NOT_VERSIONABLE(int64_t)
265OZZ_IO_TYPE_NOT_VERSIONABLE(uint64_t)
266OZZ_IO_TYPE_NOT_VERSIONABLE(bool)
267OZZ_IO_TYPE_NOT_VERSIONABLE(float)
268
269// Default loading and saving external implementation.
270template <typename _Ty>
271struct Extern {
272 inline static void Save(OArchive& _archive, const _Ty* _ty, size_t _count) {
273 for (size_t i = 0; i < _count; ++i) {
274 _ty[i].Save(_archive);
275 }
276 }
277 inline static void Load(IArchive& _archive, _Ty* _ty, size_t _count,
278 uint32_t _version) {
279 for (size_t i = 0; i < _count; ++i) {
280 _ty[i].Load(_archive, _version);
281 }
282 }
283};
284
285// Wrapper for dynamic array serialization.
286// Must be used through ozz::io::MakeArray.
287namespace internal {
288template <typename _Ty>
289struct Array {
290 OZZ_INLINE void Save(OArchive& _archive) const {
291 ozz::io::Extern<_Ty>::Save(_archive, array, count);
292 }
293 OZZ_INLINE void Load(IArchive& _archive, uint32_t _version) const {
294 ozz::io::Extern<_Ty>::Load(_archive, array, count, _version);
295 }
296 _Ty* array;
297 size_t count;
298};
299// Specialize for const _Ty which can only be saved.
300template <typename _Ty>
301struct Array<const _Ty> {
302 OZZ_INLINE void Save(OArchive& _archive) const {
303 ozz::io::Extern<_Ty>::Save(_archive, array, count);
304 }
305 const _Ty* array;
306 size_t count;
307};
308
309// Array copies version from the type it contains.
310// Definition of Array of _Ty version: _Ty version.
311template <typename _Ty>
312struct Version<const Array<_Ty>> {
313 enum { kValue = Version<const _Ty>::kValue };
314};
315
316// Specializes Array Save/Load for primitive types.
317#define OZZ_IO_PRIMITIVE_TYPE(_type) \
318 template <> \
319 inline void Array<const _type>::Save(OArchive& _archive) const { \
320 if (_archive.endian_swap()) { \
321 /* Save element by element as swapping in place the whole buffer is*/ \
322 /* not possible.*/ \
323 for (size_t i = 0; i < count; ++i) { \
324 _archive << array[i]; \
325 } \
326 } else { \
327 OZZ_IF_DEBUG(size_t size =) \
328 _archive.SaveBinary(array, count * sizeof(_type)); \
329 assert(size == count * sizeof(_type)); \
330 } \
331 } \
332 \
333 template <> \
334 inline void Array<_type>::Save(OArchive& _archive) const { \
335 if (_archive.endian_swap()) { \
336 /* Save element by element as swapping in place the whole buffer is*/ \
337 /* not possible.*/ \
338 for (size_t i = 0; i < count; ++i) { \
339 _archive << array[i]; \
340 } \
341 } else { \
342 OZZ_IF_DEBUG(size_t size =) \
343 _archive.SaveBinary(array, count * sizeof(_type)); \
344 assert(size == count * sizeof(_type)); \
345 } \
346 } \
347 \
348 template <> \
349 inline void Array<_type>::Load(IArchive& _archive, uint32_t /*_version*/) \
350 const { \
351 OZZ_IF_DEBUG(size_t size =) \
352 _archive.LoadBinary(array, count * sizeof(_type)); \
353 assert(size == count * sizeof(_type)); \
354 if (_archive.endian_swap()) { /*Can swap in-place.*/ \
355 EndianSwapper<_type>::Swap(array, count); \
356 } \
357 }
358
359OZZ_IO_PRIMITIVE_TYPE(char)
360OZZ_IO_PRIMITIVE_TYPE(int8_t)
361OZZ_IO_PRIMITIVE_TYPE(uint8_t)
362OZZ_IO_PRIMITIVE_TYPE(int16_t)
363OZZ_IO_PRIMITIVE_TYPE(uint16_t)
364OZZ_IO_PRIMITIVE_TYPE(int32_t)
365OZZ_IO_PRIMITIVE_TYPE(uint32_t)
366OZZ_IO_PRIMITIVE_TYPE(int64_t)
367OZZ_IO_PRIMITIVE_TYPE(uint64_t)
368OZZ_IO_PRIMITIVE_TYPE(bool)
369OZZ_IO_PRIMITIVE_TYPE(float)
370#undef OZZ_IO_PRIMITIVE_TYPE
371} // namespace internal
372
373// Utility function that instantiates Array wrapper.
374template <typename _Ty>
375OZZ_INLINE const internal::Array<_Ty> MakeArray(_Ty* _array, size_t _count) {
376 const internal::Array<_Ty> array = {_array, _count};
377 return array;
378}
379template <typename _Ty>
380OZZ_INLINE const internal::Array<const _Ty> MakeArray(const _Ty* _array,
381 size_t _count) {
382 const internal::Array<const _Ty> array = {_array, _count};
383 return array;
384}
385template <typename _Ty>
386OZZ_INLINE const internal::Array<_Ty> MakeArray(span<_Ty> _array) {
387 const internal::Array<_Ty> array = {_array.data(), _array.size()};
388 return array;
389}
390template <typename _Ty>
391OZZ_INLINE const internal::Array<const _Ty> MakeArray(span<const _Ty> _array) {
392 const internal::Array<const _Ty> array = {_array.data(), _array.size()};
393 return array;
394}
395template <typename _Ty, size_t _count>
396OZZ_INLINE const internal::Array<_Ty> MakeArray(_Ty (&_array)[_count]) {
397 const internal::Array<_Ty> array = {_array, _count};
398 return array;
399}
400template <typename _Ty, size_t _count>
401OZZ_INLINE const internal::Array<const _Ty> MakeArray(
402 const _Ty (&_array)[_count]) {
403 const internal::Array<const _Ty> array = {_array, _count};
404 return array;
405}
406
407namespace internal {
408// Specialization of the Tagger helper for tagged types.
409template <typename _Ty>
410struct Tagger<_Ty, true> {
411 static void Write(OArchive& _archive) {
413 OZZ_IF_DEBUG(size_t size =)
414 _archive.SaveBinary(Tag::Get(), Tag::kTagLength);
415 assert(size == Tag::kTagLength);
416 }
417 static bool Validate(IArchive& _archive) {
419 char buf[Tag::kTagLength];
420 if (Tag::kTagLength != _archive.LoadBinary(buf, Tag::kTagLength)) {
421 return false;
422 }
423 const char* tag = Tag::Get();
424 size_t i = 0;
425 for (; i < Tag::kTagLength && buf[i] == tag[i]; ++i) {
426 }
427 return i == Tag::kTagLength;
428 }
429};
430
431// Specialization of the Tagger helper for types with no tag.
432template <typename _Ty>
433struct Tagger<_Ty, false> {
434 static void Write(OArchive& /*_archive*/) {}
435 static bool Validate(IArchive& /*_archive*/) { return true; }
436};
437} // namespace internal
438} // namespace io
439} // namespace ozz
440#endif // OZZ_OZZ_BASE_IO_ARCHIVE_H_
Definition archive.h:174
Definition archive.h:105
Definition stream.h:43
Definition archive.h:271
Definition archive.h:289
Definition archive_traits.h:214
Definition archive.h:98
Definition archive_traits.h:210