Skip to main content

yo_kv/
array.rs

1//! A sparse array: a sequence indexed by a `u64`, with holes.
2//!
3//! This is the type behind the `AR*` commands Redis added in 8.9, and it is the
4//! only collection here whose index is unsigned. A list is indexed from either
5//! end and `-1` is the last element. An array is indexed by position in a space
6//! that runs to `2^64 - 2`, so `-1` is not the end of anything, it is an error,
7//! and most of that space is empty at any moment.
8//!
9//! ```text
10//!   slices, sorted by id, binary searched
11//! +---------+---------+-------------------+---------+
12//! | id 0    | id 7    |        ...        | id 9e12 |
13//! | sparse  | dense   |                   | sparse  |
14//! +---------+---------+-------------------+---------+
15//!      \                                        /
16//!       \--- offsets and words -----------------/
17//!                     |
18//!             +-------------------------------+
19//!             | one blob per array, for the   |
20//!             | values too long to inline     |
21//!             +-------------------------------+
22//! ```
23//!
24//! # Two numbers that are not the same number
25//!
26//! [`Array::len`] is the highest populated index plus one and [`Array::count`]
27//! is how many indices are populated. `ARSET k 1000000 x` gives a length of a
28//! million and one and a count of one. Every other collection here has one
29//! number for both and it is worth saying out loud, because a caller that
30//! reaches for the wrong one gets an answer rather than an error.
31//!
32//! # Where the slices live
33//!
34//! Redis keeps a flat directory of slice pointers indexed by slice id, and then
35//! a second structure over that for when the ids get far apart, because a flat
36//! array indexed by `idx >> 12` is nine billion entries for an index of nine
37//! trillion. Here it is one `Vec` of `(id, slice)` kept sorted and binary
38//! searched, which covers the whole index space in one structure with no
39//! second mode to get wrong, and costs a handful of compares on a get instead
40//! of one load. A key with a thousand slices is ten compares, and a key with a
41//! thousand slices is four million elements, so the compares are noise next to
42//! what the caller is doing with the data.
43//!
44//! # Where the values live
45//!
46//! A value of eight bytes or more is a slice of one blob owned by the array, and
47//! everything shorter is inlined in the word itself. Redis heap allocates each
48//! of those, paying a malloc header and the rounding on every one. One blob per
49//! key pays the bytes and nothing else, at the cost of having to compact when
50//! enough of it is dead. See `Word` for the four things a word can be.
51
52use std::cmp::Ordering;
53
54use yo_common::num;
55use yo_common::{Code, Error, Result};
56
57use crate::frozen::{self, Broken};
58
59/// How many indices one slice covers.
60///
61/// Redis's `AR_SLICE_SIZE_DEFAULT`, and unlike Redis it is not configurable,
62/// because the two settings either side of the default were not worth a branch
63/// in `slice_of` and nobody has ever reported tuning them.
64pub const SLICE_SIZE: u64 = 4096;
65
66/// `SLICE_SIZE` as a shift, so the divide is a shift.
67const SLICE_BITS: u32 = SLICE_SIZE.trailing_zeros();
68
69/// The most elements a slice holds while staying sparse.
70///
71/// Redis's `AR_SPARSE_KMAX_DEFAULT`. Above this a slice is worth an index, below
72/// it the pairs are cheaper than the holes.
73const SPARSE_MAX: usize = 10;
74
75/// The fewest elements a dense slice keeps before going back to pairs.
76///
77/// Redis's `AR_SPARSE_KMIN_DEFAULT`. It is half of `SPARSE_MAX` rather than
78/// equal to it so that a slice sitting on the line does not rebuild itself on
79/// every other write.
80const SPARSE_MIN: usize = 5;
81
82/// The directory and its slices, which is the only form an array is written in.
83const FORM_SLICES: u8 = 1;
84/// On the form byte, that the insert cursor has been set and follows it.
85const HAS_INSERT: u8 = 0x80;
86/// A slice held as offsets and words.
87const LAYOUT_SPARSE: u8 = 1;
88/// A slice held as a window.
89const LAYOUT_DENSE: u8 = 2;
90
91/// The largest index an array will accept.
92///
93/// Redis reserves `UINT64_MAX` as "no insert has happened yet" in the cursor
94/// that `ARINSERT` and `ARNEXT` share, so it is not a position anything can be
95/// written to, and `ARSET k 18446744073709551615 v` is an error rather than a
96/// write. Keeping the same ceiling here keeps that cursor able to mean the same
97/// thing when it lands.
98pub const INDEX_MAX: u64 = u64::MAX - 1;
99
100/// Room for the longest text an [`Element`] can turn into.
101///
102/// The float is the long one, and it is the widest double plus the `.0` that
103/// gets appended to one that came out looking like an integer.
104pub const ELEMENT_MAX: usize = num::DOUBLE_MAX + 2;
105
106/// What is stored at one index, in whichever form it was worth keeping.
107///
108/// Handing back the stored form rather than bytes is Y18: a value that went in
109/// as `12345` is held as an `i64` and formatted once, into the reply buffer, at
110/// the moment the reply is built. Use [`Element::text`] to get the bytes when
111/// the caller has nowhere better to put them.
112#[derive(Debug, Clone, Copy, PartialEq)]
113pub enum Element<'a> {
114    /// A value that was written as an integer and is held as one.
115    Int(i64),
116    /// A value that was written as a decimal and round trips as a double.
117    Float(f64),
118    /// A string long enough to live in the array's blob, borrowed from it.
119    Str(&'a [u8]),
120    /// A string of seven bytes or fewer, which was packed into the word and so
121    /// has nowhere to be borrowed from.
122    Short(Short),
123}
124
125impl<'a> Element<'a> {
126    /// The bytes a client would see, written into `buf` if they are not stored
127    /// anywhere already.
128    ///
129    /// A blob string is already bytes and comes back borrowed with `buf`
130    /// untouched. Everything else has to be written somewhere, and it goes into
131    /// the caller's stack buffer rather than a `Vec`, because the caller needs
132    /// the length before it can write the bulk header and a shard thread that
133    /// allocates aborts.
134    pub fn text<'b>(&'b self, buf: &'b mut [u8; ELEMENT_MAX]) -> &'b [u8]
135    where
136        'a: 'b,
137    {
138        match *self {
139            // The two cases with nothing to do, borrowed straight through.
140            Element::Str(s) => s,
141            Element::Short(ref s) => s.as_bytes(),
142            Element::Int(i) => {
143                let mut digits = [0u8; num::DIGITS_MAX];
144                let text = num::i64_digits(&mut digits, i);
145                let n = text.len();
146                buf[..n].copy_from_slice(text);
147                &buf[..n]
148            }
149            Element::Float(d) => {
150                let mut wide = [0u8; num::DOUBLE_MAX];
151                let text = num::write_double(&mut wide, d);
152                let mut n = text.len();
153                buf[..n].copy_from_slice(text);
154                // Redis's `arFormatFloat`: a stored double that prints without a
155                // dot or an exponent gets `.0` put back on, so that a value
156                // written as `1.0` does not read back as `1`. Nothing that
157                // reached this branch was written as `1`, because the integer
158                // encoding takes those first.
159                if !text.iter().any(|&c| c == b'.' || c == b'e' || c == b'E') {
160                    buf[n] = b'.';
161                    buf[n + 1] = b'0';
162                    n += 2;
163                }
164                &buf[..n]
165            }
166        }
167    }
168}
169
170/// A string short enough that it was stored inside the word.
171///
172/// It exists because an [`Element`] borrows and there is nothing to borrow
173/// from: the bytes were in the eight bytes that were read, and unpacking them
174/// somewhere is unavoidable. Carrying them by value in the element is seven
175/// bytes on the caller's stack, which is cheaper than the alternative of making
176/// every reader pass in a scratch buffer for the case where the value is short.
177#[derive(Clone, Copy, PartialEq, Eq)]
178pub struct Short {
179    buf: [u8; INLINE_MAX],
180    len: u8,
181}
182
183impl Short {
184    /// The bytes, as they were written.
185    #[must_use]
186    pub fn as_bytes(&self) -> &[u8] {
187        &self.buf[..usize::from(self.len)]
188    }
189}
190
191impl core::fmt::Debug for Short {
192    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
193        write!(f, "{:?}", String::from_utf8_lossy(self.as_bytes()))
194    }
195}
196
197/// One stored value, in eight bytes, or the empty slot.
198///
199/// The low two bits are a tag and the rest is payload, which is Redis's tagged
200/// pointer scheme with the pointer replaced by an offset into the array's own
201/// blob. That replacement is the whole memory argument: Redis pays eight bytes
202/// of pointer plus a malloc header plus rounding for every value of eight bytes
203/// or more, and this pays eight bytes plus the payload.
204///
205/// ```text
206///   tag 00  offset into the blob: length in bits 2..32, start in bits 32..64
207///   tag 01  a signed integer in the top 62 bits
208///   tag 10  an f64 with its low two bits cleared
209///   tag 11  a string of up to seven bytes: length in bits 2..5, bytes from 8
210/// ```
211///
212/// [`Word::EMPTY`] is all zeroes, which is unambiguous: a blob word always has a
213/// length of at least eight so its payload is never zero, and the other three
214/// tags are non zero by construction.
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216struct Word(u64);
217
218/// Values of this many bytes and up go in the blob, and shorter ones inline.
219const INLINE_MAX: usize = 7;
220
221/// The largest blob one array can hold, because a start has to fit 32 bits.
222const BLOB_MAX: usize = u32::MAX as usize;
223
224/// The longest single value, because a length has to fit the other 30 bits.
225///
226/// A gigabyte, and a client cannot send one anyway: `proto-max-bulk-len` caps a
227/// bulk string at 512 megabytes, so nothing that gets here can be over this. It
228/// is checked rather than assumed because the embedded API does not go through
229/// the protocol and can hand over whatever it likes.
230const VALUE_MAX: usize = (1 << 30) - 1;
231
232const TAG_MASK: u64 = 0b11;
233const TAG_BLOB: u64 = 0;
234const TAG_INT: u64 = 1;
235const TAG_FLOAT: u64 = 2;
236const TAG_STR: u64 = 3;
237
238/// The range of integers that fits the 62 bit payload, which is Redis's
239/// `arIntFits`. Anything outside it is kept as the text it arrived as.
240const INT_LO: i64 = -(1 << 61);
241const INT_HI: i64 = (1 << 61) - 1;
242
243impl Word {
244    /// Nothing is stored here.
245    const EMPTY: Word = Word(0);
246
247    const fn is_empty(self) -> bool {
248        self.0 == 0
249    }
250
251    const fn tag(self) -> u64 {
252        self.0 & TAG_MASK
253    }
254
255    const fn from_int(i: i64) -> Word {
256        Word(((i as u64) << 2) | TAG_INT)
257    }
258
259    const fn to_int(self) -> i64 {
260        // Arithmetic shift, so the sign comes back with it.
261        (self.0 as i64) >> 2
262    }
263
264    const fn from_float_bits(bits: u64) -> Word {
265        Word((bits & !TAG_MASK) | TAG_FLOAT)
266    }
267
268    const fn to_float(self) -> f64 {
269        f64::from_bits(self.0 & !TAG_MASK)
270    }
271
272    fn from_short(s: &[u8]) -> Word {
273        let mut v = TAG_STR | ((s.len() as u64) << 2);
274        for (i, &b) in s.iter().enumerate() {
275            v |= u64::from(b) << (8 * (i + 1));
276        }
277        Word(v)
278    }
279
280    const fn short_len(self) -> usize {
281        ((self.0 >> 2) & 0b111) as usize
282    }
283
284    fn to_short(self) -> Short {
285        let n = self.short_len();
286        let mut buf = [0u8; INLINE_MAX];
287        for (i, out) in buf.iter_mut().take(n).enumerate() {
288            *out = ((self.0 >> (8 * (i + 1))) & 0xff) as u8;
289        }
290        Short { buf, len: n as u8 }
291    }
292
293    const fn from_blob(start: usize, len: usize) -> Word {
294        Word(((start as u64) << 32) | ((len as u64) << 2) | TAG_BLOB)
295    }
296
297    const fn blob_span(self) -> (usize, usize) {
298        let start = (self.0 >> 32) as usize;
299        let len = ((self.0 >> 2) & 0x3fff_ffff) as usize;
300        (start, len)
301    }
302}
303
304/// The one slice of the index space that holds anything, in whichever layout
305/// fits it.
306#[derive(Debug, Clone)]
307struct Slice {
308    /// How many of the words are populated. Never zero: an empty slice is
309    /// dropped rather than kept, so that `len` does not have to walk past any.
310    count: u16,
311    layout: Layout,
312}
313
314/// The two ways a slice holds its words.
315///
316/// Both carry a `Vec<Word>`, which is what lets blob compaction walk every word
317/// in the array without caring which layout it is looking at.
318#[derive(Debug, Clone)]
319enum Layout {
320    /// Offsets and words in parallel, sorted by offset, binary searched.
321    ///
322    /// Ten bytes an element and no cost for the holes, which is what a slice
323    /// with a handful of scattered elements wants.
324    Sparse { offs: Vec<u16>, words: Vec<Word> },
325    /// A window of consecutive positions, with `offset` the position of
326    /// `words[0]`.
327    ///
328    /// Eight bytes a position including the holes, so this only wins when the
329    /// populated positions are close together. The window is kept trimmed of
330    /// leading and trailing empties, which is what makes the highest populated
331    /// offset derivable rather than stored.
332    Dense { offset: u16, words: Vec<Word> },
333}
334
335impl Slice {
336    /// Every word in the slice, for the one walk that rewrites them all: blob
337    /// compaction.
338    fn words_mut(&mut self) -> &mut [Word] {
339        match &mut self.layout {
340            Layout::Sparse { words, .. } | Layout::Dense { words, .. } => words,
341        }
342    }
343
344    /// The same walk, for the one that only reads: freezing the blob.
345    fn words(&self) -> &[Word] {
346        match &self.layout {
347            Layout::Sparse { words, .. } | Layout::Dense { words, .. } => words,
348        }
349    }
350
351    /// The highest populated offset. The slice is never empty, so there is one.
352    fn high(&self) -> u16 {
353        match &self.layout {
354            Layout::Sparse { offs, .. } => *offs.last().expect("a slice is never empty"),
355            // Trimmed, so the last word is populated.
356            Layout::Dense { offset, words } => offset + (words.len() as u16) - 1,
357        }
358    }
359
360    fn get(&self, off: u16) -> Word {
361        match &self.layout {
362            Layout::Sparse { offs, words } => match offs.binary_search(&off) {
363                Ok(at) => words[at],
364                Err(_) => Word::EMPTY,
365            },
366            Layout::Dense { offset, words } => {
367                if off < *offset {
368                    return Word::EMPTY;
369                }
370                let at = usize::from(off - offset);
371                words.get(at).copied().unwrap_or(Word::EMPTY)
372            }
373        }
374    }
375
376    /// Writes a word, and answers with what was there before.
377    fn put(&mut self, off: u16, w: Word) -> Word {
378        let old = match &mut self.layout {
379            Layout::Sparse { offs, words } => match offs.binary_search(&off) {
380                Ok(at) => std::mem::replace(&mut words[at], w),
381                Err(at) => {
382                    offs.insert(at, off);
383                    words.insert(at, w);
384                    Word::EMPTY
385                }
386            },
387            Layout::Dense { offset, words } => {
388                if off < *offset {
389                    // Growing downwards moves the window, which is a memmove
390                    // bounded by the slice: 32 KiB at the very worst and zero on
391                    // the ascending writes that are what an array is normally
392                    // filled by.
393                    let gap = usize::from(*offset - off);
394                    words.splice(0..0, std::iter::repeat_n(Word::EMPTY, gap));
395                    *offset = off;
396                    std::mem::replace(&mut words[0], w)
397                } else {
398                    let at = usize::from(off - *offset);
399                    if at >= words.len() {
400                        words.resize(at + 1, Word::EMPTY);
401                    }
402                    std::mem::replace(&mut words[at], w)
403                }
404            }
405        };
406        if old.is_empty() {
407            self.count += 1;
408        }
409        old
410    }
411
412    /// Clears a position, and answers with what was there.
413    fn take(&mut self, off: u16) -> Word {
414        let old = match &mut self.layout {
415            Layout::Sparse { offs, words } => match offs.binary_search(&off) {
416                Ok(at) => {
417                    offs.remove(at);
418                    words.remove(at)
419                }
420                Err(_) => Word::EMPTY,
421            },
422            Layout::Dense { offset, words } => {
423                if off < *offset {
424                    Word::EMPTY
425                } else {
426                    let at = usize::from(off - *offset);
427                    match words.get_mut(at) {
428                        Some(slot) => std::mem::replace(slot, Word::EMPTY),
429                        None => Word::EMPTY,
430                    }
431                }
432            }
433        };
434        if !old.is_empty() {
435            self.count -= 1;
436            self.trim();
437        }
438        old
439    }
440
441    /// Drops the empties off both ends of a dense window.
442    ///
443    /// This is what keeps [`Slice::high`] derivable, and it is also why a dense
444    /// slice that has been emptied from the middle out does not keep paying for
445    /// the positions nobody is using any more.
446    fn trim(&mut self) {
447        let Layout::Dense { offset, words } = &mut self.layout else {
448            return;
449        };
450        while words.last().is_some_and(|w| w.is_empty()) {
451            words.pop();
452        }
453        let lead = words.iter().take_while(|w| w.is_empty()).count();
454        if lead > 0 {
455            words.drain(..lead);
456            *offset += lead as u16;
457        }
458    }
459
460    /// The number of positions a dense window would have to cover.
461    fn span(&self) -> usize {
462        match &self.layout {
463            Layout::Sparse { offs, .. } => match (offs.first(), offs.last()) {
464                (Some(lo), Some(hi)) => usize::from(hi - lo) + 1,
465                _ => 0,
466            },
467            Layout::Dense { words, .. } => words.len(),
468        }
469    }
470
471    /// Moves the slice to whichever layout now fits it.
472    ///
473    /// Redis promotes on the count alone, at more than ten elements, which can
474    /// put eleven elements in a thirty two kilobyte window. Promotion is a
475    /// memory question and not a count question, so this asks about the span
476    /// too: dense is eight bytes a position and sparse is ten bytes an element,
477    /// so dense only wins while the positions are within about twice the count
478    /// of each other.
479    fn rebalance(&mut self) {
480        let count = usize::from(self.count);
481        match &self.layout {
482            Layout::Sparse { .. } => {
483                if count > SPARSE_MAX && self.span() <= count * 2 {
484                    self.make_dense();
485                }
486            }
487            Layout::Dense { .. } => {
488                if count <= SPARSE_MIN || self.span() > count * 4 {
489                    self.make_sparse();
490                }
491            }
492        }
493    }
494
495    fn make_dense(&mut self) {
496        let Layout::Sparse { offs, words } = &self.layout else {
497            return;
498        };
499        let base = offs[0];
500        let span = self.span();
501        let mut window = vec![Word::EMPTY; span];
502        for (&off, &w) in offs.iter().zip(words) {
503            window[usize::from(off - base)] = w;
504        }
505        self.layout = Layout::Dense {
506            offset: base,
507            words: window,
508        };
509    }
510
511    fn make_sparse(&mut self) {
512        let Layout::Dense { offset, words } = &self.layout else {
513            return;
514        };
515        let mut offs = Vec::with_capacity(usize::from(self.count));
516        let mut vals = Vec::with_capacity(usize::from(self.count));
517        for (i, &w) in words.iter().enumerate() {
518            if !w.is_empty() {
519                offs.push(offset + (i as u16));
520                vals.push(w);
521            }
522        }
523        self.layout = Layout::Sparse { offs, words: vals };
524    }
525
526    fn memory_bytes(&self) -> usize {
527        match &self.layout {
528            Layout::Sparse { offs, words } => {
529                offs.capacity() * 2 + words.capacity() * size_of::<Word>()
530            }
531            Layout::Dense { words, .. } => words.capacity() * size_of::<Word>(),
532        }
533    }
534
535    /// Hands every populated offset in `from..=to` to `f`, in whichever
536    /// direction was asked for, and stops early when `f` says to.
537    ///
538    /// Both layouts cost what is in the window rather than how wide it is. A
539    /// sparse slice binary searches for the first entry in range and walks its
540    /// entries, and a dense one walks the part of its window that overlaps and
541    /// skips the holes, which is the whole reason a scan of the entire index
542    /// space over a key holding three elements is three visits.
543    ///
544    /// Answers whether the caller should carry on to the next slice.
545    fn window<F>(&self, from: u16, to: u16, reverse: bool, f: &mut F) -> bool
546    where
547        F: FnMut(u16, Word) -> bool,
548    {
549        match &self.layout {
550            Layout::Sparse { offs, words } => {
551                // Sorted, and no entry is ever empty, so the two ends of the
552                // window are two binary searches and everything between them is
553                // a hit.
554                let a = offs.partition_point(|&o| o < from);
555                let b = offs.partition_point(|&o| o <= to);
556                if reverse {
557                    for i in (a..b).rev() {
558                        if !f(offs[i], words[i]) {
559                            return false;
560                        }
561                    }
562                } else {
563                    for i in a..b {
564                        if !f(offs[i], words[i]) {
565                            return false;
566                        }
567                    }
568                }
569            }
570            Layout::Dense { offset, words } => {
571                let base = *offset;
572                let end = base + (words.len() as u16) - 1;
573                if to < base || from > end {
574                    return true;
575                }
576                let a = usize::from(from.max(base) - base);
577                let b = usize::from(to.min(end) - base);
578                let window = &words[a..=b];
579                let at = |i: usize| base + ((a + i) as u16);
580                if reverse {
581                    for (i, w) in window.iter().enumerate().rev() {
582                        if !w.is_empty() && !f(at(i), *w) {
583                            return false;
584                        }
585                    }
586                } else {
587                    for (i, w) in window.iter().enumerate() {
588                        if !w.is_empty() && !f(at(i), *w) {
589                            return false;
590                        }
591                    }
592                }
593            }
594        }
595        true
596    }
597}
598
599/// A sparse array of values, indexed by a `u64`.
600#[derive(Debug, Clone, Default)]
601pub struct Array {
602    /// Populated slices, sorted by slice id and binary searched. No slice in
603    /// here is empty.
604    slices: Vec<(u64, Slice)>,
605    /// Every value of eight bytes or more, back to back.
606    blob: Vec<u8>,
607    /// How much of the blob is no longer pointed at by any word.
608    dead: usize,
609    /// How many indices are populated, kept rather than counted because
610    /// `ARCOUNT` is documented as O(1).
611    count: u64,
612    /// The last index `ARINSERT` or `ARRING` wrote to, or none when neither has
613    /// written yet.
614    ///
615    /// This is the array's own cursor and it is nothing to do with where the
616    /// elements are. `ARSET` does not move it, so an array filled by `ARSET`
617    /// and then appended to with `ARINSERT` gets its first append at index
618    /// zero, on top of whatever was there. Redis holds the same thing as a
619    /// `u64` with `UINT64_MAX` meaning not set, which is also why that index is
620    /// not addressable.
621    insert: Option<u64>,
622}
623
624/// How dead a blob has to get before it is worth rewriting.
625///
626/// Half, with a floor so that a small array does not compact on every overwrite.
627/// Compaction is one pass over the words and one pass over the live bytes, and
628/// paying that when a quarter of a kilobyte is dead would cost more than the
629/// kilobyte.
630const COMPACT_MIN: usize = 4096;
631
632impl Array {
633    /// A new, empty array.
634    #[must_use]
635    pub fn new() -> Array {
636        Array::default()
637    }
638
639    /// The highest populated index plus one, which is `ARLEN`.
640    ///
641    /// Zero for an empty array, and note that this is not the number of
642    /// elements. See [`Array::count`] for that.
643    #[must_use]
644    pub fn len(&self) -> u64 {
645        match self.slices.last() {
646            Some((id, slice)) => id * SLICE_SIZE + u64::from(slice.high()) + 1,
647            None => 0,
648        }
649    }
650
651    /// How many indices are populated, which is `ARCOUNT`.
652    #[must_use]
653    pub const fn count(&self) -> u64 {
654        self.count
655    }
656
657    /// Whether anything is stored at all.
658    #[must_use]
659    pub const fn is_empty(&self) -> bool {
660        self.count == 0
661    }
662
663    /// The value at `idx`, or none if that position is a hole.
664    ///
665    /// A missing key and a hole are the same answer to a client, which is why
666    /// there is one `None` here and not two.
667    #[must_use]
668    pub fn get(&self, idx: u64) -> Option<Element<'_>> {
669        let (id, off) = split(idx);
670        let at = self.find(id).ok()?;
671        let w = self.slices[at].1.get(off);
672        self.decode(w)
673    }
674
675    /// Writes `val` at `idx`, and answers whether that position was empty.
676    ///
677    /// The count of newly filled positions is what `ARSET` and `ARMSET` reply
678    /// with, so the boolean is the useful return rather than the old value.
679    ///
680    /// # Errors
681    ///
682    /// [`Code::Full`] when the blob of long values would pass four gigabytes,
683    /// which is a recorded divergence: Redis heap allocates each of those and
684    /// has no per key ceiling.
685    pub fn set(&mut self, idx: u64, val: &[u8]) -> Result<bool> {
686        let w = self.encode(val)?;
687        let (id, off) = split(idx);
688        let at = match self.find(id) {
689            Ok(at) => at,
690            Err(at) => {
691                self.slices.insert(
692                    at,
693                    (
694                        id,
695                        Slice {
696                            count: 0,
697                            layout: Layout::Sparse {
698                                offs: Vec::new(),
699                                words: Vec::new(),
700                            },
701                        },
702                    ),
703                );
704                at
705            }
706        };
707        let old = self.slices[at].1.put(off, w);
708        self.slices[at].1.rebalance();
709        self.retire(old);
710        self.maybe_compact();
711        if old.is_empty() {
712            self.count += 1;
713            Ok(true)
714        } else {
715            Ok(false)
716        }
717    }
718
719    /// Clears `idx`, and answers whether anything was there.
720    pub fn del(&mut self, idx: u64) -> bool {
721        let (id, off) = split(idx);
722        let Ok(at) = self.find(id) else {
723            return false;
724        };
725        let old = self.slices[at].1.take(off);
726        if old.is_empty() {
727            return false;
728        }
729        self.retire(old);
730        self.count -= 1;
731        if self.slices[at].1.count == 0 {
732            self.slices.remove(at);
733        } else {
734            self.slices[at].1.rebalance();
735        }
736        self.maybe_compact();
737        true
738    }
739
740    /// Clears every populated index in `lo..=hi`, and answers how many there
741    /// were.
742    ///
743    /// The cost is in the slices the range touches and not in the width of the
744    /// range, so `ARDELRANGE k 0 18446744073709551614` on a key holding three
745    /// elements is three deletes and not a walk of the index space.
746    pub fn delete_range(&mut self, lo: u64, hi: u64) -> u64 {
747        if lo > hi {
748            return 0;
749        }
750        let (lo_id, lo_off) = split(lo);
751        let (hi_id, hi_off) = split(hi);
752        let first = match self.find(lo_id) {
753            Ok(at) | Err(at) => at,
754        };
755        let mut gone = 0;
756        let mut at = first;
757        while at < self.slices.len() && self.slices[at].0 <= hi_id {
758            let id = self.slices[at].0;
759            // A slice strictly inside the range loses everything, and the two on
760            // the ends lose the part that is in it.
761            let from = if id == lo_id { lo_off } else { 0 };
762            let to = if id == hi_id {
763                hi_off
764            } else {
765                (SLICE_SIZE - 1) as u16
766            };
767            gone += self.clear_within(at, from, to);
768            if self.slices[at].1.count == 0 {
769                self.slices.remove(at);
770            } else {
771                self.slices[at].1.rebalance();
772                at += 1;
773            }
774        }
775        self.count -= gone;
776        self.maybe_compact();
777        self.maybe_compact_slices();
778        gone
779    }
780
781    /// The index the next `ARINSERT` would write to, which is `ARNEXT`.
782    ///
783    /// None when the cursor has run out of space, which happens only after an
784    /// `ARSEEK` to the very top: the next append would have nowhere to go, and
785    /// Redis answers a null rather than an index it cannot honour.
786    #[must_use]
787    pub const fn next_index(&self) -> Option<u64> {
788        match self.insert {
789            None => Some(0),
790            Some(i) if i >= INDEX_MAX => None,
791            Some(i) => Some(i + 1),
792        }
793    }
794
795    /// Points the cursor so that the next append lands on `idx`, which is
796    /// `ARSEEK`.
797    ///
798    /// Seeking to zero is not the same as seeking to one less than one: it puts
799    /// the cursor back in the state it was in before anything was appended,
800    /// which is also the state `ARRING` reads as "do not reshape me".
801    pub const fn seek(&mut self, idx: u64) {
802        self.insert = if idx == 0 { None } else { Some(idx - 1) };
803    }
804
805    /// Appends `values` at consecutive indices from the cursor, which is
806    /// `ARINSERT`, and answers where the last one landed.
807    ///
808    /// # Errors
809    ///
810    /// [`Code::Invalid`] with [`INSERT_OVERFLOW`] when the batch would run off
811    /// the top of the index space, checked before any of it is written so that
812    /// a batch either lands whole or not at all.
813    pub fn append<'v>(&mut self, values: impl Iterator<Item = &'v [u8]> + Clone) -> Result<u64> {
814        let n = values.clone().count() as u64;
815        let over = || Error::new(Code::Invalid, INSERT_OVERFLOW);
816        let start = self.next_index().ok_or_else(over)?;
817        if n == 0 {
818            return Ok(self.insert.unwrap_or(0));
819        }
820        let last = start.checked_add(n - 1).filter(|l| *l <= INDEX_MAX);
821        let last = last.ok_or_else(over)?;
822        for (i, v) in values.enumerate() {
823            self.set(start + i as u64, v)?;
824        }
825        self.insert = Some(last);
826        Ok(last)
827    }
828
829    /// Writes `values` into a ring of `size` positions, which is `ARRING`, and
830    /// answers where the last one landed.
831    ///
832    /// The ring is not a structure, it is an agreement about indices: writes go
833    /// to the cursor plus one modulo the size, so a ring of ten holds indices
834    /// zero to nine and the eleventh write goes back over the first. Changing
835    /// the size between calls is the only expensive case, because the positions
836    /// that survive have to be renumbered so that they stay in order, and that
837    /// is the `O(N + M)` in the command's complexity.
838    ///
839    /// # Errors
840    ///
841    /// Whatever [`Array::set`] can fail with, which is the two size ceilings.
842    pub fn ring<'v>(&mut self, size: u64, values: impl Iterator<Item = &'v [u8]>) -> Result<u64> {
843        debug_assert!(size > 0, "the caller refuses a size of zero");
844        let old_span = self.len();
845        // A reshape is needed when the ring shrank, and when it grew after it
846        // had already wrapped, because otherwise the next write would go back
847        // over the oldest position instead of using the room that was just
848        // added. An explicit seek to zero says where the next write goes, so it
849        // is honoured rather than reshaped around.
850        let keep = if old_span == 0 || size == old_span {
851            0
852        } else if size < old_span {
853            size
854        } else if self.insert.is_some() && self.next_cursor() < old_span {
855            old_span
856        } else {
857            0
858        };
859        if keep > 0 {
860            self.rework(old_span, keep)?;
861        }
862
863        let mut cursor = self.insert.unwrap_or(0);
864        for v in values {
865            cursor = self.next_cursor();
866            if cursor >= size {
867                cursor %= size;
868            }
869            self.set(cursor, v)?;
870            self.insert = Some(cursor);
871        }
872        Ok(cursor)
873    }
874
875    /// Where the next ring write goes before the size is applied to it.
876    ///
877    /// Wrapping, because a cursor sitting on the last addressable index steps to
878    /// the reserved one, and the modulo in [`Array::ring`] brings it back into
879    /// the ring anyway. Redis relies on the same wrap.
880    const fn next_cursor(&self) -> u64 {
881        match self.insert {
882            None => 0,
883            Some(i) => i.wrapping_add(1),
884        }
885    }
886
887    /// Renumbers the ring so the positions that survive a size change stay in
888    /// order, oldest at zero.
889    ///
890    /// The walk goes backwards from the cursor and stops at the first hole, so
891    /// a ring that somebody has been deleting out of keeps its newest unbroken
892    /// run and not a scattering either side of a gap.
893    fn rework(&mut self, old_span: u64, keep: u64) -> Result<()> {
894        let anchor = match self.insert {
895            None => old_span - 1,
896            Some(i) => i % old_span,
897        };
898        let back = |i: u64| if i == 0 { old_span - 1 } else { i - 1 };
899        let forward = |i: u64| if i + 1 == old_span { 0 } else { i + 1 };
900
901        let mut kept = 0;
902        let mut src = anchor;
903        while kept < keep && self.get(src).is_some() {
904            kept += 1;
905            src = back(src);
906        }
907        // The walk stopped one past the oldest one it kept.
908        src = forward(src);
909
910        let mut fresh = Array::new();
911        for dst in 0..kept {
912            let mut buf = [0u8; ELEMENT_MAX];
913            let el = self.get(src).expect("the walk stopped at the first hole");
914            fresh.set(dst, el.text(&mut buf))?;
915            src = forward(src);
916        }
917        fresh.insert = kept.checked_sub(1);
918        *self = fresh;
919        Ok(())
920    }
921
922    /// The last `count` positions from the cursor, which is `ARLASTITEMS`, and
923    /// answers how many that turned out to be.
924    ///
925    /// Positions and not elements, so a hole inside the window is reported as
926    /// one, and the walk wraps at the bottom of the array back to the top. `f`
927    /// is called oldest first, or newest first when `newest_first`.
928    pub fn last_items<F>(&self, count: u64, newest_first: bool, mut f: F) -> u64
929    where
930        F: FnMut(Option<Element<'_>>),
931    {
932        let steps = count.min(self.count);
933        if steps == 0 {
934            return 0;
935        }
936        let span = self.len();
937        // With no cursor the tail of the array is the anchor, which is what
938        // makes this answer something sensible for an array nobody has
939        // appended to. A cursor past the end of the array is left where it is
940        // rather than folded in, so the positions above the array read as the
941        // holes they are.
942        let anchor = self.insert.unwrap_or(span - 1);
943        // The backwards walk is at most two descending runs: from the anchor
944        // down towards zero, and then, if it ran out, from the top of the array
945        // downwards. Naming the two runs is what lets this answer in
946        // chronological order without collecting the positions first.
947        let near = steps.min(anchor + 1);
948        let wrapped = steps - near;
949        let near_lo = anchor - (near - 1);
950        let wrapped_lo = span - wrapped;
951
952        let mut emit = |i: u64| f(self.get(i));
953        if newest_first {
954            (near_lo..=anchor).rev().for_each(&mut emit);
955            (wrapped_lo..span).rev().for_each(&mut emit);
956        } else {
957            (wrapped_lo..span).for_each(&mut emit);
958            (near_lo..=anchor).for_each(&mut emit);
959        }
960        steps
961    }
962
963    /// Hands every populated index in `start..=end` to `f`, which is `ARSCAN`.
964    ///
965    /// Low to high, or high to low when the two ends come the other way round.
966    /// `f` answers whether to keep going, which is how `LIMIT` stops the walk
967    /// without the walk knowing what a limit is. Holes are skipped rather than
968    /// reported, which is the whole difference between this and `ARGETRANGE`,
969    /// and it is why this one needs no cap: the cost is the elements it finds
970    /// and the slices it has to look in, not the width of the range.
971    pub fn scan<F>(&self, start: u64, end: u64, mut f: F)
972    where
973        F: FnMut(u64, Element<'_>) -> bool,
974    {
975        let reverse = start > end;
976        let (lo, hi) = if reverse { (end, start) } else { (start, end) };
977        let (lo_id, lo_off) = split(lo);
978        let (hi_id, hi_off) = split(hi);
979        let first = match self.find(lo_id) {
980            Ok(at) | Err(at) => at,
981        };
982        let last = match self.find(hi_id) {
983            Ok(at) => at + 1,
984            Err(at) => at,
985        };
986
987        let mut visit = |at: usize| {
988            let (id, slice) = &self.slices[at];
989            let from = if *id == lo_id { lo_off } else { 0 };
990            let to = if *id == hi_id {
991                hi_off
992            } else {
993                (SLICE_SIZE - 1) as u16
994            };
995            let base = id * SLICE_SIZE;
996            slice.window(from, to, reverse, &mut |off, w| {
997                let el = self.decode(w).expect("a populated word decodes");
998                f(base + u64::from(off), el)
999            })
1000        };
1001        if reverse {
1002            for at in (first..last).rev() {
1003                if !visit(at) {
1004                    return;
1005                }
1006            }
1007        } else {
1008            for at in first..last {
1009                if !visit(at) {
1010                    return;
1011                }
1012            }
1013        }
1014    }
1015
1016    /// What `ARINFO` says about the array.
1017    ///
1018    /// The per layout numbers cost a walk of the directory and are only filled
1019    /// in when `full`, which is the same split Redis makes and for the same
1020    /// reason: the seven cheap numbers are all read off fields.
1021    #[must_use]
1022    pub fn info(&self, full: bool) -> Info {
1023        let mut info = Info {
1024            count: self.count,
1025            len: self.len(),
1026            // The terminal cursor reports zero here rather than the null
1027            // `ARNEXT` gives, which is Redis's choice and not ours.
1028            next_insert: self.next_index().unwrap_or(0),
1029            slices: self.slices.len() as u64,
1030            directory_size: self.slices.capacity() as u64,
1031            slice_size: SLICE_SIZE,
1032            ..Info::default()
1033        };
1034        if !full {
1035            return info;
1036        }
1037        let (mut window, mut filled, mut room) = (0u64, 0u64, 0u64);
1038        for (_, slice) in &self.slices {
1039            match &slice.layout {
1040                Layout::Dense { words, .. } => {
1041                    info.dense_slices += 1;
1042                    window += words.len() as u64;
1043                    filled += u64::from(slice.count);
1044                }
1045                Layout::Sparse { offs, .. } => {
1046                    info.sparse_slices += 1;
1047                    room += offs.capacity() as u64;
1048                }
1049            }
1050        }
1051        let ratio = |a: u64, b: u64| if b == 0 { 0.0 } else { a as f64 / b as f64 };
1052        info.avg_dense_size = ratio(window, info.dense_slices);
1053        info.avg_dense_fill = ratio(filled, window);
1054        info.avg_sparse_size = ratio(room, info.sparse_slices);
1055        info
1056    }
1057
1058    /// What the array is holding on the heap, for `MEMORY USAGE`.
1059    #[must_use]
1060    pub fn memory_bytes(&self) -> usize {
1061        self.slices.capacity() * size_of::<(u64, Slice)>()
1062            + self
1063                .slices
1064                .iter()
1065                .map(|(_, s)| s.memory_bytes())
1066                .sum::<usize>()
1067            + self.blob.capacity()
1068    }
1069
1070    /// Write this array out in a form a device can hold.
1071    ///
1072    /// The directory goes out as it stands, slice by slice and word by word,
1073    /// rather than as the index and value pairs a client would see. Rebuilding
1074    /// from pairs would go through [`Array::set`], and the layout a slice ends up
1075    /// in depends on the order it was written in as well as on what is in it, so
1076    /// a slice that had been filled and partly emptied would come back sparse
1077    /// where it went out dense. `ARINFO` reports that split, so an array whose
1078    /// layout changed because it was quiet long enough to be demoted would be an
1079    /// array whose answers depend on memory pressure.
1080    ///
1081    /// The blob is written live bytes only, in the order the words are walked in,
1082    /// so a demotion is also a compaction and the dead space does not reach the
1083    /// device. It goes in front of the directory because a word carries where its
1084    /// value starts, and reading the blob first is what lets every one of those
1085    /// be checked as it arrives rather than in a second pass.
1086    pub fn freeze(&self, out: &mut Vec<u8>) {
1087        out.push(match self.insert {
1088            Some(_) => FORM_SLICES | HAS_INSERT,
1089            None => FORM_SLICES,
1090        });
1091        if let Some(at) = self.insert {
1092            frozen::put_uint(out, at);
1093        }
1094        frozen::put_uint(out, self.count);
1095
1096        // The live length is known without a walk, which is the same subtraction
1097        // `compact` sizes its fresh blob with.
1098        frozen::put_uint(out, (self.blob.len() - self.dead) as u64);
1099        for (_, slice) in &self.slices {
1100            for w in slice.words() {
1101                if !w.is_empty() && w.tag() == TAG_BLOB {
1102                    let (start, len) = w.blob_span();
1103                    out.extend_from_slice(&self.blob[start..start + len]);
1104                }
1105            }
1106        }
1107
1108        frozen::put_uint(out, self.slices.len() as u64);
1109        // The same walk again, in the same order, so a value's new start is the
1110        // running total of what went before it and no table has to be kept.
1111        let mut at = 0usize;
1112        for (id, slice) in &self.slices {
1113            frozen::put_uint(out, *id);
1114            match &slice.layout {
1115                Layout::Sparse { offs, words } => {
1116                    out.push(LAYOUT_SPARSE);
1117                    frozen::put_uint(out, words.len() as u64);
1118                    for (&off, &w) in offs.iter().zip(words) {
1119                        frozen::put_uint(out, u64::from(off));
1120                        frozen::put_uint(out, moved(w, &mut at));
1121                    }
1122                }
1123                Layout::Dense { offset, words } => {
1124                    out.push(LAYOUT_DENSE);
1125                    frozen::put_uint(out, u64::from(*offset));
1126                    frozen::put_uint(out, words.len() as u64);
1127                    for &w in words {
1128                        frozen::put_uint(out, moved(w, &mut at));
1129                    }
1130                }
1131            }
1132        }
1133    }
1134
1135    /// Read back what [`Array::freeze`] wrote.
1136    ///
1137    /// Everything the rest of this file takes for granted is checked here, since
1138    /// this is the one way a directory arrives without having been built by
1139    /// [`Array::set`]: offsets inside a slice go up and stay under
1140    /// [`SLICE_SIZE`], a sparse slice holds no holes, a dense window has a
1141    /// populated word at each end, the slice ids go up, the counts add up to the
1142    /// array's own, and every value in the blob is pointed at by exactly one
1143    /// word. A body that fails any of them is an error, because the alternative
1144    /// is an `ARGET` that reads off the end of the blob.
1145    pub fn thaw(bytes: &[u8]) -> core::result::Result<Array, Broken> {
1146        let mut cut = frozen::Cut::new(bytes);
1147        let tag = cut.byte()?;
1148        if tag & !HAS_INSERT != FORM_SLICES {
1149            return Err(Broken::Form);
1150        }
1151        let insert = if tag & HAS_INSERT != 0 {
1152            let at = cut.uint()?;
1153            if at > INDEX_MAX {
1154                return Err(Broken::Body);
1155            }
1156            Some(at)
1157        } else {
1158            None
1159        };
1160        let count = cut.uint()?;
1161        let blob = cut.bytes()?.to_vec();
1162
1163        let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
1164        // A slice costs an id, a layout byte and a length, so a count larger
1165        // than what is left cannot be honest and is not worth an allocation.
1166        if n > cut.rest().len() {
1167            return Err(Broken::Body);
1168        }
1169        let mut slices: Vec<(u64, Slice)> = Vec::with_capacity(n);
1170        let mut used = 0usize;
1171        let mut seen = 0u64;
1172        for _ in 0..n {
1173            let id = cut.uint()?;
1174            if id > INDEX_MAX >> SLICE_BITS {
1175                return Err(Broken::Body);
1176            }
1177            if slices.last().is_some_and(|(last, _)| id <= *last) {
1178                return Err(Broken::Body);
1179            }
1180            let slice = read_slice(&mut cut, blob.len(), &mut used)?;
1181            seen += u64::from(slice.count);
1182            slices.push((id, slice));
1183        }
1184        if seen != count || used != blob.len() {
1185            return Err(Broken::Body);
1186        }
1187        Ok(Array {
1188            slices,
1189            blob,
1190            dead: 0,
1191            count,
1192            insert,
1193        })
1194    }
1195
1196    /// Which entry holds slice `id`, or where it would be inserted.
1197    fn find(&self, id: u64) -> core::result::Result<usize, usize> {
1198        self.slices.binary_search_by(|(have, _)| {
1199            if *have < id {
1200                Ordering::Less
1201            } else if *have > id {
1202                Ordering::Greater
1203            } else {
1204                Ordering::Equal
1205            }
1206        })
1207    }
1208
1209    /// Clears `from..=to` inside one slice, and answers how many went.
1210    ///
1211    /// The dead blob bytes are counted up here and added to the array's total
1212    /// afterwards, rather than collected into a list of words and retired one by
1213    /// one, because the list would be an allocation on the delete path and the
1214    /// only thing a retired word has left to say is how many bytes it held.
1215    fn clear_within(&mut self, at: usize, from: u16, to: u16) -> u64 {
1216        let slice = &mut self.slices[at].1;
1217        let mut gone = 0u64;
1218        let mut dead = 0usize;
1219        match &mut slice.layout {
1220            Layout::Sparse { offs, words } => {
1221                let lo = offs.partition_point(|&o| o < from);
1222                let hi = offs.partition_point(|&o| o <= to);
1223                // Every word a sparse slice holds is populated, so the range is
1224                // the count.
1225                for w in &words[lo..hi] {
1226                    gone += 1;
1227                    if w.tag() == TAG_BLOB {
1228                        dead += w.blob_span().1;
1229                    }
1230                }
1231                offs.drain(lo..hi);
1232                words.drain(lo..hi);
1233            }
1234            Layout::Dense { offset, words } => {
1235                let base = *offset;
1236                let lo = usize::from(from.saturating_sub(base));
1237                if to >= base && lo < words.len() {
1238                    let hi = usize::from(to - base).min(words.len() - 1);
1239                    for w in &mut words[lo..=hi] {
1240                        if !w.is_empty() {
1241                            gone += 1;
1242                            if w.tag() == TAG_BLOB {
1243                                dead += w.blob_span().1;
1244                            }
1245                            *w = Word::EMPTY;
1246                        }
1247                    }
1248                }
1249            }
1250        }
1251        slice.count -= gone as u16;
1252        slice.trim();
1253        self.dead += dead;
1254        gone
1255    }
1256
1257    /// Gives back whatever blob space a word was using.
1258    fn retire(&mut self, w: Word) {
1259        if !w.is_empty() && w.tag() == TAG_BLOB {
1260            self.dead += w.blob_span().1;
1261        }
1262    }
1263
1264    /// Turns bytes into the smallest word that holds them.
1265    fn encode(&mut self, val: &[u8]) -> Result<Word> {
1266        if let Some(i) = num::parse_i64(val)
1267            && (INT_LO..=INT_HI).contains(&i)
1268        {
1269            return Ok(Word::from_int(i));
1270        }
1271        if let Some(w) = float_word(val) {
1272            return Ok(w);
1273        }
1274        if val.len() <= INLINE_MAX {
1275            return Ok(Word::from_short(val));
1276        }
1277        if val.len() > VALUE_MAX {
1278            return Err(Error::new(Code::Full, VALUE_TOO_LONG));
1279        }
1280        if self.blob.len() + val.len() > BLOB_MAX {
1281            self.compact();
1282        }
1283        if self.blob.len() + val.len() > BLOB_MAX {
1284            return Err(Error::new(Code::Full, BLOB_TOO_LONG));
1285        }
1286        let start = self.blob.len();
1287        self.blob.extend_from_slice(val);
1288        Ok(Word::from_blob(start, val.len()))
1289    }
1290
1291    fn decode(&self, w: Word) -> Option<Element<'_>> {
1292        if w.is_empty() {
1293            return None;
1294        }
1295        Some(match w.tag() {
1296            TAG_INT => Element::Int(w.to_int()),
1297            TAG_FLOAT => Element::Float(w.to_float()),
1298            TAG_STR => Element::Short(w.to_short()),
1299            _ => {
1300                let (start, len) = w.blob_span();
1301                Element::Str(&self.blob[start..start + len])
1302            }
1303        })
1304    }
1305
1306    /// Rewrites the blob with the dead bytes gone, and points every word at
1307    /// where its value landed.
1308    fn compact(&mut self) {
1309        let mut fresh = Vec::with_capacity(self.blob.len() - self.dead);
1310        for (_, slice) in &mut self.slices {
1311            for w in slice.words_mut() {
1312                if w.is_empty() || w.tag() != TAG_BLOB {
1313                    continue;
1314                }
1315                let (start, len) = w.blob_span();
1316                let to = fresh.len();
1317                fresh.extend_from_slice(&self.blob[start..start + len]);
1318                *w = Word::from_blob(to, len);
1319            }
1320        }
1321        self.blob = fresh;
1322        self.dead = 0;
1323    }
1324
1325    fn maybe_compact(&mut self) {
1326        if self.dead >= COMPACT_MIN && self.dead * 2 >= self.blob.len() {
1327            self.compact();
1328        }
1329    }
1330
1331    /// Gives back the directory space a mass delete freed.
1332    ///
1333    /// `Vec::remove` leaves the capacity behind, and a key that had a million
1334    /// slices and now has one should not still be holding sixteen megabytes of
1335    /// directory.
1336    fn maybe_compact_slices(&mut self) {
1337        if self.slices.capacity() > 16 && self.slices.capacity() > self.slices.len() * 4 {
1338            self.slices.shrink_to_fit();
1339        }
1340    }
1341}
1342
1343/// What `ARINFO` reports, which is the shape of the array and not its contents.
1344///
1345/// Three of these describe our directory rather than Redis's, which is D-20:
1346/// the slice count and the two directory numbers are about a sorted vector of
1347/// slices where Redis has a growable table and, past a point, a second level
1348/// above it. Everything else means the same thing in both.
1349#[derive(Debug, Default, Clone, Copy)]
1350pub struct Info {
1351    /// How many indices hold something.
1352    pub count: u64,
1353    /// The highest populated index plus one.
1354    pub len: u64,
1355    /// Where the next append would go, and zero when there is nowhere.
1356    pub next_insert: u64,
1357    /// How many slices the array is made of.
1358    pub slices: u64,
1359    /// How many slots the directory has room for.
1360    pub directory_size: u64,
1361    /// How many indices one slice covers.
1362    pub slice_size: u64,
1363    /// How many slices are holding a window of consecutive positions.
1364    pub dense_slices: u64,
1365    /// How many are holding offsets and words in parallel.
1366    pub sparse_slices: u64,
1367    /// The mean width of a dense window, in positions.
1368    pub avg_dense_size: f64,
1369    /// How much of that width is populated, between zero and one.
1370    pub avg_dense_fill: f64,
1371    /// The mean number of entries a sparse slice has room for.
1372    pub avg_sparse_size: f64,
1373}
1374
1375/// The message when one key's long values pass four gigabytes in total.
1376pub const BLOB_TOO_LONG: &str = "array values exceed the four gigabyte per key limit";
1377
1378/// The message when one value on its own passes a gigabyte.
1379pub const VALUE_TOO_LONG: &str = "array value exceeds the one gigabyte limit";
1380
1381/// What `ARINSERT` says when the cursor has nowhere left to go.
1382///
1383/// Redis's words. This is not the same error `ARSET` gives for the same
1384/// underlying problem, which is Redis's doing and worth keeping: one of them is
1385/// about an index the client named and the other is about a cursor it did not.
1386pub const INSERT_OVERFLOW: &str = "insert index overflow";
1387
1388/// A word as it should be written, with a blob value pointed at where it is
1389/// about to land rather than where it used to be.
1390///
1391/// `at` is the running length of the frozen blob, so this is the compaction
1392/// `Array::compact` does, run against a buffer that has already been written
1393/// instead of against a fresh `Vec`.
1394fn moved(w: Word, at: &mut usize) -> u64 {
1395    if w.is_empty() || w.tag() != TAG_BLOB {
1396        return w.0;
1397    }
1398    let (_, len) = w.blob_span();
1399    let start = *at;
1400    *at += len;
1401    Word::from_blob(start, len).0
1402}
1403
1404/// A word read back, checked against the blob it may be pointing into.
1405///
1406/// `used` counts the blob bytes claimed so far, which the caller compares with
1407/// the blob's length at the end. Two words pointing at the same bytes, or a
1408/// value nothing points at, both fail that comparison.
1409fn read_word(
1410    cut: &mut frozen::Cut<'_>,
1411    blob: usize,
1412    used: &mut usize,
1413) -> core::result::Result<Word, Broken> {
1414    let w = Word(cut.uint()?);
1415    if !w.is_empty() && w.tag() == TAG_BLOB {
1416        let (start, len) = w.blob_span();
1417        // A blob word is only written for a value of `INLINE_MAX` and up, so a
1418        // shorter one is a word that was not written by `freeze`.
1419        if len <= INLINE_MAX || start + len > blob {
1420            return Err(Broken::Body);
1421        }
1422        *used += len;
1423    }
1424    Ok(w)
1425}
1426
1427/// One slice read back, in whichever layout it says it is in.
1428fn read_slice(
1429    cut: &mut frozen::Cut<'_>,
1430    blob: usize,
1431    used: &mut usize,
1432) -> core::result::Result<Slice, Broken> {
1433    match cut.byte()? {
1434        LAYOUT_SPARSE => {
1435            let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
1436            // An offset and a word are a byte each at the very least, and a
1437            // slice with nothing in it is dropped rather than kept.
1438            if n == 0 || n > cut.rest().len() {
1439                return Err(Broken::Body);
1440            }
1441            let mut offs: Vec<u16> = Vec::with_capacity(n);
1442            let mut words = Vec::with_capacity(n);
1443            for _ in 0..n {
1444                let off = u16::try_from(cut.uint()?).map_err(|_| Broken::Body)?;
1445                if u64::from(off) >= SLICE_SIZE {
1446                    return Err(Broken::Body);
1447                }
1448                if offs.last().is_some_and(|last| off <= *last) {
1449                    return Err(Broken::Body);
1450                }
1451                let w = read_word(cut, blob, used)?;
1452                // Sparse holds what is there and nothing else, so an empty word
1453                // in here would make `count` and the entries disagree.
1454                if w.is_empty() {
1455                    return Err(Broken::Body);
1456                }
1457                offs.push(off);
1458                words.push(w);
1459            }
1460            Ok(Slice {
1461                count: n as u16,
1462                layout: Layout::Sparse { offs, words },
1463            })
1464        }
1465        LAYOUT_DENSE => {
1466            let offset = u16::try_from(cut.uint()?).map_err(|_| Broken::Body)?;
1467            let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
1468            if n == 0 || n > cut.rest().len() {
1469                return Err(Broken::Body);
1470            }
1471            if u64::from(offset) + n as u64 > SLICE_SIZE {
1472                return Err(Broken::Body);
1473            }
1474            let mut words = Vec::with_capacity(n);
1475            let mut live = 0u16;
1476            for _ in 0..n {
1477                let w = read_word(cut, blob, used)?;
1478                if !w.is_empty() {
1479                    live += 1;
1480                }
1481                words.push(w);
1482            }
1483            // Trimmed at both ends, which is what makes `Slice::high` derivable
1484            // rather than stored.
1485            if words[0].is_empty() || words[n - 1].is_empty() {
1486                return Err(Broken::Body);
1487            }
1488            Ok(Slice {
1489                count: live,
1490                layout: Layout::Dense { offset, words },
1491            })
1492        }
1493        _ => Err(Broken::Form),
1494    }
1495}
1496
1497/// Splits an index into the slice that holds it and the offset inside.
1498#[inline]
1499const fn split(idx: u64) -> (u64, u16) {
1500    (idx >> SLICE_BITS, (idx & (SLICE_SIZE - 1)) as u16)
1501}
1502
1503/// Whether these bytes round trip exactly through an inline double.
1504///
1505/// This is `arTryEncodeFloat`, and the round trip is the point rather than an
1506/// optimisation. `3.140` parses to the same double as `3.14` and prints back as
1507/// `3.14`, so storing it as a number would change what the client wrote. Only a
1508/// value that prints back byte for byte is allowed to become a number, and
1509/// everything else stays a string.
1510fn float_word(val: &[u8]) -> Option<Word> {
1511    // The cheap filter first: optional minus, then digits with exactly one dot.
1512    // Nothing else can survive the round trip, and this skips the parse for the
1513    // overwhelming majority of values that are not numbers at all.
1514    let body = match val.first() {
1515        Some(b'-') if val.len() > 1 => &val[1..],
1516        Some(_) => val,
1517        None => return None,
1518    };
1519    let mut dots = 0;
1520    for &c in body {
1521        match c {
1522            b'.' => dots += 1,
1523            b'0'..=b'9' => {}
1524            _ => return None,
1525        }
1526    }
1527    if dots != 1 {
1528        return None;
1529    }
1530
1531    let d = num::parse_f64(val)?;
1532    if !d.is_finite() {
1533        return None;
1534    }
1535    // The low two bits of the payload are the tag, so the value that gets stored
1536    // is the input with those cleared, and it is that value that has to print
1537    // back to the input. Most decimals do not survive that, which is the design
1538    // working: `3.14` loses three units in the last place and prints back as
1539    // something else, so it stays a string.
1540    let trunc = f64::from_bits(d.to_bits() & !TAG_MASK);
1541    let mut buf = [0u8; ELEMENT_MAX];
1542    let el = Element::Float(trunc);
1543    if el.text(&mut buf) == val {
1544        Some(Word::from_float_bits(trunc.to_bits()))
1545    } else {
1546        None
1547    }
1548}
1549
1550#[cfg(test)]
1551mod tests {
1552    use super::*;
1553
1554    /// The bytes a client would see for whatever is at `idx`.
1555    fn read(a: &Array, idx: u64) -> Option<Vec<u8>> {
1556        let el = a.get(idx)?;
1557        let mut buf = [0u8; ELEMENT_MAX];
1558        Some(el.text(&mut buf).to_vec())
1559    }
1560
1561    fn set(a: &mut Array, idx: u64, val: &[u8]) -> bool {
1562        a.set(idx, val).expect("a value that fits")
1563    }
1564
1565    /// Everything the scan finds, as index and bytes.
1566    fn scan(a: &Array, start: u64, end: u64, limit: usize) -> Vec<(u64, Vec<u8>)> {
1567        let mut got = Vec::new();
1568        a.scan(start, end, |i, el| {
1569            let mut buf = [0u8; ELEMENT_MAX];
1570            got.push((i, el.text(&mut buf).to_vec()));
1571            got.len() < limit
1572        });
1573        got
1574    }
1575
1576    /// What `ARLASTITEMS` would reply, holes included.
1577    fn last(a: &Array, count: u64, newest_first: bool) -> Vec<Option<Vec<u8>>> {
1578        let mut got = Vec::new();
1579        let n = a.last_items(count, newest_first, |el| {
1580            got.push(el.map(|e| {
1581                let mut buf = [0u8; ELEMENT_MAX];
1582                e.text(&mut buf).to_vec()
1583            }));
1584        });
1585        assert_eq!(n as usize, got.len(), "the count is what it emitted");
1586        got
1587    }
1588
1589    fn append(a: &mut Array, vals: &[&[u8]]) -> Result<u64> {
1590        a.append(vals.iter().copied())
1591    }
1592
1593    fn ring(a: &mut Array, size: u64, vals: &[&[u8]]) -> u64 {
1594        a.ring(size, vals.iter().copied()).expect("values that fit")
1595    }
1596
1597    #[test]
1598    fn a_value_comes_back_the_way_it_went_in() {
1599        let mut a = Array::new();
1600        assert!(set(&mut a, 0, b"hello"));
1601        assert!(set(&mut a, 1, b"a much longer value than fits in a word"));
1602        assert!(set(&mut a, 2, b"42"));
1603        assert!(set(&mut a, 3, b"1.5"));
1604        assert!(set(&mut a, 4, b""));
1605
1606        assert_eq!(read(&a, 0).as_deref(), Some(&b"hello"[..]));
1607        assert_eq!(
1608            read(&a, 1).as_deref(),
1609            Some(&b"a much longer value than fits in a word"[..])
1610        );
1611        assert_eq!(read(&a, 2).as_deref(), Some(&b"42"[..]));
1612        assert_eq!(read(&a, 3).as_deref(), Some(&b"1.5"[..]));
1613        assert_eq!(read(&a, 4).as_deref(), Some(&b""[..]));
1614        assert_eq!(read(&a, 5), None);
1615    }
1616
1617    /// The length and the count are different numbers, and this is the test
1618    /// that says so.
1619    #[test]
1620    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
1621        let mut a = Array::new();
1622        assert_eq!(a.len(), 0);
1623        assert_eq!(a.count(), 0);
1624        assert!(a.is_empty());
1625
1626        set(&mut a, 1_000_000, b"x");
1627        assert_eq!(a.len(), 1_000_001);
1628        assert_eq!(a.count(), 1);
1629        assert!(!a.is_empty());
1630
1631        set(&mut a, 5, b"y");
1632        assert_eq!(a.len(), 1_000_001, "a lower index does not move the length");
1633        assert_eq!(a.count(), 2);
1634
1635        a.del(1_000_000);
1636        assert_eq!(a.len(), 6, "and the length comes back down when it goes");
1637        assert_eq!(a.count(), 1);
1638    }
1639
1640    /// Writing over a position is not a new position.
1641    #[test]
1642    fn an_overwrite_does_not_count_as_a_fill() {
1643        let mut a = Array::new();
1644        assert!(set(&mut a, 7, b"first"));
1645        assert!(!set(&mut a, 7, b"second"));
1646        assert_eq!(a.count(), 1);
1647        assert_eq!(read(&a, 7).as_deref(), Some(&b"second"[..]));
1648    }
1649
1650    #[test]
1651    fn deleting_the_last_element_leaves_nothing_behind() {
1652        let mut a = Array::new();
1653        set(&mut a, 3, b"x");
1654        assert!(a.del(3));
1655        assert!(!a.del(3), "and a second delete finds nothing");
1656        assert!(a.is_empty());
1657        assert_eq!(a.len(), 0);
1658        assert!(a.slices.is_empty(), "the slice went with the last element");
1659    }
1660
1661    /// The whole 64 bit index space, not just the part a `Vec` could index.
1662    #[test]
1663    fn the_index_space_runs_to_the_top() {
1664        let mut a = Array::new();
1665        set(&mut a, 0, b"low");
1666        set(&mut a, INDEX_MAX, b"high");
1667        assert_eq!(read(&a, INDEX_MAX).as_deref(), Some(&b"high"[..]));
1668        assert_eq!(a.count(), 2);
1669        assert_eq!(a.len(), u64::MAX, "the highest index plus one");
1670        // And it is two slices, not four thousand billion of them.
1671        assert_eq!(a.slices.len(), 2);
1672    }
1673
1674    /// A slice earns a dense window by being full enough to want one, and gives
1675    /// it back when it is not.
1676    #[test]
1677    fn a_slice_changes_layout_when_the_shape_of_it_changes() {
1678        let mut a = Array::new();
1679        for i in 0..SPARSE_MAX as u64 {
1680            set(&mut a, i, b"x");
1681        }
1682        assert!(
1683            matches!(a.slices[0].1.layout, Layout::Sparse { .. }),
1684            "ten scattered elements do not want an index"
1685        );
1686
1687        set(&mut a, 10, b"x");
1688        assert!(
1689            matches!(a.slices[0].1.layout, Layout::Dense { .. }),
1690            "eleven consecutive ones do"
1691        );
1692
1693        // Spread the same elements out and the window stops being worth it.
1694        for i in 0..8 {
1695            a.del(i);
1696        }
1697        assert!(
1698            matches!(a.slices[0].1.layout, Layout::Sparse { .. }),
1699            "three left is under the floor"
1700        );
1701        assert_eq!(a.count(), 3);
1702        assert_eq!(read(&a, 10).as_deref(), Some(&b"x"[..]));
1703    }
1704
1705    /// Eleven elements spread across a slice stay sparse, which is where this
1706    /// parts company with Redis.
1707    #[test]
1708    fn a_wide_slice_stays_sparse_however_many_elements_it_has() {
1709        let mut a = Array::new();
1710        for i in 0..40 {
1711            set(&mut a, i * 100, b"x");
1712        }
1713        assert!(
1714            matches!(a.slices[0].1.layout, Layout::Sparse { .. }),
1715            "forty elements over four thousand positions is not a window"
1716        );
1717        for i in 0..40 {
1718            assert_eq!(read(&a, i * 100).as_deref(), Some(&b"x"[..]), "at {i}");
1719        }
1720    }
1721
1722    /// A dense window is filled from the top down as well as the bottom up.
1723    #[test]
1724    fn a_dense_window_grows_downwards_too() {
1725        let mut a = Array::new();
1726        for i in (0..20u64).rev() {
1727            set(&mut a, i, b"v");
1728        }
1729        assert!(matches!(a.slices[0].1.layout, Layout::Dense { .. }));
1730        for i in 0..20 {
1731            assert_eq!(read(&a, i).as_deref(), Some(&b"v"[..]), "at {i}");
1732        }
1733        assert_eq!(a.count(), 20);
1734        assert_eq!(a.len(), 20);
1735    }
1736
1737    #[test]
1738    fn a_range_delete_costs_what_it_touches_and_not_what_it_spans() {
1739        let mut a = Array::new();
1740        set(&mut a, 1, b"a");
1741        set(&mut a, 500_000, b"b");
1742        set(&mut a, INDEX_MAX, b"c");
1743
1744        // The widest range there is, against three elements.
1745        assert_eq!(a.delete_range(0, INDEX_MAX), 3);
1746        assert!(a.is_empty());
1747        assert!(a.slices.is_empty());
1748        assert_eq!(a.delete_range(0, INDEX_MAX), 0, "and again finds nothing");
1749    }
1750
1751    #[test]
1752    fn a_range_delete_takes_the_ends_and_leaves_the_rest() {
1753        let mut a = Array::new();
1754        for i in 0..30_000u64 {
1755            set(&mut a, i, b"x");
1756        }
1757        assert_eq!(a.delete_range(100, 29_899), 29_800);
1758        assert_eq!(a.count(), 200);
1759        assert_eq!(read(&a, 99).as_deref(), Some(&b"x"[..]));
1760        assert_eq!(read(&a, 100), None);
1761        assert_eq!(read(&a, 29_899), None);
1762        assert_eq!(read(&a, 29_900).as_deref(), Some(&b"x"[..]));
1763        assert_eq!(a.len(), 30_000);
1764    }
1765
1766    #[test]
1767    fn a_backwards_range_deletes_nothing() {
1768        let mut a = Array::new();
1769        set(&mut a, 5, b"x");
1770        assert_eq!(a.delete_range(9, 4), 0);
1771        assert_eq!(a.count(), 1);
1772    }
1773
1774    /// A value that is an integer is held as one, and one that is not is not.
1775    ///
1776    /// This is the compatibility requirement rather than an implementation
1777    /// detail: `007` is not the number seven, because it does not print back as
1778    /// `007`, and an implementation that normalised it would hand a client
1779    /// different bytes than it was given.
1780    #[test]
1781    fn only_a_value_that_prints_back_the_same_becomes_a_number() {
1782        let cases: &[(&[u8], bool)] = &[
1783            (b"0", true),
1784            (b"42", true),
1785            (b"-42", true),
1786            (b"9007199254740993", true),
1787            (b"007", false),
1788            (b"+7", false),
1789            (b"-0", false),
1790            (b" 7", false),
1791            (b"7 ", false),
1792            (b"", false),
1793        ];
1794        for &(val, want) in cases {
1795            let mut a = Array::new();
1796            set(&mut a, 0, val);
1797            let is_int = matches!(a.get(0), Some(Element::Int(_)));
1798            assert_eq!(is_int, want, "{}", String::from_utf8_lossy(val));
1799            assert_eq!(read(&a, 0).as_deref(), Some(val), "round trip");
1800        }
1801    }
1802
1803    /// The same rule for the doubles, and it rejects most of them.
1804    #[test]
1805    fn only_a_double_that_prints_back_the_same_is_stored_as_one() {
1806        let cases: &[(&[u8], bool)] = &[
1807            (b"1.0", true),
1808            (b"1.5", true),
1809            (b"-2.25", true),
1810            (b"0.0", true),
1811            // Three units in the last place go missing when the tag bits are
1812            // cleared, so this one prints back as something else.
1813            (b"3.14", false),
1814            (b"1.10", false),
1815            // Negative zero keeps its sign through the printer, so `-0` comes
1816            // back and `arFormatFloat` puts the `.0` on the end of it.
1817            (b"-0.0", true),
1818            (b"1.", false),
1819            (b".5", false),
1820            (b"1e5", false),
1821            (b"nan", false),
1822            (b"inf", false),
1823        ];
1824        for &(val, want) in cases {
1825            let mut a = Array::new();
1826            set(&mut a, 0, val);
1827            let is_float = matches!(a.get(0), Some(Element::Float(_)));
1828            assert_eq!(is_float, want, "{}", String::from_utf8_lossy(val));
1829            assert_eq!(read(&a, 0).as_deref(), Some(val), "round trip");
1830        }
1831    }
1832
1833    /// Long values live in one blob, and the blob gets rewritten when enough of
1834    /// it is dead.
1835    #[test]
1836    fn the_blob_is_compacted_once_enough_of_it_is_dead() {
1837        let mut a = Array::new();
1838        let long = vec![b'a'; 64];
1839        for i in 0..1000 {
1840            set(&mut a, i, &long);
1841        }
1842        let full = a.blob.len();
1843        assert_eq!(full, 64_000);
1844
1845        // Overwrite every one of them with a value that does not need the blob.
1846        for i in 0..1000 {
1847            set(&mut a, i, b"short");
1848        }
1849        assert!(a.blob.len() < full / 2, "{} bytes left", a.blob.len());
1850        assert_eq!(a.count(), 1000);
1851        for i in 0..1000 {
1852            assert_eq!(read(&a, i).as_deref(), Some(&b"short"[..]), "at {i}");
1853        }
1854    }
1855
1856    /// Compaction moves the live bytes, so every word that pointed into the
1857    /// blob has to be moved with them.
1858    #[test]
1859    fn compaction_keeps_the_values_that_survive_it() {
1860        let mut a = Array::new();
1861        for i in 0..2000u64 {
1862            let val = format!("value number {i} padded out past the inline limit");
1863            set(&mut a, i, val.as_bytes());
1864        }
1865        // Kill the even ones, which is enough dead bytes to trigger a rewrite.
1866        for i in (0..2000u64).step_by(2) {
1867            a.del(i);
1868        }
1869        assert!(a.dead * 2 < a.blob.len(), "the blob was rewritten");
1870        for i in (1..2000u64).step_by(2) {
1871            let want = format!("value number {i} padded out past the inline limit");
1872            assert_eq!(read(&a, i).as_deref(), Some(want.as_bytes()), "at {i}");
1873        }
1874    }
1875
1876    #[test]
1877    fn a_value_over_the_ceiling_is_an_error_and_not_a_panic() {
1878        let mut a = Array::new();
1879        let huge = vec![b'x'; VALUE_MAX + 1];
1880        let e = a.set(0, &huge).unwrap_err();
1881        assert_eq!(e.code(), Code::Full);
1882        assert_eq!(e.message(), VALUE_TOO_LONG);
1883        assert!(a.is_empty(), "and nothing was written");
1884    }
1885
1886    /// Every word encoding, through the eight bytes and back.
1887    #[test]
1888    fn a_word_holds_what_it_was_given() {
1889        assert!(Word::EMPTY.is_empty());
1890        for i in [0i64, 1, -1, INT_LO, INT_HI, 12345, -99999] {
1891            let w = Word::from_int(i);
1892            assert!(!w.is_empty());
1893            assert_eq!(w.tag(), TAG_INT);
1894            assert_eq!(w.to_int(), i, "{i}");
1895        }
1896        for d in [0.0f64, 1.5, -2.25, 1e300] {
1897            let bits = d.to_bits() & !TAG_MASK;
1898            let w = Word::from_float_bits(bits);
1899            assert!(!w.is_empty());
1900            assert_eq!(w.tag(), TAG_FLOAT);
1901            assert_eq!(w.to_float().to_bits(), bits);
1902        }
1903        for s in [&b""[..], b"a", b"abc", b"1234567"] {
1904            let w = Word::from_short(s);
1905            assert!(!w.is_empty(), "{s:?}");
1906            assert_eq!(w.tag(), TAG_STR);
1907            assert_eq!(w.to_short().as_bytes(), s);
1908        }
1909        let w = Word::from_blob(4_000_000_000, 1_000_000);
1910        assert_eq!(w.tag(), TAG_BLOB);
1911        assert_eq!(w.blob_span(), (4_000_000_000, 1_000_000));
1912        assert!(!w.is_empty());
1913    }
1914
1915    #[test]
1916    fn what_it_holds_is_what_it_says_it_holds() {
1917        let mut a = Array::new();
1918        assert_eq!(a.memory_bytes(), 0);
1919        for i in 0..1000u64 {
1920            set(&mut a, i * 7, b"a value past the inline limit");
1921        }
1922        let held = a.memory_bytes();
1923        assert!(held > 29_000, "{held} bytes for 29 kilobytes of values");
1924        a.delete_range(0, u64::MAX - 1);
1925        assert!(
1926            a.memory_bytes() < held / 2,
1927            "{} bytes left of {held}",
1928            a.memory_bytes()
1929        );
1930    }
1931
1932    /// A thousand writes in a random order against a plain map, to catch the
1933    /// promotion, demotion, window and blob paths interacting.
1934    #[test]
1935    fn it_agrees_with_a_map_over_a_scramble_of_writes() {
1936        use std::collections::BTreeMap;
1937
1938        let mut a = Array::new();
1939        let mut want: BTreeMap<u64, Vec<u8>> = BTreeMap::new();
1940        let mut seed = 0x9e37_79b9_7f4a_7c15u64;
1941        let mut next = || {
1942            seed ^= seed << 13;
1943            seed ^= seed >> 7;
1944            seed ^= seed << 17;
1945            seed
1946        };
1947
1948        for step in 0..20_000u64 {
1949            let idx = next() % 20_000;
1950            match step % 5 {
1951                0..=2 => {
1952                    let val = format!("v{step}");
1953                    let was_new = set(&mut a, idx, val.as_bytes());
1954                    assert_eq!(was_new, want.insert(idx, val.into_bytes()).is_none());
1955                }
1956                3 => {
1957                    assert_eq!(a.del(idx), want.remove(&idx).is_some());
1958                }
1959                _ => {
1960                    let hi = idx + (next() % 500);
1961                    let gone = a.delete_range(idx, hi);
1962                    let keys: Vec<u64> = want.range(idx..=hi).map(|(k, _)| *k).collect();
1963                    assert_eq!(gone, keys.len() as u64);
1964                    for k in keys {
1965                        want.remove(&k);
1966                    }
1967                }
1968            }
1969            assert_eq!(a.count(), want.len() as u64, "count after step {step}");
1970        }
1971
1972        assert_eq!(
1973            a.len(),
1974            want.keys().next_back().map_or(0, |k| k + 1),
1975            "the high water mark"
1976        );
1977        for (&idx, val) in &want {
1978            assert_eq!(read(&a, idx).as_deref(), Some(&val[..]), "at {idx}");
1979        }
1980    }
1981
1982    #[test]
1983    fn a_scan_finds_the_elements_and_steps_over_the_holes() {
1984        let mut a = Array::new();
1985        set(&mut a, 0, b"a");
1986        set(&mut a, 5, b"b");
1987        // Three slices apart, so this also proves the walk moves between them.
1988        set(&mut a, SLICE_SIZE * 2 + 7, b"c");
1989
1990        let all = vec![
1991            (0, b"a".to_vec()),
1992            (5, b"b".to_vec()),
1993            (SLICE_SIZE * 2 + 7, b"c".to_vec()),
1994        ];
1995        // The whole index space costs three visits and not eighteen quintillion,
1996        // which is why this one needs no cap where ARGETRANGE does.
1997        assert_eq!(scan(&a, 0, INDEX_MAX, usize::MAX), all);
1998        let mut backwards = all.clone();
1999        backwards.reverse();
2000        assert_eq!(scan(&a, INDEX_MAX, 0, usize::MAX), backwards);
2001
2002        // A window inside one slice, a window that lands on nothing, and a
2003        // limit that stops the walk early.
2004        assert_eq!(scan(&a, 1, 5, usize::MAX), all[1..2].to_vec());
2005        assert_eq!(scan(&a, 6, SLICE_SIZE, usize::MAX), Vec::new());
2006        assert_eq!(scan(&a, 0, INDEX_MAX, 2), all[..2].to_vec());
2007        assert_eq!(scan(&Array::new(), 0, INDEX_MAX, usize::MAX), Vec::new());
2008    }
2009
2010    /// A dense slice has holes inside its window and a sparse one does not, so
2011    /// the walk has to be right in both layouts.
2012    #[test]
2013    fn a_scan_reads_both_layouts_the_same_way() {
2014        let mut a = Array::new();
2015        for i in 0..40u64 {
2016            set(&mut a, i, format!("v{i}").as_bytes());
2017        }
2018        for i in (0..40u64).step_by(2) {
2019            a.del(i);
2020        }
2021        let odd: Vec<(u64, Vec<u8>)> = (1..40u64)
2022            .step_by(2)
2023            .map(|i| (i, format!("v{i}").into_bytes()))
2024            .collect();
2025        assert_eq!(scan(&a, 0, 100, usize::MAX), odd);
2026
2027        // Now the same twenty elements spread far enough apart that the slice
2028        // has to be sparse, and the answer is the same shape.
2029        let mut b = Array::new();
2030        for i in (1..40u64).step_by(2) {
2031            set(&mut b, i, format!("v{i}").as_bytes());
2032        }
2033        assert_eq!(scan(&b, 0, 100, usize::MAX), odd);
2034    }
2035
2036    #[test]
2037    fn the_cursor_moves_only_when_something_appends_to_it() {
2038        let mut a = Array::new();
2039        assert_eq!(a.next_index(), Some(0));
2040        // A plain write does not move it, which is why the first append lands on
2041        // top of what ARSET put at zero.
2042        set(&mut a, 0, b"set");
2043        assert_eq!(a.next_index(), Some(0));
2044        assert_eq!(append(&mut a, &[b"x", b"y"]).expect("room"), 1);
2045        assert_eq!(read(&a, 0).as_deref(), Some(&b"x"[..]));
2046        assert_eq!(a.next_index(), Some(2));
2047
2048        // A seek says where the next append goes, not where the cursor is.
2049        a.seek(100);
2050        assert_eq!(a.next_index(), Some(100));
2051        assert_eq!(append(&mut a, &[b"z"]).expect("room"), 100);
2052        assert_eq!(read(&a, 100).as_deref(), Some(&b"z"[..]));
2053        a.seek(0);
2054        assert_eq!(a.next_index(), Some(0));
2055    }
2056
2057    #[test]
2058    fn an_append_that_would_run_off_the_top_writes_nothing() {
2059        let mut a = Array::new();
2060        a.seek(INDEX_MAX - 1);
2061        let e = append(&mut a, &[b"x", b"y", b"z"]).unwrap_err();
2062        assert_eq!(e.code(), Code::Invalid);
2063        assert_eq!(e.message(), INSERT_OVERFLOW);
2064        assert_eq!(a.count(), 0, "and none of the batch landed");
2065
2066        // The last index is reachable, and the cursor is finished afterwards.
2067        assert_eq!(append(&mut a, &[b"x", b"y"]).expect("room"), INDEX_MAX);
2068        assert_eq!(a.next_index(), None);
2069        assert_eq!(
2070            append(&mut a, &[b"z"]).unwrap_err().message(),
2071            INSERT_OVERFLOW
2072        );
2073    }
2074
2075    #[test]
2076    fn a_ring_wraps_round_at_its_size() {
2077        let mut a = Array::new();
2078        assert_eq!(ring(&mut a, 3, &[b"a", b"b", b"c"]), 2);
2079        assert_eq!(ring(&mut a, 3, &[b"d", b"e"]), 1);
2080        assert_eq!(a.len(), 3, "it never grows past the size it was given");
2081        assert_eq!(a.count(), 3);
2082        assert_eq!(read(&a, 0).as_deref(), Some(&b"d"[..]));
2083        assert_eq!(read(&a, 1).as_deref(), Some(&b"e"[..]));
2084        assert_eq!(read(&a, 2).as_deref(), Some(&b"c"[..]));
2085    }
2086
2087    /// A ring that changes size keeps the newest run and renumbers it, so that
2088    /// reading it back in index order is still reading it in the order it
2089    /// arrived.
2090    #[test]
2091    fn a_ring_that_changes_size_is_renumbered_oldest_first() {
2092        let mut a = Array::new();
2093        ring(&mut a, 3, &[b"a", b"b", b"c", b"d", b"e"]);
2094        // Holding d e c at 0 1 2, so the newest three in order are c d e.
2095        assert_eq!(ring(&mut a, 5, &[b"f"]), 3);
2096        assert_eq!(
2097            (0..4).map(|i| read(&a, i)).collect::<Vec<_>>(),
2098            vec![
2099                Some(b"c".to_vec()),
2100                Some(b"d".to_vec()),
2101                Some(b"e".to_vec()),
2102                Some(b"f".to_vec())
2103            ]
2104        );
2105
2106        // And shrinking drops the oldest of them.
2107        let mut b = Array::new();
2108        ring(&mut b, 3, &[b"a", b"b", b"c", b"d", b"e"]);
2109        assert_eq!(ring(&mut b, 2, &[b"f"]), 0);
2110        assert_eq!(b.count(), 2);
2111        assert_eq!(read(&b, 0).as_deref(), Some(&b"f"[..]));
2112        assert_eq!(read(&b, 1).as_deref(), Some(&b"e"[..]));
2113    }
2114
2115    /// The rebuild stops at the first hole, so a ring somebody has deleted out
2116    /// of keeps its newest unbroken run rather than a scattering.
2117    #[test]
2118    fn a_hole_cuts_what_a_resize_keeps() {
2119        let mut a = Array::new();
2120        ring(&mut a, 4, &[b"a", b"b", b"c", b"d", b"e"]);
2121        // Holding e b c d with the cursor on 0, and now c is gone.
2122        a.del(2);
2123        assert_eq!(ring(&mut a, 8, &[b"f"]), 2);
2124        // The walk back from e reached the hole where c was, so b did not
2125        // survive it and d and e did.
2126        assert_eq!(
2127            (0..3).map(|i| read(&a, i)).collect::<Vec<_>>(),
2128            vec![
2129                Some(b"d".to_vec()),
2130                Some(b"e".to_vec()),
2131                Some(b"f".to_vec())
2132            ]
2133        );
2134    }
2135
2136    #[test]
2137    fn the_last_items_walk_wraps_and_reports_the_holes() {
2138        let mut a = Array::new();
2139        ring(&mut a, 4, &[b"a", b"b", b"c", b"d", b"e"]);
2140        // Holding e b c d, with the cursor on 0, so the newest is e and the
2141        // walk has to wrap to find the three before it.
2142        assert_eq!(
2143            last(&a, 3, false),
2144            vec![
2145                Some(b"c".to_vec()),
2146                Some(b"d".to_vec()),
2147                Some(b"e".to_vec())
2148            ]
2149        );
2150        assert_eq!(
2151            last(&a, 3, true),
2152            vec![
2153                Some(b"e".to_vec()),
2154                Some(b"d".to_vec()),
2155                Some(b"c".to_vec())
2156            ]
2157        );
2158        // More than there is gets everything and no more.
2159        assert_eq!(last(&a, 99, false).len(), 4);
2160        assert_eq!(last(&a, 0, false), Vec::new());
2161        assert_eq!(last(&Array::new(), 5, false), Vec::new());
2162
2163        // With no cursor the tail of the array is the anchor, and a position
2164        // inside the window that holds nothing is reported as a hole.
2165        let mut b = Array::new();
2166        set(&mut b, 0, b"a");
2167        set(&mut b, 2, b"c");
2168        assert_eq!(last(&b, 5, false), vec![None, Some(b"c".to_vec())]);
2169    }
2170
2171    /// The cursor is part of the value, so a copy of an array is a copy of
2172    /// where it was up to.
2173    #[test]
2174    fn a_copy_of_an_array_remembers_the_cursor() {
2175        let mut a = Array::new();
2176        append(&mut a, &[b"x", b"y"]).expect("room");
2177        let mut b = a.clone();
2178        assert_eq!(b.next_index(), Some(2));
2179        assert_eq!(append(&mut b, &[b"z"]).expect("room"), 2);
2180        assert_eq!(a.next_index(), Some(2), "and the two do not share it");
2181    }
2182
2183    /// Freeze an array, read it back, and check that nothing about it moved.
2184    fn round_trip(a: &Array) -> Array {
2185        let mut buf = Vec::new();
2186        a.freeze(&mut buf);
2187        let back = Array::thaw(&buf).expect("what freeze wrote");
2188        assert_eq!(back.count(), a.count(), "the population");
2189        assert_eq!(back.len(), a.len(), "the high water mark");
2190        assert_eq!(back.next_index(), a.next_index(), "the insert cursor");
2191        assert_eq!(back.slices.len(), a.slices.len(), "the slice count");
2192        for ((id, was), (back_id, now)) in a.slices.iter().zip(&back.slices) {
2193            assert_eq!(id, back_id, "the slice ids");
2194            assert_eq!(was.count, now.count, "slice {id} holds the same number");
2195            assert_eq!(
2196                matches!(was.layout, Layout::Dense { .. }),
2197                matches!(now.layout, Layout::Dense { .. }),
2198                "slice {id} came back in the layout it left in"
2199            );
2200        }
2201        assert_eq!(
2202            scan(&back, 0, u64::MAX, usize::MAX),
2203            scan(a, 0, u64::MAX, usize::MAX)
2204        );
2205        back
2206    }
2207
2208    #[test]
2209    fn a_frozen_array_comes_back_with_every_value_it_held() {
2210        let mut a = Array::new();
2211        // One of each of the four things a word can be, and a long value that
2212        // has to live in the blob.
2213        set(&mut a, 0, b"12345");
2214        set(&mut a, 1, b"1.5");
2215        set(&mut a, 2, b"short");
2216        set(
2217            &mut a,
2218            3,
2219            b"a value well past the seven bytes a word can inline",
2220        );
2221        set(&mut a, 9_000_000_000_000, b"a long way up the index space");
2222        let back = round_trip(&a);
2223        assert_eq!(read(&back, 0).as_deref(), Some(&b"12345"[..]));
2224        assert_eq!(read(&back, 1).as_deref(), Some(&b"1.5"[..]));
2225        assert_eq!(read(&back, 2).as_deref(), Some(&b"short"[..]));
2226        assert_eq!(
2227            read(&back, 3).as_deref(),
2228            Some(&b"a value well past the seven bytes a word can inline"[..])
2229        );
2230        assert_eq!(
2231            read(&back, 9_000_000_000_000).as_deref(),
2232            Some(&b"a long way up the index space"[..])
2233        );
2234        assert_eq!(read(&back, 4), None, "and a hole is still a hole");
2235        assert_eq!(back.get(0), Some(Element::Int(12345)), "still an integer");
2236        assert_eq!(back.get(1), Some(Element::Float(1.5)), "still a double");
2237
2238        round_trip(&Array::new());
2239    }
2240
2241    #[test]
2242    fn both_layouts_come_back_in_the_layout_they_left_in() {
2243        // Dense, which is eleven consecutive positions.
2244        let mut dense = Array::new();
2245        for i in 0..=SPARSE_MAX as u64 {
2246            set(&mut dense, i, b"x");
2247        }
2248        assert!(matches!(dense.slices[0].1.layout, Layout::Dense { .. }));
2249        round_trip(&dense);
2250
2251        // Dense with holes punched in the middle, which is the case a rebuild
2252        // through `set` would have brought back sparse.
2253        let mut holed = dense.clone();
2254        for i in 2..5 {
2255            holed.del(i);
2256        }
2257        assert!(matches!(holed.slices[0].1.layout, Layout::Dense { .. }));
2258        assert_eq!(holed.count(), 8);
2259        let back = round_trip(&holed);
2260        assert_eq!(read(&back, 1).as_deref(), Some(&b"x"[..]));
2261        assert_eq!(read(&back, 4), None);
2262
2263        // Sparse, which is elements too far apart to be worth a window.
2264        let mut sparse = Array::new();
2265        for i in 0..40 {
2266            set(&mut sparse, i * 100, b"x");
2267        }
2268        assert!(matches!(sparse.slices[0].1.layout, Layout::Sparse { .. }));
2269        round_trip(&sparse);
2270    }
2271
2272    #[test]
2273    fn freezing_an_array_leaves_the_dead_blob_bytes_behind() {
2274        let mut a = Array::new();
2275        let long = vec![b'v'; 200];
2276        // Written and overwritten enough times that most of the blob is dead,
2277        // and under the floor that would have compacted it in place.
2278        for _ in 0..8 {
2279            set(&mut a, 0, &long);
2280        }
2281        assert!(a.dead > 0, "there is dead space to leave behind");
2282        let mut buf = Vec::new();
2283        a.freeze(&mut buf);
2284        let back = Array::thaw(&buf).expect("what freeze wrote");
2285        assert_eq!(back.dead, 0, "a demotion is a compaction");
2286        assert_eq!(back.blob.len(), a.blob.len() - a.dead);
2287        assert_eq!(read(&back, 0).as_deref(), Some(&long[..]));
2288        assert!(
2289            buf.len() < a.blob.len(),
2290            "and the dead bytes never went out"
2291        );
2292    }
2293
2294    #[test]
2295    fn a_frozen_array_keeps_the_insert_cursor() {
2296        let mut a = Array::new();
2297        append(&mut a, &[b"x", b"y", b"z"]).expect("room");
2298        let mut back = round_trip(&a);
2299        assert_eq!(back.next_index(), Some(3));
2300        assert_eq!(append(&mut back, &[b"w"]).expect("room"), 3);
2301
2302        // And an array that nothing has appended to comes back without one, so
2303        // its first append still lands at zero.
2304        let mut untouched = Array::new();
2305        set(&mut untouched, 99, b"x");
2306        let mut back = round_trip(&untouched);
2307        assert_eq!(back.next_index(), Some(0), "a cursor nothing has moved");
2308        assert_eq!(append(&mut back, &[b"first"]).expect("room"), 0);
2309    }
2310
2311    #[test]
2312    fn a_frozen_array_that_arrives_damaged_is_an_error_and_not_a_panic() {
2313        let mut a = Array::new();
2314        for i in 0..200u64 {
2315            set(
2316                &mut a,
2317                i * 7,
2318                format!("value:{i:04} and enough bytes to reach the blob").as_bytes(),
2319            );
2320        }
2321        let mut buf = Vec::new();
2322        a.freeze(&mut buf);
2323        assert!(Array::thaw(&buf).is_ok(), "the body it wrote reads back");
2324
2325        assert!(Array::thaw(&[]).is_err(), "nothing at all");
2326        assert!(Array::thaw(&[99]).is_err(), "a form nobody wrote");
2327        for cut in 1..buf.len().min(96) {
2328            assert!(Array::thaw(&buf[..cut]).is_err(), "cut at {cut}");
2329        }
2330        // Every single byte flipped in the header and the first slice, which is
2331        // where a length, a layout byte and a word all live.
2332        for at in 0..buf.len().min(96) {
2333            for bit in 0..8 {
2334                let mut bad = buf.clone();
2335                bad[at] ^= 1 << bit;
2336                // Whatever it decides, it decides without reading off the end of
2337                // the blob and without a subtraction going backwards.
2338                let _ = Array::thaw(&bad);
2339            }
2340        }
2341    }
2342}