Coverage Report

Created: 2024-04-30 09:35

src/zserio/Span.h
Line
Count
Source
1
#ifndef ZSERIO_SPAN_H_INC
2
#define ZSERIO_SPAN_H_INC
3
4
#include <array>
5
#include <cstddef>
6
#include <iterator>
7
#include <limits>
8
#include <type_traits>
9
#include <vector>
10
11
namespace zserio
12
{
13
14
/**
15
 * Constant used to differentiate between spans of dynamic and static extent.
16
 */
17
constexpr std::size_t dynamic_extent = std::numeric_limits<std::size_t>::max();
18
19
namespace detail
20
{
21
22
template <typename T, std::size_t Extent>
23
struct SpanStorage
24
{
25
2
    SpanStorage() = default;
26
27
    SpanStorage(T* data, std::size_t) :
28
            m_data(data)
29
29
    {}
30
31
    T* m_data = nullptr;
32
    static constexpr std::size_t m_size = Extent;
33
};
34
35
template <typename T>
36
struct SpanStorage<T, dynamic_extent>
37
{
38
853
    SpanStorage() = default;
39
40
    SpanStorage(T* data, std::size_t size) :
41
            m_data(data),
42
            m_size(size)
43
61.7k
    {}
44
45
    T* m_data = nullptr;
46
    std::size_t m_size = 0;
47
};
48
49
} // namespace detail
50
51
/**
52
 * Class that holds non-owning reference (aka. "view") to continuous sequence of objects.
53
 * The user of this class is responsible of making sure, that the referenced sequence is valid
54
 * as long as the instance of the Span is alive.
55
 * Inspired by C++20 std::span.
56
 */
57
template <typename T, std::size_t Extent = dynamic_extent>
58
class Span
59
{
60
public:
61
    using element_type = T;
62
    using value_type = typename std::remove_cv<T>::type;
63
    using size_type = std::size_t;
64
    using difference_type = std::ptrdiff_t;
65
    using pointer = T*;
66
    using const_pointer = const T*;
67
    using reference = T&;
68
    using iterator = pointer;
69
    using const_iterator = const_pointer;
70
    using reverse_iterator = std::reverse_iterator<iterator>;
71
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
72
73
    static constexpr size_type extent = Extent;
74
75
    /**
76
     * Constructor. Initializes empty Span.
77
     */
78
    template <size_type ext = Extent,
79
            typename std::enable_if<(ext == 0 || ext == dynamic_extent), int>::type = 0>
80
    constexpr Span() noexcept
81
855
    {}
82
83
    /**
84
     * Constructor. Initializes Span holding a reference starting at given pointer, having
85
     * given number of elements.
86
     *
87
     * \param first Pointer to first element in the sequence.
88
     * \param count Number of elements.
89
     */
90
    constexpr Span(pointer first, size_type count) :
91
            m_storage(first, count)
92
59.6k
    {}
93
94
    /**
95
     * Constructor. Initializes Span holding a reference starting at given pointer, ending
96
     * at another pointer.
97
     *
98
     * \param first Pointer to first element in the sequence.
99
     * \param last Pointer to one-after-last element in the sequence.
100
     */
101
    constexpr Span(pointer first, pointer last) :
102
            m_storage(first, static_cast<size_t>(last - first))
103
2
    {}
104
105
    /**
106
     * Constructor. Initializes Span holding a reference to array.
107
     *
108
     * \param arr Array to which the Span will hold a reference.
109
     */
110
    template <size_type N, size_type ext = Extent,
111
            typename std::enable_if<(ext == dynamic_extent || ext == N), int>::type = 0>
112
    constexpr Span(element_type (&arr)[N]) noexcept :
113
            m_storage(arr, N)
114
7
    {}
115
116
    /**
117
     * Constructor. Initializes Span holding a reference to std::array.
118
     *
119
     * \param arr std::array to which the Span will hold a reference.
120
     */
121
    template <typename U, size_type N, size_type ext = Extent,
122
            typename std::enable_if<(ext == dynamic_extent || ext == N) &&
123
                            std::is_convertible<U (*)[], T (*)[]>::value,
124
                    int>::type = 0>
125
    constexpr Span(std::array<U, N>& arr) noexcept :
126
            m_storage(arr.data(), arr.size())
127
10
    {}
128
129
    /**
130
     * Constructor. Initializes Span holding a reference to std::array.
131
     *
132
     * \param arr std::array to which the Span will hold a reference.
133
     */
134
    template <typename U, size_type N, size_type ext = Extent,
135
            typename std::enable_if<(ext == dynamic_extent || ext == N) &&
136
                            std::is_convertible<const U (*)[], T (*)[]>::value,
137
                    int>::type = 0>
138
    constexpr Span(const std::array<U, N>& arr) noexcept :
139
            m_storage(arr.data(), arr.size())
140
143
    {}
141
142
    /**
143
     * Constructor. Initializes Span holding a reference to std::vector.
144
     *
145
     * \param vec std::vector to which the Span will hold a reference.
146
     */
147
    template <typename U, typename ALLOC, size_type ext = Extent,
148
            typename std::enable_if<(ext == dynamic_extent) && std::is_convertible<U (*)[], T (*)[]>::value,
149
                    int>::type = 0>
150
    constexpr Span(std::vector<U, ALLOC>& vec) :
151
            m_storage(vec.data(), vec.size())
152
1.02k
    {}
153
154
    /**
155
     * Constructor. Initializes Span holding a reference to std::vector.
156
     *
157
     * \param vec std::vector to which the Span will hold a reference.
158
     */
159
    template <typename U, typename ALLOC, size_type ext = Extent,
160
            typename std::enable_if<
161
                    (ext == dynamic_extent) && std::is_convertible<const U (*)[], T (*)[]>::value, int>::type =
162
                    0>
163
    constexpr Span(const std::vector<U, ALLOC>& vec) :
164
            m_storage(vec.data(), vec.size())
165
837
    {}
166
167
    /**
168
     * Constructor. Convert between spans of different types.
169
     *
170
     * \param span Input span.
171
     */
172
    template <typename U, size_type N,
173
            typename std::enable_if<(Extent == N || Extent == dynamic_extent) &&
174
                            std::is_convertible<U (*)[], T (*)[]>::value,
175
                    int>::type = 0>
176
    constexpr Span(const Span<U, N>& span) noexcept :
177
            m_storage(span.data(), span.size())
178
71
    {}
179
180
    /**
181
     * Method generated by default.
182
     * \{
183
     */
184
    ~Span() = default;
185
186
    Span(const Span& other) noexcept = default;
187
    Span& operator=(const Span& other) noexcept = default;
188
189
    Span(Span&& other) noexcept = default;
190
    Span& operator=(Span&& other) noexcept = default;
191
    /**
192
     * \}
193
     */
194
195
    /**
196
     * Begin iteration.
197
     *
198
     * \return Iterator to the beginning of the sequence.
199
     */
200
    constexpr iterator begin() const noexcept
201
66.4k
    {
202
66.4k
        return data();
203
66.4k
    }
204
205
    /**
206
     * End iteration.
207
     *
208
     * \return Iterator one-past-last element of the sequence.
209
     */
210
    constexpr iterator end() const noexcept
211
54.0k
    {
212
54.0k
        return data() + size();
213
54.0k
    }
214
215
    /**
216
     * Begin reverse iteration.
217
     *
218
     * \return Reverse iterator to the last element.
219
     */
220
    constexpr reverse_iterator rbegin() const noexcept
221
2
    {
222
2
        return reverse_iterator(end());
223
2
    }
224
225
    /**
226
     * End reverse iteration.
227
     *
228
     * \return Reverse iterator one-before-first element of the sequence.
229
     */
230
    constexpr reverse_iterator rend() const noexcept
231
8
    {
232
8
        return reverse_iterator(begin());
233
8
    }
234
235
    /**
236
     * Get reference to the first element of the sequence. The sequence shall not be empty.
237
     *
238
     * \return Reference to the first element of the sequence
239
     */
240
    constexpr reference front() const
241
2
    {
242
2
        return data()[0];
243
2
    }
244
245
    /**
246
     * Get reference to the last element of the sequence. The sequence shall not be empty.
247
     *
248
     * \return Reference to the last element of the sequence
249
     */
250
    constexpr reference back() const
251
2
    {
252
2
        return data()[size() - 1];
253
2
    }
254
255
    /**
256
     * Access the element on given index.
257
     *
258
     * \param idx Index of element to be accessed. Must be less than size().
259
     * \return Element on given index.
260
     */
261
    constexpr reference operator[](size_type idx) const
262
239k
    {
263
239k
        return data()[idx];
264
239k
    }
265
266
    /**
267
     * Get pointer to the sequence beginning.
268
     *
269
     * \return Pointer to the sequence beginning
270
     */
271
    constexpr pointer data() const noexcept
272
466k
    {
273
466k
        return m_storage.m_data;
274
466k
    }
275
276
    /**
277
     * Get number of elements in the sequence.
278
     *
279
     * \return Number of elements in the sequence.
280
     */
281
    constexpr size_type size() const noexcept
282
167k
    {
283
167k
        return m_storage.m_size;
284
167k
    }
285
286
    /**
287
     * Get size of sequence in bytes.
288
     *
289
     * \return Size of sequence in bytes.
290
     */
291
    constexpr size_type size_bytes() const noexcept
292
2
    {
293
2
        return size() * sizeof(element_type);
294
2
    }
295
296
    /**
297
     * Check if the sequence is empty.
298
     *
299
     * \return True if the sequence is empty, false otherwise.
300
     */
301
    constexpr bool empty() const noexcept
302
6
    {
303
6
        return size() == 0;
304
6
    }
305
306
    /**
307
     * Get subspan of given number of elements from the sequence start.
308
     *
309
     * \return Subspan of given number of elements from the sequence start.
310
     */
311
    template <size_type Count>
312
    constexpr Span<element_type, Count> first() const
313
2
    {
314
2
        static_assert(Count <= Extent, "Requested number of characters out of range.");
315
2
        return Span<element_type, Count>(data(), Count);
316
2
    }
317
318
    /**
319
     * Get subspan of given number of elements from the sequence start.
320
     *
321
     * \param Count Requested number of elements in the subspan. Shall not be bigger than size().
322
     * \return Subspan of given number of elements from the sequence start.
323
     */
324
    constexpr Span<element_type, dynamic_extent> first(size_type Count) const
325
2
    {
326
2
        return Span<element_type, dynamic_extent>(data(), Count);
327
2
    }
328
329
    /**
330
     * Get subspan of given number of elements from the sequence end.
331
     *
332
     * \return Subspan of given number of elements from the sequence end.
333
     */
334
    template <size_type Count>
335
    constexpr Span<element_type, Count> last() const
336
2
    {
337
2
        static_assert(Count <= Extent, "Requested number of characters out of range.");
338
2
        return Span<element_type, Count>(data() + (size() - Count), Count);
339
2
    }
340
341
    /**
342
     * Get subspan of given number of elements from the sequence end.
343
     *
344
     * \param Count Requested number of elements in the subspan. Shall not be bigger than size().
345
     * \return Subspan of given number of elements from the sequence end.
346
     */
347
    constexpr Span<element_type, dynamic_extent> last(size_type Count) const
348
2
    {
349
2
        return Span<element_type, dynamic_extent>(data() + (size() - Count), Count);
350
2
    }
351
352
    template <size_type Offset, size_type Count>
353
    using SubspanReturnType = Span<T,
354
            Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : dynamic_extent)>;
355
356
    /**
357
     * Get subspan of given number of elements beginning at given index.
358
     *
359
     * \return Subspan of given number of elements beginning at given index.
360
     */
361
    /** \{ */
362
    template <size_type Offset, size_type Count = dynamic_extent,
363
            typename std::enable_if<Count == dynamic_extent, int>::type = 0>
364
    constexpr SubspanReturnType<Offset, Count> subspan() const
365
2
    {
366
2
        static_assert((Extent == dynamic_extent) || (Offset <= Extent),
367
2
                "Requested number of characters out of range.");
368
2
        return SubspanReturnType<Offset, Count>(data() + Offset, size() - Offset);
369
2
    }
370
371
    template <size_type Offset, size_type Count,
372
            typename std::enable_if<Count != dynamic_extent, int>::type = 0>
373
    constexpr SubspanReturnType<Offset, Count> subspan() const
374
2
    {
375
2
        static_assert((Extent == dynamic_extent) || (Offset <= Extent && Offset + Count <= Extent),
376
2
                "Requested number of characters out of range.");
377
2
        return SubspanReturnType<Offset, Count>(data() + Offset, Count);
378
2
    }
379
    /** \} */
380
381
    /**
382
     * Get subspan of given number of elements beginning at given index.
383
     *
384
     * \param Offset Index in the original sequence, where the new subspan should start
385
     * \param Count Requested number of elements in the subspan.
386
     * \return Subspan of given number of elements beginning at given index.
387
     */
388
    constexpr Span<element_type, dynamic_extent> subspan(
389
            size_type Offset, size_type Count = dynamic_extent) const
390
4
    {
391
4
        return Span<element_type, dynamic_extent>(
392
4
                data() + Offset, Count == dynamic_extent ? 
size() - Offset2
:
Count2
);
393
4
    }
394
395
private:
396
    detail::SpanStorage<T, Extent> m_storage;
397
};
398
399
} // namespace zserio
400
401
#endif // ZSERIO_SPAN_H_INC