1

I recently read some papers and code on crc computation (e.g. A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS and the implementation in Linux). As far as I understand, crc is done byte by byte starting on a certain address.

example from Linux Kernel:

u16 crc16(u16 crc, u8 const *buffer, size_t len)
{
    while (len--)
    crc = crc16_byte(crc, *buffer++);
    return crc;
}

Now I am asking myself is it possible to do this easily with a struct?

derwana
  • 115
  • 1
  • 8
  • 1
    You could cast the pointer-to-struct: `struct whatever x; crc16(crc, (u8 const *)&x, sizeof(struct whatever));` – 001 Nov 24 '16 at 15:02
  • 1
    You may encounter some trouble if you rely on the CRC to be the same cross-compiler (at least), (e.g. struct padding) – Déjà vu Nov 24 '16 at 15:04
  • @JohnnyMopp has given the answer, but there is a flaw with it: Padding. A structure can contain padding inserted by the compiler, the value of the padding is indeterminate but will be used when calculating the checksum. Explicitly setting the whole structure to zero with `memset` before initializing is recommended. Otherwise two otherwise identical structures might produce different checksums. – Some programmer dude Nov 24 '16 at 15:05

1 Answers1

5

Not in general if you want a sensible result, no.

This is because CRC is supposed to be computed over a sequence of "known" bytes, and a structure in memory is not a sequence of known bytes. There can be padding bytes for alignment purposes, which you do not necessarily know about or have control over, and of course various fields can have various sizes (as can the struct itself) on different systems/platforms.

If you first can serialize the struct to a sequence of bytes with a known and stable mapping, then of course you can apply CRC to that sequence. This is (subtly) hinted at by the buffer argument being const u8 *, i.e. pointer to constant byte, and not const void *.

If you don't care, and don't mind breaking if (for instance) you change the compiler settings and/or move the program to a different system, you could just use:

const struct mystruct s = { ... };
const u16 crc = crc16(0, (u8 *) &s, sizeof s);

But this will include any padding in the struct, so it's very dangerous.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • packing the struct may help in this situation little bit – j123b567 Nov 24 '16 at 15:40
  • @j123b567: Packing is not standard and does not guarantee anything. Not only can it be ignored, but it also does not define endianess, bit-fields, etc. Also on many architectures, packing will either slow down accesses or even require manual assembly of types with `sizeof > 1`. – too honest for this site Nov 24 '16 at 18:08
  • @Olaf You are of course right. But are you always writing a code, that needs to be compiled by every compiler and run on any processor? If you have known environment, packing of structs can be helpful and it can be easy trick to handle this situation. (e.g. writing configuration structure with CRC to EEPROM on microcontroller) – j123b567 Nov 24 '16 at 18:57
  • @j123b567: We have no idea how that is to be used. CRCs are typically used for data communication on smaller devices (otherwise one should think about signatures/hashes) and to a host computer. On such devices ABIs/toolchains can change during their lifetime of often 20+ years. Even let that apart: why write non-portable code without need? Leave optimisations to the compiler. – too honest for this site Nov 24 '16 at 19:08
  • @j123b567: As a sidenote: I write embedded software and there is hardly reason to pack structures. Instaedserialise member-wise with bitshifts. That typically is faster, because all members are be accessed with single loads instead of byte-wise access for unaligned members. This can be faster even **iff** the CPU supports unaligned accesses. (on ARM an unaligned 32 bit access is up to 3 accesses instead 1). – too honest for this site Nov 24 '16 at 19:14
  • Doing a CRC over the padding will be useful only if the entire structure, including the padding, is initialized first with a `memset(thing, 0, sizeof(thing))`, or allocated using `calloc()` (which fills the memory with zeros). Otherwise different structures with identical content will have different CRCs since the padding bytes will be whatever was left there in memory. – Mark Adler Nov 25 '16 at 05:56