diff --git a/LICENSE.txt b/LICENSE.txt index 5d4de206545..68c8e91665e 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2091,6 +2091,34 @@ René Nyffenegger rene.nyffenegger@adp-gmbh.ch -------------------------------------------------------------------------------- +The file cpp/src/arrow/vendored/span.hpp has the following license + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- + The file cpp/src/arrow/vendored/optional.hpp has the following license Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/cpp/src/arrow/c/bridge.cc b/cpp/src/arrow/c/bridge.cc index a43bf8104f2..8b8153465ee 100644 --- a/cpp/src/arrow/c/bridge.cc +++ b/cpp/src/arrow/c/bridge.cc @@ -38,6 +38,7 @@ #include "arrow/util/key_value_metadata.h" #include "arrow/util/logging.h" #include "arrow/util/macros.h" +#include "arrow/util/small_vector.h" #include "arrow/util/string_view.h" #include "arrow/util/value_parsing.h" #include "arrow/visitor_inline.h" @@ -47,6 +48,9 @@ namespace arrow { using internal::checked_cast; using internal::checked_pointer_cast; +using internal::SmallVector; +using internal::StaticVector; + using internal::ArrayExportGuard; using internal::ArrayExportTraits; using internal::SchemaExportGuard; @@ -65,9 +69,6 @@ Status ExportingNotImplemented(const DataType& type) { // XXX use Gandiva's SimpleArena? -template -using PoolVector = std::vector>; - template struct PoolAllocationMixin { static void* operator new(size_t size) { @@ -90,8 +91,8 @@ struct ExportedSchemaPrivateData : PoolAllocationMixin children_; - PoolVector child_pointers_; + SmallVector children_; + SmallVector child_pointers_; ExportedSchemaPrivateData() = default; ARROW_DEFAULT_MOVE_AND_ASSIGN(ExportedSchemaPrivateData); @@ -225,7 +226,7 @@ struct SchemaExporter { c_struct->flags = flags_; c_struct->n_children = static_cast(child_exporters_.size()); - c_struct->children = pdata->child_pointers_.data(); + c_struct->children = c_struct->n_children ? pdata->child_pointers_.data() : nullptr; c_struct->dictionary = dict_exporter_ ? &pdata->dictionary_ : nullptr; c_struct->private_data = pdata; c_struct->release = ReleaseExportedSchema; @@ -475,10 +476,10 @@ namespace { struct ExportedArrayPrivateData : PoolAllocationMixin { // The buffers are owned by the ArrayData member - PoolVector buffers_; + StaticVector buffers_; struct ArrowArray dictionary_; - PoolVector children_; - PoolVector child_pointers_; + SmallVector children_; + SmallVector child_pointers_; std::shared_ptr data_; @@ -574,7 +575,7 @@ struct ArrayExporter { c_struct_->n_buffers = static_cast(pdata->buffers_.size()); c_struct_->n_children = static_cast(pdata->child_pointers_.size()); c_struct_->buffers = pdata->buffers_.data(); - c_struct_->children = pdata->child_pointers_.data(); + c_struct_->children = c_struct_->n_children ? pdata->child_pointers_.data() : nullptr; c_struct_->dictionary = dict_exporter_ ? &pdata->dictionary_ : nullptr; c_struct_->private_data = pdata; c_struct_->release = ReleaseExportedArray; @@ -687,8 +688,8 @@ class FormatStringParser { } } - std::vector Split(util::string_view v, char delim = ',') { - std::vector parts; + SmallVector Split(util::string_view v, char delim = ',') { + SmallVector parts; size_t start = 0, end; while (true) { end = v.find_first_of(delim, start); diff --git a/cpp/src/arrow/result.h b/cpp/src/arrow/result.h index 3ef4f8cc7f7..597363f762b 100644 --- a/cpp/src/arrow/result.h +++ b/cpp/src/arrow/result.h @@ -26,6 +26,7 @@ #include "arrow/status.h" #include "arrow/util/compare.h" +#include "arrow/util/launder.h" namespace arrow { @@ -34,15 +35,6 @@ struct EnsureResult; namespace internal { -#if __cplusplus >= 201703L -using std::launder; -#else -template -constexpr T* launder(T* p) noexcept { - return p; -} -#endif - ARROW_EXPORT void DieWithMessage(const std::string& msg); ARROW_EXPORT void InvalidValueOrDie(const Status& st); @@ -168,7 +160,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { typename std::remove_cv::type>::type, Status>::value>::type> Result(U&& value) noexcept { // NOLINT(runtime/explicit) - ConstructValue(std::forward(value)); + data_.Construct(std::forward(value)); } /// Constructs a Result object that contains `value`. The resulting object @@ -183,7 +175,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { // NOTE `Result(U&& value)` above should be sufficient, but some compilers // fail matching it. Result(T&& value) noexcept { // NOLINT(runtime/explicit) - ConstructValue(std::move(value)); + data_.Construct(std::move(value)); } /// Copy constructor. @@ -198,7 +190,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { /// \param other The value to copy from. Result(const Result& other) : status_(other.status_) { if (ARROW_PREDICT_TRUE(status_.ok())) { - ConstructValue(other.ValueUnsafe()); + data_.Construct(other.ValueUnsafe()); } } @@ -213,7 +205,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { std::is_convertible::value>::type> Result(const Result& other) : status_(other.status_) { if (ARROW_PREDICT_TRUE(status_.ok())) { - ConstructValue(other.ValueUnsafe()); + data_.Construct(other.ValueUnsafe()); } } @@ -228,7 +220,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { Destroy(); status_ = other.status_; if (ARROW_PREDICT_TRUE(status_.ok())) { - ConstructValue(other.ValueUnsafe()); + data_.Construct(other.ValueUnsafe()); } return *this; } @@ -247,7 +239,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { Result(Result&& other) noexcept { if (ARROW_PREDICT_TRUE(other.status_.ok())) { status_ = std::move(other.status_); - ConstructValue(other.MoveValueUnsafe()); + data_.Construct(other.MoveValueUnsafe()); } else { // If we moved the status, the other status may become ok but the other // value hasn't been constructed => crash on other destructor. @@ -269,7 +261,7 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { Destroy(); if (ARROW_PREDICT_TRUE(other.status_.ok())) { status_ = std::move(other.status_); - ConstructValue(other.MoveValueUnsafe()); + data_.Construct(other.MoveValueUnsafe()); } else { // If we moved the status, the other status may become ok but the other // value hasn't been constructed => crash on other destructor. @@ -440,18 +432,13 @@ class ARROW_MUST_USE_TYPE Result : public util::EqualityComparable> { private: Status status_; // pointer-sized - typename std::aligned_storage::type data_; - - template - void ConstructValue(U&& u) { - new (&data_) T(std::forward(u)); - } + internal::AlignedStorage data_; void Destroy() { if (ARROW_PREDICT_TRUE(status_.ok())) { static_assert(offsetof(Result, status_) == 0, "Status is guaranteed to be at the start of Result<>"); - internal::launder(reinterpret_cast(&data_))->~T(); + data_.Destroy(); } } }; diff --git a/cpp/src/arrow/testing/gtest_util.h b/cpp/src/arrow/testing/gtest_util.h index 951fafdf968..a70330e20d0 100644 --- a/cpp/src/arrow/testing/gtest_util.h +++ b/cpp/src/arrow/testing/gtest_util.h @@ -554,22 +554,30 @@ void PrintTo(const Result& result, std::ostream* os) { } } -// A data type with only move constructors. +// A data type with only move constructors (no copy, no default). struct MoveOnlyDataType { explicit MoveOnlyDataType(int x) : data(new int(x)) {} MoveOnlyDataType(const MoveOnlyDataType& other) = delete; MoveOnlyDataType& operator=(const MoveOnlyDataType& other) = delete; - MoveOnlyDataType(MoveOnlyDataType&& other) { MoveFrom(&other); } - MoveOnlyDataType& operator=(MoveOnlyDataType&& other) { + MoveOnlyDataType(MoveOnlyDataType&& other) noexcept { MoveFrom(&other); } + MoveOnlyDataType& operator=(MoveOnlyDataType&& other) noexcept { MoveFrom(&other); return *this; } + MoveOnlyDataType& operator=(int x) { + if (data != nullptr) { + delete data; + } + data = new int(x); + return *this; + } + ~MoveOnlyDataType() { Destroy(); } - void Destroy() { + void Destroy() noexcept { if (data != nullptr) { delete data; data = nullptr; @@ -577,7 +585,7 @@ struct MoveOnlyDataType { } } - void MoveFrom(MoveOnlyDataType* other) { + void MoveFrom(MoveOnlyDataType* other) noexcept { Destroy(); data = other->data; other->data = nullptr; @@ -586,10 +594,14 @@ struct MoveOnlyDataType { int ToInt() const { return data == nullptr ? -42 : *data; } - bool operator==(int other) const { return data != nullptr && *data == other; } bool operator==(const MoveOnlyDataType& other) const { return data != nullptr && other.data != nullptr && *data == *other.data; } + bool operator<(const MoveOnlyDataType& other) const { + return data == nullptr || (other.data != nullptr && *data < *other.data); + } + + bool operator==(int other) const { return data != nullptr && *data == other; } friend bool operator==(int left, const MoveOnlyDataType& right) { return right == left; } diff --git a/cpp/src/arrow/util/CMakeLists.txt b/cpp/src/arrow/util/CMakeLists.txt index 660fb2657b6..aa875ab6bee 100644 --- a/cpp/src/arrow/util/CMakeLists.txt +++ b/cpp/src/arrow/util/CMakeLists.txt @@ -59,6 +59,7 @@ add_arrow_test(utility-test range_test.cc reflection_test.cc rle_encoding_test.cc + small_vector_test.cc stl_util_test.cc string_test.cc tdigest_test.cc @@ -88,6 +89,7 @@ add_arrow_benchmark(int_util_benchmark) add_arrow_benchmark(machine_benchmark) add_arrow_benchmark(queue_benchmark) add_arrow_benchmark(range_benchmark) +add_arrow_benchmark(small_vector_benchmark) add_arrow_benchmark(tdigest_benchmark) add_arrow_benchmark(thread_pool_benchmark) add_arrow_benchmark(trie_benchmark) diff --git a/cpp/src/arrow/util/launder.h b/cpp/src/arrow/util/launder.h new file mode 100644 index 00000000000..afdc59faa54 --- /dev/null +++ b/cpp/src/arrow/util/launder.h @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#pragma once + +#include +#include + +namespace arrow { +namespace internal { + +#if __cplusplus >= 201703L +using std::launder; +#else +template +constexpr T* launder(T* p) noexcept { + return p; +} +#endif + +template +class AlignedStorage { + public: + T* get() { return launder(reinterpret_cast(&data_)); } + constexpr const T* get() const { return launder(reinterpret_cast(&data_)); } + + T& operator*() { return *get(); } + constexpr const T& operator*() const { return *get(); } + + T* operator->() { return get(); } + constexpr const T* operator->() const { return get(); } + + void Destroy() noexcept { + if (!std::is_trivially_destructible::value) { + get()->~T(); + } + } + + template + void Construct(A&&... args) { + new (&data_) T(std::forward(args)...); + } + + void MoveFrom(AlignedStorage* other) noexcept { + static_assert(std::is_nothrow_move_constructible::value, ""); + Construct(std::move(*other->get())); + other->Destroy(); + } + + private: + typename std::aligned_storage::type data_; +}; + +} // namespace internal +} // namespace arrow diff --git a/cpp/src/arrow/util/make_unique.h b/cpp/src/arrow/util/make_unique.h index 850e20409b9..b046e6ef970 100644 --- a/cpp/src/arrow/util/make_unique.h +++ b/cpp/src/arrow/util/make_unique.h @@ -35,6 +35,7 @@ typename std::enable_if::value && std::extent::value == 0, std::unique_ptr>::type make_unique(std::size_t n) { using value_type = typename std::remove_extent::type; + // NOLINTNEXTLINE modernize-avoid-c-arrays return std::unique_ptr(new value_type[n]); } diff --git a/cpp/src/arrow/util/small_vector.h b/cpp/src/arrow/util/small_vector.h new file mode 100644 index 00000000000..e205e657d8b --- /dev/null +++ b/cpp/src/arrow/util/small_vector.h @@ -0,0 +1,431 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arrow/util/launder.h" +#include "arrow/util/macros.h" +#include "arrow/util/make_unique.h" +#include "arrow/util/span.h" + +namespace arrow { +namespace internal { + +template +class ConditionallyTriviallyDestructible { + protected: + ~ConditionallyTriviallyDestructible() { static_cast(this)->destroy(); } +}; +template +class ConditionallyTriviallyDestructible {}; + +template +struct StaticVectorStorage + : ConditionallyTriviallyDestructible, + std::is_trivially_destructible::value> { + std::array, N> static_data_; + size_t size_ = 0; + + StaticVectorStorage() noexcept = default; + + AlignedStorage* data_ptr() { return static_data_.data(); } + constexpr const AlignedStorage* const_data_ptr() const { + return static_data_.data(); + } + + util::span> data_span() { return {data_ptr(), size_}; } + + constexpr size_t capacity() const { return N; } + + constexpr size_t max_size() const { return N; } + + struct ResizeInProgress { + util::span> from, to; + + util::span> uninitialized() const { + return to.subspan(from.size()); + } + static constexpr bool to_new_allocation() { return false; } + }; + + ResizeInProgress increase_size(size_t new_size, ...) { + assert(new_size <= N); + + ResizeInProgress out; + out.from = {data_ptr(), size_}; + out.to = {data_ptr(), new_size}; + + size_ = new_size; + return out; + } + + void destroy() { + for (size_t i = 0; i < size_; ++i) { + static_data_[i].Destroy(); + } + + size_ = 0; + } + + // Move objects from another storage, but don't destroy any objects currently + // stored in *this. You need to call destroy() first if necessary (e.g. in a + // move assignment operator). + void move_from(StaticVectorStorage* other) noexcept { + for (size_t i = 0; i < other->size_; ++i) { + static_data_[i].MoveFrom(&other->static_data_[i]); + } + + size_ = other->size_; + other->size_ = 0; + } +}; + +template +struct SmallVectorStorage { + std::array, N> static_data_; + size_t size_ = 0; + AlignedStorage* data_ = static_data_.data(); + size_t dynamic_capacity_ = 0; + + SmallVectorStorage() noexcept = default; + + ~SmallVectorStorage() { destroy(); } + + AlignedStorage* data_ptr() { return data_; } + constexpr const AlignedStorage* const_data_ptr() const { return data_; } + + util::span> data_span() { return {data_ptr(), size_}; } + + constexpr size_t capacity() const { return dynamic_capacity_ ? dynamic_capacity_ : N; } + + constexpr size_t max_size() const { return std::numeric_limits::max(); } + + void destroy() { + for (size_t i = 0; i < size_; ++i) { + data_[i].Destroy(); + } + + if (dynamic_capacity_) { + delete[] data_; + } + + size_ = 0; + dynamic_capacity_ = 0; + } + + // Move objects from another storage, but don't destroy any objects currently + // stored in *this. You need to call destroy() first if necessary (e.g. in a + // move assignment operator). + void move_from(SmallVectorStorage* other) noexcept { + if (other->dynamic_capacity_) { + data_ = other->data_; + other->data_ = NULLPTR; + + dynamic_capacity_ = other->dynamic_capacity_; + other->dynamic_capacity_ = 0; + } else { + for (size_t i = 0; i < other->size_; ++i) { + data_[i].MoveFrom(&other->data_[i]); + } + } + + size_ = other->size_; + other->size_ = 0; + } + + struct ResizeInProgress { + util::span> from, to; + + util::span> uninitialized() const { + return to.subspan(from.size()); + } + bool to_new_allocation() const { return from.data() != to.data(); } + + // ensure old data_ is deleted after the resize is complete + std::unique_ptr[]> + scoped_delete; // NOLINT modernize-avoid-c-arrays + }; + + ResizeInProgress increase_size(size_t new_size, bool move_active = true) { + assert(new_size > size_); + + ResizeInProgress out; + out.from = {data_, size_}; + + if (new_size > N && new_size > dynamic_capacity_) { + // need to allocate more storage + if (dynamic_capacity_) { + out.scoped_delete.reset(data_); + } + + dynamic_capacity_ = std::max(dynamic_capacity_ * 2, new_size); + data_ = new AlignedStorage[dynamic_capacity_]; + + if (move_active) { + size_t i = 0; + for (auto& from : out.from) { + data_[i++].MoveFrom(&from); + } + } + } + + size_ = new_size; + out.to = {data_, size_}; + return out; + } + + private: + void set_dynamic_capacity(size_t new_capacity) { + assert(new_capacity >= size_); + + // NOLINTNEXTLINE modernize-avoid-c-arrays + std::unique_ptr[]> scoped_delete; + if (dynamic_capacity_) { + // delete old data_ at the close of this scope + scoped_delete.reset(data_); + } + + // NOLINTNEXTLINE modernize-avoid-c-arrays + auto new_data = internal::make_unique[]>(new_capacity); + for (size_t i = 0; i < size_; ++i) { + new_data[i].MoveFrom(&data_[i]); + } + + data_ = new_data.release(); + dynamic_capacity_ = new_capacity; + } +}; + +template +class StaticVectorImpl { + public: + using size_type = size_t; + using difference_type = ptrdiff_t; + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = T*; + using const_iterator = const T*; + + constexpr StaticVectorImpl() noexcept = default; + + // Move and copy constructors + StaticVectorImpl(StaticVectorImpl&& other) noexcept { + storage_.move_from(&other.storage_); + } + + StaticVectorImpl& operator=(StaticVectorImpl&& other) noexcept { + storage_.destroy(); + storage_.move_from(&other.storage_); + return *this; + } + + StaticVectorImpl(const StaticVectorImpl& other) { assign(other.begin(), other.end()); } + + StaticVectorImpl& operator=(const StaticVectorImpl& other) noexcept { + if (&other == this) { + return *this; + } + assign(other.begin(), other.end()); + return *this; + } + + // Automatic conversion from std::vector, for convenience + StaticVectorImpl(const std::vector& other) { // NOLINT: explicit + assign(other.begin(), other.end()); + } + + StaticVectorImpl& operator=(const std::vector& other) { + assign(other.begin(), other.end()); + return *this; + } + + StaticVectorImpl(std::vector&& other) noexcept { // NOLINT: explicit + assign(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); + } + + StaticVectorImpl& operator=(std::vector&& other) noexcept { + assign(std::make_move_iterator(other.begin()), std::make_move_iterator(other.end())); + return *this; + } + + // Constructing from count and optional initialization value + explicit StaticVectorImpl(size_t count) { resize(count); } + + StaticVectorImpl(size_t count, const T& value) { resize(count, value); } + + StaticVectorImpl(std::initializer_list values) { + assign(values.begin(), values.end()); + } + + constexpr bool empty() const { return storage_.size_ == 0; } + + constexpr size_t size() const { return storage_.size_; } + + constexpr size_t capacity() const { return storage_.capacity(); } + + constexpr size_t max_size() const { return storage_.max_size(); } + + T& operator[](size_t i) { return data()[i]; } + constexpr const T& operator[](size_t i) const { return data()[i]; } + + T& front() { return data()[0]; } + constexpr const T& front() const { return data()[0]; } + + T& back() { return data()[storage_.size_ - 1]; } + constexpr const T& back() const { return data()[storage_.size_ - 1]; } + + T* data() { return storage_.data_ptr()->get(); } + constexpr const T* data() const { return storage_.const_data_ptr()->get(); } + + iterator begin() { return iterator(data()); } + constexpr const_iterator begin() const { return data(); } + + iterator end() { return iterator(data() + storage_.size_); } + constexpr const_iterator end() const { return data() + storage_.size_; } + + void reserve(size_t n) { + if (n <= capacity()) return; + size_t old_size = size(); + storage_.increase_size(n); + storage_.size_ = old_size; + } + + void clear() { storage_.destroy(); } + + void push_back(const T& value) { emplace_back(value); } + + void push_back(T&& value) { emplace_back(std::move(value)); } + + template + void emplace_back(Args&&... args) { + auto& back = storage_.increase_size(size() + 1).uninitialized()[0]; + back.Construct(std::forward(args)...); + } + + template + iterator insert(const_iterator insert_at, ForwardIt first, ForwardIt last) { + const auto pos = insert_at - data(); + assert(pos >= 0 && static_cast(pos) <= size()); + + if (first == last) { + return data() + pos; + } + + auto num_displaced = size() - pos; + auto num_inserted = std::distance(first, last); + + auto resized = storage_.increase_size(size() + num_inserted, /*move_active=*/false); + + if (resized.to_new_allocation()) { + // even elements before pos must be moved + size_t i = 0; + for (auto& from : resized.from.first(pos)) { + resized.to[i++].MoveFrom(&from); + } + } + + // move displaced elements in reverse to make a gap for insertion + auto from_reversed = resized.from.rbegin(); + auto to_reversed = resized.to.rbegin(); + for (size_t i = 0; i < num_displaced; ++i) { + to_reversed[i].MoveFrom(&from_reversed[i]); + } + + // insert into the gap from the provided iterator + size_t i = pos; + while (first != last) { + resized.to[i++].Construct(*first++); + } + + return data() + pos; + } + + template + void assign(ForwardIt begin, ForwardIt end) { + auto n = static_cast(std::distance(begin, end)); + + if (n <= size()) { + decrease_size(n); + for (auto& slot : storage_.data_span()) { + *slot = *begin++; + } + return; + } + + auto resized = storage_.increase_size(n); + + for (auto& slot : resized.to.first(resized.from.size())) { + *slot = *begin++; + } + for (auto& slot : resized.uninitialized()) { + slot.Construct(*begin++); + } + } + + void resize(size_t n) { do_resize(n); } + + void resize(size_t n, const T& value) { do_resize(n, value); } + + private: + template + void do_resize(size_t n, const Args&... args) { + if (n <= size()) { + return decrease_size(n); + } + + auto resized = storage_.increase_size(n); + + for (auto& slot : resized.uninitialized()) { + slot.Construct(args...); + } + } + + void decrease_size(size_t n) { + assert(n <= size()); + + for (auto& slot : storage_.data_span().subspan(n)) { + slot.Destroy(); + } + + storage_.size_ = n; + } + + Storage storage_; +}; + +template +using StaticVector = StaticVectorImpl>; + +template +using SmallVector = StaticVectorImpl>; + +} // namespace internal +} // namespace arrow diff --git a/cpp/src/arrow/util/small_vector_benchmark.cc b/cpp/src/arrow/util/small_vector_benchmark.cc new file mode 100644 index 00000000000..f57670366c9 --- /dev/null +++ b/cpp/src/arrow/util/small_vector_benchmark.cc @@ -0,0 +1,348 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "arrow/testing/util.h" +#include "arrow/util/logging.h" +#include "arrow/util/macros.h" +#include "arrow/util/small_vector.h" + +namespace arrow { +namespace internal { + +template +T ValueInitializer(); +template +T ValueInitializer(int seed); + +template <> +int ValueInitializer() { + return 42; +} +template <> +int ValueInitializer(int seed) { + return 42; +} + +template <> +std::string ValueInitializer() { + return "42"; +} +template <> +std::string ValueInitializer(int seed) { + return std::string("x", seed & 0x3f); // avoid making string too long +} + +template <> +std::shared_ptr ValueInitializer>() { + return std::make_shared(42); +} +template <> +std::shared_ptr ValueInitializer>(int seed) { + return std::make_shared(seed); +} + +template +ARROW_NOINLINE int64_t ConsumeVector(Vector v) { + return reinterpret_cast(v.data()); +} + +// NOTE: The Move* benchmarks measure the overhead of constructing a +// stack-allocated vector in addition to the actual cost of moving. +// Unfortunately, other strategies such as allocating a large vector of +// vectors up front seem to fare even worse. + +template +void MoveEmptyVector(benchmark::State& state) { + constexpr int kNumIters = 1000; + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + Vector vec{}; + dummy += ConsumeVector(std::move(vec)); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters); +} + +template +void MoveShortVector(benchmark::State& state) { + using T = typename Vector::value_type; + constexpr int kSize = 3; + constexpr int kNumIters = 1000; + const auto initializer = ValueInitializer(); + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + Vector vec(kSize, initializer); + dummy += ConsumeVector(std::move(vec)); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters); +} + +template +void CopyEmptyVector(benchmark::State& state) { + constexpr int kNumIters = 1000; + + const Vector vec{}; + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + dummy += ConsumeVector(vec); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters); +} + +template +void CopyShortVector(benchmark::State& state) { + constexpr int kSize = 3; + constexpr int kNumIters = 1000; + + const Vector vec(kSize); + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + dummy += ConsumeVector(vec); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters); +} + +// With ARROW_NOINLINE, try to make sure the number of items is not constant-propagated +template +ARROW_NOINLINE void BenchmarkConstructFromStdVector(benchmark::State& state, + const int nitems) { + using T = typename Vector::value_type; + constexpr int kNumIters = 1000; + const std::vector src(nitems, ValueInitializer()); + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + Vector vec(src); + dummy += reinterpret_cast(vec.data()); + benchmark::ClobberMemory(); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters); +} + +template +void ConstructFromEmptyStdVector(benchmark::State& state) { + BenchmarkConstructFromStdVector(state, 0); +} + +template +void ConstructFromShortStdVector(benchmark::State& state) { + BenchmarkConstructFromStdVector(state, 3); +} + +// With ARROW_NOINLINE, try to make sure the number of items is not constant-propagated +template +ARROW_NOINLINE void BenchmarkVectorPushBack(benchmark::State& state, const int nitems) { + using T = typename Vector::value_type; + constexpr int kNumIters = 1000; + + ARROW_CHECK_LE(static_cast(nitems), Vector{}.max_size()); + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + Vector vec; + vec.reserve(nitems); + for (int j = 0; j < nitems; ++j) { + vec.push_back(ValueInitializer(j)); + } + dummy += reinterpret_cast(vec.data()); + benchmark::ClobberMemory(); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters * nitems); +} + +template +void ShortVectorPushBack(benchmark::State& state) { + BenchmarkVectorPushBack(state, 3); +} + +template +void LongVectorPushBack(benchmark::State& state) { + BenchmarkVectorPushBack(state, 100); +} + +// With ARROW_NOINLINE, try to make sure the source data is not constant-propagated +// (we could also use random data) +template +ARROW_NOINLINE void BenchmarkShortVectorInsert(benchmark::State& state, + const std::vector& src) { + constexpr int kNumIters = 1000; + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + Vector vec; + vec.reserve(4); + vec.insert(vec.begin(), src.begin(), src.begin() + 2); + vec.insert(vec.begin(), src.begin() + 2, src.end()); + dummy += reinterpret_cast(vec.data()); + benchmark::ClobberMemory(); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters * 4); +} + +template +void ShortVectorInsert(benchmark::State& state) { + using T = typename Vector::value_type; + const std::vector src(4, ValueInitializer()); + BenchmarkShortVectorInsert(state, src); +} + +template +ARROW_NOINLINE void BenchmarkVectorInsertAtEnd(benchmark::State& state, + const int nitems) { + using T = typename Vector::value_type; + constexpr int kNumIters = 1000; + + ARROW_CHECK_LE(static_cast(nitems), Vector{}.max_size()); + ARROW_CHECK_EQ(nitems % 2, 0); + + std::vector src; + for (int j = 0; j < nitems / 2; ++j) { + src.push_back(ValueInitializer(j)); + } + + for (auto _ : state) { + int64_t dummy = 0; + for (int i = 0; i < kNumIters; ++i) { + Vector vec; + vec.reserve(nitems); + vec.insert(vec.end(), src.begin(), src.end()); + vec.insert(vec.end(), src.begin(), src.end()); + dummy += reinterpret_cast(vec.data()); + benchmark::ClobberMemory(); + } + benchmark::DoNotOptimize(dummy); + } + + state.SetItemsProcessed(state.iterations() * kNumIters * nitems); +} + +template +void ShortVectorInsertAtEnd(benchmark::State& state) { + BenchmarkVectorInsertAtEnd(state, 4); +} + +template +void LongVectorInsertAtEnd(benchmark::State& state) { + BenchmarkVectorInsertAtEnd(state, 100); +} + +#define SHORT_VECTOR_BENCHMARKS(VEC_TYPE_FACTORY) \ + BENCHMARK_TEMPLATE(MoveEmptyVector, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(MoveEmptyVector, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(MoveEmptyVector, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(MoveShortVector, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(MoveShortVector, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(MoveShortVector, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(CopyEmptyVector, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(CopyEmptyVector, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(CopyEmptyVector, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(CopyShortVector, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(CopyShortVector, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(CopyShortVector, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(ConstructFromEmptyStdVector, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(ConstructFromEmptyStdVector, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(ConstructFromEmptyStdVector, \ + VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(ConstructFromShortStdVector, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(ConstructFromShortStdVector, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(ConstructFromShortStdVector, \ + VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(ShortVectorPushBack, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(ShortVectorPushBack, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(ShortVectorPushBack, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(ShortVectorInsert, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(ShortVectorInsert, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(ShortVectorInsert, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(ShortVectorInsertAtEnd, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(ShortVectorInsertAtEnd, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(ShortVectorInsertAtEnd, VEC_TYPE_FACTORY(std::shared_ptr)); + +#define LONG_VECTOR_BENCHMARKS(VEC_TYPE_FACTORY) \ + BENCHMARK_TEMPLATE(LongVectorPushBack, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(LongVectorPushBack, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(LongVectorPushBack, VEC_TYPE_FACTORY(std::shared_ptr)); \ + BENCHMARK_TEMPLATE(LongVectorInsertAtEnd, VEC_TYPE_FACTORY(int)); \ + BENCHMARK_TEMPLATE(LongVectorInsertAtEnd, VEC_TYPE_FACTORY(std::string)); \ + BENCHMARK_TEMPLATE(LongVectorInsertAtEnd, VEC_TYPE_FACTORY(std::shared_ptr)); + +// NOTE: the macro name below (STD_VECTOR etc.) is reflected in the +// benchmark name, so use descriptive names. + +#ifdef ARROW_WITH_BENCHMARKS_REFERENCE + +#define STD_VECTOR(T) std::vector +SHORT_VECTOR_BENCHMARKS(STD_VECTOR); +LONG_VECTOR_BENCHMARKS(STD_VECTOR); +#undef STD_VECTOR + +#endif + +#define STATIC_VECTOR(T) StaticVector +SHORT_VECTOR_BENCHMARKS(STATIC_VECTOR); +#undef STATIC_VECTOR + +#define SMALL_VECTOR(T) SmallVector +SHORT_VECTOR_BENCHMARKS(SMALL_VECTOR); +LONG_VECTOR_BENCHMARKS(SMALL_VECTOR); +#undef SMALL_VECTOR + +#undef SHORT_VECTOR_BENCHMARKS +#undef LONG_VECTOR_BENCHMARKS + +} // namespace internal +} // namespace arrow diff --git a/cpp/src/arrow/util/small_vector_test.cc b/cpp/src/arrow/util/small_vector_test.cc new file mode 100644 index 00000000000..5649a76938e --- /dev/null +++ b/cpp/src/arrow/util/small_vector_test.cc @@ -0,0 +1,720 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "arrow/testing/gtest_util.h" +#include "arrow/testing/matchers.h" +#include "arrow/util/small_vector.h" + +using testing::ElementsAre; +using testing::ElementsAreArray; + +namespace arrow { +namespace internal { + +struct HeapInt { + HeapInt() : HeapInt(0) {} + HeapInt(HeapInt&&) = default; + HeapInt& operator=(HeapInt&&) = default; + + explicit HeapInt(int x) : ptr(new int(x)) {} + + HeapInt& operator=(int x) { + ptr.reset(new int(x)); + return *this; + } + + HeapInt(const HeapInt& other) : HeapInt(other.ToInt()) {} + + HeapInt& operator=(const HeapInt& other) { + *this = other.ToInt(); + return *this; + } + + int ToInt() const { return ptr == nullptr ? -98 : *ptr; } + + bool operator==(const HeapInt& other) const { + return ptr != nullptr && other.ptr != nullptr && *ptr == *other.ptr; + } + bool operator<(const HeapInt& other) const { + return ptr == nullptr || (other.ptr != nullptr && *ptr < *other.ptr); + } + + bool operator==(int other) const { return ptr != nullptr && *ptr == other; } + + friend void PrintTo(const HeapInt& i, std::ostream* os) { *os << i.ToInt(); } + + std::unique_ptr ptr; +}; + +template +bool UsesStaticStorage(const Vector& v) { + const uint8_t* p = reinterpret_cast(v.data()); + if (p == nullptr) { + return true; + } + const uint8_t* v_start = reinterpret_cast(&v); + return (p >= v_start && p < v_start + sizeof(v)); +} + +struct StaticVectorTraits { + template + using VectorType = StaticVector; + + static bool CanOverflow() { return false; } + + static constexpr size_t MaxSizeFor(size_t n) { return n; } + + static constexpr size_t TestSizeFor(size_t max_size) { return max_size; } +}; + +struct SmallVectorTraits { + template + using VectorType = SmallVector; + + static bool CanOverflow() { return true; } + + static constexpr size_t MaxSizeFor(size_t n) { + return std::numeric_limits::max(); + } + + static constexpr size_t TestSizeFor(size_t max_size) { + return max_size > 6 ? max_size / 3 : 2; + } +}; + +using VectorTraits = ::testing::Types; + +template +struct VectorIntLikeParam { + using Traits = T; + using IntLike = I; + + static constexpr bool IsMoveOnly() { return !std::is_copy_constructible::value; } +}; + +using VectorIntLikeParams = + ::testing::Types, + VectorIntLikeParam, + VectorIntLikeParam, + VectorIntLikeParam, + VectorIntLikeParam, + VectorIntLikeParam>; + +template +class TestSmallStaticVector : public ::testing::Test { + template + using enable_if_t = typename std::enable_if::type; + + template + using enable_if_move_only = enable_if_t; + + template + using enable_if_not_move_only = enable_if_t; + + public: + using Traits = typename Param::Traits; + using IntLike = typename Param::IntLike; + + template + using VectorType = typename Traits::template VectorType; + + template + using IntVectorType = VectorType; + + template + IntVectorType MakeVector(const std::vector& init_values) { + IntVectorType ints; + for (auto v : init_values) { + ints.emplace_back(v); + } + return ints; + } + + template + IntVectorType CheckFourValues() { + IntVectorType ints; + EXPECT_EQ(ints.size(), 0); + EXPECT_EQ(ints.capacity(), N); + EXPECT_EQ(ints.max_size(), Traits::MaxSizeFor(N)); + EXPECT_TRUE(UsesStaticStorage(ints)); + + ints.emplace_back(3); + ints.emplace_back(42); + EXPECT_EQ(ints.size(), 2); + EXPECT_EQ(ints.capacity(), N); + EXPECT_EQ(ints[0], 3); + EXPECT_EQ(ints[1], 42); + EXPECT_TRUE(UsesStaticStorage(ints)); + + ints.push_back(IntLike(5)); + ints.emplace_back(false); + EXPECT_EQ(ints.size(), 4); + EXPECT_EQ(ints[2], 5); + EXPECT_EQ(ints[3], 0); + + ints[3] = IntLike(8); + EXPECT_EQ(ints[3], 8); + EXPECT_EQ(ints.back(), 8); + ints.front() = IntLike(-1); + EXPECT_EQ(ints[0], -1); + EXPECT_EQ(ints.front(), -1); + + return ints; + } + + void TestBasics() { + constexpr size_t N = Traits::TestSizeFor(4); + const auto ints = CheckFourValues(); + EXPECT_EQ(UsesStaticStorage(ints), !Traits::CanOverflow()); + } + + void TestAlwaysStatic() { + const auto ints = CheckFourValues<4>(); + EXPECT_TRUE(UsesStaticStorage(ints)); + } + + template + void CheckReserve(size_t max_size, bool expect_overflow) { + IntVectorType ints; + ints.emplace_back(123); + + size_t orig_capacity = ints.capacity(); + + ints.reserve(max_size / 3); + ASSERT_EQ(ints.capacity(), std::max(max_size / 3, orig_capacity)); + ASSERT_EQ(ints.size(), 1); + ASSERT_EQ(ints[0], 123); + + ints.reserve(4 * max_size / 5); + ASSERT_EQ(ints.capacity(), std::max(4 * max_size / 5, orig_capacity)); + ASSERT_EQ(ints.size(), 1); + ASSERT_EQ(ints[0], 123); + ASSERT_EQ(UsesStaticStorage(ints), !expect_overflow); + + size_t old_capacity = ints.capacity(); + ints.reserve(max_size / 5); // no-op + ASSERT_EQ(ints.capacity(), old_capacity); + ASSERT_EQ(ints.size(), 1); + ASSERT_EQ(ints[0], 123); + + ints.reserve(1); // no-op + ASSERT_EQ(ints.capacity(), old_capacity); + ASSERT_EQ(ints.size(), 1); + ASSERT_EQ(ints[0], 123); + } + + void TestReserve() { + CheckReserve(12, /*expect_overflow=*/Traits::CanOverflow()); + CheckReserve<12>(12, /*expect_overflow=*/false); + } + + template + void CheckClear(bool expect_overflow) { + IntVectorType ints = MakeVector({5, 6, 7, 8, 9}); + ASSERT_EQ(ints.size(), 5); + + ints.clear(); + ASSERT_EQ(ints.size(), 0); + ASSERT_EQ(ints.capacity(), N); // after clear, capacity == static capacity + ASSERT_EQ(UsesStaticStorage(ints), !expect_overflow); + } + + void TestClear() { + CheckClear(/*expect_overflow=*/Traits::CanOverflow()); + CheckClear<6>(/*expect_overflow=*/false); + } + + template + void TestConstructFromCount(enable_if_t* = 0) { + constexpr size_t N = Traits::TestSizeFor(4); + { + const IntVectorType ints(3); + ASSERT_EQ(ints.size(), 3); + ASSERT_EQ(ints.capacity(), std::max(N, 3)); + for (int i = 0; i < 3; ++i) { + ASSERT_EQ(ints[i], 0); + } + EXPECT_THAT(ints, ElementsAre(0, 0, 0)); + } + } + + template + void TestConstructFromCount(enable_if_t* = 0) { + GTEST_SKIP() << "Cannot construct vector of move-only type with value count"; + } + + template + void CheckConstructFromValues() { + { + const IntVectorType ints{}; + ASSERT_EQ(ints.size(), 0); + ASSERT_EQ(ints.capacity(), N); + } + { + const IntVectorType ints{IntLike(4), IntLike(5), IntLike(6)}; + ASSERT_EQ(ints.size(), 3); + ASSERT_EQ(ints.capacity(), std::max(N, 3)); + ASSERT_EQ(ints[0], 4); + ASSERT_EQ(ints[1], 5); + ASSERT_EQ(ints[2], 6); + ASSERT_EQ(ints.front(), 4); + ASSERT_EQ(ints.back(), 6); + } + } + + template + void TestConstructFromValues(enable_if_t* = 0) { + CheckConstructFromValues(); + CheckConstructFromValues<5>(); + } + + template + void TestConstructFromValues(enable_if_t* = 0) { + GTEST_SKIP() << "Cannot construct vector of move-only type with explicit values"; + } + + void CheckConstructFromMovedStdVector() { + constexpr size_t N = Traits::TestSizeFor(6); + { + std::vector src; + const IntVectorType ints(std::move(src)); + ASSERT_EQ(ints.size(), 0); + ASSERT_EQ(ints.capacity(), N); + } + { + std::vector src; + for (int i = 0; i < 6; ++i) { + src.emplace_back(i + 4); + } + const IntVectorType ints(std::move(src)); + ASSERT_EQ(ints.size(), 6); + ASSERT_EQ(ints.capacity(), std::max(N, 6)); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 7, 8, 9)); + } + } + + template + void CheckConstructFromCopiedStdVector(enable_if_t* = 0) { + constexpr size_t N = Traits::TestSizeFor(6); + { + const std::vector src; + const IntVectorType ints(src); + ASSERT_EQ(ints.size(), 0); + ASSERT_EQ(ints.capacity(), N); + } + { + std::vector values; + for (int i = 0; i < 6; ++i) { + values.emplace_back(i + 4); + } + const auto& src = values; + const IntVectorType ints(src); + ASSERT_EQ(ints.size(), 6); + ASSERT_EQ(ints.capacity(), std::max(N, 6)); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 7, 8, 9)); + } + } + + template + void CheckConstructFromCopiedStdVector(enable_if_t* = 0) {} + + void TestConstructFromStdVector() { + CheckConstructFromMovedStdVector(); + CheckConstructFromCopiedStdVector(); + } + + void CheckAssignFromMovedStdVector() { + constexpr size_t N = Traits::TestSizeFor(6); + { + std::vector src; + IntVectorType ints = MakeVector({42}); + ints = std::move(src); + ASSERT_EQ(ints.size(), 0); + ASSERT_EQ(ints.capacity(), N); + } + { + std::vector src; + for (int i = 0; i < 6; ++i) { + src.emplace_back(i + 4); + } + IntVectorType ints = MakeVector({42}); + ints = std::move(src); + ASSERT_EQ(ints.size(), 6); + ASSERT_EQ(ints.capacity(), std::max(N, 6)); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 7, 8, 9)); + } + } + + template + void CheckAssignFromCopiedStdVector(enable_if_t* = 0) { + constexpr size_t N = Traits::TestSizeFor(6); + { + const std::vector src; + IntVectorType ints = MakeVector({42}); + ints = src; + ASSERT_EQ(ints.size(), 0); + ASSERT_EQ(ints.capacity(), N); + } + { + std::vector values; + for (int i = 0; i < 6; ++i) { + values.emplace_back(i + 4); + } + const auto& src = values; + IntVectorType ints = MakeVector({42}); + ints = src; + ASSERT_EQ(ints.size(), 6); + ASSERT_EQ(ints.capacity(), std::max(N, 6)); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 7, 8, 9)); + } + } + + template + void CheckAssignFromCopiedStdVector(enable_if_t* = 0) {} + + void TestAssignFromStdVector() { + CheckAssignFromMovedStdVector(); + CheckAssignFromCopiedStdVector(); + } + + template + void CheckMove(bool expect_overflow) { + IntVectorType ints = MakeVector({4, 5, 6, 7, 8}); + + IntVectorType moved_ints(std::move(ints)); + ASSERT_EQ(moved_ints.size(), 5); + ASSERT_EQ(ints.size(), 0); + EXPECT_THAT(moved_ints, ElementsAre(4, 5, 6, 7, 8)); + ASSERT_EQ(UsesStaticStorage(moved_ints), !expect_overflow); + + IntVectorType moved_moved_ints = std::move(moved_ints); + ASSERT_EQ(moved_moved_ints.size(), 5); + ASSERT_EQ(moved_ints.size(), 0); + EXPECT_THAT(moved_moved_ints, ElementsAre(4, 5, 6, 7, 8)); + } + + void TestMove() { + CheckMove(/*expect_overflow=*/Traits::CanOverflow()); + CheckMove<5>(/*expect_overflow=*/false); + } + + template + void CheckCopy(bool expect_overflow) { + IntVectorType ints = MakeVector({4, 5, 6, 7, 8}); + + IntVectorType copied_ints(ints); + ASSERT_EQ(copied_ints.size(), 5); + ASSERT_EQ(ints.size(), 5); + EXPECT_THAT(copied_ints, ElementsAre(4, 5, 6, 7, 8)); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 7, 8)); + ASSERT_EQ(UsesStaticStorage(copied_ints), !expect_overflow); + + IntVectorType copied_copied_ints = copied_ints; + ASSERT_EQ(copied_copied_ints.size(), 5); + ASSERT_EQ(copied_ints.size(), 5); + EXPECT_THAT(copied_copied_ints, ElementsAre(4, 5, 6, 7, 8)); + EXPECT_THAT(copied_ints, ElementsAre(4, 5, 6, 7, 8)); + + auto copy_into = [](const IntVectorType& src, IntVectorType* dest) { + *dest = src; + }; + + // Copy into itself + // (avoiding the trivial form `copied_copied_ints = copied_copied_ints` + // that would produce a clang warning) + copy_into(copied_copied_ints, &copied_copied_ints); + ASSERT_EQ(copied_copied_ints.size(), 5); + EXPECT_THAT(copied_copied_ints, ElementsAre(4, 5, 6, 7, 8)); + } + + template + void TestCopy(enable_if_t* = 0) { + CheckCopy(/*expect_overflow=*/Traits::CanOverflow()); + CheckCopy<5>(/*expect_overflow=*/false); + } + + template + void TestCopy(enable_if_t* = 0) { + GTEST_SKIP() << "Cannot copy vector of move-only type"; + } + + template + void TestResize(enable_if_t* = 0) { + constexpr size_t N = Traits::TestSizeFor(8); + { + IntVectorType ints; + ints.resize(2); + ASSERT_GE(ints.capacity(), 2); + EXPECT_THAT(ints, ElementsAreArray(std::vector(2, 0))); + ints.resize(3); + ASSERT_GE(ints.capacity(), 3); + EXPECT_THAT(ints, ElementsAreArray(std::vector(3, 0))); + ints.resize(8); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAreArray(std::vector(8, 0))); + ints.resize(6); + ints.resize(6); // no-op + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAreArray(std::vector(6, 0))); + ints.resize(0); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAreArray(std::vector(0, 0))); + ints.resize(5); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAreArray(std::vector(5, 0))); + ints.resize(7); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAreArray(std::vector(7, 0))); + } + { + IntVectorType ints; + ints.resize(2, IntLike(2)); + ASSERT_GE(ints.capacity(), 2); + EXPECT_THAT(ints, ElementsAre(2, 2)); + ints.resize(3, IntLike(3)); + ASSERT_GE(ints.capacity(), 3); + EXPECT_THAT(ints, ElementsAre(2, 2, 3)); + ints.resize(8, IntLike(8)); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAre(2, 2, 3, 8, 8, 8, 8, 8)); + ints.resize(6, IntLike(6)); + ints.resize(6, IntLike(6)); // no-op + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAre(2, 2, 3, 8, 8, 8)); + ints.resize(0, IntLike(0)); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAre()); + ints.resize(5, IntLike(5)); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAre(5, 5, 5, 5, 5)); + ints.resize(7, IntLike(7)); + ASSERT_GE(ints.capacity(), 8); + EXPECT_THAT(ints, ElementsAre(5, 5, 5, 5, 5, 7, 7)); + } + } + + template + void TestResize(enable_if_t* = 0) { + GTEST_SKIP() << "Cannot resize vector of move-only type"; + } + + template + void CheckSort() { + IntVectorType ints; + for (int v : {42, 2, 123, -5, 6, 12, 8, 13}) { + ints.emplace_back(v); + } + std::sort(ints.begin(), ints.end()); + EXPECT_THAT(ints, ElementsAre(-5, 2, 6, 8, 12, 13, 42, 123)); + } + + void TestSort() { + CheckSort(); + CheckSort<8>(); + } + + void TestIterators() { + constexpr size_t N = Traits::TestSizeFor(5); + IntVectorType ints; + ASSERT_EQ(ints.begin(), ints.end()); + + for (int v : {5, 6, 7, 8, 42}) { + ints.emplace_back(v); + } + auto it = ints.begin(); + ASSERT_NE(it, ints.end()); + ASSERT_EQ(*it++, 5); + ASSERT_EQ(ints.end() - it, 4); + + auto it2 = ++it; + ASSERT_EQ(*it, 7); + ASSERT_EQ(*it2, 7); + ASSERT_EQ(it, it2); + + ASSERT_EQ(ints.end() - it, 3); + ASSERT_EQ(*it--, 7); + ASSERT_NE(it, it2); + + ASSERT_EQ(ints.end() - it, 4); + ASSERT_NE(it, ints.end()); + ASSERT_EQ(*--it, 5); + ASSERT_EQ(*it, 5); + ASSERT_EQ(ints.end() - it, 5); + it += 4; + ASSERT_EQ(*it, 42); + ASSERT_EQ(ints.end() - it, 1); + ASSERT_NE(it, ints.end()); + ASSERT_EQ(*(it - 3), 6); + ASSERT_EQ(++it, ints.end()); + } + + void TestConstIterators() { + constexpr size_t N = Traits::TestSizeFor(5); + { + const IntVectorType ints{}; + ASSERT_EQ(ints.begin(), ints.end()); + } + { + IntVectorType underlying_ints = MakeVector({5, 6, 7, 8, 42}); + const IntVectorType& ints = underlying_ints; + auto it = ints.begin(); + ASSERT_NE(it, ints.end()); + ASSERT_EQ(*it++, 5); + auto it2 = it++; + ASSERT_EQ(*it2, 6); + ASSERT_EQ(*it, 7); + ASSERT_NE(it, it2); + ASSERT_EQ(*++it2, 7); + ASSERT_EQ(it, it2); + + // Conversion from non-const iterator + it = underlying_ints.begin() + 1; + ASSERT_NE(it, underlying_ints.end()); + ASSERT_EQ(*it, 6); + it += underlying_ints.end() - it; + ASSERT_EQ(it, underlying_ints.end()); + } + } + + void TestInsertIteratorPair() { + // insert(const_iterator, InputIt first, InputIt last) + constexpr size_t N = Traits::TestSizeFor(10); + { + // empty source and destination + const std::vector src{}; + IntVectorType ints; + ints.insert(ints.begin(), src.begin(), src.end()); + ASSERT_EQ(ints.size(), 0); + + ints.emplace_back(42); + ints.insert(ints.begin(), src.begin(), src.end()); + ints.insert(ints.end(), src.begin(), src.end()); + EXPECT_THAT(ints, ElementsAre(42)); + } + const std::vector src{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + { + // insert at start + IntVectorType ints; + ints.insert(ints.begin(), src.begin() + 4, src.begin() + 7); + EXPECT_THAT(ints, ElementsAre(4, 5, 6)); + ints.insert(ints.begin(), src.begin() + 1, src.begin() + 4); + EXPECT_THAT(ints, ElementsAre(1, 2, 3, 4, 5, 6)); + ints.insert(ints.begin(), src.begin(), src.begin() + 1); + EXPECT_THAT(ints, ElementsAre(0, 1, 2, 3, 4, 5, 6)); + ints.insert(ints.begin(), src.begin() + 7, src.begin() + 10); + EXPECT_THAT(ints, ElementsAre(7, 8, 9, 0, 1, 2, 3, 4, 5, 6)); + } + { + // insert at end + IntVectorType ints; + ints.insert(ints.end(), src.begin() + 4, src.begin() + 7); + EXPECT_THAT(ints, ElementsAre(4, 5, 6)); + ints.insert(ints.end(), src.begin() + 1, src.begin() + 4); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 1, 2, 3)); + ints.insert(ints.end(), src.begin(), src.begin() + 1); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 1, 2, 3, 0)); + ints.insert(ints.end(), src.begin() + 7, src.begin() + 10); + EXPECT_THAT(ints, ElementsAre(4, 5, 6, 1, 2, 3, 0, 7, 8, 9)); + } + { + // insert at some point inside + IntVectorType ints; + ints.insert(ints.begin(), src.begin() + 4, src.begin() + 7); + EXPECT_THAT(ints, ElementsAre(4, 5, 6)); + ints.insert(ints.begin() + 2, src.begin() + 1, src.begin() + 4); + EXPECT_THAT(ints, ElementsAre(4, 5, 1, 2, 3, 6)); + ints.insert(ints.begin() + 2, src.begin(), src.begin() + 1); + EXPECT_THAT(ints, ElementsAre(4, 5, 0, 1, 2, 3, 6)); + ints.insert(ints.begin() + 2, src.begin() + 7, src.begin() + 10); + EXPECT_THAT(ints, ElementsAre(4, 5, 7, 8, 9, 0, 1, 2, 3, 6)); + } + { + // insert from a std::move_iterator (potentially move-only) + IntVectorType src = MakeVector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + IntVectorType ints; + auto move_it = [&](size_t i) { return std::make_move_iterator(src.begin() + i); }; + ints.insert(ints.begin(), move_it(4), move_it(7)); + EXPECT_THAT(ints, ElementsAre(4, 5, 6)); + ints.insert(ints.begin() + 2, move_it(1), move_it(4)); + EXPECT_THAT(ints, ElementsAre(4, 5, 1, 2, 3, 6)); + ints.insert(ints.begin() + 2, move_it(0), move_it(1)); + EXPECT_THAT(ints, ElementsAre(4, 5, 0, 1, 2, 3, 6)); + ints.insert(ints.begin() + 2, move_it(7), move_it(10)); + EXPECT_THAT(ints, ElementsAre(4, 5, 7, 8, 9, 0, 1, 2, 3, 6)); + } + } +}; + +TYPED_TEST_SUITE(TestSmallStaticVector, VectorIntLikeParams); + +TYPED_TEST(TestSmallStaticVector, Basics) { this->TestBasics(); } + +TYPED_TEST(TestSmallStaticVector, AlwaysStatic) { this->TestAlwaysStatic(); } + +TYPED_TEST(TestSmallStaticVector, Reserve) { this->TestReserve(); } + +TYPED_TEST(TestSmallStaticVector, Clear) { this->TestClear(); } + +TYPED_TEST(TestSmallStaticVector, ConstructFromCount) { this->TestConstructFromCount(); } + +TYPED_TEST(TestSmallStaticVector, ConstructFromValues) { + this->TestConstructFromValues(); +} + +TYPED_TEST(TestSmallStaticVector, ConstructFromStdVector) { + this->TestConstructFromStdVector(); +} + +TYPED_TEST(TestSmallStaticVector, AssignFromStdVector) { + this->TestAssignFromStdVector(); +} + +TYPED_TEST(TestSmallStaticVector, Move) { this->TestMove(); } + +TYPED_TEST(TestSmallStaticVector, Copy) { this->TestCopy(); } + +TYPED_TEST(TestSmallStaticVector, Resize) { this->TestResize(); } + +TYPED_TEST(TestSmallStaticVector, Sort) { this->TestSort(); } + +TYPED_TEST(TestSmallStaticVector, Iterators) { this->TestIterators(); } + +TYPED_TEST(TestSmallStaticVector, ConstIterators) { this->TestConstIterators(); } + +TYPED_TEST(TestSmallStaticVector, InsertIteratorPair) { this->TestInsertIteratorPair(); } + +TEST(StaticVector, Traits) { + ASSERT_TRUE((std::is_trivially_destructible>::value)); + ASSERT_FALSE((std::is_trivially_destructible>::value)); +} + +TEST(SmallVector, Traits) { + ASSERT_FALSE((std::is_trivially_destructible>::value)); +} + +} // namespace internal +} // namespace arrow diff --git a/cpp/src/arrow/util/span.h b/cpp/src/arrow/util/span.h new file mode 100644 index 00000000000..d8e22163a80 --- /dev/null +++ b/cpp/src/arrow/util/span.h @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. + +#pragma once + +#define span_FEATURE_MAKE_SPAN 1 +#define span_CONFIG_SELECT_SPAN span_SPAN_NONSTD + +#include "arrow/vendored/span.hpp" // IWYU pragma: export + +namespace arrow { +namespace util { + +using nonstd::make_span; +using nonstd::span; + +} // namespace util +} // namespace arrow diff --git a/cpp/src/arrow/vendored/span.hpp b/cpp/src/arrow/vendored/span.hpp new file mode 100644 index 00000000000..eba3afd2c01 --- /dev/null +++ b/cpp/src/arrow/vendored/span.hpp @@ -0,0 +1,1882 @@ +// +// span for C++98 and later. +// Based on http://wg21.link/p0122r7 +// For more information see https://github.com/martinmoene/span-lite +// +// Copyright 2018-2020 Martin Moene +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef NONSTD_SPAN_HPP_INCLUDED +#define NONSTD_SPAN_HPP_INCLUDED + +#define span_lite_MAJOR 0 +#define span_lite_MINOR 9 +#define span_lite_PATCH 2 + +#define span_lite_VERSION span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH) + +#define span_STRINGIFY( x ) span_STRINGIFY_( x ) +#define span_STRINGIFY_( x ) #x + +// span configuration: + +#define span_SPAN_DEFAULT 0 +#define span_SPAN_NONSTD 1 +#define span_SPAN_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include() +# include +# endif +#define span_HAVE_TWEAK_HEADER 1 +#else +#define span_HAVE_TWEAK_HEADER 0 +//# pragma message("span.hpp: Note: Tweak header not supported.") +#endif + +// span selection and configuration: + +#define span_HAVE( feature ) ( span_HAVE_##feature ) + +#ifndef span_CONFIG_SELECT_SPAN +# define span_CONFIG_SELECT_SPAN ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD ) +#endif + +#ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +#endif + +#ifndef span_CONFIG_SIZE_TYPE +# define span_CONFIG_SIZE_TYPE std::size_t +#endif + +#ifdef span_CONFIG_INDEX_TYPE +# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`. +#endif + +// span configuration (features): + +#ifndef span_FEATURE_WITH_CONTAINER +#ifdef span_FEATURE_WITH_CONTAINER_TO_STD +# define span_FEATURE_WITH_CONTAINER span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD ) +#else +# define span_FEATURE_WITH_CONTAINER 0 +#endif +#endif + +#ifndef span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE +# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE 0 +#endif + +#ifndef span_FEATURE_MEMBER_AT +# define span_FEATURE_MEMBER_AT 0 +#endif + +#ifndef span_FEATURE_MEMBER_BACK_FRONT +# define span_FEATURE_MEMBER_BACK_FRONT 1 +#endif + +#ifndef span_FEATURE_MEMBER_CALL_OPERATOR +# define span_FEATURE_MEMBER_CALL_OPERATOR 0 +#endif + +#ifndef span_FEATURE_MEMBER_SWAP +# define span_FEATURE_MEMBER_SWAP 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB 0 +#elif span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 1 +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 1 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 0 +#endif + +#ifndef span_FEATURE_COMPARISON +# define span_FEATURE_COMPARISON 0 // Note: C++20 does not provide comparison +#endif + +#ifndef span_FEATURE_SAME +# define span_FEATURE_SAME 0 +#endif + +#if span_FEATURE_SAME && !span_FEATURE_COMPARISON +# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON` +#endif + +#ifndef span_FEATURE_MAKE_SPAN +#ifdef span_FEATURE_MAKE_SPAN_TO_STD +# define span_FEATURE_MAKE_SPAN span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD ) +#else +# define span_FEATURE_MAKE_SPAN 0 +#endif +#endif + +#ifndef span_FEATURE_BYTE_SPAN +# define span_FEATURE_BYTE_SPAN 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef span_CONFIG_NO_EXCEPTIONS +# if _MSC_VER +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define span_CONFIG_NO_EXCEPTIONS 0 +# else +# define span_CONFIG_NO_EXCEPTIONS 1 +# undef span_CONFIG_CONTRACT_VIOLATION_THROWS +# undef span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# define span_CONFIG_CONTRACT_VIOLATION_THROWS 0 +# define span_CONFIG_CONTRACT_VIOLATION_TERMINATES 1 +# endif +#endif + +// Control pre- and postcondition violation behaviour: + +#if defined( span_CONFIG_CONTRACT_LEVEL_ON ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#elif defined( span_CONFIG_CONTRACT_LEVEL_OFF ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x00 +#elif defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x01 +#elif defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x10 +#else +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V span_CONFIG_CONTRACT_VIOLATION_THROWS +#else +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \ + defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both. +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef span_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define span_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define span_CPLUSPLUS __cplusplus +# endif +#endif + +#define span_CPP98_OR_GREATER ( span_CPLUSPLUS >= 199711L ) +#define span_CPP11_OR_GREATER ( span_CPLUSPLUS >= 201103L ) +#define span_CPP14_OR_GREATER ( span_CPLUSPLUS >= 201402L ) +#define span_CPP17_OR_GREATER ( span_CPLUSPLUS >= 201703L ) +#define span_CPP20_OR_GREATER ( span_CPLUSPLUS >= 202000L ) + +// C++ language version (represent 98 as 3): + +#define span_CPLUSPLUS_V ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +#define span_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V ) + +#define span_CONFIG( feature ) ( span_CONFIG_##feature ) +#define span_FEATURE( feature ) ( span_FEATURE_##feature ) +#define span_FEATURE_TO_STD( feature ) ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) ) + +// Use C++20 std::span if available and requested: + +#if span_CPP20_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define span_HAVE_STD_SPAN 1 +# else +# define span_HAVE_STD_SPAN 0 +# endif +#else +# define span_HAVE_STD_SPAN 0 +#endif + +#define span_USES_STD_SPAN ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) ) + +// +// Use C++20 std::span: +// + +#if span_USES_STD_SPAN + +#include + +namespace nonstd { + +using std::span; + +// Note: C++20 does not provide comparison +// using std::operator==; +// using std::operator!=; +// using std::operator<; +// using std::operator<=; +// using std::operator>; +// using std::operator>=; +} // namespace nonstd + +#else // span_USES_STD_SPAN + +#include + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 span_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 span_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 span_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 span_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 span_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 span_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 span_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 span_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 span_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 span_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 span_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define span_COMPILER_MSVC_VER (_MSC_VER ) +# define span_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define span_COMPILER_MSVC_VER 0 +# define span_COMPILER_MSVC_VERSION 0 +#endif + +#define span_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined(__clang__) +# define span_COMPILER_CLANG_VERSION span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define span_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define span_COMPILER_GNUC_VERSION span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define span_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define span_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler warning suppression: + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wmismatched-tags" +# define span_RESTORE_WARNINGS() _Pragma( "clang diagnostic pop" ) + +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +# define span_RESTORE_WARNINGS() _Pragma( "GCC diagnostic pop" ) + +#elif span_COMPILER_MSVC_VER >= 1900 +# define span_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +# define span_RESTORE_WARNINGS() __pragma(warning(pop )) + +// Suppress the following MSVC GSL warnings: +// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' +// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narrow +// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead +// - C26490: gsl::t.1 : don't use reinterpret_cast + +span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 ) + +#else +# define span_RESTORE_WARNINGS() /*empty*/ +#endif + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define span_HAS_CPP0X _HAS_CPP0X +#else +# define span_HAS_CPP0X 0 +#endif + +#define span_CPP11_80 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400) +#define span_CPP11_90 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500) +#define span_CPP11_100 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600) +#define span_CPP11_110 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700) +#define span_CPP11_120 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP11_140 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP14_000 (span_CPP14_OR_GREATER) +#define span_CPP14_120 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP14_140 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP17_000 (span_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define span_HAVE_ALIAS_TEMPLATE span_CPP11_140 +#define span_HAVE_AUTO span_CPP11_100 +#define span_HAVE_CONSTEXPR_11 span_CPP11_140 +#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG span_CPP11_120 +#define span_HAVE_EXPLICIT_CONVERSION span_CPP11_140 +#define span_HAVE_INITIALIZER_LIST span_CPP11_120 +#define span_HAVE_IS_DEFAULT span_CPP11_140 +#define span_HAVE_IS_DELETE span_CPP11_140 +#define span_HAVE_NOEXCEPT span_CPP11_140 +#define span_HAVE_NULLPTR span_CPP11_100 +#define span_HAVE_STATIC_ASSERT span_CPP11_100 + +// Presence of C++14 language features: + +#define span_HAVE_CONSTEXPR_14 span_CPP14_000 + +// Presence of C++17 language features: + +#define span_HAVE_DEPRECATED span_CPP17_000 +#define span_HAVE_NODISCARD span_CPP17_000 +#define span_HAVE_NORETURN span_CPP17_000 + +// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 + +#if defined(__cpp_deduction_guides) +# define span_HAVE_DEDUCTION_GUIDES 1 +#else +# define span_HAVE_DEDUCTION_GUIDES (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 )) +#endif + +// Presence of C++ library features: + +#define span_HAVE_ADDRESSOF span_CPP17_000 +#define span_HAVE_ARRAY span_CPP11_110 +#define span_HAVE_BYTE span_CPP17_000 +#define span_HAVE_CONDITIONAL span_CPP11_120 +#define span_HAVE_CONTAINER_DATA_METHOD (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X )) +#define span_HAVE_DATA span_CPP17_000 +#define span_HAVE_LONGLONG span_CPP11_80 +#define span_HAVE_REMOVE_CONST span_CPP11_110 +#define span_HAVE_SNPRINTF span_CPP11_140 +#define span_HAVE_STRUCT_BINDING span_CPP11_120 +#define span_HAVE_TYPE_TRAITS span_CPP11_90 + +// Presence of byte-lite: + +#ifdef NONSTD_BYTE_LITE_HPP +# define span_HAVE_NONSTD_BYTE 1 +#else +# define span_HAVE_NONSTD_BYTE 0 +#endif + +// C++ feature usage: + +#if span_HAVE_ADDRESSOF +# define span_ADDRESSOF(x) std::addressof(x) +#else +# define span_ADDRESSOF(x) (&x) +#endif + +#if span_HAVE_CONSTEXPR_11 +# define span_constexpr constexpr +#else +# define span_constexpr /*span_constexpr*/ +#endif + +#if span_HAVE_CONSTEXPR_14 +# define span_constexpr14 constexpr +#else +# define span_constexpr14 /*span_constexpr*/ +#endif + +#if span_HAVE_EXPLICIT_CONVERSION +# define span_explicit explicit +#else +# define span_explicit /*explicit*/ +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete = delete +#else +# define span_is_delete +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete_access public +#else +# define span_is_delete_access private +#endif + +#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V +# define span_noexcept noexcept +#else +# define span_noexcept /*noexcept*/ +#endif + +#if span_HAVE_NULLPTR +# define span_nullptr nullptr +#else +# define span_nullptr NULL +#endif + +#if span_HAVE_DEPRECATED +# define span_deprecated(msg) [[deprecated(msg)]] +#else +# define span_deprecated(msg) /*[[deprecated]]*/ +#endif + +#if span_HAVE_NODISCARD +# define span_nodiscard [[nodiscard]] +#else +# define span_nodiscard /*[[nodiscard]]*/ +#endif + +#if span_HAVE_NORETURN +# define span_noreturn [[noreturn]] +#else +# define span_noreturn /*[[noreturn]]*/ +#endif + +// Other features: + +#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG +#define span_HAVE_ITERATOR_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG + +// Additional includes: + +#if span_HAVE( ADDRESSOF ) +# include +#endif + +#if span_HAVE( ARRAY ) +# include +#endif + +#if span_HAVE( BYTE ) +# include +#endif + +#if span_HAVE( DATA ) +# include // for std::data(), std::size() +#endif + +#if span_HAVE( TYPE_TRAITS ) +# include +#endif + +#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) +# include +#endif + +#if span_FEATURE( MEMBER_AT ) > 1 +# include +#endif + +#if ! span_CONFIG( NO_EXCEPTIONS ) +# include +#endif + +// Contract violation + +#define span_ELIDE_CONTRACT_EXPECTS ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) ) +#define span_ELIDE_CONTRACT_ENSURES ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) ) + +#if span_ELIDE_CONTRACT_EXPECTS +# define span_constexpr_exp span_constexpr +# define span_EXPECTS( cond ) /* Expect elided */ +#else +# define span_constexpr_exp span_constexpr14 +# define span_EXPECTS( cond ) span_CONTRACT_CHECK( "Precondition", cond ) +#endif + +#if span_ELIDE_CONTRACT_ENSURES +# define span_constexpr_ens span_constexpr +# define span_ENSURES( cond ) /* Ensures elided */ +#else +# define span_constexpr_ens span_constexpr14 +# define span_ENSURES( cond ) span_CONTRACT_CHECK( "Postcondition", cond ) +#endif + +#define span_CONTRACT_CHECK( type, cond ) \ + cond ? static_cast< void >( 0 ) \ + : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." ) + +#ifdef __GNUG__ +# define span_LOCATION( file, line ) file ":" span_STRINGIFY( line ) +#else +# define span_LOCATION( file, line ) file "(" span_STRINGIFY( line ) ")" +#endif + +// Method enabling + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +#define span_REQUIRES_0(VA) \ + template< bool B = (VA), typename std::enable_if::type = 0 > + +# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 ) +// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments +# define span_REQUIRES_T(VA) \ + , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type +# else +# define span_REQUIRES_T(VA) \ + , typename std::enable_if< (VA), int >::type = 0 +# endif + +#define span_REQUIRES_R(R, VA) \ + typename std::enable_if< (VA), R>::type + +#define span_REQUIRES_A(VA) \ + , typename std::enable_if< (VA), void*>::type = nullptr + +#else + +# define span_REQUIRES_0(VA) /*empty*/ +# define span_REQUIRES_T(VA) /*empty*/ +# define span_REQUIRES_R(R, VA) R +# define span_REQUIRES_A(VA) /*empty*/ + +#endif + +namespace nonstd { +namespace span_lite { + +// [views.constants], constants + +typedef span_CONFIG_EXTENT_TYPE extent_t; +typedef span_CONFIG_SIZE_TYPE size_t; + +span_constexpr const extent_t dynamic_extent = static_cast( -1 ); + +template< class T, extent_t Extent = dynamic_extent > +class span; + +// Tag to select span constructor taking a container (prevent ms-gsl warning C26426): + +struct with_container_t { span_constexpr with_container_t() span_noexcept {} }; +const span_constexpr with_container_t with_container; + +// C++11 emulation: + +namespace std11 { + +#if span_HAVE( REMOVE_CONST ) + +using std::remove_cv; +using std::remove_const; +using std::remove_volatile; + +#else + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const< T const > { typedef T type; }; + +template< class T > struct remove_volatile { typedef T type; }; +template< class T > struct remove_volatile< T volatile > { typedef T type; }; + +template< class T > +struct remove_cv +{ + typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type; +}; + +#endif // span_HAVE( REMOVE_CONST ) + +#if span_HAVE( TYPE_TRAITS ) + +using std::is_same; +using std::is_signed; +using std::integral_constant; +using std::true_type; +using std::false_type; +using std::remove_reference; + +#else + +template< class T, T v > struct integral_constant { enum { value = v }; }; +typedef integral_constant< bool, true > true_type; +typedef integral_constant< bool, false > false_type; + +template< class T, class U > struct is_same : false_type{}; +template< class T > struct is_same : true_type{}; + +template< typename T > struct is_signed : false_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; +template<> struct is_signed : true_type {}; + +#endif + +} // namespace std11 + +// C++17 emulation: + +namespace std17 { + +template< bool v > struct bool_constant : std11::integral_constant{}; + +#if span_CPP11_120 + +template< class...> +using void_t = void; + +#endif + +#if span_HAVE( DATA ) + +using std::data; +using std::size; + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< typename T, std::size_t N > +inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t +{ + return N; +} + +template< typename C > +inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() ) +{ + return cont.size(); +} + +template< typename T, std::size_t N > +inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T* +{ + return &arr[0]; +} + +template< typename C > +inline span_constexpr auto data( C & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename C > +inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename E > +inline span_constexpr auto data( std::initializer_list il ) span_noexcept -> E const * +{ + return il.begin(); +} + +#endif // span_HAVE( DATA ) + +#if span_HAVE( BYTE ) +using std::byte; +#elif span_HAVE( NONSTD_BYTE ) +using nonstd::byte; +#endif + +} // namespace std17 + +// C++20 emulation: + +namespace std20 { + +#if span_HAVE( DEDUCTION_GUIDES ) +template< class T > +using iter_reference_t = decltype( *std::declval() ); +#endif + +} // namespace std20 + +// Implementation details: + +namespace detail { + +/*enum*/ struct enabler{}; + +template< typename T > +bool is_positive( T x ) +{ + return std11::is_signed::value ? x >= 0 : true; +} + +#if span_HAVE( TYPE_TRAITS ) + +template< class Q > +struct is_span_oracle : std::false_type{}; + +template< class T, span_CONFIG_EXTENT_TYPE Extent > +struct is_span_oracle< span > : std::true_type{}; + +template< class Q > +struct is_span : is_span_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_std_array_oracle : std::false_type{}; + +#if span_HAVE( ARRAY ) + +template< class T, std::size_t Extent > +struct is_std_array_oracle< std::array > : std::true_type{}; + +#endif + +template< class Q > +struct is_std_array : is_std_array_oracle< typename std::remove_cv::type >{}; + +template< class Q > +struct is_array : std::false_type {}; + +template< class T > +struct is_array : std::true_type {}; + +template< class T, std::size_t N > +struct is_array : std::true_type {}; + +#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 ) + +template< class, class = void > +struct has_size_and_data : std::false_type{}; + +template< class C > +struct has_size_and_data +< + C, std17::void_t< + decltype( std17::size(std::declval()) ), + decltype( std17::data(std::declval()) ) > +> : std::true_type{}; + +template< class, class, class = void > +struct is_compatible_element : std::false_type {}; + +template< class C, class E > +struct is_compatible_element +< + C, E, std17::void_t< + decltype( std17::data(std::declval()) ) > +> : std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >{}; + +template< class C > +struct is_container : std17::bool_constant +< + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && has_size_and_data< C >::value +>{}; + +template< class C, class E > +struct is_compatible_container : std17::bool_constant +< + is_container::value + && is_compatible_element::value +>{}; + +#else // span_CPP11_140 + +template< + class C, class E + span_REQUIRES_T(( + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && ( std::is_convertible< typename std::remove_pointer() ) )>::type(*)[], E(*)[] >::value) + // && has_size_and_data< C >::value + )) + , class = decltype( std17::size(std::declval()) ) + , class = decltype( std17::data(std::declval()) ) +> +struct is_compatible_container : std::true_type{}; + +#endif // span_CPP11_140 + +#endif // span_HAVE( TYPE_TRAITS ) + +#if ! span_CONFIG( NO_EXCEPTIONS ) +#if span_FEATURE( MEMBER_AT ) > 1 + +// format index and size: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wlong-long" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wformat=ll" +# pragma GCC diagnostic ignored "-Wlong-long" +#endif + +inline void throw_out_of_range( size_t idx, size_t size ) +{ + const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)"; + char buffer[ 2 * 20 + sizeof fmt ]; + sprintf( buffer, fmt, static_cast(idx), static_cast(size) ); + + throw std::out_of_range( buffer ); +} + +#else // MEMBER_AT + +inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ ) +{ + throw std::out_of_range( "span::at(): index outside span" ); +} +#endif // MEMBER_AT +#endif // NO_EXCEPTIONS + +#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +struct contract_violation : std::logic_error +{ + explicit contract_violation( char const * const message ) + : std::logic_error( message ) + {} +}; + +inline void report_contract_violation( char const * msg ) +{ + throw contract_violation( msg ); +} + +#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept +{ + std::terminate(); +} + +#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +} // namespace detail + +// Prevent signed-unsigned mismatch: + +#define span_sizeof(T) static_cast( sizeof(T) ) + +template< class T > +inline span_constexpr size_t to_size( T size ) +{ + return static_cast( size ); +} + +// +// [views.span] - A view over a contiguous, single-dimension sequence of objects +// +template< class T, extent_t Extent /*= dynamic_extent*/ > +class span +{ +public: + // constants and types + + typedef T element_type; + typedef typename std11::remove_cv< T >::type value_type; + + typedef T & reference; + typedef T * pointer; + typedef T const * const_pointer; + typedef T const & const_reference; + + typedef size_t size_type; + typedef extent_t extent_type; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef std::ptrdiff_t difference_type; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + +// static constexpr extent_type extent = Extent; + enum { extent = Extent }; + + // 26.7.3.2 Constructors, copy, and assignment [span.cons] + + span_REQUIRES_0( + ( Extent == 0 ) || + ( Extent == dynamic_extent ) + ) + span_constexpr span() span_noexcept + : data_( span_nullptr ) + , size_( 0 ) + { + // span_EXPECTS( data() == span_nullptr ); + // span_EXPECTS( size() == 0 ); + } + +#if span_HAVE( ITERATOR_CTOR ) + // Didn't yet succeed in combining the next two constructors: + + span_constexpr_exp span( std::nullptr_t, size_type count ) + : data_( span_nullptr ) + , size_( count ) + { + span_EXPECTS( data_ == span_nullptr && count == 0 ); + } + + template< typename It + span_REQUIRES_T(( + std::is_convertible()), element_type>::value + )) + > + span_constexpr_exp span( It first, size_type count ) + : data_( to_address( first ) ) + , size_( count ) + { + span_EXPECTS( + ( data_ == span_nullptr && count == 0 ) || + ( data_ != span_nullptr && detail::is_positive( count ) ) + ); + } +#else + span_constexpr_exp span( pointer ptr, size_type count ) + : data_( ptr ) + , size_( count ) + { + span_EXPECTS( + ( ptr == span_nullptr && count == 0 ) || + ( ptr != span_nullptr && detail::is_positive( count ) ) + ); + } +#endif + +#if span_HAVE( ITERATOR_CTOR ) + template< typename It, typename End + span_REQUIRES_T(( + std::is_convertible()), element_type>::value + && ! std::is_convertible::value + )) + > + span_constexpr_exp span( It first, End last ) + : data_( to_address( first ) ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#else + span_constexpr_exp span( pointer first, pointer last ) + : data_( first ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#endif + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > + span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept + : data_( span_ADDRESSOF( arr[0] ) ) + , size_( N ) + {} + +#if span_HAVE( ARRAY ) + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > +# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE ) + span_constexpr span( std::array< element_type, N > & arr ) span_noexcept +# else + span_constexpr span( std::array< value_type, N > & arr ) span_noexcept +# endif + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + + template< std::size_t N +# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) +# endif + > + span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + +#endif // span_HAVE( ARRAY ) + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + template< class Container + span_REQUIRES_T(( + detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + + template< class Container + span_REQUIRES_T(( + std::is_const< element_type >::value + && detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container const & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + +#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +#if span_FEATURE( WITH_CONTAINER ) + + template< class Container > + span_constexpr span( with_container_t, Container & cont ) + : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) ) + , size_( to_size( cont.size() ) ) + {} + + template< class Container > + span_constexpr span( with_container_t, Container const & cont ) + : data_( cont.size() == 0 ? span_nullptr : const_cast( span_ADDRESSOF( cont[0] ) ) ) + , size_( to_size( cont.size() ) ) + {} +#endif + +#if span_HAVE( IS_DEFAULT ) + span_constexpr span( span const & other ) span_noexcept = default; + + ~span() span_noexcept = default; + + span_constexpr14 span & operator=( span const & other ) span_noexcept = default; +#else + span_constexpr span( span const & other ) span_noexcept + : data_( other.data_ ) + , size_( other.size_ ) + {} + + ~span() span_noexcept + {} + + span_constexpr14 span & operator=( span const & other ) span_noexcept + { + data_ = other.data_; + size_ = other.size_; + + return *this; + } +#endif + + template< class OtherElementType, extent_type OtherExtent + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == OtherExtent) + && std::is_convertible::value + )) + > + span_constexpr_exp span( span const & other ) span_noexcept + : data_( reinterpret_cast( other.data() ) ) + , size_( other.size() ) + { + span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) ); + } + + // 26.7.3.3 Subviews [span.sub] + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + first() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data(), Count ); + } + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + last() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data() + (size() - Count), Count ); + } + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + template< size_type Offset, extent_type Count = dynamic_extent > +#else + template< size_type Offset, extent_type Count /*= dynamic_extent*/ > +#endif + span_constexpr_exp span< element_type, Count > + subspan() const + { + span_EXPECTS( + ( detail::is_positive( Offset ) && Offset <= size() ) && + ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) ) + ); + + return span< element_type, Count >( + data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + first( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data(), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + last( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data() + ( size() - count ), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + subspan( size_type offset, size_type count = static_cast(dynamic_extent) ) const + { + span_EXPECTS( + ( ( detail::is_positive( offset ) && offset <= size() ) ) && + ( count == static_cast(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) ) + ); + + return span< element_type, dynamic_extent >( + data() + offset, count == static_cast(dynamic_extent) ? size() - offset : count ); + } + + // 26.7.3.4 Observers [span.obs] + + span_constexpr size_type size() const span_noexcept + { + return size_; + } + + span_constexpr std::ptrdiff_t ssize() const span_noexcept + { + return static_cast( size_ ); + } + + span_constexpr size_type size_bytes() const span_noexcept + { + return size() * to_size( sizeof( element_type ) ); + } + + span_nodiscard span_constexpr bool empty() const span_noexcept + { + return size() == 0; + } + + // 26.7.3.5 Element access [span.elem] + + span_constexpr_exp reference operator[]( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } + +#if span_FEATURE( MEMBER_CALL_OPERATOR ) + span_deprecated("replace operator() with operator[]") + + span_constexpr_exp reference operator()( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } +#endif + +#if span_FEATURE( MEMBER_AT ) + span_constexpr14 reference at( size_type idx ) const + { +#if span_CONFIG( NO_EXCEPTIONS ) + return this->operator[]( idx ); +#else + if ( !detail::is_positive( idx ) || size() <= idx ) + { + detail::throw_out_of_range( idx, size() ); + } + return *( data() + idx ); +#endif + } +#endif + + span_constexpr pointer data() const span_noexcept + { + return data_; + } + +#if span_FEATURE( MEMBER_BACK_FRONT ) + + span_constexpr_exp reference front() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *data(); + } + + span_constexpr_exp reference back() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *( data() + size() - 1 ); + } + +#endif + + // xx.x.x.x Modifiers [span.modifiers] + +#if span_FEATURE( MEMBER_SWAP ) + + span_constexpr14 void swap( span & other ) span_noexcept + { + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } +#endif + + // 26.7.3.6 Iterator support [span.iterators] + + span_constexpr iterator begin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return iterator( data() ); +#endif + } + + span_constexpr iterator end() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return iterator( data() + size() ); +#endif + } + + span_constexpr const_iterator cbegin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return const_iterator( data() ); +#endif + } + + span_constexpr const_iterator cend() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return const_iterator( data() + size() ); +#endif + } + + span_constexpr reverse_iterator rbegin() const span_noexcept + { + return reverse_iterator( end() ); + } + + span_constexpr reverse_iterator rend() const span_noexcept + { + return reverse_iterator( begin() ); + } + + span_constexpr const_reverse_iterator crbegin() const span_noexcept + { + return const_reverse_iterator ( cend() ); + } + + span_constexpr const_reverse_iterator crend() const span_noexcept + { + return const_reverse_iterator( cbegin() ); + } + +private: + + // Note: C++20 has std::pointer_traits::to_address( it ); + +#if span_HAVE( ITERATOR_CTOR ) + static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept + { + return nullptr; + } + + template< typename U > + static inline span_constexpr U * to_address( U * p ) span_noexcept + { + return p; + } + + template< typename Ptr + span_REQUIRES_T(( ! std::is_pointer::value )) + > + static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept + { + return to_address( it.operator->() ); + } +#endif // span_HAVE( ITERATOR_CTOR ) + +private: + pointer data_; + size_type size_; +}; + +// class template argument deduction guides: + +#if span_HAVE( DEDUCTION_GUIDES ) + +template< class T, size_t N > +span( T (&)[N] ) -> span(N)>; + +template< class T, size_t N > +span( std::array & ) -> span(N)>; + +template< class T, size_t N > +span( std::array const & ) -> span(N)>; + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< class Container > +span( Container& ) -> span; + +template< class Container > +span( Container const & ) -> span; + +#endif + +// iterator: constraints: It satisfies contiguous_­iterator. + +template< class It, class EndOrSize > +span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t >::type >; + +#endif // span_HAVE( DEDUCTION_GUIDES ) + +// 26.7.3.7 Comparison operators [span.comparison] + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool same( span const & l, span const & r ) span_noexcept +{ + return std11::is_same::value + && l.size() == r.size() + && static_cast( l.data() ) == r.data(); +} + +#endif + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator==( span const & l, span const & r ) +{ + return +#if span_FEATURE( SAME ) + same( l, r ) || +#endif + ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<( span const & l, span const & r ) +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator!=( span const & l, span const & r ) +{ + return !( l == r ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<=( span const & l, span const & r ) +{ + return !( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>( span const & l, span const & r ) +{ + return ( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>=( span const & l, span const & r ) +{ + return !( l < r ); +} + +#endif // span_FEATURE( COMPARISON ) + +// 26.7.2.6 views of object representation [span.objectrep] + +#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow: + +template< typename T, extent_t Extent > +struct BytesExtent +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = span_sizeof(T) * Extent }; +#else + enum ET { value = span_sizeof(T) * Extent }; +#endif +}; + +template< typename T > +struct BytesExtent< T, dynamic_extent > +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = dynamic_extent }; +#else + enum ET { value = dynamic_extent }; +#endif +}; + +template< class T, extent_t Extent > +inline span_constexpr span< const std17::byte, BytesExtent::value > +as_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() }; +#else + return span< const std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +template< class T, extent_t Extent > +inline span_constexpr span< std17::byte, BytesExtent::value > +as_writable_bytes( span spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() }; +#else + return span< std17::byte, BytesExtent::value >( + reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// 27.8 Container and view access [iterator.container] + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::size_t size( span const & spn ) +{ + return static_cast( spn.size() ); +} + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::ptrdiff_t ssize( span const & spn ) +{ + return static_cast( spn.size() ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { + +using span_lite::dynamic_extent; + +using span_lite::span; + +using span_lite::with_container; + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) +using span_lite::same; +#endif + +using span_lite::operator==; +using span_lite::operator!=; +using span_lite::operator<; +using span_lite::operator<=; +using span_lite::operator>; +using span_lite::operator>=; +#endif + +#if span_HAVE( BYTE ) +using span_lite::as_bytes; +using span_lite::as_writable_bytes; +#endif + +using span_lite::size; +using span_lite::ssize; + +} // namespace nonstd + +#endif // span_USES_STD_SPAN + +// make_span() [span-lite extension]: + +#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +#if span_USES_STD_SPAN +# define span_constexpr constexpr +# define span_noexcept noexcept +# define span_nullptr nullptr +# ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +# endif +using extent_t = span_CONFIG_EXTENT_TYPE; +#endif // span_USES_STD_SPAN + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr span +make_span( T * ptr, size_t count ) span_noexcept +{ + return span( ptr, count ); +} + +template< class T > +inline span_constexpr span +make_span( T * first, T * last ) span_noexcept +{ + return span( first, last ); +} + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( T ( &arr )[ N ] ) span_noexcept +{ + return span(N)>( &arr[ 0 ], N ); +} + +#if span_USES_STD_SPAN || span_HAVE( ARRAY ) + +template< class T, std::size_t N > +inline span_constexpr span(N)> +make_span( std::array< T, N > & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +template< class T, std::size_t N > +inline span_constexpr span< const T, static_cast(N) > +make_span( std::array< T, N > const & arr ) span_noexcept +{ + return span(N)>( arr ); +} + +#endif // span_HAVE( ARRAY ) + +#if span_USES_STD_SPAN + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO ) + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer::type > +{ + return span< typename std::remove_pointer::type >( cont ); +} + +template< class Container, class EP = decltype( std17::data(std::declval())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer::type > +{ + return span< const typename std::remove_pointer::type >( cont ); +} + +#else + +template< class T > +inline span_constexpr span +make_span( span spn ) span_noexcept +{ + return spn; +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +template< class T, class Allocator > +inline span_constexpr span +make_span( std::vector const & cont ) span_noexcept +{ + return span( with_container, cont ); +} + +#endif // span_USES_STD_SPAN || ( ... ) + +#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container & cont ) span_noexcept +{ + return span< typename Container::value_type >( with_container, cont ); +} + +template< class Container > +inline span_constexpr span +make_span( with_container_t, Container const & cont ) span_noexcept +{ + return span< const typename Container::value_type >( with_container, cont ); +} + +#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +// extensions: non-member views: +// this feature implies the presence of make_span() + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span +first( span spn ) +{ + return spn.template first(); +} + +template< class T, extent_t Extent > +span_constexpr span +first( span spn, size_t count ) +{ + return spn.first( count ); +} + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span +last( span spn ) +{ + return spn.template last(); +} + +template< class T, extent_t Extent > +span_constexpr span +last( span spn, size_t count ) +{ + return spn.last( count ); +} + +template< size_t Offset, extent_t Count, class T, extent_t Extent > +span_constexpr span +subspan( span spn ) +{ + return spn.template subspan(); +} + +template< class T, extent_t Extent > +span_constexpr span +subspan( span spn, size_t offset, extent_t count = dynamic_extent ) +{ + return spn.subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 + +template< extent_t Count, class T > +span_constexpr auto +first( T & t ) -> decltype( make_span(t).template first() ) +{ + return make_span( t ).template first(); +} + +template< class T > +span_constexpr auto +first( T & t, size_t count ) -> decltype( make_span(t).first(count) ) +{ + return make_span( t ).first( count ); +} + +template< extent_t Count, class T > +span_constexpr auto +last( T & t ) -> decltype( make_span(t).template last() ) +{ + return make_span(t).template last(); +} + +template< class T > +span_constexpr auto +last( T & t, extent_t count ) -> decltype( make_span(t).last(count) ) +{ + return make_span( t ).last( count ); +} + +template< size_t Offset, extent_t Count = dynamic_extent, class T > +span_constexpr auto +subspan( T & t ) -> decltype( make_span(t).template subspan() ) +{ + return make_span( t ).template subspan(); +} + +template< class T > +span_constexpr auto +subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) ) +{ + return make_span( t ).subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::make_span; + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || ( span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 ) + +using span_lite::first; +using span_lite::last; +using span_lite::subspan; + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_[SPAN|CONTAINER] ) + +} // namespace nonstd + +#endif // #if span_FEATURE_TO_STD( MAKE_SPAN ) + +#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) ) + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr auto +byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) > +{ + return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) ); +} + +template< class T > +inline span_constexpr auto +byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) > +{ + return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::byte_span; +} // namespace nonstd + +#endif // span_FEATURE( BYTE_SPAN ) + +#if span_HAVE( STRUCT_BINDING ) + +#if span_CPP14_OR_GREATER +# include +#elif span_CPP11_OR_GREATER +# include +namespace std { + template< std::size_t I, typename T > + using tuple_element_t = typename tuple_element::type; +} +#else +namespace std { + template< typename T > + class tuple_size; /*undefined*/ + + template< std::size_t I, typename T > + class tuple_element; /* undefined */ +} +#endif // span_CPP14_OR_GREATER + +namespace std { + +// 26.7.X Tuple interface + +// std::tuple_size<>: + +template< typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_size< nonstd::span > : public integral_constant(Extent)> {}; + +// std::tuple_size<>: Leave undefined for dynamic extent: + +template< typename ElementType > +class tuple_size< nonstd::span >; + +// std::tuple_element<>: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_element< I, nonstd::span > +{ +public: +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element: dynamic extent or index out of range" ); +#endif + using type = ElementType; +}; + +// std::get<>(), 2 variants: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType & get( nonstd::span & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType const & get( nonstd::span const & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +} // end namespace std + +#endif // span_HAVE( STRUCT_BINDING ) + +#if ! span_USES_STD_SPAN +span_RESTORE_WARNINGS() +#endif // span_USES_STD_SPAN + +#endif // NONSTD_SPAN_HPP_INCLUDED