Skip to main content

yo_kv/
stream.rs

1//! A stream, as a log of listpack nodes in ID order (`08` section 7).
2//!
3//! A stream is the one collection here that is only ever appended to at one end
4//! and only ever trimmed at the other. Nothing is inserted in the middle, ever,
5//! because the ID of a new entry has to be greater than the last one there. That
6//! is a much stronger promise than any other type makes, and the structure is
7//! built on it rather than around it.
8//!
9//! ```text
10//! +--------------+--------------+--------------+
11//! | node         | node         | node         |
12//! | master 5-0   | master 812-0 | master 990-0 |
13//! | 100 entries  | 100 entries  | 12 entries   |
14//! +--------------+--------------+--------------+
15//!   ^ trimmed from here          appended here ^
16//! ```
17//!
18//! # Why a node holds a hundred entries and not one
19//!
20//! One entry per allocation is what a naive log does and it is wrong twice over.
21//! It costs an allocator header and a pointer per entry, which for a stream of
22//! short entries is most of the memory, and it puts the entries in whatever
23//! order the allocator felt like, which is the opposite of what a range scan
24//! wants. A node is one blob holding a hundred consecutive entries, so a scan is
25//! a walk through memory the prefetcher can see coming, and the per entry
26//! overhead is a few bytes rather than a few dozen.
27//!
28//! Two tricks inside the node take it further down, and both are Redis's:
29//!
30//! The **ID is stored as a difference** from the node's first ID rather than
31//! whole. Entries arrive milliseconds apart and a difference of a few hundred is
32//! two bytes where a pair of 64 bit integers is sixteen.
33//!
34//! The **field names are stored once** for the node rather than once per entry.
35//! A stream is almost always the same shape repeated, `sensor` and `reading`
36//! over and over, so the first entry's field names become the node's master
37//! fields and every later entry with exactly those names, in that order, stores
38//! only its values. An entry with different fields stores its own names and
39//! costs what it would have cost anyway.
40//!
41//! # Why the nodes are a deque and not a radix tree
42//!
43//! Redis keeps its nodes in a rax keyed by the sixteen byte big endian ID, and
44//! `08` says radix log, so this is the place to say why there is no radix tree
45//! here. The keys are appended in sorted order and never inserted between, which
46//! means the index is a sorted array and stays one for free. Finding the node a
47//! range starts in is then a binary search over the node count, which for a
48//! million entries is fourteen steps over an array that is a few pages long. A
49//! radix tree over the same keys is four or five levels of pointer chasing, each
50//! one a cache miss, and it exists to solve the insertion problem that this
51//! structure does not have.
52//!
53//! Trimming is what a plain `Vec` would get wrong, since it takes from the front,
54//! so the nodes live in a `VecDeque` and dropping the oldest node is a pop.
55//!
56//! # The bytes are Redis's
57//!
58//! A node is a [`Listpack`] laid out exactly as `t_stream.c` lays one out, and
59//! the node's first ID is held beside it exactly as the rax key holds it there.
60//! That is not deference, it is the cheapest route to `DUMP` and `RESTORE` and
61//! an RDB that Redis can read, since the node is already the thing that goes on
62//! the wire and no conversion has to exist at all.
63//!
64//! ```text
65//! master entry
66//! +-------+---------+------------+---------+-----+---------+---+
67//! | count | deleted | num-fields | field-1 | ... | field-N | 0 |
68//! +-------+---------+------------+---------+-----+---------+---+
69//!
70//! an entry with the master's fields
71//! +-------+---------+----------+---------+-----+---------+----------+
72//! | flags | ms-diff | seq-diff | value-1 | ... | value-N | lp-count |
73//! +-------+---------+----------+---------+-----+---------+----------+
74//!
75//! an entry with its own
76//! +-------+---------+----------+------------+---------+---------+-----+----------+
77//! | flags | ms-diff | seq-diff | num-fields | field-1 | value-1 | ... | lp-count |
78//! +-------+---------+----------+------------+---------+---------+-----+----------+
79//! ```
80//!
81//! `lp-count` is how many listpack elements the entry occupies before it, which
82//! is what makes the node walkable backwards. It is written because Redis writes
83//! it and the bytes have to match, and nothing here reads it yet: `XREVRANGE`
84//! buffers a node's marks and hands them back reversed instead. That costs, and
85//! the benchmark says how much, 6.83 microseconds against 1.95 for the same
86//! hundred entries forwards. Stepping back over `lp-count` is the fix and it is
87//! its own change, because it needs its own before and after.
88//!
89//! Both ID halves are a plain wrapping difference from the master, which is what
90//! Redis writes and what it adds back. The sequence usually goes down when the
91//! millisecond goes up, so the second difference is usually negative, and
92//! wrapping arithmetic is the exact inverse either way.
93//!
94//! That claim is checked rather than asserted. A `DUMP` taken from Redis 8.10.1
95//! is hard coded in the tests, and two of them run it in both directions: one
96//! builds the same stream here and compares the node's bytes to Redis's byte for
97//! byte, the other takes Redis's node and reads the entries back out of it.
98//!
99//! # Deleting does not move anything
100//!
101//! `XDEL` sets a bit in the entry's flags and leaves the bytes where they are.
102//! Compacting the node would move every entry behind it, which on a node of a
103//! hundred is a memmove per delete, and a stream is not a structure people
104//! delete from in bulk. The node's master entry counts how many of its entries
105//! are dead, and a node whose last live entry goes is dropped whole.
106
107use std::cmp::Ordering;
108use std::collections::VecDeque;
109
110use yo_common::num::{DIGITS_MAX, i64_digits, push_u64, u64_digits};
111
112use crate::frozen::{self, Broken};
113use crate::listpack::{self, Entry, Listpack};
114
115pub mod groups;
116
117pub use groups::{Consumer, Filter, Group, Nack, Retry};
118
119/// How many bytes a node holds before the next entry starts a new one.
120///
121/// `stream-node-max-bytes`, which is 4096 in Redis and is here for the same
122/// reason: a node is rewritten in place when an entry is deleted from it and is
123/// copied whole when it is written out, so a node that grows without limit
124/// turns both of those into a problem.
125pub const NODE_BYTES: usize = 4096;
126
127/// How many entries a node holds before the next one starts a new node.
128///
129/// `stream-node-max-entries`, which is 100 in Redis.
130pub const NODE_ENTRIES: usize = 100;
131
132/// The entry is live.
133const LIVE: i64 = 0;
134
135/// The entry has been deleted and its bytes are still here.
136const DELETED: i64 = 1;
137
138/// The entry's field names are the node's master fields.
139const SAME_FIELDS: i64 = 2;
140
141/// Where the master entry's field names start.
142///
143/// After the count, the deleted count and the number of fields.
144const MASTER_FIELDS: usize = 3;
145
146/// An entry ID, which is a millisecond and a sequence number inside it.
147///
148/// Two 64 bit halves rather than one 128 bit number, because both halves are
149/// addressable on the wire: `XADD key 5-*` asks for the next sequence inside
150/// millisecond five, and `XRANGE key 5 5` is every sequence inside it.
151#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
152pub struct Id {
153    /// The millisecond, which is a unix timestamp for an ID the server made up.
154    pub ms: u64,
155    /// Which entry within that millisecond.
156    pub seq: u64,
157}
158
159impl Id {
160    /// The lowest ID there is, which is what `-` means in a range.
161    pub const MIN: Id = Id { ms: 0, seq: 0 };
162
163    /// The highest, which is what `+` means.
164    pub const MAX: Id = Id {
165        ms: u64::MAX,
166        seq: u64::MAX,
167    };
168
169    /// An ID from its two halves.
170    #[must_use]
171    #[inline]
172    pub const fn new(ms: u64, seq: u64) -> Id {
173        Id { ms, seq }
174    }
175
176    /// The next ID after this one, or `None` at [`Id::MAX`].
177    ///
178    /// What an exclusive range start turns into, and what `XADD key ms-*`
179    /// resolves to when the millisecond is already the last one used.
180    #[must_use]
181    pub const fn next(self) -> Option<Id> {
182        if self.seq != u64::MAX {
183            Some(Id {
184                ms: self.ms,
185                seq: self.seq + 1,
186            })
187        } else if self.ms != u64::MAX {
188            Some(Id {
189                ms: self.ms + 1,
190                seq: 0,
191            })
192        } else {
193            None
194        }
195    }
196
197    /// The ID before this one, or `None` at [`Id::MIN`].
198    #[must_use]
199    pub const fn prev(self) -> Option<Id> {
200        if self.seq != 0 {
201            Some(Id {
202                ms: self.ms,
203                seq: self.seq - 1,
204            })
205        } else if self.ms != 0 {
206            Some(Id {
207                ms: self.ms - 1,
208                seq: u64::MAX,
209            })
210        } else {
211            None
212        }
213    }
214
215    /// The sixteen big endian bytes Redis keys a node by.
216    ///
217    /// Big endian because that is the order that sorts, which is the whole
218    /// reason the format picked it.
219    #[must_use]
220    pub fn to_bytes(self) -> [u8; 16] {
221        let mut out = [0u8; 16];
222        out[..8].copy_from_slice(&self.ms.to_be_bytes());
223        out[8..].copy_from_slice(&self.seq.to_be_bytes());
224        out
225    }
226
227    /// The ID those bytes hold.
228    #[must_use]
229    pub fn from_bytes(bytes: [u8; 16]) -> Id {
230        let mut ms = [0u8; 8];
231        let mut seq = [0u8; 8];
232        ms.copy_from_slice(&bytes[..8]);
233        seq.copy_from_slice(&bytes[8..]);
234        Id {
235            ms: u64::from_be_bytes(ms),
236            seq: u64::from_be_bytes(seq),
237        }
238    }
239
240    /// `ms-seq`, which is how an ID looks everywhere a client can see one.
241    pub fn write_to(self, out: &mut Vec<u8>) {
242        push_u64(out, self.ms);
243        out.push(b'-');
244        push_u64(out, self.seq);
245    }
246
247    /// The same as a fresh `Vec`, for a caller that is not building a reply.
248    #[must_use]
249    pub fn to_vec(self) -> Vec<u8> {
250        let mut out = Vec::with_capacity(41);
251        self.write_to(&mut out);
252        out
253    }
254
255    /// `ms` or `ms-seq`, with a missing sequence read as `default`.
256    ///
257    /// `XRANGE key 5 5` means every entry in millisecond five, so the start
258    /// defaults its sequence to zero and the end to the largest there is. The
259    /// special forms a client can send, `-`, `+`, `$`, `*` and `ms-*`, are the
260    /// command layer's business and not this one's.
261    #[must_use]
262    pub fn parse(s: &[u8], default: u64) -> Option<Id> {
263        let (ms, seq) = match s.iter().position(|c| *c == b'-') {
264            Some(at) => (&s[..at], Some(&s[at + 1..])),
265            None => (s, None),
266        };
267        Some(Id {
268            ms: digits(ms)?,
269            seq: match seq {
270                Some(seq) => digits(seq)?,
271                None => default,
272            },
273        })
274    }
275}
276
277/// A run of digits as a `u64`, refusing a sign, a space or an empty string.
278///
279/// `parse_i64` would take `-1` and `+5` and this must not: an ID is unsigned
280/// and the minus is the separator.
281fn digits(s: &[u8]) -> Option<u64> {
282    if s.is_empty() || s.len() > 20 {
283        return None;
284    }
285    let mut n = 0u64;
286    for c in s {
287        let d = c.wrapping_sub(b'0');
288        if d > 9 {
289            return None;
290        }
291        n = n.checked_mul(10)?.checked_add(u64::from(d))?;
292    }
293    Some(n)
294}
295
296/// Why an append was refused.
297#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum Refused {
299    /// The ID is not greater than the last one in the stream.
300    NotGreater,
301    /// The ID is zero, which no entry can have because nothing sorts below it.
302    Zero,
303    /// The stream is at [`Id::MAX`] and there is no next ID to hand out.
304    Full,
305}
306
307/// Where a node stops taking entries.
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub struct Limits {
310    /// The most bytes a node holds.
311    pub max_node_bytes: usize,
312    /// The most entries a node holds, live and deleted together.
313    pub max_node_entries: usize,
314}
315
316impl Default for Limits {
317    /// `stream-node-max-bytes 4096` and `stream-node-max-entries 100`.
318    fn default() -> Limits {
319        Limits {
320            max_node_bytes: NODE_BYTES,
321            max_node_entries: NODE_ENTRIES,
322        }
323    }
324}
325
326/// What a delete does about the consumer groups still pointing at the entry.
327///
328/// `XDEL` takes an entry out from under whoever was handed it and leaves the
329/// pending list holding an ID that can never be read, which is a state `XCLAIM`
330/// then has to clean up. The 8.2 commands let a caller say what it wants
331/// instead, and the three answers are the three words here.
332#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
333pub enum Refs {
334    /// `KEEPREF`: take the entry and leave every pending list alone, which is
335    /// what `XDEL` has always done and is still the default.
336    #[default]
337    Keep,
338    /// `DELREF`: take the entry and take it out of every pending list with it.
339    Drop,
340    /// `ACKED`: only take the entry when no group could still be handed it.
341    Acked,
342}
343
344/// What one ID a delete was asked about came to.
345///
346/// The numbers are Redis's and they are not a success flag: a caller sending a
347/// list of IDs gets one of these each and has to be able to tell an entry that
348/// was never there from one that is still there on purpose.
349#[derive(Debug, Clone, Copy, PartialEq, Eq)]
350pub enum Fate {
351    /// There was nothing here to do.
352    Missing,
353    /// It went.
354    Gone,
355    /// Somebody could still be handed it, so it stayed.
356    Held,
357}
358
359impl Fate {
360    /// The integer this is on the wire.
361    #[must_use]
362    #[inline]
363    pub fn code(self) -> i64 {
364        match self {
365            Fate::Missing => -1,
366            Fate::Gone => 1,
367            Fate::Held => 2,
368        }
369    }
370}
371
372/// One node: a run of consecutive entries and the ID the run starts at.
373#[derive(Debug, Clone, PartialEq, Eq)]
374struct Node {
375    /// The ID every entry in this node stores its own as a difference from.
376    ///
377    /// It is the first entry's ID, and it stays that even after the first entry
378    /// is deleted, because the differences behind it are relative to it.
379    master: Id,
380    lp: Listpack,
381}
382
383/// What a stream looks like from the outside, for working out a group's
384/// counters.
385///
386/// Five numbers taken together at one moment. The reason they travel as a
387/// bundle is that both rules below read several of them and the answers have to
388/// come from the same instant, and the reason it is a private type is that
389/// nobody outside wants five loose numbers, they want [`Stream::lag`].
390#[derive(Debug, Clone, Copy)]
391struct Edges {
392    added: u64,
393    length: u64,
394    first: Option<Id>,
395    last: Id,
396    max_deleted: Id,
397}
398
399impl Edges {
400    /// How many entries a reader sitting at `id` must have passed, when that is
401    /// exactly knowable, which is the three positions where it is.
402    ///
403    /// At or past the last ID ever handed out, everything that was ever added
404    /// has gone by. Below the oldest entry left, with nothing deleted inside
405    /// what is left, everything that has gone is behind and the length is what
406    /// is in front. Exactly on the oldest entry left, the same plus one.
407    ///
408    /// Anywhere else the answer is genuinely unknown rather than approximate,
409    /// because working it out would mean counting the entries between here and
410    /// there, and that is the walk the whole counter exists to avoid.
411    fn estimate(&self, id: Id) -> Option<u64> {
412        if self.added == 0 {
413            return Some(0);
414        }
415        if id >= self.last {
416            return Some(self.added);
417        }
418        let first = self.first?;
419        // A hole below the oldest entry left is not a hole in what is left, so
420        // the subtraction below still holds. That is the case a trim makes, and
421        // it is why a trim does not cost a group its lag.
422        if self.max_deleted != Id::MIN && self.max_deleted >= first {
423            return None;
424        }
425        let behind = self.added - self.length;
426        match id.cmp(&first) {
427            Ordering::Less => Some(behind),
428            Ordering::Equal => Some(behind + 1),
429            Ordering::Greater => None,
430        }
431    }
432
433    /// Whether anything has been deleted at or above `id`.
434    ///
435    /// Only [`Stream::delete`] moves `max_deleted`, so a trim does not count,
436    /// and a stream with nothing left in it does not either, since there is no
437    /// gap in an empty stream for a reader to fall into.
438    fn holed_from(&self, id: Id) -> bool {
439        if self.length == 0 || self.max_deleted == Id::MIN {
440            return false;
441        }
442        if self.first.is_some_and(|first| first > self.max_deleted) {
443            return false;
444        }
445        id <= self.max_deleted
446    }
447
448    /// A group's lag, the good way first and the subtraction second.
449    fn lag(&self, group: &Group) -> Option<u64> {
450        if let Some(read) = self.estimate(group.last_id()) {
451            return Some(self.added.saturating_sub(read));
452        }
453        let read = group.entries_read()?;
454        if self.holed_from(group.last_id()) {
455            return None;
456        }
457        Some(self.added.saturating_sub(read))
458    }
459
460    /// What a group's read counter becomes once `id` has been handed out.
461    ///
462    /// One more than it was, while it is known and nothing has been deleted
463    /// ahead of the entry just delivered. Otherwise the estimate above gets a
464    /// go, which is what lets a group that has read all the way to the end come
465    /// back from not knowing.
466    fn on_deliver(&self, group: &Group, id: Id) -> Option<u64> {
467        match group.entries_read() {
468            Some(read) if !self.holed_from(id) => Some(read + 1),
469            _ => self.estimate(id),
470        }
471    }
472}
473
474/// The only frozen form there is: the nodes as they stand, then the groups.
475///
476/// There is no packed form to sit beside it the way the other collections have
477/// one, because a stream is a listpack per node from the first entry and never
478/// changes shape.
479const FORM_NODES: u8 = 1;
480
481/// Where a group read stopped, so the next one does not start from the front.
482///
483/// `XREADGROUP GROUP g c COUNT 1 STREAMS key >` hands over one entry and moves
484/// the group's bookmark one along. Without a mark the read after it walks the
485/// node's blob from the first entry to find the one after the bookmark, and at
486/// the default hundred entries a node that is fifty entries of decoding to hand
487/// back one of them, which makes a consumer draining a stream quadratic in the
488/// node rather than linear.
489///
490/// The mark is only believed when the stream has not moved any bytes since it
491/// was taken and the read is asking for exactly the ID the walk that left it
492/// would be asked for next. Anything else, an `XGROUP SETID` most of all, walks
493/// from the front the way it always did.
494#[derive(Debug, Clone, Copy)]
495pub(crate) struct Cursor {
496    /// The stream's mutation count when this was taken.
497    epoch: u64,
498    /// The ID the walk that left this mark would be asked for next, which is
499    /// one past the last entry it handed over. A read starting anywhere else
500    /// cannot use it, because the mark says nothing about what is behind it.
501    next: Id,
502    /// Which node, by its master ID, which is unique and never reused.
503    master: Id,
504    /// Where in that node's blob to pick up.
505    byte: usize,
506}
507
508/// A log of entries in ID order.
509#[derive(Debug, Clone, Default)]
510pub struct Stream {
511    nodes: VecDeque<Node>,
512    /// Live entries, which is what `XLEN` answers.
513    length: u64,
514    /// The greatest ID ever appended, which does not go down when it is deleted.
515    last: Id,
516    /// The greatest ID ever deleted, which `XINFO` reports.
517    max_deleted: Id,
518    /// How many entries have ever been appended.
519    ///
520    /// Not the length. It only goes up, and it is what a consumer group uses to
521    /// work out how far behind it is without walking anything.
522    added: u64,
523    /// The consumer groups, by name.
524    ///
525    /// A vector because a stream has a handful of groups and the name is looked
526    /// up once a command, so a linear scan beats hashing and brings nothing
527    /// with it. The same argument the group makes about its consumers.
528    groups: Vec<(Vec<u8>, Group)>,
529    /// How many times something has moved bytes inside a node.
530    ///
531    /// A [`Cursor`] is a byte offset into a node's blob and it is only good for
532    /// as long as the bytes it counted past are where they were. An append is
533    /// not one of these events, because it writes at the end and the header
534    /// fields it rewrites are the same width either side of it almost always,
535    /// and the almost is what this counts.
536    epoch: u64,
537}
538
539/// Two streams are the same when they hold the same entries and the same
540/// groups. The mutation count is not part of that. It is a number the resume
541/// cursors compare themselves against, nothing outside this file can see it,
542/// and a stream frozen and thawed is the same stream even though its count
543/// starts again at zero.
544impl PartialEq for Stream {
545    fn eq(&self, other: &Stream) -> bool {
546        self.nodes == other.nodes
547            && self.length == other.length
548            && self.last == other.last
549            && self.max_deleted == other.max_deleted
550            && self.added == other.added
551            && self.groups == other.groups
552    }
553}
554
555impl Eq for Stream {}
556
557impl Stream {
558    /// An empty stream.
559    #[must_use]
560    pub fn new() -> Stream {
561        Stream::default()
562    }
563
564    /// The nodes as they stand, each one its master ID and its blob.
565    ///
566    /// This is what an RDB payload for a stream is made of, and it is why the
567    /// node layout was copied from Redis in the first place. The master ID is
568    /// the sixteen byte rax key a real server writes and the blob is the
569    /// listpack it writes after it, so [`crate::rdb`] puts both on the wire with
570    /// nothing converted on the way.
571    pub(crate) fn raw_nodes(&self) -> impl Iterator<Item = (Id, &[u8])> + '_ {
572        self.nodes.iter().map(|n| (n.master, n.lp.as_bytes()))
573    }
574
575    /// Put a node on the end of a stream that is being built from a payload.
576    ///
577    /// Refuses a master that does not beat the one before it, for the reason
578    /// [`Stream::thaw`] gives: the nodes are searched by binary search over
579    /// their masters, so a run that is not in order would leave a lookup unable
580    /// to say which node an ID belongs in.
581    pub(crate) fn push_raw_node(&mut self, master: Id, lp: Listpack) -> bool {
582        if self.nodes.back().is_some_and(|n| n.master >= master) {
583            return false;
584        }
585        self.nodes.push_back(Node { master, lp });
586        true
587    }
588
589    /// Put the counters on a stream that is being built from a payload.
590    ///
591    /// The same three checks [`Stream::thaw`] makes, because a payload from a
592    /// client has even less claim to be believed than bytes off our own disk.
593    pub(crate) fn set_counters(
594        &mut self,
595        length: u64,
596        last: Id,
597        max_deleted: Id,
598        added: u64,
599    ) -> bool {
600        if length > added || max_deleted > last || (self.nodes.is_empty() && length != 0) {
601            return false;
602        }
603        self.length = length;
604        self.last = last;
605        self.max_deleted = max_deleted;
606        self.added = added;
607        true
608    }
609
610    /// Put a group on a stream that is being built from a payload.
611    pub(crate) fn push_group(&mut self, name: &[u8], group: Group) -> bool {
612        if self.groups.iter().any(|(had, _)| had == name) {
613            return false;
614        }
615        self.groups.push((name.to_vec(), group));
616        true
617    }
618
619    /// Write the stream out as the bytes a tier can hold, for
620    /// [`crate::keyspace::Keyspace`] to hand back to [`Stream::thaw`].
621    ///
622    /// The nodes go out as the listpacks they already are, one master ID and one
623    /// blob each. That is the whole point of the node layout: a run of entries
624    /// is already a flat sequence of bytes with no pointers in it, so freezing
625    /// one is a copy and thawing it is a length check. Only the counters and the
626    /// consumer groups need a form of their own.
627    pub fn freeze(&self, out: &mut Vec<u8>) {
628        out.push(FORM_NODES);
629        frozen::put_uint(out, self.length);
630        frozen::put_uint(out, self.last.ms);
631        frozen::put_uint(out, self.last.seq);
632        frozen::put_uint(out, self.max_deleted.ms);
633        frozen::put_uint(out, self.max_deleted.seq);
634        frozen::put_uint(out, self.added);
635
636        frozen::put_uint(out, self.nodes.len() as u64);
637        for node in &self.nodes {
638            frozen::put_uint(out, node.master.ms);
639            frozen::put_uint(out, node.master.seq);
640            frozen::put_bytes(out, node.lp.as_bytes());
641        }
642
643        frozen::put_uint(out, self.groups.len() as u64);
644        for (name, group) in &self.groups {
645            frozen::put_bytes(out, name);
646            group.freeze(out);
647        }
648    }
649
650    /// Read back a stream [`Stream::freeze`] wrote.
651    ///
652    /// What is inside a node is checked as far as [`Listpack::from_bytes`]
653    /// checks it, which is that the header, the lengths and the terminator all
654    /// agree, and no further. Every walk over a node's contents already returns
655    /// early on anything it does not understand rather than trusting what it
656    /// finds, so a structurally sound listpack full of nonsense answers an empty
657    /// range instead of panicking. That is the same trust a node written by
658    /// Redis and loaded from an RDB file gets today.
659    pub fn thaw(bytes: &[u8]) -> Result<Stream, Broken> {
660        let mut cut = frozen::Cut::new(bytes);
661        if cut.byte()? != FORM_NODES {
662            return Err(Broken::Form);
663        }
664        let length = cut.uint()?;
665        let last = Id::new(cut.uint()?, cut.uint()?);
666        let max_deleted = Id::new(cut.uint()?, cut.uint()?);
667        let added = cut.uint()?;
668        // Deleting an entry needs an entry, and so does reading one, so a stream
669        // that has lost more than it ever took or is holding more than it was
670        // ever given did not come from `freeze`.
671        if length > added || max_deleted > last {
672            return Err(Broken::Body);
673        }
674
675        let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
676        // A node is a master ID and a listpack, so it cannot be under a byte and
677        // a count past what is left is short rather than a reservation to make.
678        if n > cut.rest().len() {
679            return Err(Broken::Short);
680        }
681        let mut nodes = VecDeque::with_capacity(n);
682        let mut prev: Option<Id> = None;
683        for _ in 0..n {
684            let master = Id::new(cut.uint()?, cut.uint()?);
685            // Nodes are consecutive runs in ID order, so a master that does not
686            // beat the one before it would leave a lookup unable to pick the
687            // node an ID belongs in.
688            if prev.is_some_and(|p| p >= master) {
689                return Err(Broken::Body);
690            }
691            prev = Some(master);
692            let lp = Listpack::from_bytes(cut.bytes()?).map_err(|_| Broken::Body)?;
693            nodes.push_back(Node { master, lp });
694        }
695        if nodes.is_empty() && length != 0 {
696            return Err(Broken::Body);
697        }
698
699        let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
700        if n > cut.rest().len() {
701            return Err(Broken::Short);
702        }
703        let mut groups: Vec<(Vec<u8>, Group)> = Vec::with_capacity(n);
704        for _ in 0..n {
705            let name = cut.bytes()?;
706            // Groups are found by a scan for the name, so a repeat would leave
707            // the second one holding entries that nothing could acknowledge.
708            if groups.iter().any(|(had, _)| had == name) {
709                return Err(Broken::Body);
710            }
711            groups.push((name.to_vec(), Group::thaw(&mut cut)?));
712        }
713
714        Ok(Stream {
715            nodes,
716            length,
717            last,
718            max_deleted,
719            added,
720            groups,
721            epoch: 0,
722        })
723    }
724
725    /// How many live entries there are, which is `XLEN`.
726    #[must_use]
727    #[inline]
728    pub fn len(&self) -> u64 {
729        self.length
730    }
731
732    /// Whether there are no live entries.
733    ///
734    /// A stream can be empty and still exist, unlike every other collection
735    /// here, because `XADD` followed by `XDEL` leaves a key whose last ID a new
736    /// entry still has to beat.
737    #[must_use]
738    #[inline]
739    pub fn is_empty(&self) -> bool {
740        self.length == 0
741    }
742
743    /// The greatest ID ever appended, whether or not it is still here.
744    #[must_use]
745    #[inline]
746    pub fn last_id(&self) -> Id {
747        self.last
748    }
749
750    /// The greatest ID ever deleted, or [`Id::MIN`] if none ever was.
751    #[must_use]
752    #[inline]
753    pub fn max_deleted_id(&self) -> Id {
754        self.max_deleted
755    }
756
757    /// How many entries have ever been appended.
758    #[must_use]
759    #[inline]
760    pub fn added(&self) -> u64 {
761        self.added
762    }
763
764    /// The lowest live ID, or `None` when there are none.
765    ///
766    /// Walks the first node, because the first entry in it may have been
767    /// deleted and its bytes are still there. That is at most a node's worth of
768    /// steps and it is only asked for by `XINFO`.
769    #[must_use]
770    pub fn first_id(&self) -> Option<Id> {
771        let mut found = None;
772        self.walk(Id::MIN, Id::MAX, Some(1), &mut |id, _| {
773            found = Some(id);
774            false
775        });
776        found
777    }
778
779    /// The highest live ID, or `None` when there are none.
780    ///
781    /// Not the same as [`Stream::last_id`], which is the last ID handed out and
782    /// stays where it is when that entry is deleted. This is the greatest ID a
783    /// reader can still find, and `XSETID` is the one caller that needs the
784    /// difference, because it refuses to move the bookmark below an entry that
785    /// is still there.
786    #[must_use]
787    pub fn top_id(&self) -> Option<Id> {
788        let mut found = None;
789        self.rev_range(Id::MIN, Id::MAX, Some(1), |id, _| {
790            found = Some(id);
791            false
792        });
793        found
794    }
795
796    /// `XSETID`, which moves the bookmark and the two counters behind it.
797    ///
798    /// The bookmark decides what the next `XADD *` hands out and what a group
799    /// created at `$` starts from, so moving it is how a replica is made to
800    /// agree with a primary and how a stream is rebuilt from a log. The two
801    /// counters are optional because Redis added them later and a caller that
802    /// does not name them leaves them alone.
803    ///
804    /// # Errors
805    ///
806    /// [`Refused::NotGreater`] when `last` is below an entry that is still in
807    /// the stream, since a reader holding that entry's ID would then be reading
808    /// past the end of a stream that has not ended.
809    pub fn set_id(
810        &mut self,
811        last: Id,
812        added: Option<u64>,
813        max_deleted: Option<Id>,
814    ) -> Result<(), Refused> {
815        if self.top_id().is_some_and(|top| last < top) {
816            return Err(Refused::NotGreater);
817        }
818        self.last = last;
819        if let Some(added) = added {
820            self.added = added;
821        }
822        if let Some(id) = max_deleted {
823            self.max_deleted = id;
824        }
825        Ok(())
826    }
827
828    /// What the ID would be if `XADD key *` ran now with clock reading `now`.
829    ///
830    /// The clock unless the clock has not moved, or has gone backwards, in which
831    /// case it is the last ID with one added. A stream never goes back on its
832    /// word about ordering just because the machine's clock did.
833    #[must_use]
834    pub fn auto_id(&self, now: u64) -> Option<Id> {
835        if now > self.last.ms {
836            Some(Id { ms: now, seq: 0 })
837        } else {
838            self.last.next()
839        }
840    }
841
842    /// The next sequence inside `ms`, which is what `XADD key ms-*` asks for.
843    #[must_use]
844    pub fn auto_seq(&self, ms: u64) -> Option<Id> {
845        if ms > self.last.ms {
846            Some(Id { ms, seq: 0 })
847        } else if ms == self.last.ms {
848            self.last.next().filter(|id| id.ms == ms)
849        } else {
850            None
851        }
852    }
853
854    /// Append an entry, which is `XADD` once the ID has been settled.
855    ///
856    /// # Errors
857    ///
858    /// [`Refused`] when the ID is zero or is not greater than [`Stream::last_id`].
859    /// Nothing else can fail: an append never needs to move an entry that is
860    /// already here.
861    pub fn append(
862        &mut self,
863        id: Id,
864        fields: &[(&[u8], &[u8])],
865        limits: Limits,
866    ) -> Result<(), Refused> {
867        if id == Id::MIN {
868            return Err(Refused::Zero);
869        }
870        if id <= self.last {
871            return Err(Refused::NotGreater);
872        }
873
874        // Everything but the field names and values, which is why it is a guess
875        // rather than a measurement: the point is to start a new node before the
876        // current one goes past its limit, not to predict its size exactly.
877        let size: usize = fields
878            .iter()
879            .map(|(f, v)| f.len() + v.len() + 11)
880            .sum::<usize>()
881            + 32;
882
883        let fits = match self.nodes.back() {
884            Some(node) => {
885                let (count, deleted) = counts(&node.lp);
886                node.lp.byte_len() + size < limits.max_node_bytes
887                    && (count + deleted) < limits.max_node_entries as u64
888            }
889            None => false,
890        };
891
892        if !fits {
893            self.nodes.push_back(Node {
894                master: id,
895                lp: master_of(fields),
896            });
897        }
898        let node = self.nodes.back_mut().expect("a node was just made sure of");
899        let same = same_fields(&node.lp, fields);
900        write_entry(&mut node.lp, node.master, id, fields, same);
901        // The entry itself goes on the end and moves nothing, so a group's mark
902        // into this node survives an append and lands on what was just written,
903        // which is the whole point. The counts in front of it are the one part
904        // that can move, and this is where that gets noticed.
905        if bump(&mut node.lp, 1, 0) {
906            self.epoch = self.epoch.wrapping_add(1);
907        }
908
909        self.length += 1;
910        self.added += 1;
911        self.last = id;
912        Ok(())
913    }
914
915    /// Delete the entry with that ID, answering whether there was one.
916    ///
917    /// The bytes stay where they are and a bit says the entry is gone, unless it
918    /// was the last live entry in its node, in which case the node goes.
919    pub fn delete(&mut self, id: Id) -> bool {
920        if !self.remove(id) {
921            return false;
922        }
923        self.max_deleted = self.max_deleted.max(id);
924        true
925    }
926
927    /// Delete an entry, saying what to do about the groups, which is `XDELEX`.
928    ///
929    /// [`Refs::Acked`] is the interesting one and it asks a wider question than
930    /// its name does. An entry is safe to take when no group is holding it in a
931    /// pending list and no group's bookmark is still behind it, because a group
932    /// that has not reached the entry yet has not had its chance at it. So a
933    /// stream with one group sitting at `0-0` refuses every `ACKED` delete, and
934    /// that is a real server's answer and not an over careful reading of it.
935    pub fn delete_ref(&mut self, id: Id, refs: Refs) -> Fate {
936        if refs == Refs::Acked && self.still_wanted(id) {
937            return Fate::Held;
938        }
939        if !self.delete(id) {
940            return Fate::Missing;
941        }
942        if refs == Refs::Drop {
943            self.drop_refs(id);
944        }
945        Fate::Gone
946    }
947
948    /// Acknowledge an entry for one group and then delete it, which is `XACKDEL`.
949    ///
950    /// The acknowledgement is the part that decides the answer. An ID this group
951    /// was not holding is [`Fate::Missing`] whether or not the entry is in the
952    /// stream, and an ID it was holding is never `Missing`, so a caller reading
953    /// the reply is being told about its own pending list and not about the log.
954    pub fn ack_delete(&mut self, group: &[u8], id: Id, refs: Refs) -> Fate {
955        let Some(g) = self.group_mut(group) else {
956            return Fate::Missing;
957        };
958        if !g.ack(id) {
959            return Fate::Missing;
960        }
961        if refs == Refs::Acked && self.still_wanted(id) {
962            return Fate::Held;
963        }
964        self.delete(id);
965        if refs == Refs::Drop {
966            self.drop_refs(id);
967        }
968        Fate::Gone
969    }
970
971    /// Hand an entry back to a group without acknowledging it, which is `XNACK`.
972    ///
973    /// `force` makes a pending entry out of one that was not pending, and like
974    /// [`Stream::claim`]'s `FORCE` it only works on an entry that is really in
975    /// the stream. Answers whether anything happened, and `None` when there is
976    /// no such group.
977    pub fn nack(&mut self, group: &[u8], id: Id, retry: Retry, force: bool) -> Option<bool> {
978        let here = self.contains(id);
979        let g = self.group_mut(group)?;
980        if g.release(id, retry) {
981            return Some(true);
982        }
983        if force && here {
984            g.force_release(id, retry);
985            return Some(true);
986        }
987        Some(false)
988    }
989
990    /// Whether any group could still be handed `id`, which is what `ACKED` asks.
991    fn still_wanted(&self, id: Id) -> bool {
992        self.groups
993            .iter()
994            .any(|(_, g)| g.nack(id).is_some() || id > g.last_id())
995    }
996
997    /// Take an ID out of every group's pending list, which is what `DELREF` does.
998    fn drop_refs(&mut self, id: Id) {
999        for (_, g) in &mut self.groups {
1000            g.forget(id);
1001        }
1002    }
1003
1004    /// The same without recording it.
1005    ///
1006    /// `XDEL` moves `max-deleted-entry-id` and trimming does not, which is
1007    /// Redis's rule and a reasonable one: that field is there so a reader can
1008    /// tell whether an ID it is holding was taken out from under it, and a
1009    /// trim that took the oldest entries says nothing about that.
1010    fn remove(&mut self, id: Id) -> bool {
1011        let Some(at) = self.node_of(id) else {
1012            return false;
1013        };
1014        let node = &self.nodes[at];
1015        let Some((offset, flags)) = find(&node.lp, node.master, id) else {
1016            return false;
1017        };
1018        if flags & DELETED != 0 {
1019            return false;
1020        }
1021
1022        let (count, _) = counts(&node.lp);
1023        if count == 1 {
1024            self.nodes.remove(at);
1025        } else {
1026            let node = &mut self.nodes[at];
1027            set_int(&mut node.lp, offset, flags | DELETED);
1028            bump(&mut node.lp, -1, 1);
1029        }
1030        // Marking an entry dead writes into the middle of the node, so anything
1031        // behind it has moved and every group's mark is now a byte offset to
1032        // nowhere. Deletes are rare next to appends and reads, so this throws
1033        // all of them away rather than working out which ones survived.
1034        self.epoch = self.epoch.wrapping_add(1);
1035        self.length -= 1;
1036        true
1037    }
1038
1039    /// The five facts every counter a group keeps is worked out from.
1040    ///
1041    /// Read once and carried, rather than asked for again per entry, because
1042    /// [`Stream::first_id`] walks a node and a delivery cannot change any of the
1043    /// five: handing an entry to a consumer neither adds one nor removes one.
1044    fn edges(&self) -> Edges {
1045        Edges {
1046            added: self.added,
1047            length: self.length,
1048            first: self.first_id(),
1049            last: self.last,
1050            max_deleted: self.max_deleted,
1051        }
1052    }
1053
1054    /// How far behind a group is, or `None` when that cannot be worked out.
1055    ///
1056    /// Two ways of answering and the good one is tried first. If the group's
1057    /// bookmark is somewhere the distance from the start of time is exactly
1058    /// known, which is the last ID, past it, or before the first entry left,
1059    /// that distance is the answer. Otherwise the group's own counter will do,
1060    /// but only while nothing has been deleted at or above the bookmark, since
1061    /// a hole ahead of the group means it will read fewer entries than the
1062    /// subtraction is expecting.
1063    ///
1064    /// Both paths and their order were read off Redis 8.10.1 rather than worked
1065    /// out, because reasoning gives the wrong answer on the case that matters:
1066    /// a group sitting at `0-0` on a stream trimmed from five entries to two
1067    /// reports a lag of two and not five, which is the estimate winning over a
1068    /// subtraction that is valid and is further from the truth.
1069    #[must_use]
1070    pub fn lag(&self, group: &Group) -> Option<u64> {
1071        self.edges().lag(group)
1072    }
1073
1074    /// Cut the stream down to `len` entries, dropping the oldest, which is
1075    /// `XTRIM key MAXLEN len`. Answers how many went.
1076    ///
1077    /// `exact` is Redis's `=` against `~`. Without it only whole nodes are
1078    /// dropped, so the stream is left at `len` or a little over and no node is
1079    /// ever rewritten. That is the form to use, and it is why `~` exists.
1080    ///
1081    /// `limit` is Redis's `LIMIT`, which stops the trim once that many entries
1082    /// have gone rather than once the stream is short enough. It exists because
1083    /// a capped stream that has fallen a long way behind would otherwise spend
1084    /// one command dropping millions of entries with the shard doing nothing
1085    /// else, and the next write will carry on where this one stopped.
1086    pub fn trim_maxlen(&mut self, len: u64, exact: bool, limit: Option<u64>) -> u64 {
1087        let mut gone = 0;
1088        while self.length > len && !limit.is_some_and(|cap| gone >= cap) {
1089            let Some(node) = self.nodes.front() else {
1090                break;
1091            };
1092            let (count, _) = counts(&node.lp);
1093            if self.length - count >= len {
1094                self.length -= count;
1095                gone += count;
1096                self.nodes.pop_front();
1097                continue;
1098            }
1099            if !exact {
1100                break;
1101            }
1102            let Some(id) = self.first_id() else { break };
1103            self.remove(id);
1104            gone += 1;
1105        }
1106        gone
1107    }
1108
1109    /// Drop every entry below `id`, which is `XTRIM key MINID id`. Answers how
1110    /// many went.
1111    ///
1112    /// `exact` and `limit` mean what they do for [`Stream::trim_maxlen`].
1113    pub fn trim_minid(&mut self, id: Id, exact: bool, limit: Option<u64>) -> u64 {
1114        let mut gone = 0;
1115        while let Some(node) = self.nodes.front() {
1116            if limit.is_some_and(|cap| gone >= cap) {
1117                break;
1118            }
1119            let (count, _) = counts(&node.lp);
1120            if last_of(node) < id {
1121                self.length -= count;
1122                gone += count;
1123                self.nodes.pop_front();
1124                continue;
1125            }
1126            if !exact {
1127                break;
1128            }
1129            let Some(first) = self.first_id() else { break };
1130            if first >= id {
1131                break;
1132            }
1133            self.remove(first);
1134            gone += 1;
1135        }
1136        gone
1137    }
1138
1139    /// Every live entry from `start` to `end`, both ends included, oldest first.
1140    ///
1141    /// `count` stops the walk early, which is `XRANGE ... COUNT n`. The callback
1142    /// answers whether to carry on, so a caller filling a fixed reply can stop
1143    /// without knowing how many it wanted up front. Answers how many entries the
1144    /// callback saw.
1145    pub fn range<F>(&self, start: Id, end: Id, count: Option<usize>, mut f: F) -> usize
1146    where
1147        F: FnMut(Id, Fields<'_>) -> bool,
1148    {
1149        self.walk(start, end, count, &mut f)
1150    }
1151
1152    /// The same, newest first, which is `XREVRANGE`.
1153    ///
1154    /// `start` and `end` are still the low and the high end of the range, so a
1155    /// caller does not have to swap them and the command layer does, once, where
1156    /// the argument order is Redis's problem.
1157    pub fn rev_range<'s, F>(&'s self, start: Id, end: Id, count: Option<usize>, mut f: F) -> usize
1158    where
1159        F: FnMut(Id, Fields<'_>) -> bool,
1160    {
1161        let mut seen = 0;
1162        // A node is a hundred entries, so buffering one node's worth of marks
1163        // and handing them back in reverse is cheaper and a great deal clearer
1164        // than walking the blob backwards over the entry lengths. The buffer is
1165        // reused across nodes, so the whole reverse scan allocates once.
1166        let mut buf: Vec<(Id, Fields<'s>)> = Vec::new();
1167        // Straight to the node the high end falls in, the same binary search the
1168        // forward walk starts with. Walking back from the newest node instead
1169        // would skip over every node above `end` one at a time, which for a
1170        // window in the middle of a million entries is five thousand nodes
1171        // touched to read a hundred.
1172        let last = self.node_from(end);
1173        for node in self.nodes.iter().take(last + 1).rev() {
1174            // Only reachable when every node is above `end`, since the search
1175            // clamps to the front rather than saying there is nothing.
1176            if node.master > end {
1177                continue;
1178            }
1179            if last_of(node) < start {
1180                break;
1181            }
1182            buf.clear();
1183            each(&node.lp, node.master, None, &mut |id, _, fields| {
1184                if id >= start && id <= end {
1185                    buf.push((id, fields));
1186                }
1187                id <= end
1188            });
1189            for (id, fields) in buf.drain(..).rev() {
1190                if count.is_some_and(|want| seen >= want) {
1191                    return seen;
1192                }
1193                seen += 1;
1194                if !f(id, fields) {
1195                    return seen;
1196                }
1197            }
1198        }
1199        seen
1200    }
1201
1202    /// Whether an entry with this ID is there and live.
1203    ///
1204    /// What `XCLAIM` asks before it hands a pending entry to somebody, since an
1205    /// entry that has been deleted or trimmed away is work nobody can do.
1206    #[must_use]
1207    pub fn contains(&self, id: Id) -> bool {
1208        let Some(at) = self.node_of(id) else {
1209            return false;
1210        };
1211        let node = &self.nodes[at];
1212        find(&node.lp, node.master, id).is_some_and(|(_, flags)| flags & DELETED == 0)
1213    }
1214
1215    /// Make a consumer group, and say whether it was not already there.
1216    ///
1217    /// `XGROUP CREATE`. `last` is where it starts reading after, which is
1218    /// [`Stream::last_id`] for `$` and [`Id::MIN`] for `0`.
1219    pub fn create_group(&mut self, name: &[u8], last: Id, read: Option<u64>) -> bool {
1220        if self.group(name).is_some() {
1221            return false;
1222        }
1223        self.groups.push((name.to_vec(), Group::new(last, read)));
1224        true
1225    }
1226
1227    /// Take a group out, and say whether it was there.
1228    pub fn destroy_group(&mut self, name: &[u8]) -> bool {
1229        let Some(at) = self.groups.iter().position(|(n, _)| n == name) else {
1230            return false;
1231        };
1232        self.groups.remove(at);
1233        true
1234    }
1235
1236    /// One group by name.
1237    #[must_use]
1238    pub fn group(&self, name: &[u8]) -> Option<&Group> {
1239        self.groups
1240            .iter()
1241            .find(|(n, _)| n.as_slice() == name)
1242            .map(|(_, g)| g)
1243    }
1244
1245    /// One group by name, to change.
1246    pub fn group_mut(&mut self, name: &[u8]) -> Option<&mut Group> {
1247        self.groups
1248            .iter_mut()
1249            .find(|(n, _)| n.as_slice() == name)
1250            .map(|(_, g)| g)
1251    }
1252
1253    /// Every group, with its name.
1254    pub fn groups(&self) -> impl Iterator<Item = (&[u8], &Group)> + '_ {
1255        self.groups.iter().map(|(n, g)| (n.as_slice(), g))
1256    }
1257
1258    /// Hand new entries to a consumer, which is `XREADGROUP ... >`.
1259    ///
1260    /// Every entry after the group's bookmark, up to `count`, delivered to
1261    /// `consumer` and written into the pending list as it goes. The consumer is
1262    /// created if it is not there, because a consumer exists by turning up.
1263    ///
1264    /// `noack` is Redis's `NOACK`, which hands the entries over without writing
1265    /// them into the pending list at all. The group still counts them as read,
1266    /// so the lag is the same either way, and the consumer is on its own if it
1267    /// dies holding one.
1268    ///
1269    /// Answers how many entries the callback saw, or `None` when there is no
1270    /// such group.
1271    pub fn read_group<F>(
1272        &mut self,
1273        group: &[u8],
1274        consumer: &[u8],
1275        count: Option<usize>,
1276        noack: bool,
1277        now: u64,
1278        mut f: F,
1279    ) -> Option<usize>
1280    where
1281        F: FnMut(Id, Fields<'_>) -> bool,
1282    {
1283        // Before the split borrow, because it walks a node and the walk below
1284        // holds the nodes. Nothing a delivery does can change any of it.
1285        let edges = self.edges();
1286        // Field by field, so that walking the nodes and writing the pending list
1287        // are two borrows the compiler can see are disjoint.
1288        let Stream {
1289            nodes,
1290            groups,
1291            epoch,
1292            ..
1293        } = self;
1294        let epoch = *epoch;
1295        let (_, g) = groups.iter_mut().find(|(n, _)| n.as_slice() == group)?;
1296        let slot = g.consumer_or_create(consumer, now);
1297        let Some(from) = g.last_id().next() else {
1298            // The bookmark is at the very last ID there is, so there is nothing
1299            // after it and never will be.
1300            g.touch(slot, now, false);
1301            return Some(0);
1302        };
1303        // Where the last read of this group stopped, when it is still good for
1304        // this one. A consumer draining a stream one entry at a time asks for
1305        // the entry right after the one it just got, and this is what saves it
1306        // decoding the node from the front to find it.
1307        let resume = g.resume(epoch, from);
1308        let (seen, mark) = walk_nodes(nodes, from, Id::MAX, count, resume, &mut |id, fields| {
1309            // Worked out before the bookmark moves, because the rule asks where
1310            // the group was when the entry was handed over.
1311            let read = edges.on_deliver(g, id);
1312            if noack {
1313                g.skip(id);
1314            } else {
1315                g.deliver(slot, id, now);
1316            }
1317            g.set_read(read);
1318            f(id, fields)
1319        });
1320        let next = g.last_id().next();
1321        g.set_resume(match (mark, next) {
1322            (Some((master, byte)), Some(next)) => Some(Cursor {
1323                epoch,
1324                next,
1325                master,
1326                byte,
1327            }),
1328            _ => None,
1329        });
1330        g.touch(slot, now, seen > 0);
1331        Some(seen)
1332    }
1333
1334    /// Re-read what a consumer is already holding, which is `XREADGROUP` with an
1335    /// ID rather than `>`.
1336    ///
1337    /// Every pending entry of that consumer after `after`, oldest first. Each
1338    /// one counts as handed out again, so its delivery time is reset and its
1339    /// count goes up. That is Redis's behaviour, checked rather than assumed,
1340    /// and it is the right one: the count is how many times a consumer has been
1341    /// told to do this work, and a consumer re-reading its backlog after a
1342    /// restart has been told again.
1343    ///
1344    /// An entry that has since been deleted or trimmed is still in the pending
1345    /// list and is handed to the callback with no fields, which is the null
1346    /// Redis puts in the reply. Clearing those out is [`Stream::claim`]'s job
1347    /// and not this one.
1348    pub fn read_group_pending<F>(
1349        &mut self,
1350        group: &[u8],
1351        consumer: &[u8],
1352        after: Id,
1353        count: Option<usize>,
1354        now: u64,
1355        mut f: F,
1356    ) -> Option<usize>
1357    where
1358        F: FnMut(Id, Option<Fields<'_>>) -> bool,
1359    {
1360        // Which IDs, decided before anything is touched, so that the redelivery
1361        // and the walk are two passes over a small list rather than one pass
1362        // holding the group and the nodes at the same time.
1363        //
1364        // The consumer is created rather than looked up, because a history read
1365        // by a name nobody has used is an empty list and not a missing group. A
1366        // worker that restarts under a new name and asks for its own backlog
1367        // first is exactly that case, and Redis answers it with an empty list
1368        // and the consumer left behind.
1369        let g = self.group_mut(group)?;
1370        let slot = g.consumer_or_create(consumer, now);
1371        let ids: Vec<Id> = g
1372            .consumer(slot)
1373            .expect("the slot that was just made")
1374            .pending()
1375            .filter(|&id| id > after)
1376            .take(count.unwrap_or(usize::MAX))
1377            .collect();
1378        for &id in &ids {
1379            g.redeliver(id, now);
1380        }
1381        g.touch(slot, now, !ids.is_empty());
1382
1383        let mut seen = 0;
1384        for &id in &ids {
1385            seen += 1;
1386            let mut go = true;
1387            let mut found = false;
1388            self.walk(id, id, Some(1), &mut |got, fields| {
1389                found = true;
1390                go = f(got, Some(fields));
1391                false
1392            });
1393            if !found {
1394                go = f(id, None);
1395            }
1396            if !go {
1397                break;
1398            }
1399        }
1400        Some(seen)
1401    }
1402
1403    /// Move pending entries to a consumer, which is `XCLAIM`.
1404    ///
1405    /// Only entries idle at least `min_idle` move. `time` is what the delivery
1406    /// time becomes, `retry` replaces the delivery count when it is given, and
1407    /// `bump` says whether to add one to it, which `JUSTID` turns off. `force`
1408    /// makes a pending entry for an ID that is in the stream but was not
1409    /// pending.
1410    ///
1411    /// An ID that is pending but no longer in the stream is dropped from the
1412    /// pending list rather than claimed, and reported through `gone`, which is
1413    /// what Redis does and what stops a deleted entry being handed round
1414    /// forever. Answers the IDs that moved.
1415    #[allow(clippy::too_many_arguments)]
1416    pub fn claim(
1417        &mut self,
1418        group: &[u8],
1419        consumer: &[u8],
1420        ids: &[Id],
1421        min_idle: u64,
1422        time: u64,
1423        retry: Option<u64>,
1424        bump: bool,
1425        force: bool,
1426        now: u64,
1427        gone: &mut Vec<Id>,
1428    ) -> Option<Vec<Id>> {
1429        // Before the loop, so that a claim which takes nothing still leaves the
1430        // consumer behind. Redis creates it either way, and an `XAUTOCLAIM`
1431        // against an empty pending list is the ordinary way that happens: the
1432        // consumer turns up in `XINFO CONSUMERS` straight after, holding
1433        // nothing.
1434        self.group_mut(group)?.consumer_or_create(consumer, now);
1435        let mut took = Vec::new();
1436        for &id in ids {
1437            let here = self.contains(id);
1438            let g = self.group_mut(group)?;
1439            let slot = g.consumer_or_create(consumer, now);
1440            match g.nack(id) {
1441                Some(nack) => {
1442                    if !here {
1443                        g.forget(id);
1444                        gone.push(id);
1445                        continue;
1446                    }
1447                    if nack.idle(now) < min_idle {
1448                        continue;
1449                    }
1450                    if g.claim(id, slot, time, retry, bump) {
1451                        took.push(id);
1452                    }
1453                }
1454                None => {
1455                    // FORCE makes one out of nothing, but only for an entry that
1456                    // is really there. Redis ignores the rest in silence.
1457                    if force && here && g.force(id, slot, time, retry.unwrap_or(1)) {
1458                        took.push(id);
1459                    }
1460                }
1461            }
1462        }
1463        // Active only when something moved, which is the same rule a read
1464        // follows. A claim that found nothing idle enough leaves the consumer
1465        // reading as never active.
1466        if !took.is_empty() {
1467            let g = self.group_mut(group).expect("the group found a moment ago");
1468            let slot = g.consumer_or_create(consumer, now);
1469            g.touch(slot, now, true);
1470        }
1471        Some(took)
1472    }
1473
1474    /// Sweep the pending list for stale entries and claim them, which is
1475    /// `XAUTOCLAIM`.
1476    ///
1477    /// Starts at `start` and takes up to `count` entries that have been idle at
1478    /// least `min_idle`. Answers where a following call should carry on from,
1479    /// which is `None` at the end of the list, along with what was claimed and
1480    /// what was dropped for no longer being in the stream.
1481    #[allow(clippy::too_many_arguments)]
1482    pub fn autoclaim(
1483        &mut self,
1484        group: &[u8],
1485        consumer: &[u8],
1486        start: Id,
1487        min_idle: u64,
1488        count: usize,
1489        bump: bool,
1490        now: u64,
1491        gone: &mut Vec<Id>,
1492    ) -> Option<(Option<Id>, Vec<Id>)> {
1493        let mut ids = Vec::new();
1494        let cursor = self
1495            .group(group)?
1496            .claimable(start, min_idle, now, count, &mut ids);
1497        let took = self.claim(
1498            group, consumer, &ids, min_idle, now, None, bump, false, now, gone,
1499        )?;
1500        Some((cursor, took))
1501    }
1502
1503    /// How many bytes the entries and the groups take, not counting this struct.
1504    ///
1505    /// The name is the one every other body in this crate uses, because the
1506    /// keyspace asks all of them the same question through one trait and a
1507    /// stream that answered it under a different name would need its own arm.
1508    #[must_use]
1509    pub fn memory_bytes(&self) -> usize {
1510        let nodes: usize = self
1511            .nodes
1512            .iter()
1513            .map(|node| node.lp.byte_len() + std::mem::size_of::<Node>())
1514            .sum();
1515        let groups: usize = self
1516            .groups
1517            .iter()
1518            .map(|(name, g)| {
1519                name.capacity() + std::mem::size_of::<(Vec<u8>, Group)>() + g.memory_bytes()
1520            })
1521            .sum();
1522        nodes + groups
1523    }
1524
1525    /// How many nodes there are, which only a test and `XINFO STREAM FULL` care
1526    /// about.
1527    #[must_use]
1528    pub fn nodes(&self) -> usize {
1529        self.nodes.len()
1530    }
1531
1532    /// The shared walk behind [`Stream::range`] and [`Stream::first_id`].
1533    fn walk<F>(&self, start: Id, end: Id, count: Option<usize>, f: &mut F) -> usize
1534    where
1535        F: FnMut(Id, Fields<'_>) -> bool,
1536    {
1537        walk_nodes(&self.nodes, start, end, count, None, f).0
1538    }
1539
1540    /// The first node that can hold an entry at or after `id`.
1541    fn node_from(&self, id: Id) -> usize {
1542        node_from(&self.nodes, id)
1543    }
1544
1545    /// The node that would hold `id`, or `None` when no node covers it.
1546    fn node_of(&self, id: Id) -> Option<usize> {
1547        let at = self.node_from(id);
1548        let node = self.nodes.get(at)?;
1549        (node.master <= id && id <= last_of(node)).then_some(at)
1550    }
1551}
1552
1553/// The first node that can hold an entry at or after `id`.
1554///
1555/// The binary search the module docs are about. `partition_point` answers how
1556/// many nodes start strictly before `id`, and the one before that is the one
1557/// `id` would be in, since a node holds everything from its master ID up to the
1558/// next node's.
1559///
1560/// Free rather than a method so that a group read can hold the nodes and the
1561/// groups at the same time, which it has to because it walks the one to write
1562/// into the other.
1563fn node_from(nodes: &VecDeque<Node>, id: Id) -> usize {
1564    let after = nodes.partition_point(|node| node.master <= id);
1565    after.saturating_sub(1)
1566}
1567
1568/// Every live entry from `start` to `end`, both included, oldest first.
1569///
1570/// Free for the same reason [`node_from`] is.
1571/// `resume` is a node's master ID and a byte offset inside it from an earlier
1572/// walk, and the answer carries the same pair back for where this one stopped.
1573/// Only a group read has one, and it is the caller's job to have checked that
1574/// nothing has moved the bytes since.
1575fn walk_nodes<F>(
1576    nodes: &VecDeque<Node>,
1577    start: Id,
1578    end: Id,
1579    count: Option<usize>,
1580    resume: Option<(Id, usize)>,
1581    f: &mut F,
1582) -> (usize, Option<(Id, usize)>)
1583where
1584    F: FnMut(Id, Fields<'_>) -> bool,
1585{
1586    let mut seen = 0;
1587    let mut stop = false;
1588    let first = node_from(nodes, start);
1589    // Only when the mark is about the node this walk is starting in. Node master
1590    // IDs are unique and never reused, so a node that has since been trimmed
1591    // away cannot be mistaken for the one that took its place.
1592    let mut from =
1593        resume.and_then(|(master, byte)| (nodes.get(first)?.master == master).then_some(byte));
1594    let mut mark = None;
1595    for node in nodes.iter().skip(first) {
1596        if node.master > end {
1597            break;
1598        }
1599        let at = each(&node.lp, node.master, from.take(), &mut |id, _, fields| {
1600            if id > end {
1601                stop = true;
1602                return false;
1603            }
1604            if id < start {
1605                return true;
1606            }
1607            if count.is_some_and(|want| seen >= want) {
1608                stop = true;
1609                return false;
1610            }
1611            seen += 1;
1612            if !f(id, fields) {
1613                stop = true;
1614                return false;
1615            }
1616            true
1617        });
1618        mark = Some((node.master, at));
1619        if stop {
1620            break;
1621        }
1622    }
1623    (seen, mark)
1624}
1625
1626/// The field names and values of one entry.
1627///
1628/// Two walks rather than one because an entry that shares the node's field names
1629/// reads them from the master entry and its values from itself, and the whole
1630/// point of that layout is that the names are not copied per entry. Neither walk
1631/// allocates and neither is a copy.
1632#[derive(Debug, Clone)]
1633pub struct Fields<'a> {
1634    /// Where the names come from, when they are not interleaved with the values.
1635    names: Option<listpack::Iter<'a>>,
1636    body: listpack::Iter<'a>,
1637    left: usize,
1638}
1639
1640impl<'a> Iterator for Fields<'a> {
1641    type Item = (Entry<'a>, Entry<'a>);
1642
1643    fn next(&mut self) -> Option<(Entry<'a>, Entry<'a>)> {
1644        if self.left == 0 {
1645            return None;
1646        }
1647        self.left -= 1;
1648        let name = match &mut self.names {
1649            Some(names) => names.next()?,
1650            None => self.body.next()?,
1651        };
1652        Some((name, self.body.next()?))
1653    }
1654
1655    fn size_hint(&self) -> (usize, Option<usize>) {
1656        (self.left, Some(self.left))
1657    }
1658}
1659
1660impl ExactSizeIterator for Fields<'_> {}
1661
1662impl Fields<'_> {
1663    /// Whether there are no fields left.
1664    #[must_use]
1665    #[inline]
1666    pub fn is_empty(&self) -> bool {
1667        self.left == 0
1668    }
1669}
1670
1671/// A fresh node whose master fields are this entry's.
1672fn master_of(fields: &[(&[u8], &[u8])]) -> Listpack {
1673    let mut lp = Listpack::new();
1674    push_int(&mut lp, 0);
1675    push_int(&mut lp, 0);
1676    push_int(&mut lp, fields.len() as i64);
1677    for (name, _) in fields {
1678        lp.push(name);
1679    }
1680    push_int(&mut lp, 0);
1681    lp
1682}
1683
1684/// Whether these fields are exactly the node's master fields, in order.
1685fn same_fields(lp: &Listpack, fields: &[(&[u8], &[u8])]) -> bool {
1686    let mut it = lp.iter();
1687    let (_, _, want) = match (it.next(), it.next(), it.next()) {
1688        (Some(_), Some(_), Some(Entry::Int(n))) => ((), (), n),
1689        _ => return false,
1690    };
1691    if want != fields.len() as i64 {
1692        return false;
1693    }
1694    fields.iter().all(|(name, _)| match it.next() {
1695        Some(Entry::Str(s)) => s == *name,
1696        Some(Entry::Int(n)) => {
1697            let mut buf = [0u8; DIGITS_MAX];
1698            i64_digits(&mut buf, n) == *name
1699        }
1700        None => false,
1701    })
1702}
1703
1704/// The master entry's live and deleted counts.
1705fn counts(lp: &Listpack) -> (u64, u64) {
1706    let mut it = lp.iter();
1707    let count = int_or_zero(it.next());
1708    let deleted = int_or_zero(it.next());
1709    (count.max(0) as u64, deleted.max(0) as u64)
1710}
1711
1712/// Add to the master entry's two counts, answering whether that moved any bytes.
1713///
1714/// Both counts sit in front of every entry in the node, so a write that encodes
1715/// to a different width than what was there shifts the whole rest of the blob
1716/// along and every [`Cursor`] into it is a byte offset to nowhere. It usually
1717/// does not: a node holds a hundred entries by default and a listpack keeps
1718/// anything under a hundred and twenty eight in one byte, so an append writes
1719/// the same width it read almost every time. Almost, because the node limits are
1720/// the caller's to set.
1721fn bump(lp: &mut Listpack, live: i64, dead: i64) -> bool {
1722    let was = lp.byte_len();
1723    let (count, deleted) = counts(lp);
1724    let mut buf = [0u8; DIGITS_MAX];
1725    let at = count as i64 + live;
1726    lp.replace(0, u64_digits(&mut buf, at.max(0) as u64));
1727    let at = deleted as i64 + dead;
1728    lp.replace(1, u64_digits(&mut buf, at.max(0) as u64));
1729    lp.byte_len() != was
1730}
1731
1732/// An entry as an integer, or zero for anything else.
1733fn int_or_zero(entry: Option<Entry<'_>>) -> i64 {
1734    match entry {
1735        Some(Entry::Int(n)) => n,
1736        _ => 0,
1737    }
1738}
1739
1740/// Append an integer, which the listpack encodes as one because it parses as one.
1741fn push_int(lp: &mut Listpack, n: i64) {
1742    let mut buf = [0u8; DIGITS_MAX];
1743    lp.push(i64_digits(&mut buf, n));
1744}
1745
1746/// Overwrite the element at `index` with an integer.
1747fn set_int(lp: &mut Listpack, index: usize, n: i64) {
1748    let mut buf = [0u8; DIGITS_MAX];
1749    lp.replace(index, i64_digits(&mut buf, n));
1750}
1751
1752/// Write one entry onto the end of a node.
1753fn write_entry(lp: &mut Listpack, master: Id, id: Id, fields: &[(&[u8], &[u8])], same: bool) {
1754    let flags = if same { LIVE | SAME_FIELDS } else { LIVE };
1755    push_int(lp, flags);
1756    // Both halves as a plain difference from the master, wrapping, which is what
1757    // Redis writes. The sequence often goes down when the millisecond goes up,
1758    // so that difference is usually negative and it does not matter: adding it
1759    // back is the exact inverse whichever way it went.
1760    push_int(lp, id.ms.wrapping_sub(master.ms) as i64);
1761    push_int(lp, id.seq.wrapping_sub(master.seq) as i64);
1762    if same {
1763        for (_, value) in fields {
1764            lp.push(value);
1765        }
1766        push_int(lp, fields.len() as i64 + 3);
1767    } else {
1768        push_int(lp, fields.len() as i64);
1769        for (name, value) in fields {
1770            lp.push(name);
1771            lp.push(value);
1772        }
1773        push_int(lp, fields.len() as i64 * 2 + 4);
1774    }
1775}
1776
1777/// The greatest ID in a node, live or not.
1778fn last_of(node: &Node) -> Id {
1779    let mut last = node.master;
1780    each(&node.lp, node.master, None, &mut |id, _, _| {
1781        last = id;
1782        true
1783    });
1784    last
1785}
1786
1787/// Where the entry with that ID starts, and its flags, or `None`.
1788///
1789/// The offset is a listpack element index rather than a byte offset, because
1790/// that is what `replace` takes.
1791fn find(lp: &Listpack, master: Id, id: Id) -> Option<(usize, i64)> {
1792    let mut at = None;
1793    walk_node(lp, master, None, &mut |mark, flags, _| {
1794        if mark.id == id {
1795            // Always a `Some`, because this walk starts at the front, and the
1796            // `if let` rather than an unwrap keeps that a fact about this call
1797            // rather than something the mark has to promise everybody.
1798            if let Some(index) = mark.index {
1799                at = Some((index, flags));
1800            }
1801            return false;
1802        }
1803        mark.id < id
1804    });
1805    at
1806}
1807
1808/// Every entry in a node, live ones only, oldest first.
1809/// `from` and the answer are the resume mark [`walk_node`] takes and gives back.
1810fn each<'a, F>(lp: &'a Listpack, master: Id, from: Option<usize>, f: &mut F) -> usize
1811where
1812    F: FnMut(Id, usize, Fields<'a>) -> bool,
1813{
1814    walk_node(lp, master, from, &mut |mark, flags, fields| {
1815        if flags & DELETED != 0 {
1816            return true;
1817        }
1818        f(mark.id, mark.byte, fields)
1819    })
1820}
1821
1822/// Every entry in a node, deleted ones included, with its element index.
1823///
1824/// One forward walk of the whole blob. Nothing here reaches into the middle by
1825/// index, because a listpack index is a walk from the front and doing that per
1826/// entry would turn a node scan into a quadratic one.
1827fn walk_node<'a, F>(lp: &'a Listpack, master: Id, from: Option<usize>, f: &mut F) -> usize
1828where
1829    F: FnMut(Mark, i64, Fields<'a>) -> bool,
1830{
1831    let mut it = lp.iter();
1832    let (Some(_), Some(_), Some(Entry::Int(masters))) = (it.next(), it.next(), it.next()) else {
1833        return it.offset();
1834    };
1835    let masters = masters.max(0) as usize;
1836    // The master field names, kept as a mark to hand to the entries that share
1837    // them, and then stepped over along with the zero that ends the master entry.
1838    let names = it.clone();
1839    let mut index = MASTER_FIELDS;
1840    for _ in 0..=masters {
1841        if it.next().is_none() {
1842            return it.offset();
1843        }
1844        index += 1;
1845    }
1846    // A resume skips straight to where a previous walk stopped. The header above
1847    // still has to be read whichever way this is called, because the field names
1848    // a shared entry hands back live in it, but that is a handful of elements
1849    // rather than every entry in the node.
1850    let counting = from.is_none();
1851    if let Some(byte) = from.filter(|byte| *byte > it.offset()) {
1852        it = lp.iter_at(byte);
1853    }
1854
1855    loop {
1856        let at = it.offset();
1857        let element = index;
1858        let (Some(Entry::Int(flags)), Some(Entry::Int(ms)), Some(Entry::Int(seq))) =
1859            (it.next(), it.next(), it.next())
1860        else {
1861            return at;
1862        };
1863        index += 3;
1864        let id = Id {
1865            ms: master.ms.wrapping_add(ms as u64),
1866            seq: master.seq.wrapping_add(seq as u64),
1867        };
1868
1869        let same = flags & SAME_FIELDS != 0;
1870        let (fields, skip) = if same {
1871            let fields = Fields {
1872                names: Some(names.clone()),
1873                body: it.clone(),
1874                left: masters,
1875            };
1876            (fields, masters + 1)
1877        } else {
1878            let Some(Entry::Int(n)) = it.next() else {
1879                return at;
1880            };
1881            index += 1;
1882            let fields = Fields {
1883                names: None,
1884                body: it.clone(),
1885                left: n.max(0) as usize,
1886            };
1887            (fields, n.max(0) as usize * 2 + 1)
1888        };
1889
1890        let mark = Mark {
1891            id,
1892            index: counting.then_some(element),
1893            byte: at,
1894        };
1895        if !f(mark, flags, fields) {
1896            return at;
1897        }
1898        for _ in 0..skip {
1899            if it.next().is_none() {
1900                return it.offset();
1901            }
1902            index += 1;
1903        }
1904    }
1905}
1906
1907/// Where one entry sits inside its node.
1908#[derive(Debug, Clone, Copy)]
1909struct Mark {
1910    /// The entry's ID, worked out from the node's master and the two
1911    /// differences the entry stores.
1912    id: Id,
1913    /// Which listpack element the entry's flags are, for the writes that reach
1914    /// back in by index. Only filled in on a walk that started at the front,
1915    /// because counting elements from a resume point would give a number
1916    /// [`Listpack::replace`] cannot use.
1917    index: Option<usize>,
1918    /// Where in the blob the entry's flags element starts, which is what
1919    /// [`Listpack::iter_at`] takes to come back here.
1920    byte: usize,
1921}
1922
1923#[cfg(test)]
1924mod tests {
1925    use super::*;
1926
1927    fn pairs<'a>(of: &'a [(&'a str, &'a str)]) -> Vec<(&'a [u8], &'a [u8])> {
1928        of.iter()
1929            .map(|(f, v)| (f.as_bytes(), v.as_bytes()))
1930            .collect()
1931    }
1932
1933    /// One entry, with its fields owned, which is what the tests compare.
1934    type Flat = (Id, Vec<(Vec<u8>, Vec<u8>)>);
1935
1936    /// Everything in the stream, oldest first.
1937    fn dump(s: &Stream) -> Vec<Flat> {
1938        let mut out = Vec::new();
1939        s.range(Id::MIN, Id::MAX, None, |id, fields| {
1940            out.push((id, fields.map(|(f, v)| (f.to_vec(), v.to_vec())).collect()));
1941            true
1942        });
1943        out
1944    }
1945
1946    fn add(s: &mut Stream, ms: u64, seq: u64, fields: &[(&str, &str)]) {
1947        s.append(Id::new(ms, seq), &pairs(fields), Limits::default())
1948            .expect("an append");
1949    }
1950
1951    #[test]
1952    fn an_entry_comes_back_as_it_went_in() {
1953        let mut s = Stream::new();
1954        add(&mut s, 5, 0, &[("sensor", "1"), ("reading", "23.4")]);
1955        let got = dump(&s);
1956        assert_eq!(got.len(), 1);
1957        assert_eq!(got[0].0, Id::new(5, 0));
1958        assert_eq!(
1959            got[0].1,
1960            vec![
1961                (b"sensor".to_vec(), b"1".to_vec()),
1962                (b"reading".to_vec(), b"23.4".to_vec())
1963            ]
1964        );
1965    }
1966
1967    #[test]
1968    fn entries_come_back_in_order() {
1969        let mut s = Stream::new();
1970        for ms in 1..200u64 {
1971            add(&mut s, ms, 0, &[("n", "x")]);
1972        }
1973        let got = dump(&s);
1974        assert_eq!(got.len(), 199);
1975        for (at, (id, _)) in got.iter().enumerate() {
1976            assert_eq!(*id, Id::new(at as u64 + 1, 0));
1977        }
1978        assert_eq!(s.len(), 199);
1979        assert_eq!(s.added(), 199);
1980        assert_eq!(s.last_id(), Id::new(199, 0));
1981    }
1982
1983    /// The whole reason a node holds a hundred entries.
1984    #[test]
1985    fn a_long_stream_is_many_nodes() {
1986        let mut s = Stream::new();
1987        for ms in 1..=1000u64 {
1988            add(&mut s, ms, 0, &[("n", "x")]);
1989        }
1990        assert_eq!(s.nodes(), 10, "a hundred entries a node");
1991        assert_eq!(dump(&s).len(), 1000);
1992    }
1993
1994    /// Sharing the field names is worth most of the entry on a real stream.
1995    #[test]
1996    fn the_field_names_are_not_stored_twice() {
1997        let mut shared = Stream::new();
1998        let mut apart = Stream::new();
1999        for ms in 1..=100u64 {
2000            add(
2001                &mut shared,
2002                ms,
2003                0,
2004                &[("temperature_celsius", "21"), ("relative_humidity", "44")],
2005            );
2006            let a = format!("temperature_celsius{ms}");
2007            let b = format!("relative_humidity{ms}");
2008            apart
2009                .append(
2010                    Id::new(ms, 0),
2011                    &[(a.as_bytes(), b"21"), (b.as_bytes(), b"44")],
2012                    Limits::default(),
2013                )
2014                .expect("an append");
2015        }
2016        assert!(
2017            shared.memory_bytes() * 3 < apart.memory_bytes(),
2018            "{} against {}",
2019            shared.memory_bytes(),
2020            apart.memory_bytes()
2021        );
2022    }
2023
2024    /// What a real stream entry costs, so that a change that quietly doubles it
2025    /// has somewhere to fail.
2026    ///
2027    /// Ten thousand `sensor` and `reading` entries a millisecond apart, which is
2028    /// the shape the benchmark uses and the shape a stream almost always has.
2029    /// That measures 23.9 bytes an entry today, against 48.7 for the same
2030    /// entries with field names that cannot be shared. Thirty two is a bar with
2031    /// room in it rather than a target, because the point is to catch a
2032    /// regression and not to freeze the encoder.
2033    #[test]
2034    fn an_entry_costs_about_two_dozen_bytes() {
2035        let mut s = Stream::new();
2036        for ms in 1..=10_000u64 {
2037            let reading = format!("{:.3}", ms as f64 / 7.0);
2038            s.append(
2039                Id::new(ms, 0),
2040                &[(b"sensor", b"a4"), (b"reading", reading.as_bytes())],
2041                Limits::default(),
2042            )
2043            .expect("an append");
2044        }
2045        let each = s.memory_bytes() as f64 / 10_000.0;
2046        assert!(each < 32.0, "{each:.2} bytes an entry");
2047    }
2048
2049    #[test]
2050    fn an_entry_with_its_own_fields_still_reads_back() {
2051        let mut s = Stream::new();
2052        add(&mut s, 1, 0, &[("a", "1"), ("b", "2")]);
2053        add(&mut s, 2, 0, &[("c", "3")]);
2054        add(&mut s, 3, 0, &[("a", "4"), ("b", "5")]);
2055        let got = dump(&s);
2056        assert_eq!(got[1].1, vec![(b"c".to_vec(), b"3".to_vec())]);
2057        assert_eq!(
2058            got[2].1,
2059            vec![
2060                (b"a".to_vec(), b"4".to_vec()),
2061                (b"b".to_vec(), b"5".to_vec())
2062            ]
2063        );
2064    }
2065
2066    /// The same names in a different order is not the same shape.
2067    #[test]
2068    fn the_order_of_the_names_matters() {
2069        let mut s = Stream::new();
2070        add(&mut s, 1, 0, &[("a", "1"), ("b", "2")]);
2071        add(&mut s, 2, 0, &[("b", "3"), ("a", "4")]);
2072        let got = dump(&s);
2073        assert_eq!(
2074            got[1].1,
2075            vec![
2076                (b"b".to_vec(), b"3".to_vec()),
2077                (b"a".to_vec(), b"4".to_vec())
2078            ]
2079        );
2080    }
2081
2082    #[test]
2083    fn an_id_must_beat_the_last_one() {
2084        let mut s = Stream::new();
2085        add(&mut s, 5, 5, &[("n", "x")]);
2086        let f = pairs(&[("n", "x")]);
2087        for id in [Id::new(5, 5), Id::new(5, 4), Id::new(1, 0)] {
2088            assert_eq!(
2089                s.append(id, &f, Limits::default()),
2090                Err(Refused::NotGreater),
2091                "{id:?}"
2092            );
2093        }
2094        assert_eq!(s.append(Id::new(5, 6), &f, Limits::default()), Ok(()));
2095    }
2096
2097    #[test]
2098    fn nothing_can_be_added_at_zero() {
2099        let mut s = Stream::new();
2100        assert_eq!(
2101            s.append(Id::MIN, &pairs(&[("n", "x")]), Limits::default()),
2102            Err(Refused::Zero)
2103        );
2104    }
2105
2106    #[test]
2107    fn a_range_takes_both_ends() {
2108        let mut s = Stream::new();
2109        for ms in 1..=10u64 {
2110            add(&mut s, ms, 0, &[("n", "x")]);
2111        }
2112        let mut seen = Vec::new();
2113        s.range(Id::new(3, 0), Id::new(6, 0), None, |id, _| {
2114            seen.push(id.ms);
2115            true
2116        });
2117        assert_eq!(seen, vec![3, 4, 5, 6]);
2118    }
2119
2120    /// A range whose ends fall between entries, and one that misses entirely.
2121    #[test]
2122    fn a_range_that_lands_between_entries() {
2123        let mut s = Stream::new();
2124        for ms in [10u64, 20, 30] {
2125            add(&mut s, ms, 0, &[("n", "x")]);
2126        }
2127        let mut seen = Vec::new();
2128        s.range(Id::new(11, 0), Id::new(29, 0), None, |id, _| {
2129            seen.push(id.ms);
2130            true
2131        });
2132        assert_eq!(seen, vec![20]);
2133
2134        let mut none = 0;
2135        s.range(Id::new(31, 0), Id::MAX, None, |_, _| {
2136            none += 1;
2137            true
2138        });
2139        assert_eq!(none, 0);
2140    }
2141
2142    #[test]
2143    fn a_count_stops_the_walk() {
2144        let mut s = Stream::new();
2145        for ms in 1..=500u64 {
2146            add(&mut s, ms, 0, &[("n", "x")]);
2147        }
2148        let mut seen = 0;
2149        let answered = s.range(Id::MIN, Id::MAX, Some(7), |_, _| {
2150            seen += 1;
2151            true
2152        });
2153        assert_eq!((seen, answered), (7, 7));
2154    }
2155
2156    #[test]
2157    fn the_callback_can_stop_the_walk() {
2158        let mut s = Stream::new();
2159        for ms in 1..=500u64 {
2160            add(&mut s, ms, 0, &[("n", "x")]);
2161        }
2162        let mut seen = 0;
2163        s.range(Id::MIN, Id::MAX, None, |_, _| {
2164            seen += 1;
2165            seen < 3
2166        });
2167        assert_eq!(seen, 3);
2168    }
2169
2170    #[test]
2171    fn a_reverse_range_is_the_forward_one_backwards() {
2172        let mut s = Stream::new();
2173        for ms in 1..=350u64 {
2174            add(&mut s, ms, 0, &[("n", "x")]);
2175        }
2176        let mut forward = Vec::new();
2177        s.range(Id::new(50, 0), Id::new(300, 0), None, |id, _| {
2178            forward.push(id);
2179            true
2180        });
2181        let mut back = Vec::new();
2182        s.rev_range(Id::new(50, 0), Id::new(300, 0), None, |id, _| {
2183            back.push(id);
2184            true
2185        });
2186        back.reverse();
2187        assert_eq!(forward, back);
2188        assert_eq!(forward.len(), 251);
2189    }
2190
2191    #[test]
2192    fn a_reverse_range_takes_a_count_from_the_new_end() {
2193        let mut s = Stream::new();
2194        for ms in 1..=350u64 {
2195            add(&mut s, ms, 0, &[("n", "x")]);
2196        }
2197        let mut seen = Vec::new();
2198        s.rev_range(Id::MIN, Id::MAX, Some(3), |id, _| {
2199            seen.push(id.ms);
2200            true
2201        });
2202        assert_eq!(seen, vec![350, 349, 348]);
2203    }
2204
2205    #[test]
2206    fn deleting_leaves_the_rest_readable() {
2207        let mut s = Stream::new();
2208        for ms in 1..=10u64 {
2209            add(&mut s, ms, 0, &[("n", "x")]);
2210        }
2211        assert!(s.delete(Id::new(4, 0)));
2212        assert!(!s.delete(Id::new(4, 0)), "twice is not twice");
2213        assert!(!s.delete(Id::new(99, 0)));
2214        assert_eq!(s.len(), 9);
2215        assert_eq!(s.max_deleted_id(), Id::new(4, 0));
2216        let seen: Vec<u64> = dump(&s).iter().map(|(id, _)| id.ms).collect();
2217        assert_eq!(seen, vec![1, 2, 3, 5, 6, 7, 8, 9, 10]);
2218    }
2219
2220    #[test]
2221    fn deleting_the_first_entry_moves_the_first_id() {
2222        let mut s = Stream::new();
2223        for ms in 1..=5u64 {
2224            add(&mut s, ms, 0, &[("n", "x")]);
2225        }
2226        assert_eq!(s.first_id(), Some(Id::new(1, 0)));
2227        s.delete(Id::new(1, 0));
2228        assert_eq!(s.first_id(), Some(Id::new(2, 0)));
2229    }
2230
2231    /// The last live entry going takes the node with it.
2232    #[test]
2233    fn emptying_a_node_drops_it() {
2234        let mut s = Stream::new();
2235        for ms in 1..=250u64 {
2236            add(&mut s, ms, 0, &[("n", "x")]);
2237        }
2238        assert_eq!(s.nodes(), 3);
2239        for ms in 1..=100u64 {
2240            assert!(s.delete(Id::new(ms, 0)), "{ms}");
2241        }
2242        assert_eq!(s.nodes(), 2);
2243        assert_eq!(s.len(), 150);
2244        assert_eq!(dump(&s).len(), 150);
2245    }
2246
2247    /// The stream can be empty and still know what came before.
2248    #[test]
2249    fn an_emptied_stream_still_remembers_its_last_id() {
2250        let mut s = Stream::new();
2251        add(&mut s, 7, 0, &[("n", "x")]);
2252        s.delete(Id::new(7, 0));
2253        assert!(s.is_empty());
2254        assert_eq!(s.last_id(), Id::new(7, 0));
2255        assert_eq!(s.added(), 1);
2256        assert_eq!(s.first_id(), None);
2257        assert_eq!(
2258            s.append(Id::new(7, 0), &pairs(&[("n", "x")]), Limits::default()),
2259            Err(Refused::NotGreater),
2260            "a deleted id is still used up"
2261        );
2262    }
2263
2264    #[test]
2265    fn trimming_to_a_length_takes_the_oldest() {
2266        let mut s = Stream::new();
2267        for ms in 1..=1000u64 {
2268            add(&mut s, ms, 0, &[("n", "x")]);
2269        }
2270        assert_eq!(s.trim_maxlen(150, true, None), 850);
2271        assert_eq!(s.len(), 150);
2272        assert_eq!(s.first_id(), Some(Id::new(851, 0)));
2273        assert_eq!(s.last_id(), Id::new(1000, 0));
2274    }
2275
2276    /// The point of `~`: whole nodes only, so nothing is rewritten.
2277    #[test]
2278    fn an_approximate_trim_stops_at_a_node() {
2279        let mut s = Stream::new();
2280        for ms in 1..=1000u64 {
2281            add(&mut s, ms, 0, &[("n", "x")]);
2282        }
2283        assert_eq!(s.trim_maxlen(150, false, None), 800);
2284        assert_eq!(s.len(), 200, "left at the node boundary above 150");
2285        assert_eq!(s.nodes(), 2);
2286    }
2287
2288    #[test]
2289    fn trimming_to_a_length_that_is_already_met_does_nothing() {
2290        let mut s = Stream::new();
2291        for ms in 1..=10u64 {
2292            add(&mut s, ms, 0, &[("n", "x")]);
2293        }
2294        assert_eq!(s.trim_maxlen(50, true, None), 0);
2295        assert_eq!(s.len(), 10);
2296    }
2297
2298    #[test]
2299    fn trimming_to_zero_empties_it() {
2300        let mut s = Stream::new();
2301        for ms in 1..=250u64 {
2302            add(&mut s, ms, 0, &[("n", "x")]);
2303        }
2304        assert_eq!(s.trim_maxlen(0, true, None), 250);
2305        assert!(s.is_empty());
2306        assert_eq!(s.nodes(), 0);
2307        assert_eq!(s.last_id(), Id::new(250, 0));
2308    }
2309
2310    #[test]
2311    fn trimming_below_an_id_takes_everything_under_it() {
2312        let mut s = Stream::new();
2313        for ms in 1..=1000u64 {
2314            add(&mut s, ms, 0, &[("n", "x")]);
2315        }
2316        assert_eq!(s.trim_minid(Id::new(400, 0), true, None), 399);
2317        assert_eq!(s.first_id(), Some(Id::new(400, 0)));
2318        assert_eq!(s.len(), 601);
2319    }
2320
2321    #[test]
2322    fn an_approximate_minid_trim_stops_at_a_node() {
2323        let mut s = Stream::new();
2324        for ms in 1..=1000u64 {
2325            add(&mut s, ms, 0, &[("n", "x")]);
2326        }
2327        assert_eq!(s.trim_minid(Id::new(450, 0), false, None), 400);
2328        assert_eq!(s.first_id(), Some(Id::new(401, 0)));
2329    }
2330
2331    #[test]
2332    fn several_entries_share_a_millisecond() {
2333        let mut s = Stream::new();
2334        for seq in 0..250u64 {
2335            add(&mut s, 5, seq, &[("n", "x")]);
2336        }
2337        let got = dump(&s);
2338        assert_eq!(got.len(), 250);
2339        for (at, (id, _)) in got.iter().enumerate() {
2340            assert_eq!(*id, Id::new(5, at as u64), "at {at}");
2341        }
2342        let mut seen = Vec::new();
2343        s.range(Id::new(5, 100), Id::new(5, 102), None, |id, _| {
2344            seen.push(id.seq);
2345            true
2346        });
2347        assert_eq!(seen, vec![100, 101, 102]);
2348    }
2349
2350    /// Sequence numbers that run on across a node boundary, which is where an
2351    /// ID stored as a difference is easiest to get wrong.
2352    #[test]
2353    fn a_sequence_that_crosses_a_node() {
2354        let mut s = Stream::new();
2355        for seq in 0..300u64 {
2356            add(&mut s, 1, seq, &[("n", "x")]);
2357        }
2358        assert!(s.nodes() > 1);
2359        let got = dump(&s);
2360        assert_eq!(got.len(), 300);
2361        assert_eq!(got[299].0, Id::new(1, 299));
2362        assert!(s.delete(Id::new(1, 250)));
2363        assert_eq!(dump(&s).len(), 299);
2364    }
2365
2366    #[test]
2367    fn an_entry_with_no_fields_is_still_an_entry() {
2368        let mut s = Stream::new();
2369        s.append(Id::new(1, 0), &[], Limits::default())
2370            .expect("an append");
2371        add(&mut s, 2, 0, &[("n", "x")]);
2372        let got = dump(&s);
2373        assert_eq!(got.len(), 2);
2374        assert!(got[0].1.is_empty());
2375    }
2376
2377    #[test]
2378    fn a_value_that_looks_like_a_number_comes_back_as_it_went_in() {
2379        let mut s = Stream::new();
2380        add(&mut s, 1, 0, &[("n", "007"), ("m", "7")]);
2381        let got = dump(&s);
2382        assert_eq!(got[0].1[0].1, b"007".to_vec());
2383        assert_eq!(got[0].1[1].1, b"7".to_vec());
2384    }
2385
2386    #[test]
2387    fn the_auto_id_follows_the_clock_and_never_goes_back() {
2388        let mut s = Stream::new();
2389        assert_eq!(s.auto_id(1000), Some(Id::new(1000, 0)));
2390        add(&mut s, 1000, 0, &[("n", "x")]);
2391        assert_eq!(s.auto_id(1000), Some(Id::new(1000, 1)), "same millisecond");
2392        assert_eq!(s.auto_id(900), Some(Id::new(1000, 1)), "clock went back");
2393        assert_eq!(s.auto_id(1001), Some(Id::new(1001, 0)));
2394    }
2395
2396    #[test]
2397    fn an_explicit_millisecond_takes_the_next_sequence() {
2398        let mut s = Stream::new();
2399        add(&mut s, 5, 0, &[("n", "x")]);
2400        assert_eq!(s.auto_seq(5), Some(Id::new(5, 1)));
2401        assert_eq!(s.auto_seq(6), Some(Id::new(6, 0)));
2402        assert_eq!(s.auto_seq(4), None, "below the last one");
2403    }
2404
2405    #[test]
2406    fn an_id_reads_and_writes() {
2407        for (text, default, want) in [
2408            (&b"5"[..], 0, Some(Id::new(5, 0))),
2409            (b"5", u64::MAX, Some(Id::new(5, u64::MAX))),
2410            (b"5-3", 0, Some(Id::new(5, 3))),
2411            (b"0-0", 0, Some(Id::MIN)),
2412            (b"", 0, None),
2413            (b"-1", 0, None),
2414            (b"5-", 0, None),
2415            (b"a", 0, None),
2416            (b"5-a", 0, None),
2417            (b"18446744073709551616", 0, None),
2418        ] {
2419            assert_eq!(
2420                Id::parse(text, default),
2421                want,
2422                "{:?}",
2423                String::from_utf8_lossy(text)
2424            );
2425        }
2426        assert_eq!(Id::new(5, 3).to_vec(), b"5-3".to_vec());
2427    }
2428
2429    #[test]
2430    fn an_id_round_trips_through_its_bytes() {
2431        for id in [Id::MIN, Id::MAX, Id::new(1, 2), Id::new(u64::MAX, 0)] {
2432            assert_eq!(Id::from_bytes(id.to_bytes()), id);
2433        }
2434        // Big endian is the order that sorts, which is why the format uses it.
2435        assert!(Id::new(1, 2).to_bytes() < Id::new(1, 3).to_bytes());
2436        assert!(Id::new(1, u64::MAX).to_bytes() < Id::new(2, 0).to_bytes());
2437    }
2438
2439    #[test]
2440    fn stepping_an_id_carries_and_stops() {
2441        assert_eq!(Id::new(1, 2).next(), Some(Id::new(1, 3)));
2442        assert_eq!(Id::new(1, u64::MAX).next(), Some(Id::new(2, 0)));
2443        assert_eq!(Id::MAX.next(), None);
2444        assert_eq!(Id::new(1, 3).prev(), Some(Id::new(1, 2)));
2445        assert_eq!(Id::new(2, 0).prev(), Some(Id::new(1, u64::MAX)));
2446        assert_eq!(Id::MIN.prev(), None);
2447    }
2448
2449    /// Nothing about the answer may depend on where the node boundaries fell.
2450    #[test]
2451    fn the_node_size_changes_nothing_but_the_node_count() {
2452        let mut want = None;
2453        for entries in [1usize, 2, 7, 100, 4096] {
2454            let mut s = Stream::new();
2455            let limits = Limits {
2456                max_node_bytes: NODE_BYTES,
2457                max_node_entries: entries,
2458            };
2459            for ms in 1..=400u64 {
2460                let value = format!("v{ms}");
2461                s.append(Id::new(ms, 0), &[(b"n", value.as_bytes())], limits)
2462                    .expect("an append");
2463            }
2464            for ms in (1..=400u64).step_by(7) {
2465                s.delete(Id::new(ms, 0));
2466            }
2467            let got = dump(&s);
2468            match &want {
2469                None => want = Some(got),
2470                Some(want) => assert_eq!(&got, want, "at {entries} entries a node"),
2471            }
2472        }
2473    }
2474
2475    /// A byte limit small enough that every entry is its own node.
2476    #[test]
2477    fn a_tiny_byte_limit_still_works() {
2478        let limits = Limits {
2479            max_node_bytes: 1,
2480            max_node_entries: NODE_ENTRIES,
2481        };
2482        let mut s = Stream::new();
2483        for ms in 1..=20u64 {
2484            s.append(Id::new(ms, 0), &[(b"n", b"x")], limits)
2485                .expect("an append");
2486        }
2487        assert_eq!(s.nodes(), 20);
2488        assert_eq!(dump(&s).len(), 20);
2489    }
2490
2491    /// A `DUMP` of a stream taken from a real Redis, as hexadecimal.
2492    ///
2493    /// Captured from Redis 8.10.1 in the official Docker image on 2026-09-02,
2494    /// from a server that had been given exactly this:
2495    ///
2496    /// ```text
2497    /// XADD s 1-1 temperature_celsius 21 relative_humidity 55
2498    /// XADD s 1-2 temperature_celsius 22 relative_humidity 56
2499    /// XADD s 2-1 temperature_celsius 23 relative_humidity 57
2500    /// XADD s 3-1 sensor a
2501    /// XDEL s 1-2
2502    /// ```
2503    ///
2504    /// Three entries share the master fields and one brings its own, one entry
2505    /// is deleted rather than taken out, and the ids climb in both halves, so
2506    /// between them the four cover every branch the encoder has.
2507    const REDIS_DUMP: &str = "1b0110000000000000000100000000000000014070\
2508        700000001f000301010102019374656d70657261747572655f63656c736975731491\
2509        72656c61746976655f68756d69646974791200010201000100011501370105010301\
2510        0001010116013801050102010101000117013901050100010201000101018673656e\
2511        736f72078161020601ff030301010101020400406440640000000f00239a5c2c7208\
2512        ea0a";
2513
2514    fn unhex(s: &str) -> Vec<u8> {
2515        let digits: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
2516        digits
2517            .chunks(2)
2518            .map(|pair| {
2519                let of = |b: u8| (b as char).to_digit(16).expect("a hex digit") as u8;
2520                of(pair[0]) << 4 | of(pair[1])
2521            })
2522            .collect()
2523    }
2524
2525    /// One RDB length, and how many bytes it took.
2526    ///
2527    /// Only the two plain forms, six bits in one byte and fourteen in two,
2528    /// because those are the only ones this fixture uses and a test that
2529    /// quietly accepted more would be claiming to check something it does not.
2530    fn rdb_len(bytes: &[u8], at: usize) -> (usize, usize) {
2531        match bytes[at] >> 6 {
2532            0 => (usize::from(bytes[at] & 0x3F), 1),
2533            1 => (
2534                usize::from(bytes[at] & 0x3F) << 8 | usize::from(bytes[at + 1]),
2535                2,
2536            ),
2537            other => panic!("the fixture used length form {other}"),
2538        }
2539    }
2540
2541    /// The one node out of the captured dump, and the counters after it.
2542    fn redis_node() -> (Id, Vec<u8>, Vec<u8>) {
2543        let dump = unhex(REDIS_DUMP);
2544        assert_eq!(dump[0], 0x1B, "RDB_TYPE_STREAM_LISTPACKS_3");
2545        let (nodes, n) = rdb_len(&dump, 1);
2546        assert_eq!(nodes, 1, "the fixture is one node");
2547        let mut at = 1 + n;
2548        let (key, n) = rdb_len(&dump, at);
2549        assert_eq!(key, 16, "a node key is an id in sixteen bytes");
2550        at += n;
2551        let master = Id::from_bytes(dump[at..at + 16].try_into().expect("sixteen bytes"));
2552        at += 16;
2553        let (len, n) = rdb_len(&dump, at);
2554        at += n;
2555        let lp = dump[at..at + len].to_vec();
2556        // The last ten bytes are the RDB version and the checksum, which belong
2557        // to DUMP rather than to the stream.
2558        let rest = dump[at + len..dump.len() - 10].to_vec();
2559        (master, lp, rest)
2560    }
2561
2562    #[test]
2563    fn a_node_is_written_the_way_redis_writes_one() {
2564        let mut s = Stream::new();
2565        add(
2566            &mut s,
2567            1,
2568            1,
2569            &[("temperature_celsius", "21"), ("relative_humidity", "55")],
2570        );
2571        add(
2572            &mut s,
2573            1,
2574            2,
2575            &[("temperature_celsius", "22"), ("relative_humidity", "56")],
2576        );
2577        add(
2578            &mut s,
2579            2,
2580            1,
2581            &[("temperature_celsius", "23"), ("relative_humidity", "57")],
2582        );
2583        add(&mut s, 3, 1, &[("sensor", "a")]);
2584        assert!(s.delete(Id::new(1, 2)));
2585
2586        let (master, lp, _) = redis_node();
2587        assert_eq!(s.nodes(), 1, "all four fit in one node");
2588        assert_eq!(s.nodes[0].master, master);
2589        assert_eq!(s.nodes[0].lp.as_bytes(), &lp[..]);
2590    }
2591
2592    #[test]
2593    fn a_node_redis_wrote_reads_back() {
2594        let (master, lp, rest) = redis_node();
2595        let lp = Listpack::from_bytes(&lp).expect("a listpack Redis wrote");
2596        let s = Stream {
2597            nodes: VecDeque::from(vec![Node { master, lp }]),
2598            length: u64::from(rest[0]),
2599            last: Id::new(u64::from(rest[1]), u64::from(rest[2])),
2600            max_deleted: Id::new(u64::from(rest[5]), u64::from(rest[6])),
2601            added: u64::from(rest[7]),
2602            groups: Vec::new(),
2603            epoch: 0,
2604        };
2605
2606        assert_eq!(s.len(), 3);
2607        assert_eq!(s.last_id(), Id::new(3, 1));
2608        assert_eq!(s.max_deleted_id(), Id::new(1, 2));
2609        assert_eq!(s.added(), 4);
2610        assert_eq!(s.first_id(), Some(Id::new(1, 1)));
2611        assert_eq!(
2612            dump(&s),
2613            vec![
2614                (
2615                    Id::new(1, 1),
2616                    vec![
2617                        (b"temperature_celsius".to_vec(), b"21".to_vec()),
2618                        (b"relative_humidity".to_vec(), b"55".to_vec())
2619                    ]
2620                ),
2621                (
2622                    Id::new(2, 1),
2623                    vec![
2624                        (b"temperature_celsius".to_vec(), b"23".to_vec()),
2625                        (b"relative_humidity".to_vec(), b"57".to_vec())
2626                    ]
2627                ),
2628                (Id::new(3, 1), vec![(b"sensor".to_vec(), b"a".to_vec())]),
2629            ]
2630        );
2631    }
2632
2633    /// A stream of `n` entries at 1-0 up to n-0, one field each.
2634    fn logged(n: u64) -> Stream {
2635        let mut s = Stream::new();
2636        for ms in 1..=n {
2637            add(&mut s, ms, 0, &[("job", "x")]);
2638        }
2639        s
2640    }
2641
2642    /// What a group read hands back, with the fields flattened.
2643    fn read(s: &mut Stream, group: &str, who: &str, count: Option<usize>, now: u64) -> Vec<Id> {
2644        let mut out = Vec::new();
2645        s.read_group(
2646            group.as_bytes(),
2647            who.as_bytes(),
2648            count,
2649            false,
2650            now,
2651            |id, _| {
2652                out.push(id);
2653                true
2654            },
2655        )
2656        .expect("the group");
2657        out
2658    }
2659
2660    /// The read the resume cursor exists for.
2661    ///
2662    /// Five hundred entries is five nodes, so this also covers the mark being
2663    /// carried over a node boundary and the walk picking up in the next one.
2664    #[test]
2665    fn a_group_draining_one_at_a_time_gets_every_entry_once() {
2666        let mut s = logged(500);
2667        s.create_group(b"workers", Id::MIN, Some(0));
2668        let mut got = Vec::new();
2669        for _ in 0..500 {
2670            got.extend(read(&mut s, "workers", "alice", Some(1), 100));
2671        }
2672        assert_eq!(got, (1..=500).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>());
2673        assert!(read(&mut s, "workers", "alice", Some(1), 100).is_empty());
2674    }
2675
2676    /// The state a consumer keeping up with a producer sits in, where the mark
2677    /// points past the last entry there was and an append lands right on it.
2678    #[test]
2679    fn a_group_that_has_caught_up_is_handed_the_next_append() {
2680        let mut s = logged(3);
2681        s.create_group(b"workers", Id::MIN, Some(0));
2682        assert_eq!(read(&mut s, "workers", "alice", None, 100).len(), 3);
2683        for ms in 4..=200u64 {
2684            add(&mut s, ms, 0, &[("job", "x")]);
2685            assert_eq!(
2686                read(&mut s, "workers", "alice", Some(1), 100),
2687                vec![Id::new(ms, 0)],
2688                "the entry appended a moment ago"
2689            );
2690        }
2691    }
2692
2693    /// A delete moves every byte behind it, so the mark has to be thrown away
2694    /// rather than followed into the middle of an entry.
2695    #[test]
2696    fn a_delete_between_two_group_reads_does_not_lose_the_rest() {
2697        let mut s = logged(300);
2698        s.create_group(b"workers", Id::MIN, Some(0));
2699        let mut got = read(&mut s, "workers", "alice", Some(10), 100);
2700        assert!(s.delete(Id::new(150, 0)));
2701        while got.len() < 299 {
2702            let more = read(&mut s, "workers", "alice", Some(1), 100);
2703            assert_eq!(more.len(), 1, "at {}", got.len());
2704            got.extend(more);
2705        }
2706        let want: Vec<Id> = (1..=300)
2707            .filter(|ms| *ms != 150)
2708            .map(|ms| Id::new(ms, 0))
2709            .collect();
2710        assert_eq!(got, want);
2711    }
2712
2713    /// `XGROUP SETID` back to the start puts the bookmark somewhere the mark
2714    /// says nothing about, and the read after it has to walk from the front.
2715    #[test]
2716    fn moving_the_bookmark_back_reads_it_all_again() {
2717        let mut s = logged(250);
2718        s.create_group(b"workers", Id::MIN, Some(0));
2719        for _ in 0..120 {
2720            read(&mut s, "workers", "alice", Some(1), 100);
2721        }
2722        s.group_mut(b"workers")
2723            .expect("the group")
2724            .set_id(Id::MIN, Some(0));
2725        let mut got = Vec::new();
2726        for _ in 0..250 {
2727            got.extend(read(&mut s, "workers", "alice", Some(1), 100));
2728        }
2729        assert_eq!(got, (1..=250).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>());
2730    }
2731
2732    /// A trim drops whole nodes from the front, one of which may be the one a
2733    /// group's mark is about. Node master IDs are never reused, so the mark
2734    /// cannot be mistaken for one about the node that took its place.
2735    #[test]
2736    fn a_trim_under_a_group_does_not_hand_out_the_wrong_entries() {
2737        let mut s = logged(500);
2738        s.create_group(b"workers", Id::MIN, Some(0));
2739        for _ in 0..50 {
2740            read(&mut s, "workers", "alice", Some(1), 100);
2741        }
2742        assert_eq!(s.trim_maxlen(200, false, None), 300);
2743        let mut got = Vec::new();
2744        for _ in 0..250 {
2745            got.extend(read(&mut s, "workers", "alice", Some(1), 100));
2746        }
2747        assert_eq!(
2748            got,
2749            (301..=500).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>(),
2750            "everything the trim left that the group had not read"
2751        );
2752    }
2753
2754    #[test]
2755    fn a_group_is_made_once() {
2756        let mut s = logged(3);
2757        assert!(s.create_group(b"workers", Id::MIN, Some(0)));
2758        assert!(!s.create_group(b"workers", Id::MIN, Some(0)));
2759        assert!(s.group(b"workers").is_some());
2760        assert!(s.destroy_group(b"workers"));
2761        assert!(!s.destroy_group(b"workers"));
2762        assert!(s.group(b"workers").is_none());
2763    }
2764
2765    #[test]
2766    fn a_group_read_hands_out_what_comes_after_the_bookmark() {
2767        let mut s = logged(5);
2768        s.create_group(b"workers", Id::MIN, Some(0));
2769
2770        assert_eq!(
2771            read(&mut s, "workers", "alice", Some(2), 100),
2772            vec![Id::new(1, 0), Id::new(2, 0)]
2773        );
2774        // The bookmark moved, so bob gets what alice did not.
2775        assert_eq!(
2776            read(&mut s, "workers", "bob", Some(2), 100),
2777            vec![Id::new(3, 0), Id::new(4, 0)]
2778        );
2779        assert_eq!(
2780            read(&mut s, "workers", "alice", None, 100),
2781            vec![Id::new(5, 0)]
2782        );
2783        assert_eq!(read(&mut s, "workers", "alice", None, 100), vec![]);
2784    }
2785
2786    #[test]
2787    fn a_group_read_fills_the_pending_list() {
2788        let mut s = logged(3);
2789        s.create_group(b"workers", Id::MIN, Some(0));
2790        read(&mut s, "workers", "alice", None, 500);
2791
2792        let g = s.group(b"workers").expect("the group");
2793        assert_eq!(g.pending_len(), 3);
2794        assert_eq!(g.last_id(), Id::new(3, 0));
2795        assert_eq!(g.entries_read(), Some(3));
2796        assert_eq!(s.lag(g), Some(0));
2797        let c = g.consumer_named(b"alice").expect("alice");
2798        assert_eq!(c.len(), 3);
2799        assert_eq!(c.active(), Some(500));
2800    }
2801
2802    #[test]
2803    fn a_read_that_finds_nothing_is_seen_but_not_active() {
2804        let mut s = logged(1);
2805        s.create_group(b"workers", Id::MIN, Some(0));
2806        read(&mut s, "workers", "alice", None, 100);
2807        read(&mut s, "workers", "alice", None, 900);
2808
2809        let c = s
2810            .group(b"workers")
2811            .expect("the group")
2812            .consumer_named(b"alice")
2813            .expect("alice");
2814        assert_eq!((c.seen(), c.active()), (900, Some(100)));
2815    }
2816
2817    #[test]
2818    fn a_group_starting_at_the_end_reads_only_what_comes_next() {
2819        let mut s = logged(3);
2820        s.create_group(b"workers", s.last_id(), Some(s.added()));
2821        assert_eq!(read(&mut s, "workers", "alice", None, 1), vec![]);
2822        add(&mut s, 4, 0, &[("job", "x")]);
2823        assert_eq!(
2824            read(&mut s, "workers", "alice", None, 1),
2825            vec![Id::new(4, 0)]
2826        );
2827    }
2828
2829    #[test]
2830    fn reading_a_group_that_is_not_there_says_so() {
2831        let mut s = logged(1);
2832        assert!(
2833            s.read_group(b"nope", b"alice", None, false, 1, |_, _| true)
2834                .is_none()
2835        );
2836    }
2837
2838    #[test]
2839    fn a_consumer_can_re_read_what_it_is_holding() {
2840        let mut s = logged(4);
2841        s.create_group(b"workers", Id::MIN, Some(0));
2842        read(&mut s, "workers", "alice", Some(2), 1);
2843        read(&mut s, "workers", "bob", Some(2), 1);
2844
2845        let mut out = Vec::new();
2846        s.read_group_pending(b"workers", b"alice", Id::MIN, None, 2, |id, fields| {
2847            out.push((id, fields.map(|f| f.len())));
2848            true
2849        })
2850        .expect("the group");
2851        assert_eq!(
2852            out,
2853            vec![(Id::new(1, 0), Some(1)), (Id::new(2, 0), Some(1))]
2854        );
2855
2856        // From an ID, which is how a consumer pages through its own backlog.
2857        let mut after = Vec::new();
2858        s.read_group_pending(b"workers", b"alice", Id::new(1, 0), None, 2, |id, _| {
2859            after.push(id);
2860            true
2861        });
2862        assert_eq!(after, vec![Id::new(2, 0)]);
2863    }
2864
2865    /// A history read counts as a delivery, which is Redis's behaviour and not
2866    /// the one I would have guessed.
2867    ///
2868    /// Checked against Redis 8.10.1: an entry left idle for 2006 milliseconds
2869    /// and then read back through `XREADGROUP ... 0` came out idle for 2 with
2870    /// its delivery count up by one. The count is how many times a consumer has
2871    /// been told to do the work, and a consumer re-reading its backlog after a
2872    /// restart has been told again.
2873    #[test]
2874    fn re_reading_counts_as_being_handed_it_again() {
2875        let mut s = logged(2);
2876        s.create_group(b"workers", Id::MIN, Some(0));
2877        read(&mut s, "workers", "alice", None, 100);
2878        s.read_group_pending(
2879            b"workers",
2880            b"alice",
2881            Id::MIN,
2882            None,
2883            700,
2884            |_: Id, _: Option<Fields<'_>>| true,
2885        );
2886
2887        let g = s.group(b"workers").expect("the group");
2888        let nack = g.nack(Id::new(1, 0)).expect("a nack");
2889        assert_eq!((nack.count(), nack.time()), (2, 700));
2890        // The bookmark does not move, because nothing new was handed out.
2891        assert_eq!(g.last_id(), Id::new(2, 0));
2892        assert_eq!(g.pending_len(), 2);
2893    }
2894
2895    /// The lag and the read counter, which are two answers and not one.
2896    ///
2897    /// Every line here was run against Redis 8.10.1 first and the numbers are
2898    /// its numbers. The one worth pointing at is the last pair: the counter goes
2899    /// away and the group's bookmark keeps moving, because a delete ahead of a
2900    /// group makes the counter unknowable rather than merely stale.
2901    #[test]
2902    fn a_hole_in_front_of_a_group_takes_its_lag() {
2903        let mut s = logged(5);
2904        s.create_group(b"workers", Id::MIN, Some(0));
2905        read(&mut s, "workers", "alice", Some(3), 1);
2906        assert_eq!(counters(&s), (Some(3), Some(2)));
2907
2908        // Deleting something the group has already read leaves both alone,
2909        // since the hole is behind the bookmark and the entries in front of it
2910        // are all still there.
2911        assert!(s.delete(Id::new(1, 0)));
2912        assert_eq!(counters(&s), (Some(3), Some(2)));
2913
2914        // Deleting something it has not reached takes the lag, and leaves the
2915        // counter exactly where it was. Redis does not clear it here.
2916        assert!(s.delete(Id::new(5, 0)));
2917        assert_eq!(counters(&s), (Some(3), None));
2918
2919        // Reading what is left moves the bookmark to 4-0, which is not the last
2920        // ID the stream ever handed out, so there is still no way to say how far
2921        // along that is and the counter goes too.
2922        read(&mut s, "workers", "alice", None, 1);
2923        assert_eq!(s.group(b"workers").expect("g").last_id(), Id::new(4, 0));
2924        assert_eq!(counters(&s), (None, None));
2925    }
2926
2927    /// A trim is not a hole, so it costs a group nothing, and once it has cut
2928    /// past the bookmark the lag becomes what is left rather than nothing.
2929    ///
2930    /// Checked against Redis 8.10.1 at twenty entries, where the three lines
2931    /// below read 5, 5 and 2.
2932    #[test]
2933    fn trimming_past_a_group_leaves_it_the_length() {
2934        let mut s = logged(500);
2935        s.create_group(b"workers", Id::MIN, Some(0));
2936        read(&mut s, "workers", "alice", Some(400), 1);
2937        assert_eq!(counters(&s), (Some(400), Some(100)));
2938
2939        // Whole nodes off the front, all of them well behind the bookmark.
2940        assert_eq!(s.trim_maxlen(200, true, None), 300);
2941        assert_eq!(counters(&s), (Some(400), Some(100)));
2942
2943        // And now past it. The bookmark is below every entry left, so the lag is
2944        // the length: those are exactly the entries the group has still to read.
2945        assert_eq!(s.trim_maxlen(10, true, None), 190);
2946        assert_eq!(counters(&s), (Some(400), Some(10)));
2947    }
2948
2949    /// The counter and the lag of the one group, which every lag test reads.
2950    fn counters(s: &Stream) -> (Option<u64>, Option<u64>) {
2951        let g = s.group(b"workers").expect("the group");
2952        (g.entries_read(), s.lag(g))
2953    }
2954
2955    #[test]
2956    fn an_entry_that_went_away_still_comes_back_as_a_hole() {
2957        let mut s = logged(3);
2958        s.create_group(b"workers", Id::MIN, Some(0));
2959        read(&mut s, "workers", "alice", None, 1);
2960        assert!(s.delete(Id::new(2, 0)));
2961
2962        let mut out = Vec::new();
2963        s.read_group_pending(b"workers", b"alice", Id::MIN, None, 2, |id, fields| {
2964            out.push((id, fields.is_some()));
2965            true
2966        });
2967        assert_eq!(
2968            out,
2969            vec![
2970                (Id::new(1, 0), true),
2971                (Id::new(2, 0), false),
2972                (Id::new(3, 0), true)
2973            ]
2974        );
2975    }
2976
2977    #[test]
2978    fn acking_clears_the_pending_list() {
2979        let mut s = logged(3);
2980        s.create_group(b"workers", Id::MIN, Some(0));
2981        read(&mut s, "workers", "alice", None, 1);
2982        let g = s.group_mut(b"workers").expect("the group");
2983        assert!(g.ack(Id::new(2, 0)));
2984        assert_eq!(g.pending_len(), 2);
2985    }
2986
2987    #[test]
2988    fn a_claim_moves_work_off_a_consumer_that_stopped() {
2989        let mut s = logged(2);
2990        s.create_group(b"workers", Id::MIN, Some(0));
2991        read(&mut s, "workers", "alice", None, 100);
2992
2993        let mut gone = Vec::new();
2994        let took = s
2995            .claim(
2996                b"workers",
2997                b"bob",
2998                &[Id::new(1, 0), Id::new(2, 0)],
2999                500,
3000                5_000,
3001                None,
3002                true,
3003                false,
3004                5_000,
3005                &mut gone,
3006            )
3007            .expect("the group");
3008        assert_eq!(took, vec![Id::new(1, 0), Id::new(2, 0)]);
3009        assert!(gone.is_empty());
3010
3011        let g = s.group(b"workers").expect("the group");
3012        assert!(g.consumer_named(b"alice").expect("alice").is_empty());
3013        assert_eq!(g.consumer_named(b"bob").expect("bob").len(), 2);
3014        assert_eq!(g.nack(Id::new(1, 0)).expect("a nack").count(), 2);
3015    }
3016
3017    #[test]
3018    fn a_claim_leaves_work_that_is_not_idle_enough_alone() {
3019        let mut s = logged(1);
3020        s.create_group(b"workers", Id::MIN, Some(0));
3021        read(&mut s, "workers", "alice", None, 100);
3022
3023        let mut gone = Vec::new();
3024        let took = s
3025            .claim(
3026                b"workers",
3027                b"bob",
3028                &[Id::new(1, 0)],
3029                5_000,
3030                200,
3031                None,
3032                true,
3033                false,
3034                200,
3035                &mut gone,
3036            )
3037            .expect("the group");
3038        assert!(took.is_empty());
3039        assert_eq!(
3040            s.group(b"workers")
3041                .expect("the group")
3042                .consumer_named(b"alice")
3043                .expect("alice")
3044                .len(),
3045            1
3046        );
3047    }
3048
3049    #[test]
3050    fn claiming_an_entry_that_went_away_drops_it_instead() {
3051        let mut s = logged(2);
3052        s.create_group(b"workers", Id::MIN, Some(0));
3053        read(&mut s, "workers", "alice", None, 100);
3054        assert!(s.delete(Id::new(1, 0)));
3055
3056        let mut gone = Vec::new();
3057        let took = s
3058            .claim(
3059                b"workers",
3060                b"bob",
3061                &[Id::new(1, 0), Id::new(2, 0)],
3062                0,
3063                5_000,
3064                None,
3065                true,
3066                false,
3067                5_000,
3068                &mut gone,
3069            )
3070            .expect("the group");
3071        assert_eq!(took, vec![Id::new(2, 0)]);
3072        assert_eq!(gone, vec![Id::new(1, 0)]);
3073        assert_eq!(s.group(b"workers").expect("the group").pending_len(), 1);
3074    }
3075
3076    #[test]
3077    fn force_only_works_on_an_entry_that_is_really_there() {
3078        let mut s = logged(2);
3079        s.create_group(b"workers", s.last_id(), Some(2));
3080
3081        let mut gone = Vec::new();
3082        let took = s
3083            .claim(
3084                b"workers",
3085                b"bob",
3086                &[Id::new(1, 0), Id::new(99, 0)],
3087                0,
3088                100,
3089                None,
3090                true,
3091                true,
3092                100,
3093                &mut gone,
3094            )
3095            .expect("the group");
3096        assert_eq!(took, vec![Id::new(1, 0)], "99-0 is not in the stream");
3097        assert_eq!(s.group(b"workers").expect("the group").pending_len(), 1);
3098    }
3099
3100    #[test]
3101    fn autoclaim_sweeps_the_stale_ones_and_says_where_it_stopped() {
3102        let mut s = logged(6);
3103        s.create_group(b"workers", Id::MIN, Some(0));
3104        read(&mut s, "workers", "alice", Some(3), 100);
3105        read(&mut s, "workers", "alice", None, 900);
3106
3107        let mut gone = Vec::new();
3108        let (cursor, took) = s
3109            .autoclaim(
3110                b"workers",
3111                b"bob",
3112                Id::MIN,
3113                500,
3114                100,
3115                true,
3116                1_000,
3117                &mut gone,
3118            )
3119            .expect("the group");
3120        assert_eq!(cursor, None, "the sweep reached the end");
3121        assert_eq!(took, vec![Id::new(1, 0), Id::new(2, 0), Id::new(3, 0)]);
3122        assert_eq!(
3123            s.group(b"workers")
3124                .expect("the group")
3125                .consumer_named(b"bob")
3126                .expect("bob")
3127                .len(),
3128            3
3129        );
3130    }
3131
3132    #[test]
3133    fn autoclaim_hands_back_a_cursor_when_it_hits_the_count() {
3134        let mut s = logged(10);
3135        s.create_group(b"workers", Id::MIN, Some(0));
3136        read(&mut s, "workers", "alice", None, 100);
3137
3138        let mut gone = Vec::new();
3139        let (cursor, took) = s
3140            .autoclaim(b"workers", b"bob", Id::MIN, 0, 4, true, 1_000, &mut gone)
3141            .expect("the group");
3142        assert_eq!(took.len(), 4);
3143        assert_eq!(cursor, Some(Id::new(5, 0)));
3144
3145        // And carrying on from the cursor takes the rest.
3146        let (cursor, took) = s
3147            .autoclaim(
3148                b"workers",
3149                b"bob",
3150                cursor.expect("a cursor"),
3151                0,
3152                100,
3153                true,
3154                1_000,
3155                &mut gone,
3156            )
3157            .expect("the group");
3158        assert_eq!(took.len(), 6);
3159        assert_eq!(cursor, None);
3160    }
3161
3162    #[test]
3163    fn a_group_survives_the_stream_being_trimmed_under_it() {
3164        let mut s = logged(10);
3165        s.create_group(b"workers", Id::MIN, Some(0));
3166        read(&mut s, "workers", "alice", Some(5), 100);
3167        // Trimming takes entries alice is still holding.
3168        assert_eq!(s.trim_maxlen(3, true, None), 7);
3169
3170        assert_eq!(s.group(b"workers").expect("the group").pending_len(), 5);
3171        let mut gone = Vec::new();
3172        let took = s
3173            .claim(
3174                b"workers",
3175                b"bob",
3176                &(1..=5).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>(),
3177                0,
3178                1_000,
3179                None,
3180                true,
3181                false,
3182                1_000,
3183                &mut gone,
3184            )
3185            .expect("the group");
3186        assert!(took.is_empty(), "none of them are there any more");
3187        assert_eq!(gone.len(), 5);
3188        assert_eq!(s.group(b"workers").expect("the group").pending_len(), 0);
3189    }
3190
3191    /// Out and back, checking everything a client can see on the way.
3192    fn round_trip(s: &Stream) -> Stream {
3193        let mut bytes = Vec::new();
3194        s.freeze(&mut bytes);
3195        let back = Stream::thaw(&bytes).expect("our own bytes");
3196        assert_eq!(dump(&back), dump(s), "the entries");
3197        assert_eq!(back.len(), s.len(), "the length");
3198        assert_eq!(back.added(), s.added(), "the count added");
3199        assert_eq!(back.last_id(), s.last_id(), "the last ID");
3200        assert_eq!(back.max_deleted_id(), s.max_deleted_id(), "the max deleted");
3201        assert_eq!(back.nodes(), s.nodes(), "the node count");
3202        assert_eq!(back, *s, "the whole thing");
3203        back
3204    }
3205
3206    #[test]
3207    fn a_frozen_stream_comes_back_with_every_entry_it_held() {
3208        let mut s = Stream::new();
3209        for ms in 1..=500u64 {
3210            add(&mut s, ms, 0, &[("job", "x"), ("n", "1")]);
3211        }
3212        add(&mut s, 500, 1, &[("job", "y")]);
3213        assert!(s.nodes() > 1, "more than one node, so the walk is tested");
3214        round_trip(&s);
3215    }
3216
3217    #[test]
3218    fn a_frozen_stream_keeps_the_holes_and_the_counters() {
3219        let mut s = logged(200);
3220        for ms in [3u64, 4, 5, 100, 199] {
3221            assert!(s.delete(Id::new(ms, 0)));
3222        }
3223        s.trim_minid(Id::new(20, 0), true, None);
3224        let back = round_trip(&s);
3225        assert_eq!(back.first_id(), Some(Id::new(20, 0)));
3226        assert!(!back.contains(Id::new(100, 0)), "a hole is still a hole");
3227        assert_eq!(back.max_deleted_id(), Id::new(199, 0));
3228    }
3229
3230    #[test]
3231    fn a_frozen_stream_keeps_its_groups_and_who_is_holding_what() {
3232        let mut s = logged(20);
3233        s.create_group(b"workers", Id::MIN, Some(0));
3234        s.create_group(b"audit", Id::new(5, 0), None);
3235        read(&mut s, "workers", "alice", Some(6), 1_000);
3236        read(&mut s, "workers", "bob", Some(4), 2_000);
3237        // One handed back to the group with nobody holding it, so the NACK count
3238        // has something to come back as.
3239        assert_eq!(
3240            s.nack(b"workers", Id::new(2, 0), Retry::Keep, true),
3241            Some(true)
3242        );
3243        // And one consumer deleted, so a slot in the middle is empty and the
3244        // slot numbers behind it have to survive.
3245        s.group_mut(b"workers")
3246            .expect("the group")
3247            .create_consumer(b"carol", 3_000);
3248        read(&mut s, "workers", "dave", Some(2), 4_000);
3249        s.group_mut(b"workers")
3250            .expect("the group")
3251            .delete_consumer(b"carol");
3252
3253        let back = round_trip(&s);
3254        let g = back.group(b"workers").expect("the group");
3255        assert_eq!(g.pending_len(), 12);
3256        assert_eq!(g.nacked_len(), 1);
3257        assert_eq!(g.entries_read(), Some(12));
3258        // Six, less the one handed back to the group.
3259        assert_eq!(g.consumer_named(b"alice").expect("alice").len(), 5);
3260        assert_eq!(g.consumer_named(b"bob").expect("bob").len(), 4);
3261        assert_eq!(g.consumer_named(b"dave").expect("dave").len(), 2);
3262        assert_eq!(g.consumer_named(b"carol"), None);
3263        // Dave came after carol, so his slot is the fourth one and reading the
3264        // empties back as empties is what keeps his entries his.
3265        assert_eq!(g.slot(b"dave"), Some(3));
3266        assert_eq!(g.nack(Id::new(1, 0)).expect("a nack").owner(), Some(0));
3267        assert_eq!(g.nack(Id::new(2, 0)).expect("a nack").owner(), None);
3268        assert_eq!(
3269            back.group(b"audit").expect("audit").last_id(),
3270            Id::new(5, 0)
3271        );
3272        assert_eq!(back.group(b"audit").expect("audit").entries_read(), None);
3273    }
3274
3275    #[test]
3276    fn a_stream_that_came_back_still_takes_entries_and_reads_them() {
3277        let mut s = logged(10);
3278        s.create_group(b"workers", Id::MIN, Some(0));
3279        read(&mut s, "workers", "alice", Some(4), 1_000);
3280
3281        let mut back = round_trip(&s);
3282        add(&mut back, 11, 0, &[("job", "new")]);
3283        assert_eq!(back.len(), 11);
3284        assert_eq!(
3285            read(&mut back, "workers", "alice", Some(3), 2_000),
3286            vec![Id::new(5, 0), Id::new(6, 0), Id::new(7, 0)]
3287        );
3288        assert!(
3289            back.group_mut(b"workers")
3290                .expect("the group")
3291                .ack(Id::new(1, 0))
3292        );
3293        assert_eq!(back.group(b"workers").expect("the group").pending_len(), 6);
3294    }
3295
3296    #[test]
3297    fn an_empty_stream_that_still_exists_comes_back() {
3298        let mut s = logged(3);
3299        for ms in 1..=3u64 {
3300            assert!(s.delete(Id::new(ms, 0)));
3301        }
3302        assert_eq!(s.nodes(), 0, "the last node went with the last entry");
3303        let back = round_trip(&s);
3304        assert!(back.is_empty());
3305        // The whole reason an empty stream is kept: a new entry still has to
3306        // beat the ID of one that is gone.
3307        assert_eq!(back.last_id(), Id::new(3, 0));
3308        round_trip(&Stream::new());
3309    }
3310
3311    #[test]
3312    fn a_frozen_stream_that_arrives_damaged_is_an_error_and_not_a_panic() {
3313        let mut s = logged(8);
3314        s.create_group(b"workers", Id::MIN, Some(0));
3315        read(&mut s, "workers", "alice", Some(3), 1_000);
3316        let mut bytes = Vec::new();
3317        s.freeze(&mut bytes);
3318
3319        for cut in 0..bytes.len() {
3320            assert!(Stream::thaw(&bytes[..cut]).is_err(), "cut at {cut}");
3321        }
3322        // Every bit of the header and the front of the first node, which is
3323        // where the counts and the lengths that a reader trusts all live.
3324        for at in 0..bytes.len().min(40) {
3325            for bit in 0..8 {
3326                let mut bad = bytes.clone();
3327                bad[at] ^= 1 << bit;
3328                // It either parses into some other stream or it does not. Either
3329                // way it comes back rather than going through a length that was
3330                // never checked.
3331                let _ = Stream::thaw(&bad);
3332            }
3333        }
3334        assert_eq!(Stream::thaw(&[]), Err(Broken::Short));
3335        assert_eq!(Stream::thaw(&[9]), Err(Broken::Form));
3336    }
3337
3338    #[test]
3339    fn nothing_at_all() {
3340        let mut s = Stream::new();
3341        assert!(s.is_empty());
3342        assert_eq!(s.len(), 0);
3343        assert_eq!(s.first_id(), None);
3344        assert_eq!(s.last_id(), Id::MIN);
3345        assert_eq!(s.trim_maxlen(0, true, None), 0);
3346        assert_eq!(s.trim_minid(Id::MAX, true, None), 0);
3347        assert_eq!(dump(&s), vec![]);
3348    }
3349}