/* Copyright (c) 2014, Matt Stancliff * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Redis nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "crc64speed.h" /* If CRC64SPEED_DUAL is defined, we allow calls to * both _little and _big CRC. * By default, we only allow one endianness to be used * and the first call to either _init function will set the * lookup table endianness for the life of this module. * We don't enable dual lookups by default because * each 8x256 lookup table is 16k. */ #ifndef CRC64SPEED_DUAL static uint64_t crc64_table[8][256] = {{0}}; static void *crc64_table_little = NULL; static void *crc64_table_big = NULL; static const bool dual = false; #else static uint64_t crc64_table_little[8][256] = {{0}}; static uint64_t crc64_table_big[8][256] = {{0}}; static void *crc64_table = NULL; static const bool dual = true; #endif /* value of crc64_table[0][1], architecture dependent. */ #define LITTLE1 UINT64_C(0x7ad870c830358979) #define BIG1 UINT64_C(0x79893530c870d87a) /* Define CRC64SPEED_SAFE if you want runtime checks to stop * CRCs from being calculated by uninitialized tables (and also stop tables * from being initialized more than once). */ #ifdef CRC64SPEED_SAFE #define should_init(table, val) \ do { \ if ((table)[0][1] == (val)) \ return false; \ } while (0) #define check_init(table, val) \ do { \ if ((table)[0][1] != (val)) \ return false; \ } while (0) #else #define should_init(a, b) #define check_init(a, b) #endif #define POLY UINT64_C(0xad93d23594c935a9) /******************** BEGIN GENERATED PYCRC FUNCTIONS ********************/ /** * Generated on Sun Dec 21 14:14:07 2014, * by pycrc v0.8.2, https://www.tty1.net/pycrc/ * * LICENSE ON GENERATED CODE: * ========================== * As of version 0.6, pycrc is released under the terms of the MIT licence. * The code generated by pycrc is not considered a substantial portion of the * software, therefore the author of pycrc will not claim any copyright on * the generated code. * ========================== * * CRC configuration: * Width = 64 * Poly = 0xad93d23594c935a9 * XorIn = 0xffffffffffffffff * ReflectIn = True * XorOut = 0x0000000000000000 * ReflectOut = True * Algorithm = bit-by-bit-fast * * Modifications after generation (by matt): * - included finalize step in-line with update for single-call generation * - re-worked some inner variable architectures * - adjusted function parameters to match expected prototypes. *****************************************************************************/ /** * Reflect all bits of a \a data word of \a data_len bytes. * * \param data The data word to be reflected. * \param data_len The width of \a data expressed in number of bits. * \return The reflected data. *****************************************************************************/ static inline uint_fast64_t crc_reflect(uint_fast64_t data, size_t data_len) { uint_fast64_t ret = data & 0x01; for (size_t i = 1; i < data_len; i++) { data >>= 1; ret = (ret << 1) | (data & 0x01); } return ret; } /** * Update the crc value with new data. * * \param crc The current crc value. * \param data Pointer to a buffer of \a data_len bytes. * \param data_len Number of bytes in the \a data buffer. * \return The updated crc value. ******************************************************************************/ uint64_t crc64(uint_fast64_t crc, const void *in_data, const uint64_t len) { const uint8_t *data = (uint8_t *) in_data; bool bit; for (uint64_t offset = 0; offset < len; offset++) { uint8_t c = data[offset]; for (uint_fast8_t i = 0x01; i & 0xff; i <<= 1) { bit = crc & 0x8000000000000000; if (c & i) { bit = !bit; } crc <<= 1; if (bit) { crc ^= POLY; } } crc &= 0xffffffffffffffff; } crc = crc & 0xffffffffffffffff; return crc_reflect(crc, 64) ^ 0x0000000000000000; } /******************** END GENERATED PYCRC FUNCTIONS ********************/ /* Only for testing; doesn't support DUAL */ uint64_t crc64_lookup(uint64_t crc, const void *in_data, const uint64_t len) { const uint8_t *data = (uint8_t *) in_data; for (size_t i = 0; i < len; i++) { crc = crc64_table[0][(uint8_t)crc ^ data[i]] ^ (crc >> 8); } return crc; } /* Returns false if CRC64SPEED_SAFE and table already initialized. */ bool crc64speed_init(void) { #ifndef CRC64SPEED_DUAL should_init(crc64_table, LITTLE1); #else should_init(crc64_table_little, LITTLE1); #endif crcspeed64little_init(crc64, dual ? (uint64_t (*)[256]) crc64_table_little : crc64_table); return true; } /* Returns false if CRC64SPEED_SAFE and table already initialized. */ bool crc64speed_init_big(void) { #ifndef CRC64SPEED_DUAL should_init(crc64_table, BIG1); #else should_init(crc64_table_big, BIG1); #endif crcspeed64big_init(crc64, dual ? (uint64_t (*)[256]) crc64_table_big : crc64_table); return true; } uint64_t crc64speed(uint64_t crc, const void *s, const uint64_t l) { /* Quickly check if CRC table is initialized to little endian correctly. */ #ifndef CRC64SPEED_DUAL check_init(crc64_table, LITTLE1); #else check_init(crc64_table_little, LITTLE1); #endif return crcspeed64little(dual ? (uint64_t (*)[256]) crc64_table_little : crc64_table, crc, (void *)s, l); } uint64_t crc64speed_big(uint64_t crc, const void *s, const uint64_t l) { /* Quickly check if CRC table is initialized to big endian correctly. */ #ifndef CRC64SPEED_DUAL check_init(crc64_table, BIG1); #else check_init(crc64_table_big, BIG1); #endif return crcspeed64big(dual ? (uint64_t (*)[256]) crc64_table_big : crc64_table, crc, (void *)s, l); } bool crc64speed_init_native(void) { const uint64_t n = 1; return *(char *)&n ? crc64speed_init() : crc64speed_init_big(); } /* Iterate over table to fully load it into a cache near the CPU. */ void crc64speed_cache_table(void) { uint64_t m; for (int i = 0; i < 8; ++i) { for (int j = 0; j < 256; ++j) { #ifndef CRC64SPEED_DUAL m = crc64_table[i][j]; #else m = crc64_table_little[i][j]; m += crc64_table_big[i][j]; #endif ++m; } } } /* If you are on a platform where endianness can change at runtime, this * will break unless you compile with CRC64SPEED_DUAL and manually run * _init() and _init_big() instead of using _init_native() */ uint64_t crc64speed_native(uint64_t crc, const void *s, const uint64_t l) { const uint64_t n = 1; return *(char *)&n ? crc64speed(crc, s, l) : crc64speed_big(crc, s, l); } /* Test main */ #if defined(REDIS_TEST) || defined(REDIS_TEST_MAIN) #include #define UNUSED(x) (void)(x) int crc64Test(int argc, char *argv[]) { UNUSED(argc); UNUSED(argv); crc64speed_init(); printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n", (uint64_t)crc64(0, "123456789", 9)); printf("[lookupt]: e9c6d914c4b8d9ca == %016" PRIx64 "\n", (uint64_t)crc64_lookup(0, "123456789", 9)); printf("[64speed]: e9c6d914c4b8d9ca == %016" PRIx64 "\n", (uint64_t)crc64speed(0, "123456789", 9)); char li[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed " "do eiusmod tempor incididunt ut labore et dolore magna " "aliqua. Ut enim ad minim veniam, quis nostrud exercitation " "ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis " "aute irure dolor in reprehenderit in voluptate velit esse " "cillum dolore eu fugiat nulla pariatur. Excepteur sint " "occaecat cupidatat non proident, sunt in culpa qui officia " "deserunt mollit anim id est laborum."; printf("[calcula]: c7794709e69683b3 == %016" PRIx64 "\n", (uint64_t)crc64(0, li, sizeof(li))); printf("[lookupt]: c7794709e69683b3 == %016" PRIx64 "\n", (uint64_t)crc64_lookup(0, li, sizeof(li))); printf("[64speed]: c7794709e69683b3 == %016" PRIx64 "\n", (uint64_t)crc64speed(0, li, sizeof(li))); return 0; } #endif #ifdef REDIS_TEST_MAIN int main(int argc, char *argv[]) { return crc64Test(argc, argv); } #endif