Files
86Box/src/crcspeed/crc64speed.c
2023-05-29 06:21:17 -04:00

277 lines
10 KiB
C

/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>
* 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 <stdio.h>
#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