123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- /*************************************************************************
- *
- * Copyright 2022 Realm Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- **************************************************************************/
- #ifndef REALM_UTIL_SPAN_HPP
- #define REALM_UTIL_SPAN_HPP
- #include <realm/util/assert.hpp>
- #include <array>
- #include <cstddef>
- #include <iterator>
- #include <limits>
- #include <type_traits>
- namespace realm::util {
- // This is an implementation of C++20's std::span. This should be an exact
- // drop-in replacement that can be deleted once we switch to building as C++20.
- // See https://en.cppreference.com/w/cpp/container/span for documentation on this type.
- inline constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
- template <typename T, size_t extent = dynamic_extent>
- class Span;
- } // namespace realm::util
- namespace realm::_impl {
- // SFINAE helpers: anything which has std::data() and std::size() and
- // std::data() produces the correct type can be converted to Span, but
- // std::array, C arrays, and Span have separate conversions which need to be
- // used instead of the generic one.
- template <typename T>
- struct IsSpan : public std::false_type {
- };
- template <typename T, size_t extent>
- struct IsSpan<util::Span<T, extent>> : public std::true_type {
- };
- template <typename T>
- struct IsStdArray : public std::false_type {
- };
- template <typename T, size_t size>
- struct IsStdArray<std::array<T, size>> : public std::true_type {
- };
- // msvc v19.28 hits an internal compiler error if these are inline in the
- // template using them rather than type aliases. This appears to be fixed in
- // newer versions
- template <typename T>
- using StdDataT = decltype(std::data(std::declval<T>()));
- template <typename T>
- using StdSizeT = decltype(std::size(std::declval<T>()));
- template <typename T, typename U, typename A = StdDataT<T>, typename B = StdSizeT<T>>
- constexpr bool is_span_compatible()
- {
- return !IsSpan<std::remove_cv_t<T>>::value && !IsStdArray<std::remove_cv_t<T>>::value && !std::is_array_v<T> &&
- std::is_convertible_v<std::remove_pointer_t<decltype(std::data(std::declval<T&>()))>(*)[], U(*)[]>;
- }
- template <typename C, typename T>
- using EnableIfSpanCompatible = std::enable_if_t<is_span_compatible<C, T>(), int>;
- } // namespace realm::_impl
- namespace realm::util {
- template <typename T, size_t Extent>
- class Span {
- public:
- using element_type = T;
- using value_type = std::remove_cv_t<T>;
- using size_type = size_t;
- using difference_type = ptrdiff_t;
- using pointer = T*;
- using const_pointer = const T*;
- using reference = T&;
- using const_reference = const T&;
- using iterator = pointer;
- using reverse_iterator = std::reverse_iterator<iterator>;
- static constexpr size_type extent = Extent;
- template <size_t size = extent, std::enable_if_t<size == 0, int> = 0>
- constexpr Span() noexcept
- {
- }
- constexpr Span(const Span&) noexcept = default;
- constexpr Span& operator=(const Span&) noexcept = default;
- constexpr explicit Span(pointer ptr, size_type count)
- : m_data{ptr}
- {
- REALM_ASSERT(extent == count);
- }
- constexpr explicit Span(pointer begin, pointer end)
- : m_data{begin}
- {
- REALM_ASSERT(extent == std::distance(begin, end));
- }
- #if 0 // VS 16.9 incorrectly rejects this. 16.10+ support it
- constexpr Span(element_type (&arr)[extent]) noexcept
- : m_data{arr}
- {
- }
- #endif
- template <class U, std::enable_if_t<std::is_convertible_v<U (*)[], element_type (*)[]>, int> = 0>
- constexpr Span(std::array<U, extent>& arr) noexcept
- : m_data{arr.data()}
- {
- }
- template <class U, std::enable_if_t<std::is_convertible_v<const U (*)[], element_type (*)[]>, int> = 0>
- constexpr Span(const std::array<U, extent>& arr) noexcept
- : m_data{arr.data()}
- {
- }
- template <class Container, _impl::EnableIfSpanCompatible<Container, T> = 0>
- constexpr explicit Span(Container& c)
- : m_data{std::data(c)}
- {
- REALM_ASSERT(extent == std::size(c));
- }
- template <class Container, _impl::EnableIfSpanCompatible<const Container, T> = 0>
- constexpr explicit Span(const Container& c)
- : m_data{std::data(c)}
- {
- REALM_ASSERT(extent == std::size(c));
- }
- template <class U, std::enable_if_t<std::is_convertible_v<U (*)[], element_type (*)[]>, int> = 0>
- constexpr Span(const Span<U, extent>& other)
- : m_data{other.data()}
- {
- }
- #if 0 // VS 16.9 incorrectly rejects this. 16.10+ support it
- template <class U, std::enable_if_t<std::is_convertible_v<U (*)[], element_type (*)[]>, int> = 0>
- constexpr explicit Span(const Span<U, dynamic_extent>& other) noexcept
- : m_data{other.data()}
- {
- REALM_ASSERT(extent == other.size());
- }
- #endif
- template <size_t count>
- constexpr Span<element_type, count> first() const noexcept
- {
- static_assert(count <= extent);
- return Span<element_type, count>{data(), count};
- }
- template <size_t count>
- constexpr Span<element_type, count> last() const noexcept
- {
- static_assert(count <= extent);
- return Span<element_type, count>{data() + size() - count, count};
- }
- constexpr Span<element_type, dynamic_extent> first(size_type count) const noexcept
- {
- REALM_ASSERT(count <= size());
- return {data(), count};
- }
- constexpr Span<element_type, dynamic_extent> last(size_type count) const noexcept
- {
- REALM_ASSERT(count <= size());
- return {data() + size() - count, count};
- }
- template <size_t offset, size_t count = dynamic_extent>
- constexpr auto sub_span() const noexcept
- {
- static_assert(offset <= extent);
- static_assert(count == dynamic_extent || count <= extent - offset);
- using Ret = Span<element_type, count != dynamic_extent ? count : extent - offset>;
- return Ret{data() + offset, count == dynamic_extent ? size() - offset : count};
- }
- constexpr Span<element_type, dynamic_extent> sub_span(size_type offset,
- size_type count = dynamic_extent) const noexcept
- {
- REALM_ASSERT(offset <= size());
- REALM_ASSERT(count <= size() || count == dynamic_extent);
- if (count == dynamic_extent)
- return {data() + offset, size() - offset};
- REALM_ASSERT(count <= size() - offset);
- return {data() + offset, count};
- }
- constexpr size_type size() const noexcept
- {
- return extent;
- }
- constexpr size_type size_bytes() const noexcept
- {
- return extent * sizeof(element_type);
- }
- constexpr bool empty() const noexcept
- {
- return extent == 0;
- }
- constexpr reference operator[](size_type idx) const noexcept
- {
- REALM_ASSERT(idx < size());
- return m_data[idx];
- }
- constexpr reference front() const noexcept
- {
- REALM_ASSERT(!empty());
- return m_data[0];
- }
- constexpr reference back() const noexcept
- {
- REALM_ASSERT(!empty());
- return m_data[size() - 1];
- }
- constexpr pointer data() const noexcept
- {
- return m_data;
- }
- constexpr iterator begin() const noexcept
- {
- return data();
- }
- constexpr iterator end() const noexcept
- {
- return data() + size();
- }
- constexpr reverse_iterator rbegin() const noexcept
- {
- return reverse_iterator(end());
- }
- constexpr reverse_iterator rend() const noexcept
- {
- return reverse_iterator(begin());
- }
- Span<const std::byte, extent * sizeof(element_type)> as_bytes() const noexcept
- {
- return Span<const std::byte, extent * sizeof(element_type)>{reinterpret_cast<const std::byte*>(data()),
- size_bytes()};
- }
- Span<std::byte, extent * sizeof(element_type)> as_writable_bytes() const noexcept
- {
- return Span<std::byte, extent * sizeof(element_type)>{reinterpret_cast<std::byte*>(data()), size_bytes()};
- }
- private:
- pointer m_data = nullptr;
- };
- template <typename T>
- class Span<T, dynamic_extent> {
- public:
- using element_type = T;
- using value_type = std::remove_cv_t<T>;
- using size_type = size_t;
- using difference_type = ptrdiff_t;
- using pointer = T*;
- using const_pointer = const T*;
- using reference = T&;
- using const_reference = const T&;
- using iterator = pointer;
- using reverse_iterator = std::reverse_iterator<iterator>;
- static constexpr size_type extent = dynamic_extent;
- constexpr Span() noexcept = default;
- constexpr Span(const Span&) noexcept = default;
- constexpr Span& operator=(const Span&) noexcept = default;
- constexpr Span(pointer ptr, size_type count)
- : m_data{ptr}
- , m_size{count}
- {
- }
- constexpr Span(pointer f, pointer l)
- : m_data{f}
- , m_size{static_cast<size_t>(std::distance(f, l))}
- {
- }
- template <size_t size>
- constexpr Span(element_type (&arr)[size]) noexcept
- : m_data{arr}
- , m_size{size}
- {
- }
- template <class U, size_t size, std::enable_if_t<std::is_convertible_v<U (*)[], element_type (*)[]>, int> = 0>
- constexpr Span(std::array<U, size>& arr) noexcept
- : m_data{arr.data()}
- , m_size{size}
- {
- }
- template <class U, size_t size,
- std::enable_if_t<std::is_convertible_v<const U (*)[], element_type (*)[]>, int> = 0>
- constexpr Span(const std::array<U, size>& arr) noexcept
- : m_data{arr.data()}
- , m_size{size}
- {
- }
- template <class Container, _impl::EnableIfSpanCompatible<Container, T> = 0>
- constexpr Span(Container& c)
- : m_data{std::data(c)}
- , m_size{(size_type)std::size(c)}
- {
- }
- template <class Container, _impl::EnableIfSpanCompatible<const Container, T> = 0>
- constexpr Span(const Container& c)
- : m_data{std::data(c)}
- , m_size{(size_type)std::size(c)}
- {
- }
- template <class U, size_t E, std::enable_if_t<std::is_convertible_v<U (*)[], element_type (*)[]>, int> = 0>
- constexpr Span(const Span<U, E>& other) noexcept
- : m_data{other.data()}
- , m_size{other.size()}
- {
- }
- template <size_t count>
- constexpr Span<element_type, count> first() const noexcept
- {
- REALM_ASSERT(count <= m_size);
- return Span<element_type, count>{m_data, count};
- }
- template <size_t count>
- constexpr Span<element_type, count> last() const noexcept
- {
- REALM_ASSERT(count <= m_size);
- return Span<element_type, count>{m_data + m_size - count, count};
- }
- constexpr Span<element_type, dynamic_extent> first(size_type count) const noexcept
- {
- REALM_ASSERT(count <= m_size);
- return {m_data, count};
- }
- constexpr Span<element_type, dynamic_extent> last(size_type count) const noexcept
- {
- REALM_ASSERT(count <= m_size);
- return {m_data + m_size - count, count};
- }
- template <size_t offset, size_t count = dynamic_extent>
- constexpr Span<element_type, count> sub_span() const noexcept
- {
- REALM_ASSERT(offset <= m_size);
- REALM_ASSERT(count == dynamic_extent || count <= m_size - offset);
- return Span<element_type, count>{m_data + offset, count == dynamic_extent ? m_size - offset : count};
- }
- constexpr Span<element_type, dynamic_extent> sub_span(size_type offset,
- size_type count = dynamic_extent) const noexcept
- {
- REALM_ASSERT(offset <= m_size);
- REALM_ASSERT(count <= m_size || count == dynamic_extent);
- if (count == dynamic_extent)
- return {m_data + offset, m_size - offset};
- REALM_ASSERT(count <= m_size - offset);
- return {m_data + offset, count};
- }
- constexpr size_type size() const noexcept
- {
- return m_size;
- }
- constexpr size_type size_bytes() const noexcept
- {
- return m_size * sizeof(element_type);
- }
- constexpr bool empty() const noexcept
- {
- return m_size == 0;
- }
- constexpr reference operator[](size_type idx) const noexcept
- {
- REALM_ASSERT(idx < m_size);
- return m_data[idx];
- }
- constexpr reference front() const noexcept
- {
- REALM_ASSERT(m_size);
- return m_data[0];
- }
- constexpr reference back() const noexcept
- {
- REALM_ASSERT(m_size);
- return m_data[m_size - 1];
- }
- constexpr pointer data() const noexcept
- {
- return m_data;
- }
- constexpr iterator begin() const noexcept
- {
- return m_data;
- }
- constexpr iterator end() const noexcept
- {
- return m_data + m_size;
- }
- constexpr reverse_iterator rbegin() const noexcept
- {
- return reverse_iterator(end());
- }
- constexpr reverse_iterator rend() const noexcept
- {
- return reverse_iterator(begin());
- }
- Span<const std::byte, dynamic_extent> as_bytes() const noexcept
- {
- return {reinterpret_cast<const std::byte*>(m_data), size_bytes()};
- }
- Span<std::byte, dynamic_extent> as_writable_bytes() const noexcept
- {
- return {reinterpret_cast<std::byte*>(m_data), size_bytes()};
- }
- private:
- pointer m_data = nullptr;
- size_type m_size = 0;
- };
- template <typename T, size_t extent>
- auto as_bytes(Span<T, extent> s) noexcept -> decltype(s.as_bytes())
- {
- return s.as_bytes();
- }
- template <typename T, size_t extent>
- auto as_writable_bytes(Span<T, extent> s) noexcept
- -> std::enable_if_t<!std::is_const_v<T>, decltype(s.as_writable_bytes())>
- {
- return s.as_writable_bytes();
- }
- template <typename T, typename... Args>
- constexpr auto unsafe_span_cast(Args&&... args)
- {
- auto temp = Span(std::forward<Args>(args)...);
- return Span<T, decltype(temp)::extent>(reinterpret_cast<T*>(temp.data()), temp.size());
- }
- // Deduction guides
- template <typename T, size_t extent>
- Span(T (&)[extent]) -> Span<T, extent>;
- template <typename T, size_t extent>
- Span(std::array<T, extent>&) -> Span<T, extent>;
- template <typename T, size_t extent>
- Span(const std::array<T, extent>&) -> Span<const T, extent>;
- template <class Container>
- Span(Container&) -> Span<typename Container::value_type>;
- template <class Container>
- Span(const Container&) -> Span<const typename Container::value_type>;
- } // namespace realm::util
- #endif // REALM_UTIL_SPAN_HPP
|