Elide storage of bssl::Span size if known at compile time. This does not yet migrate callers to actually benefit from this; only a few callers needed changes due to loss of some implicit conversions. Tested: IDA/Diaphora and ghidriff find no differences in generated code other than type identifiers with the extra template argument. Change-Id: I36de895ca96c50a6f8a624049fcca93db2602a11 Bug: 390229582 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/82847 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/bytestring/bytestring_test.cc b/crypto/bytestring/bytestring_test.cc index 0407f3f..5f3fe4b 100644 --- a/crypto/bytestring/bytestring_test.cc +++ b/crypto/bytestring/bytestring_test.cc
@@ -31,6 +31,18 @@ namespace { +TEST(CBSTest, CtorFromSpan) { + const uint8_t buf[4] = "foo"; + + auto span_from_static_extent = bssl::Span<const uint8_t, 4>(buf); + CBS cbs_from_static_extent(span_from_static_extent); + EXPECT_EQ(4u, CBS_len(&cbs_from_static_extent)); + + auto span_from_dynamic_extent = bssl::Span<const uint8_t>(buf); + CBS cbs_from_dynamic_extent(span_from_dynamic_extent); + EXPECT_EQ(4u, CBS_len(&cbs_from_dynamic_extent)); +} + TEST(CBSTest, Skip) { static const uint8_t kData[] = {1, 2, 3}; CBS data;
diff --git a/include/openssl/span.h b/include/openssl/span.h index 440b657..93de8d5 100644 --- a/include/openssl/span.h +++ b/include/openssl/span.h
@@ -24,6 +24,7 @@ #include <stdlib.h> #include <algorithm> +#include <limits> #include <string_view> #include <type_traits> @@ -33,24 +34,27 @@ #if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L #include <ranges> -BSSL_NAMESPACE_BEGIN -template <typename T> -class Span; -BSSL_NAMESPACE_END - -// Mark `Span` as satisfying the `view` and `borrowed_range` concepts. This -// should be done before the definition of `Span`, so that any inlined calls to -// range functionality use the correct specializations. -template <typename T> -inline constexpr bool std::ranges::enable_view<bssl::Span<T>> = true; -template <typename T> -inline constexpr bool std::ranges::enable_borrowed_range<bssl::Span<T>> = true; #endif BSSL_NAMESPACE_BEGIN +inline constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max(); -template <typename T> +template <typename T, size_t N = dynamic_extent> class Span; +BSSL_NAMESPACE_END + +#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L +// Mark `Span` as satisfying the `view` and `borrowed_range` concepts. This +// should be done before the definition of `Span`, so that any inlined calls to +// range functionality use the correct specializations. +template <typename T, size_t N> +inline constexpr bool std::ranges::enable_view<bssl::Span<T, N>> = true; +template <typename T, size_t N> +inline constexpr bool std::ranges::enable_borrowed_range<bssl::Span<T, N>> = + true; +#endif + +BSSL_NAMESPACE_BEGIN namespace internal { template <typename T> @@ -67,6 +71,32 @@ friend bool operator!=(Span<T> lhs, Span<T> rhs) { return !(lhs == rhs); } }; +// Container class to store the size of a span at runtime or compile time. +template <typename T, size_t N> +class SpanStorage : private SpanBase<const T> { + public: + constexpr SpanStorage(T *data, size_t size) : data_(data) { + BSSL_CHECK(size == N); + } + constexpr T *data() const { return data_; } + constexpr size_t size() const { return N; } + + private: + T *data_; +}; + +template <typename T> +class SpanStorage<T, dynamic_extent> : private SpanBase<const T> { + public: + constexpr SpanStorage(T *data, size_t size) : data_(data), size_(size) {} + constexpr T *data() const { return data_; } + constexpr size_t size() const { return size_; } + + private: + T *data_; + size_t size_; +}; + // Heuristically test whether C is a container type that can be converted into // a Span<T> by checking for data() and size() member functions. template <typename C, typename T> @@ -74,6 +104,10 @@ std::is_convertible_v<decltype(std::declval<C>().data()), T *> && std::is_integral_v<decltype(std::declval<C>().size())>>; +// A fake type used to be able to SFINAE between two different container +// constructors - by giving one this as a second default argument, and one not. +struct AllowRedeclaringConstructor {}; + } // namespace internal // A Span<T> is a non-owning reference to a contiguous array of objects of type @@ -107,11 +141,9 @@ // Note that Spans have value type sematics. They are cheap to construct and // copy, and should be passed by value whenever a method would otherwise accept // a reference or pointer to a container or array. -template <typename T> -class Span : private internal::SpanBase<const T> { +template <typename T, size_t N> +class Span : public internal::SpanStorage<T, N> { public: - static const size_t npos = static_cast<size_t>(-1); - using element_type = T; using value_type = std::remove_cv_t<T>; using size_type = size_t; @@ -123,90 +155,161 @@ using iterator = T *; using const_iterator = const T *; - constexpr Span() : Span(nullptr, 0) {} - constexpr Span(T *ptr, size_t len) : data_(ptr), size_(len) {} + template <typename U = T, + typename = std::enable_if_t<N == 0 || N == dynamic_extent, U>> + constexpr Span() : internal::SpanStorage<T, N>(nullptr, 0) {} - template <size_t N> - constexpr Span(T (&array)[N]) : Span(array, N) {} + // NOTE: This constructor may abort() at runtime if len differs from the + // compile-time size, if any. + constexpr Span(T *ptr, size_t len) : internal::SpanStorage<T, N>(ptr, len) {} + + template <size_t NA, + typename = std::enable_if_t<N == NA || N == dynamic_extent>> + // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. + constexpr Span(T (&array)[NA]) : internal::SpanStorage<T, N>(array, NA) {} + + template < + size_t NA, typename U, + typename = std::enable_if_t<std::is_convertible_v<U (*)[], T (*)[]>>, + typename = std::enable_if_t<N == dynamic_extent || N == NA>> + // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. + constexpr Span(Span<U, NA> other) + : internal::SpanStorage<T, N>(other.data(), other.size()) {} template <typename C, typename = internal::EnableIfContainer<C, T>, - typename = std::enable_if_t<std::is_const<T>::value, C>> + typename = std::enable_if_t<std::is_const<T>::value, C>, + typename = std::enable_if_t<N == dynamic_extent, C>> + // NOLINTNEXTLINE(google-explicit-constructor): same as std::span. constexpr Span(const C &container) - : data_(container.data()), size_(container.size()) {} + : internal::SpanStorage<T, N>(container.data(), container.size()) {} + // NOTE: This constructor may abort() at runtime if the container's length + // differs from the compile-time size, if any. + template <typename C, typename = internal::EnableIfContainer<C, T>, + typename = std::enable_if_t<std::is_const<T>::value, C>, + typename = std::enable_if_t<N != dynamic_extent, C>> + constexpr explicit Span(const C &container, + internal::AllowRedeclaringConstructor = {}) + : internal::SpanStorage<T, N>(container.data(), container.size()) {} + + // NOTE: This constructor may abort() at runtime if the container's length + // differs from the compile-time size, if any. template <typename C, typename = internal::EnableIfContainer<C, T>, typename = std::enable_if_t<!std::is_const<T>::value, C>> constexpr explicit Span(C &container) - : data_(container.data()), size_(container.size()) {} + : internal::SpanStorage<T, N>(container.data(), container.size()) {} - constexpr T *data() const { return data_; } - constexpr size_t size() const { return size_; } - constexpr bool empty() const { return size_ == 0; } + using internal::SpanStorage<T, N>::data; + using internal::SpanStorage<T, N>::size; + constexpr bool empty() const { return size() == 0; } - constexpr iterator begin() const { return data_; } - constexpr const_iterator cbegin() const { return data_; } - constexpr iterator end() const { return data_ + size_; } + constexpr iterator begin() const { return data(); } + constexpr const_iterator cbegin() const { return data(); } + constexpr iterator end() const { return data() + size(); } constexpr const_iterator cend() const { return end(); } constexpr T &front() const { - if (size_ == 0) { - abort(); - } - return data_[0]; + BSSL_CHECK(size() != 0); + return data()[0]; } constexpr T &back() const { - if (size_ == 0) { - abort(); - } - return data_[size_ - 1]; + BSSL_CHECK(size() != 0); + return data()[size() - 1]; } constexpr T &operator[](size_t i) const { - if (i >= size_) { - abort(); - } - return data_[i]; + BSSL_CHECK(i < size()); + return data()[i]; } T &at(size_t i) const { return (*this)[i]; } - constexpr Span subspan(size_t pos = 0, size_t len = npos) const { - if (pos > size_) { - // absl::Span throws an exception here. Note std::span and Chromium - // base::span additionally forbid pos + len being out of range, with a - // special case at npos/dynamic_extent, while absl::Span::subspan clips - // the span. For now, we align with absl::Span in case we switch to it in - // the future. - abort(); - } - return Span(data_ + pos, std::min(size_ - pos, len)); - } - - constexpr Span first(size_t len) const { - if (len > size_) { - abort(); - } - return Span(data_, len); - } - - constexpr Span last(size_t len) const { - if (len > size_) { - abort(); - } - return Span(data_ + size_ - len, len); - } - private: - T *data_; - size_t size_; -}; + static constexpr size_t SubspanOutLen(size_t size, size_t pos, size_t len) { + // NOTE: This differs from std::span's subspan length in that this one + // performs clipping. + // + // For std::span, this would be: + // + // len != dynamic_extent ? len : size - pos + return std::min(size - pos, len); + } + static constexpr size_t SubspanTypeOutLen(size_t size, size_t pos, + size_t len) { + // NOTE: This differs from std::span's subspan length in that this one + // performs clipping, and thus has to return dynamic extent whenever the + // input span has dynamic extent. + // + // For std::span, this would be: + // + // len != dynamic_extent + // ? len + // : (size != dynamic_extent ? size - pos : dynamic_extent) + if (size == dynamic_extent) { + return dynamic_extent; + } + return SubspanOutLen(size, pos, len); + } -template <typename T> -const size_t Span<T>::npos; + public: + // NOTE: This method may abort() at runtime if pos is out of range. + constexpr Span<T> subspan(size_t pos = 0, size_t len = dynamic_extent) const { + // absl::Span throws an exception here. Note std::span and Chromium + // base::span additionally forbid pos + len being out of range, with a + // special case at npos/dynamic_extent, while absl::Span::subspan clips + // the span. For now, we align with absl::Span in case we switch to it in + // the future. + BSSL_CHECK(pos <= size()); + return Span<T>(data() + pos, SubspanOutLen(size(), pos, len)); + } + + // NOTE: This method may abort() at runtime if len is out of range. + template <size_t pos, size_t len = dynamic_extent> + constexpr Span<T, SubspanTypeOutLen(N, pos, len)> subspan() const { + // absl::Span throws an exception here. Note std::span and Chromium + // base::span additionally forbid pos + len being out of range, with a + // special case at npos/dynamic_extent, while absl::Span::subspan clips + // the span. For now, we align with absl::Span in case we switch to it in + // the future. + // + // Removing clipping however will allow making the return type have a + // static extent whenever len is static, which matches std::span and + // could improve efficiency. + BSSL_CHECK(pos <= size()); + return Span<T, SubspanTypeOutLen(N, pos, len)>(data() + pos, + std::min(size() - pos, len)); + } + + // NOTE: This method may abort() at runtime if len is out of range. + constexpr Span<T> first(size_t len) const { + BSSL_CHECK(len <= size()); + return Span<T>(data(), len); + } + + // NOTE: This method may abort() at runtime if len is out of range. + template <size_t len> + constexpr Span<T, len> first() const { + BSSL_CHECK(len <= size()); + return Span<T, len>(data(), len); + } + + // NOTE: This method may abort() at runtime if len is out of range. + constexpr Span<T> last(size_t len) const { + BSSL_CHECK(len <= size()); + return Span<T>(data() + size() - len, len); + } + + // NOTE: This method may abort() at runtime if len is out of range. + template <size_t len> + constexpr Span<T, len> last() const { + BSSL_CHECK(len <= size()); + return Span<T, len>(data() + size() - len, len); + } +}; template <typename T> Span(T *, size_t) -> Span<T>; template <typename T, size_t size> -Span(T (&array)[size]) -> Span<T>; +Span(T (&array)[size]) -> Span<T, size>; template < typename C, typename T = std::remove_pointer_t<decltype(std::declval<C>().data())>, @@ -224,8 +327,8 @@ } template <typename T, size_t N> -constexpr Span<T> MakeSpan(T (&array)[N]) { - return Span<T>(array, N); +constexpr Span<T, N> MakeSpan(T (&array)[N]) { + return array; } template <typename T> @@ -240,7 +343,7 @@ } template <typename T, size_t size> -constexpr Span<const T> MakeConstSpan(T (&array)[size]) { +constexpr Span<const T, size> MakeConstSpan(T (&array)[size]) { return array; }
diff --git a/pki/input.h b/pki/input.h index 933c537..9e2d5d3 100644 --- a/pki/input.h +++ b/pki/input.h
@@ -95,8 +95,7 @@ constexpr uint8_t operator[](size_t idx) const { return data_[idx]; } constexpr uint8_t front() const { return data_.front(); } constexpr uint8_t back() const { return data_.back(); } - constexpr Input subspan(size_t pos = 0, - size_t len = Span<const uint8_t>::npos) const { + constexpr Input subspan(size_t pos = 0, size_t len = dynamic_extent) const { return Input(data_.subspan(pos, len)); } constexpr Input first(size_t len) const { return Input(data_.first(len)); }
diff --git a/ssl/extensions.cc b/ssl/extensions.cc index 0820d92..b5f19bd 100644 --- a/ssl/extensions.cc +++ b/ssl/extensions.cc
@@ -4578,7 +4578,8 @@ } Span<const uint16_t> sigalgs = - cred->sigalgs.empty() ? Span(kSignSignatureAlgorithms) : cred->sigalgs; + cred->sigalgs.empty() ? Span<const uint16_t>(kSignSignatureAlgorithms) + : Span<const uint16_t>(cred->sigalgs); for (uint16_t sigalg : sigalgs) { if (!ssl_pkey_supports_algorithm(ssl, cred->pubkey.get(), sigalg, /*is_verify=*/false)) {
diff --git a/ssl/internal.h b/ssl/internal.h index cd5b67d..e62202a 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -118,10 +118,10 @@ // it would have liked to have written. The strings written consist of // |fixed_names_len| strings from |fixed_names| followed by |objects_len| // strings taken by projecting |objects| through |name|. -template <typename T, typename Name> +template <typename T, typename Name, size_t S1, size_t S2> inline size_t GetAllNames(const char **out, size_t max_out, - Span<const char *const> fixed_names, Name(T::*name), - Span<const T> objects) { + Span<const char *const, S1> fixed_names, + Name(T::*name), Span<const T, S2> objects) { auto span = bssl::Span(out, max_out); for (size_t i = 0; !span.empty() && i < fixed_names.size(); i++) { span[0] = fixed_names[i];
diff --git a/ssl/span_test.cc b/ssl/span_test.cc index 55258e5..61deb58 100644 --- a/ssl/span_test.cc +++ b/ssl/span_test.cc
@@ -22,59 +22,153 @@ BSSL_NAMESPACE_BEGIN namespace { -static void TestCtor(Span<int> s, const int *ptr, size_t size) { +template <size_t N> +static void TestCtor(Span<int, N> s, const int *ptr, size_t size) { EXPECT_EQ(s.data(), ptr); EXPECT_EQ(s.size(), size); } -static void TestConstCtor(Span<const int> s, const int *ptr, size_t size) { +template <size_t N> +static void TestConstCtor(Span<const int, N> s, const int *ptr, size_t size) { EXPECT_EQ(s.data(), ptr); EXPECT_EQ(s.size(), size); } +template <class T, size_t N> +constexpr static bool IsRuntimeSized(Span<T, N>) { + return N == dynamic_extent; +} + +TEST(SpanTest, CompileTimeSizes) { + static_assert(sizeof(Span<int, 4>) == sizeof(int *)); + static_assert(sizeof(Span<int>) == sizeof(std::pair<int *, size_t>)); +} + TEST(SpanTest, CtorEmpty) { Span<int> s; TestCtor(s, nullptr, 0); } +TEST(SpanTest, CtorEmptyCompileTIme) { + Span<int, 0> s; + TestCtor(s, nullptr, 0); +} + TEST(SpanTest, CtorFromPtrAndSize) { std::vector<int> v = {7, 8, 9, 10}; Span<int> s(v.data(), v.size()); TestCtor(s, v.data(), v.size()); + TestConstCtor<dynamic_extent>(Span<int>(v.data(), v.size()), v.data(), + v.size()); +} + +TEST(SpanTest, CtorFromPtrAndSizeCompileTime) { + std::vector<int> v = {7, 8, 9, 10}; + Span<int, 4> s(v.data(), v.size()); + TestCtor(s, v.data(), v.size()); + TestConstCtor<4>(Span<int, 4>(v.data(), v.size()), v.data(), v.size()); +} + +TEST(SpanTest, ConstCtorFromVector) { + std::vector<int> v = {1, 2}; + // Const ctor is implicit. + TestConstCtor<dynamic_extent>(v, v.data(), v.size()); +} + +TEST(SpanTest, ConstCtorFromVectorCompileTime) { + std::vector<int> v = {1, 2}; + // Static extent constructor can only be invoked explicitly. + TestConstCtor<2>(Span<const int, 2>(v), v.data(), v.size()); } TEST(SpanTest, CtorFromVector) { std::vector<int> v = {1, 2}; - // Const ctor is implicit. - TestConstCtor(v, v.data(), v.size()); // Mutable is explicit. Span<int> s(v); TestCtor(s, v.data(), v.size()); } +TEST(SpanTest, CtorFromVectorCompileTime) { + std::vector<int> v = {1, 2}; + // Mutable is explicit. + Span<int, 2> s(v); + TestCtor(s, v.data(), v.size()); +} + TEST(SpanTest, CtorConstFromArray) { int v[] = {10, 11}; // Array ctor is implicit for const and mutable T. - TestConstCtor(v, v, 2); - TestCtor(v, v, 2); + TestConstCtor<dynamic_extent>(v, v, 2); + TestCtor<dynamic_extent>(v, v, 2); +} + +TEST(SpanTest, CtorConstFromArrayCompileTime) { + int v[] = {10, 11}; + // Array ctor is implicit for const and mutable T. + TestConstCtor<2>(v, v, 2); + TestCtor<2>(v, v, 2); +} + +TEST(SpanTest, Compare) { + int v[] = {10, 11}; + int w[] = {10, 11}; + int x[] = {11, 10, 11}; + Span<int> sv = v; + Span<int> sw = w; + Span<int> sx = x; + EXPECT_TRUE(sv == sw); + EXPECT_FALSE(sv != sw); + EXPECT_FALSE(sv == sx); + EXPECT_TRUE(sv != sx); +} + +TEST(SpanTest, CompareCompileTime) { + int v[] = {10, 11}; + int w[] = {10, 11}; + int x[] = {11, 10, 11}; + Span<int, 2> sv = v; + Span<int, 2> sw = w; + Span<int, 3> sx = x; + EXPECT_TRUE(sv == sw); + EXPECT_FALSE(sv != sw); + EXPECT_FALSE(sv == sx); + EXPECT_TRUE(sv != sx); } TEST(SpanTest, MakeSpan) { std::vector<int> v = {100, 200, 300}; + EXPECT_TRUE(IsRuntimeSized(MakeSpan(v))); TestCtor(MakeSpan(v), v.data(), v.size()); TestCtor(MakeSpan(v.data(), v.size()), v.data(), v.size()); - TestConstCtor(MakeSpan(v.data(), v.size()), v.data(), v.size()); - TestConstCtor(MakeSpan(v), v.data(), v.size()); + TestConstCtor<dynamic_extent>(MakeSpan(v.data(), v.size()), v.data(), + v.size()); + TestConstCtor<dynamic_extent>(MakeSpan(v), v.data(), v.size()); +} + +TEST(SpanTest, MakeSpanCompileTime) { + int v[3] = {100, 200, 300}; + EXPECT_FALSE(IsRuntimeSized(MakeSpan(v))); + TestCtor(MakeSpan(v), v, 3); + TestConstCtor<3>(MakeSpan(v), v, 3); } TEST(SpanTest, MakeConstSpan) { std::vector<int> v = {100, 200, 300}; + EXPECT_TRUE(IsRuntimeSized(MakeConstSpan(v))); TestConstCtor(MakeConstSpan(v), v.data(), v.size()); TestConstCtor(MakeConstSpan(v.data(), v.size()), v.data(), v.size()); // But not: // TestConstCtor(MakeSpan(v), v.data(), v.size()); } +TEST(SpanTest, MakeConstSpanCompileTime) { + int v[3] = {100, 200, 300}; + EXPECT_FALSE(IsRuntimeSized(MakeConstSpan(v))); + TestConstCtor(MakeConstSpan(v), v, 3); + // But not: + // TestConstCtor(MakeSpan(v), v.data(), v.size()); +} + TEST(SpanTest, Accessor) { std::vector<int> v({42, 23, 5, 101, 80}); Span<int> s(v); @@ -86,15 +180,64 @@ EXPECT_EQ(s.end(), v.data() + v.size()); } +TEST(SpanTest, AccessorCompiletime) { + std::vector<int> v({42, 23, 5, 101, 80}); + Span<int, 5> s(v.data(), v.size()); + for (size_t i = 0; i < s.size(); ++i) { + EXPECT_EQ(s[i], v[i]); + EXPECT_EQ(s.at(i), v.at(i)); + } + EXPECT_EQ(s.begin(), v.data()); + EXPECT_EQ(s.end(), v.data() + v.size()); +} + TEST(SpanTest, ConstExpr) { static constexpr int v[] = {1, 2, 3, 4}; constexpr bssl::Span<const int> span1(v); static_assert(span1.size() == 4u, "wrong size"); + static_assert(IsRuntimeSized(span1), "unexpectedly compile-time sized"); constexpr bssl::Span<const int> span2 = MakeConstSpan(v); static_assert(span2.size() == 4u, "wrong size"); + static_assert(IsRuntimeSized(span2), "unexpectedly compile-time sized"); static_assert(span2.subspan(1).size() == 3u, "wrong size"); + static_assert(IsRuntimeSized(span2.subspan(1)), + "unexpectedly compile-time sized"); + static_assert(IsRuntimeSized(span2.subspan<1>()), + "unexpectedly compile-time sized"); static_assert(span2.first(1).size() == 1u, "wrong size"); + static_assert(IsRuntimeSized(span2.first(1)), + "unexpectedly compile-time sized"); + static_assert(!IsRuntimeSized(span2.first<1>()), + "unexpectedly runtime sized"); static_assert(span2.last(1).size() == 1u, "wrong size"); + static_assert(IsRuntimeSized(span2.last(1)), + "unexpectedly compile-time sized"); + static_assert(!IsRuntimeSized(span2.last<1>()), "unexpectedly runtime sized"); + static_assert(span2[0] == 1, "wrong value"); +} + +TEST(SpanTest, ConstExprCompileTime) { + static constexpr int v[] = {1, 2, 3, 4}; + constexpr bssl::Span<const int, 4> span1(v); + static_assert(span1.size() == 4u, "wrong size"); + static_assert(!IsRuntimeSized(span1), "unexpectedly runtime sized"); + constexpr bssl::Span<const int, 4> span2 = MakeConstSpan(v); + static_assert(span2.size() == 4u, "wrong size"); + static_assert(!IsRuntimeSized(span2), "unexpectedly runtime sized"); + static_assert(span2.subspan(1).size() == 3u, "wrong size"); + static_assert(IsRuntimeSized(span2.subspan(1)), + "unexpectedly compile-time sized"); + static_assert(!IsRuntimeSized(span2.subspan<1>()), + "unexpectedly runtime sized"); + static_assert(span2.first(1).size() == 1u, "wrong size"); + static_assert(IsRuntimeSized(span2.first(1)), + "unexpectedly compile-time sized"); + static_assert(!IsRuntimeSized(span2.first<1>()), + "unexpectedly runtime sized"); + static_assert(span2.last(1).size() == 1u, "wrong size"); + static_assert(IsRuntimeSized(span2.last(1)), + "unexpectedly compile-time sized"); + static_assert(!IsRuntimeSized(span2.last<1>()), "unexpectedly runtime sized"); static_assert(span2[0] == 1, "wrong value"); } @@ -115,5 +258,22 @@ EXPECT_DEATH_IF_SUPPORTED(empty.back(), ""); } +TEST(SpanDeathTest, BoundsChecksCompileTime) { + // Make an array that's larger than we need, so that a failure to bounds check + // won't crash. + const int v[] = {1, 2, 3, 4}; + Span<const int, 3> span(v, 3); + // Out of bounds access. + EXPECT_DEATH_IF_SUPPORTED(span[3], ""); + EXPECT_DEATH_IF_SUPPORTED(span.subspan(4), ""); + EXPECT_DEATH_IF_SUPPORTED(span.first(4), ""); + EXPECT_DEATH_IF_SUPPORTED(span.last(4), ""); + // Accessing an empty span. + Span<const int, 0> empty(v, 0); + EXPECT_DEATH_IF_SUPPORTED(empty[0], ""); + EXPECT_DEATH_IF_SUPPORTED(empty.front(), ""); + EXPECT_DEATH_IF_SUPPORTED(empty.back(), ""); +} + } // namespace BSSL_NAMESPACE_END
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc index d81d00a..227daaf 100644 --- a/ssl/ssl_cipher.cc +++ b/ssl/ssl_cipher.cc
@@ -1346,7 +1346,7 @@ return TLS1_2_VERSION; } -static const char *kUnknownCipher = "(NONE)"; +static const char *const kUnknownCipher = "(NONE)"; // return the actual cipher being used const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher) { @@ -1573,6 +1573,6 @@ } size_t SSL_get_all_standard_cipher_names(const char **out, size_t max_out) { - return GetAllNames(out, max_out, Span<const char *>(), + return GetAllNames(out, max_out, Span<const char *const>(), &SSL_CIPHER::standard_name, Span(kCiphers)); }
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc index fcf7643..d155b55 100644 --- a/ssl/ssl_key_share.cc +++ b/ssl/ssl_key_share.cc
@@ -533,6 +533,6 @@ } size_t SSL_get_all_group_names(const char **out, size_t max_out) { - return GetAllNames(out, max_out, Span<const char *>(), &NamedGroup::name, + return GetAllNames(out, max_out, Span<const char *const>(), &NamedGroup::name, Span(kNamedGroups)); }
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc index 74a330a..41c1cb1 100644 --- a/ssl/ssl_privkey.cc +++ b/ssl/ssl_privkey.cc
@@ -506,9 +506,9 @@ } size_t SSL_get_all_signature_algorithm_names(const char **out, size_t max_out) { - const char *kPredefinedNames[] = {"ecdsa_sha256", "ecdsa_sha384", - "ecdsa_sha512"}; - return GetAllNames(out, max_out, kPredefinedNames, + const char *const kPredefinedNames[] = {"ecdsa_sha256", "ecdsa_sha384", + "ecdsa_sha512"}; + return GetAllNames(out, max_out, Span(kPredefinedNames), &SignatureAlgorithmName::name, Span(kSignatureAlgorithmNames)); }
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index efb4bce..659efbd 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -10932,7 +10932,8 @@ // record_size_limit 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01}; - auto in = dtls ? Span(kClientHelloDTLS) : Span(kClientHelloTLS); + auto in = dtls ? Span<const uint8_t>(kClientHelloDTLS) + : Span<const uint8_t>(kClientHelloTLS); SSL_CLIENT_HELLO client_hello; ASSERT_TRUE( SSL_parse_client_hello(ssl.get(), &client_hello, in.data(), in.size()));
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc index 0bbeed8..1a772f3 100644 --- a/ssl/ssl_versions.cc +++ b/ssl/ssl_versions.cc
@@ -90,7 +90,7 @@ // The following functions map between API versions and wire versions. The // public API works on wire versions. -static const char *kUnknownVersion = "unknown"; +static const char *const kUnknownVersion = "unknown"; struct VersionInfo { uint16_t version;