diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp new file mode 100644 index 0000000..1cc0cef --- /dev/null +++ b/include/rosa/support/atom.hpp @@ -0,0 +1,131 @@ +/******************************************************************************* + * + * File: atom.hpp + * + * Contents: Facility for atoms, short strings statically encoded as integers. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + * This implementation is based on the Atom implementation of CAF. + * TODO: Check license. + * + ******************************************************************************/ + +#ifndef ROSA_SUPPORT_ATOM_HPP +#define ROSA_SUPPORT_ATOM_HPP + +#include "rosa/support/debug.hpp" +#include + + +// Atoms can be used to turn short string literals into statically generated +// types. The literals may consist of at most 10 non-special characters, legal +// characters are _0-9A-Za-z and the whitespace character. Special characters +// are turned into whitespace, which may result in different string literals +// being encoded into the same integer value, if any of those contain at least +// one special character. Note that the usage of special characters in the +// string literals used to create atoms cannot be checked by the compiler. +// +// Example: +// +// constexpr AtomValue NameValue = atom("name"); +// using NameAtom = AtomConstant; +// +// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) +// + +namespace rosa { + +// Maximal length of valid atom strings. +constexpr size_t MaxAtomLength = 10; + +// Underlying integer type of atom values. +using atom_t = uint64_t; + +// Turn atom_t into a strongly typed enumeration, so atom_t values casted to +// AtomValue may be used in a type-safe way. +enum class AtomValue : atom_t {}; + +// Anonymous namespace with implementational details, consider it private. +namespace { + +// Encodes ASCII characters to 6-bit encoding. +constexpr unsigned char AtomEncodingTable[] = { +/* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */ +/* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, +/* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, +/* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37, +/* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, +/* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0}; + +// Decodes 6-bit characters to ASCII +constexpr char AtomDecodingTable[] = " 0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" + "abcdefghijklmnopqrstuvwxyz"; + +// Encodes one character and updates the integer representation. +constexpr atom_t nextInterim(atom_t Current, size_t CharCode) { + return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0]; +} + +// Encodes a C-string into an integer value to be used as AtomValue. +constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) { + return (*CStr == '\0') + ? Interim + : atomValue(CStr + 1, + nextInterim(Interim, static_cast(*CStr))); +} + +} // End namespace + +// Converts an AtomValue into string. +std::string to_string(const AtomValue &What); + +// Creates an AtomValue from the given string. +AtomValue atom_from_string(const std::string &S); + +// Creates an AtomValue from the given string literal. +template +constexpr AtomValue atom(char const (&Str)[Size]) { + // Last character is the NULL terminator. + STATIC_ASSERT(Size <= MaxAtomLength + 1, + "Too many characters in atom definition"); + return static_cast(atomValue(Str)); +} + +// Lifts an AtomValue to a compile-time constant. +template +struct AtomConstant { + constexpr AtomConstant() { + // nop + } + + // Returns the wrapped value. + constexpr operator AtomValue() const { return V; } + + // Returns the wrapped value as an integer as type atom_t. + static constexpr atom_t uint_value() { return static_cast(V); } + + // Returns an instance *of this constant* (*not* an `AtomValue`). + static const AtomConstant Value; +}; + +// Implementation of the static member field Value of AtomConstant. +template +const AtomConstant AtomConstant::Value = AtomConstant{}; + +// Converts an AtomConstant into string. +template +std::string to_string(const AtomConstant &) { + return to_string(V); +} + +} // End namespace rosa + +#endif // ROSA_SUPPORT_ATOM_HPP + diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index a0c1727..e15e2ee 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,86 +1,89 @@ /******************************************************************************* * * File: debug.hpp * * Contents: Facility for debugging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/types.hpp" #include "rosa/support/terminal_colors.h" #include #include namespace rosa { // Returns an output stream to use for debugging. std::ostream &dbgs(void); // Prints an array to the ostream. template std::ostream &operator<<(std::ostream &os, const std::array &arr) { os << '['; for (unsigned I = 0; I < size; ++I) { if (I) { os << ','; } os << (PRINTABLE(T)) arr[I]; } os << ']'; return os; } } // End namespace rosa #ifndef ROSA_ENABLE_ASSERTIONS #define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) #define DEBUG(X) ROSA_IGNORE_UNUSED(X) #define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) #endif // defined(NDEBUG) +// Static assertions are always emitted to the code. +#define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) + #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt index 410552a..b698397 100644 --- a/lib/support/CMakeLists.txt +++ b/lib/support/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(ROSASupport debug.cpp terminal_colors.cpp log.cpp + atom.cpp ) diff --git a/lib/support/atom.cpp b/lib/support/atom.cpp new file mode 100644 index 0000000..8e5a933 --- /dev/null +++ b/lib/support/atom.cpp @@ -0,0 +1,50 @@ +/******************************************************************************* + * + * File: atom.cpp + * + * Contents: Implementation of non-static part of atom facilities. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + * This implementation is based on the Atom implementation of CAF. + * TODO: Check license. + * + ******************************************************************************/ + +#include "rosa/support/atom.hpp" +#include + +namespace rosa { + +std::string to_string(const AtomValue &What) { + auto X = static_cast(What); + std::string S; + S.reserve(MaxAtomLength + 1); + // Don't read characters before we found the leading 0xF. + // First four bits set? + bool ReadChars = ((X & 0xF000000000000000) >> 60) == 0xF; + uint64_t Mask = 0x0FC0000000000000; + for (int BitShift = 54; BitShift >= 0; BitShift -= 6, Mask >>= 6) { + if (ReadChars) { + S += AtomDecodingTable[(X & Mask) >> BitShift]; + } else if (((X & Mask) >> BitShift) == 0xF) { + ReadChars = true; + } + } + return S; +} + +AtomValue atom_from_string(const std::string &S) { + if (S.size() > MaxAtomLength) { + return atom(""); + } + char AtomBuf[MaxAtomLength + 1]; + std::memcpy(AtomBuf, S.c_str(), S.size()); + AtomBuf[S.size()] = '\0'; + return atom(AtomBuf); +} + +} // End namespace rosa +