mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We've been running doxygen with the autobrief option for a couple of
years now. This makes the \brief markers into our comments
redundant. Since they are a visual distraction and we don't want to
encourage more \brief markers in new code either, this patch removes
them all.
Patch produced by
for i in $(git grep -l '\\brief'); do perl -pi -e 's/\\brief //g' $i & done
367 lines
13 KiB
C++
367 lines
13 KiB
C++
//===--- 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
|