blob: ad7bd5ba328ac3cbcc3c321daa3140d9fdbc1ab5 [file] [log] [blame]
// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Defines a set of simple serialization primitives.
//
// *** BASIC USAGE
//
// Serialization of a simple object works as follows:
//
// Object object;
// FILE* file = OpenFile("foo.dat", "wb");
// FileOutStream out_stream(file);
// NativeBinaryOutArchive out_archive(out_stream);
// out_archive.Save(object);
// out_archive.Flush();
//
// Note that an output stream must be flushed as the archive or the stream may
// introduce some buffering. If not explicitly called, it will be called for an
// OutStream or OutArchive when it is destroyed.
//
// To deserialize an object:
//
// Object object;
// FILE* file = OpenFile("foo.dat", "rb");
// FileInStream in_stream(file);
// NativeBinaryInArchive in_archive(in_stream);
// in_archive.Load(&object);
//
// Serialization of primitive types (bool, char, wchar_t, float, double,
// int8_t/16/32/64, uint8_t/16/32/64), C-arrays of serializable types, and STL
// containers (map, set, vector, basic_string, pair) of serializable types is
// supported by default. Support can be added for further types by extending
// the serialization system directly.
//
// There are currently two stream types defined: File*Stream, which uses a
// FILE* under the hood; and Byte*Stream, which uses iterators to containers
// of Bytes. Adding further stream types is trivial. Refer to to the comments/
// declarations of File*Stream and Byte*Stream for details.
//
// There is currently a single archive type defined, NativeBinary, which is a
// non-portable binary format. Additional archive formats may be easily added
// as well. Refer to the comments/declaration of NativeBinaryInArchive and
// NativeBinaryOutArchive for details.
//
// *** ADDING SERIALIZATION SUPPORT DIRECTLY TO A CLASS:
//
// The object itself supports serialization through a public member function:
//
// class Object {
// ...
// template<class OutArchive> bool Save(OutArchive* out_archive) const {
// ... calls to out_archive.Save ...
// }
// };
//
// To support deserialization, a similar 'Load' function is created with the
// signature:
//
// template<class InArchive> bool Load(InArchive* in_archive);
//
// *** ADDING SERIALIZATION SUPPORT TO A CLASS IN AN EXTERNAL LIBRARY
//
// If you need to add serialization support to a class in an external library
// (where you can't directly add member functions), you can do so using the
// templated core::Save and core::Load functions. Simply provide an override
// for the appropriate type, with signatures of the form:
//
// template<class OutArchive> bool Save(const ClassName& data,
// OutArchive* out_archive);
// template<class InArchive> bool Load(ClassName* data,
// InArchive* in_archive);
//
// *** UNDER THE HOOD
//
// We trace the calltree for the serialization of a Foo object foo:
//
// - An object foo of type Foo is saved to an OutArchive by calling
// out_archive.Save(foo).
//
// - If OutArchive specializes Save(Foo), then the object is serialized
// directly (this is the case for primitive types). Otherwise, it is forwarded
// to core::Save(foo, out_archive).
//
// - If there is a specialized version of core::Save(foo, out_archive), it
// will be called. This is how STL types and C-arrays are serialized.
// Otherwise, the generic version of the function will delegate to
// foo.Save(out_archive).
//
// - If Foo::Save is not defined, compilation will fail.
#ifndef SYZYGY_CORE_SERIALIZATION_H_
#define SYZYGY_CORE_SERIALIZATION_H_
#include <stdint.h>
#include <iterator>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/logging.h"
namespace core {
typedef uint8_t Byte;
typedef std::vector<Byte> ByteVector;
namespace internal {
// Forward declares of some utilities we need. These are defined in
// serialization_impl.h.
template<typename T> struct IsByteLike;
template<typename IteratorTag> struct IteratorsAreEqualFunctor;
} // namespace internal
// Serialization passes through these static functions before being routed
// to 'Save' and 'Load' member functions. Overriding this function provides a
// method to implement serialization for classes whose internals we can not
// modify. This is how serialization is implemented for STL containers in
// serialization_impl.h.
template<class Data, class OutArchive> bool Save(
const Data& data, OutArchive* out_archive);
template<class Data, class InArchive> bool Load(
Data* data, InArchive* in_archive);
// We define extremely lightweight stream-like objects for use as the I/O
// layer in serialization. This is so that our Serialization system can easily
// stick to the use of FILE objects.
class OutStream {
public:
virtual ~OutStream() { }
// An OutStream is expected to write all data provided to it. If it fails this
// is considered a fatal error, and the stream is no longer usable.
virtual bool Write(size_t length, const Byte* bytes) = 0;
// Flushes any buffered data currently contained in this stream. Flush should
// only be called at most once for a given stream and should be interpreted
// as an end of stream event.
virtual bool Flush() { return true; }
};
class InStream {
public:
virtual ~InStream() { }
// An input stream is expected to read all data asked of it, unless the data
// source has been exhausted and only a partial read is possible. In this
// case it must still return true. A return value of false is fatal and
// indicates that the stream is no longer usable.
// @param length the number of bytes to read.
// @param bytes a pointer to a buffer of length at least @p length to receive
// the bytes that are read.
// @param bytes_read to receive the number of bytes actually read. It is
// possible for this to be any value from 0 to length, inclusive.
// @returns true if the stream is reusable, false if it is errored.
bool Read(size_t length, Byte* bytes, size_t* bytes_read) {
return ReadImpl(length, bytes, bytes_read);
}
// Calling this version of read is only safe if you have implicit knowledge
// of the length of the input stream. This is inline so that common_lib, which
// uses serialization, doesn't need a (circular) dependency on core_lib.
// @param length the number of bytes to read.
// @param bytes a pointer to a buffer of length at least @p length to receive
// the bytes that are read.
// @returns true if the entire length of bytes was able to be read, false
// otherwise.
bool Read(size_t length, Byte* bytes) {
size_t bytes_read = 0;
if (!ReadImpl(length, bytes, &bytes_read))
return false;
if (bytes_read != length)
return false;
return true;
}
protected:
// Needs to be implemented by derived classes. See description of Read above.
// @param length the number of bytes to read.
// @param bytes a pointer to a buffer of length at least @p length to receive
// the bytes that are read.
// @param bytes_read to receive the number of bytes actually read. It is
// possible for this to be any value from 0 to length, inclusive.
// @returns true if the stream is reusable, false if it is errored.
virtual bool ReadImpl(size_t length, Byte* bytes, size_t* bytes_read) = 0;
};
typedef std::unique_ptr<OutStream> ScopedOutStreamPtr;
typedef std::unique_ptr<InStream> ScopedInStreamPtr;
// A simple OutStream wrapper for FILE pointers.
class FileOutStream : public OutStream {
public:
explicit FileOutStream(FILE* file);
virtual ~FileOutStream() { }
virtual bool Write(size_t length, const Byte* bytes);
virtual bool Flush();
private:
FILE* file_;
};
// A simple InStream wrapper for FILE pointers.
class FileInStream : public InStream {
public:
explicit FileInStream(FILE* file);
virtual ~FileInStream() { }
protected:
virtual bool ReadImpl(size_t length, Byte* bytes, size_t* bytes_read);
private:
FILE* file_;
};
// A simple OutStream wrapper for containers of bytes. Uses an output iterator
// to push data to some container, or a pair of non-const iterators to write
// data to a preallocated container. The underlying container should store
// 'byte-like' elements (integer of size 1).
template<typename OutputIterator> class ByteOutStream : public OutStream {
public:
// We can't use a compile time assert to validate the value_type of an
// output iterator, as it is undefined.
// This constructor is for adding new elements to an existing container.
// The iterator should be a bonafide OutputIterator.
explicit ByteOutStream(OutputIterator iter)
: iter_(iter), end_(iter), have_end_(false) {
}
virtual ~ByteOutStream() { }
// This constructor is for overwriting elements in an existing container.
// The iterators should be normal non-const iterators.
ByteOutStream(OutputIterator iter, OutputIterator end)
: iter_(iter), end_(end), have_end_(true) {
}
virtual bool Write(size_t length, const Byte* bytes);
private:
typedef typename std::iterator_traits<OutputIterator>::iterator_category
IteratorTag;
typedef internal::IteratorsAreEqualFunctor<IteratorTag> IteratorsAreEqual;
OutputIterator iter_;
OutputIterator end_;
bool have_end_;
};
// This is for implicit creation of ByteOutStreams without needing to specify
// template parameters. Use with ScopedOutStreamPtr.
template<typename OutputIterator>
OutStream* CreateByteOutStream(OutputIterator iter) {
return new ByteOutStream<OutputIterator>(iter);
}
template<typename OutputIterator>
OutStream* CreateByteOutStream(OutputIterator iter, OutputIterator end) {
return new ByteOutStream<OutputIterator>(iter, end);
}
// A simple InStream wrapper for containers of bytes. Uses a range of input
// iterators to traverse a container. The value type of the iterator must be
// 'byte-like' (integer type of size 1). Use with ScopedInStreamPtr.
template<typename InputIterator> class ByteInStream : public InStream {
public:
typedef typename std::iterator_traits<InputIterator>::value_type ValueType;
static_assert(internal::IsByteLike<ValueType>::Value,
"ValueType must be byte like.");
ByteInStream(InputIterator begin, InputIterator end)
: iter_(begin), end_(end) {
}
virtual ~ByteInStream() { }
protected:
virtual bool ReadImpl(size_t length, Byte* bytes, size_t* bytes_read);
private:
InputIterator iter_;
InputIterator end_;
};
// This is for implicit creation of ByteInStreams without needing to specify
// template parameters.
template<typename InputIterator>
ByteInStream<InputIterator>* CreateByteInStream(InputIterator iter,
InputIterator end) {
return new ByteInStream<InputIterator>(iter, end);
}
// This class defines a non-portable native binary serialization format.
class NativeBinaryOutArchive {
public:
// All classes implementing the OutArchive concept must implement the
// following 2 functions.
explicit NativeBinaryOutArchive(OutStream* out_stream)
: out_stream_(out_stream) {
DCHECK(out_stream != NULL);
}
~NativeBinaryOutArchive() { }
template<class Data> bool Save(const Data& data) {
return core::Save(data, this);
}
// The following are specializations for primitive data types. Every
// OutArchive should implement these types directly.
#define NATIVE_BINARY_OUT_ARCHIVE_SAVE(Type) \
bool Save(const Type& x) { \
DCHECK(out_stream_ != NULL); \
return out_stream_->Write(sizeof(Type), \
reinterpret_cast<const Byte*>(&x)); \
}
NATIVE_BINARY_OUT_ARCHIVE_SAVE(bool);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(char);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(wchar_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(float);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(double);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(int8_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(int16_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(int32_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(int64_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(uint8_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(uint16_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(uint32_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(uint64_t);
NATIVE_BINARY_OUT_ARCHIVE_SAVE(unsigned long);
#undef NATIVE_BINARY_OUT_ARCHIVE_SAVE
bool Flush() { return out_stream_->Flush(); }
OutStream* out_stream() { return out_stream_; }
private:
OutStream* out_stream_;
};
// For now this is the only archive type, but if there are more OutArchive
// would be the common pure-virtual base class.
typedef NativeBinaryOutArchive OutArchive;
class NativeBinaryInArchive {
public:
// All classes implementing the InArchive concept must implement the
// following 3 functions.
explicit NativeBinaryInArchive(InStream* in_stream)
: in_stream_(in_stream) {
DCHECK(in_stream != NULL);
}
template<class Data> bool Load(Data* data) {
return core::Load(data, this);
}
// The following are specializations for primitive data types. Every
// InArchive should implement these types directly.
#define NATIVE_BINARY_IN_ARCHIVE_LOAD(Type) \
bool Load(Type* x) { \
DCHECK(in_stream_ != NULL); \
return in_stream_->Read(sizeof(Type), reinterpret_cast<Byte*>(x)); \
}
NATIVE_BINARY_IN_ARCHIVE_LOAD(bool);
NATIVE_BINARY_IN_ARCHIVE_LOAD(char);
NATIVE_BINARY_IN_ARCHIVE_LOAD(wchar_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(float);
NATIVE_BINARY_IN_ARCHIVE_LOAD(double);
NATIVE_BINARY_IN_ARCHIVE_LOAD(int8_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(int16_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(int32_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(int64_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(uint8_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(uint16_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(uint32_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(uint64_t);
NATIVE_BINARY_IN_ARCHIVE_LOAD(unsigned long);
#undef NATIVE_BINARY_IN_ARCHIVE_LOAD
InStream* in_stream() { return in_stream_; }
private:
InStream* in_stream_;
};
// For now this is the only archive type, but if there are more OutArchive
// would be the common pure-virtual base class.
typedef NativeBinaryInArchive InArchive;
} // namespace core
// Bring in the implementation of the various templated functions.
#include "syzygy/core/serialization_impl.h"
#endif // SYZYGY_CORE_SERIALIZATION_H_