mirror of
https://github.com/google/pebble.git
synced 2025-05-30 23:13:11 +00:00
128 lines
4.6 KiB
C
128 lines
4.6 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "util/legacy_checksum.h"
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
// Software implementation of the legacy checksum. This emulates the behaviour
|
|
// of the CRC peripheral in the STM32F2/F4 series MCUs and the bugs in the
|
|
// legacy CRC driver implementation. While the raw throughput of the hardware
|
|
// CRC peripheral is greater, it is not reentrant and so a mutex is required
|
|
// to prevent concurrent access to the peripheral by multiple tasks. The
|
|
// overhead of locking and unlocking the mutex, while not yet benchmarked, is
|
|
// potentially significant enough to cancel out the increased throughput of
|
|
// the hardware acceleration. There is also no evidence (for or against) that
|
|
// checksums are a performance bottleneck in the first place. Given the added
|
|
// complexity of the hardware-accelerated implementation, and the dubious
|
|
// performance improvement of using it, hardware acceleration of checksums is a
|
|
// case of premature optimization.
|
|
//
|
|
// That being said, the API for the legacy checksum is fully capable of
|
|
// supporting a non-reentrant implementation. Simply acquire the mutex in
|
|
// legacy_defective_checksum_init and release it in
|
|
// legacy_defective_checksum_finish.
|
|
|
|
// The implementation is based on a nybble-wide table driven CRC.
|
|
// The CRC implementation (but not the checksum based on it) has the
|
|
// model parameters:
|
|
// Width: 32
|
|
// Poly: 04C11DB7
|
|
// Init: FFFFFFFF
|
|
// RefIn: False
|
|
// RefOut: False
|
|
// XorOut: 00000000
|
|
//
|
|
// The CRC lookup table was generated by legacy_checksum_crc_table.py
|
|
|
|
static const uint32_t s_lookup_table[] = {
|
|
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
|
|
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
|
|
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
|
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
|
|
};
|
|
|
|
static uint32_t prv_crc_byte(uint32_t crc, uint8_t input) {
|
|
crc = (crc << 4) ^ s_lookup_table[((crc >> 28) ^ (input >> 4)) & 0x0f];
|
|
crc = (crc << 4) ^ s_lookup_table[((crc >> 28) ^ (input >> 0)) & 0x0f];
|
|
return crc;
|
|
}
|
|
|
|
void legacy_defective_checksum_init(LegacyChecksum *checksum) {
|
|
*checksum = (LegacyChecksum) {
|
|
.reg = 0xffffffff,
|
|
};
|
|
}
|
|
|
|
void legacy_defective_checksum_update(
|
|
LegacyChecksum * restrict checksum,
|
|
const void * restrict data, size_t length) {
|
|
const char * restrict data_bytes = data;
|
|
uint32_t * restrict reg = &checksum->reg;
|
|
uint8_t * restrict accumulator = checksum->accumulator;
|
|
uint8_t * restrict accumulated_length = &checksum->accumulated_length;
|
|
|
|
if (*accumulated_length) {
|
|
for (; *accumulated_length < 3 && length; length--) {
|
|
accumulator[(*accumulated_length)++] = *data_bytes++;
|
|
}
|
|
|
|
if (*accumulated_length == 3 && length) {
|
|
*reg = prv_crc_byte(*reg, *data_bytes++);
|
|
length--;
|
|
*reg = prv_crc_byte(*reg, accumulator[2]);
|
|
*reg = prv_crc_byte(*reg, accumulator[1]);
|
|
*reg = prv_crc_byte(*reg, accumulator[0]);
|
|
*accumulated_length = 0;
|
|
}
|
|
}
|
|
|
|
for (; length >= 4; length -= 4) {
|
|
*reg = prv_crc_byte(*reg, data_bytes[3]);
|
|
*reg = prv_crc_byte(*reg, data_bytes[2]);
|
|
*reg = prv_crc_byte(*reg, data_bytes[1]);
|
|
*reg = prv_crc_byte(*reg, data_bytes[0]);
|
|
data_bytes += 4;
|
|
}
|
|
|
|
for (; length; length--) {
|
|
accumulator[(*accumulated_length)++] = *data_bytes++;
|
|
}
|
|
}
|
|
|
|
uint32_t legacy_defective_checksum_finish(LegacyChecksum *checksum) {
|
|
if (checksum->accumulated_length) {
|
|
// CRC the final bytes forwards (reversed relative to the normal checksum)
|
|
// padded on the left(!) with null bytes.
|
|
for (int padding = 4 - checksum->accumulated_length; padding; padding--) {
|
|
checksum->reg = prv_crc_byte(checksum->reg, 0);
|
|
}
|
|
for (int i = 0; i < checksum->accumulated_length; ++i) {
|
|
checksum->reg = prv_crc_byte(checksum->reg, checksum->accumulator[i]);
|
|
}
|
|
}
|
|
|
|
return checksum->reg;
|
|
}
|
|
|
|
uint32_t legacy_defective_checksum_memory(const void * restrict data,
|
|
size_t length) {
|
|
LegacyChecksum checksum;
|
|
legacy_defective_checksum_init(&checksum);
|
|
legacy_defective_checksum_update(&checksum, data, length);
|
|
return legacy_defective_checksum_finish(&checksum);
|
|
}
|