Coverage Report

Created: 2023-12-13 14:58

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