Ethernet packet checksum

计算以太网包首部中使用的校验和

算法: first summing all numbers(every 16-bit, might with padding)
and adding the carry (or carries) to the result,
then compute ones' complement:
– 发送端或接收端计算发送端的校验和时, 需要首先把校验和字段设置为 0
– 然后累积每个 16-bit
– 然后将最终结果"折叠"成 16 bits
– 然后 ones' complement (算法: 所有按位取反)
– 最后只要低 16 bits, 最后结果是网络字节顺序

/**
 * 计算以太网包首部中使用的校验和
 * @param data data to compute, in network byte order
 * @param bytes data size in bytes
 * @return in network byte order
 */
uint16_t ComputeChecksum(uint16_t const* const data, size_t const bytes)
{
    // initialize sum to zero
    unsigned long sum = 0;
    // n to keep current left bytes
    size_t n;
    // data index for each 16-bit
    long dataIndex;
    // accumulate sum
    for (n = bytes, dataIndex = -1; n > 1; n -= 2) {
        sum += data[++dataIndex];
    }
    /*
     * if any bytes left, pad the bytes with 0x00 and add
     * 最后一个 16-bit, 网络字节序:
     * 最后一个字节(位于最后一个 16-bit高位): MSB, then 0x00,
     * 而 MSB 在低地址, 因此计算使用最后一个 16-bit 是 0x00 then 最后一个字节:
     * - 即最后一个字节,
     * - => ((data[++dataIndex] >> 8) & 0xff) ... but data[...] bad
     * - => ((data[++dataIndex]) & htons(0xff00) ... but data[...] bad
     * - => uint8_t const msb = (reinterpret_cast<uint8_t const* const>(data))[
     *          long(bytes) - 1];
     */
    if (n > 0) {
        uint8_t const msb = (reinterpret_cast<uint8_t const* const>(data))[
            long(bytes) - 1];
        sum += msb;
    }
    // fold sum to 16 bits: add carrier to result
    while (sum >> 16) {
        sum = (sum >> 16) + (sum & 0xffff);
    }
    /*
     * ones' complement:
     * The ones' complement of a binary number is defined as the value
     * obtained by inverting all the bits in the binary representation of the
     * number (swapping 0s for 1s and vice versa)
     */
    return uint16_t(~sum);
}