diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp index 696679e..06ce66f 100644 --- a/examples/type-facilities/type-facilities.cpp +++ b/examples/type-facilities/type-facilities.cpp @@ -1,94 +1,94 @@ //===-- examples/type-facilities/type-facilities.cpp ------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file examples/type-facilities/type-facilities.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief An example showcasing various type-related support facilities. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_token.hpp" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "type facilities" << Color::Default << '\n'; auto &Log = LOG_TRACE_STREAM; Log << "\nNumberOfBuiltinTypes: " << NumberOfBuiltinTypes << "\nTokenBits: " << token::TokenBits << "\nRepresentationBits: " << token::RepresentationBits << "\nMaxTokenizableListSize: " << token::MaxTokenizableListSize << "\n\n"; Log << "Type number information on 'uint8_t':"; constexpr TypeNumber TN = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TN) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information on 'std::string':"; constexpr TypeNumber TNS = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TNS) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information of AtomConstants:"; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << "\n std::is_same::value: " << std::is_same::value << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n name: " << TypeForNumber::Value>::Name << "\n\n"; Log << "Type token information on 'TypeList':"; // \c rosa::Token is generated statically. constexpr Token T = TypeToken::Value; STATIC_ASSERT( (T == TypeListToken>::Value), "alias template definition is wrong"); Token T_ = T; // We need a non-const value for dropping head later. // Iterate over encoded entries in \c T_. while (!emptyToken(T_)) { Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_) << "\n length: " << static_cast(lengthOfToken(T_)) - << "\n full size: " << sizeOfValuesOfToken(T_) << "\n head type number: " << PRINTABLE_TN(headOfToken(T_)) << "\n size of head: " << sizeOfHeadOfToken(T_) + << "\n alignment of head: " << alignmentOfHeadOfToken(T_) << "\n name of head: " << nameOfHeadOfToken(T_) << "\n is head uint8_t: " << isHeadOfTokenTheSameType(T_) << "\n is head uint16_t: " << isHeadOfTokenTheSameType(T_) << "\n is head std::string: " << isHeadOfTokenTheSameType(T_) << "\nDropping head..."; dropHeadOfToken(T_); } // Here when Token became empty. Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_) << '\n'; return 0; } diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index b11035a..3157a39 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,629 +1,655 @@ //===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/tokenized_storages.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief Definition of storage helper template for storing values in a /// type-safe way based on type tokens. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP #include "rosa/support/log.h" #include "rosa/support/type_token.hpp" #include #include namespace rosa { /// Defines a simple interface for storing and accessing values of different /// types. /// /// While the interface provides features to access values and know their /// types, it is the users responsibility to use particular values according to /// their actual types. No facilities for type-safe access of values is /// provided by the class. /// /// \see \c rosa::TokenizedStorage for a type-safe specialization of the /// interface. class AbstractTokenizedStorage { protected: /// Protected constructor restricts instantiation for derived classes. AbstractTokenizedStorage(void) noexcept = default; public: /// No copying and moving of \c rosa::AbstractTokenizedStorage instances. ///@{ AbstractTokenizedStorage(const AbstractTokenizedStorage &) = delete; AbstractTokenizedStorage & operator=(const AbstractTokenizedStorage &) = delete; AbstractTokenizedStorage(AbstractTokenizedStorage &&Other) = delete; AbstractTokenizedStorage &operator=(AbstractTokenizedStorage &&) = delete; ///@} /// Destroys \p this object. virtual ~AbstractTokenizedStorage(void) noexcept = default; /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object virtual size_t size(void) const noexcept = 0; /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode virtual TypeNumber typeAt(const token_size_t Pos) const noexcept = 0; /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode virtual void *pointerTo(const token_size_t Pos) noexcept = 0; /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code - /// Pos < Offsets.size() + /// Pos < size() /// \endcode virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0; }; /// Template class storing values and providing dynamic type-safe access to /// them in a lightweight way based on type tokens. /// /// \see rosa/support/type_token.hpp /// /// \tparam Types types whose values are to be stored template class TokenizedStorage; /// \defgroup TokenizedStorageForTypeList Implementation of /// rosa::TokenizedStorageForTypeList /// /// \brief Transforms a \c rosa::TypeList instance to the corresponding /// \c rosa::TokenizedStorage instance. /// /// A \c rosa::TypeList \c List instance can be turned into a corresponding \c /// rosa::TokenizedStorage instance as \code /// typename TokenizedStorageForTypeList::Type /// \endcode /// /// For example, the following expression evaluates to `true`: \code /// std::is_same>::Type, /// TokenizedStorage>::value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to transform template struct TokenizedStorageForTypeList; /// Implementation of the template for \c rosa::TypeList instances. template struct TokenizedStorageForTypeList> { using Type = TokenizedStorage; }; ///@} /// Nested namespace with implementation for \c rosa::TokenizedStorage, consider /// it private. namespace { /// Initializes a pre-allocated memory area with values from constant lvalue /// references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept; /// \defgroup createLvalueArenaElement Implementation of creating lvalue arena /// elements /// /// Stores values from constant lvalue references into a pre-allocated memory /// area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ /// \note This terminal case is used for both constant lvalue references and /// value references. template inline void createArenaElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast(static_cast(Arena) + Offsets[Pos]))) Type(T); createArenaElement(Arena, Offsets, Ts...); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, const AtomConstant &, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createArenaElement(Arena, Offsets, Ts...); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. template inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, Ts...); + createArenaElement<0>(Arena, TokenizedStorage::offsets(), Ts...); } /// Initializes a pre-allocated memory area with values from rvalue references. /// /// \tparam Types types whose values are to be stored /// /// \param Arena pre-allocated memory area to store values to /// \param Ts the values to store in \p Arena /// /// \note \p Arena needs to be a valid pointer to a memory area big enough for /// values of \p Types. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept; /// \defgroup createRvalueArenaElement Implementation of creating rvalue arena /// elements /// /// Stores values from rvalue references into a pre-allocated memory area. /// /// \note To be used by the implementation of \c createArenaElements. /// /// \todo Document these functions. ///@{ template inline void createArenaElement(void *const Arena, const std::vector &Offsets, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast( static_cast(Arena) + Offsets[Pos]))) Type(std::move(T)); createArenaElement(Arena, Offsets, std::move(Ts)...); } template inline void createArenaElement(void *const Arena, const std::vector &Offsets, AtomConstant &&, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createArenaElement(Arena, Offsets, std::move(Ts)...); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to /// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. template inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, + createArenaElement<0>(Arena, TokenizedStorage::offsets(), std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// /// \tparam Types types whose values are stored in \p Arena /// /// \param Arena the memory area to destroy values from /// /// \note \p Arena needs to be a valid pointer to a memory area where values of /// \p Types are stored. template inline void destroyArenaElements(void *const Arena) noexcept; /// \defgroup destroyArenaElement Implementation of destroying arena elements /// /// Destroys values from a memory area. /// /// \note To be used by the implementation of \c destroyArenaElements. /// /// \todo Document these functions. ///@{ template inline void destroyArenaElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyArenaElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyArenaElement(Arena, Offsets); } ///@} /// Implementation of the template. /// /// \tparam Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); - destroyArenaElement<0, Types...>(Arena, TokenizedStorage::Offsets); + destroyArenaElement<0, Types...>(Arena, + TokenizedStorage::offsets()); } } // End namespace /// Implementation of the template \c rosa::TokenizedStorage as a /// specialization of \c rosa::AbstractTokenizedStorage. /// /// The class provides facilities for storing values and providing type-safe /// access to them. /// /// \tparam Types types of values to store template class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken...>::Value; - /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. - static const std::vector Offsets; - -private: - /// A BLOB storing all the values one after the other. - void *const Arena; - /// Generates byte offsets for accessing values stored in /// \c rosa::TokenizedStorage::Arena. /// + /// \note Alignment requirement of each value is taken into account. + /// /// \return \c std::vector containing byte offsets for accessing values stored /// in \c rosa::TokenizedStorage::Arena - static std::vector offsets(void) noexcept { - Token T = ST; // Need a mutable copy. - const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T. - std::vector O(N); // Allocate vector of proper size. - // Do nothing for 0 elements. - if (N > 0) { + static const std::vector &offsets(void) noexcept { + static std::vector Offsets; + + // Initialize Offsets during the first invocation. + if (Offsets.empty()) { + Token T = ST; // Need a mutable copy. + const token_size_t N = + lengthOfToken(T); // Number of types encoded in \c T. + Offsets.resize(N); // Allocate proper size right away. + + ASSERT(N > 0); // Sanity check, 0 element case has a specialization. + token_size_t I = 0; // Start indexing from position \c 0. - O[0] = 0; // First offset is always \c 0. + Offsets[0] = 0; // First offset is always \c 0; correctly aligned. while (I < N - 1) { - ASSERT((size_t)I + 1 < O.size() && lengthOfToken(T) == N - I); + ASSERT((size_t)I + 1 < Offsets.size() && lengthOfToken(T) == N - I); // Calculate next offset based on the previous one. - // \note The offset of the last value is stored at `O[N - 1]`, which is - // set when `I == N - 2`. Hence the limit of the loop. - O[I + 1] = O[I] + sizeOfHeadOfToken(T); + // \note The offset of the last value is stored at `O[N - 1]`, which + // is set when `I == N - 2`. Hence the limit of the loop. + + const auto LastOffset = Offsets[I]; + auto &Offset = Offsets[++I]; + + const auto LastSize = sizeOfHeadOfToken(T); dropHeadOfToken(T); - ++I; + const auto CurrentAlign = alignmentOfHeadOfToken(T); + + Offset = LastOffset + LastSize; + Offset += CurrentAlign - 1; + Offset &= -std::make_signed_t(CurrentAlign); } - ASSERT((size_t)I + 1 == O.size() && lengthOfToken(T) == 1); + ASSERT((size_t)I + 1 == Offsets.size() && lengthOfToken(T) == 1); } - return O; + return Offsets; } + /// Tells the size of \c rosa::TokenizedStorage::Arena. + /// + /// \note Alignment requirement of each value is taken into account. + /// + /// \return size of \c rosa::TokenizedStorage::Arena to store values + static size_t arenaSize(void) noexcept { + static size_t ArenaSize = 0; + // Initialize ArenaSize during first invocation. + if (!ArenaSize) { + const auto &Offsets = offsets(); + Token T = ST; + const auto N = lengthOfToken(T); + dropNOfToken(T, N - 1); // Get to the last type + // Full size is the offset for plus the size of the last value. + ArenaSize = Offsets[N - 1] + sizeOfHeadOfToken(T); + } + return ArenaSize; + } + +private: + /// A BLOB storing all the values one after the other with necessary padding + /// for correct alignment. + void *const Arena; + public: /// Creates an instance with default values. /// /// \note This constructor requires that all actual template arguments \p /// Types... are default constructible. TokenizedStorage(void) noexcept - : Arena(::operator new(sizeOfValuesOfToken(ST))) { + : Arena(::operator new(arenaSize())) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// /// \param Ts values to store TokenizedStorage(const std::decay_t &... Ts) noexcept - : Arena(::operator new(sizeOfValuesOfToken(ST))) { + : Arena(::operator new(arenaSize())) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// /// \param Ts values to store TokenizedStorage(std::decay_t &&... Ts) noexcept - : Arena(::operator new(sizeOfValuesOfToken(ST))) { + : Arena(::operator new(arenaSize())) { ASSERT(Arena != nullptr); // Sanity check. createArenaElements(Arena, std::move(Ts)...); } /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage &) = delete; TokenizedStorage &operator=(const TokenizedStorage &) = delete; TokenizedStorage(TokenizedStorage &&Other) = delete; TokenizedStorage &operator=(TokenizedStorage &&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) { destroyArenaElements...>(Arena); ::operator delete(Arena); } /// Tells how many values are stored in \p this object. /// /// \return number of values stored in \p this object - size_t size(void) const noexcept override { return Offsets.size(); } + size_t size(void) const noexcept override { return offsets().size(); } /// Tells the type of the value stored at a position. /// /// \param Pos the index of the value whose type is to returned /// /// \return \c rosa::TypeNumber for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode TypeNumber typeAt(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return headOfToken(TT); } /// Provides an untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code /// Pos < size() /// \endcode void *pointerTo(const token_size_t Pos) noexcept override { ASSERT(Pos < size()); - return static_cast(Arena) + Offsets[Pos]; + return static_cast(Arena) + offsets()[Pos]; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \param Pos the index of the value to return an untyped pointer for /// /// \return constant untyped pointer for the value stored at index \p Pos /// /// \pre \p Pos is a valid index:\code - /// Pos < Offsets.size() + /// Pos < size() /// \endcode const void *pointerTo(const token_size_t Pos) const noexcept override { ASSERT(Pos < size()); - return static_cast(Arena) + Offsets[Pos]; + return static_cast(Arena) + offsets()[Pos]; } /// Tells if the value stored at a given index is of a given type. /// /// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as /// the \c rosa::AtomValue wrapped into it. /// /// \tparam T type to match against /// /// \param Pos index the type of the value at is to be matched against \p Type /// /// \return if the value at index \p Pos of type \p T /// /// \pre \p Pos is a valid index:\code - /// Pos < Offsets.size() + /// Pos < size() /// \endcode template bool isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } /// Gives a reference of a value of a given type stored at a given index. /// /// \note The constant variant of the function relies on this implementation, /// the function may not modify \p this object! /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// /// \return reference of \p T for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode template T &valueAt(const token_size_t Pos) noexcept { ASSERT(Pos < size() && isTypeAt(Pos)); return *static_cast(pointerTo(Pos)); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \param Pos index to set the reference for /// /// \return constant reference of \p T for the value stored at index \p Pos /// /// \pre \p Pos is a valid index and the value at index \p Pos is of type /// \p T: /// \code /// Pos < Size && isTypeAt(Pos) /// \endcode template const T &valueAt(const token_size_t Pos) const noexcept { // \note Just use the non-const implementation as that does not modify // \p this object. return const_cast(this)->valueAt(Pos); } }; -// Implementation of the static member field \c rosa::TokenizedStorage::Offsets. -template -const std::vector - TokenizedStorage::Offsets = TokenizedStorage::offsets(); - /// Specialization of the template \c rosa::TokenizedStorage for storing /// nothing. /// /// \note The specialization implements the interface defined by \c /// rosa::AbstractTokenizedStorage but most of the functions cannot be called /// because nothing is stored in instances of the class. template <> class TokenizedStorage<> : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. static constexpr Token ST = TypeToken<>::Value; - /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. - static const std::vector Offsets; - /// Creates an instance. TokenizedStorage(void) noexcept = default; /// No copying and moving of \c rosa::TokenizedStorage instances. /// /// \note This restriction may be relaxed as moving should be easy to /// implement, only requires the possiblity to validate Arena pointer. ///@{ TokenizedStorage(const TokenizedStorage &) = delete; TokenizedStorage &operator=(const TokenizedStorage &) = delete; TokenizedStorage(TokenizedStorage &&Other) = delete; TokenizedStorage &operator=(TokenizedStorage &&) = delete; ///@} // Destroys \p this object. ~TokenizedStorage(void) override {} /// Tells how many values are stored in \p this object. /// /// \return `0` size_t size(void) const noexcept override { return 0; } /// Tells the type of the value stored at a position. /// /// \pre Do not call. TypeNumber typeAt(const token_size_t) const noexcept override { LOG_ERROR("Called unsupported function"); return TypeNumber(0); } /// Provides an untyped pointer for the value stored at a position. /// /// \pre Do not call. void *pointerTo(const token_size_t) noexcept override { LOG_ERROR("Called unsupported function"); return nullptr; } /// Provides a constant untyped pointer for the value stored at a position. /// /// \pre Do not call. const void *pointerTo(const token_size_t) const noexcept override { LOG_ERROR("Called unsupported function"); return nullptr; } /// Tells if the value stored at a given index is of a given type. /// /// \pre Do not call. template bool isTypeAt(const size_t) const noexcept { LOG_ERROR("Called unsupported function"); return false; } /// Gives a reference of a value of a given type stored at a given index. /// /// \tparam T type to give a reference of /// \pre Do not call. template T &valueAt(const token_size_t) noexcept { LOG_ERROR("Called unsupported function"); return *static_cast(nullptr); } /// Gives a constant reference of a value of a given type stored at a given /// index. /// /// \tparam T type to give a reference of /// /// \pre Do not call. template const T &valueAt(const token_size_t) const noexcept { LOG_ERROR("Called unsupported function"); // \note Just use the non-const implementation as that does not modify // \p this object. return *static_cast(nullptr); } }; } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp index 024b147..82c516a 100644 --- a/include/rosa/support/type_numbers.hpp +++ b/include/rosa/support/type_numbers.hpp @@ -1,245 +1,248 @@ //===-- rosa/support/type_numbers.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_numbers.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief Facilities for registering supported types and representing them with /// numbers. /// /// \note This implementation is partially based on the \c type_number /// implementation of CAF. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_NUMBERS_HPP #define ROSA_SUPPORT_TYPE_NUMBERS_HPP #include "rosa/support/atom.hpp" #include "rosa/support/math.hpp" #include "rosa/support/squashed_int.hpp" #include "rosa/support/type_helper.hpp" #include "rosa/support/types.hpp" #include #include namespace rosa { /// Compile-time list of all built-in types. /// \note Appending new types to the end of this list maintains backward /// compatibility in the sense that old builtin types have the same type number /// associated to them in both the old and new versions. But changing any of /// the already present types in the list breaks that backward compatibility. /// Should compatibility be broken, step \c rosa::TypeNumberVersion below! /// \note Keep this list in sync with the definition of /// \c rosa::NumberedTypeNames. /// \note The built-in types are explicitly listed in the definition of /// rosa::app::AppAgent. Keep those definitions in sync with this list. using BuiltinTypes = TypeList; /// Indicates the version number of \c rosa::BuiltinTypes. /// /// Software with the same version number are supposed to have backward /// compatible type numbering. /// /// \sa \c rosa::BultinTypes on backward compatibility. constexpr size_t TypeNumberVersion = 0; /// The number of built-in types. static constexpr size_t NumberOfBuiltinTypes = TypeListSize::Value; /// Anonymous namespace for helper facilities, consider it private. namespace { /// Tells if a type is not \c rosa::NoneType. /// /// \tparam T the type to check template struct IsNotNoneType { /// Denotes if \p T is the \c rosa::NoneType or not. static constexpr bool Value = !std::is_same::value; }; } // End namespace /// Integer type to store type numbers. /// \note The narrowest unsigned integer type that is wide enough to represent /// \c NumberOfBuiltinTypes different values. using type_nr_t = typename TypeListFind< typename TypeListDrop::Type, IsNotNoneType>::Type::Second; /// Turn \c rosa::type_nr_t into a strongly typed enumeration. /// /// Values of \c rosa::type_nr_t casted to \c rosa::TypeNumbers can be used in a /// type-safe way. enum class TypeNumber : type_nr_t {}; /// A type to cast type numbers into in order to output them to streams as /// numbers and not ASCII-codes. /// /// \note Use it for safety, necessary for printing \c uint8_t values. using printable_tn_t = printable_t; /// Casts a \c rosa::TypeNumber into \c rosa::printable_tn_t. /// /// \param TN \c rosa::TypeNumber to cast. #define PRINTABLE_TN(TN) static_cast(TN) /// \name TypeNumberOf /// \brief Computes \c rosa::TypeNumber for a type. /// /// The \c rosa::TypeNumber for a type \c T can be obtained as \code /// TypeNumberOf::Value /// \endcode /// /// \note \c rosa::TypeNumber for a type is based on the corresponding squashed /// type, except for \c rosa::AtomConstant types. /// /// \sa \c rosa::SquashedType /// /// \note \c rosa::TypeNumber is the index of the type in \c rosa::BuiltinTypes /// starting from \c 1; index \c 0 indicates a non-builtin type. ///@{ /// Definition of the template for the general case. /// /// \tparam T type to get \c rosa::TypeNumber for template struct TypeNumberOf { static constexpr TypeNumber Value = static_cast( TypeListIndexOf>::Value + 1); }; /// Specialization for \c rosa::AtomConstant. /// /// \note For a \c rosa::AtomConstant type, \c rosa::TypeNumber is based on the /// \c rosa::AtomValue wrapped into the actual \c rosa::AtomConstant. template struct TypeNumberOf> { static constexpr TypeNumber Value = TypeNumberOf::Value; }; ///@} // clang-format off /// List of type names for all builtin-types, indexed via \c rosa::TypeNumber. /// /// \note Keep this definition in sync with \c rosa::BuiltinTypes. constexpr std::array NumberedTypeNames {{ "atom", "i16", "i32", "i64", "i8", "ldouble", "str", "u16", "u32", "u64", "u8", "unit", "bool", "double", "float" }}; // clang-format on /// Tells if a \c rosa::TypeNumber is valid in the software. /// /// \note A \c rosa::TypeNumber generated by an incompatible version may be /// valid but may denote a type that is different from the \c rosa::TypeNumber /// denotes in the current software. That is why this validation needs to be /// done in connection to checking \c rosa::TypeNumberVersion as well. /// /// \param TN \c rosa::TypeNumber to validate in the context of the current /// software /// /// \return Whether \p TN is valid in the current software constexpr bool validTypeNumber(const TypeNumber TN) { // \todo Duplication of static_cast into a const variable would be // possible in C++14. return 0 < static_cast(TN) && static_cast(TN) <= NumberOfBuiltinTypes; } /// Provides information about the type corresponding to a \c rosa::TypeNumber. /// /// \tparam TN \c rosa::TypeNumber to get information for /// /// \pre Statically, \p TN is a valid \c rosa::TypeNumber: /// \code /// validTypeNumber(TN) /// \endcode template struct TypeForNumber { STATIC_ASSERT(validTypeNumber(TN), "not a valid type number"); /// \p TN as \c rosa::type_nr_t. static constexpr type_nr_t TNI = static_cast(TN); /// The builtin-type corresponding to \p TN. using Type = typename TypeListAt::Type; /// The size of \c Type. static constexpr size_t Size = sizeof(Type); + /// The alignment of \c Type. + static constexpr size_t Align = alignof(Type); + /// Textual representation of the builtin-type. static constexpr const char *Name = NumberedTypeNames[TNI - 1]; }; } // End namespace rosa namespace std { /// Converts a \c rosa::TypeNumber into \c std::string. /// /// \param TN \c rosa::TypeNumber to convert /// /// \return \c std::string representing \p TN inline string to_string(const rosa::TypeNumber TN) { return to_string(static_cast(TN)); } /// Dumps a \c rosa::TypeNumber to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param TN \c rosa::TypeNumber to dump /// /// \return \p OS after dumping \p TN to it inline ostream &operator<<(ostream &OS, const rosa::TypeNumber &TN) { OS << to_string(TN); return OS; } } // End namespace std #endif // ROSA_SUPPORT_TYPE_NUMBERS_HPP diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp index 57a860c..f989b5a 100644 --- a/include/rosa/support/type_token.hpp +++ b/include/rosa/support/type_token.hpp @@ -1,327 +1,325 @@ //===-- rosa/support/type_token.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_token.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief Facilities for encoding TypeLists as unsigned integer values. /// /// \note **On the compatibility between different versions of the type token /// implementation:** /// Different software versions produce compatible type tokens as long as /// *backward compatibility* of \c rosa::BuiltinTypes is maintained (denoted by /// \c rosa::TypeNumberVersion) and the type token implementation uses the same /// type as \c rosa::token_t (boiling down to the same \c rosa::token::TokenBits /// value) and the same \c rosa::token::RepresentationBits value. Thus, /// interacting software need to cross-validate the aforementioned values to /// check compatibility. Interoperation between compatible sofware is limited /// to backward compatiblity, that is builtin types defined in both software /// versions are handled correctly but a newer system may produce a type token /// which is invalid in an old one. Therefore, tokens obtained from a compatible /// remote system need to be validated. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP #define ROSA_SUPPORT_TYPE_TOKEN_HPP #include "rosa/support/type_numbers.hpp" namespace rosa { /// Integer type to store type tokens. /// /// \note The trade-off between the binary overhead of type encoding and the /// maximal size of encodable lists can be tuned by using unsigned integer types /// of different widths as \c rosa::token_t. using token_t = uint64_t; /// Sanity check in case someone would change \c rosa::token_t. STATIC_ASSERT(std::is_unsigned::value, "token_t is not an unsigned integer"); /// Turn \c rosa::token_t into a strongly typed enumeration. /// /// Values of \c rosa::token_t casted to \c rosa::Token can be used in a /// type-safe way. enum class Token : token_t {}; /// A type to cast tokens into in order to output them to streams as /// numbers and not ASCII-codes. /// /// \note Use it for safety, necessary for printing \c uint8_t values. using printable_token_t = printable_t; /// Casts a \c rosa::Token into \c rosa::printable_token_t. /// /// \param T \c rosa::Token to cast #define PRINTABLE_TOKEN(T) static_cast(T) /// Encloses constants related to the implementation of \c rosa::Token. namespace token { /// The number of bits in one \c rosa::Token. constexpr size_t TokenBits = sizeof(Token) * 8; /// The number of bits a builtin type can be uniquely encoded into, that is any /// valid \c rosa::TypeNumber can fit into. /// /// \note There is one extra bit position added for encoding so that providing a /// better chance to maintain backward comaptibility when \c rosa::BuiltinTypes /// is extended. constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1; /// Maximal size of uniquely tokenizable \c rosa::TypeList. constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits; } // End namespace token /// Integer type to store length of a \c rosa::Token. /// /// \note The narrowest unsigned integer type that is wide enough to /// represent \c rosa::token::MaxTokenizableListSize different values. using token_size_t = typename TypeListFind< typename TypeListDrop::Type, IsNotNoneType>::Type::Second; /// \defgroup TypeListTokenImpl Implementation of rosa::TypeListToken /// /// \brief Generates a \c rosa::Token for a squashed \c rosa::TypeList. /// /// \note Only to be used by the implementation of \c rosa::TypeListToken. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to generate \c rosa::Token for template struct TypeListTokenImpl; /// Specialization for \c rosa::EmptyTypeList. template <> struct TypeListTokenImpl { static constexpr Token Value = static_cast(0); }; /// Specialization for a non-empty \c rosa::TypeList. template struct TypeListTokenImpl> { static constexpr TypeNumber TN = TypeNumberOf::Value; // Check if the generated type number is valid. STATIC_ASSERT(validTypeNumber(TN), "non-builtin type"); static constexpr Token Value = static_cast( (static_cast(TypeListTokenImpl>::Value) << token::RepresentationBits) | static_cast(TN)); }; ///@} /// \name TypeListToken /// /// \brief Generates a \c rosa::Token for a \c rosa::TypeList. /// /// \c rosa::Token for a \c rosa::TypeList \c List can be obtained as \code /// TypeListToken::Value /// \endcode /// /// \note The \c rosa::TypeList cannot have more than /// \c rosa::token::MaxTokenizableListSize elements and must be a subset of /// \c rosa::BuiltinTypes with respect to squashed integers. /// /// \note A generated \c rosa::Token uniquely represents a list of types, except /// for \c rosa::AtomConstant types. Observe that any \c rosa::AtomConstant is /// encoded as the type \c rosa::AtomValue. The type information on all separate /// \c rosa::AtomConstant types are lost and replaced by the \c rosa::AtomValue /// type whose actual value needs to be used in order to obtain the original /// \c rosa::AtomConstant type and so the full type information on the encoded /// \c rosa::TypeList. /// ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to generate \c rosa::Token for template struct TypeListToken; /// Implementation using \c rosa::TypeListTokenImpl. template struct TypeListToken> { /// \note \c rosa::TypeNumber is computed against \c rosa::squased_t, so let's /// do the same here. using List = typename SquashedTypeList>::Type; /// Check the length of the list here. /// \note Type validation is done one-by-one in \c rosa::TypeListTokenImpl. STATIC_ASSERT((TypeListSize::Value <= token::MaxTokenizableListSize), "too long list of types"); /// The \c rosa::Token for \p List. static constexpr Token Value = TypeListTokenImpl::Value; }; ///@} /// Convenience template to generate \c rosa::Token for a list of types. template using TypeToken = TypeListToken>; /// Tells if a given \c rosa::Token is valid. /// /// A \c rosa::Token is considered valid if it can be decoded by the current /// software. /// /// \note Validation gives a correct result only when \p T was generated by a /// compatible software. /// /// \sa Note for type_token.hpp on compatibility. /// /// \param T \c rosa::Token to validate /// /// \return if \p T is valid bool validToken(const Token T); /// Tells if a \c rosa::Token does encode an empty list of types. /// /// \param T \c rosa::Token to check /// /// \return if \p T encodes an empty list of types bool emptyToken(const Token T); /// Tells how many types are encoded in a \c rosa::Token. /// /// \param T \c rosa::Token to check /// /// \return how many types are encoded in \p T token_size_t lengthOfToken(const Token T); -/// Tells the full memory size of the types encoded in a \c rosa::Token. -/// -/// The full memory size of the types encoded in a \c rosa::Token is the sum of -/// the separate memory sizes of all the types encoded in the \c rosa::Token. -/// -/// \param T \c rosa::Token to check -/// -/// \return full memory size of the types encoded in \p T -/// -/// \pre \p T is valid:\code -/// validToken(T) -/// \endcode -size_t sizeOfValuesOfToken(const Token T); - /// Extracts the \c rosa::TypeNumber of the first encoded type of a /// \c rosa::Token. /// /// \note The returned type number is not validated. /// /// \param T \c rosa::Token to take the first \c rosa::TypeNumber from /// /// \return the first \c rosa::TypeNumber encoded in \p T inline TypeNumber headOfToken(const Token T) { return static_cast(static_cast(T) & ((1 << token::RepresentationBits) - 1)); } /// Tells the memory size of the first type encoded in a \c rosa::Token. /// /// \param T \c rosa::Token to check the first encoded type of /// /// \return memory size of the first type encoded in \p T /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode size_t sizeOfHeadOfToken(const Token T); +/// Tells the required memory alignment of the first type encoded in a \c +/// rosa::Token. +/// +/// \param T \c rosa::Token to check the first encoded type of +/// +/// \return memory alignment of the first type encoded in \p T +/// +/// \pre \p T is not empty and valid:\code +/// !empty(T) && validToken(T) +/// \endcode +size_t alignmentOfHeadOfToken(const Token T); + /// Gives the textual representation of the first type encoded in a /// \c rosa::Token. /// /// \param T \c rosa::Token to take the name of its first encoded type /// /// \return textual representation of the first type encoded in \p T /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode const char *nameOfHeadOfToken(const Token T); /// Updates a \c rosa::Token by dropping its first encoded type. /// /// \param [in,out] T \c rosa::Token to drop the first encoded type of void dropHeadOfToken(Token &T); /// Updates a \c rosa::Token by dropping a number of its first encoded types /// /// \param [in,out] T \c rosa::Token to drop the first \p N encoded types of /// \param N the number of types to drop from \p T void dropNOfToken(Token &T, const size_t N); /// Tells what type is encoded at Position \p Pos in the \c rosa::Token \p T /// /// \param T \c rosa::Token to check /// \param Pos the position to check /// /// \return \c rosa::TypeNumber of the type encodeded at position \p Pos of \c /// rosa::Token \p T /// /// \pre \p T is valid and it encodes \p Pos types: \code /// validToken(T) && Pos < static_cast(lengthOfToken(T)) /// \endcode inline TypeNumber typeAtPositionOfToken(const Token T, const size_t Pos) { ASSERT(validToken(T) && Pos < static_cast(lengthOfToken(T))); Token TT = T; dropNOfToken(TT, Pos); return headOfToken(TT); } /// Tells if the first encoded type of a \c rosa::Token is a given type. /// /// \tparam Type type to match the first encoded type of \p T against /// /// \param T \c rosa::Token whose first encoded type is to be matched against /// \p Type /// /// \return if the first encoded type of \p T is \p Type /// /// \pre \p T is not empty and valid:\code /// !empty(T) && validToken(T) /// \endcode template bool isHeadOfTokenTheSameType(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); return TypeNumberOf::Value == headOfToken(T); } } // End namespace rosa namespace std { /// Converts a \c rosa::Token into \c std::string. /// /// \param T \c rosa::Token to convert /// /// \return \c std::string representing \p T inline string to_string(const rosa::Token T) { return to_string(static_cast(T)); } /// Dumps a \c rosa::Token to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param T \c rosa::Token to dump /// /// \return \p OS after dumping \p N to it inline ostream &operator<<(ostream &OS, const rosa::Token &T) { OS << to_string(T); return OS; } } // End namespace std #endif // ROSA_SUPPORT_TYPE_TOKEN_HPP diff --git a/lib/support/tokenized_storages.cpp b/lib/support/tokenized_storages.cpp index 69d1ea8..37e929e 100755 --- a/lib/support/tokenized_storages.cpp +++ b/lib/support/tokenized_storages.cpp @@ -1,31 +1,23 @@ //===-- support/tokenized_storages.cpp --------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file support/tokenized_storages.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief Implementation of rosa/support/tokenized_storages.hpp. /// //===----------------------------------------------------------------------===// #include "rosa/support/tokenized_storages.hpp" - -namespace rosa { - -// Implementation of the static member field \c -// rosa::TokenizedStorage<>::Offsets. -const std::vector TokenizedStorage<>::Offsets = std::vector(0); - -} // End namespace rosa \ No newline at end of file diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp index 5a729bb..23513c6 100644 --- a/lib/support/type_token.cpp +++ b/lib/support/type_token.cpp @@ -1,137 +1,152 @@ //===-- support/type_token.cpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file support/type_token.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017-2019 +/// \date 2017-2020 /// /// \brief Implementation for rosa/support/type_token.hpp. /// /// \todo Automatically generate proper cases for the switches with a big number /// of handcrafted similar cases in them. /// //===----------------------------------------------------------------------===// #include "rosa/support/type_token.hpp" namespace rosa { using namespace token; bool validToken(const Token T) { Token TT = T; bool Valid = true; while (Valid && !emptyToken(TT)) { Valid &= validTypeNumber(headOfToken(TT)); dropHeadOfToken(TT); } return Valid; } bool emptyToken(const Token T) { return static_cast(T) == 0; } token_size_t lengthOfToken(const Token T) { Token TT = T; token_size_t N = 0; while (!emptyToken(TT)) { ++N; dropHeadOfToken(TT); } return N; } -size_t sizeOfValuesOfToken(const Token T) { - ASSERT(validToken(T)); - Token TT = T; - size_t S = 0; - while (!emptyToken(TT)) { - S += sizeOfHeadOfToken(TT); - dropHeadOfToken(TT); - } - return S; -} - -#define SIZECASE(N) \ +#define TYPENUMBERCASE(N, FIELD)\ { \ case N: \ - return TypeForNumber(N)>::Size; \ + return TypeForNumber(N)>::FIELD; \ } +#define SIZECASE(N) TYPENUMBERCASE(N, Size) + size_t sizeOfHeadOfToken(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); switch (static_cast(headOfToken(T))) { default: { // Should never come here when \p T is valid and the case-list below covers // \c rosa::BuiltinTypes. ROSA_CRITICAL("unknown type number"); } SIZECASE(1); SIZECASE(2); SIZECASE(3); SIZECASE(4); SIZECASE(5); SIZECASE(6); SIZECASE(7); SIZECASE(8); SIZECASE(9); SIZECASE(10); SIZECASE(11); SIZECASE(12); SIZECASE(13); SIZECASE(14); SIZECASE(15); } } -#define NAMECASE(N) \ - { \ - case N: \ - return TypeForNumber(N)>::Name; \ +#define ALIGNCASE(N) TYPENUMBERCASE(N, Align) + +size_t alignmentOfHeadOfToken(const Token T) { + ASSERT(!emptyToken(T) && validToken(T)); + switch (static_cast(headOfToken(T))) { + default: { + // Should never come here when \p T is valid and the case-list below covers + // \c rosa::BuiltinTypes. + ROSA_CRITICAL("unknown type number"); + } + SIZECASE(1); + SIZECASE(2); + SIZECASE(3); + SIZECASE(4); + SIZECASE(5); + SIZECASE(6); + SIZECASE(7); + SIZECASE(8); + SIZECASE(9); + SIZECASE(10); + SIZECASE(11); + SIZECASE(12); + SIZECASE(13); + SIZECASE(14); + SIZECASE(15); } +} + +#define NAMECASE(N) TYPENUMBERCASE(N, Name) const char *nameOfHeadOfToken(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); switch (static_cast(headOfToken(T))) { default: { // Should never come here when \p T is valid and the case-list below covers // \c rosa::BuiltinTypes. ROSA_CRITICAL("unknown type number"); } NAMECASE(1); NAMECASE(2); NAMECASE(3); NAMECASE(4); NAMECASE(5); NAMECASE(6); NAMECASE(7); NAMECASE(8); NAMECASE(9); NAMECASE(10); NAMECASE(11); NAMECASE(12); NAMECASE(13); NAMECASE(14); NAMECASE(15); } } void dropHeadOfToken(Token &T) { T = static_cast(static_cast(T) >> RepresentationBits); } void dropNOfToken(Token &T, const size_t N) { T = static_cast(static_cast(T) >> (N * RepresentationBits)); } } // End namespace rosa