diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp index 63611ec..9d1a552 100644 --- a/examples/type-facilities/type-facilities.cpp +++ b/examples/type-facilities/type-facilities.cpp @@ -1,85 +1,86 @@ /******************************************************************************* * * File: type-facilities.cpp * * Contents: An example showcasing various type-related support facilities. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_token.hpp" #include using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "type facilities" << Color::Default << std::endl; auto &Log = LOG_TRACE_STREAM << std::endl; Log << "NumberOfBuiltinTypes: " << NumberOfBuiltinTypes << std::endl << "TokenBits: " << token::TokenBits << std::endl << "RepresentationBits: " << token::RepresentationBits << std::endl << "MaxTokenizableListSize: " << token::MaxTokenizableListSize << std::endl << std::endl; Log << "Type number information on 'uint8_t':" << std::endl; constexpr TypeNumber TN = TypeNumberOf::Value; Log << " type number: " << PRINTABLE_TN(TN) << std::endl << " size: " << TypeForNumber::Size << std::endl << " name: " << TypeForNumber::Name << std::endl << std::endl; Log << "Type number information of AtomConstants:" << std::endl; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << " std::is_same::value: " << std::is_same::value << std::endl << " TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << std::endl << " TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << std::endl << " name: " << TypeForNumber::Value>::Name << std::endl << std::endl; Log << "Type token information on 'TypeList':" << std::endl; // 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 Token. while (!emptyToken(T_)) { Log << " token: " << PRINTABLE_TOKEN(T_) << std::endl << " valid: " << validToken(T_) << std::endl << " empty: " << emptyToken(T_) << std::endl - << " full size: " << sizeOfToken(T_) << std::endl + << " length: " << lengthOfToken(T_) << std::endl + << " full size: " << sizeOfValuesOfToken(T_) << std::endl << " head type number: " << PRINTABLE_TN(typeNumberOfHeadOfToken(T_)) << std::endl << " size of head: " << sizeOfHeadOfToken(T_) << std::endl << " name of head: " << nameOfHeadOfToken(T_) << std::endl << " is head uint8_t: " << isHeadOfTokenTheSameType(T_) << std::endl << " is head uint16_t: " << isHeadOfTokenTheSameType(T_) << std::endl << "Dropping head..." << std::endl; dropHeadOfToken(T_); } // Here when Token became empty. Log << " token: " << PRINTABLE_TOKEN(T_) << std::endl << " empty: " << emptyToken(T_) << std::endl; return 0; } diff --git a/include/rosa/support/type_numbers.hpp b/include/rosa/support/type_numbers.hpp index ebd7362..9eb6e28 100644 --- a/include/rosa/support/type_numbers.hpp +++ b/include/rosa/support/type_numbers.hpp @@ -1,163 +1,168 @@ /****************************************************************************** * * File: type_numbers.hpp * * Contents: Facilities for registering supported types and representing them * with numbers. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * * This implementation is partially based on the type_number implementation of * CAF. * TODO: Check license. * ******************************************************************************/ #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 TypeNumberVersion below! // NOTE: Keep this list in sync with the definition of NumberedTypeNames. using BuiltinTypes = TypeList; // Indicates the version number of BuiltinTypes. Software with the same version // number are supposed to have backward compatible type numbering. // NOTE: See note above on backward compatiblity of BultinTypes. 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 T is not UnitType. template struct IsNotUnitType { 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 // NumberOfBuiltinTypes different values. using type_nr_t = typename TypeListFind::Type, IsNotUnitType>::Type::Second; // Turn type_nr_t into a strongly typed enumeration, so 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 uint8_t values. using printable_tn_t = PRINTABLE(type_nr_t); // Helper preprocessor macro to cast type numbers into printable_tn_t. #define PRINTABLE_TN(N) static_cast(N) +// Converts a TypeNumber into string. +inline std::string to_string(const TypeNumber TN) { + return std::to_string(static_cast(TN)); +} + // Computes the type number for T. // NOTE: TypeNumber is the index of T in BuiltinTypes starting from 1, // index 0 indicates a non-builtin type. template ::value> struct TypeNumberOf { static constexpr TypeNumber Value = static_cast(TypeListIndexOf::Value + 1); }; template struct TypeNumberOf { using Type = squashed_int_t; static constexpr TypeNumber Value = static_cast(TypeListIndexOf::Value + 1); }; template <> struct TypeNumberOf { static constexpr TypeNumber Value = static_cast(TypeListIndexOf::Value + 1); }; template struct TypeNumberOf, false> { static constexpr TypeNumber Value = TypeNumberOf::Value; }; // List of all type names, indexed via TypeNumber. // NOTE: Keep this definition in sync with BuiltinTypes. constexpr std::array NumberedTypeNames {{ "atom", "i16", "i32", "i64", "i8", "ldouble", "str", "u16", "u32", "u64", "u8", "unit", "bool", "double", "float" }}; // Tells if the given TypeNumber is valid in the software. // NOTE: A type number generated by an incompatible version may be valid but // supposed to denote a type different than that in the current software. constexpr bool validTypeNumber(const TypeNumber TN) { // FIXME: Duplication of static_cast into a const variable would be // possible in C+14. return 0 < static_cast(TN) && static_cast(TN) <= NumberOfBuiltinTypes; } // Computes the corresponding builtin type with some information from a type // number. // PRE: validTypeNumber(TN) template struct TypeForNumber { STATIC_ASSERT(validTypeNumber(TN), "not a valid type number"); static constexpr type_nr_t TNI = static_cast(TN); using Type = typename TypeListAt::Type; static constexpr size_t Size = sizeof(Type); static constexpr const char *Name = NumberedTypeNames[TNI - 1]; }; } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_NUMBERS_HPP diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp index b164b86..cea2764 100644 --- a/include/rosa/support/type_token.hpp +++ b/include/rosa/support/type_token.hpp @@ -1,164 +1,175 @@ /****************************************************************************** * * File: type_token.hpp * * Contents: Facilities for encoding TypeLists as unsigned integer values. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP #define ROSA_SUPPORT_TYPE_TOKEN_HPP #include "rosa/support/type_numbers.hpp" namespace rosa { // NOTE on compatibility between different versions of the type token // implementation: // Different software versions produce compatible type tokens as long as // backward compatibility of BuiltinTypes is maintained (denoted by // TypeNumberVersion, see a note on that) and the type token implementation // uses the same type as token_t (boiling down to the same token::TokenBits // value) and the same 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. // 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 token_t. using token_t = uint64_t; // Sanity check in case someone would change token_t. STATIC_ASSERT(std::is_unsigned::value, "token_t is not an unsigned integer"); // Turn token_t into a strongly typed enumeration, so Tokens 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 uint8_t values. using printable_token_t = PRINTABLE(token_t); // Helper preprocessor macro to cast tokens into printable_token_t. #define PRINTABLE_TOKEN(T) static_cast(T) +// Converts a Token into string. +inline std::string to_string(const Token T) { + return std::to_string(static_cast(T)); +} + // Nested namespace for protecting constants related to type tokens. namespace token { // The number of bits in one token. constexpr size_t TokenBits = sizeof(Token) * 8; // The number of bits a builtin type can be uniquely encoded into, that is any // valid 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 BuiltinTypes is // extended. constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1; // Maximal size of uniquely tokenizable TypeList. constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits; } // End namespace token // Generates a token, unsigned integer representation, for the TypeList. // NOTE: The TypeList cannot have more than MaxTokenizableListSize elements and // must be a subset of BuiltinTypes with respect to squashed integers. // NOTE: A generated token uniquely represents a list of types, except for // AtomConstant types. Observe that any AtomConstant is encoded as the type // AtomValue. The type information on all separate AtomConstant types are lost // and replaced by the AtomValue type whose actual value needs to be considered // in order to obtain the original AtomConstant type and so the full type // information on the encoded TypeList. template struct TypeListTokenImpl; template <> struct TypeListTokenImpl { static constexpr Token Value = static_cast(0); }; 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)); }; template struct TypeListToken; template struct TypeListToken> { // NOTE: TypeNumber is computed against squased_int_t for integral types, 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 TypeListTokenImpl. STATIC_ASSERT((TypeListSize::Value <= token::MaxTokenizableListSize), "too long list of types"); static constexpr Token Value = TypeListTokenImpl::Value; }; // Convenience template turning a list of types into a TypeList to generate // TypeListToken for it. template using TypeToken = TypeListToken>; // Anonymous namespace with helper facilities, consider it private. namespace { // Extracts the type number of the first encoded type of T. // NOTE: The returned type number is not validated. inline TypeNumber typeNumberOfHeadOfToken(const Token T) { return static_cast(static_cast(T) & ((1 << token::RepresentationBits) - 1)); } } // End namespace // Tells if the given Token is valid, can be decoded by the current software. // NOTE: Validation gives a correct result only when Token was generated by a // compatible software (see note above). bool validToken(const Token T); // Tells if the token does encode an empty list. bool emptyToken(const Token T); +// Tells how many types are encoded in T. +size_t lengthOfToken(const Token T); + // Tells the full memory size of the list encoded in T. // PRE: validToken(T) -size_t sizeOfToken(const Token T); +size_t sizeOfValuesOfToken(const Token T); // Tells the memory size of the first type encoded in T. // PRE: !empty(T) && validToken(T) size_t sizeOfHeadOfToken(const Token T); // Tells the name of the first type encoded in T. // PRE: !empty(T) && validToken(T) const char *nameOfHeadOfToken(const Token T); // Drops the head element of the encoded list. void dropHeadOfToken(Token &T); +// Drops the first N element of the encoded list. +void dropNOfToken(Token &T, const size_t N); + // Tells if the head of the encoded list is of type Type. // PRE: !empty(T) && validToken(T) template bool isHeadOfTokenTheSameType(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); return TypeNumberOf::Value == typeNumberOfHeadOfToken(T); } } // End namespace rosa #endif // ROSA_SUPPORT_TYPE_TOKEN_HPP diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp index 9e8a1aa..afbf901 100644 --- a/lib/support/type_token.cpp +++ b/lib/support/type_token.cpp @@ -1,114 +1,128 @@ /****************************************************************************** * * File: type_token.cpp * * Contents: Runtime part of type token handling. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/support/type_token.hpp" // FIXME: Automatically generate a proper number of cases for the functions // with a big number of mechanical cases in them below. namespace rosa { using namespace token; bool validToken(const Token T) { - Token T_ = T; + Token T_ = T; // NOLINT bool Valid = true; while (Valid && !emptyToken(T_)) { Valid &= validTypeNumber(typeNumberOfHeadOfToken(T_)); dropHeadOfToken(T_); } return Valid; } bool emptyToken(const Token T) { return static_cast(T) == 0; } -size_t sizeOfToken(const Token T) { +size_t lengthOfToken(const Token T) { + Token T_ = T; // NOLINT + size_t N = 0; + while (!emptyToken(T_)) { + ++N; + dropHeadOfToken(T_); + } + return N; +} + +size_t sizeOfValuesOfToken(const Token T) { ASSERT(validToken(T)); - Token T_ = T; + Token T_ = T; // NOLINT size_t S = 0; while (!emptyToken(T_)) { S += sizeOfHeadOfToken(T_); dropHeadOfToken(T_); } return S; } #define SIZECASE(N) \ { \ case N: \ return TypeForNumber(N)>::Size; \ } size_t sizeOfHeadOfToken(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); switch (static_cast(typeNumberOfHeadOfToken(T))) { default: { // Should never come here when T is valid and the case-list below covers // 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; \ } const char *nameOfHeadOfToken(const Token T) { ASSERT(!emptyToken(T) && validToken(T)); switch (static_cast(typeNumberOfHeadOfToken(T))) { default: { // Should never come here when T is valid and the case-list below covers // 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