Skip to main content

yo_kv/
bits.rs

1//! Bit level kernels: counting, searching, combining and packed fields.
2//!
3//! Redis calls a string used this way a bitmap, and it is not a separate type:
4//! `SETBIT`, `BITCOUNT`, `BITOP` and `BITFIELD` all work on the ordinary string
5//! a `SET` would have left behind, which is why this file is kernels over byte
6//! slices and nothing else. The keyspace side, which is where a key turns into
7//! bytes and where growing a value is decided, is in
8//! [`bitmaps`](crate::bitmaps).
9//!
10//! # Which end a bit is
11//!
12//! Bit zero is the top bit of byte zero. That is the convention every one of
13//! these commands uses and it is the opposite of the one a language's shift
14//! operators suggest, so it is worth being blunt about it: bit `i` is
15//!
16//! ```text
17//! bytes[i / 8] & (0x80 >> (i % 8))
18//! ```
19//!
20//! It falls out of wanting `BITPOS` over a bitmap of user ids to answer in the
21//! order the ids were assigned, and it is why a `u64` loaded out of the middle
22//! of a bitmap has to be read big endian for [`u64::leading_zeros`] to mean the
23//! distance to the next set bit.
24//!
25//! # Counting
26//!
27//! [`count`] is four `u64` accumulators fed by `count_ones`. That is the same
28//! shape as Redis's `redisPopcount`, which unrolls by four for the same reason:
29//! `popcnt` has a three cycle latency and one per cycle throughput on every x86
30//! since Nehalem, so a loop with one accumulator is latency bound at a third of
31//! the rate and four independent chains fill the pipe. On aarch64 there is no
32//! scalar popcount at all and LLVM turns the same loop into `cnt` over a vector
33//! register plus a widening add tree, which is why this is written as an
34//! ordinary loop rather than as intrinsics: the ordinary loop is what both
35//! backends already do well, and an intrinsic version would be two more code
36//! paths to keep right for no measured gain.
37//!
38//! # Combining
39//!
40//! [`combine`] does `BITOP`, including the four operations Redis 8.2 added:
41//! `DIFF`, `DIFF1`, `ANDOR` and `ONE`. It works a block at a time over a fixed
42//! stack buffer rather than allocating one accumulator per source, so a `BITOP`
43//! over eight sources touches the same two kibibytes of stack whatever the
44//! bitmaps weigh, and each block of each source is read once while it is warm.
45//! The alternative, folding whole bitmaps one source at a time, walks the
46//! destination once per source and that is where a `BITOP` over big bitmaps
47//! spends its time.
48
49/// How many bytes of each source a block pass works on at once.
50///
51/// Two of these live on the stack in the worst case, which is `ONE` and its
52/// "seen more than once" mask, so the whole of `BITOP` is two kibibytes of
53/// stack. Big enough that the per block overhead disappears against the byte
54/// loops, small enough to sit in L1 next to a block of every source.
55const BLOCK: usize = 1024;
56
57/// How many bits are set.
58#[must_use]
59pub fn count(bytes: &[u8]) -> u64 {
60    let (words, tail_bytes) = bytes.as_chunks::<32>();
61    let (mut a, mut b, mut c, mut d) = (0u32, 0u32, 0u32, 0u32);
62    for w in words {
63        a += u64::from_le_bytes(w[0..8].try_into().expect("eight bytes")).count_ones();
64        b += u64::from_le_bytes(w[8..16].try_into().expect("eight bytes")).count_ones();
65        c += u64::from_le_bytes(w[16..24].try_into().expect("eight bytes")).count_ones();
66        d += u64::from_le_bytes(w[24..32].try_into().expect("eight bytes")).count_ones();
67    }
68    let tail: u32 = tail_bytes.iter().map(|&x| x.count_ones()).sum();
69    u64::from(a) + u64::from(b) + u64::from(c) + u64::from(d) + u64::from(tail)
70}
71
72/// How many bits are set in the half open bit range `from..to`.
73///
74/// Both ends are bit indexes and the caller has already clamped them to the
75/// bitmap, which is where the negative index and the `BYTE` or `BIT` word are
76/// dealt with. An empty or backwards range is zero.
77#[must_use]
78pub fn count_range(bytes: &[u8], from: u64, to: u64) -> u64 {
79    let Some((head, whole, tail)) = split(bytes, from, to) else {
80        return 0;
81    };
82    u64::from(head.count_ones()) + count(whole) + u64::from(tail.count_ones())
83}
84
85/// The first bit equal to `set` in the half open bit range `from..to`.
86///
87/// `None` when the range holds no such bit, which the command layer turns into
88/// minus one or into the bit past the end depending on which of the two
89/// questions was asked.
90#[must_use]
91pub fn find(bytes: &[u8], set: bool, from: u64, to: u64) -> Option<u64> {
92    if from >= to || from >= (bytes.len() as u64) * 8 {
93        return None;
94    }
95    let end = to.min((bytes.len() as u64) * 8);
96    // A byte at a time over the ragged ends and a word at a time in the middle
97    // would be three loops to get right. This is one loop over bytes with the
98    // two ends masked, and the word scan below it only has to find the first
99    // byte that is not uniform, which is where the time goes on a long bitmap.
100    let (first, last) = ((from / 8) as usize, ((end - 1) / 8) as usize);
101    let mut at = first;
102    while at <= last {
103        let mut byte = bytes[at];
104        if !set {
105            byte = !byte;
106        }
107        // Off the ends of the range, pretend the bits are not what we want.
108        if at == first {
109            byte &= 0xffu8 >> (from % 8);
110        }
111        if at == last && !end.is_multiple_of(8) {
112            byte &= !(0xffu8 >> (end % 8));
113        }
114        if byte != 0 {
115            return Some(at as u64 * 8 + u64::from(byte.leading_zeros()));
116        }
117        // Nothing in this byte, so skip whole words of nothing. The scan reads
118        // eight bytes at a time and only past the first byte, so the masks
119        // above never apply to what it skips.
120        at += 1;
121        let uniform = if set { 0 } else { u64::MAX };
122        while at + 8 <= last {
123            let w = u64::from_ne_bytes(bytes[at..at + 8].try_into().expect("eight bytes"));
124            if w != uniform {
125                break;
126            }
127            at += 8;
128        }
129    }
130    None
131}
132
133/// The masked first byte, the whole bytes and the masked last byte of a range.
134///
135/// `None` for a range that holds nothing. The two ends come back as values
136/// rather than as slices because they are masked copies and not what is in the
137/// bitmap, and the middle comes back as a slice so that [`count`] can have it.
138fn split(bytes: &[u8], from: u64, to: u64) -> Option<(u8, &[u8], u8)> {
139    let bits = (bytes.len() as u64) * 8;
140    let (from, to) = (from.min(bits), to.min(bits));
141    if from >= to {
142        return None;
143    }
144    let (first, last) = ((from / 8) as usize, ((to - 1) / 8) as usize);
145    let low = 0xffu8 >> (from % 8);
146    let high = if to % 8 == 0 {
147        0xff
148    } else {
149        !(0xffu8 >> (to % 8))
150    };
151    if first == last {
152        return Some((bytes[first] & low & high, &[], 0));
153    }
154    Some((
155        bytes[first] & low,
156        &bytes[first + 1..last],
157        bytes[last] & high,
158    ))
159}
160
161/// The operations `BITOP` takes.
162///
163/// The first four are Redis 2.6's and the last four went in with 8.2. They all
164/// answer a bitmap as long as the longest source, since a source that is
165/// shorter reads as zeros past its end, and `NOT` is the only one that will not
166/// take more than one source.
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub enum Op {
169    /// Bits set in every source.
170    And,
171    /// Bits set in any source.
172    Or,
173    /// Bits set in an odd number of sources.
174    Xor,
175    /// The complement of the one source.
176    Not,
177    /// Bits set in the first source and in none of the others.
178    Diff,
179    /// Bits set in one or more of the others and not in the first.
180    Diff1,
181    /// Bits set in the first source and in one or more of the others.
182    AndOr,
183    /// Bits set in exactly one source.
184    One,
185}
186
187impl Op {
188    /// The word a client sends, in any case.
189    #[must_use]
190    pub fn parse(word: &[u8]) -> Option<Op> {
191        const NAMES: [(&[u8], Op); 8] = [
192            (b"and", Op::And),
193            (b"or", Op::Or),
194            (b"xor", Op::Xor),
195            (b"not", Op::Not),
196            (b"diff", Op::Diff),
197            (b"diff1", Op::Diff1),
198            (b"andor", Op::AndOr),
199            (b"one", Op::One),
200        ];
201        NAMES
202            .iter()
203            .find(|(name, _)| name.eq_ignore_ascii_case(word))
204            .map(|&(_, op)| op)
205    }
206
207    /// The name, upper case, which is how the error sentences spell it.
208    #[must_use]
209    pub const fn name(self) -> &'static str {
210        match self {
211            Op::And => "AND",
212            Op::Or => "OR",
213            Op::Xor => "XOR",
214            Op::Not => "NOT",
215            Op::Diff => "DIFF",
216            Op::Diff1 => "DIFF1",
217            Op::AndOr => "ANDOR",
218            Op::One => "ONE",
219        }
220    }
221
222    /// Whether this operation reads the first source differently from the rest.
223    ///
224    /// The three set difference shapes do, and it is the only reason `combine`
225    /// keeps the first source apart from the fold over the others.
226    const fn asymmetric(self) -> bool {
227        matches!(self, Op::Diff | Op::Diff1 | Op::AndOr)
228    }
229}
230
231/// How long the result of `op` over `srcs` will be.
232///
233/// As long as the longest source, since a shorter one reads as zeros past its
234/// end. A caller sizes its destination with this and then fills it with
235/// [`combine`].
236///
237/// # Panics
238///
239/// If `srcs` is empty, which is refused with a message on the wire.
240pub fn width<'a, I>(srcs: I) -> usize
241where
242    I: Iterator<Item = &'a [u8]>,
243{
244    srcs.map(<[u8]>::len).max().expect("BITOP with no source")
245}
246
247/// Run `op` over `srcs`, filling `out`.
248///
249/// The sources are taken as an iterator that can be cloned rather than as a
250/// slice, so that a caller with its sources end to end in one buffer does not
251/// have to build a list of slices into it. The iterator is walked once per block
252/// of the destination, which is why it has to be cloneable and why it should be
253/// cheap to walk.
254///
255/// `out` is filled to whatever length it already has, and the caller gets that
256/// length from [`width`]. Handing over a longer one is not wrong, it reads the
257/// sources as zero padded out to there, which is the same rule that applies
258/// inside the result anyway.
259///
260/// # Panics
261///
262/// If `srcs` is empty, or if it holds more than one source for [`Op::Not`].
263/// Both are refused on the wire before this is called.
264pub fn combine<'a, I>(op: Op, srcs: I, out: &mut [u8])
265where
266    I: Iterator<Item = &'a [u8]> + Clone,
267{
268    let mut count = srcs.clone();
269    assert!(count.next().is_some(), "BITOP with no source");
270    assert!(
271        op != Op::Not || count.next().is_none(),
272        "BITOP NOT with more"
273    );
274    let len = out.len();
275
276    // One block of whichever source is being folded in, and one block held to
277    // one side. Nothing needs the side buffer for two purposes at once: the
278    // three difference shapes keep the first source there and `ONE` keeps its
279    // "already seen once" mask there.
280    let mut blk = [0u8; BLOCK];
281    let mut side = [0u8; BLOCK];
282    let mut at = 0;
283    while at < len {
284        let n = BLOCK.min(len - at);
285        let acc = &mut out[at..at + n];
286        let mut rest = srcs.clone();
287        let first = rest.next().expect("a first source");
288        // The first source seeds the accumulator for everything except the
289        // three difference shapes, which need it again at the end and so keep
290        // it to one side while the others are folded together.
291        if op.asymmetric() {
292            load(&mut side[..n], first, at);
293            acc.fill(0);
294        } else {
295            load(acc, first, at);
296            if op == Op::One {
297                side[..n].fill(0);
298            }
299        }
300
301        for src in rest {
302            load(&mut blk[..n], src, at);
303            let s = &blk[..n];
304            match op {
305                Op::And => fold(acc, s, |a, b| a & b),
306                Op::Or | Op::Diff | Op::Diff1 | Op::AndOr => fold(acc, s, |a, b| a | b),
307                Op::Xor => fold(acc, s, |a, b| a ^ b),
308                Op::One => {
309                    for (i, &b) in s.iter().enumerate() {
310                        side[i] |= acc[i] & b;
311                        acc[i] |= b;
312                    }
313                }
314                Op::Not => unreachable!("NOT takes one source"),
315            }
316        }
317
318        match op {
319            Op::Not => {
320                for a in acc.iter_mut() {
321                    *a = !*a;
322                }
323            }
324            // Set anywhere, minus set more than once.
325            Op::One => fold(acc, &side[..n], |a, b| a & !b),
326            // `side` still holds the first source's block, and `acc` holds the
327            // others folded together with `OR`.
328            Op::Diff => {
329                for (i, a) in acc.iter_mut().enumerate() {
330                    *a = side[i] & !*a;
331                }
332            }
333            Op::Diff1 => {
334                for (i, a) in acc.iter_mut().enumerate() {
335                    *a &= !side[i];
336                }
337            }
338            Op::AndOr => {
339                for (i, a) in acc.iter_mut().enumerate() {
340                    *a &= side[i];
341                }
342            }
343            Op::And | Op::Or | Op::Xor => {}
344        }
345        at += n;
346    }
347}
348
349/// A block of `src` starting at `at`, zero padded past its end.
350fn load(dst: &mut [u8], src: &[u8], at: usize) {
351    let from = at.min(src.len());
352    let take = (src.len() - from).min(dst.len());
353    dst[..take].copy_from_slice(&src[from..from + take]);
354    dst[take..].fill(0);
355}
356
357/// `acc[i] = f(acc[i], src[i])`, written so both backends vectorise it.
358#[inline]
359fn fold(acc: &mut [u8], src: &[u8], f: impl Fn(u8, u8) -> u8) {
360    for (a, &b) in acc.iter_mut().zip(src) {
361        *a = f(*a, b);
362    }
363}
364
365// ------------------------------------------------------------ packed fields
366
367/// One `BITFIELD` field type: `u8`, `i37` and so on.
368///
369/// Unsigned goes up to 63 bits and signed to 64, which is Redis's rule and not
370/// an accident of the reply type: every value comes back as a RESP integer and
371/// a RESP integer is signed, so a `u64` could not be reported.
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub struct Field {
374    /// Whether the top bit is a sign.
375    signed: bool,
376    /// How many bits wide, 1 to 64 signed and 1 to 63 unsigned.
377    bits: u32,
378}
379
380impl Field {
381    /// A field of `bits` bits, or `None` if that is not a width Redis takes.
382    #[must_use]
383    pub const fn new(signed: bool, bits: u32) -> Option<Field> {
384        let top = if signed { 64 } else { 63 };
385        if bits == 0 || bits > top {
386            return None;
387        }
388        Some(Field { signed, bits })
389    }
390
391    /// The `u8` or `i37` a client sends.
392    #[must_use]
393    pub fn parse(word: &[u8]) -> Option<Field> {
394        let (&kind, digits) = word.split_first()?;
395        let signed = match kind {
396            b'i' => true,
397            b'u' => false,
398            _ => return None,
399        };
400        if digits.is_empty() || digits.len() > 2 || !digits.iter().all(u8::is_ascii_digit) {
401            return None;
402        }
403        let bits = digits
404            .iter()
405            .fold(0u32, |n, d| n * 10 + u32::from(d - b'0'));
406        Field::new(signed, bits)
407    }
408
409    /// How wide.
410    #[must_use]
411    pub const fn bits(self) -> u32 {
412        self.bits
413    }
414
415    /// Whether the top bit is a sign.
416    #[must_use]
417    pub const fn signed(self) -> bool {
418        self.signed
419    }
420
421    /// The largest value it holds.
422    #[must_use]
423    pub const fn max(self) -> i64 {
424        if self.signed {
425            if self.bits == 64 {
426                i64::MAX
427            } else {
428                (1i64 << (self.bits - 1)) - 1
429            }
430        } else if self.bits == 63 {
431            i64::MAX
432        } else {
433            (1i64 << self.bits) - 1
434        }
435    }
436
437    /// The smallest value it holds, which is zero when it is unsigned.
438    #[must_use]
439    pub const fn min(self) -> i64 {
440        if !self.signed {
441            0
442        } else if self.bits == 64 {
443            i64::MIN
444        } else {
445            -(1i64 << (self.bits - 1))
446        }
447    }
448
449    /// The last bit a field at `at` touches, which is what a caller grows to.
450    #[must_use]
451    pub const fn last_bit(self, at: u64) -> u64 {
452        at + self.bits as u64 - 1
453    }
454
455    /// The value truncated to this many bits, sign extended if it is signed.
456    ///
457    /// This is `OVERFLOW WRAP`, and it is the only one of the three that has to
458    /// think about the width at all.
459    #[must_use]
460    const fn wrapped(self, n: i128) -> i64 {
461        if self.bits == 64 {
462            return n as i64;
463        }
464        let mask = (1i128 << self.bits) - 1;
465        let low = n & mask;
466        if self.signed && low > self.max() as i128 {
467            (low - (1i128 << self.bits)) as i64
468        } else {
469            low as i64
470        }
471    }
472}
473
474/// What to do about a value that will not fit.
475#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
476pub enum Overflow {
477    /// Keep the low bits, which is what a counter that is allowed to lap does.
478    #[default]
479    Wrap,
480    /// Stop at the end of the range.
481    Sat,
482    /// Do nothing and answer nothing.
483    Fail,
484}
485
486impl Overflow {
487    /// The word a client sends, in any case.
488    #[must_use]
489    pub fn parse(word: &[u8]) -> Option<Overflow> {
490        if word.eq_ignore_ascii_case(b"wrap") {
491            Some(Overflow::Wrap)
492        } else if word.eq_ignore_ascii_case(b"sat") {
493            Some(Overflow::Sat)
494        } else if word.eq_ignore_ascii_case(b"fail") {
495            Some(Overflow::Fail)
496        } else {
497            None
498        }
499    }
500}
501
502/// The field of `f` bits at bit `at`, reading past the end as zeros.
503#[must_use]
504pub fn get(bytes: &[u8], at: u64, f: Field) -> i64 {
505    let raw = window(bytes, at, f.bits);
506    if f.signed && f.bits < 64 && raw >= 1u64 << (f.bits - 1) {
507        // The subtraction is done wide because at 63 bits the thing being taken
508        // off is one past what an `i64` holds.
509        (i128::from(raw) - (1i128 << f.bits)) as i64
510    } else {
511        raw as i64
512    }
513}
514
515/// Write `val` into the field of `f` bits at bit `at`.
516///
517/// # Panics
518///
519/// If the slice does not reach the end of the field. Growing the value is the
520/// caller's job, because only the caller knows whether it is allowed to.
521pub fn set(bytes: &mut [u8], at: u64, f: Field, val: i64) {
522    let last = ((at + u64::from(f.bits) - 1) / 8) as usize;
523    assert!(last < bytes.len(), "the field runs off the end");
524    let (byte, off) = ((at / 8) as usize, (at % 8) as u32);
525    let span = ((off + f.bits).div_ceil(8)) as usize;
526    // Nine bytes is the worst case, a 64 bit field starting one bit into a
527    // byte, so the window is a u128 and never a wider read than that.
528    let mut win: u128 = 0;
529    for &b in &bytes[byte..byte + span] {
530        win = (win << 8) | u128::from(b);
531    }
532    let shift = span as u32 * 8 - off - f.bits;
533    let mask = ((1u128 << f.bits) - 1) << shift;
534    win = (win & !mask) | ((u128::from(val as u64) << shift) & mask);
535    for (i, b) in bytes[byte..byte + span].iter_mut().enumerate() {
536        *b = (win >> ((span - 1 - i) * 8)) as u8;
537    }
538}
539
540/// The raw bits of a field, as an unsigned number, zero past the end.
541fn window(bytes: &[u8], at: u64, bits: u32) -> u64 {
542    let (byte, off) = ((at / 8) as usize, (at % 8) as u32);
543    let span = ((off + bits).div_ceil(8)) as usize;
544    let mut win: u128 = 0;
545    for i in 0..span {
546        win = (win << 8) | u128::from(bytes.get(byte + i).copied().unwrap_or(0));
547    }
548    let shift = span as u32 * 8 - off - bits;
549    let mask = (1u128 << bits) - 1;
550    ((win >> shift) & mask) as u64
551}
552
553/// The value a `SET` of `val` should write, or `None` for `FAIL`.
554///
555/// A negative value into an unsigned field is the one that surprises people. It
556/// is not clamped to zero: Redis reads the value as the unsigned number its
557/// two's complement bits spell, which is enormous, so it overflows off the top
558/// and `SAT` gives the field's maximum rather than nothing. Measured on 8.10.1:
559/// `OVERFLOW SAT SET u8 0 -5` leaves 255 and `OVERFLOW WRAP` leaves 251.
560#[must_use]
561pub fn setting(f: Field, val: i64, on: Overflow) -> Option<i64> {
562    let want = if f.signed {
563        i128::from(val)
564    } else {
565        i128::from(val as u64)
566    };
567    fit(f, want, on)
568}
569
570/// The value an `INCRBY` of `by` should write, or `None` for `FAIL`.
571#[must_use]
572pub fn adding(f: Field, had: i64, by: i64, on: Overflow) -> Option<i64> {
573    fit(f, i128::from(had) + i128::from(by), on)
574}
575
576/// `want` brought into the field's range the way `on` says to.
577fn fit(f: Field, want: i128, on: Overflow) -> Option<i64> {
578    if want > i128::from(f.max()) {
579        return match on {
580            Overflow::Wrap => Some(f.wrapped(want)),
581            Overflow::Sat => Some(f.max()),
582            Overflow::Fail => None,
583        };
584    }
585    if want < i128::from(f.min()) {
586        return match on {
587            Overflow::Wrap => Some(f.wrapped(want)),
588            Overflow::Sat => Some(f.min()),
589            Overflow::Fail => None,
590        };
591    }
592    Some(want as i64)
593}
594
595#[cfg(test)]
596mod tests {
597    use super::*;
598
599    /// The obvious version of everything in this file, one bit at a time.
600    fn slow_bit(bytes: &[u8], at: u64) -> bool {
601        let (byte, off) = ((at / 8) as usize, (at % 8) as u32);
602        bytes.get(byte).is_some_and(|b| b & (0x80 >> off) != 0)
603    }
604
605    fn slow_count(bytes: &[u8], from: u64, to: u64) -> u64 {
606        (from..to).filter(|&i| slow_bit(bytes, i)).count() as u64
607    }
608
609    fn slow_find(bytes: &[u8], set: bool, from: u64, to: u64) -> Option<u64> {
610        (from..to.min((bytes.len() as u64) * 8)).find(|&i| slow_bit(bytes, i) == set)
611    }
612
613    fn slow_get(bytes: &[u8], at: u64, f: Field) -> i64 {
614        let mut raw = 0u64;
615        for i in 0..u64::from(f.bits()) {
616            raw = (raw << 1) | u64::from(slow_bit(bytes, at + i));
617        }
618        if f.signed() && f.bits() < 64 && raw >= 1u64 << (f.bits() - 1) {
619            (i128::from(raw) - (1i128 << f.bits())) as i64
620        } else {
621            raw as i64
622        }
623    }
624
625    /// A repeatable spread of bytes, since none of this is about randomness.
626    fn noise(n: usize, seed: u64) -> Vec<u8> {
627        let mut x = seed | 1;
628        (0..n)
629            .map(|_| {
630                x ^= x << 13;
631                x ^= x >> 7;
632                x ^= x << 17;
633                (x >> 24) as u8
634            })
635            .collect()
636    }
637
638    #[test]
639    fn counting_agrees_with_counting_one_bit_at_a_time() {
640        for len in [0usize, 1, 7, 8, 31, 32, 33, 100, 257] {
641            let bytes = noise(len, len as u64 + 7);
642            assert_eq!(count(&bytes), slow_count(&bytes, 0, len as u64 * 8));
643        }
644    }
645
646    #[test]
647    fn every_range_counts_what_a_bit_loop_counts() {
648        let bytes = noise(37, 99);
649        let bits = 37 * 8;
650        for from in (0..bits).step_by(7) {
651            for to in (from..bits + 16).step_by(5) {
652                assert_eq!(
653                    count_range(&bytes, from, to),
654                    slow_count(&bytes, from, to.min(bits)),
655                    "{from}..{to}"
656                );
657            }
658        }
659        // Backwards and empty ranges are nothing rather than a panic.
660        assert_eq!(count_range(&bytes, 10, 10), 0);
661        assert_eq!(count_range(&bytes, 20, 3), 0);
662        assert_eq!(count_range(&[], 0, 64), 0);
663    }
664
665    #[test]
666    fn finding_agrees_with_scanning_one_bit_at_a_time() {
667        // Long enough that the word skip in the middle runs, and with runs of
668        // all ones and all zeros in it so that it has something to skip.
669        let mut bytes = noise(300, 5);
670        bytes[64..128].fill(0);
671        bytes[160..224].fill(0xff);
672        let bits = bytes.len() as u64 * 8;
673        for set in [true, false] {
674            for from in (0..bits).step_by(11) {
675                for to in [from, from + 1, from + 63, from + 700, bits, bits + 9] {
676                    assert_eq!(
677                        find(&bytes, set, from, to),
678                        slow_find(&bytes, set, from, to),
679                        "set={set} {from}..{to}"
680                    );
681                }
682            }
683        }
684        assert_eq!(find(&[], true, 0, 64), None);
685        assert_eq!(find(&[0xff], false, 0, 8), None);
686        assert_eq!(find(&[0xff], true, 0, 8), Some(0));
687    }
688
689    #[test]
690    fn a_bit_is_counted_from_the_top_of_the_first_byte() {
691        assert_eq!(find(&[0x01], true, 0, 8), Some(7));
692        assert_eq!(find(&[0x80], true, 0, 8), Some(0));
693        assert_eq!(count(&[0x01]), 1);
694    }
695
696    /// The shapes measured on 8.10.1, which is where these came from.
697    #[test]
698    fn the_eight_operations_are_what_a_real_server_does() {
699        let a: &[u8] = &[0xf0, 0x0f, 0xff];
700        let b: &[u8] = &[0xff, 0x00];
701        let c: &[u8] = &[0x0f];
702        let mut out = Vec::new();
703        let run = |op, srcs: &[&[u8]], out: &mut Vec<u8>| {
704            out.clear();
705            out.resize(width(srcs.iter().copied()), 0);
706            combine(op, srcs.iter().copied(), out);
707        };
708
709        run(Op::And, &[a, b], &mut out);
710        assert_eq!(out, vec![0xf0, 0x00, 0x00], "and, padded with zeros");
711        run(Op::Or, &[a, b], &mut out);
712        assert_eq!(out, vec![0xff, 0x0f, 0xff]);
713        run(Op::Xor, &[a, b], &mut out);
714        assert_eq!(out, vec![0x0f, 0x0f, 0xff]);
715        run(Op::Not, &[a], &mut out);
716        assert_eq!(out, vec![0x0f, 0xf0, 0x00]);
717        run(Op::Diff, &[a, b], &mut out);
718        assert_eq!(out, vec![0x00, 0x0f, 0xff], "in a and in nothing else");
719        run(Op::Diff1, &[a, b], &mut out);
720        assert_eq!(out, vec![0x0f, 0x00, 0x00], "in the others and not in a");
721        run(Op::AndOr, &[a, b, c], &mut out);
722        assert_eq!(out, vec![0xf0, 0x00, 0x00]);
723        run(Op::One, &[a, b, c], &mut out);
724        assert_eq!(out, vec![0x00, 0x0f, 0xff], "set in exactly one of them");
725
726        // One source is a copy for everything that takes one.
727        for op in [Op::And, Op::Or, Op::Xor, Op::One] {
728            run(op, &[a], &mut out);
729            assert_eq!(out, a, "{} of one source", op.name());
730        }
731    }
732
733    /// The block loop is only exercised by something longer than a block.
734    #[test]
735    fn combining_crosses_the_block_boundary() {
736        let a = noise(BLOCK * 2 + 37, 1);
737        let b = noise(BLOCK + 3, 2);
738        let c = noise(BLOCK * 3, 3);
739        let srcs: [&[u8]; 3] = [&a, &b, &c];
740        let mut out = Vec::new();
741
742        let at = |s: &[u8], i: usize| s.get(i).copied().unwrap_or(0);
743        for op in [
744            Op::And,
745            Op::Or,
746            Op::Xor,
747            Op::Diff,
748            Op::Diff1,
749            Op::AndOr,
750            Op::One,
751        ] {
752            out.clear();
753            out.resize(width(srcs.iter().copied()), 0);
754            combine(op, srcs.iter().copied(), &mut out);
755            assert_eq!(out.len(), c.len(), "{}", op.name());
756            for (i, &got) in out.iter().enumerate() {
757                let (x, y, z) = (at(&a, i), at(&b, i), at(&c, i));
758                let want = match op {
759                    Op::And => x & y & z,
760                    Op::Or => x | y | z,
761                    Op::Xor => x ^ y ^ z,
762                    Op::Diff => x & !(y | z),
763                    Op::Diff1 => (y | z) & !x,
764                    Op::AndOr => x & (y | z),
765                    Op::One => (x & !y & !z) | (y & !x & !z) | (z & !x & !y),
766                    Op::Not => unreachable!(),
767                };
768                assert_eq!(got, want, "{} at byte {i}", op.name());
769            }
770        }
771    }
772
773    #[test]
774    fn an_operation_is_named_in_any_case() {
775        assert_eq!(Op::parse(b"AND"), Some(Op::And));
776        assert_eq!(Op::parse(b"diff1"), Some(Op::Diff1));
777        assert_eq!(Op::parse(b"AnDoR"), Some(Op::AndOr));
778        assert_eq!(Op::parse(b"nope"), None);
779        assert_eq!(Op::And.name(), "AND");
780    }
781
782    #[test]
783    fn a_field_type_is_a_letter_and_a_width() {
784        assert_eq!(Field::parse(b"u8").map(Field::bits), Some(8));
785        assert_eq!(Field::parse(b"i64").map(Field::signed), Some(true));
786        assert_eq!(Field::parse(b"u63").map(Field::bits), Some(63));
787        // The two Redis refuses, for the reason it refuses them: a u64 would
788        // not fit in the signed integer the reply is.
789        assert_eq!(Field::parse(b"u64"), None);
790        assert_eq!(Field::parse(b"i65"), None);
791        assert_eq!(Field::parse(b"u0"), None);
792        assert_eq!(Field::parse(b"x8"), None);
793        assert_eq!(Field::parse(b"u"), None);
794        assert_eq!(Field::parse(b""), None);
795        assert_eq!(Field::parse(b"u008"), None);
796    }
797
798    #[test]
799    fn a_field_knows_its_own_range() {
800        let f = |s, b| Field::new(s, b).expect("a width");
801        assert_eq!((f(false, 8).min(), f(false, 8).max()), (0, 255));
802        assert_eq!((f(true, 8).min(), f(true, 8).max()), (-128, 127));
803        assert_eq!((f(true, 1).min(), f(true, 1).max()), (-1, 0));
804        assert_eq!((f(false, 1).min(), f(false, 1).max()), (0, 1));
805        assert_eq!((f(true, 64).min(), f(true, 64).max()), (i64::MIN, i64::MAX));
806        assert_eq!((f(false, 63).min(), f(false, 63).max()), (0, i64::MAX));
807    }
808
809    #[test]
810    fn reading_and_writing_a_field_agrees_with_a_bit_loop() {
811        let mut bytes = noise(64, 3);
812        for bits in [1u32, 2, 7, 8, 9, 31, 32, 33, 63, 64] {
813            for signed in [true, false] {
814                let Some(f) = Field::new(signed, bits) else {
815                    continue;
816                };
817                for at in 0..64u64 {
818                    assert_eq!(get(&bytes, at, f), slow_get(&bytes, at, f), "{f:?} at {at}");
819                }
820            }
821        }
822        // A field that runs off the end reads the missing bytes as zeros, which
823        // is what `BITFIELD GET` past the end of a string answers.
824        let f = Field::new(false, 16).expect("a width");
825        assert_eq!(get(&[0xff], 0, f), 0xff00);
826        assert_eq!(get(&[], 0, f), 0);
827
828        // And a write comes back out, wherever it is put.
829        for bits in [1u32, 5, 8, 13, 32, 64] {
830            for signed in [true, false] {
831                let Some(f) = Field::new(signed, bits) else {
832                    continue;
833                };
834                for at in [0u64, 1, 7, 8, 63, 100] {
835                    // Both ends of the range, since an `i1` holds -1 and 0 and
836                    // nothing an ordinary loop over small numbers would try.
837                    for want in [f.min(), f.max()] {
838                        set(&mut bytes, at, f, want);
839                        assert_eq!(get(&bytes, at, f), want, "{f:?} at {at}");
840                    }
841                }
842            }
843        }
844    }
845
846    #[test]
847    fn a_write_leaves_the_bits_around_it_alone() {
848        let mut bytes = [0xffu8; 4];
849        let f = Field::new(false, 3).expect("a width");
850        set(&mut bytes, 5, f, 0);
851        assert_eq!(bytes, [0xf8, 0xff, 0xff, 0xff]);
852        set(&mut bytes, 29, f, 0);
853        assert_eq!(bytes, [0xf8, 0xff, 0xff, 0xf8]);
854    }
855
856    /// Every one of these was read off a running 8.10.1.
857    #[test]
858    fn overflow_is_what_a_real_server_does() {
859        let u8f = Field::new(false, 8).expect("a width");
860        let i8f = Field::new(true, 8).expect("a width");
861
862        assert_eq!(setting(u8f, 300, Overflow::Wrap), Some(44));
863        assert_eq!(setting(u8f, 300, Overflow::Sat), Some(255));
864        assert_eq!(setting(u8f, 300, Overflow::Fail), None);
865        // The negative into unsigned case, which is the one nobody guesses.
866        assert_eq!(setting(u8f, -5, Overflow::Wrap), Some(251));
867        assert_eq!(setting(u8f, -5, Overflow::Sat), Some(255));
868        assert_eq!(setting(u8f, -5, Overflow::Fail), None);
869        // Signed compares as signed, so it saturates at the near end.
870        assert_eq!(setting(i8f, -200, Overflow::Sat), Some(-128));
871        assert_eq!(setting(i8f, 200, Overflow::Sat), Some(127));
872        assert_eq!(setting(i8f, -200, Overflow::Wrap), Some(56));
873
874        assert_eq!(adding(u8f, 255, 10, Overflow::Wrap), Some(9));
875        assert_eq!(adding(u8f, 255, 250, Overflow::Sat), Some(255));
876        assert_eq!(adding(u8f, 255, 250, Overflow::Fail), None);
877        assert_eq!(adding(u8f, 0, -1000, Overflow::Sat), Some(0));
878        assert_eq!(adding(u8f, 0, -1, Overflow::Wrap), Some(255));
879
880        let i64f = Field::new(true, 64).expect("a width");
881        assert_eq!(adding(i64f, i64::MAX, 1, Overflow::Wrap), Some(i64::MIN));
882        assert_eq!(adding(i64f, i64::MAX, 1, Overflow::Sat), Some(i64::MAX));
883        assert_eq!(adding(i64f, i64::MIN, -1, Overflow::Sat), Some(i64::MIN));
884        assert_eq!(adding(i64f, i64::MIN, -1, Overflow::Fail), None);
885
886        let u1 = Field::new(false, 1).expect("a width");
887        assert_eq!(adding(u1, 1, 5, Overflow::Sat), Some(1));
888        assert_eq!(adding(u1, 1, 3, Overflow::Wrap), Some(0));
889    }
890
891    #[test]
892    fn an_overflow_word_is_read_in_any_case() {
893        assert_eq!(Overflow::parse(b"WRAP"), Some(Overflow::Wrap));
894        assert_eq!(Overflow::parse(b"sat"), Some(Overflow::Sat));
895        assert_eq!(Overflow::parse(b"Fail"), Some(Overflow::Fail));
896        assert_eq!(Overflow::parse(b"nope"), None);
897        assert_eq!(Overflow::default(), Overflow::Wrap);
898    }
899}