density_rs/algorithms/cheetah/
cheetah.rs

1use crate::algorithms::PLAIN_FLAG;
2use crate::codec::codec::Codec;
3use crate::codec::decoder::Decoder;
4use crate::codec::quad_encoder::QuadEncoder;
5use crate::errors::decode_error::DecodeError;
6use crate::errors::encode_error::EncodeError;
7use crate::io::read_buffer::ReadBuffer;
8use crate::io::read_signature::ReadSignature;
9use crate::io::write_buffer::WriteBuffer;
10use crate::io::write_signature::WriteSignature;
11use crate::{BIT_SIZE_U16, BIT_SIZE_U32, BYTE_SIZE_U32};
12use std::slice::{from_raw_parts, from_raw_parts_mut};
13
14pub(crate) const CHEETAH_HASH_BITS: usize = BIT_SIZE_U16;
15pub(crate) const CHEETAH_HASH_MULTIPLIER: u32 = 0x9D6EF916;
16
17
18pub(crate) const FLAG_SIZE_BITS: u8 = 2;
19pub(crate) const MAP_A_FLAG: u64 = 0x1;
20pub(crate) const MAP_B_FLAG: u64 = 0x2;
21pub(crate) const PREDICTED_FLAG: u64 = 0x3;
22pub(crate) const DECODE_FLAG_MASK: u64 = 0x3;
23pub(crate) const DECODE_FLAG_MASK_BITS: u8 = 2;
24
25pub struct State {
26    pub(crate) last_hash: u16,
27    pub(crate) chunk_map: Vec<ChunkData>,
28    pub(crate) prediction_map: Vec<PredictionData>,
29}
30
31#[derive(Copy, Clone)]
32pub struct ChunkData {
33    pub(crate) chunk_a: u32,
34    pub(crate) chunk_b: u32,
35}
36
37#[derive(Copy, Clone)]
38pub struct PredictionData {
39    pub(crate) next: u32,
40}
41
42pub struct Cheetah {
43    pub state: State,
44}
45
46impl Cheetah {
47    pub fn new() -> Self {
48        Cheetah {
49            state: State {
50                last_hash: 0,
51                chunk_map: vec![ChunkData { chunk_a: 0, chunk_b: 0 }; 1 << CHEETAH_HASH_BITS],
52                prediction_map: vec![PredictionData { next: 0 }; 1 << CHEETAH_HASH_BITS],
53            },
54        }
55    }
56
57    pub fn encode(input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
58        let mut cheetah = Cheetah::new();
59        cheetah.encode(input, output)
60    }
61
62    pub fn decode(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
63        let mut cheetah = Cheetah::new();
64        cheetah.decode(input, output)
65    }
66
67    #[inline(always)]
68    fn decode_plain(&mut self, in_buffer: &mut ReadBuffer) -> (u16, u32) {
69        let quad = in_buffer.read_u32_le();
70        let hash = (quad.wrapping_mul(CHEETAH_HASH_MULTIPLIER) >> (BIT_SIZE_U32 - CHEETAH_HASH_BITS)) as u16;
71        let chunk_data = &mut self.state.chunk_map[hash as usize];
72        chunk_data.chunk_b = chunk_data.chunk_a;
73        chunk_data.chunk_a = quad;
74        self.state.prediction_map[self.state.last_hash as usize] = PredictionData { next: quad };
75        (hash, quad)
76    }
77
78    #[inline(always)]
79    fn decode_map_a(&mut self, in_buffer: &mut ReadBuffer) -> (u16, u32) {
80        let hash = in_buffer.read_u16_le();
81        let chunk_data = &mut self.state.chunk_map[hash as usize];
82        let quad = chunk_data.chunk_a;
83        self.state.prediction_map[self.state.last_hash as usize] = PredictionData { next: quad };
84        (hash, quad)
85    }
86
87    #[inline(always)]
88    fn decode_map_b(&mut self, in_buffer: &mut ReadBuffer) -> (u16, u32) {
89        let hash = in_buffer.read_u16_le();
90        let chunk_data = &mut self.state.chunk_map[hash as usize];
91        let quad = chunk_data.chunk_b;
92        chunk_data.chunk_b = chunk_data.chunk_a;
93        chunk_data.chunk_a = quad;
94        self.state.prediction_map[self.state.last_hash as usize] = PredictionData { next: quad };
95        (hash, quad)
96    }
97
98    #[inline(always)]
99    fn decode_predicted(&mut self) -> (u16, u32) {
100        let quad = self.state.prediction_map[self.state.last_hash as usize].next;
101        let hash = (quad.wrapping_mul(CHEETAH_HASH_MULTIPLIER) >> (BIT_SIZE_U32 - CHEETAH_HASH_BITS)) as u16;
102        (hash, quad)
103    }
104
105    #[unsafe(no_mangle)]
106    pub extern "C" fn cheetah_encode(input: *const u8, input_size: usize, output: *mut u8, output_size: usize) -> usize {
107        unsafe { Self::encode(from_raw_parts(input, input_size), from_raw_parts_mut(output, output_size)).unwrap_or(0) }
108    }
109
110    #[unsafe(no_mangle)]
111    pub extern "C" fn cheetah_decode(input: *const u8, input_size: usize, output: *mut u8, output_size: usize) -> usize {
112        unsafe { Self::decode(from_raw_parts(input, input_size), from_raw_parts_mut(output, output_size)).unwrap_or(0) }
113    }
114
115    #[unsafe(no_mangle)]
116    pub extern "C" fn cheetah_safe_encode_buffer_size(size: usize) -> usize {
117        Self::safe_encode_buffer_size(size)
118    }
119}
120
121impl QuadEncoder for Cheetah {
122    #[inline(always)]
123    fn encode_quad(&mut self, quad: u32, out_buffer: &mut WriteBuffer, signature: &mut WriteSignature) {
124        let hash_u16 = (quad.wrapping_mul(CHEETAH_HASH_MULTIPLIER) >> (BIT_SIZE_U32 - CHEETAH_HASH_BITS)) as u16;
125        let predicted_chunk = &mut self.state.prediction_map[self.state.last_hash as usize].next;
126        if *predicted_chunk != quad {
127            let chunk_data = &mut self.state.chunk_map[hash_u16 as usize];
128            let offer_a = &mut chunk_data.chunk_a;
129            if *offer_a != quad {
130                let offer_b = &mut chunk_data.chunk_b;
131                if *offer_b != quad {
132                    signature.push_bits(PLAIN_FLAG, FLAG_SIZE_BITS); // Chunk
133                    out_buffer.push(&quad.to_le_bytes());
134                } else {
135                    signature.push_bits(MAP_B_FLAG, FLAG_SIZE_BITS); // Map B
136                    out_buffer.push(&hash_u16.to_le_bytes());
137                }
138                *offer_b = *offer_a;
139                *offer_a = quad;
140            } else {
141                signature.push_bits(MAP_A_FLAG, FLAG_SIZE_BITS); // Map A
142                out_buffer.push(&hash_u16.to_le_bytes());
143            }
144            *predicted_chunk = quad;
145        } else {
146            signature.push_bits(PREDICTED_FLAG, FLAG_SIZE_BITS); // Predicted
147        }
148        self.state.last_hash = hash_u16;
149    }
150}
151
152impl Decoder for Cheetah {
153    #[inline(always)]
154    fn decode_unit(&mut self, in_buffer: &mut ReadBuffer, signature: &mut ReadSignature, out_buffer: &mut WriteBuffer) {
155        let (hash, quad) = match signature.read_bits(DECODE_FLAG_MASK, DECODE_FLAG_MASK_BITS) {
156            PLAIN_FLAG => { self.decode_plain(in_buffer) }
157            MAP_A_FLAG => { self.decode_map_a(in_buffer) }
158            MAP_B_FLAG => { self.decode_map_b(in_buffer) }
159            _ => { self.decode_predicted() }
160        };
161        self.state.last_hash = hash;
162        out_buffer.push(&quad.to_le_bytes());
163    }
164
165    #[inline(always)]
166    fn decode_partial_unit(&mut self, in_buffer: &mut ReadBuffer, signature: &mut ReadSignature, out_buffer: &mut WriteBuffer) -> bool {
167        let (hash, quad) = match signature.read_bits(DECODE_FLAG_MASK, DECODE_FLAG_MASK_BITS) {
168            PLAIN_FLAG => {
169                match in_buffer.remaining() {
170                    0 => { return true; }
171                    1..=3 => {
172                        out_buffer.push(in_buffer.read(in_buffer.remaining()));
173                        return true;
174                    }
175                    _ => { self.decode_plain(in_buffer) }
176                }
177            }
178            MAP_A_FLAG => { self.decode_map_a(in_buffer) }
179            MAP_B_FLAG => { self.decode_map_b(in_buffer) }
180            _ => { self.decode_predicted() }
181        };
182        self.state.last_hash = hash;
183        out_buffer.push(&quad.to_le_bytes());
184        false
185    }
186}
187
188impl Codec for Cheetah {
189    #[inline(always)]
190    fn block_size() -> usize { BYTE_SIZE_U32 * (Self::signature_significant_bytes() << 3) / FLAG_SIZE_BITS as usize }
191
192    #[inline(always)]
193    fn decode_unit_size() -> usize { 4 }
194
195    #[inline(always)]
196    fn signature_significant_bytes() -> usize { 8 }
197
198    fn clear_state(&mut self) {
199        self.state.last_hash = 0;
200        self.state.chunk_map.fill(ChunkData { chunk_a: 0, chunk_b: 0 });
201        self.state.prediction_map.fill(PredictionData { next: 0 });
202    }
203}