Define function in Python as it defined in C - python

I have function in C that reads byte by byte from a given buffer and returns the result of a mathematical formula.
I need to write the same function in Python
The buffer in C is struct and in python i used ctypes Structure class
my prototype in c is int calc_formula(char *buff,int len)
so calling the function in c is staright forward but how i define such function in Python?
I try to define the following and have some questions
def calc_formula(buff,len):
some code
In C I called the function with pointer to the strcut first char. How do I do it in Python? is buff passed as pointer? My buffer is very large and if it can't be done, I will use global variable (which is less preferred).
I need to read the buffer byte by byte, so in c I simply increment the buffer pointer. What's the way to do it in python? I read about ctypes union class that I can define over the Structure and go over it byte by byte. Do you have a better solution?
UPDATE
i tried bbrame solution :
def calc_formula(buff, len):
sum = 0
for curChar in buff:
numericByteValue = ord(curChar)
sum += numericByteValue
return sum
with When i try its code with calc_formula(input_buff,len) , i get the following:
"*error:TypeError: 't_input_buff' object is not iterable*" - input_buff is instance of t_input_buff that is Class(Structure) . what can be the problem?
(it give me the error when it try to do the for command)

In c, try using the type c_char_p rather than char* (see the ctypes documentation).
In python the parameter (buff) will be a python string. Loop through it as follows:
def calc_formula(buff, len):
sum = 0
for curChar in buff:
numericByteValue = ord(curChar)
sum += numericByteValue
return sum

UPDATE
i solve it with ctypes union class
for answer look in this question

Related

Sending null terminated string to my C code through Python

