diff options
author | Klaus Aehlig <klaus.aehlig@huawei.com> | 2022-02-22 17:03:21 +0100 |
---|---|---|
committer | Klaus Aehlig <klaus.aehlig@huawei.com> | 2022-02-22 17:03:21 +0100 |
commit | 619def44c1cca9f3cdf63544d5f24f2c7a7d9b77 (patch) | |
tree | 01868de723cb82c86842f33743fa7b14e24c1fa3 /src/utils | |
download | justbuild-619def44c1cca9f3cdf63544d5f24f2c7a7d9b77.tar.gz |
Initial self-hosting commit
This is the initial version of our tool that is able to
build itself. In can be bootstrapped by
./bin/bootstrap.py
Co-authored-by: Oliver Reiche <oliver.reiche@huawei.com>
Co-authored-by: Victor Moreno <victor.moreno1@huawei.com>
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/TARGETS | 1 | ||||
-rw-r--r-- | src/utils/cpp/TARGETS | 40 | ||||
-rw-r--r-- | src/utils/cpp/atomic.hpp | 119 | ||||
-rw-r--r-- | src/utils/cpp/concepts.hpp | 55 | ||||
-rw-r--r-- | src/utils/cpp/hash_combine.hpp | 15 | ||||
-rw-r--r-- | src/utils/cpp/hex_string.hpp | 19 | ||||
-rw-r--r-- | src/utils/cpp/json.hpp | 83 | ||||
-rw-r--r-- | src/utils/cpp/type_safe_arithmetic.hpp | 197 |
8 files changed, 529 insertions, 0 deletions
diff --git a/src/utils/TARGETS b/src/utils/TARGETS new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/src/utils/TARGETS @@ -0,0 +1 @@ +{}
\ No newline at end of file diff --git a/src/utils/cpp/TARGETS b/src/utils/cpp/TARGETS new file mode 100644 index 00000000..6b4347a2 --- /dev/null +++ b/src/utils/cpp/TARGETS @@ -0,0 +1,40 @@ +{ "hash_combine": + { "type": ["@", "rules", "CC", "library"] + , "name": ["hash_combine"] + , "hdrs": ["hash_combine.hpp"] + , "deps": [["@", "gsl-lite", "", "gsl-lite"]] + , "stage": ["src", "utils", "cpp"] + } +, "type_safe_arithmetic": + { "type": ["@", "rules", "CC", "library"] + , "name": ["type_safe_arithmetic"] + , "hdrs": ["type_safe_arithmetic.hpp"] + , "deps": [["@", "gsl-lite", "", "gsl-lite"]] + , "stage": ["src", "utils", "cpp"] + } +, "json": + { "type": ["@", "rules", "CC", "library"] + , "name": ["json"] + , "hdrs": ["json.hpp"] + , "deps": [["@", "json", "", "json"], ["@", "gsl-lite", "", "gsl-lite"]] + , "stage": ["src", "utils", "cpp"] + } +, "concepts": + { "type": ["@", "rules", "CC", "library"] + , "name": ["concepts"] + , "hdrs": ["concepts.hpp"] + , "stage": ["src", "utils", "cpp"] + } +, "atomic": + { "type": ["@", "rules", "CC", "library"] + , "name": ["atomic"] + , "hdrs": ["atomic.hpp"] + , "stage": ["src", "utils", "cpp"] + } +, "hex_string": + { "type": ["@", "rules", "CC", "library"] + , "name": ["hex_string"] + , "hdrs": ["hex_string.hpp"] + , "stage": ["src", "utils", "cpp"] + } +}
\ No newline at end of file diff --git a/src/utils/cpp/atomic.hpp b/src/utils/cpp/atomic.hpp new file mode 100644 index 00000000..7f7631d0 --- /dev/null +++ b/src/utils/cpp/atomic.hpp @@ -0,0 +1,119 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_ATOMIC_HPP +#define INCLUDED_SRC_UTILS_CPP_ATOMIC_HPP + +#include <atomic> +#include <condition_variable> +#include <shared_mutex> + +// Atomic wrapper with notify/wait capabilities. +// TODO(modernize): Replace any use this class by C++20's std::atomic<T>, once +// libcxx adds support for notify_*() and wait(). +// [https://libcxx.llvm.org/docs/Cxx2aStatus.html] +template <class T> +class atomic { + public: + atomic() = default; + explicit atomic(T value) : value_{std::move(value)} {} + atomic(atomic const& other) = delete; + atomic(atomic&& other) = delete; + ~atomic() = default; + + auto operator=(atomic const& other) -> atomic& = delete; + auto operator=(atomic&& other) -> atomic& = delete; + auto operator=(T desired) -> T { // NOLINT + std::shared_lock lock(mutex_); + value_ = desired; + return desired; + } + operator T() const { return static_cast<T>(value_); } // NOLINT + + void store(T desired, std::memory_order order = std::memory_order_seq_cst) { + std::shared_lock lock(mutex_); + value_.store(std::move(desired), order); + } + [[nodiscard]] auto load( + std::memory_order order = std::memory_order_seq_cst) const -> T { + return value_.load(order); + } + + template <class U = T, class = std::enable_if_t<std::is_integral_v<U>>> + auto operator++() -> T { + std::shared_lock lock(mutex_); + return ++value_; + } + template <class U = T, class = std::enable_if_t<std::is_integral_v<U>>> + [[nodiscard]] auto operator++(int) -> T { + std::shared_lock lock(mutex_); + return value_++; + } + template <class U = T, class = std::enable_if_t<std::is_integral_v<U>>> + auto operator--() -> T { + std::shared_lock lock(mutex_); + return --value_; + } + template <class U = T, class = std::enable_if_t<std::is_integral_v<U>>> + [[nodiscard]] auto operator--(int) -> T { + std::shared_lock lock(mutex_); + return value_--; + } + + void notify_one() { cv_.notify_one(); } + void notify_all() { cv_.notify_all(); } + void wait(T old, + std::memory_order order = std::memory_order::seq_cst) const { + std::unique_lock lock(mutex_); + cv_.wait(lock, + [this, &old, order]() { return value_.load(order) != old; }); + } + + private: + std::atomic<T> value_{}; + mutable std::shared_mutex mutex_{}; + mutable std::condition_variable_any cv_{}; +}; + +// Atomic shared_pointer with notify/wait capabilities. +// TODO(modernize): Replace any use this class by C++20's +// std::atomic<std::shared_ptr<T>>, once libcxx adds support for it. +// [https://libcxx.llvm.org/docs/Cxx2aStatus.html] +template <class T> +class atomic_shared_ptr { + using ptr_t = std::shared_ptr<T>; + + public: + atomic_shared_ptr() = default; + explicit atomic_shared_ptr(ptr_t value) : value_{std::move(value)} {} + atomic_shared_ptr(atomic_shared_ptr const& other) = delete; + atomic_shared_ptr(atomic_shared_ptr&& other) = delete; + ~atomic_shared_ptr() = default; + + auto operator=(atomic_shared_ptr const& other) + -> atomic_shared_ptr& = delete; + auto operator=(atomic_shared_ptr&& other) -> atomic_shared_ptr& = delete; + auto operator=(ptr_t desired) -> ptr_t { // NOLINT + std::shared_lock lock(mutex_); + value_ = desired; + return desired; + } + operator ptr_t() const { value_; } // NOLINT + + void store(ptr_t desired) { + std::shared_lock lock(mutex_); + value_ = std::move(desired); + } + [[nodiscard]] auto load() const -> ptr_t { return value_; } + + void notify_one() { cv_.notify_one(); } + void notify_all() { cv_.notify_all(); } + void wait(ptr_t old) const { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this, &old]() { return value_ != old; }); + } + + private: + ptr_t value_{}; + mutable std::shared_mutex mutex_{}; + mutable std::condition_variable_any cv_{}; +}; + +#endif // INCLUDED_SRC_UTILS_CPP_ATOMIC_HPP diff --git a/src/utils/cpp/concepts.hpp b/src/utils/cpp/concepts.hpp new file mode 100644 index 00000000..92718b43 --- /dev/null +++ b/src/utils/cpp/concepts.hpp @@ -0,0 +1,55 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_CONCEPTS_HPP +#define INCLUDED_SRC_UTILS_CPP_CONCEPTS_HPP + +#include <string> +#include <type_traits> + +// TODO(modernize): remove this once std::derived_from is shipped with libcxx +template <class T, class U> +concept derived_from = std::is_base_of_v<U, T>&& + std::is_convertible_v<const volatile T*, const volatile U*>; + +// TODO(modernize): remove this once std::same_as is shipped with libcxx +template <class T, class U> +concept same_as = std::is_same_v<T, U>and std::is_same_v<U, T>; + +template <class T> +concept ContainsString = requires { + typename T::value_type; +} +and std::is_same_v<typename T::value_type, std::string>; + +template <class T> +concept HasSize = requires(T const c) { + { c.size() } + ->same_as<std::size_t>; // TODO(modernize): replace by std::same_as +}; + +template <typename T> +concept HasToString = requires(T const t) { + { t.ToString() } + ->same_as<std::string>; // TODO(modernize): replace by std::same_as +}; + +template <class T> +concept InputIterableContainer = requires(T const c) { + { c.begin() } + ->same_as<typename T::const_iterator>; // TODO(modernize): replace by + // std::input_iterator + { c.end() } + ->same_as<typename T::const_iterator>; // TODO(modernize): replace by + // std::input_iterator +}; + +template <class T> +concept OutputIterableContainer = InputIterableContainer<T>and requires(T c) { + { std::inserter(c, c.begin()) } + ->same_as<std::insert_iterator<T>>; // TODO(modernize): replace by + // std::output_iterator +}; + +template <class T> +concept InputIterableStringContainer = + InputIterableContainer<T>and ContainsString<T>; + +#endif // INCLUDED_SRC_UTILS_CPP_CONCEPTS_HPP diff --git a/src/utils/cpp/hash_combine.hpp b/src/utils/cpp/hash_combine.hpp new file mode 100644 index 00000000..65c0c8ad --- /dev/null +++ b/src/utils/cpp/hash_combine.hpp @@ -0,0 +1,15 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_HASH_COMBINE_HPP +#define INCLUDED_SRC_UTILS_CPP_HASH_COMBINE_HPP + +#include "gsl-lite/gsl-lite.hpp" + +// Taken from Boost, as hash_combine did not yet make it to STL. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0814r0.pdf +template <class T> +inline auto hash_combine(gsl::not_null<std::size_t*> const& seed, T const& v) + -> void { + *seed ^= + std::hash<T>{}(v) + 0x9e3779b9 + (*seed << 6) + (*seed >> 2); // NOLINT +} + +#endif diff --git a/src/utils/cpp/hex_string.hpp b/src/utils/cpp/hex_string.hpp new file mode 100644 index 00000000..86ea1b9e --- /dev/null +++ b/src/utils/cpp/hex_string.hpp @@ -0,0 +1,19 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_HEX_STRING_HPP +#define INCLUDED_SRC_UTILS_CPP_HEX_STRING_HPP + +#include <iomanip> +#include <sstream> +#include <string> + +[[nodiscard]] static inline auto ToHexString(std::string const& bytes) + -> std::string { + std::ostringstream ss{}; + ss << std::hex << std::setfill('0'); + for (auto const& b : bytes) { + ss << std::setw(2) + << static_cast<int>(static_cast<unsigned char const>(b)); + } + return ss.str(); +} + +#endif // INCLUDED_SRC_UTILS_CPP_HEX_STRING_HPP diff --git a/src/utils/cpp/json.hpp b/src/utils/cpp/json.hpp new file mode 100644 index 00000000..8945e975 --- /dev/null +++ b/src/utils/cpp/json.hpp @@ -0,0 +1,83 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_JSON_HPP +#define INCLUDED_SRC_UTILS_CPP_JSON_HPP + +#include <algorithm> +#include <optional> +#include <sstream> +#include <string> + +#include "nlohmann/json.hpp" +#include "gsl-lite/gsl-lite.hpp" + +template <typename ValueT> +auto ExtractValueAs( + nlohmann::json const& j, + std::string const& key, + std::function<void(std::string const& error)>&& logger = + [](std::string const & /*unused*/) -> void {}) noexcept + -> std::optional<ValueT> { + try { + auto it = j.find(key); + if (it == j.end()) { + logger("key " + key + " cannot be found in JSON object"); + return std::nullopt; + } + return it.value().template get<ValueT>(); + } catch (std::exception& e) { + logger(e.what()); + return std::nullopt; + } +} + +namespace detail { + +[[nodiscard]] static inline auto IndentListsOnlyUntilDepth( + nlohmann::json const& json, + std::string const& indent, + std::size_t until, + std::size_t depth) -> std::string { + using iterator = std::ostream_iterator<std::string>; + if (json.is_object()) { + std::size_t i{}; + std::ostringstream oss{}; + oss << '{' << std::endl; + for (auto const& [key, value] : json.items()) { + std::fill_n(iterator{oss}, depth + 1, indent); + oss << nlohmann::json(key).dump() << ": " + << IndentListsOnlyUntilDepth(value, indent, until, depth + 1) + << (++i == json.size() ? "" : ",") << std::endl; + } + std::fill_n(iterator{oss}, depth, indent); + oss << '}'; + gsl_EnsuresAudit(nlohmann::json::parse(oss.str()) == json); + return oss.str(); + } + if (json.is_array() and depth < until) { + std::size_t i{}; + std::ostringstream oss{}; + oss << '[' << std::endl; + for (auto const& value : json) { + std::fill_n(iterator{oss}, depth + 1, indent); + oss << IndentListsOnlyUntilDepth(value, indent, until, depth + 1) + << (++i == json.size() ? "" : ",") << std::endl; + } + std::fill_n(iterator{oss}, depth, indent); + oss << ']'; + gsl_EnsuresAudit(nlohmann::json::parse(oss.str()) == json); + return oss.str(); + } + return json.dump(); +} + +} // namespace detail + +/// \brief Dump json with indent. Indent lists only until specified depth. +[[nodiscard]] static inline auto IndentListsOnlyUntilDepth( + nlohmann::json const& json, + std::size_t indent, + std::size_t until_depth = 0) -> std::string { + return detail::IndentListsOnlyUntilDepth( + json, std::string(indent, ' '), until_depth, 0); +} + +#endif // INCLUDED_SRC_UTILS_CPP_JSON_HPP diff --git a/src/utils/cpp/type_safe_arithmetic.hpp b/src/utils/cpp/type_safe_arithmetic.hpp new file mode 100644 index 00000000..21bba0b5 --- /dev/null +++ b/src/utils/cpp/type_safe_arithmetic.hpp @@ -0,0 +1,197 @@ +#ifndef INCLUDED_SRC_UTILS_CPP_TYPE_SAFE_ARITHMETIC_HPP +#define INCLUDED_SRC_UTILS_CPP_TYPE_SAFE_ARITHMETIC_HPP + +#include <limits> +#include <type_traits> + +#include "gsl-lite/gsl-lite.hpp" + +/// \struct type_safe_arithmetic_tag +/// \brief Abstract tag defining types and limits for custom arithmetic types. +/// Usage example: +/// struct my_type_tag : type_safe_arithmetic_tag<int, -2, +3> {}; +/// using my_type_t = type_safe_arithmetic<my_type_tag>; +template <typename T, + T MIN_VALUE = std::numeric_limits<T>::lowest(), + T MAX_VALUE = std::numeric_limits<T>::max(), + T SMALLEST_VALUE = std::numeric_limits<T>::min()> +struct type_safe_arithmetic_tag { + static_assert(std::is_arithmetic<T>::value, + "T must be an arithmetic type (integer or floating-point)"); + + using value_t = T; + using reference_t = T&; + using const_reference_t = T const&; + using pointer_t = T*; + using const_pointer_t = T const*; + + static constexpr value_t max_value = MAX_VALUE; + static constexpr value_t min_value = MIN_VALUE; + static constexpr value_t smallest_value = SMALLEST_VALUE; +}; + +/// \class type_safe_arithmetic +/// \brief Abstract class for defining custom arithmetic types. +/// \tparam TAG The actual \ref type_safe_arithmetic_tag +template <typename TAG> +class type_safe_arithmetic { + typename TAG::value_t m_value{}; + + public: + using tag_t = TAG; + using value_t = typename tag_t::value_t; + using reference_t = typename tag_t::reference_t; + using const_reference_t = typename tag_t::const_reference_t; + using pointer_t = typename tag_t::pointer_t; + using const_pointer_t = typename tag_t::const_pointer_t; + + static constexpr value_t max_value = tag_t::max_value; + static constexpr value_t min_value = tag_t::min_value; + static constexpr value_t smallest_value = tag_t::smallest_value; + + constexpr type_safe_arithmetic() = default; + + // NOLINTNEXTLINE + constexpr /*explicit*/ type_safe_arithmetic(value_t value) { set(value); } + + type_safe_arithmetic(type_safe_arithmetic const&) = default; + type_safe_arithmetic(type_safe_arithmetic&&) noexcept = default; + auto operator=(type_safe_arithmetic const&) + -> type_safe_arithmetic& = default; + auto operator=(type_safe_arithmetic&&) noexcept + -> type_safe_arithmetic& = default; + ~type_safe_arithmetic() = default; + + auto operator=(value_t value) -> type_safe_arithmetic& { + set(value); + return *this; + } + + // NOLINTNEXTLINE + constexpr /*explicit*/ operator value_t() const { return m_value; } + + constexpr auto get() const -> value_t { return m_value; } + + constexpr void set(value_t value) { + gsl_Expects(value >= min_value && value <= max_value && + "value output of range"); + m_value = value; + } + + auto pointer() const -> const_pointer_t { return &m_value; } +}; + +// template <typename TAG> +// bool operator==(type_safe_arithmetic<TAG> lhs, type_safe_arithmetic<TAG> rhs) +// { +// return lhs.get() == rhs.get(); +// } +// +// template <typename TAG> +// bool operator!=(type_safe_arithmetic<TAG> lhs, type_safe_arithmetic<TAG> rhs) +// { +// return !(lhs == rhs); +// } +// +// template <typename TAG> +// bool operator>(type_safe_arithmetic<TAG> lhs, type_safe_arithmetic<TAG> rhs) +// { +// return lhs.get() > rhs.get(); +// } +// +// template <typename TAG> +// bool operator>=(type_safe_arithmetic<TAG> lhs, type_safe_arithmetic<TAG> rhs) +// { +// return lhs.get() >= rhs.get(); +// } +// +// template <typename TAG> +// bool operator<(type_safe_arithmetic<TAG> lhs, type_safe_arithmetic<TAG> rhs) +// { +// return lhs.get() < rhs.get(); +// } +// +// template <typename TAG> +// bool operator<=(type_safe_arithmetic<TAG> lhs, type_safe_arithmetic<TAG> rhs) +// { +// return lhs.get() <= rhs.get(); +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG> operator+(type_safe_arithmetic<TAG> lhs, +// type_safe_arithmetic<TAG> rhs) { +// return type_safe_arithmetic<TAG>{lhs.get() + rhs.get()}; +// } + +template <typename TAG> +auto operator+=(type_safe_arithmetic<TAG>& lhs, type_safe_arithmetic<TAG> rhs) + -> type_safe_arithmetic<TAG>& { + lhs.set(lhs.get() + rhs.get()); + return lhs; +} + +// template <typename TAG> +// type_safe_arithmetic<TAG> operator-(type_safe_arithmetic<TAG> lhs, +// type_safe_arithmetic<TAG> rhs) { +// return type_safe_arithmetic<TAG>{lhs.get() - rhs.get()}; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG>& operator-=(type_safe_arithmetic<TAG>& lhs, +// type_safe_arithmetic<TAG> rhs) { +// lhs.set(lhs.get() - rhs.get()); +// return lhs; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG> operator*(type_safe_arithmetic<TAG> lhs, +// typename TAG::value_t rhs) { +// return type_safe_arithmetic<TAG>{lhs.get() - rhs}; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG>& operator*=(type_safe_arithmetic<TAG>& lhs, +// typename TAG::value_t rhs) { +// lhs.set(lhs.get() * rhs); +// return lhs; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG> operator/(type_safe_arithmetic<TAG> lhs, +// typename TAG::value_t rhs) { +// return type_safe_arithmetic<TAG>{lhs.get() / rhs}; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG>& operator/=(type_safe_arithmetic<TAG>& lhs, +// typename TAG::value_t rhs) { +// lhs.set(lhs.get() / rhs); +// return lhs; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG>& operator++(type_safe_arithmetic<TAG>& a) { +// return a += type_safe_arithmetic<TAG>{1}; +// } + +template <typename TAG> +auto operator++(type_safe_arithmetic<TAG>& a, int) + -> type_safe_arithmetic<TAG> { + auto r = a; + a += type_safe_arithmetic<TAG>{1}; + return r; +} + +// template <typename TAG> +// type_safe_arithmetic<TAG>& operator--(type_safe_arithmetic<TAG>& a) { +// return a -= type_safe_arithmetic<TAG>{1}; +// } +// +// template <typename TAG> +// type_safe_arithmetic<TAG> operator--(type_safe_arithmetic<TAG>& a, int) { +// auto r = a; +// a += type_safe_arithmetic<TAG>{1}; +// return r; +// } + +#endif // INCLUDED_SRC_UTILS_CPP_TYPE_SAFE_ARITHMETIC_HPP |