OXYGENHUB
◆ main openmsx / src / utils / AlignedBuffer.hh
3.1 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef ALIGNEDBUFFER_HH
2
#define ALIGNEDBUFFER_HH
3
4
#include <array>
5
#include <bit>
6
#include <cassert>
7
#include <concepts>
8
#include <cstddef>
9
#include <cstdint>
10
#include <type_traits>
11
12
namespace openmsx {
13
14
// Interface for an aligned buffer.
15
// This type doesn't itself provide any storage, it only 'refers' to some
16
// storage. In that sense it is very similar in usage to a 'uint8_t*'.
17
//
18
// For example:
19
//   void f1(uint8_t* buf) {
20
//     ... uint8_t x = buf[7];
21
//     ... buf[4] = 10;
22
//     ... uint8_t* begin = buf;
23
//     ... uint8_t* end   = buf + 12;
24
//   }
25
//   void f2(AlignedBuffer& buf) {
26
//     // The exact same syntax as above, the only difference is that here
27
//     // the compiler knows at compile-time the alignment of 'buf', while
28
//     // above the compiler must assume worst case alignment.
29
//   }
30
class alignas(std::max_align_t) AlignedBuffer
31
{
32
private:
33
	[[nodiscard]] auto* p()       { return std::bit_cast<      uint8_t*>(this); }
34
	[[nodiscard]] auto* p() const { return std::bit_cast<const uint8_t*>(this); }
35
36
public:
37
	static constexpr auto ALIGNMENT = alignof(std::max_align_t);
38
39
	[[nodiscard]] operator       uint8_t*()       { return p(); }
40
	[[nodiscard]] operator const uint8_t*() const { return p(); }
41
	[[nodiscard]]       auto* data()       { return p(); }
42
	[[nodiscard]] const auto* data() const { return p(); }
43
44
	template<std::integral I>
45
	[[nodiscard]] friend       auto* operator+(      AlignedBuffer& b, I i) { return b.p() + i; }
46
	template<std::integral I>
47
	[[nodiscard]] friend const auto* operator+(const AlignedBuffer& b, I i) { return b.p() + i; }
48
49
	template<std::integral I>
50
	[[nodiscard]]       auto& operator[](I i)       { return *(p() + i); }
51
	template<std::integral I>
52
	[[nodiscard]] const auto& operator[](I i) const { return *(p() + i); }
53
};
54
static_assert(alignof(AlignedBuffer) == AlignedBuffer::ALIGNMENT, "must be aligned");
55
56
57
// Provide actual storage for the AlignedBuffer
58
// A possible alternative is to use a union.
59
template<size_t N> class AlignedByteArray : public AlignedBuffer
60
{
61
public:
62
	[[nodiscard]] auto  size()  const { return dat.size(); }
63
	[[nodiscard]] auto* data()        { return dat.data(); }
64
	[[nodiscard]] auto* data()  const { return dat.data(); }
65
	[[nodiscard]] auto  begin()       { return dat.begin(); }
66
	[[nodiscard]] auto  begin() const { return dat.begin(); }
67
	[[nodiscard]] auto  end()         { return dat.end(); }
68
	[[nodiscard]] auto  end()   const { return dat.end(); }
69
70
private:
71
	std::array<uint8_t, N> dat;
72
73
};
74
static_assert(alignof(AlignedByteArray<13>) == AlignedBuffer::ALIGNMENT,
75
              "must be aligned");
76
static_assert(sizeof(AlignedByteArray<32>) == 32,
77
              "we rely on the empty-base optimization");
78
79
80
/** Cast one pointer type to another pointer type.
81
  * When asserts are enabled this checks whether the original pointer is
82
  * properly aligned to point to the destination type.
83
  */
84
template<typename T> [[nodiscard]] static inline T aligned_cast(void* p)
85
{
86
	static_assert(std::is_pointer_v<T>,
87
	              "can only perform aligned_cast on pointers");
88
	assert((std::bit_cast<uintptr_t>(p) %
89
	        alignof(std::remove_pointer_t<T>)) == 0);
90
	return std::bit_cast<T>(p);
91
}
92
93
} //namespace openmsx
94
95
#endif