diff options
author | Joakim Tjernlund <Joakim.Tjernlund@transmode.se> | 2008-11-05 19:02:49 +0100 |
---|---|---|
committer | David Lamparter <equinox@diac24.net> | 2010-02-04 01:18:21 +0100 |
commit | 69dd8e5692bc22967b5508f763be7d056fffa9de (patch) | |
tree | d8412964a82c86bc03766186bdaa5af2fad47eca | |
parent | dca6c883ea6219460efbe3dadde4b8b9cb12c845 (diff) | |
download | quagga-69dd8e5692bc22967b5508f763be7d056fffa9de.tar.bz2 quagga-69dd8e5692bc22967b5508f763be7d056fffa9de.tar.xz |
lib: Optimize Fletcher and Internet checksums
Did some simple optimizations to reduce the number
of instructions in the hot path for both fletcher_checksum()
and in_cksum().
-rw-r--r-- | lib/checksum.c | 53 |
1 files changed, 19 insertions, 34 deletions
diff --git a/lib/checksum.c b/lib/checksum.c index 3ddde815..af4f2550 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -14,27 +14,20 @@ in_cksum(void *parg, int nbytes) { u_short *ptr = parg; register long sum; /* assumes long == 32 bits */ - u_short oddbyte; register u_short answer; /* assumes u_short == 16 bits */ - + register int count; /* * Our algorithm is simple, using a 32-bit accumulator (sum), * we add sequential 16-bit words to it, and at the end, fold back * all the carry bits from the top 16 bits into the lower 16 bits. */ - sum = 0; - while (nbytes > 1) { - sum += *ptr++; - nbytes -= 2; - } - - /* mop up an odd byte, if necessary */ - if (nbytes == 1) { - oddbyte = 0; /* make sure top half is zero */ - *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ - sum += oddbyte; - } + count = nbytes >> 1; /* div by 2 */ + for(ptr--; count; --count) + sum += *++ptr; + + if (nbytes & 1) /* Odd */ + sum += *(u_char *)(++ptr); /* one byte only */ /* * Add back carry outs from top 16 bits to low 16 bits. @@ -56,11 +49,8 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) { u_int8_t *p; int x, y, c0, c1; - u_int16_t checksum; u_int16_t *csum; size_t partial_len, i, left = len; - - checksum = 0; assert (offset < len); @@ -68,47 +58,42 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) * Zero the csum in the packet. */ csum = (u_int16_t *) (buffer + offset); - *(csum) = 0; + *csum = 0; - p = buffer; + p = buffer - 1; c0 = 0; c1 = 0; while (left != 0) { partial_len = MIN(left, MODX); + left -= partial_len; - for (i = 0; i < partial_len; i++) + do { - c0 = c0 + *(p++); + c0 = c0 + *(++p); c1 += c0; - } + } while (--partial_len); c0 = c0 % 255; c1 = c1 % 255; - - left -= partial_len; } - + /* The cast is important, to ensure the mod is taken as a signed value. */ x = (int)((len - offset - 1) * c0 - c1) % 255; if (x <= 0) x += 255; y = 510 - c0 - x; - if (y > 255) + if (y > 255) y -= 255; - + /* * Now we write this to the packet. * We could skip this step too, since the checksum returned would * be stored into the checksum field by the caller. + * Checksum is always big endian. */ - buffer[offset] = x; - buffer[offset + 1] = y; - - /* Take care of the endian issue */ - checksum = htons((x << 8) | (y & 0xFF)); - - return checksum; + *csum = htons((x << 8) | (y & 0xFF)); + return *csum; } |