Nugget
Loading...
Searching...
No Matches
bitfield.hh
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2025 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29#include <stdint.h>
30
31#include <concepts>
32#include <type_traits>
33
34#include "util.h"
35
36namespace Utilities {
37
38namespace BitFieldInternal {
39
40template <typename T>
41concept IntegralLike = std::is_integral_v<T> || std::is_enum_v<T>;
42
43template <IntegralLike T>
45 static constexpr unsigned size = sizeof(T) * 8;
46};
47
48template <>
49struct DefaultBitSize<bool> {
50 static constexpr unsigned size = 1;
51};
52
53template <typename... T>
55 static constexpr unsigned size() { return (sizeInBits() + 7) / 8; }
56
57 private:
58 static constexpr unsigned sizeInBits() { return recSize<0, T...>(); }
59 template <unsigned index>
60 static constexpr unsigned recSize() {
61 return 0;
62 }
63 template <unsigned index, typename One, typename... Rest>
64 static constexpr unsigned recSize() {
65 return One::Width + recSize<index + 1, Rest...>();
66 }
67};
68
69template <typename Target, typename... T>
71 static constexpr unsigned offset() { return recOffset<0, T...>(); }
72
73 private:
74 template <unsigned index>
75 static constexpr unsigned recOffset() {
76 return 0;
77 }
78 template <unsigned index, typename One, typename... Rest>
79 static constexpr unsigned recOffset() {
80 if constexpr (std::is_same_v<Target, One>) {
81 return 0;
82 } else {
83 return recOffset<index + 1, Rest...>() + One::Width;
84 }
85 }
86};
87
88template <std::integral T, std::integral U = T>
89using SignedType = typename std::conditional_t<std::is_signed_v<T>, std::make_signed_t<U>, std::make_unsigned_t<U>>;
90
91template <unsigned span>
92using StorageType = typename std::conditional_t<
93 span <= 8, uint8_t,
94 typename std::conditional_t<span <= 16, uint16_t, typename std::conditional_t<span <= 32, uint32_t, void>>>;
95
96template <unsigned span, std::integral T>
98
99template <unsigned Offset, unsigned Width, unsigned storageSize, std::integral T>
101 static constexpr unsigned offset = Offset;
102 static constexpr unsigned width = Width;
103 static constexpr unsigned firstByteOffset = offset / 8;
104 static constexpr unsigned lastByteOffset = (offset + width - 1) / 8;
105 static constexpr unsigned bytesCount = lastByteOffset - firstByteOffset + 1;
106 static constexpr unsigned shift = offset % 8;
107 static constexpr uint32_t mask = (1 << width) - 1;
108 static constexpr bool isAlignedAndSafe =
109 ((firstByteOffset % sizeof(T)) == 0) && (firstByteOffset + sizeof(T)) <= storageSize;
110 static constexpr bool fullBytes = ((width % 8) == 0) && ((offset % 8) == 0);
112 static_assert(bytesCount <= 4, "Type too large");
113 static_assert(width > 0, "Width must be greater than 0");
114 static_assert(width <= 32, "Width must be less than or equal to 32");
115 static_assert(offset + width <= storageSize * 8, "Offset + Width must be less than or equal to storage size");
116 }
117};
118
119enum Dummy : int;
120
121} // namespace BitFieldInternal
122
129template <BitFieldInternal::IntegralLike T, unsigned width = BitFieldInternal::DefaultBitSize<T>::size>
130struct BitSpan {
131 static constexpr unsigned Width = width;
132 using Type = T;
134 std::conditional_t<std::is_enum_v<T>,
135 std::underlying_type_t<std::conditional_t<std::is_enum_v<T>, T, BitFieldInternal::Dummy>>,
136 T>;
137};
138
154template <typename... T>
155struct BitField {
156 template <typename Field>
157 constexpr Field::Type get() const {
158 constexpr unsigned offset = BitFieldInternal::ComputeOffset<Field, T...>::offset();
159 auto ret = get<offset, Field::Width,
160 BitFieldInternal::SignedStorageType<(offset % 8) + Field::Width, typename Field::Underlying>>();
161 return static_cast<Field::Type>(ret);
162 }
163 template <typename Field>
164 constexpr void set(Field::Type v_) {
165 constexpr unsigned offset = BitFieldInternal::ComputeOffset<Field, T...>::offset();
166 auto v = static_cast<Field::Underlying>(v_);
167 set<offset, Field::Width,
168 BitFieldInternal::SignedStorageType<(offset % 8) + Field::Width, typename Field::Underlying>>(v);
169 }
170 void clear() {
171 for (unsigned i = 0; i < sizeof(storage); i++) {
172 storage[i] = 0;
173 }
174 }
175
176 private:
177 template <unsigned offset, unsigned width, std::integral U>
178 constexpr U get() const {
179 using helper = BitFieldInternal::BitFieldHelper<offset, width, sizeof(storage), U>;
180 if constexpr (helper::isAlignedAndSafe) {
181 return reinterpret_cast<const U*>(storage)[helper::firstByteOffset / sizeof(U)] >> helper::shift &
182 helper::mask;
183 } else {
184 return (loadUnaligned<U, helper::bytesCount>(storage + helper::firstByteOffset) >> helper::shift) &
185 helper::mask;
186 }
187 return 0;
188 }
189 template <unsigned offset, unsigned width, std::integral U>
190 constexpr void set(U v) {
191 using helper = BitFieldInternal::BitFieldHelper<offset, width, sizeof(storage), U>;
192 if constexpr (helper::fullBytes) {
193 if constexpr (helper::bytesCount == 1) {
194 storage[helper::firstByteOffset] = static_cast<uint8_t>(v);
195 } else if constexpr (helper::bytesCount == 2) {
196 if constexpr (helper::isAlignedAndSafe) {
197 *reinterpret_cast<U*>(storage + helper::firstByteOffset) = v;
198 } else {
199 storeUnaligned<U>(storage + helper::firstByteOffset, v);
200 }
201 } else if constexpr (helper::bytesCount == 3) {
202 if constexpr ((helper::firstByteOffset % 2) == 0) {
203 *reinterpret_cast<uint16_t*>(storage + helper::firstByteOffset) = static_cast<uint16_t>(v);
204 storage[helper::firstByteOffset + 2] = static_cast<uint8_t>(v >> 16);
205 } else {
206 storage[helper::firstByteOffset] = static_cast<uint8_t>(v);
207 *reinterpret_cast<uint16_t*>(storage + helper::firstByteOffset + 1) = static_cast<uint16_t>(v >> 8);
208 }
209 } else if constexpr (helper::bytesCount == 4) {
210 if constexpr (helper::isAlignedAndSafe) {
211 *reinterpret_cast<U*>(storage + helper::firstByteOffset) = v;
212 } else {
213 storeUnaligned<U>(storage + helper::firstByteOffset, v);
214 }
215 }
216 } else if constexpr (helper::isAlignedAndSafe) {
217 U* ptr = reinterpret_cast<U*>(storage);
218 ptr[helper::firstByteOffset / sizeof(U)] &= ~(helper::mask << helper::shift);
219 ptr[helper::firstByteOffset / sizeof(U)] |= (v & helper::mask) << helper::shift;
220 } else {
221 U span = loadUnaligned<U, helper::bytesCount>(storage + helper::firstByteOffset);
222 span &= ~(helper::mask << helper::shift);
223 span |= (v & helper::mask) << helper::shift;
224 storeUnaligned<U, helper::bytesCount>(storage + helper::firstByteOffset, span);
225 }
226 }
228};
229
230} // namespace Utilities
volatile uint32_t * ptr
Definition cop0.c:80
typename std::conditional_t< span<=8, uint8_t, typename std::conditional_t< span<=16, uint16_t, typename std::conditional_t< span<=32, uint32_t, void > > > StorageType
Definition bitfield.hh:94
SignedType< T, StorageType< span > > SignedStorageType
Definition bitfield.hh:97
typename std::conditional_t< std::is_signed_v< T >, std::make_signed_t< U >, std::make_unsigned_t< U > > SignedType
Definition bitfield.hh:89
Definition bitfield.hh:36
static constexpr bool fullBytes
Definition bitfield.hh:110
static constexpr unsigned firstByteOffset
Definition bitfield.hh:103
static constexpr bool isAlignedAndSafe
Definition bitfield.hh:108
static constexpr unsigned shift
Definition bitfield.hh:106
static constexpr unsigned lastByteOffset
Definition bitfield.hh:104
static constexpr unsigned width
Definition bitfield.hh:102
static constexpr unsigned offset
Definition bitfield.hh:101
static constexpr unsigned bytesCount
Definition bitfield.hh:105
BitFieldHelper()
Definition bitfield.hh:111
static constexpr uint32_t mask
Definition bitfield.hh:107
static constexpr unsigned offset()
Definition bitfield.hh:71
static constexpr unsigned size()
Definition bitfield.hh:55
static constexpr unsigned size
Definition bitfield.hh:45
A bit field that can hold multiple bit field elements of different types.
Definition bitfield.hh:155
constexpr void set(Field::Type v_)
Definition bitfield.hh:164
constexpr Field::Type get() const
Definition bitfield.hh:157
void clear()
Definition bitfield.hh:170
A bit field element to be used in a BitField.
Definition bitfield.hh:130
T Type
Definition bitfield.hh:132
static constexpr unsigned Width
Definition bitfield.hh:131
std::conditional_t< std::is_enum_v< T >, std::underlying_type_t< std::conditional_t< std::is_enum_v< T >, T, BitFieldInternal::Dummy > >, T > Underlying
Definition bitfield.hh:136
static size_t size_t width
Definition syscalls.h:157
static int ret
Definition syscalls.h:72
void int(code1, code2)
void uint32_t(classId, spec)