1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
extern crate self as absolut;
/// Composite algorithm.
///
/// This procedural macro expects an `enum` item where all variants are annotated with either the
/// `matches` attribute, or the `wilcard` attribute. It accepts no arguments.
///
/// The `matches` attribute takes a description of the set of bytes mapped to the corresponding enum
/// variant. This description takes one of the the following forms:
/// - A single byte literal, e.g. `#[matches(b'\xA')]`)
/// - A set of byte literals separated by a vartical bat, e.g. `#[matches(b'\xA' | b'\xD')]`
/// - An range of byte literals where the start (inclusive) and the end (exclusive) are join by two
/// dots, e.g. `#[matches(b'\xA'..b'\xD')]`
///
/// The `wilcard` attribute must be present on exactly one of the enum variants. It signifies the
/// value to which all non-matched bytes are mapped.
///
/// See [`Composite`] for more information.
pub use composite;
/// One-cold algorithm.
///
/// This procedural macro expects an `enum` item where all variants are annotated with either the
/// `matches` attribute, or the `wilcard` attribute. It accepts no arguments.
///
/// The `matches` attribute takes a description of the set of bytes mapped to the corresponding enum
/// variant. This description takes one of the the following forms:
/// - A single byte literal, e.g. `#[matches(b'\xA')]`)
/// - A set of byte literals separated by a vartical bat, e.g. `#[matches(b'\xA' | b'\xD')]`
/// - An range of byte literals where the start (inclusive) and the end (exclusive) are join by two
/// dots, e.g. `#[matches(b'\xA'..b'\xD')]`
///
/// The `wilcard` attribute must be present on exactly one of the enum variants. It signifies the
/// value to which all non-matched bytes are mapped.
///
/// See [`OneCold`] for more information.
pub use one_cold;
/// The One-hot algorithm.
///
/// This procedural macro expects an `enum` item where all variants are annotated with either the
/// `matches` attribute, or the `wilcard` attribute. It accepts no arguments.
///
/// The `matches` attribute takes a description of the set of bytes mapped to the corresponding enum
/// variant. This description takes one of the the following forms:
/// - A single byte literal, e.g. `#[matches(b'\xA')]`)
/// - A set of byte literals separated by a vartical bat, e.g. `#[matches(b'\xA' | b'\xD')]`
/// - An range of byte literals where the start (inclusive) and the end (exclusive) are join by two
/// dots, e.g. `#[matches(b'\xA'..b'\xD')]`
///
/// The `wilcard` attribute must be present on exactly one of the enum variants. It signifies the
/// value to which all non-matched bytes are mapped.
///
/// See [`OneHot`] for more information.
pub use one_hot;
/// General algorithm.
///
/// This procedural macro expects an `enum` item where all variants are annotated with either the
/// `matches` attribute, or the `wilcard` attribute. It accepts no arguments.
///
/// The `matches` attribute takes a description of the set of bytes mapped to the corresponding enum
/// variant. This description takes one of the the following forms:
/// - A single byte literal, e.g. `#[matches(b'\xA')]`)
/// - A set of byte literals separated by a vartical bat, e.g. `#[matches(b'\xA' | b'\xD')]`
/// - An range of byte literals where the start (inclusive) and the end (exclusive) are join by two
/// dots, e.g. `#[matches(b'\xA'..b'\xD')]`
///
/// The `wilcard` attribute must be present on exactly one of the enum variants. It signifies the
/// value to which all non-matched bytes are mapped.
///
/// See [`General`] for more information.
pub use general;
/// Interface for tables generated by [`one_hot`].
///
/// The name "One-hot" refers to the [One-hot](https://en.wikipedia.org/wiki/One-hot) binary
/// encoding, where at most one bit is 1 and all the others are 0 in any given set of bits. For the
/// [`one_hot`] algorithm, all table outputs follow the One-hot encoding (thus being powers of two).
///
/// Consequently, the [`one_hot`] algorithm only supports tables whose range (i.e. the set of all
/// possible outputs) contains no more than 9 elements (including the wildcard).
///
/// Lookup table [`OneHot::TABLE_LOW_NIBBLES`] is used to perform a lookup on the low nibbles of
/// input bytes while [`OneHot::TABLE_HIGH_NIBBLES`] is used to perform a lookup on the high nibbles
/// of input bytes. The results of the two lookups are combined using a __bitwise AND__ operation to
/// yield the final lookup.
///
/// The following `lookup` function implements the lookup algorithm supported by all [`OneHot`]
/// tables (using non-vectorized code for demonstration purposes).
///
/// ```rust
/// use absolut::OneHot;
///
/// fn lookup<T: OneHot>(input: &[u8; 16], output: &mut [u8; 16]) {
/// for (index, byte) in input.iter().copied().enumerate() {
/// let (lo, hi) = (byte & 0b1111, byte >> 4);
/// output[index] = T::TABLE_LOW_NIBBLES[lo as usize]
/// & T::TABLE_HIGH_NIBBLES[hi as usize];
/// }
/// }
///
/// #[absolut::one_hot]
/// #[derive(Debug)]
/// enum Table {
/// #[matches(b'A')] A,
/// #[matches(b'B')] B,
/// #[matches(b'C')] C,
/// #[wildcard] Other,
/// }
///
/// use Table::*;
/// let input = b"A___B___C___D___";
/// let expected_output = [
/// A as u8, Other as u8, Other as u8, Other as u8,
/// B as u8, Other as u8, Other as u8, Other as u8,
/// C as u8, Other as u8, Other as u8, Other as u8,
/// Other as u8, Other as u8, Other as u8, Other as u8,
/// ];
///
/// let mut output = [0; 16];
/// lookup::<Table>(&input, &mut output);
/// assert_eq!(&output, &expected_output);
/// ```
/// Interface for tables generated by [`one_cold`].
///
/// The name `one_cold` refers to the [One-cold](https://en.wikipedia.org/wiki/One-hot) binary
/// encoding, where at most one bit is 0 and all the others are 1 in any given set of bits. For the
/// [`one_cold`] algorithm, all table outputs follow the One-cold encoding.
///
/// Consequently, the [`one_cold`] algorithm only supports tables whose range (i.e. the set of all
/// possible outputs) contains no more than 9 elements (including the wildcard).
///
/// Lookup table [`OneCold::TABLE_LOW_NIBBLES`] is used to perform a lookup on the low nibbles of
/// input bytes while [`OneCold::TABLE_HIGH_NIBBLES`] is used to perform a lookup on the high nibbles
/// of input bytes. The results of the two lookups are combined using a __bitwise OR__ operation to
/// yield the final lookup.
///
/// The following `lookup` function implements the lookup algorithm supported by all [`OneCold`]
/// tables (using non-vectorized code for demonstration purposes).
///
/// ```rust
/// use absolut::OneCold;
///
/// fn lookup<T: OneCold>(input: &[u8; 16], output: &mut [u8; 16]) {
/// for (index, byte) in input.iter().copied().enumerate() {
/// let (lo, hi) = (byte & 0b1111, byte >> 4);
/// output[index] = T::TABLE_LOW_NIBBLES[lo as usize]
/// | T::TABLE_HIGH_NIBBLES[hi as usize];
/// }
/// }
///
/// #[absolut::one_cold]
/// #[derive(Debug)]
/// enum Table {
/// #[matches(b'E')] E,
/// #[matches(b'F')] F,
/// #[matches(b'G')] G,
/// #[wildcard] Other,
/// }
///
/// use Table::*;
/// let input = b"E___F___G___H___";
/// let expected_output = [
/// E as u8, Other as u8, Other as u8, Other as u8,
/// F as u8, Other as u8, Other as u8, Other as u8,
/// G as u8, Other as u8, Other as u8, Other as u8,
/// Other as u8, Other as u8, Other as u8, Other as u8,
/// ];
///
/// let mut output = [0; 16];
/// lookup::<Table>(&input, &mut output);
/// assert_eq!(&output, &expected_output);
/// ```
/// Interface for tables generated by [`composite`].
///
/// This algorithm supports [arbitrary byte-to-byte
/// map](https://lemire.me/blog/2019/07/23/arbitrary-byte-to-byte-maps-using-arm-neon/) (i.e. every
/// possible map description has a solution). The downside of this approach is that it only works on
/// AArch64 as it requires four distinct 64-element lookup tables. Moreover, it requires four
/// distinct lookup tables which may lead to disappointing performance.
///
/// The following `lookup` function implements the lookup algorithm supported by all [`Composite`]
/// tables (using non-vectorized code for demonstration purposes).
///
/// ```rust
/// use absolut::Composite;
///
/// fn lookup<T: Composite>(input: &[u8; 16], output: &mut [u8; 16]) {
/// for (index, byte) in input.iter().copied().enumerate() {
/// output[index] = match byte {
/// 0..=63 => T::TABLE_QUARTERS[0][byte as usize],
/// 64..=127 => T::TABLE_QUARTERS[1][byte as usize - 64],
/// 128..=191 => T::TABLE_QUARTERS[2][byte as usize - 128],
/// 192..=255 => T::TABLE_QUARTERS[3][byte as usize - 192],
/// };
/// }
/// }
///
/// #[absolut::composite]
/// #[derive(Debug)]
/// enum Table {
/// #[matches(b'I')] I,
/// #[matches(b'J')] J,
/// #[matches(b'K')] K,
/// #[wildcard] Other,
/// }
///
/// use Table::*;
/// let input = b"I___J___K___L___";
/// let expected_output = [
/// I as u8, Other as u8, Other as u8, Other as u8,
/// J as u8, Other as u8, Other as u8, Other as u8,
/// K as u8, Other as u8, Other as u8, Other as u8,
/// Other as u8, Other as u8, Other as u8, Other as u8,
/// ];
///
/// let mut output = [0; 16];
/// lookup::<Table>(&input, &mut output);
/// assert_eq!(&output, &expected_output);
/// ```
/// Interface for tables generated by [`general`].
///
/// Similarly to [`OneHot`], lookup table [`General::TABLE_LOW_NIBBLES`] is used to perform a lookup
/// on the low nibbles of input bytes while [`General::TABLE_HIGH_NIBBLES`] is used to perform a
/// lookup on the high nibbles of input bytes. The results of the two lookups are combined using a
/// __bitwise AND__ operation to yield the final lookup.
///
/// However, lookup tables generated by [`general`] do not follow the One-hot encoding. Instead, a
/// SAT solver is used to compute the lookup table. Thus supporting more than 9 possible output
/// values that needn't necessarily be powers of two.
///
/// Consequently, [`general`] may find the a lookup table satisfiable when [`one_hot`] doesn't.
///
/// The following `lookup` function implements the lookup algorithm supported by all [`General`] (and [`OneHot`])
/// tables (using non-vectorized code for demonstration purposes).
///
/// ```rust
/// use absolut::General;
///
/// fn lookup<T: General>(input: &[u8; 16], output: &mut [u8; 16]) {
/// for (index, byte) in input.iter().copied().enumerate() {
/// let (lo, hi) = (byte & 0b1111, byte >> 4);
/// output[index] = T::TABLE_LOW_NIBBLES[lo as usize]
/// & T::TABLE_HIGH_NIBBLES[hi as usize];
/// }
/// }
///
/// #[absolut::general]
/// #[derive(Debug)]
/// enum Table {
/// #[matches(b'M')] M,
/// #[matches(b'N')] N,
/// #[matches(b'O')] O,
/// #[wildcard] Other,
/// }
///
/// use Table::*;
/// let input = b"M___N___O___P___";
/// let expected_output = [
/// M as u8, Other as u8, Other as u8, Other as u8,
/// N as u8, Other as u8, Other as u8, Other as u8,
/// O as u8, Other as u8, Other as u8, Other as u8,
/// Other as u8, Other as u8, Other as u8, Other as u8,
/// ];
///
/// let mut output = [0; 16];
/// lookup::<Table>(&input, &mut output);
/// assert_eq!(&output, &expected_output);
/// ```