diff options
author | Devtools Arcadia <[email protected]> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <[email protected]> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/generic/array_ref.h |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/generic/array_ref.h')
-rw-r--r-- | util/generic/array_ref.h | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/util/generic/array_ref.h b/util/generic/array_ref.h new file mode 100644 index 00000000000..1ac60ac7d3c --- /dev/null +++ b/util/generic/array_ref.h @@ -0,0 +1,280 @@ +#pragma once + +#include <util/generic/yexception.h> + +#include <algorithm> +#include <initializer_list> +#include <iterator> + +/** + * `TArrayRef` works pretty much like `std::span` with dynamic extent, presenting + * an array-like interface into a contiguous sequence of objects. + * + * It can be used at interface boundaries instead of `TVector` or + * pointer-size pairs, and is actually a preferred way to pass contiguous data + * into functions. + * + * Note that `TArrayRef` can be auto-constructed from any contiguous container + * (with `size` and `data` members), and thus you don't have to change client code + * when switching over from passing `TVector` to `TArrayRef`. + * + * Note that `TArrayRef` has the same const-semantics as raw pointers: + * - `TArrayRef<T>` is a non-const reference to non-const data (like `T*`); + * - `TArrayRef<const T>` is a non-const reference to const data (like `const T*`); + * - `const TArrayRef<T>` is a const reference to non-const data (like `T* const`); + * - `const TArrayRef<const T>` is a const reference to const data (like `const T* const`). + */ +template <class T> +class TArrayRef { +public: + using iterator = T*; + using const_iterator = const T*; + using reference = T&; + using const_reference = const T&; + using value_type = T; + using reverse_iterator = std::reverse_iterator<iterator>; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + constexpr inline TArrayRef() noexcept + : T_(nullptr) + , S_(0) + { + } + + constexpr inline TArrayRef(T* data, size_t len) noexcept + : T_(data) + , S_(len) + { + } + + constexpr inline TArrayRef(T* begin, T* end) noexcept + : T_(begin) + , S_(end - begin) + { + } + + constexpr inline TArrayRef(std::initializer_list<T> list) noexcept + : T_(list.begin()) + , S_(list.size()) + { + } + + template <class Container> + constexpr inline TArrayRef(Container&& container, decltype(std::declval<T*&>() = container.data(), nullptr) = nullptr) noexcept + : T_(container.data()) + , S_(container.size()) + { + } + + template <size_t N> + constexpr inline TArrayRef(T (&array)[N]) noexcept + : T_(array) + , S_(N) + { + } + + template <class TT, typename = std::enable_if_t<std::is_same<std::remove_const_t<T>, std::remove_const_t<TT>>::value>> + bool operator==(const TArrayRef<TT>& other) const noexcept { + return (S_ == other.size()) && std::equal(begin(), end(), other.begin()); + } + + constexpr inline T* data() const noexcept { + return T_; + } + + constexpr inline size_t size() const noexcept { + return S_; + } + + constexpr size_t size_bytes() const noexcept { + return (size() * sizeof(T)); + } + + constexpr inline bool empty() const noexcept { + return (S_ == 0); + } + + constexpr inline iterator begin() const noexcept { + return T_; + } + + constexpr inline iterator end() const noexcept { + return (T_ + S_); + } + + constexpr inline const_iterator cbegin() const noexcept { + return T_; + } + + constexpr inline const_iterator cend() const noexcept { + return (T_ + S_); + } + + constexpr inline reverse_iterator rbegin() const noexcept { + return reverse_iterator(T_ + S_); + } + + constexpr inline reverse_iterator rend() const noexcept { + return reverse_iterator(T_); + } + + constexpr inline const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(T_ + S_); + } + + constexpr inline const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(T_); + } + + constexpr inline reference front() const noexcept { + return *T_; + } + + inline reference back() const noexcept { + Y_ASSERT(S_ > 0); + + return *(end() - 1); + } + + inline reference operator[](size_t n) const noexcept { + Y_ASSERT(n < S_); + + return *(T_ + n); + } + + inline reference at(size_t n) const { + if (n >= S_) { + throw std::out_of_range("array ref range error"); + } + + return (*this)[n]; + } + + constexpr inline explicit operator bool() const noexcept { + return (S_ > 0); + } + + /** + * Obtains a ref that is a view over the first `count` elements of this TArrayRef. + * + * The behavior is undefined if count > size(). + */ + TArrayRef first(size_t count) const { + Y_ASSERT(count <= size()); + return TArrayRef(data(), count); + } + + /** + * Obtains a ref that is a view over the last `count` elements of this TArrayRef. + * + * The behavior is undefined if count > size(). + */ + TArrayRef last(size_t count) const { + Y_ASSERT(count <= size()); + return TArrayRef(end() - count, end()); + } + + /** + * Obtains a ref that is a view over the `count` elements of this TArrayRef starting at `offset`. + * + * The behavior is undefined in either offset or count is out of range. + */ + TArrayRef subspan(size_t offset) const { + Y_ASSERT(offset <= size()); + return TArrayRef(data() + offset, size() - offset); + } + + TArrayRef subspan(size_t offset, size_t count) const { + Y_ASSERT(offset + count <= size()); + return TArrayRef(data() + offset, count); + } + + TArrayRef Slice(size_t offset) const { + return subspan(offset); + } + + TArrayRef Slice(size_t offset, size_t size) const { + return subspan(offset, size); + } + + /* FIXME: + * This method is placed here for backward compatibility only and should be removed. + * Keep in mind that it's behavior is different from Slice(): + * SubRegion() never throws. It returns empty TArrayRef in case of invalid input. + * + * DEPRECATED. DO NOT USE. + */ + TArrayRef SubRegion(size_t offset, size_t size) const { + if (size == 0 || offset >= S_) { + return TArrayRef(); + } + + if (size > S_ - offset) { + size = S_ - offset; + } + + return TArrayRef(T_ + offset, size); + } + + constexpr inline yssize_t ysize() const noexcept { + return static_cast<yssize_t>(this->size()); + } + +private: + T* T_; + size_t S_; +}; + +/** + * Obtains a view to the object representation of the elements of the TArrayRef arrayRef. + * + * Named as its std counterparts, std::as_bytes. + */ +template <typename T> +TArrayRef<const char> as_bytes(TArrayRef<T> arrayRef) noexcept { + return TArrayRef<const char>( + reinterpret_cast<const char*>(arrayRef.data()), + arrayRef.size_bytes()); +} + +/** + * Obtains a view to the writable object representation of the elements of the TArrayRef arrayRef. + * + * Named as its std counterparts, std::as_writable_bytes. + */ +template <typename T> +TArrayRef<char> as_writable_bytes(TArrayRef<T> arrayRef) noexcept { + return TArrayRef<char>( + reinterpret_cast<char*>(arrayRef.data()), + arrayRef.size_bytes()); +} + +template <class Range> +constexpr TArrayRef<const typename Range::value_type> MakeArrayRef(const Range& range) { + return TArrayRef<const typename Range::value_type>(range); +} + +template <class Range> +constexpr TArrayRef<typename Range::value_type> MakeArrayRef(Range& range) { + return TArrayRef<typename Range::value_type>(range); +} + +template <class Range> +constexpr TArrayRef<const typename Range::value_type> MakeConstArrayRef(const Range& range) { + return TArrayRef<const typename Range::value_type>(range); +} + +template <class Range> +constexpr TArrayRef<const typename Range::value_type> MakeConstArrayRef(Range& range) { + return TArrayRef<const typename Range::value_type>(range); +} + +template <class T> +constexpr TArrayRef<T> MakeArrayRef(T* data, size_t size) { + return TArrayRef<T>(data, size); +} + +template <class T> +constexpr TArrayRef<T> MakeArrayRef(T* begin, T* end) { + return TArrayRef<T>(begin, end); +} |