Bit-fields in structures don't seem to give you back the data you put in?
fromctypesimportStructure, c_uint, c_ulonglong, c_ushortclassFoo(Structure):
_fields_= [("A", c_uint, 1), ("B", c_ushort, 16)]
classBar(Structure):
_fields_= [("A", c_ulonglong, 1), ("B", c_uint, 32)]
if__name__=="__main__":
forain [Foo(), Bar()]:
a.A=0a.B=1print(a.A, a.B)The above should print
But it actually prints
$ python3.10 mini.py 0 0
0 0
For comparison and to test my understanding, I expect the following C code to be equivalent to the Python code above:
#include<stdio.h>structFoo {
unsigned intA: 1;
unsigned shortB: 16;
};
structBar {
unsigned long long intA: 1;
unsigned intB: 32;
};
intmain(intargc, char**argv) {
structFoofoo;
foo.A=0;
foo.B=1;
printf("%d %d\n", foo.A, foo.B);
structBarbar;
bar.A=0;
bar.B=1;
printf("%d %d\n", bar.A, bar.B);
return0;
}The C version prints what we expect:
$ gcc -fsanitize=undefined test.c && ./a.out
0 1
0 1
Your environment
I am on ArchLinux with Python 3.10.7. Python 3.11 and main are also affected. I also randomly tried Python 3.6 with the same result. (Python 3.6 is the oldest one that was easy to install.)
More comprehensive test case
Here's how I actually found the problem reported above. Using Hypothesis:
importctypesimportstringfromhypothesisimportassume, example, given, notefromhypothesisimportstrategiesasstunsigned= [(ctypes.c_ushort, 16), (ctypes.c_uint, 32), (ctypes.c_ulonglong, 64)]
signed= [(ctypes.c_short, 16), (ctypes.c_int, 32), (ctypes.c_longlong, 64)]
types=unsigned+signedunsigned_types=list(zip(*unsigned))[0]
signed_types=list(zip(*signed))[0]
names=st.lists(st.text(alphabet=string.ascii_letters, min_size=1), unique=True)
@st.compositedeffields_and_set(draw):
names_=draw(names)
ops= []
results= []
fornameinnames_:
t, l=draw(st.sampled_from(types))
res= (name, t, draw(st.integers(min_value=1, max_value=l)))
results.append(res)
values=draw(st.lists(st.integers()))
forvalueinvalues:
ops.append((res, value))
ops=draw(st.permutations(ops))
returnresults, opsdeffit_in_bits(value, type_, size):
expect=value% (2**size)
iftype_notinunsigned_types:
ifexpect>=2** (size-1):
expect-=2**sizereturnexpect@given(fops=fields_and_set())deftest(fops):
(fields, ops) =fopsclassBITS(ctypes.Structure):
_fields_=fieldsb=BITS()
for (name, type_, size), valueinops:
expect=fit_in_bits(value, type_, size)
setattr(b, name, value)
j=getattr(b, name)
assertexpect==j, f"{expect} != {j}"if__name__=="__main__":
test()Thanks to @mdickinson for pointing me in this direction.
Linked PRs
Bit-fields in structures don't seem to give you back the data you put in?
The above should print
But it actually prints
For comparison and to test my understanding, I expect the following C code to be equivalent to the Python code above:
The C version prints what we expect:
Your environment
I am on ArchLinux with Python 3.10.7. Python 3.11 and
mainare also affected. I also randomly tried Python 3.6 with the same result. (Python 3.6 is the oldest one that was easy to install.)More comprehensive test case
Here's how I actually found the problem reported above. Using Hypothesis:
Thanks to @mdickinson for pointing me in this direction.
Linked PRs