Skip to main content

links_fs/
codec.rs

1//! Little-endian field helpers and CRC-32 used by every on-disk structure.
2//!
3//! All multi-byte integers are stored little-endian, so images are portable
4//! between machines (the doublets store uses native endianness, which is
5//! little-endian on every platform LFS targets).
6
7/// Reads a little-endian `u64` at `offset`.
8#[must_use]
9pub fn get_u64(bytes: &[u8], offset: usize) -> u64 {
10    let mut raw = [0u8; 8];
11    raw.copy_from_slice(&bytes[offset..offset + 8]);
12    u64::from_le_bytes(raw)
13}
14
15/// Writes a little-endian `u64` at `offset`.
16pub fn put_u64(bytes: &mut [u8], offset: usize, value: u64) {
17    bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
18}
19
20/// Reads a little-endian `u32` at `offset`.
21#[must_use]
22pub fn get_u32(bytes: &[u8], offset: usize) -> u32 {
23    let mut raw = [0u8; 4];
24    raw.copy_from_slice(&bytes[offset..offset + 4]);
25    u32::from_le_bytes(raw)
26}
27
28/// Writes a little-endian `u32` at `offset`.
29pub fn put_u32(bytes: &mut [u8], offset: usize, value: u32) {
30    bytes[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
31}
32
33/// Reads a little-endian `i64` at `offset`.
34#[must_use]
35pub fn get_i64(bytes: &[u8], offset: usize) -> i64 {
36    get_u64(bytes, offset) as i64
37}
38
39/// Writes a little-endian `i64` at `offset`.
40pub fn put_i64(bytes: &mut [u8], offset: usize, value: i64) {
41    put_u64(bytes, offset, value as u64);
42}
43
44const fn crc32_table() -> [u32; 256] {
45    let mut table = [0u32; 256];
46    let mut index = 0;
47    while index < 256 {
48        let mut crc = index as u32;
49        let mut bit = 0;
50        while bit < 8 {
51            crc = if crc & 1 == 1 {
52                (crc >> 1) ^ 0xEDB8_8320
53            } else {
54                crc >> 1
55            };
56            bit += 1;
57        }
58        table[index] = crc;
59        index += 1;
60    }
61    table
62}
63
64static CRC32_TABLE: [u32; 256] = crc32_table();
65
66/// CRC-32 (IEEE 802.3, the zlib/PNG polynomial).
67#[must_use]
68pub fn crc32(bytes: &[u8]) -> u32 {
69    crc32_update(0, bytes)
70}
71
72/// Continues a CRC-32 computation over another slice.
73#[must_use]
74pub fn crc32_update(crc: u32, bytes: &[u8]) -> u32 {
75    let mut value = !crc;
76    for &byte in bytes {
77        value = CRC32_TABLE[((value ^ u32::from(byte)) & 0xFF) as usize] ^ (value >> 8);
78    }
79    !value
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn crc32_matches_reference_vector() {
88        assert_eq!(crc32(b"123456789"), 0xCBF4_3926);
89        assert_eq!(crc32_update(crc32(b"1234"), b"56789"), 0xCBF4_3926);
90    }
91
92    #[test]
93    fn integers_round_trip() {
94        let mut bytes = [0u8; 16];
95        put_u64(&mut bytes, 0, 0x0102_0304_0506_0708);
96        put_u32(&mut bytes, 8, 0xAABB_CCDD);
97        put_i64(&mut bytes, 8, -5);
98        assert_eq!(get_u64(&bytes, 0), 0x0102_0304_0506_0708);
99        assert_eq!(bytes[0], 0x08);
100        assert_eq!(get_i64(&bytes, 8), -5);
101        put_u32(&mut bytes, 12, 7);
102        assert_eq!(get_u32(&bytes, 12), 7);
103    }
104}