What would the conversion of this algorithm from C to Python look like

Asked

Viewed 266 times

0

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;
}
  • Do you know Python? If so, what did you try? What was the difficulty?

  • Yes, I do. What I couldn’t do was understand the simple code in C, more specifically in the part "uSum = (~uSum) + 1" that I believe is a conversion to complement two.

1 answer

3

The algorithm is straightforward in Python, without any changes - however, the composition of the final value, which has to be binary, has to take some precautions. To start, of course, the buffer has to be an object of type "bytes" or some other that lasts one byte at a time (bytearray, memoryview, array.array, etc...). As in Python these objects already have the known length, the second parameter is not required.

The "for" could be done with 3 lines:

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

But - then you notice that if it’s just to add up all the elements of an eternal, native function sum Python already does this - that is, the 3 lines above are equivalent to:

result = sum(buffer)

Then comes the part where you have to know what’s going on with language-independent numbers. In its C function, the "uSum" accumulator is a unsigned Char, which means it is a variable that only holds numbers from 0 to 255 (8 bits) - and when the sum extrapolates this, the top bits are simply dropped on the machine architecture itself (on the CPU).

Python integers, by default, have indeterminate length - so the function sum returns the sum total of the bytes of a buffer. To discard bits at positions greater than 8, and only have the value between 0 and 255, we have two options: put this number in a data structure that only accepts 8 bit numbers, discarding the rest - as in C, or, use the binary operator "and" bit-a-bit (& in both Python and C) with a number that has only the 8 bits that matter in 1 - (this ensures that all others go to 0). Or the module operator (%) - split rest - since we only want the lowest bits.

The first option is actually a little more complicated - because unlike C, where you should know what you’re doing and if you truncate or lose bits of your data is your problem. (there may be warnings at compile time, but the code of its same function is valid), Python by rule 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 represent the direct mask in binary even:

result = sum(buffer) & 0b11111111

The add-on operator of 2 that so worried you, is exactly the same in Python - the ~ unary. Only that, as previously, the number operated - i.e., 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, the result is negative. So to get that positive number back, the best way can be to put that number inside a bytes object created by the module struct python - and then read that byte. That’s why the method struct.pack lets you define exactly how to interpret the given number as bytes in memory - and then we say we want to import our number less than 8 bits with signal - (using code "b" as in the documentation) - and read that byte later:

import struct

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.