text: //===--- 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 <map>
  
  namespace {
  // Only used by compiler if both template types are the same
  template <typename T, T>
  struct SameType;
  } // anonymous namespace
  
  namespace swift {
  namespace byteTree {
  class ByteTreeWriter;
  
  using UserInfoMap = std::map<void *, void *>;
  
  /// Add a template specialization of \c ObjectTraits for any type that
  /// serializes as an object consisting of multiple fields.
  template <class T>
  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 <class T>
  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 <class T>
  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 <class T>
  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<T> is defined on type T.
  template <class T>
  struct has_ObjectTraits {
    using Signature_numFields = unsigned (*)(const T &, UserInfoMap &UserInfo);
    using Signature_write = void (*)(ByteTreeWriter &Writer, const T &Object,
                                     UserInfoMap &UserInfo);
  
    template <typename U>
    static char test(SameType<Signature_numFields, &U::numFields> *,
                     SameType<Signature_write, &U::write> *);
  
    template <typename U>
    static double test(...);
  
  public:
    static bool const value =
        (sizeof(test<ObjectTraits<T>>(nullptr, nullptr)) == 1);
  };
  
  // Test if ScalarTraits<T> is defined on type T.
  template <class T>
  struct has_ScalarTraits {
    using Signature_size = unsigned (*)(const T &Object);
    using Signature_write = llvm::Error (*)(llvm::BinaryStreamWriter &Writer,
                                            const T &Object);
  
    template <typename U>
    static char test(SameType<Signature_size, &U::size> *,
                     SameType<Signature_write, &U::write> *);
  
    template <typename U>
    static double test(...);
  
  public:
    static bool const value =
        (sizeof(test<ScalarTraits<T>>(nullptr, nullptr)) == 1);
  };
  
  // Test if WrapperTypeTraits<T> is defined on type T.
  template <class T>
  struct has_WrapperTypeTraits {
    using Signature_write = void (*)(ByteTreeWriter &Writer, const T &Object,
                                     unsigned Index);
  
    template <typename U>
    static char test(SameType<Signature_write, &U::write> *);
  
    template <typename U>
    static double test(...);
  
  public:
    static bool const value = (sizeof(test<WrapperTypeTraits<T>>(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.writeRaw
    /// 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 the same form in which it is
    /// represented on the serializing machine.
    template <typename T>
    llvm::Error writeRaw(T Value) {
      // FIXME: We implicitly inherit the endianess of the serializing machine.
      // Since we're currently only supporting macOS that's not a problem for now.
      auto Error = Stream.writeRaw(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 = writeRaw(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 T>
    typename std::enable_if<has_ObjectTraits<T>::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.writeRaw(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 T>
    typename std::enable_if<has_ObjectTraits<T>::value, void>::type
    write(const T &Object, unsigned Index) {
      validateAndIncreaseFieldIndex(Index);
  
      auto ObjectWriter = ByteTreeWriter(Stream, StreamWriter, UserInfo);
      ObjectWriter.setNumFields(ObjectTraits<T>::numFields(Object, UserInfo));
  
      ObjectTraits<T>::write(ObjectWriter, Object, UserInfo);
    }
  
    template <typename T>
    typename std::enable_if<has_ScalarTraits<T>::value, void>::type
    write(const T &Value, unsigned Index) {
      validateAndIncreaseFieldIndex(Index);
  
      uint32_t ValueSize = ScalarTraits<T>::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 = writeRaw(ValueSize);
      (void)SizeError;
      assert(!SizeError);
  
      auto StartOffset = StreamWriter.getOffset();
      auto ContentError = ScalarTraits<T>::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<T>::size");
    }
  
    template <typename T>
    typename std::enable_if<DirectlyEncodable<T>::value, void>::type
    write(const T &Value, unsigned Index) {
      validateAndIncreaseFieldIndex(Index);
  
      uint32_t ValueSize = sizeof(T);
      auto SizeError = writeRaw(ValueSize);
      (void)SizeError;
      assert(!SizeError);
  
      auto ContentError = writeRaw(Value);
      (void)ContentError;
      assert(!ContentError);
    }
  
    template <typename T>
    typename std::enable_if<has_WrapperTypeTraits<T>::value, void>::type
    write(const T &Value, unsigned Index) {
      auto LengthBeforeWrite = CurrentFieldIndex;
      WrapperTypeTraits<T>::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<uint8_t> {
    static bool const value = true;
  };
  
  template <>
  struct DirectlyEncodable<uint16_t> {
    static bool const value = true;
  };
  
  template <>
  struct DirectlyEncodable<uint32_t> {
    static bool const value = true;
  };
  
  template <>
  struct WrapperTypeTraits<bool> {
    static void write(ByteTreeWriter &Writer, const bool &Value,
                      unsigned Index) {
      Writer.write(static_cast<uint8_t>(Value), Index);
    }
  };
  
  template <>
  struct ScalarTraits<llvm::StringRef> {
    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<llvm::NoneType> {
    // 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
  