vsimd/
alsw.rs

1// ALSW: Avgr, Lookup, Saturating_add, Wrapping_add
2// Inspired by <https://gist.github.com/aqrit/a2ccea48d7cac7e9d4d99f19d4759666>
3//
4
5use crate::algorithm::{avgr, lookup};
6use crate::pod::POD;
7use crate::table::u8x16xn_lookup;
8use crate::vector::{V128, V256};
9use crate::Scalable;
10
11use core::ops::Not;
12
13#[inline]
14#[must_use]
15pub const fn hash(hash_lut: &[u8; 16], c: u8) -> u8 {
16    avgr(0xE0 | (c >> 3), lookup(hash_lut, c))
17}
18
19#[inline]
20#[must_use]
21pub const fn check(hash_lut: &[u8; 16], offset: &[u8; 16], c: u8) -> u8 {
22    let h = hash(hash_lut, c);
23    let o = lookup(offset, h);
24    (c as i8).saturating_add(o as i8) as u8
25}
26
27#[inline]
28#[must_use]
29pub const fn decode(hash_lut: &[u8; 16], offset: &[u8; 16], c: u8) -> u8 {
30    let h = hash(hash_lut, c);
31    let o = lookup(offset, h);
32    c.wrapping_add(o)
33}
34
35#[derive(Debug, Clone, Copy)]
36pub struct AlswLut<V> {
37    pub hash: V,
38    pub offset: V,
39}
40
41impl AlswLut<V128> {
42    #[inline]
43    #[must_use]
44    pub const fn x2(self) -> AlswLut<V256> {
45        AlswLut {
46            hash: self.hash.x2(),
47            offset: self.offset.x2(),
48        }
49    }
50}
51
52#[inline(always)]
53pub fn check_ascii_xn<S: Scalable<V>, V: POD>(s: S, x: V, check: AlswLut<V>) -> bool {
54    let shr3 = s.u32xn_shr::<3>(x);
55    let h1 = s.u8xn_avgr(shr3, u8x16xn_lookup(s, check.hash, x));
56    let o1 = u8x16xn_lookup(s, check.offset, h1);
57    let c1 = s.i8xn_add_sat(x, o1);
58    s.u8xn_highbit_any(c1).not()
59}
60
61#[inline(always)]
62pub fn decode_ascii_xn<S: Scalable<V>, V: POD>(s: S, x: V, check: AlswLut<V>, decode: AlswLut<V>) -> (V, V) {
63    let shr3 = s.u32xn_shr::<3>(x);
64
65    let h1 = s.u8xn_avgr(shr3, u8x16xn_lookup(s, check.hash, x));
66    let h2 = s.u8xn_avgr(shr3, u8x16xn_lookup(s, decode.hash, x));
67
68    let o1 = u8x16xn_lookup(s, check.offset, h1);
69    let o2 = u8x16xn_lookup(s, decode.offset, h2);
70
71    let c1 = s.i8xn_add_sat(x, o1);
72    let c2 = s.u8xn_add(x, o2);
73
74    (c1, c2)
75}
76
77#[macro_export]
78macro_rules! impl_alsw {
79    ($spec:ty) => {
80        impl $spec {
81            const CHECK_HASH: [u8; 16] = {
82                let mut arr = [0; 16];
83                let mut i = 0;
84                while i < 16 {
85                    let x: u8 = Self::check_hash(i as u8);
86                    arr[i] = (x << 1) - 1;
87                    i += 1;
88                }
89                arr
90            };
91
92            const CHECK_OFFSET: [u8; 16] = {
93                let mut arr = [0x80; 16];
94                let mut c: u8 = 255;
95                loop {
96                    if Self::decode(c) != 0xff {
97                        let h = $crate::alsw::hash(&Self::CHECK_HASH, c);
98                        arr[(h & 0x0f) as usize] = 0u8.wrapping_sub(c);
99                    }
100                    if c == 0 {
101                        break;
102                    }
103                    c -= 1;
104                }
105                arr
106            };
107
108            const DECODE_HASH: [u8; 16] = {
109                let mut arr = [0; 16];
110                let mut i = 0;
111                while i < 16 {
112                    let x: u8 = Self::decode_hash(i as u8);
113                    arr[i] = (x << 1) - 1;
114                    i += 1;
115                }
116                arr
117            };
118
119            const DECODE_OFFSET: [u8; 16] = {
120                let mut arr = [0x80; 16];
121                let mut c: u8 = 255;
122                loop {
123                    let idx = Self::decode(c);
124                    if idx != 0xff {
125                        let h = $crate::alsw::hash(&Self::DECODE_HASH, c);
126                        arr[(h & 0x0f) as usize] = idx.wrapping_sub(c);
127                    }
128                    if c == 0 {
129                        break;
130                    }
131                    c -= 1;
132                }
133                arr
134            };
135
136            #[inline]
137            #[must_use]
138            const fn check_lut() -> AlswLut<V128> {
139                AlswLut {
140                    hash: V128::from_bytes(Self::CHECK_HASH),
141                    offset: V128::from_bytes(Self::CHECK_OFFSET),
142                }
143            }
144
145            #[inline]
146            #[must_use]
147            const fn decode_lut() -> AlswLut<V128> {
148                AlswLut {
149                    hash: V128::from_bytes(Self::DECODE_HASH),
150                    offset: V128::from_bytes(Self::DECODE_OFFSET),
151                }
152            }
153
154            #[cfg(test)]
155            fn test_check() {
156                let hash = &Self::CHECK_HASH;
157                let offset = &Self::CHECK_OFFSET;
158
159                let check = |c: u8| $crate::alsw::check(hash, offset, c);
160
161                for c in 0..=255u8 {
162                    assert_eq!(check(c) < 0x80, Self::decode(c) != 0xff);
163                }
164            }
165
166            #[cfg(test)]
167            fn test_decode() {
168                let hash = &Self::DECODE_HASH;
169                let offset = &Self::DECODE_OFFSET;
170
171                let decode = |c: u8| $crate::alsw::decode(hash, offset, c);
172
173                for c in 0..=255u8 {
174                    let idx = Self::decode(c);
175                    if idx != 0xff {
176                        assert_eq!(decode(c), idx);
177                    }
178                }
179            }
180        }
181    };
182}