//===--- ByteTreeSerialization.h - ByteTree serialization -------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // /// \file /// Provides an interface for serializing an object tree to a custom /// binary format called ByteTree. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_BASIC_BYTETREESERIALIZATION_H #define SWIFT_BASIC_BYTETREESERIALIZATION_H #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/BinaryStreamWriter.h" #include "swift/Basic/ExponentialGrowthAppendingBinaryByteStream.h" #include namespace { // Only used by compiler if both template types are the same template struct SameType; } // anonymous namespace namespace swift { namespace byteTree { class ByteTreeWriter; using UserInfoMap = std::map; /// Add a template specialization of \c ObjectTraits for any type that /// serializes as an object consisting of multiple fields. template struct ObjectTraits { // Must provide: /// Return the number of fields that will be written in \c write when /// \p Object gets serialized. \p UserInfo can contain arbitrary values that /// can modify the serialization behaviour and gets passed down from the /// serialization invocation. // static unsigned numFields(const T &Object, UserInfoMap &UserInfo); /// Serialize \p Object by calling \c Writer.write for all the fields of /// \p Object. \p UserInfo can contain arbitrary values that can modify the /// serialization behaviour and gets passed down from the serialization /// invocation. // static void write(ByteTreeWriter &Writer, const T &Object, // UserInfoMap &UserInfo); }; /// Add a template specialization of \c ScalarTraits for any type that /// serializes into a raw set of bytes. template struct ScalarTraits { // Must provide: /// Return the number of bytes the serialized format of \p Value will take up. // static unsigned size(const T &Value); /// Serialize \p Value by writing its binary format into \p Writer. Any errors /// that may be returned by \p Writer can be returned by this function and /// will be handled on the call-side. // static llvm::Error write(llvm::BinaryStreamWriter &Writer, const T &Value); }; /// Add a template specialization of \c DirectlyEncodable for any type whose /// serialized form is equal to its binary representation on the serializing /// machine. template struct DirectlyEncodable { // Must provide: // static bool const value = true; }; /// Add a template specialization of \c WrapperTypeTraits for any type that /// serializes as a type that already has a specialization of \c ScalarTypes. /// This will typically be useful for types like enums that have a 1-to-1 /// mapping to e.g. an integer. template struct WrapperTypeTraits { // Must provide: /// Write the serializable representation of \p Value to \p Writer. This will /// typically take the form \c Writer.write(convertedValue(Value), Index) /// where \c convertedValue has to be defined. // static void write(ByteTreeWriter &Writer, const T &Value, unsigned Index); }; // Test if ObjectTraits is defined on type T. template struct has_ObjectTraits { using Signature_numFields = unsigned (*)(const T &, UserInfoMap &UserInfo); using Signature_write = void (*)(ByteTreeWriter &Writer, const T &Object, UserInfoMap &UserInfo); template static char test(SameType *, SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr, nullptr)) == 1); }; // Test if ScalarTraits is defined on type T. template struct has_ScalarTraits { using Signature_size = unsigned (*)(const T &Object); using Signature_write = llvm::Error (*)(llvm::BinaryStreamWriter &Writer, const T &Object); template static char test(SameType *, SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr, nullptr)) == 1); }; // Test if WrapperTypeTraits is defined on type T. template struct has_WrapperTypeTraits { using Signature_write = void (*)(ByteTreeWriter &Writer, const T &Object, unsigned Index); template static char test(SameType *); template static double test(...); public: static bool const value = (sizeof(test>(nullptr)) == 1); }; class ByteTreeWriter { private: /// The writer to which the binary data is written. llvm::BinaryStreamWriter &StreamWriter; /// The underlying stream of the StreamWriter. We need this reference so that /// we can call \c ExponentialGrowthAppendingBinaryByteStream.writeInteger /// which is more efficient than the generic \c writeBytes of /// \c llvm::BinaryStreamWriter since it avoids the arbitrary size memcopy. ExponentialGrowthAppendingBinaryByteStream &Stream; /// The number of fields this object contains. \c UINT_MAX if it has not been /// set yet. No member may be written to the object if expected number of /// fields has not been set yet. unsigned NumFields = UINT_MAX; /// The index of the next field to write. Used in assertion builds to keep /// track that no indicies are jumped and that the object contains the /// expected number of fields. unsigned CurrentFieldIndex = 0; UserInfoMap &UserInfo; /// The \c ByteTreeWriter can only be constructed internally. Use /// \c ByteTreeWriter.write to serialize a new object. /// \p Stream must be the underlying stream of \p SteamWriter. ByteTreeWriter(ExponentialGrowthAppendingBinaryByteStream &Stream, llvm::BinaryStreamWriter &StreamWriter, UserInfoMap &UserInfo) : StreamWriter(StreamWriter), Stream(Stream), UserInfo(UserInfo) {} /// Write the given value to the ByteTree in little-endian byte order. template llvm::Error writeInteger(T Value) { auto Error = Stream.writeInteger(StreamWriter.getOffset(), Value); StreamWriter.setOffset(StreamWriter.getOffset() + sizeof(T)); return Error; } /// Set the expected number of fields the object written by this writer is /// expected to have. void setNumFields(uint32_t NumFields) { assert(NumFields != UINT_MAX && "NumFields may not be reset since it has already been written to " "the byte stream"); assert((this->NumFields == UINT_MAX) && "NumFields has already been set"); // Num fields cannot exceed (1 << 31) since it would otherwise interfere // with the bitflag that indicates if the next construct in the tree is an // object or a scalar. assert((NumFields & ((uint32_t)1 << 31)) == 0 && "Field size too large"); // Set the most significant bit to indicate that the next construct is an // object and not a scalar. uint32_t ToWrite = NumFields | (1 << 31); auto Error = writeInteger(ToWrite); (void)Error; assert(!Error); this->NumFields = NumFields; } /// Validate that \p Index is the next field that is expected to be written, /// does not exceed the number of fields in this object and that /// \c setNumFields has already been called. void validateAndIncreaseFieldIndex(unsigned Index) { assert((NumFields != UINT_MAX) && "setNumFields must be called before writing any value"); assert(Index == CurrentFieldIndex && "Writing index out of order"); assert(Index < NumFields && "Writing more fields than object is expected to have"); CurrentFieldIndex++; } ~ByteTreeWriter() { assert(CurrentFieldIndex == NumFields && "Object had more or less elements than specified"); } public: /// Write a binary serialization of \p Object to \p StreamWriter, prefixing /// the stream by the specified ProtocolVersion. template typename std::enable_if::value, void>::type static write(ExponentialGrowthAppendingBinaryByteStream &Stream, uint32_t ProtocolVersion, const T &Object, UserInfoMap &UserInfo) { llvm::BinaryStreamWriter StreamWriter(Stream); ByteTreeWriter Writer(Stream, StreamWriter, UserInfo); auto Error = Writer.writeInteger(ProtocolVersion); (void)Error; assert(!Error); // There always is one root. We need to set NumFields so that index // validation succeeds, but we don't want to serialize this. Writer.NumFields = 1; Writer.write(Object, /*Index=*/0); } template typename std::enable_if::value, void>::type write(const T &Object, unsigned Index) { validateAndIncreaseFieldIndex(Index); auto ObjectWriter = ByteTreeWriter(Stream, StreamWriter, UserInfo); ObjectWriter.setNumFields(ObjectTraits::numFields(Object, UserInfo)); ObjectTraits::write(ObjectWriter, Object, UserInfo); } template typename std::enable_if::value, void>::type write(const T &Value, unsigned Index) { validateAndIncreaseFieldIndex(Index); uint32_t ValueSize = ScalarTraits::size(Value); // Size cannot exceed (1 << 31) since it would otherwise interfere with the // bitflag that indicates if the next construct in the tree is an object // or a scalar. assert((ValueSize & ((uint32_t)1 << 31)) == 0 && "Value size too large"); auto SizeError = writeInteger(ValueSize); (void)SizeError; assert(!SizeError); auto StartOffset = StreamWriter.getOffset(); auto ContentError = ScalarTraits::write(StreamWriter, Value); (void)ContentError; assert(!ContentError); (void)StartOffset; assert((StreamWriter.getOffset() - StartOffset == ValueSize) && "Number of written bytes does not match size returned by " "ScalarTraits::size"); } template typename std::enable_if::value, void>::type write(const T &Value, unsigned Index) { validateAndIncreaseFieldIndex(Index); uint32_t ValueSize = sizeof(T); auto SizeError = writeInteger(ValueSize); (void)SizeError; assert(!SizeError); auto ContentError = writeInteger(Value); (void)ContentError; assert(!ContentError); } template typename std::enable_if::value, void>::type write(const T &Value, unsigned Index) { auto LengthBeforeWrite = CurrentFieldIndex; WrapperTypeTraits::write(*this, Value, Index); (void)LengthBeforeWrite; assert(CurrentFieldIndex == LengthBeforeWrite + 1 && "WrapperTypeTraits did not call BinaryWriter.write"); } }; // Define serialization schemes for common types template <> struct DirectlyEncodable { static bool const value = true; }; template <> struct DirectlyEncodable { static bool const value = true; }; template <> struct DirectlyEncodable { static bool const value = true; }; template <> struct WrapperTypeTraits { static void write(ByteTreeWriter &Writer, const bool &Value, unsigned Index) { Writer.write(static_cast(Value), Index); } }; template <> struct ScalarTraits { static unsigned size(const llvm::StringRef &Str) { return Str.size(); } static llvm::Error write(llvm::BinaryStreamWriter &Writer, const llvm::StringRef &Str) { return Writer.writeFixedString(Str); } }; template <> struct ObjectTraits { // Serialize llvm::None as an object without any elements static unsigned numFields(const llvm::NoneType &Object, UserInfoMap &UserInfo) { return 0; } static void write(ByteTreeWriter &Writer, const llvm::NoneType &Object, UserInfoMap &UserInfo) { // Nothing to write } }; } // end namespace byteTree } // end namespace swift #endif