Oren
Oren

Reputation: 94

edit the buffer of a Ctypes structure

I have a Ctypes structure which needs to be both editable in the standard way [setattr(structure,value)] which is easy to implement, but I also need to be able to edit the raw buffer, since I want to be able to assign a certain bit to a value (bit 25 = 0xd5 for example) How do I do that?

very simplified example code, if it helps

import ctypes as *

#ctypes array with ~250,000 c_uint32 elements
huge_arr = (c_uint32*250,000)(*range(250,000))  # Fill with dummy data for this example

class Example(Structure):
    _pack_ = 1
    _fields_ = [
        ("a", c_uint16),
        ("b", c_uint16, 14),
        ("c", c_uint16, 2),
        ("d", c_uint32, 24),
        ("e", c_uint32, 8),
        ("f", c_uint16),
        ("g", c_uint16)
    ]

offset = 123456
example_struct = Example.from_buffer(huge_arr, offset)

# Ideally, I'd like to be able to set bits in example_struct. for example, example_struct[2] = 0x2b

I know it's possible to do example_struct[2] = 0x2b by doing huge_arr[offset+2] = 0x2b, but my program is more complicated than this example, and huge_arr is defined (and stays) in a main file, while example_struct is transferred as a parameter to another function in a different file, so huge_arr is out of scope. Is there any way to change the n-th bit of exmaple_struct?

Another thing to note, this program is written in Python 2.7, but even a python3 solution would be appreciated

Thank you in advance for your help (and I will most certainly mark a best answer to any kind soul who can solve this issue)

Upvotes: 1

Views: 795

Answers (1)

CristiFati
CristiFati

Reputation: 41137

Listing [Python 3.Docs]: ctypes - A foreign function library for Python.

Several places in the question you used the term "bit", but you probably meant "byte" (as a bit can only have a value of 0 or 1).

To achieve your goal, you can wrap the structure in a union.

code00.py:

#!/usr/bin/env python3

import sys
import ctypes as ct


class ExampleStruct(ct.Structure):
    _pack_ = 1
    _fields_ = [
        ("a", ct.c_uint16),
        ("b", ct.c_uint16, 14),
        ("c", ct.c_uint16, 2),
        ("d", ct.c_uint32, 24),
        ("e", ct.c_uint32, 8),  # @TODO - cfati: Why not c_uint8 ???
        ("f", ct.c_uint16),
        ("g", ct.c_uint16),
        ("test_field", ct.c_uint8),  # One byte field would make the example more eloquent
    ]


class Example(ct.Union):
    _anonymous_ = ["struct"]
    _fields_ = [
        ("struct", ExampleStruct),
        ("raw", ct.c_ubyte * ct.sizeof(ExampleStruct)),
    ]


def main():
    huge_arr_size = 250000
    huge_arr = (ct.c_uint32 * huge_arr_size)(*range(huge_arr_size))
    arr_offset = 123456
    example = Example.from_buffer(huge_arr, arr_offset)
    print("example.test_field: {0:d}".format(example.test_field))
    test_field_offset = Example.test_field.offset
    example.raw[test_field_offset] = 123
    print("example.test_field: {0:d}".format(example.test_field))


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

Output:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q058460001]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code00.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32

example.test_field: 147
example.test_field: 123

Done.

Upvotes: 1

Related Questions