3

I have a unsigned char array of which I'd like to calculate the CRC32 checksum.

The CRC32 function also expects a unsigned char pointer, however, it interprets the array as an ASCII array.

This is the CRC function:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

unsigned int crc32(unsigned char *message) 
{
   int i, j;
   unsigned int byte, crc, mask;

   i = 0;
   crc = 0xFFFFFFFF;
   while (message[i] != 0) {
      byte = message[i];            // Get next byte.
      crc = crc ^ byte;
      for (j = 7; j >= 0; j--) {    // Do eight times.
         mask = -(crc & 1);
         crc = (crc >> 1) ^ (0xEDB88320 & mask);
      }
      i = i + 1;
   }
   return ~crc;
}

int main(int argc, char **argv)
{
    unsigned char *arr;
    if ((arr = malloc(64)) == NULL) {
        perror("Could not allocate memory");
        exit(EXIT_FAILURE);
    }
    char str[] = "47d46d17e759a1dec810758c08004510002127d90000401152e4c0a8b21fc0a8b2255b9b5b9c000db20caabbccddee00000000000000000000000000";
    memcpy(arr, str, strlen(str));
    // ...
    unsigned int crc = crc32(arr);
    printf("CRC: 0x%x\n", crc); // 0xB6BA014A instead of 0xBF6B57A2

    return 0;
}

Now, I'd like to calculate the CRC32 but the unsigned char array has to be interpreted as an hex array.

F.ex., this is the result of the calculated CRC:
Input:
"47d46d17e759a1dec810758c08004510002127d90000401152e4c0a8b21fc0a8b2255b9b5b9c000db20caabbccddee00000000000000000000000000"

  • as ASCII: 0xB6BA014A (this is what I usually get because it's interpreted as ASCII)
  • as Hex.: 0xBF6B57A2 (this is the checksum I want)
12
  • 1
    It sounds like you are reading your input as a string rather than converting each pair of characters to the number the hex value represents. Hard to say without a minimal reproducible example, but it seems like you're just passing the wrong data to the function. Commented Dec 1, 2018 at 19:25
  • What you want to do is convert the hexadecimal numeral in str to data in a new array of unsigned char, which you can then pass to crc32. Commented Dec 1, 2018 at 19:37
  • Ok. Then my question is how to achieve that in a swift way. Commented Dec 1, 2018 at 19:39
  • Create a new array of the required size. Examine the characters in str. You may wish to examine them in pairs. For each character, prepare the four bits it represents when interpreted as a hexadecimal digit. Put those bits into str. (If you do that two characters at a time, you will be putting eight bits into the new array at a time, which is generally more convenient.) If you do it this way, you will need to modify crc32 to take the length as a separate parameter instead of deducing it from a null byte (since a null byte is valid data). Commented Dec 1, 2018 at 19:42
  • 1
    Alternately, you can do the hexadecimal conversion inside crc32: Examine the characters in message two at a time. For each character, prepare the four bits it represents as a hexadecimal digit. Put the two four-bit values together to make one eight-bit value, and use that in the CRC. Commented Dec 1, 2018 at 19:43

1 Answer 1

2

How to interpret an unsigned char array as hex array?

  • Convert each pair of hexadecimal characters in the string to a byte value. Code below converts via a compound literal to form a 3 byte string, followed by a call to strtoul().

    //                    v----------------------------------v _compound literal_
    arr2[i / 2] = strtoul((char[3]) {str[i], str[i + 1], '\0'}, 0, 16);
    

    More advanced code would test for the unexpected presence of non-hexadecimal characters or an odd/zero length.


CRC calculation changes needed

  • Change CRC calculation to a length based one rather than a string one.

    // unsigned int crc32(const char *)
    unsigned int crc32(const void *m, size_t len)
    

    Although not coded below, consider uint32_t instead of unsigned int in crc32() for correct operation when unsigned is not 32-bit.


Altogether

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

unsigned int crc32(const void *m, size_t len) {
  const unsigned char *message = m;
  size_t i;
  int j;
  unsigned int byte, crc, mask;

  i = 0;
  crc = 0xFFFFFFFF;
  //while (message[i] != 0) {
  while (i < len) {
    byte = message[i];            // Get next byte.
    crc = crc ^ byte;
    for (j = 7; j >= 0; j--) {    // Do eight times.
      mask = -(crc & 1);
      crc = (crc >> 1) ^ (0xEDB88320 & mask);
    }
    i = i + 1;
  }
  return ~crc;
}

Sample usage

int main() {
  char str[] =
      "47d46d17e759a1dec810758c08004510002127d90000401152e4c0a8b21fc0a8b2255b9b5b9c000db20caabbccddee00000000000000000000000000";
  size_t len = strlen(str);
  unsigned int crc = crc32(str, len);
  printf("CRC: 0x%X\n", crc); // 0xB6BA014A instead of 0xBF6B57A2

  size_t len2 = (len + 1) / 2;
  unsigned char arr2[len2];
  for (size_t i = 0; i < len; i += 2) {
    arr2[i / 2] = strtoul((char[3]) {str[i], str[i + 1], '\0'}, 0, 16);
  }
  crc = crc32(arr2, len2);
  printf("CRC: 0x%X\n", crc); // 0xB6BA014A instead of 0xBF6B57A2

  return 0;
}

Output

CRC: 0xB6BA014A
CRC: 0xBF6B57A2

OP original code had undefined behavior in that it looked for a null character with while (message[i] != 0) {, yet memcpy(arr, str, strlen(str)); failed to provide one.

Sign up to request clarification or add additional context in comments.

5 Comments

Where did you hide the lookup table?
You must be clever, that's a nice answer. I appreciate that.
@Swordfish Unclear, what lookup table do you mean?
@Swordfish The for (j = 7; j >= 0; j--) { does that. Usual trade-off of speed vs code/data size.
@chux does that – ah.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.