Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork34.1k
Closed
Description
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
0 10 1But it actually prints
$ python3.10 mini.py 0 00 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 {unsignedintA:1;unsigned shortB:16;};structBar {unsigned long longintA:1;unsignedintB: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.out0 10 1Your environment
I am on ArchLinux with Python 3.10.7. Python 3.11 andmain 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. UsingHypothesis:
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
Metadata
Metadata
Assignees
Projects
Status
Done