Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #pragma once
18 : :
19 : : #include <cstdint>
20 : : #include <span>
21 : : #include <stdexcept>
22 : :
23 : : namespace wirestead {
24 : : namespace memory {
25 : :
26 : : /**
27 : : * @brief C++20 std::span based memory view
28 : : *
29 : : * SafeSpan is now a specialized version of std::span that maintains
30 : : * backward compatibility for at() and ensures subspan returns SafeSpan.
31 : : */
32 : : template <typename T, std::size_t Extent = std::dynamic_extent>
33 : : class SafeSpan : public std::span<T, Extent> {
34 : : public:
35 : : using Base = std::span<T, Extent>;
36 : : using Base::Base;
37 : :
38 : : // Polyfill for C++17 pointer+size constructor (std::span requires contiguous_iterator)
39 : 4745 : constexpr SafeSpan(T* data, std::size_t size) noexcept : Base(data, size) {}
40 : :
41 : : // Constructor from std::span
42 : 1044 : constexpr SafeSpan(Base s) noexcept : Base(s) {}
43 : :
44 : : // Polyfill for at() which is missing in std::span
45 : : constexpr typename Base::reference at(std::size_t index) const {
46 : : if (index >= this->size()) {
47 : : throw std::out_of_range("SafeSpan index out of range");
48 : : }
49 : : return (*this)[index];
50 : : }
51 : :
52 : : // Override subspan to return SafeSpan instead of std::span, and to check
53 : : // bounds - std::span::subspan()'s out-of-range offset/count is undefined
54 : : // behavior, which would otherwise be the only unchecked accessor left in a
55 : : // class named "Safe" (at() is already checked above).
56 : 1044 : constexpr SafeSpan<T, std::dynamic_extent> subspan(std::size_t offset,
57 : : std::size_t count = std::dynamic_extent) const {
58 [ - + ]: 1044 : if (offset > this->size()) {
59 : 0 : throw std::out_of_range("SafeSpan::subspan: offset out of range");
60 : : }
61 [ + - - + : 1044 : if (count != std::dynamic_extent && count > this->size() - offset) {
- + ]
62 : 0 : throw std::out_of_range("SafeSpan::subspan: count out of range");
63 : : }
64 : 1044 : return SafeSpan<T, std::dynamic_extent>(Base::subspan(offset, count));
65 : : }
66 : :
67 : : constexpr SafeSpan<T, std::dynamic_extent> first(std::size_t count) const {
68 : : if (count > this->size()) {
69 : : throw std::out_of_range("SafeSpan::first: count out of range");
70 : : }
71 : : return SafeSpan<T, std::dynamic_extent>(Base::first(count));
72 : : }
73 : :
74 : : constexpr SafeSpan<T, std::dynamic_extent> last(std::size_t count) const {
75 : : if (count > this->size()) {
76 : : throw std::out_of_range("SafeSpan::last: count out of range");
77 : : }
78 : : return SafeSpan<T, std::dynamic_extent>(Base::last(count));
79 : : }
80 : : };
81 : :
82 : : // Type aliases for common types
83 : : using ByteSpan = SafeSpan<uint8_t>;
84 : : using ConstByteSpan = SafeSpan<const uint8_t>;
85 : : using CharSpan = SafeSpan<char>;
86 : : using ConstCharSpan = SafeSpan<const char>;
87 : :
88 : : } // namespace memory
89 : : } // namespace wirestead
|