Skip to main content

yo_kv/
listpack.rs

1//! The inline band: one packed blob, walked linearly.
2//!
3//! A collection under a hundred and twenty eight elements is stored as one blob
4//! with no index, walked from the front. This is Redis's listpack, in Redis's
5//! bytes, and it is the bottom rung of the size ladder in `05` section 4.
6//!
7//! # Why, and it is not the reason the spec gives
8//!
9//! `05` section 4.1 argues for this band on speed, citing L6: a dense positional
10//! structure probes in about 70 ns where a listpack walk costs 1 to 2 ns, a fifty
11//! times gap. Half of that reproduces and half of it does not. A walk here does
12//! cost 1 to 2 ns an element, which is L6's number. A probe in our element table
13//! costs 8 ns and not 70, so the gap it was being compared against is not there,
14//! and at eight members the blob is ahead on both of them: 8.2 ns against 7.0 to
15//! find a member that is present, 4.2 against 8.5 to find one that is not, 0.4 ns
16//! an element against 1.3 to walk the whole thing, and 268 ns against 261 to
17//! build it. At a hundred and twenty eight the table is six times faster to probe
18//! and the gap only widens from there. `benches/listpack.rs` is where those come
19//! from.
20//!
21//! The find numbers used to be much worse for the blob and have been re-measured
22//! twice, once when the scan stopped decoding every element it walked past and
23//! again when it stopped waiting for the header byte to work out where the next
24//! element starts. Both are written up on `scan_for` below. Neither changes the
25//! conclusion, because the conclusion never rested on them.
26//!
27//! What the band is actually for is memory, and there the gap is real and the
28//! other way round. With an eleven byte member the blob costs 13.1 bytes an
29//! element and the table costs 31.0, because the table pays twelve bytes of row
30//! and about eight of slot on top of the name while the blob pays an encoding
31//! byte and a back length. G8 asks for a set member to cost under three bytes
32//! plus its payload. The blob comes in at 2.1 and the table at 20. A server
33//! holding a million small hashes is holding them here or it is not holding them.
34//!
35//! The threshold is not ours to move anyway. `OBJECT ENCODING` has to say
36//! `listpack` for exactly the collections Redis says it for, so the promotion
37//! points are `hash-max-listpack-entries` and its neighbours whatever we would
38//! have picked. Worth knowing which argument is load bearing, though, because the
39//! speed one would have sent us looking for a faster walk and the real one sends
40//! us to the arena.
41//!
42//! It is byte compatible with a Redis listpack, not merely similar in spirit.
43//! `05` section 4.1 asks for that so an RDB export is a copy rather than a
44//! transcode, and it means the encodings, the header, the terminator and the
45//! back length are all Redis's. Every boundary here was read off `listpack.c`
46//! from the 8.10.1 tarball, which is the same version `yo-compat` pins, and the
47//! ones that are easy to get a byte wrong are pinned in the tests with Redis's
48//! own numbers written out.
49//!
50//! ```text
51//! +---------+--------+---------+-----+---------+------+
52//! | u32 len | u16 n  | entry 0 | ... | entry k | 0xFF |
53//! +---------+--------+---------+-----+---------+------+
54//!   total bytes, header included         terminator
55//! ```
56//!
57//! An entry is an encoding byte, then its payload, then a back length, and the
58//! back length is what makes the walk work in both directions. A forward walk
59//! reads the encoding and steps over the payload. A backward walk reads the back
60//! length from its last byte leftward and steps over the whole entry, which is
61//! how `SPOP` reaches the end of a blob without walking it from the front, and
62//! how the downward scan cursor in [`crate::scan`] works in this band.
63//!
64//! # What is here and what is not
65//!
66//! Everything a collection needs to hold its elements: append, read by position,
67//! find, replace, insert and delete, all of them working on the blob in place.
68//! Every one of them is linear in the number of elements, on purpose, because
69//! the band is bounded and an index would cost more than it saved.
70//!
71//! Not the promotion policy. When a collection stops being small is a decision
72//! for the collection, since Redis makes it configurable per type and the
73//! thresholds have to keep matching `hash-max-listpack-entries` and its
74//! neighbours. This module holds elements and says how many bytes they cost.
75//!
76//! # The element codec is shared
77//!
78//! [`crate::chunk`] holds the same entries in a run with a cursor at each end
79//! rather than in a blob with a header, so it needs the encoding and not the
80//! container. `entry_len`, `write_entry`, `decode` and `read_backlen`
81//! are `pub(crate)` for that, and they are the only things a second holder of
82//! these bytes needs. Two copies of the fourteen encodings is how a list and a
83//! set end up disagreeing about what `SADD s 1` stored.
84
85use yo_common::{parse_i64, push_i64};
86
87/// Header is four bytes of total length and two of element count.
88const HDR: usize = 6;
89
90/// The terminator, which is also the encoding byte that means end.
91const END: u8 = 0xFF;
92
93/// What the element count field holds when the real count does not fit.
94///
95/// A collection in this band holds a hundred and twenty eight elements, so this
96/// never comes from us. It comes from a listpack somebody else wrote, and the
97/// answer to it is to walk and count.
98const COUNT_UNKNOWN: u16 = 65535;
99
100/// The largest count that fits in the field.
101const COUNT_MAX: usize = 65534;
102
103/// A packed blob of elements.
104///
105/// Owns its bytes today. When the arena lands under it the bytes move there and
106/// this becomes a view, which is why nothing here hands out a `Vec` or takes
107/// one back.
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct Listpack {
110    bytes: Vec<u8>,
111}
112
113/// One element, as it is stored.
114///
115/// Redis stores a member that looks like an integer as an integer, so `SADD s 1`
116/// and `SADD s 01` are two different members that are stored two different ways.
117/// Handing back which one it was is what lets a caller answer `OBJECT ENCODING`
118/// and write an RDB without re-deciding it.
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum Entry<'a> {
121    /// Stored as an integer, in one of the six integer encodings.
122    Int(i64),
123    /// Stored as bytes.
124    Str(&'a [u8]),
125}
126
127impl Entry<'_> {
128    /// The element as a client would see it, appended to `out`.
129    ///
130    /// An integer entry is formatted here, which is the same round trip Redis
131    /// does on the way out, because the client asked for a member and members
132    /// are strings on the wire.
133    pub fn write_to(&self, out: &mut Vec<u8>) {
134        match self {
135            Entry::Int(n) => push_i64(out, *n),
136            Entry::Str(s) => out.extend_from_slice(s),
137        }
138    }
139
140    /// How many bytes a client would see, without formatting anything.
141    ///
142    /// This is `HSTRLEN` and `STRLEN`, which both have to answer for a value
143    /// stored as an integer, and neither of them should have to write the digits
144    /// out to count them.
145    #[must_use]
146    #[inline]
147    pub fn byte_len(&self) -> usize {
148        match self {
149            Entry::Int(n) => yo_common::num::i64_len(*n),
150            Entry::Str(s) => s.len(),
151        }
152    }
153
154    /// The element as bytes, allocating only for an integer.
155    #[must_use]
156    pub fn to_vec(&self) -> Vec<u8> {
157        let mut out = Vec::new();
158        self.write_to(&mut out);
159        out
160    }
161}
162
163impl Default for Listpack {
164    fn default() -> Listpack {
165        Listpack::new()
166    }
167}
168
169impl Listpack {
170    /// An empty blob, which is a header and a terminator and nothing else.
171    #[must_use]
172    pub fn new() -> Listpack {
173        let mut bytes = Vec::with_capacity(HDR + 1 + 64);
174        bytes.extend_from_slice(&[0, 0, 0, 0, 0, 0, END]);
175        let mut lp = Listpack { bytes };
176        lp.set_total(HDR + 1);
177        lp
178    }
179
180    /// Take bytes somebody else wrote, after checking them.
181    ///
182    /// An RDB, a `.yo` file and a `RESTORE` all arrive this way, so the walk is
183    /// not optional. A blob that does not check out is refused whole rather than
184    /// read up to the bad entry, because half a collection is worse than none.
185    pub fn from_bytes(bytes: &[u8]) -> Result<Listpack, Malformed> {
186        if bytes.len() < HDR + 1 {
187            return Err(Malformed::Short);
188        }
189        let total = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;
190        if total != bytes.len() {
191            return Err(Malformed::Length);
192        }
193        if bytes[total - 1] != END {
194            return Err(Malformed::Terminator);
195        }
196        // Walk it. Every entry has to decode, its back length has to agree with
197        // how long the entry actually was, and the last one has to land exactly
198        // on the terminator rather than past it.
199        let mut at = HDR;
200        let mut seen = 0usize;
201        while at < total - 1 {
202            let (_, len) = decode(&bytes[at..total - 1]).ok_or(Malformed::Entry)?;
203            let back = backlen_len(len);
204            if at + len + back > total - 1 {
205                return Err(Malformed::Entry);
206            }
207            if read_backlen(&bytes[..at + len + back]) != Some(len) {
208                return Err(Malformed::BackLength);
209            }
210            at += len + back;
211            seen += 1;
212        }
213        let count = u16::from_le_bytes([bytes[4], bytes[5]]);
214        if count != COUNT_UNKNOWN && count as usize != seen {
215            return Err(Malformed::Count);
216        }
217        Ok(Listpack {
218            bytes: bytes.to_vec(),
219        })
220    }
221
222    /// The bytes, ready to be written to a file or an RDB unchanged.
223    #[inline]
224    #[must_use]
225    pub fn as_bytes(&self) -> &[u8] {
226        &self.bytes
227    }
228
229    /// The entry region on its own, without the header or the terminator.
230    ///
231    /// [`crate::chunk`] holds entries in exactly this encoding, so promoting a
232    /// list out of the packed band is one copy of this slice rather than a walk
233    /// that re-encodes every element.
234    #[inline]
235    #[must_use]
236    pub(crate) fn entries(&self) -> &[u8] {
237        &self.bytes[HDR..self.bytes.len() - 1]
238    }
239
240    /// How many elements.
241    ///
242    /// The header answers this, which is why it is here and why it is kept
243    /// right on every edit. A blob from elsewhere with an unknown count is
244    /// walked instead, once, rather than being rejected.
245    #[must_use]
246    pub fn len(&self) -> usize {
247        let count = u16::from_le_bytes([self.bytes[4], self.bytes[5]]);
248        if count == COUNT_UNKNOWN {
249            self.iter().count()
250        } else {
251            count as usize
252        }
253    }
254
255    /// Whether there is nothing in it, which for Redis means it does not exist.
256    #[inline]
257    #[must_use]
258    pub fn is_empty(&self) -> bool {
259        self.bytes.len() == HDR + 1
260    }
261
262    /// What the blob costs, which is what it costs on disk too.
263    #[inline]
264    #[must_use]
265    pub fn byte_len(&self) -> usize {
266        self.bytes.len()
267    }
268
269    /// Every element, front to back.
270    pub fn iter(&self) -> Iter<'_> {
271        Iter {
272            bytes: &self.bytes,
273            at: HDR,
274        }
275    }
276
277    /// The element at a position, counting from the front.
278    ///
279    /// Linear, because the blob is linear. That is the whole design: at a
280    /// hundred and twenty eight elements the walk is cheaper than the index that
281    /// would have avoided it. From whichever end is nearer, though, because a
282    /// list in this band holds eight kilobytes and that is four hundred odd
283    /// entries rather than a hundred and twenty eight, and `LINDEX key -1` on
284    /// one of those should not read all of it.
285    #[must_use]
286    pub fn get(&self, index: usize) -> Option<Entry<'_>> {
287        let at = self.offset_of(index)?;
288        decode(&self.bytes[at..self.bytes.len() - 1]).map(|(e, _)| e)
289    }
290
291    /// A forward walk that starts at `index` rather than at the front.
292    ///
293    /// `LRANGE key 300 320` on a packed list would otherwise decode three
294    /// hundred entries and throw them away, which is what a `skip` on the walk
295    /// does.
296    pub fn iter_from(&self, index: usize) -> Iter<'_> {
297        Iter {
298            bytes: &self.bytes,
299            at: self
300                .offset_of(index)
301                .unwrap_or(self.bytes.len().saturating_sub(1)),
302        }
303    }
304
305    /// Every element, back to front.
306    ///
307    /// The trailing length on each entry is what makes this cost the same per
308    /// element as the forward walk. `LPOS` with a negative rank counts matches
309    /// from the tail and stops when it has enough, so walking forward and
310    /// keeping the answers would be the wrong shape as well as the wrong cost.
311    pub fn iter_back(&self) -> RevIter<'_> {
312        let entries = self.entries();
313        RevIter {
314            bytes: entries,
315            at: entries.len(),
316        }
317    }
318
319    /// The element at a position, counting from the back.
320    ///
321    /// Reads the back length of the last entry and steps left, which is what the
322    /// trailing length field is for and why a `RPOP` on a small list does not
323    /// walk the whole blob.
324    #[must_use]
325    pub fn get_back(&self, from_end: usize) -> Option<Entry<'_>> {
326        let mut end = self.bytes.len() - 1;
327        for _ in 0..=from_end {
328            // Stepping onto the header means the walk asked for more elements
329            // than are here. Without this the header's own bytes decode as an
330            // entry and the answer is nonsense rather than nothing.
331            if end <= HDR {
332                return None;
333            }
334            let len = read_backlen(&self.bytes[..end])?;
335            end = end.checked_sub(len + backlen_len(len))?;
336        }
337        if end < HDR {
338            return None;
339        }
340        decode(&self.bytes[end..self.bytes.len() - 1]).map(|(e, _)| e)
341    }
342
343    /// Where an element is, or nothing.
344    ///
345    /// `step` is what makes this work for a hash. A hash in this band is field,
346    /// value, field, value, so a field lookup is a find with a step of two, which
347    /// is the same trick Redis's `lpFind` plays and the reason a hash does not
348    /// need a second structure down here.
349    #[must_use]
350    pub fn find(&self, needle: &[u8], step: usize) -> Option<usize> {
351        self.find_parsed(needle, parse_i64(needle), step)
352    }
353
354    /// The same walk with the needle already parsed.
355    ///
356    /// Set algebra asks one member of one set about every other set, so the
357    /// parse would otherwise happen once per question about the same bytes. It
358    /// is also the only form that can answer about a member which was never
359    /// text: an intset holds the number and the digits do not exist anywhere
360    /// until somebody writes them.
361    #[must_use]
362    pub fn find_parsed(&self, needle: &[u8], as_int: Option<i64>, step: usize) -> Option<usize> {
363        scan_for(self.entries(), needle, as_int, step)
364    }
365
366    /// Every place an element is, front to back, handed over as they are found.
367    ///
368    /// `limit` is how many elements may be looked at with 0 meaning all of them,
369    /// `hit` says whether to carry on, and what comes back is how many elements
370    /// were looked at. The walk itself is `scan_each` below.
371    pub fn find_each(
372        &self,
373        needle: &[u8],
374        as_int: Option<i64>,
375        limit: usize,
376        hit: &mut dyn FnMut(usize) -> bool,
377    ) -> usize {
378        scan_each(self.entries(), needle, as_int, limit, hit)
379    }
380
381    /// The same from the back, with indexes counted from the last element.
382    pub fn find_each_back(
383        &self,
384        needle: &[u8],
385        as_int: Option<i64>,
386        limit: usize,
387        hit: &mut dyn FnMut(usize) -> bool,
388    ) -> usize {
389        scan_each_back(self.entries(), needle, as_int, limit, hit)
390    }
391
392    /// Add an element at the end.
393    pub fn push(&mut self, value: &[u8]) {
394        let at = self.bytes.len() - 1;
395        self.splice(at, 0, Some(value), 1);
396    }
397
398    /// Put an element in front of the one at `index`.
399    ///
400    /// An index at or past the end appends, which is what a sorted insert wants
401    /// when the new element sorts last and saves the caller a branch.
402    pub fn insert(&mut self, index: usize, value: &[u8]) {
403        let at = self.offset_of(index).unwrap_or(self.bytes.len() - 1);
404        self.splice(at, 0, Some(value), 1);
405    }
406
407    /// Overwrite the element at `index`, keeping its position.
408    ///
409    /// `HSET` on a field that is already there, and `ZADD` on a member whose
410    /// score has changed but whose place has not.
411    pub fn replace(&mut self, index: usize, value: &[u8]) -> bool {
412        let Some(at) = self.offset_of(index) else {
413            return false;
414        };
415        let old = self.entry_bytes(at);
416        self.splice(at, old, Some(value), 0);
417        true
418    }
419
420    /// Take out `count` elements starting at `index`.
421    ///
422    /// `HDEL` takes two, a field and its value, and it has to take them as one
423    /// edit or the blob is briefly a hash with an odd number of entries.
424    pub fn delete(&mut self, index: usize, count: usize) -> bool {
425        let Some(at) = self.offset_of(index) else {
426            return false;
427        };
428        let mut end = at;
429        let mut gone = 0usize;
430        while gone < count && end < self.bytes.len() - 1 {
431            end += self.entry_bytes(end);
432            gone += 1;
433        }
434        if gone == 0 {
435            return false;
436        }
437        self.splice(at, end - at, None, -(gone as i32));
438        true
439    }
440
441    /// Byte offset of the element at `index`, or nothing if it is past the end.
442    ///
443    /// Forward from the header or backward from the terminator, whichever is
444    /// the shorter walk. Going backward reads the length each entry carries
445    /// behind it, which is the same field [`Listpack::get_back`] reads and the
446    /// reason that field is there. A list in the packed band is eight kilobytes
447    /// and four hundred odd entries, not a hundred and twenty eight like the
448    /// other packed bands, so the half that this saves is worth having.
449    ///
450    /// [`Listpack::len`] is a header read at every size this crate builds: the
451    /// count field only stops being the count past sixty five thousand entries
452    /// and no band here comes close.
453    fn offset_of(&self, index: usize) -> Option<usize> {
454        let n = self.len();
455        if index >= n {
456            return None;
457        }
458        if index * 2 <= n {
459            let mut at = HDR;
460            for _ in 0..index {
461                at += self.entry_bytes(at);
462            }
463            return Some(at);
464        }
465        let mut end = self.bytes.len() - 1;
466        for _ in index..n {
467            let len = read_backlen(&self.bytes[..end])?;
468            end = end.checked_sub(len + backlen_len(len))?;
469        }
470        Some(end)
471    }
472
473    /// How many bytes the entry at `at` occupies, back length included.
474    fn entry_bytes(&self, at: usize) -> usize {
475        let (_, len) = decode(&self.bytes[at..self.bytes.len() - 1]).expect("our own blob decodes");
476        len + backlen_len(len)
477    }
478
479    /// The one edit primitive: drop some bytes, put some back, fix the header.
480    ///
481    /// Everything that changes the blob goes through here, so there is one place
482    /// that can leave the length or the count wrong.
483    ///
484    /// It writes the new entry into the blob rather than building it in a `Vec`
485    /// and handing that to `Vec::splice`. The `Vec` was a malloc and a free on
486    /// every `RPUSH`, `LPUSH`, `HSET`, `SADD` and `ZADD` that landed in the
487    /// packed band, which is most of them, for a buffer of a few dozen bytes
488    /// that never outlived the call. Making the hole first and then writing the
489    /// head, the payload and the back length straight into it costs one
490    /// `copy_within` of the tail, which `Vec::splice` was doing as well as the
491    /// allocation.
492    fn splice(&mut self, at: usize, remove: usize, insert: Option<&[u8]>, delta: i32) {
493        let mut buf = [0u8; 16];
494        // Both halves of the new entry, measured before anything moves. An
495        // integer entry is entirely in its head and has no payload, which is
496        // what `encode` says with its second answer.
497        let (head, body) = match insert {
498            Some(v) => {
499                let (head, payload) = encode(v, &mut buf);
500                (head, if payload { v } else { &[][..] })
501            }
502            None => (&[][..], &[][..]),
503        };
504        let entry = head.len() + body.len();
505        // A pure removal puts nothing back, so it has no back length either.
506        let add = if entry == 0 {
507            0
508        } else {
509            entry + backlen_len(entry)
510        };
511
512        // Size the hole before writing into it. Growing moves the tail rightward
513        // and shrinking moves it leftward, and `copy_within` is a `memmove` in
514        // both directions, so an overlap reads what it should either way.
515        let old = self.bytes.len();
516        match add.cmp(&remove) {
517            std::cmp::Ordering::Greater => {
518                self.bytes.resize(old + (add - remove), 0);
519                self.bytes.copy_within(at + remove..old, at + add);
520            }
521            std::cmp::Ordering::Less => {
522                self.bytes.copy_within(at + remove..old, at + add);
523                self.bytes.truncate(old - (remove - add));
524            }
525            std::cmp::Ordering::Equal => {}
526        }
527        if add > 0 {
528            let hole = &mut self.bytes[at..at + add];
529            hole[..head.len()].copy_from_slice(head);
530            hole[head.len()..entry].copy_from_slice(body);
531            write_backlen_into(&mut hole[entry..], entry);
532        }
533
534        let total = self.bytes.len();
535        self.set_total(total);
536        let count = i64::from(u16::from_le_bytes([self.bytes[4], self.bytes[5]]));
537        let count = usize::try_from(count + i64::from(delta)).unwrap_or(0);
538        let count = u16::try_from(count.min(COUNT_MAX)).expect("clamped to the field");
539        self.bytes[4..6].copy_from_slice(&count.to_le_bytes());
540    }
541
542    /// Write the total length into the header.
543    fn set_total(&mut self, total: usize) {
544        let total = u32::try_from(total).expect("the inline band is far under 4 GiB");
545        self.bytes[0..4].copy_from_slice(&total.to_le_bytes());
546    }
547}
548
549/// A forward walk.
550///
551/// Cloneable because a stream node holds several logical records inside one
552/// blob and a reader has to be able to keep a mark on where a record's fields
553/// started while it walks on to find where the record ends. Cloning one is
554/// copying a slice and an offset.
555#[derive(Debug, Clone)]
556pub struct Iter<'a> {
557    bytes: &'a [u8],
558    at: usize,
559}
560
561impl<'a> Iterator for Iter<'a> {
562    type Item = Entry<'a>;
563
564    #[inline]
565    fn next(&mut self) -> Option<Entry<'a>> {
566        if self.at >= self.bytes.len() - 1 {
567            return None;
568        }
569        let (entry, len) = decode(&self.bytes[self.at..self.bytes.len() - 1])?;
570        self.at += len + backlen_len(len);
571        Some(entry)
572    }
573}
574
575/// A backward walk.
576#[derive(Debug, Clone)]
577pub struct RevIter<'a> {
578    bytes: &'a [u8],
579    at: usize,
580}
581
582impl<'a> Iterator for RevIter<'a> {
583    type Item = Entry<'a>;
584
585    #[inline]
586    fn next(&mut self) -> Option<Entry<'a>> {
587        if self.at == 0 {
588            return None;
589        }
590        let len = read_backlen(&self.bytes[..self.at])?;
591        let start = self.at.checked_sub(len + backlen_len(len))?;
592        let (entry, _) = decode(&self.bytes[start..self.at])?;
593        self.at = start;
594        Some(entry)
595    }
596}
597
598/// Why a blob from somewhere else was refused.
599#[derive(Debug, Clone, Copy, PartialEq, Eq)]
600pub enum Malformed {
601    /// Shorter than an empty listpack.
602    Short,
603    /// The header's total length is not the length of what arrived.
604    Length,
605    /// It does not end in a terminator.
606    Terminator,
607    /// An entry's encoding is not one of the fourteen.
608    Entry,
609    /// An entry's back length disagrees with how long the entry is.
610    BackLength,
611    /// The header's element count is not how many elements are in it.
612    Count,
613}
614
615/// The encoding bytes for an element, written into `buf`.
616///
617/// Redis's fourteen encodings, and the choice between them is the same one
618/// `lpEncodeGetType` makes: an element that parses as an integer is stored as
619/// one, in the narrowest form that holds it, and everything else is stored as
620/// bytes with a length that is six, twelve or thirty two bits wide.
621///
622/// The flag says whether the element's own bytes follow the encoding. An integer
623/// is entirely inside its encoding, and that is where the memory target in G8
624/// comes from: a set of small integers costs two bytes an element, encoding and
625/// back length, with nothing else stored at all.
626fn encode<'b>(v: &[u8], buf: &'b mut [u8; 16]) -> (&'b [u8], bool) {
627    if let Some(n) = parse_i64(v) {
628        let head: &[u8] = match n {
629            0..=127 => {
630                buf[0] = n as u8;
631                &buf[..1]
632            }
633            -4096..=4095 => {
634                let u = (n as u16) & 0x1FFF;
635                buf[0] = 0xC0 | (u >> 8) as u8;
636                buf[1] = (u & 0xFF) as u8;
637                &buf[..2]
638            }
639            -32768..=32767 => {
640                buf[0] = 0xF1;
641                buf[1..3].copy_from_slice(&(n as i16).to_le_bytes());
642                &buf[..3]
643            }
644            -8_388_608..=8_388_607 => {
645                buf[0] = 0xF2;
646                buf[1..4].copy_from_slice(&(n as i32).to_le_bytes()[..3]);
647                &buf[..4]
648            }
649            -2_147_483_648..=2_147_483_647 => {
650                buf[0] = 0xF3;
651                buf[1..5].copy_from_slice(&(n as i32).to_le_bytes());
652                &buf[..5]
653            }
654            _ => {
655                buf[0] = 0xF4;
656                buf[1..9].copy_from_slice(&n.to_le_bytes());
657                &buf[..9]
658            }
659        };
660        return (head, false);
661    }
662    let head: &[u8] = match v.len() {
663        0..=63 => {
664            buf[0] = 0x80 | v.len() as u8;
665            &buf[..1]
666        }
667        64..=4095 => {
668            buf[0] = 0xE0 | (v.len() >> 8) as u8;
669            buf[1] = (v.len() & 0xFF) as u8;
670            &buf[..2]
671        }
672        _ => {
673            buf[0] = 0xF0;
674            buf[1..5].copy_from_slice(&(v.len() as u32).to_le_bytes());
675            &buf[..5]
676        }
677    };
678    (head, true)
679}
680
681/// How many bytes an entry for `v` takes, its back length included.
682///
683/// The chunk has to know before it writes, because a chunk that runs out of
684/// room halfway through an entry has no way to put itself back.
685#[inline]
686pub(crate) fn entry_len(v: &[u8]) -> usize {
687    let mut buf = [0u8; 16];
688    let (head, payload) = encode(v, &mut buf);
689    let len = head.len() + if payload { v.len() } else { 0 };
690    len + backlen_len(len)
691}
692
693/// Write one entry into `dst`, and say how many bytes it took.
694///
695/// `dst` must be at least [`entry_len`] long, which every caller knows because
696/// it asked first.
697#[inline]
698pub(crate) fn write_entry(dst: &mut [u8], v: &[u8]) -> usize {
699    let mut buf = [0u8; 16];
700    let (head, payload) = encode(v, &mut buf);
701    dst[..head.len()].copy_from_slice(head);
702    let mut at = head.len();
703    if payload {
704        dst[at..at + v.len()].copy_from_slice(v);
705        at += v.len();
706    }
707    at + write_backlen_into(&mut dst[at..], at)
708}
709
710/// Read one entry, and say how many bytes it took before its back length.
711///
712/// Inlined on purpose. It hands back a fat enum and a length, and left out of
713/// line that pair goes through memory once per element, which is most of what a
714/// walk costs.
715#[inline]
716pub(crate) fn decode(b: &[u8]) -> Option<(Entry<'_>, usize)> {
717    let first = *b.first()?;
718    // A string encoding's payload starts after its length, so both arms below
719    // hand back the same pair and the caller does not care which it was.
720    let (at, len) = match first {
721        0x00..=0x7F => return Some((Entry::Int(i64::from(first)), 1)),
722        0x80..=0xBF => (1, (first & 0x3F) as usize),
723        0xC0..=0xDF => {
724            let raw = (u16::from(first & 0x1F) << 8) | u16::from(*b.get(1)?);
725            // Thirteen bits, signed, so the top bit of the thirteen is the sign.
726            let n = if raw & 0x1000 != 0 {
727                i64::from(raw) - 8192
728            } else {
729                i64::from(raw)
730            };
731            return Some((Entry::Int(n), 2));
732        }
733        0xE0..=0xEF => {
734            let lo = *b.get(1)?;
735            (2, (usize::from(first & 0x0F) << 8) | usize::from(lo))
736        }
737        0xF0 => {
738            let n = u32::from_le_bytes([*b.get(1)?, *b.get(2)?, *b.get(3)?, *b.get(4)?]);
739            (5, n as usize)
740        }
741        0xF1 => {
742            let n = i16::from_le_bytes([*b.get(1)?, *b.get(2)?]);
743            return Some((Entry::Int(i64::from(n)), 3));
744        }
745        0xF2 => {
746            // Twenty four bits, sign extended by putting them in the top of a
747            // thirty two bit word and shifting back down.
748            let n = i32::from_le_bytes([0, *b.get(1)?, *b.get(2)?, *b.get(3)?]) >> 8;
749            return Some((Entry::Int(i64::from(n)), 4));
750        }
751        0xF3 => {
752            let n = i32::from_le_bytes([*b.get(1)?, *b.get(2)?, *b.get(3)?, *b.get(4)?]);
753            return Some((Entry::Int(i64::from(n)), 5));
754        }
755        0xF4 => {
756            let mut w = [0u8; 8];
757            w.copy_from_slice(b.get(1..9)?);
758            return Some((Entry::Int(i64::from_le_bytes(w)), 9));
759        }
760        // 0xF5 to 0xFE are unused by Redis, and 0xFF is the terminator, which
761        // the caller has already stopped before.
762        _ => return None,
763    };
764    let s = b.get(at..at + len)?;
765    Some((Entry::Str(s), at + len))
766}
767
768/// Eight bytes of `s` starting at `at`, as a number.
769///
770/// The caller has already checked that they are there, so the `try_into` cannot
771/// fail and the compiler knows it, which is what keeps this to one unaligned
772/// load on every target this runs on.
773#[inline(always)]
774fn word(s: &[u8], at: usize) -> u64 {
775    u64::from_le_bytes(s[at..at + 8].try_into().expect("eight bytes"))
776}
777
778/// The needle of a scan, with everything that does not change worked out once.
779///
780/// This exists because of what the generated code looked like without it. The
781/// comparison started as a length check, a first byte check and then `a == b`,
782/// which is a call to `memcmp`, and on the workload that actually matters none
783/// of the first two filter anything: list elements are overwhelmingly a fixed
784/// shape with a varying tail, so a million of them called `element:00000000`
785/// through `element:00999999` are all the same length and all start with the
786/// same letter. Every element paid for the call.
787///
788/// Comparing whole words instead fixes that, and eight bytes from each end
789/// covers any length up to sixteen exactly, because at that length the two
790/// windows overlap and between them cover the whole value. Longer than sixteen
791/// and the two words are a filter in front of the call rather than a
792/// replacement for it, which is fine, since a value agreeing on both ends and
793/// differing in the middle is rare enough to be worth a `memcmp` when it turns
794/// up.
795///
796/// Working the two words out here rather than in the loop is not a
797/// micro-optimisation, it is two loads an element. Left inline the compiler
798/// reloads them from the needle every time round, because the calls further down
799/// the body could have written to it as far as it knows, and it has no way to
800/// prove otherwise.
801struct Needle<'a> {
802    bytes: &'a [u8],
803    /// The length, which is the first thing every element is rejected on.
804    len: usize,
805    /// The first and last eight bytes, both zero and never looked at when the
806    /// value is shorter than eight bytes.
807    head: u64,
808    tail: u64,
809    /// What the value is as a number, if it is one. An element stored under an
810    /// integer encoding can only match this.
811    num: Option<i64>,
812}
813
814impl<'a> Needle<'a> {
815    fn new(bytes: &'a [u8], num: Option<i64>) -> Needle<'a> {
816        let len = bytes.len();
817        let wide = len >= 8;
818        Needle {
819            bytes,
820            len,
821            head: if wide { word(bytes, 0) } else { 0 },
822            tail: if wide { word(bytes, len - 8) } else { 0 },
823            num,
824        }
825    }
826
827    /// Whether a string payload of the same length is this value.
828    ///
829    /// `always` rather than `inline`, and it is worth saying why, because this
830    /// is the difference between three and a half nanoseconds an element and one
831    /// and a half. Left to its own judgement the compiler kept this out of line,
832    /// so the scan below made a call per element, spilled around it, and paid
833    /// more to set the arguments up than the comparison itself costs.
834    ///
835    /// Every length here is taken from `p` and not from the needle, which reads
836    /// like a pointless difference and is not. The caller has already checked
837    /// that they are equal, but nothing in the types says so, so a bound written
838    /// in terms of the needle is a bound the compiler has to check against `p`
839    /// all over again, and it emitted a second comparison and a panic landing pad
840    /// on the hot path to do it. Written this way the check that lets the first
841    /// word be read is the same check that says the value is long enough to have
842    /// two words at all.
843    #[inline(always)]
844    fn is(&self, p: &[u8]) -> bool {
845        debug_assert_eq!(p.len(), self.len, "the caller checks the length first");
846        let n = p.len();
847        if n < 8 {
848            return p == self.bytes;
849        }
850        word(p, 0) == self.head
851            && word(p, n - 8) == self.tail
852            && (n <= 16 || p[8..n - 8] == self.bytes[8..n - 8])
853    }
854}
855
856/// Walk a run of entries looking for `needle`, and say which one it was.
857///
858/// `b` is entries and nothing else, which is what a chunk holds and what a
859/// listpack holds between its header and its terminator, so both callers get the
860/// same walk. `step` is the hash trick: a field lookup over field, value, field,
861/// value is a find with a step of two.
862///
863/// # Why this is not the obvious walk
864///
865/// The obvious walk is `self.iter().position(|e| e.is(needle, as_int))`, which
866/// is what this was, and it costs about 6.7 nanoseconds an element. That is
867/// twenty seven cycles to answer "are these bytes those bytes", and almost none
868/// of it is the comparison. Every step ran the whole of [`decode`], which is a
869/// fourteen way match with a bounds checked read per header byte, built an
870/// [`Entry`] out of what it found, handed that back through the iterator, and
871/// only then compared. On a million element `LINSERT` that is three and a third
872/// milliseconds of work to reach an insert that takes two hundred and eighty
873/// five nanoseconds.
874///
875/// So the walk below never builds an `Entry` and never reads a payload it is not
876/// about to compare. The header alone says how long the entry is, the length
877/// rejects most elements with one comparison, and what gets past that is
878/// compared as two words rather than as a call. See [`Needle`] for that half of
879/// it.
880///
881/// The integer arm is the rare one and it is deliberately left to `decode`. It
882/// is four sign extensions of different widths, getting one of them subtly wrong
883/// is exactly the kind of bug that hides for a year, and having a second copy of
884/// them here to save a call on a path that is cold in every list workload is a
885/// bad trade.
886///
887/// # What was left on the table by the first version of that
888///
889/// The walk above got to about 2.3 nanoseconds an element and stopped, and it
890/// stopped there because of one instruction. Stepping to the next element is
891/// `at += len + 2`, `len` comes out of the encoding byte that was just loaded,
892/// and the loaded value is therefore in the way of working out where the next
893/// load goes. Every element paid a load latency plus the arithmetic stacked on
894/// it before the element after it could start, which is about eight cycles, and
895/// nothing else in the loop mattered because everything else could run while that
896/// chain was resolving. It is not a throughput problem and no amount of removing
897/// instructions from the body would have touched it.
898///
899/// The way out is that on the path that matters the length is already known. An
900/// element is only compared when its length equals the needle's, and the needle's
901/// length has been in a register since before the loop started, so on that path
902/// the step can be written in terms of the needle instead of in terms of the
903/// byte that was just read. The next element's address is then worked out while
904/// this one is still being compared, and the loop halves to about four cycles an
905/// element. That is why the length test is a branch of its own below rather than
906/// the first half of the comparison, which is the shape it had and reads more
907/// naturally.
908///
909/// It only pays when the lengths do match, and on a list they either all match
910/// or none of them do, which is the same property the two word comparison in
911/// [`Needle`] leans on. A million element `LINSERT` went from 1.17 ms to 456 us
912/// on it and the bare search from 2.37 ms to 904 us, both on an M4.
913#[inline]
914pub(crate) fn scan_for(b: &[u8], needle: &[u8], as_int: Option<i64>, step: usize) -> Option<usize> {
915    let mut got = None;
916    let needle = Needle::new(needle, as_int);
917    // Four walks out of one body, and both const parameters earn their keep the
918    // same way. A list finds by stepping over every element and a hash steps
919    // over every other one, and carrying a counter for a step that is always one
920    // costs four instructions and a branch on the hottest loop in `LINSERT`.
921    // `LIMITED` is the same argument for `LPOS`'s `MAXLEN`, which every other
922    // caller passes as no limit at all. As constants both fold away entirely.
923    if step <= 1 {
924        walk::<true, false, _>(b, &needle, 1, 0, &mut |at| {
925            got = Some(at);
926            false
927        });
928    } else {
929        walk::<false, false, _>(b, &needle, step, 0, &mut |at| {
930            got = Some(at);
931            false
932        });
933    }
934    got
935}
936
937/// Walk a run of entries handing back every `needle` in it, front to back.
938///
939/// This is [`scan_for`] without the stop on the first one, which is `LPOS` and
940/// `LREM` rather than `LINSERT`. `hit` is given each index as it is found and
941/// says whether to keep going, so a `COUNT` stops the walk where it is reached
942/// rather than after reading the rest of the list. `limit` is how many elements
943/// may be looked at, with 0 meaning no limit, which is `MAXLEN`.
944///
945/// What comes back is how many elements were looked at, which is what a caller
946/// spanning several runs of entries needs in order to carry one `MAXLEN` budget
947/// across all of them.
948#[inline]
949pub(crate) fn scan_each(
950    b: &[u8],
951    needle: &[u8],
952    as_int: Option<i64>,
953    limit: usize,
954    mut hit: &mut dyn FnMut(usize) -> bool,
955) -> usize {
956    let needle = Needle::new(needle, as_int);
957    if limit == 0 {
958        walk::<true, false, _>(b, &needle, 1, 0, &mut hit)
959    } else {
960        walk::<true, true, _>(b, &needle, 1, limit, &mut hit)
961    }
962}
963
964/// The same walk from the other end, with indexes counted from the back.
965///
966/// The index handed to `hit` is 0 for the last element, 1 for the one before
967/// it and so on, because this does not know how many elements are in front of
968/// the run it was given and the caller does. `LPOS` with a negative rank is the
969/// only thing that wants this, and it wants it because a rank of -1 has to find
970/// the last match without reading past it.
971#[inline]
972pub(crate) fn scan_each_back(
973    b: &[u8],
974    needle: &[u8],
975    as_int: Option<i64>,
976    limit: usize,
977    mut hit: &mut dyn FnMut(usize) -> bool,
978) -> usize {
979    let needle = Needle::new(needle, as_int);
980    if limit == 0 {
981        walk_back::<false, _>(b, &needle, 0, &mut hit)
982    } else {
983        walk_back::<true, _>(b, &needle, limit, &mut hit)
984    }
985}
986
987/// How long the entry at `at` is, split into its header and its payload, and
988/// whether the payload is text.
989///
990/// Every encoding except the short string, which the walk below handles itself.
991/// An integer encoding is entirely header, so its payload length is zero and it
992/// can never match a string of any length.
993///
994/// Out of line on purpose, and it is not because this is rare in general, it is
995/// because of what having it inline did to the loop it was in. Thirteen arms
996/// need registers, and the register allocator paid for them by spilling the
997/// needle's two comparison words to the stack and reloading them on every single
998/// element, including the overwhelming majority that never reach this function
999/// at all. A call on the encodings a list does not use is a good trade for two
1000/// loads on the ones it does.
1001#[inline(never)]
1002fn head_at(b: &[u8], at: usize) -> Option<(usize, usize, bool)> {
1003    let tag = *b.get(at)?;
1004    Some(match tag {
1005        0x00..=0x7F => (1, 0, false),
1006        0x80..=0xBF => (1, (tag & 0x3F) as usize, true),
1007        0xC0..=0xDF => (2, 0, false),
1008        0xE0..=0xEF => (
1009            2,
1010            (usize::from(tag & 0x0F) << 8) | usize::from(*b.get(at + 1)?),
1011            true,
1012        ),
1013        0xF0 => (
1014            5,
1015            u32::from_le_bytes([
1016                *b.get(at + 1)?,
1017                *b.get(at + 2)?,
1018                *b.get(at + 3)?,
1019                *b.get(at + 4)?,
1020            ]) as usize,
1021            true,
1022        ),
1023        0xF1 => (3, 0, false),
1024        0xF2 => (4, 0, false),
1025        0xF3 => (5, 0, false),
1026        0xF4 => (9, 0, false),
1027        // 0xF5 to 0xFE are unused by Redis and 0xFF is the terminator, which is
1028        // not in this run. Either way there is nothing after it that can be read
1029        // as an entry.
1030        _ => return None,
1031    })
1032}
1033
1034/// The scan itself, with `EVERY` saying whether `step` is worth carrying and
1035/// `LIMITED` whether `limit` is.
1036///
1037/// `hit` is a generic and it was a `&mut dyn` first, on the reasoning that it is
1038/// only reached on a match and so is called a handful of times against a million
1039/// iterations of the body around it. That reasoning is right about a long list
1040/// and wrong about a short one. A blob holds at most a hundred and twenty eight
1041/// elements and the thing it does all day is find one that is there, so the call
1042/// is not one in a million, it is one in four, and it cost 12 percent on the
1043/// eight member row in `benches/listpack.rs`. As a generic the sink for a find
1044/// inlines back to a store and a break, which is what the loop had before it took
1045/// a sink at all. It is two copies of this function and not more, because every
1046/// caller that wants every match already goes through a `&mut dyn` of its own.
1047///
1048/// What comes back is how many elements were looked at, which for the single
1049/// answer case is not interesting and folds away with everything else.
1050#[inline]
1051fn walk<const EVERY: bool, const LIMITED: bool, F: FnMut(usize) -> bool>(
1052    b: &[u8],
1053    needle: &Needle<'_>,
1054    step: usize,
1055    limit: usize,
1056    hit: &mut F,
1057) -> usize {
1058    let want = needle.len;
1059    let mut at = 0usize;
1060    let mut idx = 0usize;
1061    // Counted down rather than `idx % step`, because `step` is a runtime value
1062    // and the remainder compiles to a real division on every element. That is
1063    // twenty cycles to answer a question about a two element cycle, and it cost
1064    // more than the comparison it was guarding.
1065    let mut until = 0usize;
1066    while at < b.len() {
1067        if LIMITED && idx == limit {
1068            break;
1069        }
1070        let tag = b[at];
1071        // The one encoding a list is actually made of, given a path with nothing
1072        // in it. A string of sixty three bytes or less is a one byte header, and
1073        // its total is at most sixty four so its back length is one byte too,
1074        // which means stepping to the next element is an add of a number that
1075        // came straight out of the tag. No second read, no table, and one branch
1076        // instead of the five the general match below needs.
1077        //
1078        // The general walk is not slow because of how many instructions it runs.
1079        // It is slow because of how many of its branches are taken: a core
1080        // retires about one taken branch a cycle, and a five way tag match that
1081        // ends in a four way back length match spends more time being fetched
1082        // than being executed. Straightening the common case out is worth more
1083        // than anything done to the comparison inside it.
1084        if EVERY && tag & 0xC0 == 0x80 {
1085            let len = (tag & 0x3F) as usize;
1086            if len != want {
1087                at += len + 2;
1088                idx += 1;
1089                continue;
1090            }
1091            let Some(p) = b.get(at + 1..at + 1 + want) else {
1092                break;
1093            };
1094            let matched = needle.is(p);
1095            // `want` rather than `len`, which are the same number here and are
1096            // not the same instruction: one of them is in the way of the next
1097            // load and the other has been in a register all along. It halves the
1098            // loop. `scan_for` above has the long version.
1099            at += want + 2;
1100            idx += 1;
1101            if matched && !hit(idx - 1) {
1102                break;
1103            }
1104            continue;
1105        }
1106        let Some((hdr, len, text)) = head_at(b, at) else {
1107            break;
1108        };
1109        let total = hdr + len;
1110        let mut matched = false;
1111        if EVERY || until == 0 {
1112            matched = if text {
1113                let Some(p) = b.get(at + hdr..at + total) else {
1114                    break;
1115                };
1116                len == want && needle.is(p)
1117            } else {
1118                // `at` is inside `b`, which is the loop condition, so this
1119                // slice is always there.
1120                needle
1121                    .num
1122                    .is_some_and(|v| matches!(decode(&b[at..]), Some((Entry::Int(n), _)) if n == v))
1123            };
1124            if !EVERY {
1125                until = step;
1126            }
1127        }
1128        if !EVERY {
1129            until -= 1;
1130        }
1131        at += total + backlen_len(total);
1132        idx += 1;
1133        if matched && !hit(idx - 1) {
1134            break;
1135        }
1136    }
1137    idx
1138}
1139
1140/// The same walk from the back, which only a list ever asks for.
1141///
1142/// No `EVERY`, because the caller that steps is a hash and a hash has no back to
1143/// walk from. The step from one entry to the one in front of it is the back
1144/// length that ends where this entry starts, and reading it is a byte and a
1145/// branch in the case that covers everything up to a hundred and twenty seven
1146/// bytes, which is every element the fast path in [`walk`] handles and then
1147/// some. Longer than that and it falls back to [`read_backlen`], which is the
1148/// leftward walk.
1149#[inline]
1150fn walk_back<const LIMITED: bool, F: FnMut(usize) -> bool>(
1151    b: &[u8],
1152    needle: &Needle<'_>,
1153    limit: usize,
1154    hit: &mut F,
1155) -> usize {
1156    let want = needle.len;
1157    let mut at = b.len();
1158    let mut idx = 0usize;
1159    while at > 0 {
1160        if LIMITED && idx == limit {
1161            break;
1162        }
1163        // A back length of one byte has its top bit clear and holds the whole
1164        // value, which is what `write_backlen_into` writes and what makes this
1165        // one load rather than a loop.
1166        let last = b[at - 1];
1167        let (total, blen) = if last < 128 {
1168            (usize::from(last), 1)
1169        } else {
1170            let Some(t) = read_backlen(&b[..at]) else {
1171                break;
1172            };
1173            (t, backlen_len(t))
1174        };
1175        let Some(start) = at.checked_sub(total + blen) else {
1176            break;
1177        };
1178        let tag = b[start];
1179        let matched = if tag & 0xC0 == 0x80 {
1180            let len = usize::from(tag & 0x3F);
1181            match b.get(start + 1..start + 1 + len) {
1182                Some(p) => len == want && needle.is(p),
1183                None => break,
1184            }
1185        } else {
1186            match head_at(b, start) {
1187                Some((hdr, len, true)) => match b.get(start + hdr..start + hdr + len) {
1188                    Some(p) => len == want && needle.is(p),
1189                    None => break,
1190                },
1191                Some((_, _, false)) => needle.num.is_some_and(
1192                    |v| matches!(decode(&b[start..]), Some((Entry::Int(n), _)) if n == v),
1193                ),
1194                None => break,
1195            }
1196        };
1197        at = start;
1198        idx += 1;
1199        if matched && !hit(idx - 1) {
1200            break;
1201        }
1202    }
1203    idx
1204}
1205
1206/// How many bytes the back length of an entry of `len` bytes takes.
1207///
1208/// Seven bits a byte, so the boundaries are `2^7 - 1`, `2^14 - 1` and so on, and
1209/// they are Redis's `lpEncodeBacklenBytes` boundaries exactly. They are checked
1210/// against the real ones in the tests rather than taken on trust, because a
1211/// listpack whose back lengths are a byte out is one Redis walks off the end of.
1212#[inline]
1213pub(crate) const fn backlen_len(len: usize) -> usize {
1214    if len <= 127 {
1215        1
1216    } else if len <= 16383 {
1217        2
1218    } else if len <= 2_097_151 {
1219        3
1220    } else if len <= 268_435_455 {
1221        4
1222    } else {
1223        5
1224    }
1225}
1226
1227/// Write the back length for an entry of `len` bytes, and say how long it was.
1228///
1229/// The first byte holds the high seven bits and every later byte has its top bit
1230/// set, which is what lets it be read from the right hand end leftward.
1231#[inline]
1232fn write_backlen_into(dst: &mut [u8], len: usize) -> usize {
1233    let n = backlen_len(len);
1234    // High seven bits first, then seven at a time downward, and every byte
1235    // after the first carries the continuation bit that stops the leftward
1236    // walk from running past the front of the entry.
1237    for (i, b) in dst[..n].iter_mut().enumerate() {
1238        let shift = 7 * (n - 1 - i);
1239        *b = ((len >> shift) & 127) as u8 | if i == 0 { 0 } else { 128 };
1240    }
1241    n
1242}
1243
1244/// Read a back length that ends at the last byte of `upto`.
1245///
1246/// Walks left while the top bit is set, seven bits at a time, which is the
1247/// mirror image of how it was written.
1248pub(crate) fn read_backlen(upto: &[u8]) -> Option<usize> {
1249    let mut val = 0usize;
1250    let mut shift = 0u32;
1251    let mut at = upto.len().checked_sub(1)?;
1252    loop {
1253        let b = *upto.get(at)?;
1254        val |= usize::from(b & 127) << shift;
1255        if b & 128 == 0 {
1256            return Some(val);
1257        }
1258        shift += 7;
1259        if shift > 28 {
1260            return None;
1261        }
1262        at = at.checked_sub(1)?;
1263    }
1264}
1265
1266#[cfg(test)]
1267mod tests {
1268    use super::*;
1269
1270    fn of(members: &[&[u8]]) -> Listpack {
1271        let mut lp = Listpack::new();
1272        for m in members {
1273            lp.push(m);
1274        }
1275        lp
1276    }
1277
1278    fn all(lp: &Listpack) -> Vec<Vec<u8>> {
1279        lp.iter().map(|e| e.to_vec()).collect()
1280    }
1281
1282    /// Every edit used to build the new entry in a `Vec` and throw it away, so
1283    /// a blob with room in it still paid a malloc and a free per write. These
1284    /// three shapes are the whole of `splice`: an edit that grows the blob, one
1285    /// that shrinks it, and one that leaves it the same size. None of them may
1286    /// touch the allocator once the blob's own buffer is big enough.
1287    #[test]
1288    fn editing_a_blob_that_has_room_does_not_allocate() {
1289        let mut lp = Listpack::new();
1290        for i in 0..200 {
1291            lp.push(format!("member:{i:04}").as_bytes());
1292        }
1293        // Down to a hundred and back up, so the buffer is at its high water
1294        // mark and nothing below measures growth.
1295        lp.delete(100, 100);
1296        for i in 0..100 {
1297            lp.push(format!("member:{i:04}").as_bytes());
1298        }
1299
1300        // Built up here rather than inside the count, because `format!` is an
1301        // allocation of the test's own and would drown out what is measured.
1302        let names: Vec<(Vec<u8>, Vec<u8>)> = (0..100)
1303            .map(|i| {
1304                (
1305                    format!("other:{i:05}").into_bytes(),
1306                    format!("member:{i:04}").into_bytes(),
1307                )
1308            })
1309            .collect();
1310
1311        let (_, allocs) = crate::tally::counted(|| {
1312            for (i, (other, original)) in names.iter().enumerate() {
1313                // Same length, so the blob does not change size at all.
1314                lp.replace(i, other);
1315                // Shorter, then back to the original length.
1316                lp.replace(i, b"x");
1317                lp.replace(i, original);
1318            }
1319        });
1320        assert_eq!(allocs, 0, "editing allocated {allocs} times");
1321        assert_eq!(lp.len(), 200);
1322        assert_eq!(lp.get(0), Some(Entry::Str(b"member:0000")));
1323    }
1324
1325    #[test]
1326    fn an_empty_blob_is_a_header_and_a_terminator() {
1327        let lp = Listpack::new();
1328        assert!(lp.is_empty());
1329        assert_eq!(lp.len(), 0);
1330        assert_eq!(lp.byte_len(), 7);
1331        assert_eq!(lp.as_bytes(), &[7, 0, 0, 0, 0, 0, 0xFF]);
1332        assert_eq!(lp.get(0), None);
1333        assert_eq!(lp.iter().count(), 0);
1334    }
1335
1336    #[test]
1337    fn what_goes_in_comes_out_in_order() {
1338        let lp = of(&[b"one", b"two", b"three"]);
1339        assert_eq!(lp.len(), 3);
1340        assert_eq!(
1341            all(&lp),
1342            vec![b"one".to_vec(), b"two".to_vec(), b"three".to_vec()]
1343        );
1344        assert_eq!(lp.get(1), Some(Entry::Str(b"two")));
1345        assert_eq!(lp.get(3), None);
1346    }
1347
1348    /// Redis stores a member that parses as an integer as an integer, and the
1349    /// narrowest one that holds it. Every boundary is here because every one of
1350    /// them is a different encoding byte, and a byte wrong is an RDB Redis will
1351    /// not read.
1352    #[test]
1353    fn an_integer_takes_the_narrowest_encoding_that_holds_it() {
1354        for (text, first, len) in [
1355            (&b"0"[..], 0x00u8, 1usize),
1356            (b"127", 0x7F, 1),
1357            (b"128", 0xC0, 2),
1358            (b"4095", 0xCF, 2),
1359            (b"-4096", 0xD0, 2),
1360            (b"-1", 0xDF, 2),
1361            (b"4096", 0xF1, 3),
1362            (b"-4097", 0xF1, 3),
1363            (b"32767", 0xF1, 3),
1364            (b"32768", 0xF2, 4),
1365            (b"8388607", 0xF2, 4),
1366            (b"8388608", 0xF3, 5),
1367            (b"2147483647", 0xF3, 5),
1368            (b"2147483648", 0xF4, 9),
1369            (b"-9223372036854775808", 0xF4, 9),
1370        ] {
1371            let lp = of(&[text]);
1372            let at = HDR;
1373            assert_eq!(
1374                lp.as_bytes()[at],
1375                first,
1376                "{} took the wrong encoding",
1377                String::from_utf8_lossy(text)
1378            );
1379            assert_eq!(lp.byte_len(), HDR + len + 1 + 1, "{first:#x}");
1380            assert_eq!(
1381                lp.get(0),
1382                Some(Entry::Int(parse_i64(text).expect("a number"))),
1383                "{first:#x}"
1384            );
1385            assert_eq!(all(&lp), vec![text.to_vec()], "and it formats back");
1386        }
1387    }
1388
1389    /// What `string2ll` refuses is a string, and it has to stay one, because
1390    /// `SADD s 01` and `SADD s 1` are two different members to Redis.
1391    #[test]
1392    fn something_that_only_looks_like_a_number_stays_a_string() {
1393        for text in [&b"01"[..], b"+1", b"1 ", b" 1", b"1.0", b"-0", b""] {
1394            let lp = of(&[text]);
1395            assert_eq!(
1396                lp.get(0),
1397                Some(Entry::Str(text)),
1398                "{}",
1399                String::from_utf8_lossy(text)
1400            );
1401        }
1402    }
1403
1404    #[test]
1405    fn a_string_takes_the_narrowest_length_field() {
1406        for (len, first, head) in [(1usize, 0x81u8, 1usize), (63, 0xBF, 1), (64, 0xE0, 2)] {
1407            let s = vec![b'x'; len];
1408            let lp = of(&[&s]);
1409            assert_eq!(lp.as_bytes()[HDR], first, "length {len}");
1410            assert_eq!(
1411                lp.byte_len(),
1412                HDR + head + len + backlen_len(head + len) + 1
1413            );
1414            assert_eq!(lp.get(0), Some(Entry::Str(&s[..])));
1415        }
1416    }
1417
1418    /// Over 4095 bytes the length field is the thirty two bit one, and the entry
1419    /// is long enough that its own back length needs two bytes, which is the
1420    /// other boundary in the same test.
1421    #[test]
1422    fn a_long_string_takes_the_wide_length_and_a_wide_back_length() {
1423        let s = vec![b'y'; 5000];
1424        let lp = of(&[&s, b"after"]);
1425        assert_eq!(lp.as_bytes()[HDR], 0xF0);
1426        assert_eq!(backlen_len(5005), 2);
1427        assert_eq!(lp.get(0), Some(Entry::Str(&s[..])));
1428        assert_eq!(lp.get(1), Some(Entry::Str(b"after")));
1429        assert_eq!(lp.get_back(0), Some(Entry::Str(b"after")));
1430        assert_eq!(lp.get_back(1), Some(Entry::Str(&s[..])));
1431    }
1432
1433    #[test]
1434    fn the_back_length_reads_the_same_as_it_was_written() {
1435        for len in [1usize, 127, 128, 16382, 16383, 16384, 2_097_150, 2_097_151] {
1436            let mut buf = [0u8; 5];
1437            let n = write_backlen_into(&mut buf, len);
1438            let out = &buf[..n];
1439            assert_eq!(out.len(), backlen_len(len), "length {len}");
1440            assert_eq!(read_backlen(out), Some(len), "length {len}");
1441        }
1442    }
1443
1444    /// Reading itself back is not enough, because a wrong boundary is wrong
1445    /// consistently. These are Redis's `lpEncodeBacklenBytes` boundaries written
1446    /// out from `listpack.c` at 8.10.1, which is the version `yo-compat` pins,
1447    /// and they are what a listpack from an RDB will have been written with.
1448    #[test]
1449    fn the_back_length_boundaries_are_the_ones_redis_uses() {
1450        for (len, want) in [
1451            (0usize, 1usize),
1452            (127, 1),
1453            (128, 2),
1454            (16383, 2),
1455            (16384, 3),
1456            (2_097_151, 3),
1457            (2_097_152, 4),
1458            (268_435_455, 4),
1459            (268_435_456, 5),
1460        ] {
1461            assert_eq!(backlen_len(len), want, "an entry of {len} bytes");
1462        }
1463    }
1464
1465    #[test]
1466    fn a_walk_backward_reaches_every_element() {
1467        let lp = of(&[b"a", b"bb", b"1", b"999999", b"dddd"]);
1468        let back: Vec<Vec<u8>> = (0..5)
1469            .map(|i| lp.get_back(i).expect("in range").to_vec())
1470            .collect();
1471        let mut forward = all(&lp);
1472        forward.reverse();
1473        assert_eq!(back, forward);
1474        assert_eq!(lp.get_back(5), None);
1475    }
1476
1477    #[test]
1478    fn find_locates_an_element_however_it_is_stored() {
1479        let lp = of(&[b"alpha", b"42", b"01", b"beta"]);
1480        assert_eq!(lp.find(b"alpha", 1), Some(0));
1481        assert_eq!(lp.find(b"42", 1), Some(1), "stored as an integer");
1482        assert_eq!(lp.find(b"01", 1), Some(2), "stored as a string");
1483        assert_eq!(lp.find(b"beta", 1), Some(3));
1484        assert_eq!(lp.find(b"gamma", 1), None);
1485        assert_eq!(lp.find(b"1", 1), None, "01 is not 1");
1486    }
1487
1488    /// One of every encoding, at every length that changes which path through
1489    /// the scan an element takes.
1490    ///
1491    /// The lengths are the ones that matter to the comparison rather than
1492    /// arbitrary: nothing, under a word, exactly a word, between one and two
1493    /// words where the two window compare covers the whole value, exactly two,
1494    /// and past two where it stops being a whole answer and becomes a filter in
1495    /// front of `memcmp`. Sixty three and sixty four are the last length with a
1496    /// one byte header and the first with two, which is the boundary the short
1497    /// path is drawn on, and a hundred and twenty seven and a hundred and twenty
1498    /// eight are where the back length grows a second byte, which is the
1499    /// boundary the backward walk is drawn on.
1500    fn every_encoding() -> Vec<Vec<u8>> {
1501        let mut members: Vec<Vec<u8>> = Vec::new();
1502        // One of each integer encoding, including the boundaries where Redis
1503        // steps up to a wider one and both signs of each.
1504        for n in [
1505            0i64,
1506            127,
1507            -1,
1508            4095,
1509            -4096,
1510            32767,
1511            -32768,
1512            8_388_607,
1513            -8_388_608,
1514            2_147_483_647,
1515            -2_147_483_648,
1516            i64::MAX,
1517            i64::MIN,
1518        ] {
1519            members.push(n.to_string().into_bytes());
1520        }
1521        for len in [
1522            0usize, 1, 7, 8, 9, 15, 16, 17, 31, 63, 64, 100, 125, 126, 127, 128, 200,
1523        ] {
1524            let mut v = vec![b'a'; len];
1525            // A varying tail, so that two different lengths are not two
1526            // prefixes of each other and the length check is doing work rather
1527            // than being the only thing that separates them.
1528            if len > 0 {
1529                v[len - 1] = b'0' + (len % 10) as u8;
1530            }
1531            members.push(v);
1532        }
1533        members
1534    }
1535
1536    /// The scan has a short path for the one encoding a list is made of and a
1537    /// general one for the other thirteen, and the danger with two paths is that
1538    /// they disagree about some element neither author had in mind. So this
1539    /// builds a blob holding every encoding, at every length that changes which
1540    /// path an element takes, and asks for each of them in turn.
1541    ///
1542    /// The lengths are the ones that matter to the comparison rather than
1543    /// arbitrary: nothing, under a word, exactly a word, between one and two
1544    /// words where the two window compare covers the whole value, exactly two,
1545    /// and past two where it stops being a whole answer and becomes a filter in
1546    /// front of `memcmp`. Sixty three and sixty four are the last length with a
1547    /// one byte header and the first with two, which is the boundary the short
1548    /// path is drawn on.
1549    #[test]
1550    fn both_paths_through_the_scan_agree_about_every_encoding() {
1551        let members = every_encoding();
1552        let lp = of(&members.iter().map(Vec::as_slice).collect::<Vec<_>>());
1553        assert_eq!(lp.len(), members.len());
1554        for (at, m) in members.iter().enumerate() {
1555            assert_eq!(lp.find(m, 1), Some(at), "member {at} went missing");
1556        }
1557        // And a handful that are not there, each one a near miss of something
1558        // that is: a different length, the same length with a different first
1559        // byte, and the same length with a different last byte.
1560        for miss in [
1561            b"aaaaaaaaaaaa".as_slice(),
1562            b"baaaaaa7".as_slice(),
1563            b"aaaaaaa9".as_slice(),
1564            b"128".as_slice(),
1565            b"-2".as_slice(),
1566        ] {
1567            assert_eq!(lp.find(miss, 1), None, "{miss:?} is not in here");
1568        }
1569    }
1570
1571    /// The same blob under a step of two, which is the other instantiation of
1572    /// the walk and the one a hash uses. Nothing at an odd position may be
1573    /// found, whatever it is encoded as.
1574    #[test]
1575    fn a_stepped_scan_agrees_with_itself_about_every_encoding() {
1576        let members: Vec<Vec<u8>> = (0..40i32)
1577            .map(|i| {
1578                if i % 3 == 0 {
1579                    (i64::from(i) * 1000 - 20_000).to_string().into_bytes()
1580                } else {
1581                    format!("field:{i:0width$}", width = (i % 20) as usize).into_bytes()
1582                }
1583            })
1584            .collect();
1585        let lp = of(&members.iter().map(Vec::as_slice).collect::<Vec<_>>());
1586        for (at, m) in members.iter().enumerate() {
1587            let want = if at % 2 == 0 {
1588                // Every member here is distinct, so an even one is found where
1589                // it is and an odd one is not found at all.
1590                Some(at)
1591            } else {
1592                None
1593            };
1594            assert_eq!(lp.find(m, 2), want, "member {at} under a step of two");
1595        }
1596    }
1597
1598    /// The backward walk finds its way from one entry to the one in front of it
1599    /// through the back length rather than through a header, so it is a third
1600    /// path over the same bytes and it has to agree with the other two about all
1601    /// of them. Every member is asked for from the back and has to come back at
1602    /// the position the forward walk gives it.
1603    #[test]
1604    fn the_backward_scan_agrees_with_the_forward_one_about_every_encoding() {
1605        let members = every_encoding();
1606        let lp = of(&members.iter().map(Vec::as_slice).collect::<Vec<_>>());
1607        let n = members.len();
1608        for (at, m) in members.iter().enumerate() {
1609            let mut got = Vec::new();
1610            lp.find_each_back(m, parse_i64(m), 0, &mut |back| {
1611                got.push(n - back - 1);
1612                true
1613            });
1614            assert_eq!(got, vec![at], "member {at} from the back");
1615        }
1616        for miss in [
1617            b"aaaaaaaaaaaa".as_slice(),
1618            b"baaaaaa7".as_slice(),
1619            b"aaaaaaa9".as_slice(),
1620            b"128".as_slice(),
1621            b"-2".as_slice(),
1622        ] {
1623            let mut got = 0usize;
1624            lp.find_each_back(miss, parse_i64(miss), 0, &mut |_| {
1625                got += 1;
1626                true
1627            });
1628            assert_eq!(got, 0, "{miss:?} is not in here");
1629        }
1630    }
1631
1632    /// Both walks over a blob where the same value is in it several times, which
1633    /// is what `LPOS` and `LREM` are actually for and what the single answer
1634    /// scan never exercises. The stop and the budget are checked here too, since
1635    /// they are the two things the walk carries that a find does not.
1636    #[test]
1637    fn a_walk_over_every_match_gives_them_all_in_order_from_either_end() {
1638        let members: Vec<Vec<u8>> = (0..30)
1639            .map(|i| {
1640                if i % 4 == 0 {
1641                    b"x".to_vec()
1642                } else {
1643                    format!("element:{i:08}").into_bytes()
1644                }
1645            })
1646            .collect();
1647        let lp = of(&members.iter().map(Vec::as_slice).collect::<Vec<_>>());
1648        let want: Vec<usize> = (0..30).filter(|i| i % 4 == 0).collect();
1649
1650        let mut got = Vec::new();
1651        let looked = lp.find_each(b"x", None, 0, &mut |at| {
1652            got.push(at);
1653            true
1654        });
1655        assert_eq!(got, want);
1656        assert_eq!(looked, 30, "no budget means the whole thing is read");
1657
1658        let mut got = Vec::new();
1659        lp.find_each_back(b"x", None, 0, &mut |back| {
1660            got.push(29 - back);
1661            true
1662        });
1663        got.reverse();
1664        assert_eq!(got, want, "the same matches, found the other way round");
1665
1666        // A stop after two, which must not read the rest.
1667        let mut got = Vec::new();
1668        let looked = lp.find_each(b"x", None, 0, &mut |at| {
1669            got.push(at);
1670            got.len() < 2
1671        });
1672        assert_eq!(got, vec![0, 4]);
1673        assert_eq!(looked, 5, "the walk stopped where the second match was");
1674
1675        // And a budget, which is `MAXLEN`: ten elements looked at reaches the
1676        // matches at 0, 4 and 8 and nothing after them.
1677        let mut got = Vec::new();
1678        let looked = lp.find_each(b"x", None, 10, &mut |at| {
1679            got.push(at);
1680            true
1681        });
1682        assert_eq!(got, vec![0, 4, 8]);
1683        assert_eq!(looked, 10);
1684
1685        let mut got = Vec::new();
1686        let looked = lp.find_each_back(b"x", None, 10, &mut |back| {
1687            got.push(29 - back);
1688            true
1689        });
1690        assert_eq!(got, vec![28, 24, 20], "ten from the back is 20 up");
1691        assert_eq!(looked, 10);
1692    }
1693
1694    /// A hash in this band is field, value, field, value, and a field lookup has
1695    /// to skip the values or a value that happens to equal a field name comes
1696    /// back as one.
1697    #[test]
1698    fn find_with_a_step_only_looks_at_the_fields() {
1699        let lp = of(&[b"name", b"age", b"age", b"41"]);
1700        assert_eq!(lp.find(b"name", 2), Some(0));
1701        assert_eq!(lp.find(b"age", 2), Some(2), "the value at 1 is not a field");
1702        assert_eq!(lp.find(b"41", 2), None);
1703        assert_eq!(lp.get(3), Some(Entry::Int(41)));
1704    }
1705
1706    #[test]
1707    fn inserting_puts_an_element_in_front_of_another() {
1708        let mut lp = of(&[b"a", b"c"]);
1709        lp.insert(1, b"b");
1710        assert_eq!(all(&lp), vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]);
1711        lp.insert(0, b"start");
1712        lp.insert(99, b"end");
1713        assert_eq!(lp.len(), 5);
1714        assert_eq!(all(&lp)[0], b"start".to_vec());
1715        assert_eq!(all(&lp)[4], b"end".to_vec());
1716    }
1717
1718    #[test]
1719    fn replacing_keeps_the_position_and_can_change_the_size() {
1720        let mut lp = of(&[b"a", b"b", b"c"]);
1721        assert!(lp.replace(1, b"a much longer value than before"));
1722        assert_eq!(lp.len(), 3);
1723        assert_eq!(all(&lp)[1], b"a much longer value than before".to_vec());
1724        assert!(lp.replace(1, b"7"));
1725        assert_eq!(lp.get(1), Some(Entry::Int(7)), "and can shrink to an int");
1726        assert_eq!(all(&lp), vec![b"a".to_vec(), b"7".to_vec(), b"c".to_vec()]);
1727        assert!(!lp.replace(9, b"nothing there"));
1728    }
1729
1730    #[test]
1731    fn deleting_takes_out_a_run_in_one_edit() {
1732        let mut lp = of(&[b"f1", b"v1", b"f2", b"v2", b"f3", b"v3"]);
1733        assert!(lp.delete(2, 2), "a field and its value together");
1734        assert_eq!(lp.len(), 4);
1735        assert_eq!(
1736            all(&lp),
1737            vec![
1738                b"f1".to_vec(),
1739                b"v1".to_vec(),
1740                b"f3".to_vec(),
1741                b"v3".to_vec()
1742            ]
1743        );
1744        assert!(!lp.delete(9, 1));
1745        assert!(
1746            lp.delete(0, 99),
1747            "asking for more than is there takes the rest"
1748        );
1749        assert!(lp.is_empty());
1750        assert_eq!(lp.len(), 0);
1751    }
1752
1753    /// Every edit has to leave the header right, because the header is what
1754    /// `from_bytes` checks and what a reader trusts.
1755    #[test]
1756    fn the_header_survives_every_edit() {
1757        let mut lp = Listpack::new();
1758        for i in 0..64u32 {
1759            lp.push(format!("member-{i}").as_bytes());
1760        }
1761        for i in 0..20 {
1762            lp.delete(i, 1);
1763            lp.replace(i, b"replaced");
1764            lp.insert(i, b"1234567");
1765        }
1766        let total = u32::from_le_bytes([lp.bytes[0], lp.bytes[1], lp.bytes[2], lp.bytes[3]]);
1767        assert_eq!(total as usize, lp.byte_len());
1768        assert_eq!(lp.len(), lp.iter().count());
1769        assert_eq!(Listpack::from_bytes(lp.as_bytes()), Ok(lp.clone()));
1770    }
1771
1772    #[test]
1773    fn a_blob_round_trips_through_its_bytes() {
1774        let lp = of(&[b"a", b"12345", b"", &[b'z'; 200]]);
1775        let back = Listpack::from_bytes(lp.as_bytes()).expect("our own bytes check out");
1776        assert_eq!(back, lp);
1777        assert_eq!(all(&back), all(&lp));
1778    }
1779
1780    /// Bytes from an RDB or a `RESTORE` are somebody else's, so every way they
1781    /// can be wrong is a refusal and not a panic.
1782    #[test]
1783    fn a_blob_that_does_not_check_out_is_refused() {
1784        assert_eq!(Listpack::from_bytes(&[]), Err(Malformed::Short));
1785        assert_eq!(Listpack::from_bytes(&[0; 4]), Err(Malformed::Short));
1786
1787        let good = of(&[b"alpha", b"beta"]);
1788
1789        let mut wrong_len = good.as_bytes().to_vec();
1790        wrong_len[0] = 99;
1791        assert_eq!(Listpack::from_bytes(&wrong_len), Err(Malformed::Length));
1792
1793        let mut no_end = good.as_bytes().to_vec();
1794        let last = no_end.len() - 1;
1795        no_end[last] = 0x00;
1796        assert_eq!(Listpack::from_bytes(&no_end), Err(Malformed::Terminator));
1797
1798        let mut wrong_count = good.as_bytes().to_vec();
1799        wrong_count[4] = 7;
1800        assert_eq!(Listpack::from_bytes(&wrong_count), Err(Malformed::Count));
1801
1802        let mut bad_entry = good.as_bytes().to_vec();
1803        bad_entry[HDR] = 0xF7;
1804        assert_eq!(Listpack::from_bytes(&bad_entry), Err(Malformed::Entry));
1805
1806        let mut bad_back = good.as_bytes().to_vec();
1807        bad_back[HDR + 6] = 3;
1808        assert_eq!(Listpack::from_bytes(&bad_back), Err(Malformed::BackLength));
1809    }
1810
1811    /// A blob written by somebody who did not keep the count, which Redis does
1812    /// above 65534 elements, is walked rather than rejected.
1813    #[test]
1814    fn an_unknown_count_is_walked_and_not_refused() {
1815        let mut lp = of(&[b"a", b"b", b"c"]);
1816        lp.bytes[4..6].copy_from_slice(&COUNT_UNKNOWN.to_le_bytes());
1817        let back = Listpack::from_bytes(lp.as_bytes()).expect("unknown is allowed");
1818        assert_eq!(back.len(), 3);
1819    }
1820
1821    fn hex(lp: &Listpack) -> String {
1822        lp.as_bytes().iter().map(|b| format!("{b:02x}")).collect()
1823    }
1824
1825    /// The claim this module makes is that the bytes are Redis's bytes, and the
1826    /// only way to check that is against Redis's bytes.
1827    ///
1828    /// These came out of `lpAppend` in `listpack.c` from the 8.10.1 tarball,
1829    /// compiled and run, not out of reading the source and working out what it
1830    /// would do. The vectors are the boundaries: every integer encoding and both
1831    /// ends of each, the strings that look like integers and must not become
1832    /// them, the empty string, and a hash shaped pack where a value is an
1833    /// integer and the fields around it are not.
1834    #[test]
1835    fn the_bytes_are_the_ones_redis_writes() {
1836        assert_eq!(hex(&Listpack::new()), "070000000000ff");
1837
1838        assert_eq!(
1839            hex(&of(&[b"one", b"two", b"three"])),
1840            "180000000300836f6e65048374776f0485746872656506ff"
1841        );
1842
1843        let ints: Vec<&[u8]> = vec![
1844            b"0",
1845            b"127",
1846            b"128",
1847            b"4095",
1848            b"-4096",
1849            b"-1",
1850            b"4096",
1851            b"-4097",
1852            b"32767",
1853            b"32768",
1854            b"8388607",
1855            b"8388608",
1856            b"2147483647",
1857            b"2147483648",
1858            b"-9223372036854775808",
1859        ];
1860        assert_eq!(
1861            hex(&of(&ints)),
1862            "4d0000000f0000017f01c08002cfff02d00002dfff02f1001003f1ffef03f1ff7f\
1863             03f200800004f2ffff7f04f30000800005f3ffffff7f05f4000000800000000009\
1864             f4000000000000008009ff"
1865        );
1866
1867        let not_ints: Vec<&[u8]> = vec![b"01", b"+1", b"1 ", b" 1", b"1.0", b"-0", b""];
1868        assert_eq!(
1869            hex(&of(&not_ints)),
1870            "22000000070082303103822b3103823120038220310383312e3004822d30038001ff"
1871        );
1872
1873        assert_eq!(
1874            hex(&of(&[b"name", b"age", b"age", b"41"])),
1875            "190000000400846e616d6505836167650483616765042901ff"
1876        );
1877    }
1878
1879    /// The same check for the length boundaries, where the whole middle of the
1880    /// blob is one repeated byte and only the ends carry any information.
1881    #[test]
1882    fn a_long_element_is_framed_the_way_redis_frames_it() {
1883        for (len, total, head, tail) in [
1884            (
1885                63usize,
1886                72usize,
1887                "480000000100bf7878787878",
1888                "78787878787840ff",
1889            ),
1890            (64, 74, "4a0000000100e04078787878", "78787878787842ff"),
1891            (4095, 4106, "0a1000000100efff78787878", "78787878782081ff"),
1892            (4096, 4110, "0e1000000100f00010000078", "78787878782085ff"),
1893        ] {
1894            let lp = of(&[&vec![b'x'; len]]);
1895            let h = hex(&lp);
1896            assert_eq!(lp.byte_len(), total, "a {len} byte element");
1897            assert_eq!(&h[..head.len()], head, "a {len} byte element");
1898            assert_eq!(&h[h.len() - tail.len()..], tail, "a {len} byte element");
1899        }
1900
1901        let lp = of(&[&vec![b'y'; 5000], b"after"]);
1902        let h = hex(&lp);
1903        assert_eq!(lp.byte_len(), 5021);
1904        assert_eq!(&h[..24], "9d1300000200f08813000079");
1905        assert_eq!(&h[h.len() - 16..], "85616674657206ff");
1906    }
1907
1908    /// The band it exists for. A hundred and twenty eight elements, every one of
1909    /// them findable, and the whole thing inside a few cache lines.
1910    #[test]
1911    fn a_full_inline_band_still_reads_correctly() {
1912        let members: Vec<Vec<u8>> = (0..128u32).map(|i| format!("m{i}").into_bytes()).collect();
1913        let mut lp = Listpack::new();
1914        for m in &members {
1915            lp.push(m);
1916        }
1917        assert_eq!(lp.len(), 128);
1918        for (i, m) in members.iter().enumerate() {
1919            assert_eq!(lp.find(m, 1), Some(i));
1920        }
1921        assert!(lp.byte_len() < 1024, "{} bytes", lp.byte_len());
1922    }
1923}