I am sending strings to my BPF C code and I am not sure if the strings passed in are null-terminated. If they are not, is there a way to make them null terminated? I am sending in my lines of code to BPF so I can count them manually using my stringCounter function but I keep hitting a forever loop sadly. Here is what my Python code looks like:
b = BPF(src_file="hello.c")
lookupTable = b["lookupTable"]
#add hello.csv to the lookupTable array
f = open("hello copy.csv","r")
contents = f.readlines()
for i in range(0,len(contents)):
string = contents[i].encode('utf-8')
lookupTable[ctypes.c_int(i)] = ctypes.create_string_buffer(string, len(string))
And here is the code I found for my null terminated string counter
int stringLength(char* txt)
{
int i=0,count=0;
while(txt[i++]!='\0'){
count+=1;
}
return count;
}
ctypes.create_string_buffer(string, len(string)) is not zero-terminated. But ctypes.create_string_buffer(string) is. It's easy to see that, since ctypes.create_string_buffer(string)[-1] is b'\x00', whereas ctypes.create_string_buffer(string, len(string))[-1] is the last byte in string.
In other words, if you want a zero-terminated buffer, let create_string_buffer figure out the length. (It uses the actual length from the Python bytes object, so it doesn't get fooled by internal NUL bytes, if you were worried about that.)
I'm unfamiliar with BPF but for ctypes, if your string isn't modified by the C code you don't need create_string_buffer as it is used to create mutable buffers, and Python Unicode and byte strings are both always passed nul-terminated wchar_t* or char*, respectively, to C code. Assuming your function is in test.dll or test.so:
import ctypes as ct
dll = ct.CDLL('./test')
dll.stringLength.argtypes = ct.c_char_p,
dll.stringLength.restype = ct.c_int
print(dll.stringLength('somestring'.encode())) # If string is Unicode
print(dll.stringLength(b'someotherstring')) # If already a byte string
Output:
10
15
Note this doesn't preclude having a nul in the string itself, but your count function will return a shorter value in that case:
print(dll.stringLength(b'some\0string')) # Output: 4
Your code could be probably be written as the following assuming there isn't some requirement that a BPF object have hard-coded ctypes types as indexes and values.
with open("hello copy.csv") as file:
for i,line in enumerate(file):
lookupTable[i] = string.encode()

Pass ByteArray from Python to C function

I would like to pass a ByteArray variable from my Python program to my DLL written in C in order to accelerate some specific processing which is too slow in Python. I have gone through the Web, tried Ctypes with combinations of byref, cast, memoryviews, addressof, but nothing works. Is there any simple way to achieve this without copying my ByteArray into something else that will pass ?
Here is what I am trying to do:
/* My C DLL */
__declspec(dllexport) bool FastProc(char *P, int L)
{
/* Do some complex processing on the char buffer */
;
return true;
}
# My Python program
from ctypes import *
def main(argv):
MyData = ByteArray([1,2,3,4,5,6])
dll = CDLL('CHELPER.dll')
dll.FastProc.argtypes = (c_char_p, c_int)
dll.FastProc.restype = c_bool
Result = dll.FastProc(MyData, len(MyData))
print(Result)
But I get a type error when passing the first parameter (MyData) to the C function.
Is there any solution that doesn't require too much overhead that would waste the benefits of my C function ?
Olivier
I'll assume that ByteArray is supposed to be bytearray. We can use create_string_buffer to create a mutable character buffer which is a ctypes array of c_char. But create_string_buffer will not accept a bytearray, we need to pass it a bytes object to initialize it; fortunately, casting between bytes and bytearray is fast and efficient.
I don't have your DLL, so to test that the array behaves correctly I'll use the libc.strfry function to shuffle its chars.
from ctypes import CDLL, create_string_buffer
libc = CDLL("libc.so.6")
# Some test data, NUL-terminated so we can safely pass it to a str function.
mydata = bytearray([65, 66, 67, 68, 69, 70, 0])
print(mydata)
# Convert the Python bytearray to a C array of char
p = create_string_buffer(bytes(mydata), len(mydata))
#Shuffle the bytes before the NUL terminator byte, in-place.
libc.strfry(p)
# Convert the modified C array back to a Python bytearray
newdata = bytearray(p.raw)
print(newdata)
typical output
bytearray(b'ABCDEF\x00')
bytearray(b'BFDACE\x00')

Reading array returned by c function in ctypes

I've got some C code, which I'm trying to access from ctypes in python. A particular function looks something like this:
float *foo(void) {
static float bar[2];
// Populate bar
return bar;
}
I know this isn't an ideal way to write C, but it does the job in this case. I'm struggling to write the python to get the two floats contained in the response. I'm fine with return values that are single variables, but I can't work out from the ctypes docs how to handle a pointer to an array.
Any ideas?
Specify restypes as [POINTER][1](c_float):
import ctypes
libfoo = ctypes.cdll.LoadLibrary('./foo.so')
foo = libfoo.foo
foo.argtypes = ()
foo.restype = ctypes.POINTER(ctypes.c_float)
result = foo()
print(result[0], result[1])
Thanks to #falsetru, believe I found a somehow better solution, which takes into account the fact that the C function returns a pointer to two floats:
import ctypes
libfoo = ctypes.cdll.LoadLibrary('./foo.so')
foo = libfoo.foo
foo.argtypes = ()
foo.restype = ctypes.POINTER(ctypes.c_float * 2)
result = foo().contents
print('length of the returned list: ' + len(result))
print(result[0], result[1])

'str' object is not applicable

I have the following little function written in Python:
def encode(str):
out = ""
for i in str:
ret += str(ord(i.upper()) - 64)
return ret
Basically, what I want to do is get the number of the letter in the alphabat and concatenate it to the 'out' string. With this code I get a traceback at line 4: 'str' object is not applicable.
Could someone please explain me why it throws this error and how I can fix this? (Sorry if this was already asked once, I couldn't find it, probably also because I'm pretty new to Python and programming)
Never name your variable on the pre-defined built-in name.
In your code, str is not a built-in function. It's the variable you have used as parameter in your function.
Another problem is, you have declared out variable, and using ret which will give you error. Change out = "" to ret = "".
Don't call your variable str, you're shadowing the built-in function.
Also, you need to fix the naming of out/ret.
I personally would write this function as follows:
def encode(s):
return ''.join(str(ord(c.upper()) - 64) for c in s)
(I don't really follow what the str(ord(...)) is meant to be doing, so I've just copied it from your code.)
As the others have said, do not use str as a variable.
I suspect this is what you want though:
def encode(s):
return "".join(chr(ord(c.upper()) - 64) for c in s)
This is equivalent to:
def encode(s):
out = ""
for c in s:
ret += chr(ord(c.upper()) - 64)
return ret
You were looking for the chr() function, which converts a numerical ASCII/Unicode value into a 1-character string. Running str() would convert 5 to "5".
Also on a stylistic note, it's customary to do for c in s when iterating over a string, and for i in x when iterating over a sequence of integers.

ctype question char**

I'm trying to figure out why this works after lots and lots of messing about with
obo.librar_version is a c function which requires char ** as the input and does a strcpy
to passed in char.
from ctypes import *
_OBO_C_DLL = 'obo.dll'
STRING = c_char_p
OBO_VERSION = _stdcall_libraries[_OBO_C_DLL].OBO_VERSION
OBO_VERSION.restype = c_int
OBO_VERSION.argtypes = [POINTER(STRING)]
def library_version():
s = create_string_buffer('\000' * 32)
t = cast(s, c_char_p)
res = obo.library_version(byref(t))
if res != 0:
raise Error("OBO error %r" % res)
return t.value, s.raw, s.value
library_version()
The above code returns
('OBO Version 1.0.1', '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', '')
What I don't understand is why 's' does not have any value? Anyone have any ideas? Thx
When you cast s to c_char_p you store a new object in t, not a reference. So when you pass t to your function by reference, s doesn't get updated.
UPDATE:
You are indeed correct:
cast takes two parameters, a ctypes
object that is or can be converted to
a pointer of some kind, and a ctypes
pointer type. It returns an instance
of the second argument, which
references the same memory block as
the first argument.
In order to get a reference to your string buffer, you need to use the following for your cast:
t = cast(s, POINTER(c_char*33))
I have no idea why c_char_p doesn't create a reference where this does, but there you go.
Because library_version requires a char**, they don't want you to allocate the characters (as you're doing with create_string_buffer. Instead, they just want you to pass in a reference to a pointer so they can return the address of where to find the version string.
So all you need to do is allocate the pointer, and then pass in a reference to that pointer.
The following code should work, although I don't have obo.dll (or know of a suitable replacement) to test it.
from ctypes import *
_OBO_C_DLL = 'obo.dll'
STRING = c_char_p
_stdcall_libraries = dict()
_stdcall_libraries[_OBO_C_DLL] = WinDLL(_OBO_C_DLL)
OBO_VERSION = _stdcall_libraries[_OBO_C_DLL].OBO_VERSION
OBO_VERSION.restype = c_int
OBO_VERSION.argtypes = [POINTER(STRING)]
def library_version():
s_res = c_char_p()
res = OBO_VERSION(byref(s_res))
if res != 0:
raise Error("OBO error %r" % res)
return s_res.value
library_version()
[Edit]
I've gone a step further and written my own DLL that implements a possible implementation of OBO_VERSION that does not require an allocated character buffer, and is not subject to any memory leaks.
int OBO_VERSION(char **pp_version)
{
static char result[] = "Version 2.0";
*pp_version = result;
return 0; // success
}
As you can see, OBO_VERSION simply sets the value of *pp_version to a pointer to a null-terminated character array. This is likely how the real OBO_VERSION works. I've tested this against my originally suggested technique above, and it works as prescribed.

Categories

Resources