How would the conversion of this algorithm from C to Python

1
unsigned char CheckSum(unsigned char *uBuff, unsigned char uBuffLen){
  unsigned char i, uSum=0;
  for(i=0; i < uBuffLen; i++){
    uSum = uSum + uBuff[i];
  }
  uSum = (~uSum) + 1;
  return uSum;
}
    
asked by anonymous 07.09.2017 / 14:59

1 answer

2

The algorithm is straightforward in Python, with no change - however, the final value composition, which has to be binary, has to be taken care of. To begin, of course the buffer has to be an object of the type "bytes" or some other one that a byte at a time intervenes (bytearray, memoryview, array.array, etc ...). As in Python these objects already have the known length, the second parameter is not necessary.

The "for" could be done with 3 lines:

result = 0
for element in buffer:
    result += element

But - there you note that if it is just to sum all the elements of an iterable, the native function sum of Python already does this - that is, the t = 3 linash above are equivalent to:

result = sum (buffer)

From here comes the part where you have to know what is happening with the numbers - regardless of language. In its C function, the accumulator "uSum" is an unsigned Char, which means that it is a variable that only contains numbers from 0 to 255 (8 bits) - and when the sum exceeds this, the upper bits are simply discarded in machine architecture (in the CPU). The integers in Python, by default, have indeterminate length - so the sum function returns the total sum of bytes in a buffer. To discard the bits in positions greater than 8, and only have the value between 0 and 255, we have two options: place that number in a data structure that only accepts 8-bit numbers, discarding the rest - as in C, or , it uses the bit and bit-by-bit operator ( & in both Python and C), with a number that has only the 8 bits that are of interest in 1 - (this ensures that all others go to 0) . Or even the module operator ( % ) - remainder of division - since we only want the lowest bits.

The first option is actually a bit more complicated - because unlike C, where you should know what I'm doing and if you truncate or lose bits of your data is a problem of your own. (there may be warnings at compile time, but the code of your same function is valid), the Python watering path will give an error if you try to put a number greater than 255 in a single byte. The second option however is very simple, and we can even re-populate the direct mask in binary itself:

result = sum(buffer) & 0b11111111

The 2 add-on operator that worried you so much, is exactly the same in Python - the ~ unary. Only, as before, the number operated - that is, so far the whole function could be written as:

def checksum(buffer):
    result = ~(sum(buffer) & 0b11111111) + 1
    return result

Now - there is one last thing - which is precisely because the complement of 2 is done in a 1-byte variable with no signal in C: the result is always positive in C. In Python, again, with its numbers without limitation, result is negative. So to get that positive number back, the best way might be to drop that number into a bytes object created by the struct module of Python - and then read that byte. This is because the struct.pack method allows you to define exactly how to interpret the given number as bytes in memory - and then say that we want to import our number less than 8 bits with a signal - (using the code "b" as in the documentation ) - and read this byte later:

import struct

def checksum(buffer):
    result = sum(buffer) & 0b11111111
    result = ~result + 1
    result = struct.pack("b", result)[0]
    return result
    
08.09.2017 / 16:13