summaryrefslogtreecommitdiff
path: root/lib/nes/common/span.hh
blob: 6779c370bc9593e9dbd7cc25eb804a09c74f3175 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#pragma once

#include "nes/common/types.hh"
#include "nes/common/debug.hh"

namespace nes
{
	namespace detail
	{
		struct span_unchecked{};

		inline constexpr auto span_dynamic_length = u32{ 0xFFFF };

		template<typename T>
		struct remove_const
		{
			using type = T;
		};

		template<typename T>
		struct remove_const<T const>
		{
			using type = T;
		};

		template<typename T>
		using remove_const_type = typename remove_const<T>::type;
	} // namespace detail

	/// Implementation of a container similar to std::span in C++20.
	template<typename T, u32 Length = detail::span_dynamic_length>
	class span
	{
		template<typename U, u32 OtherLength>
		friend class span;

		T* data_{ nullptr };
		u32 length_{ 0 };

	public:
		using iterator = T*;

		explicit span()
		{
			static_assert(Length == 0 || Length == detail::span_dynamic_length);
		}

		explicit span(T* data, u32 const length)
			: span{ data, length, {} }
		{
			static_assert(Length == detail::span_dynamic_length);
		}

		template<u32 ActualLength>
		span(T (&data)[ActualLength])
			: span{ data, ActualLength, {} }
		{
			static_assert(Length == ActualLength || Length == detail::span_dynamic_length);
		}

		span(span<detail::remove_const_type<T>, Length> const other)
			: span{ other.data_, other.length_, {} }
		{
		}

		auto get_data() const -> T* { return data_; }
		auto get_length() const -> u32 { return length_; }

		auto begin() const -> iterator { return data_; }
		auto end() const -> iterator { return data_ + length_; }

		auto subspan(u32 const first, u32 const length) const -> span<T>
		{
			NES_ASSERT(first + length <= get_length() && "subspan out of bounds");
			return span{ data_ + first, length };
		}

		auto subspan(u32 const first) const -> span<T>
		{
			NES_ASSERT(first <= get_length() && "subspan out of bounds");
			return subspan(length_ - first);
		}

		template<u32 OtherLength>
		auto subspan(u32 const first) const -> span<T, OtherLength>
		{
			static_assert(OtherLength < Length);
			NES_ASSERT(first + OtherLength <= get_length() && "subspan out of bounds");
			return span<T, OtherLength>{ data_ + first, OtherLength, {} };
		}

		auto operator[](u32 const index) const -> T&
		{
			NES_ASSERT(index < get_length() && "index out of bounds");
			return data_[index];
		}

	private:
		explicit span(T* data, u32 const length, detail::span_unchecked)
			: data_{ data }
			, length_{ length }
		{
		}
	};
} // namespace nes