-
-
Notifications
You must be signed in to change notification settings - Fork 554
/
Copy pathusertype_bitfields.cpp
210 lines (181 loc) · 5.37 KB
/
usertype_bitfields.cpp
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <cstddef>
#include <cstdint>
#include <climits>
#include <type_traits>
namespace itsy_bitsy {
template <std::size_t sz, typename C = void>
struct bit_type {
typedef uint64_t type;
};
template <std::size_t sz>
struct bit_type<sz, std::enable_if_t<(sz <= 1)>> {
typedef bool type;
};
template <std::size_t sz>
struct bit_type<sz,
std::enable_if_t<(sz > 2 && sz <= 16)>> {
typedef uint16_t type;
};
template <std::size_t sz>
struct bit_type<sz,
std::enable_if_t<(sz > 16 && sz <= 32)>> {
typedef uint32_t type;
};
template <std::size_t sz>
struct bit_type<sz,
std::enable_if_t<(sz > 32 && sz <= 64)>> {
typedef uint64_t type;
};
template <std::size_t sz>
using bit_type_t = typename bit_type<sz>::type;
template <typename T, typename V>
bool vcxx_warning_crap(std::true_type, V val) {
return val != 0;
}
template <typename T, typename V>
T vcxx_warning_crap(std::false_type, V val) {
return static_cast<T>(val);
}
template <typename T, typename V>
auto vcxx_warning_crap(V val) {
return vcxx_warning_crap<T>(
std::is_same<bool, T>(), val);
}
template <typename Base, std::size_t bit_target = 0x0,
std::size_t size = 0x1>
void write(Base& b, bit_type_t<size> bits) {
typedef bit_type_t<sizeof(Base) * CHAR_BIT>
aligned_type;
static const std::size_t aligned_type_bit_size
= sizeof(aligned_type) * CHAR_BIT;
static_assert(
sizeof(Base) * CHAR_BIT >= (bit_target + size),
"bit offset and size are too large for the "
"desired structure.");
static_assert((bit_target % aligned_type_bit_size)
<= ((bit_target + size)
% aligned_type_bit_size),
"bit offset and size cross beyond largest "
"integral constant boundary.");
const std::size_t aligned_target
= (bit_target + size) / aligned_type_bit_size;
const aligned_type bits_left
= static_cast<aligned_type>(
bit_target - aligned_target);
const aligned_type shifted_mask
= ((static_cast<aligned_type>(1) << size) - 1)
<< bits_left;
const aligned_type compl_shifted_mask = ~shifted_mask;
// Jump by native size of a pointer to target
// then OR the bits
aligned_type* jumper = static_cast<aligned_type*>(
static_cast<void*>(&b));
jumper += aligned_target;
aligned_type& aligned = *jumper;
aligned &= compl_shifted_mask;
aligned
|= (static_cast<aligned_type>(bits) << bits_left);
}
template <typename Base, std::size_t bit_target = 0x0,
std::size_t size = 0x1>
bit_type_t<size> read(Base& b) {
typedef bit_type_t<sizeof(Base) * CHAR_BIT>
aligned_type;
typedef bit_type_t<size> field_type;
static const std::size_t aligned_type_bit_size
= sizeof(aligned_type) * CHAR_BIT;
static_assert(
sizeof(Base) * CHAR_BIT >= (bit_target + size),
"bit offset and size are too large for the "
"desired structure.");
static_assert((bit_target % aligned_type_bit_size)
<= ((bit_target + size)
% aligned_type_bit_size),
"bit offset and size cross beyond largest "
"integral constant boundary.");
const std::size_t aligned_target
= (bit_target + size) / aligned_type_bit_size;
const aligned_type bits_left
= static_cast<aligned_type>(
bit_target - aligned_target);
const aligned_type mask
= (static_cast<aligned_type>(1) << size) - 1;
// Jump by native size of a pointer to target
// then OR the bits
aligned_type* jumper = static_cast<aligned_type*>(
static_cast<void*>(&b));
jumper += aligned_target;
const aligned_type& aligned = *jumper;
aligned_type field_bits
= (aligned >> bits_left) & mask;
field_type bits
= vcxx_warning_crap<field_type>(field_bits);
return bits;
}
} // namespace itsy_bitsy
#include <iostream>
#if defined(_MSC_VER) || defined(__MINGW32__)
#pragma pack(1)
struct alignas(sizeof(uint32_t)) flags_t {
#else
struct __attribute__((packed, aligned(sizeof(uint32_t))))
flags_t {
#endif
uint8_t C : 1;
uint8_t N : 1;
uint8_t PV : 1;
uint8_t _3 : 1;
uint8_t H : 1;
uint8_t _5 : 1;
uint8_t Z : 1;
uint8_t S : 1;
uint16_t D : 14;
} flags { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int main() {
std::cout << "=== usertype_bitfields ===" << std::endl;
#ifdef __MINGW32__
std::cout << "MinGW Detected, packing structs is broken in "
"MinGW and this test may fail"
<< std::endl;
#endif
sol::state lua;
lua.open_libraries();
lua.new_usertype<flags_t>("flags_t",
"C",
sol::property(itsy_bitsy::read<flags_t, 0>,
itsy_bitsy::write<flags_t, 0>),
"N",
sol::property(itsy_bitsy::read<flags_t, 1>,
itsy_bitsy::write<flags_t, 1>),
"D",
sol::property(itsy_bitsy::read<flags_t, 8, 14>,
itsy_bitsy::write<flags_t, 8, 14>));
lua["f"] = std::ref(flags);
lua.script(R"(
print(f.C)
f.C = true;
print(f.C)
print(f.N)
f.N = true;
print(f.N)
print(f.D)
f.D = 0xDF;
print(f.D)
)");
bool C = flags.C;
bool N = flags.N;
uint16_t D = flags.D;
std::cout << std::hex;
std::cout << "sizeof(flags): " << sizeof(flags)
<< std::endl;
std::cout << "C: " << C << std::endl;
std::cout << "N: " << N << std::endl;
std::cout << "D: " << D << std::endl;
SOL_ASSERT(C);
SOL_ASSERT(N);
SOL_ASSERT(D == 0xDF);
std::cout << std::endl;
return 0;
}