Why does Python char binding to C fails in c function? - python

I need to use in Python C-coded translation of string to reverse polish notation and calculation of it.
For this I do next:
import ctypes
input_string = bytes(memoryview("2 + 3.2 + 4".encode()))
result = str(self.model.functions['equal'](input_string))
where equal function is in:
def init_functions(self):
equal = self.c_lib.equal
equal.restype = ctypes.c_double
equal.argtypes = [ctypes.c_char_p]
self.functions.update({'equal': equal})
Where function in C is:
double equal(char *input) {
stack S = parseInput(input);
stack polish = toReversePolishNotation(&S);
return calculate(&polish);
}
It does calculation perfectly fine in C (checked with tests and memory leaks), always delivering the right answer.
But when I use this function in Python, it occasionally delivers some wrong output.
Sometimes it is 0, sometimes it is some strange number, sometimes it is endless cycle.
For example:
C result
Some random Python result
Strictly the next after random result in Python
More of it, my beloved example is "39.9 - 52.0". In C it is always as stated. But Python passes this string to C as "39.9 - 52.9".
I tried to set input_string = '' in Python, tried to memset char* input by '\0', tried to use different implementations of input_string:
input_string = ctypes.c_char_p("2 + 3.2 + 4".encode('utf-8'))
input_string = bytes("2 + 3.2 + 4", encoding='utf-8')
Python never delivers right answer consequently. It always fails. I don't know, if I need to free input_string somehow or something else is wrong.

The C code expects a pointer to mutable memory (probably for some tokenization). For that you need to use create_string_buffer.
input_string = ctypes.create_string_buffer(b"2 + 3.2 + 4")
See the documentation and tutorial

Related

Foobar - Test cases not passing - Disorderly escape

0/10 test cases are passing.
Here is the challenge description:
(to keep formatting nice - i put the description in a paste bin)
Link to challenge description: https://pastebin.com/UQM4Hip9
Here is my trial code - PYTHON - (0/10 test cases passed)
from math import factorial
from collections import Counter
from fractions import gcd
def cycle_count(c, n):
cc=factorial(n)
for a, b in Counter(c).items():
cc//=(a**b)*factorial(b)
return cc
def cycle_partitions(n, i=1):
yield [n]
for i in range(i, n//2 + 1):
for p in cycle_partitions(n-i, i):
yield [i] + p
def solution(w, h, s):
grid=0
for cpw in cycle_partitions(w):
for cph in cycle_partitions(h):
m=cycle_count(cpw, w)*cycle_count(cph, h)
grid+=m*(s**sum([sum([gcd(i, j) for i in cpw]) for j in cph]))
return grid//(factorial(w)*factorial(h))
print(solution(2, 2, 2)) #Outputs 7
This code works in my python compiler on my computer but not on the foobar challenge??
Am I losing accuracy or something?
NOT HELPFUL
Just a guess: wrong types of the return values of the functions? str vs. int?
Improved Answer
First a more detailed explanation for the 'type of values' thing:
In python, values are typed also if you do not have to declare a type. E.g.:
>>> i = 7
>>> s = '7'
>>> print(i, s, type(i), type(s), i == s)
7 7 <class 'int'> <class 'str'> False
I do not know the exact tests that are applied to the code, but typically those involve some equality test with ==. If the type of the expected value and those value returned does not match, the equality fails. Perhaps Elegant ways to support equivalence ("equality") in Python classes might help.
The instruction to the challenge (see pastebin) also explicitly mentions the expected return types: the returned values must be string (see line 41 / 47).
In addtion, the instruction also states that one should 'write a function answer(w, h, s)' (line 6). The posted solution implements a function solution.
I was also stuck on this problem.
The error is because of data type mismatch i.e your function returns an integer but the question specifically asked for a string.
Wrapping the return statement in str() will solve the problem.
Your return type is an integer. Convert it to a string, and it will work.
return str(grid//(factorial(w)*factorial(h)))

How to rebuild the libmem_crc32_direct CRC function in python?

I like to rebuild the libmem_crc32_direct function in python.
I used the crcmod python package before. So I like to setup the crc generator by using it.
the c-code looks like:
uint32_t crc_process_chunk(uint8_t* data, uint32_t len) {
return ~libmem_crc32_direct(data, len, 0xFFFFFFFF);
}
my python code looks so far:
def bit_not(n, numbits=8):
return (1 << numbits) - 1 - n
def getCRC(imageBA):
crcGen = crcmod.mkCrcFun(0x104C11DB7, initCrc=0xFFFFFFFF)
val = crcGen(imageBA)
val = bit_not(val, 32)
return val
The returned value of the python code is not equal of the one in c. So I guess I mad some error.
Any ideas?
Doesn't (1 << numbits) == 0? If this is two's complement math it should work as bit_not could be return 0-1-n. However, this isn't needed, since there is an optional xorOut parameter for crcmod. I'm thinking that since the optional rev parameter for reversed (reflected) input and output defaults to true, it needs to be set to false. I think the call to create the crc generator should be:
crcGen = crcmod.mkCrcFun(0x104C11DB7, initCrc=0xFFFFFFF, rev=False, xorOut=0xFFFFFFFF)
B bit tricky because 64Bit arithmetic on PC vs 32Bit arithmetic on ARM STM32F4, but finally this solution works:
def libmem_crc32_direct_with_xor(im, startAddr, l):
fw = im[startAddr:startAddr+l]
crcGen = crcmod.Crc(0x104C11DB7, initCrc=0xFFFFFFFF, rev = False)
crcGen.update(fw)
return (~crcGen.crcValue ) & 0xFFFFFFFF # 32bit xor

Read a binary file using unpack in Python compared with a IDL method

I have a IDL procedure reading a binary file and I try to translate it into a Python routine.
The IDL code look like :
a = uint(0)
b = float(0)
c = float(0)
d = float(0)
e = float(0)
x=dblarr(nptx)
y=dblarr(npty)
z=dblarr(nptz)
openr,11,name_file_data,/f77_unformatted
readu,11,a
readu,11,b,c,d,e
readu,11,x
readu,11,y
readu,11,z
it works perfectly. So I'm writing the same thing in python but I can't find the same results (even the value of 'a' is different). Here is my code :
x=np.zeros(nptx,float)
y=np.zeros(npty,float)
z=np.zeros(nptz,float)
with open(name_file_data, "rb") as fb:
a, = struct.unpack("I", fb.read(4))
b,c,d,e = struct.unpack("ffff", fb.read(16))
x[:] = struct.unpack(str(nptx)+"d", fb.read(nptx*8))[:]
y[:] = struct.unpack(str(npty)+"d", fb.read(npty*8))[:]
z[:] = struct.unpack(str(nptz)+"d", fb.read(nptz*8))[:]
Hope it will help anyone to answer me.
Update : As suggested in the answers, I'm now trying the module "FortranFile", but I'm not sure I understood everything about its use.
from scipy.io import FortranFile
f=FortranFile(name_file_data, 'r')
a=f.read_record('H')
b=f.read_record('f','f','f','f')
However, instead of having an integer for 'a', I got : array([0, 0], dtype=uint16).
And I had this following error for 'b': Size obtained (1107201884) is not a multiple of the dtypes given (16)
According to a table of IDL data types, UINT(0) creates a 16 bit integer (i.e. two bytes). In the Python struct module, the I format character denotes a 4 byte integer, and H denotes an unsigned 16 bit integer.
Try changing the line that unpacks a to
a, = struct.unpack("H", fb.read(2))
Unfortunately, this probably won't fix the problem. You use the option /f77_unformatted with openr, which means the file contains more than just the raw bytes of the variables. (See the documentation of the OPENR command for more information about /f77_unformatted.)
You could try to use scipy.io.FortranFile to read the file, but there are no gaurantees that it will work. The binary layout of an unformatted Fortran file is compiler dependent.

unpacking pythons struct.pack in another language

I want to "unpack" OR de-serialize the formatted data that is outputed from python's struct.pack() function. The data is sent over the network to another platform that uses Java only.
The Python function that sends data over the network, uses this formater:
def formatOutputMsg_Array(self, mac, arr):
mac_bin = mac.encode("ascii");
mac_len = len(mac_bin);
arr_bin = array.array('d', arr).tobytes();
arr_len = len(arr_bin);
m = struct.pack('qqd%ss%ss' % (mac_len, arr_len), mac_len, arr_len, time.time(), mac_bin, arr_bin);
return m
Here are the docs for python's struct (refer to section 7.3.2.2. Format Characters):
https://docs.python.org/2/library/struct.html
1) The issue is what does 'qqd%ss%ss' mean ???
Does it mean -> long,long,double,char,char,[],char[],char,char[],char[]
2) why is modulo "%" used here with a tuple 'qqd%ss%ss' % (mac_len, arr_len) ?
The first argument to pack is the result of the expression 'qqd%ss%ss' % (mac_len, arr_len), where the two %s are replaced by the values of the given variables. Assuming mac_len == 8 and arr_len == 4, for example, the result is qqd8s4s. s preceded by a number simply means to copy the given bytes for that format into the result.

