/* * Common code for checksum implementations * * Copyright (c) 2020, Arm Limited. * SPDX-License-Identifier: MIT */ #ifndef CHKSUM_COMMON_H #define CHKSUM_COMMON_H #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ #error Only little endian supported #endif #include #include #include #include /* Assertions must be explicitly enabled */ #if WANT_ASSERT #undef NDEBUG #include #define Assert(exp) assert(exp) #else #define Assert(exp) (void) (exp) #endif #ifdef __GNUC__ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define may_alias __attribute__((__may_alias__)) #define always_inline __attribute__((always_inline)) #ifdef __clang__ #define no_unroll_loops #else #define no_unroll_loops __attribute__((optimize("no-unroll-loops"))) #endif #define bswap16(x) __builtin_bswap16((x)) #else #define likely(x) (x) #define unlikely(x) (x) #define may_alias #define always_inline #define no_unroll_loops #define bswap16(x) ((uint8_t)((x) >> 8) | ((uint8_t)(x) << 8)) #endif #define ALL_ONES ~UINT64_C(0) static inline uint64_t load64(const void *ptr) { /* GCC will optimise this to a normal load instruction */ uint64_t v; memcpy(&v, ptr, sizeof v); return v; } static inline uint32_t load32(const void *ptr) { /* GCC will optimise this to a normal load instruction */ uint32_t v; memcpy(&v, ptr, sizeof v); return v; } static inline uint16_t load16(const void *ptr) { /* GCC will optimise this to a normal load instruction */ uint16_t v; memcpy(&v, ptr, sizeof v); return v; } /* slurp_small() is for small buffers, don't waste cycles on alignment */ no_unroll_loops always_inline static inline uint64_t slurp_small(const void *ptr, uint32_t nbytes) { const unsigned char *cptr = ptr; uint64_t sum = 0; while (nbytes >= 4) { sum += load32(cptr); cptr += 4; nbytes -= 4; } if (nbytes & 2) { sum += load16(cptr); cptr += 2; } if (nbytes & 1) { sum += (uint8_t) *cptr; } return sum; } static inline const void * align_ptr(const void *ptr, size_t bytes) { return (void *) ((uintptr_t) ptr & -(uintptr_t) bytes); } always_inline static inline uint16_t fold_and_swap(uint64_t sum, bool swap) { /* Fold 64-bit sum to 32 bits */ sum = (sum & 0xffffffff) + (sum >> 32); sum = (sum & 0xffffffff) + (sum >> 32); Assert(sum == (uint32_t) sum); /* Fold 32-bit sum to 16 bits */ sum = (sum & 0xffff) + (sum >> 16); sum = (sum & 0xffff) + (sum >> 16); Assert(sum == (uint16_t) sum); if (unlikely(swap)) /* Odd base pointer is unexpected */ { sum = bswap16(sum); } return (uint16_t) sum; } #endif