Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
Closed
Labels
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 1 0 1 But it actually prints
$ python3.10 mini.py 0 0 0 0For 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
Metadata
Metadata
Assignees
Labels
Projects
Status
Done