Python IP to integer conversion not working as expected

I am using the answer provided here to convert string IP to integer. But I am not getting expected output. Specifically, the method is
>>> ipstr = '1.2.3.4'
>>> parts = ipstr.split('.')
>>> (int(parts[0]) << 24) + (int(parts[1]) << 16) + \
(int(parts[2]) << 8) + int(parts[3])
but when I provide it the value 172.31.22.98 I get 2887718498 back. However, I expect to see value -1407248798 as provided by Google's Guava library https://google.github.io/guava/releases/20.0/api/docs/com/google/common/net/InetAddresses.html#fromInteger-int-
Also, I've verified that this service provides expected output but all of the answers provided by the aforementioned StackOverflow answer return 2887718498
Note that I cannot use any third party library. So I am pretty much limited to using a hand-written code (no imports)
A better way is to use the library method
>>> from socket import inet_aton
>>> int.from_bytes(inet_aton('172.31.22.98'), byteorder="big")
2887718498
This is still the same result you had above
Here is one way to view it as a signed int
>>> from ctypes import c_long
>>> c_long(2887718498)
c_long(-1407248798)
To do it without imports (but why? The above is all first party CPython)
>>> x = 2887718498
>>> if x >= (1<<31): x-= (1<<32)
>>> x
-1407248798
Found this post whilst trying to do the same as OP. Developed the below for my simple mind to understand and for someone to benefit from.
baseIP = '192.168.1.0'
baseIPFirstOctet = (int((baseIP).split('.')[0]))
baseIPSecondOctet = (int((baseIP).split('.')[1]))
baseIPThirdOctet = (int((baseIP).split('.')[2]))
baseIPFourthOctet = (int((baseIP).split('.')[3]))

Categories

Resources