1use 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}