#include "absl/crc/internal/crc.h"
#include <cstdint>
#include "absl/base/internal/endian.h"
#include "absl/base/internal/raw_logging.h"
#include "absl/base/prefetch.h"
#include "absl/crc/internal/crc_internal.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace crc_internal {
namespace {
#if defined(__i386__) || defined(__x86_64__)
constexpr bool kNeedAlignedLoads = false;
#else
constexpr bool kNeedAlignedLoads = true;
#endif
constexpr int ZEROES_BASE_LG = 4; constexpr int ZEROES_BASE = (1 << ZEROES_BASE_LG);
constexpr uint32_t kCrc32cPoly = 0x82f63b78;
uint32_t ReverseBits(uint32_t bits) {
bits = (bits & 0xaaaaaaaau) >> 1 | (bits & 0x55555555u) << 1;
bits = (bits & 0xccccccccu) >> 2 | (bits & 0x33333333u) << 2;
bits = (bits & 0xf0f0f0f0u) >> 4 | (bits & 0x0f0f0f0fu) << 4;
return absl::gbswap_32(bits);
}
void PolyMultiply(uint32_t* val, uint32_t m, uint32_t poly) {
uint32_t l = *val;
uint32_t result = 0;
auto onebit = uint32_t{0x80000000u};
for (uint32_t one = onebit; one != 0; one >>= 1) {
if ((l & one) != 0) {
result ^= m;
}
if (m & 1) {
m = (m >> 1) ^ poly;
} else {
m >>= 1;
}
}
*val = result;
}
}
void CRCImpl::FillWordTable(uint32_t poly, uint32_t last, int word_size,
Uint32By256* t) {
for (int j = 0; j != word_size; j++) { t[j][0] = 0; for (int i = 128; i != 0; i >>= 1) { if (j == 0 && i == 128) {
t[j][i] = last; } else {
uint32_t pred;
if (i == 128) {
pred = t[j - 1][1];
} else {
pred = t[j][i << 1];
}
if (pred & 1) {
t[j][i] = (pred >> 1) ^ poly;
} else {
t[j][i] = pred >> 1;
}
}
}
for (int i = 2; i != 256; i <<= 1) {
for (int k = i + 1; k != (i << 1); k++) {
t[j][k] = t[j][i] ^ t[j][k - i];
}
}
}
}
int CRCImpl::FillZeroesTable(uint32_t poly, Uint32By256* t) {
uint32_t inc = 1;
inc <<= 31;
inc >>= 1;
for (int i = 0; i < 3; ++i) {
PolyMultiply(&inc, inc, poly);
}
int j = 0;
for (uint64_t inc_len = 1; inc_len != 0; inc_len <<= ZEROES_BASE_LG) {
uint32_t v = inc;
for (int a = 1; a != ZEROES_BASE; a++) {
t[0][j] = v;
PolyMultiply(&v, inc, poly);
j++;
}
inc = v;
}
ABSL_RAW_CHECK(j <= 256, "");
return j;
}
CRCImpl* CRCImpl::NewInternal() {
CRCImpl* result = TryNewCRC32AcceleratedX86ARMCombined();
if (result == nullptr) {
result = new CRC32();
}
result->InitTables();
return result;
}
void CRC32::InitTables() {
Uint32By256* t = new Uint32By256[4];
FillWordTable(kCrc32cPoly, kCrc32cPoly, 1, t);
for (int i = 0; i != 256; i++) {
this->table0_[i] = t[0][i];
}
uint32_t last = kCrc32cPoly;
const size_t size = 12;
for (size_t i = 0; i < size; ++i) {
last = (last >> 8) ^ this->table0_[last & 0xff];
}
FillWordTable(kCrc32cPoly, last, 4, t);
for (size_t b = 0; b < 4; ++b) {
for (int i = 0; i < 256; ++i) {
this->table_[b][i] = t[b][i];
}
}
int j = FillZeroesTable(kCrc32cPoly, t);
ABSL_RAW_CHECK(j <= static_cast<int>(ABSL_ARRAYSIZE(this->zeroes_)), "");
for (int i = 0; i < j; i++) {
this->zeroes_[i] = t[0][i];
}
delete[] t;
const uint32_t kCrc32cUnextendPoly =
ReverseBits(static_cast<uint32_t>((kCrc32cPoly << 1) ^ 1));
FillWordTable(kCrc32cUnextendPoly, kCrc32cUnextendPoly, 1, &reverse_table0_);
j = FillZeroesTable(kCrc32cUnextendPoly, &reverse_zeroes_);
ABSL_RAW_CHECK(j <= static_cast<int>(ABSL_ARRAYSIZE(this->reverse_zeroes_)),
"");
}
void CRC32::Extend(uint32_t* crc, const void* bytes, size_t length) const {
const uint8_t* p = static_cast<const uint8_t*>(bytes);
const uint8_t* e = p + length;
uint32_t l = *crc;
auto step_one_byte = [this, &p, &l]() {
int c = (l & 0xff) ^ *p++;
l = this->table0_[c] ^ (l >> 8);
};
if (kNeedAlignedLoads) {
const uint8_t* x = RoundUp<4>(p);
if (x <= e) {
while (p != x) {
step_one_byte();
}
}
}
const size_t kSwathSize = 16;
if (static_cast<size_t>(e - p) >= kSwathSize) {
uint32_t buf0 = absl::little_endian::Load32(p) ^ l;
uint32_t buf1 = absl::little_endian::Load32(p + 4);
uint32_t buf2 = absl::little_endian::Load32(p + 8);
uint32_t buf3 = absl::little_endian::Load32(p + 12);
p += kSwathSize;
const auto step_swath = [this](uint32_t crc_in, const std::uint8_t* ptr) {
return absl::little_endian::Load32(ptr) ^
this->table_[3][crc_in & 0xff] ^
this->table_[2][(crc_in >> 8) & 0xff] ^
this->table_[1][(crc_in >> 16) & 0xff] ^
this->table_[0][crc_in >> 24];
};
const auto step_stride = [&]() {
buf0 = step_swath(buf0, p);
buf1 = step_swath(buf1, p + 4);
buf2 = step_swath(buf2, p + 8);
buf3 = step_swath(buf3, p + 12);
p += 16;
};
while ((e - p) > kPrefetchHorizon) {
PrefetchToLocalCacheNta(
reinterpret_cast<const void*>(p + kPrefetchHorizon));
step_stride();
step_stride();
step_stride();
step_stride();
}
while (static_cast<size_t>(e - p) >= kSwathSize) {
step_stride();
}
while (static_cast<size_t>(e - p) >= 4) {
buf0 = step_swath(buf0, p);
uint32_t tmp = buf0;
buf0 = buf1;
buf1 = buf2;
buf2 = buf3;
buf3 = tmp;
p += 4;
}
auto combine_one_word = [this](uint32_t crc_in, uint32_t w) {
w ^= crc_in;
for (size_t i = 0; i < 4; ++i) {
w = (w >> 8) ^ this->table0_[w & 0xff];
}
return w;
};
l = combine_one_word(0, buf0);
l = combine_one_word(l, buf1);
l = combine_one_word(l, buf2);
l = combine_one_word(l, buf3);
}
while (p != e) {
step_one_byte();
}
*crc = l;
}
void CRC32::ExtendByZeroesImpl(uint32_t* crc, size_t length,
const uint32_t zeroes_table[256],
const uint32_t poly_table[256]) {
if (length != 0) {
uint32_t l = *crc;
for (int i = 0; length != 0;
i += ZEROES_BASE - 1, length >>= ZEROES_BASE_LG) {
int c = length & (ZEROES_BASE - 1); if (c != 0) { uint64_t m = zeroes_table[c + i - 1];
m <<= 1;
uint64_t m2 = m << 1;
uint64_t mtab[4] = {0, m, m2, m2 ^ m};
uint64_t result = 0;
for (int x = 0; x < 32; x += 8) {
result ^= mtab[l & 3] ^ (mtab[(l >> 2) & 3] << 2) ^
(mtab[(l >> 4) & 3] << 4) ^ (mtab[(l >> 6) & 3] << 6);
l >>= 8;
result = (result >> 8) ^ poly_table[result & 0xff];
}
l = static_cast<uint32_t>(result);
}
}
*crc = l;
}
}
void CRC32::ExtendByZeroes(uint32_t* crc, size_t length) const {
return CRC32::ExtendByZeroesImpl(crc, length, zeroes_, table0_);
}
void CRC32::UnextendByZeroes(uint32_t* crc, size_t length) const {
*crc = ReverseBits(*crc);
ExtendByZeroesImpl(crc, length, reverse_zeroes_, reverse_table0_);
*crc = ReverseBits(*crc);
}
void CRC32::Scramble(uint32_t* crc) const {
constexpr int scramble_rotate = (32 / 2) + 1;
*crc = RotateRight<uint32_t>(static_cast<unsigned int>(*crc + kScrambleLo),
32, scramble_rotate) &
MaskOfLength<uint32_t>(32);
}
void CRC32::Unscramble(uint32_t* crc) const {
constexpr int scramble_rotate = (32 / 2) + 1;
uint64_t rotated = RotateRight<uint32_t>(static_cast<unsigned int>(*crc), 32,
32 - scramble_rotate);
*crc = (rotated - kScrambleLo) & MaskOfLength<uint32_t>(32);
}
CRC::~CRC() {}
CRC::CRC() {}
CRC* CRC::Crc32c() {
static CRC* singleton = CRCImpl::NewInternal();
return singleton;
}
} ABSL_NAMESPACE_END
}