I see a number of things that may allow you to improve your program.
Show all required #include
s
To get size_t
, malloc
and memcpy
we need the following includes:
#include <cstdint>
#include <cstdlib>
#include <cstring>
Because they are required by the crc
routine, they are important to show.
Use const
where practical
The crc
function does not need to alter the passed message, so that parameter should be declared const
.
Avoid using a leading underscore for items in global namespace
As you can read in this answer, global names that begin with an underscore are "reserved to the implementation;" that is, they are for your compiler rather than for you.
Don't leak memory
The code calls malloc
but never calls free
so it leaks memory. That's not good, but in this case it's easily fixed because the copy isn't needed anyway.
Use better names
The crc_seed
is not a bad name because it suggests the meaning of the variable within the context of the function, but _crc
as the input variable is a terrible name because that's not at all what it represents. I'd call it message
or maybe msg
instead.
Use appropriate data types
There's not much reason to have _crc
passed in as a void *
. It allows the user to pass in just about anything without an explicit cast, but I'd suggest that it would be better to have it be const uint8_t *
instead and have the caller cast if necessary. To me, it makes the use of the variable more clear. Also, the use of ssize_t
is incorrect in this instance. Read this question for details on size_t
versus ssize_t
. Also note that in C++, these are actually in the std
namespace.
Document your code
A short comment in the code would be sufficient to make this code much more understandable to anyone reading it. In particular, the polynomial used and the fact that it processes memory from high to low (reverse from the usual) arre important points to note.
Don't introduce arbitrary restrictions
There is nothing in the signature of the crc
routine that would suggest to the user that it's limited to a 32-bit message, and nothing in the code that checks for or enforces that. In this case, I'd recommend simply removing that restriction, since it's simpler than enforcing a limit.
Don't create lots of spurious variables
The _crc_cpy
, crc_cpy_pointer
and _crc_u
variables are not necessary. What the code is attempting to do is to calculate an 8-bit CRC from high memory to low memory, so the logical way to do that is also the most straightforward:
/*
* calculates 8-bit CRC using polynomial x^8 + x^2 + x^1 + 1
* processing the bytes from the end of the message to the beginning
*/
std::uint8_t rev_crc(const std::uint8_t* msg, std::size_t msg_len, std::uint8_t crc_seed) {
constexpr uint8_t poly{0x07}; // represents x^8 (implicit) + x^2 + x^1 + x^0
for(msg += msg_len - 1; msg_len; --msg_len, --msg) {
crc_seed ^= *msg;
for(int i = 0; i < 8; i++) {
crc_seed = (crc_seed & 0x80) ? poly ^ (crc_seed << 1) : (crc_seed << 1);
}
}
return crc_seed;
}
Understand the engineering trade-off
This particular polynomial is not terrible, but it may be useful for you to consider alternatives. In particular, if the concern is in detecting 4-bit errors in a 32-bit message, there are other polynomials that perform better. See Koopman's CRC Zoo for more information about that, and how to interpret Hamming Distance and Hamming weights.