diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -52,6 +52,14 @@ .uint ) +add_header_library( + span + HDRS + span.h + DEPENDS + .type_traits +) + add_header_library( string_view HDRS diff --git a/libc/src/__support/CPP/span.h b/libc/src/__support/CPP/span.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/CPP/span.h @@ -0,0 +1,94 @@ +//===-- Standalone implementation std::span ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_SPAN_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_SPAN_H + +#include // For size_t + +#include "array.h" // For array +#include "type_traits.h" // For remove_cv_t + +namespace __llvm_libc::cpp { + +// A trimmed down implementation of std::span. +// Missing features: +// - No constant size spans (e.g. Span), +// - Only handle pointer like types, no fancy interators nor object overriding +// the & operator, +// - No implicit type conversion (e.g. Span, initialized with As where A +// inherits from B), +// - No reverse iterators +template class span { +public: + using element_type = T; + using value_type = remove_cv_t; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; + using iterator = T *; + + static constexpr size_type dynamic_extent = -1; + + constexpr span() : Data(nullptr), Size(0) {} + + constexpr span(pointer first, size_type count) : Data(first), Size(count) {} + + constexpr span(pointer first, pointer end) : Data(first), Size(end - first) {} + + template + constexpr span(element_type (&arr)[N]) : Data(arr), Size(N) {} + + template + constexpr span(array &arr) : Data(arr.data()), Size(arr.size()) {} + + constexpr span(const span &s) = default; + constexpr span &operator=(const span &s) = default; + ~span() = default; + constexpr reference operator[](size_type index) const { + return data()[index]; + } + constexpr iterator begin() const { return data(); } + constexpr iterator end() const { return data() + size(); } + constexpr reference front() const { return (*this)[0]; } + constexpr reference back() const { return (*this)[size() - 1]; } + constexpr pointer data() const { return Data; } + constexpr size_type size() const { return Size; } + constexpr size_type size_bytes() const { return sizeof(T) * size(); } + constexpr bool empty() const { return size() == 0; } + + constexpr span subspan(size_type offset, + size_type count = dynamic_extent) const { + return span(data() + offset, countToSize(offset, count)); + } + + constexpr span first(size_type count) const { + return subspan(0, count); + } + + constexpr span last(size_type count) const { + return span(data() + (size() - count), count); + } + +private: + constexpr size_type countToSize(size_type offset, size_type count) const { + if (count == dynamic_extent) { + return size() - offset; + } + return count; + } + + T *Data; + size_t Size; +}; + +} // namespace __llvm_libc::cpp + +#endif /* LLVM_LIBC_SRC_SUPPORT_CPP_SPAN_H */ diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt --- a/libc/test/src/__support/CPP/CMakeLists.txt +++ b/libc/test/src/__support/CPP/CMakeLists.txt @@ -101,3 +101,13 @@ DEPENDS libc.src.__support.CPP.optional ) + +add_libc_unittest( + span_test + SUITE + libc_cpp_utils_unittests + SRCS + span_test.cpp + DEPENDS + libc.src.__support.CPP.span +) diff --git a/libc/test/src/__support/CPP/span_test.cpp b/libc/test/src/__support/CPP/span_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/CPP/span_test.cpp @@ -0,0 +1,109 @@ +//===-- Unittests for span ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/array.h" +#include "src/__support/CPP/span.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::cpp::array; +using __llvm_libc::cpp::span; + +TEST(LlvmLibcSpanTest, InitializeEmpty) { + span s; + ASSERT_EQ(s.size(), size_t(0)); + ASSERT_TRUE(s.empty()); + ASSERT_TRUE(s.data() == nullptr); +} + +TEST(LlvmLibcSpanTest, InitializeSingleton) { + int a = 42; + span s(&a, 1); + ASSERT_EQ(s.size(), size_t(1)); + ASSERT_FALSE(s.empty()); + ASSERT_TRUE(s.data() == &a); + ASSERT_EQ(s.front(), 42); + ASSERT_EQ(s.back(), 42); +} + +TEST(LlvmLibcSpanTest, InitializeCArray) { + int a[] = {1, 2, 3}; + span s(a); + ASSERT_EQ(s.size(), size_t(3)); + ASSERT_FALSE(s.empty()); + ASSERT_TRUE(s.data() == &a[0]); + ASSERT_EQ(s.front(), 1); + ASSERT_EQ(s.back(), 3); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + ASSERT_EQ(s[2], 3); +} + +TEST(LlvmLibcSpanTest, InitializeArray) { + array a = {1, 2, 3}; + span s(a); + ASSERT_EQ(s.size(), size_t(3)); + ASSERT_FALSE(s.empty()); + ASSERT_TRUE(s.data() == &a[0]); + ASSERT_EQ(s.front(), 1); + ASSERT_EQ(s.back(), 3); + ASSERT_EQ(s[0], 1); + ASSERT_EQ(s[1], 2); + ASSERT_EQ(s[2], 3); +} + +TEST(LlvmLibcSpanTest, Modify) { + int a[] = {1, 2, 3}; + span s(a); + for (int &value : s) + ++value; + ASSERT_EQ(s.size(), size_t(3)); + ASSERT_EQ(s[0], 2); + ASSERT_EQ(s[1], 3); + ASSERT_EQ(s[2], 4); +} + +TEST(LlvmLibcSpanTest, SubSpan) { + int a[] = {1, 2, 3}; + span s(a); + { // same span + const auto _ = s.subspan(0); + ASSERT_EQ(_.size(), size_t(3)); + ASSERT_EQ(_[0], 1); + ASSERT_EQ(_[1], 2); + ASSERT_EQ(_[2], 3); + } + { // last element + const auto _ = s.subspan(2); + ASSERT_EQ(_.size(), size_t(1)); + ASSERT_EQ(_[0], 3); + } + { // no element + const auto _ = s.subspan(3); + ASSERT_EQ(_.size(), size_t(0)); + } + { // first element + const auto _ = s.subspan(0, 1); + ASSERT_EQ(_.size(), size_t(1)); + ASSERT_EQ(_[0], 1); + } +} + +TEST(LlvmLibcSpanTest, FirstAndLastSubSpan) { + int a[] = {1, 2, 3}; + span s(a); + + const auto first = s.first(2); + ASSERT_EQ(first.size(), size_t(2)); + ASSERT_EQ(first[0], 1); + ASSERT_EQ(first[1], 2); + + const auto last = s.last(2); + ASSERT_EQ(last.size(), size_t(2)); + ASSERT_EQ(last[0], 2); + ASSERT_EQ(last[1], 3); +}