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    use crate::many;
1554
1555    /// The bytes a client would see for whatever is at `idx`.
1556    fn read(a: &Array, idx: u64) -> Option<Vec<u8>> {
1557        let el = a.get(idx)?;
1558        let mut buf = [0u8; ELEMENT_MAX];
1559        Some(el.text(&mut buf).to_vec())
1560    }
1561
1562    fn set(a: &mut Array, idx: u64, val: &[u8]) -> bool {
1563        a.set(idx, val).expect("a value that fits")
1564    }
1565
1566    /// Everything the scan finds, as index and bytes.
1567    fn scan(a: &Array, start: u64, end: u64, limit: usize) -> Vec<(u64, Vec<u8>)> {
1568        let mut got = Vec::new();
1569        a.scan(start, end, |i, el| {
1570            let mut buf = [0u8; ELEMENT_MAX];
1571            got.push((i, el.text(&mut buf).to_vec()));
1572            got.len() < limit
1573        });
1574        got
1575    }
1576
1577    /// What `ARLASTITEMS` would reply, holes included.
1578    fn last(a: &Array, count: u64, newest_first: bool) -> Vec<Option<Vec<u8>>> {
1579        let mut got = Vec::new();
1580        let n = a.last_items(count, newest_first, |el| {
1581            got.push(el.map(|e| {
1582                let mut buf = [0u8; ELEMENT_MAX];
1583                e.text(&mut buf).to_vec()
1584            }));
1585        });
1586        assert_eq!(n as usize, got.len(), "the count is what it emitted");
1587        got
1588    }
1589
1590    fn append(a: &mut Array, vals: &[&[u8]]) -> Result<u64> {
1591        a.append(vals.iter().copied())
1592    }
1593
1594    fn ring(a: &mut Array, size: u64, vals: &[&[u8]]) -> u64 {
1595        a.ring(size, vals.iter().copied()).expect("values that fit")
1596    }
1597
1598    #[test]
1599    fn a_value_comes_back_the_way_it_went_in() {
1600        let mut a = Array::new();
1601        assert!(set(&mut a, 0, b"hello"));
1602        assert!(set(&mut a, 1, b"a much longer value than fits in a word"));
1603        assert!(set(&mut a, 2, b"42"));
1604        assert!(set(&mut a, 3, b"1.5"));
1605        assert!(set(&mut a, 4, b""));
1606
1607        assert_eq!(read(&a, 0).as_deref(), Some(&b"hello"[..]));
1608        assert_eq!(
1609            read(&a, 1).as_deref(),
1610            Some(&b"a much longer value than fits in a word"[..])
1611        );
1612        assert_eq!(read(&a, 2).as_deref(), Some(&b"42"[..]));
1613        assert_eq!(read(&a, 3).as_deref(), Some(&b"1.5"[..]));
1614        assert_eq!(read(&a, 4).as_deref(), Some(&b""[..]));
1615        assert_eq!(read(&a, 5), None);
1616    }
1617
1618    /// The length and the count are different numbers, and this is the test
1619    /// that says so.
1620    #[test]
1621    fn the_length_is_the_high_water_mark_and_the_count_is_the_population() {
1622        let mut a = Array::new();
1623        assert_eq!(a.len(), 0);
1624        assert_eq!(a.count(), 0);
1625        assert!(a.is_empty());
1626
1627        set(&mut a, 1_000_000, b"x");
1628        assert_eq!(a.len(), 1_000_001);
1629        assert_eq!(a.count(), 1);
1630        assert!(!a.is_empty());
1631
1632        set(&mut a, 5, b"y");
1633        assert_eq!(a.len(), 1_000_001, "a lower index does not move the length");
1634        assert_eq!(a.count(), 2);
1635
1636        a.del(1_000_000);
1637        assert_eq!(a.len(), 6, "and the length comes back down when it goes");
1638        assert_eq!(a.count(), 1);
1639    }
1640
1641    /// Writing over a position is not a new position.
1642    #[test]
1643    fn an_overwrite_does_not_count_as_a_fill() {
1644        let mut a = Array::new();
1645        assert!(set(&mut a, 7, b"first"));
1646        assert!(!set(&mut a, 7, b"second"));
1647        assert_eq!(a.count(), 1);
1648        assert_eq!(read(&a, 7).as_deref(), Some(&b"second"[..]));
1649    }
1650
1651    #[test]
1652    fn deleting_the_last_element_leaves_nothing_behind() {
1653        let mut a = Array::new();
1654        set(&mut a, 3, b"x");
1655        assert!(a.del(3));
1656        assert!(!a.del(3), "and a second delete finds nothing");
1657        assert!(a.is_empty());
1658        assert_eq!(a.len(), 0);
1659        assert!(a.slices.is_empty(), "the slice went with the last element");
1660    }
1661
1662    /// The whole 64 bit index space, not just the part a `Vec` could index.
1663    #[test]
1664    fn the_index_space_runs_to_the_top() {
1665        let mut a = Array::new();
1666        set(&mut a, 0, b"low");
1667        set(&mut a, INDEX_MAX, b"high");
1668        assert_eq!(read(&a, INDEX_MAX).as_deref(), Some(&b"high"[..]));
1669        assert_eq!(a.count(), 2);
1670        assert_eq!(a.len(), u64::MAX, "the highest index plus one");
1671        // And it is two slices, not four thousand billion of them.
1672        assert_eq!(a.slices.len(), 2);
1673    }
1674
1675    /// A slice earns a dense window by being full enough to want one, and gives
1676    /// it back when it is not.
1677    #[test]
1678    fn a_slice_changes_layout_when_the_shape_of_it_changes() {
1679        let mut a = Array::new();
1680        for i in 0..SPARSE_MAX as u64 {
1681            set(&mut a, i, b"x");
1682        }
1683        assert!(
1684            matches!(a.slices[0].1.layout, Layout::Sparse { .. }),
1685            "ten scattered elements do not want an index"
1686        );
1687
1688        set(&mut a, 10, b"x");
1689        assert!(
1690            matches!(a.slices[0].1.layout, Layout::Dense { .. }),
1691            "eleven consecutive ones do"
1692        );
1693
1694        // Spread the same elements out and the window stops being worth it.
1695        for i in 0..8 {
1696            a.del(i);
1697        }
1698        assert!(
1699            matches!(a.slices[0].1.layout, Layout::Sparse { .. }),
1700            "three left is under the floor"
1701        );
1702        assert_eq!(a.count(), 3);
1703        assert_eq!(read(&a, 10).as_deref(), Some(&b"x"[..]));
1704    }
1705
1706    /// Eleven elements spread across a slice stay sparse, which is where this
1707    /// parts company with Redis.
1708    #[test]
1709    fn a_wide_slice_stays_sparse_however_many_elements_it_has() {
1710        let mut a = Array::new();
1711        for i in 0..40 {
1712            set(&mut a, i * 100, b"x");
1713        }
1714        assert!(
1715            matches!(a.slices[0].1.layout, Layout::Sparse { .. }),
1716            "forty elements over four thousand positions is not a window"
1717        );
1718        for i in 0..40 {
1719            assert_eq!(read(&a, i * 100).as_deref(), Some(&b"x"[..]), "at {i}");
1720        }
1721    }
1722
1723    /// A dense window is filled from the top down as well as the bottom up.
1724    #[test]
1725    fn a_dense_window_grows_downwards_too() {
1726        let mut a = Array::new();
1727        for i in (0..20u64).rev() {
1728            set(&mut a, i, b"v");
1729        }
1730        assert!(matches!(a.slices[0].1.layout, Layout::Dense { .. }));
1731        for i in 0..20 {
1732            assert_eq!(read(&a, i).as_deref(), Some(&b"v"[..]), "at {i}");
1733        }
1734        assert_eq!(a.count(), 20);
1735        assert_eq!(a.len(), 20);
1736    }
1737
1738    #[test]
1739    fn a_range_delete_costs_what_it_touches_and_not_what_it_spans() {
1740        let mut a = Array::new();
1741        set(&mut a, 1, b"a");
1742        set(&mut a, 500_000, b"b");
1743        set(&mut a, INDEX_MAX, b"c");
1744
1745        // The widest range there is, against three elements.
1746        assert_eq!(a.delete_range(0, INDEX_MAX), 3);
1747        assert!(a.is_empty());
1748        assert!(a.slices.is_empty());
1749        assert_eq!(a.delete_range(0, INDEX_MAX), 0, "and again finds nothing");
1750    }
1751
1752    #[test]
1753    fn a_range_delete_takes_the_ends_and_leaves_the_rest() {
1754        // A hundred left at each end whatever the length is, because the ends
1755        // are what this is about and the middle is only there to be long.
1756        let n = many(30_000u64);
1757        let mut a = Array::new();
1758        for i in 0..n {
1759            set(&mut a, i, b"x");
1760        }
1761        assert_eq!(a.delete_range(100, n - 101), n - 200);
1762        assert_eq!(a.count(), 200);
1763        assert_eq!(read(&a, 99).as_deref(), Some(&b"x"[..]));
1764        assert_eq!(read(&a, 100), None);
1765        assert_eq!(read(&a, n - 101), None);
1766        assert_eq!(read(&a, n - 100).as_deref(), Some(&b"x"[..]));
1767        assert_eq!(a.len(), n);
1768    }
1769
1770    #[test]
1771    fn a_backwards_range_deletes_nothing() {
1772        let mut a = Array::new();
1773        set(&mut a, 5, b"x");
1774        assert_eq!(a.delete_range(9, 4), 0);
1775        assert_eq!(a.count(), 1);
1776    }
1777
1778    /// A value that is an integer is held as one, and one that is not is not.
1779    ///
1780    /// This is the compatibility requirement rather than an implementation
1781    /// detail: `007` is not the number seven, because it does not print back as
1782    /// `007`, and an implementation that normalised it would hand a client
1783    /// different bytes than it was given.
1784    #[test]
1785    fn only_a_value_that_prints_back_the_same_becomes_a_number() {
1786        let cases: &[(&[u8], bool)] = &[
1787            (b"0", true),
1788            (b"42", true),
1789            (b"-42", true),
1790            (b"9007199254740993", true),
1791            (b"007", false),
1792            (b"+7", false),
1793            (b"-0", false),
1794            (b" 7", false),
1795            (b"7 ", false),
1796            (b"", false),
1797        ];
1798        for &(val, want) in cases {
1799            let mut a = Array::new();
1800            set(&mut a, 0, val);
1801            let is_int = matches!(a.get(0), Some(Element::Int(_)));
1802            assert_eq!(is_int, want, "{}", String::from_utf8_lossy(val));
1803            assert_eq!(read(&a, 0).as_deref(), Some(val), "round trip");
1804        }
1805    }
1806
1807    /// The same rule for the doubles, and it rejects most of them.
1808    #[test]
1809    fn only_a_double_that_prints_back_the_same_is_stored_as_one() {
1810        let cases: &[(&[u8], bool)] = &[
1811            (b"1.0", true),
1812            (b"1.5", true),
1813            (b"-2.25", true),
1814            (b"0.0", true),
1815            // Three units in the last place go missing when the tag bits are
1816            // cleared, so this one prints back as something else.
1817            (b"3.14", false),
1818            (b"1.10", false),
1819            // Negative zero keeps its sign through the printer, so `-0` comes
1820            // back and `arFormatFloat` puts the `.0` on the end of it.
1821            (b"-0.0", true),
1822            (b"1.", false),
1823            (b".5", false),
1824            (b"1e5", false),
1825            (b"nan", false),
1826            (b"inf", false),
1827        ];
1828        for &(val, want) in cases {
1829            let mut a = Array::new();
1830            set(&mut a, 0, val);
1831            let is_float = matches!(a.get(0), Some(Element::Float(_)));
1832            assert_eq!(is_float, want, "{}", String::from_utf8_lossy(val));
1833            assert_eq!(read(&a, 0).as_deref(), Some(val), "round trip");
1834        }
1835    }
1836
1837    /// Long values live in one blob, and the blob gets rewritten when enough of
1838    /// it is dead.
1839    #[test]
1840    fn the_blob_is_compacted_once_enough_of_it_is_dead() {
1841        let mut a = Array::new();
1842        let long = vec![b'a'; 64];
1843        for i in 0..1000 {
1844            set(&mut a, i, &long);
1845        }
1846        let full = a.blob.len();
1847        assert_eq!(full, 64_000);
1848
1849        // Overwrite every one of them with a value that does not need the blob.
1850        for i in 0..1000 {
1851            set(&mut a, i, b"short");
1852        }
1853        assert!(a.blob.len() < full / 2, "{} bytes left", a.blob.len());
1854        assert_eq!(a.count(), 1000);
1855        for i in 0..1000 {
1856            assert_eq!(read(&a, i).as_deref(), Some(&b"short"[..]), "at {i}");
1857        }
1858    }
1859
1860    /// Compaction moves the live bytes, so every word that pointed into the
1861    /// blob has to be moved with them.
1862    #[test]
1863    fn compaction_keeps_the_values_that_survive_it() {
1864        let n = many(2000u64);
1865        let mut a = Array::new();
1866        for i in 0..n {
1867            let val = format!("value number {i} padded out past the inline limit");
1868            set(&mut a, i, val.as_bytes());
1869        }
1870        // Kill the even ones, which is enough dead bytes to trigger a rewrite.
1871        // Half of them at fifty bytes each is still tens of kilobytes when this
1872        // runs small, so the rewrite happens there too and the assert below is
1873        // what says so rather than anything here.
1874        for i in (0..n).step_by(2) {
1875            a.del(i);
1876        }
1877        assert!(a.dead * 2 < a.blob.len(), "the blob was rewritten");
1878        for i in (1..n).step_by(2) {
1879            let want = format!("value number {i} padded out past the inline limit");
1880            assert_eq!(read(&a, i).as_deref(), Some(want.as_bytes()), "at {i}");
1881        }
1882    }
1883
1884    #[test]
1885    fn a_value_over_the_ceiling_is_an_error_and_not_a_panic() {
1886        let mut a = Array::new();
1887        let huge = vec![b'x'; VALUE_MAX + 1];
1888        let e = a.set(0, &huge).unwrap_err();
1889        assert_eq!(e.code(), Code::Full);
1890        assert_eq!(e.message(), VALUE_TOO_LONG);
1891        assert!(a.is_empty(), "and nothing was written");
1892    }
1893
1894    /// Every word encoding, through the eight bytes and back.
1895    #[test]
1896    fn a_word_holds_what_it_was_given() {
1897        assert!(Word::EMPTY.is_empty());
1898        for i in [0i64, 1, -1, INT_LO, INT_HI, 12345, -99999] {
1899            let w = Word::from_int(i);
1900            assert!(!w.is_empty());
1901            assert_eq!(w.tag(), TAG_INT);
1902            assert_eq!(w.to_int(), i, "{i}");
1903        }
1904        for d in [0.0f64, 1.5, -2.25, 1e300] {
1905            let bits = d.to_bits() & !TAG_MASK;
1906            let w = Word::from_float_bits(bits);
1907            assert!(!w.is_empty());
1908            assert_eq!(w.tag(), TAG_FLOAT);
1909            assert_eq!(w.to_float().to_bits(), bits);
1910        }
1911        for s in [&b""[..], b"a", b"abc", b"1234567"] {
1912            let w = Word::from_short(s);
1913            assert!(!w.is_empty(), "{s:?}");
1914            assert_eq!(w.tag(), TAG_STR);
1915            assert_eq!(w.to_short().as_bytes(), s);
1916        }
1917        let w = Word::from_blob(4_000_000_000, 1_000_000);
1918        assert_eq!(w.tag(), TAG_BLOB);
1919        assert_eq!(w.blob_span(), (4_000_000_000, 1_000_000));
1920        assert!(!w.is_empty());
1921    }
1922
1923    #[test]
1924    fn what_it_holds_is_what_it_says_it_holds() {
1925        let mut a = Array::new();
1926        assert_eq!(a.memory_bytes(), 0);
1927        for i in 0..1000u64 {
1928            set(&mut a, i * 7, b"a value past the inline limit");
1929        }
1930        let held = a.memory_bytes();
1931        assert!(held > 29_000, "{held} bytes for 29 kilobytes of values");
1932        a.delete_range(0, u64::MAX - 1);
1933        assert!(
1934            a.memory_bytes() < held / 2,
1935            "{} bytes left of {held}",
1936            a.memory_bytes()
1937        );
1938    }
1939
1940    /// A long run of writes in a random order against a plain map, to catch the
1941    /// promotion, demotion, window and blob paths interacting.
1942    #[test]
1943    fn it_agrees_with_a_map_over_a_scramble_of_writes() {
1944        use std::collections::BTreeMap;
1945
1946        let mut a = Array::new();
1947        let mut want: BTreeMap<u64, Vec<u8>> = BTreeMap::new();
1948        let mut seed = 0x9e37_79b9_7f4a_7c15u64;
1949        let mut next = || {
1950            seed ^= seed << 13;
1951            seed ^= seed >> 7;
1952            seed ^= seed << 17;
1953            seed
1954        };
1955
1956        // Three numbers that have to move together. The index space is the step
1957        // count so that a write lands on an occupied slot about as often as not,
1958        // and the range width is a fortieth of the index space so that a range
1959        // delete takes a handful rather than everything or nothing. Shrinking
1960        // any one of them alone changes which paths this actually reaches.
1961        let steps = many(20_000u64);
1962        let width = many(500u64);
1963        for step in 0..steps {
1964            let idx = next() % steps;
1965            match step % 5 {
1966                0..=2 => {
1967                    let val = format!("v{step}");
1968                    let was_new = set(&mut a, idx, val.as_bytes());
1969                    assert_eq!(was_new, want.insert(idx, val.into_bytes()).is_none());
1970                }
1971                3 => {
1972                    assert_eq!(a.del(idx), want.remove(&idx).is_some());
1973                }
1974                _ => {
1975                    let hi = idx + (next() % width);
1976                    let gone = a.delete_range(idx, hi);
1977                    let keys: Vec<u64> = want.range(idx..=hi).map(|(k, _)| *k).collect();
1978                    assert_eq!(gone, keys.len() as u64);
1979                    for k in keys {
1980                        want.remove(&k);
1981                    }
1982                }
1983            }
1984            assert_eq!(a.count(), want.len() as u64, "count after step {step}");
1985        }
1986
1987        assert_eq!(
1988            a.len(),
1989            want.keys().next_back().map_or(0, |k| k + 1),
1990            "the high water mark"
1991        );
1992        for (&idx, val) in &want {
1993            assert_eq!(read(&a, idx).as_deref(), Some(&val[..]), "at {idx}");
1994        }
1995    }
1996
1997    #[test]
1998    fn a_scan_finds_the_elements_and_steps_over_the_holes() {
1999        let mut a = Array::new();
2000        set(&mut a, 0, b"a");
2001        set(&mut a, 5, b"b");
2002        // Three slices apart, so this also proves the walk moves between them.
2003        set(&mut a, SLICE_SIZE * 2 + 7, b"c");
2004
2005        let all = vec![
2006            (0, b"a".to_vec()),
2007            (5, b"b".to_vec()),
2008            (SLICE_SIZE * 2 + 7, b"c".to_vec()),
2009        ];
2010        // The whole index space costs three visits and not eighteen quintillion,
2011        // which is why this one needs no cap where ARGETRANGE does.
2012        assert_eq!(scan(&a, 0, INDEX_MAX, usize::MAX), all);
2013        let mut backwards = all.clone();
2014        backwards.reverse();
2015        assert_eq!(scan(&a, INDEX_MAX, 0, usize::MAX), backwards);
2016
2017        // A window inside one slice, a window that lands on nothing, and a
2018        // limit that stops the walk early.
2019        assert_eq!(scan(&a, 1, 5, usize::MAX), all[1..2].to_vec());
2020        assert_eq!(scan(&a, 6, SLICE_SIZE, usize::MAX), Vec::new());
2021        assert_eq!(scan(&a, 0, INDEX_MAX, 2), all[..2].to_vec());
2022        assert_eq!(scan(&Array::new(), 0, INDEX_MAX, usize::MAX), Vec::new());
2023    }
2024
2025    /// A dense slice has holes inside its window and a sparse one does not, so
2026    /// the walk has to be right in both layouts.
2027    #[test]
2028    fn a_scan_reads_both_layouts_the_same_way() {
2029        let mut a = Array::new();
2030        for i in 0..40u64 {
2031            set(&mut a, i, format!("v{i}").as_bytes());
2032        }
2033        for i in (0..40u64).step_by(2) {
2034            a.del(i);
2035        }
2036        let odd: Vec<(u64, Vec<u8>)> = (1..40u64)
2037            .step_by(2)
2038            .map(|i| (i, format!("v{i}").into_bytes()))
2039            .collect();
2040        assert_eq!(scan(&a, 0, 100, usize::MAX), odd);
2041
2042        // Now the same twenty elements spread far enough apart that the slice
2043        // has to be sparse, and the answer is the same shape.
2044        let mut b = Array::new();
2045        for i in (1..40u64).step_by(2) {
2046            set(&mut b, i, format!("v{i}").as_bytes());
2047        }
2048        assert_eq!(scan(&b, 0, 100, usize::MAX), odd);
2049    }
2050
2051    #[test]
2052    fn the_cursor_moves_only_when_something_appends_to_it() {
2053        let mut a = Array::new();
2054        assert_eq!(a.next_index(), Some(0));
2055        // A plain write does not move it, which is why the first append lands on
2056        // top of what ARSET put at zero.
2057        set(&mut a, 0, b"set");
2058        assert_eq!(a.next_index(), Some(0));
2059        assert_eq!(append(&mut a, &[b"x", b"y"]).expect("room"), 1);
2060        assert_eq!(read(&a, 0).as_deref(), Some(&b"x"[..]));
2061        assert_eq!(a.next_index(), Some(2));
2062
2063        // A seek says where the next append goes, not where the cursor is.
2064        a.seek(100);
2065        assert_eq!(a.next_index(), Some(100));
2066        assert_eq!(append(&mut a, &[b"z"]).expect("room"), 100);
2067        assert_eq!(read(&a, 100).as_deref(), Some(&b"z"[..]));
2068        a.seek(0);
2069        assert_eq!(a.next_index(), Some(0));
2070    }
2071
2072    #[test]
2073    fn an_append_that_would_run_off_the_top_writes_nothing() {
2074        let mut a = Array::new();
2075        a.seek(INDEX_MAX - 1);
2076        let e = append(&mut a, &[b"x", b"y", b"z"]).unwrap_err();
2077        assert_eq!(e.code(), Code::Invalid);
2078        assert_eq!(e.message(), INSERT_OVERFLOW);
2079        assert_eq!(a.count(), 0, "and none of the batch landed");
2080
2081        // The last index is reachable, and the cursor is finished afterwards.
2082        assert_eq!(append(&mut a, &[b"x", b"y"]).expect("room"), INDEX_MAX);
2083        assert_eq!(a.next_index(), None);
2084        assert_eq!(
2085            append(&mut a, &[b"z"]).unwrap_err().message(),
2086            INSERT_OVERFLOW
2087        );
2088    }
2089
2090    #[test]
2091    fn a_ring_wraps_round_at_its_size() {
2092        let mut a = Array::new();
2093        assert_eq!(ring(&mut a, 3, &[b"a", b"b", b"c"]), 2);
2094        assert_eq!(ring(&mut a, 3, &[b"d", b"e"]), 1);
2095        assert_eq!(a.len(), 3, "it never grows past the size it was given");
2096        assert_eq!(a.count(), 3);
2097        assert_eq!(read(&a, 0).as_deref(), Some(&b"d"[..]));
2098        assert_eq!(read(&a, 1).as_deref(), Some(&b"e"[..]));
2099        assert_eq!(read(&a, 2).as_deref(), Some(&b"c"[..]));
2100    }
2101
2102    /// A ring that changes size keeps the newest run and renumbers it, so that
2103    /// reading it back in index order is still reading it in the order it
2104    /// arrived.
2105    #[test]
2106    fn a_ring_that_changes_size_is_renumbered_oldest_first() {
2107        let mut a = Array::new();
2108        ring(&mut a, 3, &[b"a", b"b", b"c", b"d", b"e"]);
2109        // Holding d e c at 0 1 2, so the newest three in order are c d e.
2110        assert_eq!(ring(&mut a, 5, &[b"f"]), 3);
2111        assert_eq!(
2112            (0..4).map(|i| read(&a, i)).collect::<Vec<_>>(),
2113            vec![
2114                Some(b"c".to_vec()),
2115                Some(b"d".to_vec()),
2116                Some(b"e".to_vec()),
2117                Some(b"f".to_vec())
2118            ]
2119        );
2120
2121        // And shrinking drops the oldest of them.
2122        let mut b = Array::new();
2123        ring(&mut b, 3, &[b"a", b"b", b"c", b"d", b"e"]);
2124        assert_eq!(ring(&mut b, 2, &[b"f"]), 0);
2125        assert_eq!(b.count(), 2);
2126        assert_eq!(read(&b, 0).as_deref(), Some(&b"f"[..]));
2127        assert_eq!(read(&b, 1).as_deref(), Some(&b"e"[..]));
2128    }
2129
2130    /// The rebuild stops at the first hole, so a ring somebody has deleted out
2131    /// of keeps its newest unbroken run rather than a scattering.
2132    #[test]
2133    fn a_hole_cuts_what_a_resize_keeps() {
2134        let mut a = Array::new();
2135        ring(&mut a, 4, &[b"a", b"b", b"c", b"d", b"e"]);
2136        // Holding e b c d with the cursor on 0, and now c is gone.
2137        a.del(2);
2138        assert_eq!(ring(&mut a, 8, &[b"f"]), 2);
2139        // The walk back from e reached the hole where c was, so b did not
2140        // survive it and d and e did.
2141        assert_eq!(
2142            (0..3).map(|i| read(&a, i)).collect::<Vec<_>>(),
2143            vec![
2144                Some(b"d".to_vec()),
2145                Some(b"e".to_vec()),
2146                Some(b"f".to_vec())
2147            ]
2148        );
2149    }
2150
2151    #[test]
2152    fn the_last_items_walk_wraps_and_reports_the_holes() {
2153        let mut a = Array::new();
2154        ring(&mut a, 4, &[b"a", b"b", b"c", b"d", b"e"]);
2155        // Holding e b c d, with the cursor on 0, so the newest is e and the
2156        // walk has to wrap to find the three before it.
2157        assert_eq!(
2158            last(&a, 3, false),
2159            vec![
2160                Some(b"c".to_vec()),
2161                Some(b"d".to_vec()),
2162                Some(b"e".to_vec())
2163            ]
2164        );
2165        assert_eq!(
2166            last(&a, 3, true),
2167            vec![
2168                Some(b"e".to_vec()),
2169                Some(b"d".to_vec()),
2170                Some(b"c".to_vec())
2171            ]
2172        );
2173        // More than there is gets everything and no more.
2174        assert_eq!(last(&a, 99, false).len(), 4);
2175        assert_eq!(last(&a, 0, false), Vec::new());
2176        assert_eq!(last(&Array::new(), 5, false), Vec::new());
2177
2178        // With no cursor the tail of the array is the anchor, and a position
2179        // inside the window that holds nothing is reported as a hole.
2180        let mut b = Array::new();
2181        set(&mut b, 0, b"a");
2182        set(&mut b, 2, b"c");
2183        assert_eq!(last(&b, 5, false), vec![None, Some(b"c".to_vec())]);
2184    }
2185
2186    /// The cursor is part of the value, so a copy of an array is a copy of
2187    /// where it was up to.
2188    #[test]
2189    fn a_copy_of_an_array_remembers_the_cursor() {
2190        let mut a = Array::new();
2191        append(&mut a, &[b"x", b"y"]).expect("room");
2192        let mut b = a.clone();
2193        assert_eq!(b.next_index(), Some(2));
2194        assert_eq!(append(&mut b, &[b"z"]).expect("room"), 2);
2195        assert_eq!(a.next_index(), Some(2), "and the two do not share it");
2196    }
2197
2198    /// Freeze an array, read it back, and check that nothing about it moved.
2199    fn round_trip(a: &Array) -> Array {
2200        let mut buf = Vec::new();
2201        a.freeze(&mut buf);
2202        let back = Array::thaw(&buf).expect("what freeze wrote");
2203        assert_eq!(back.count(), a.count(), "the population");
2204        assert_eq!(back.len(), a.len(), "the high water mark");
2205        assert_eq!(back.next_index(), a.next_index(), "the insert cursor");
2206        assert_eq!(back.slices.len(), a.slices.len(), "the slice count");
2207        for ((id, was), (back_id, now)) in a.slices.iter().zip(&back.slices) {
2208            assert_eq!(id, back_id, "the slice ids");
2209            assert_eq!(was.count, now.count, "slice {id} holds the same number");
2210            assert_eq!(
2211                matches!(was.layout, Layout::Dense { .. }),
2212                matches!(now.layout, Layout::Dense { .. }),
2213                "slice {id} came back in the layout it left in"
2214            );
2215        }
2216        assert_eq!(
2217            scan(&back, 0, u64::MAX, usize::MAX),
2218            scan(a, 0, u64::MAX, usize::MAX)
2219        );
2220        back
2221    }
2222
2223    #[test]
2224    fn a_frozen_array_comes_back_with_every_value_it_held() {
2225        let mut a = Array::new();
2226        // One of each of the four things a word can be, and a long value that
2227        // has to live in the blob.
2228        set(&mut a, 0, b"12345");
2229        set(&mut a, 1, b"1.5");
2230        set(&mut a, 2, b"short");
2231        set(
2232            &mut a,
2233            3,
2234            b"a value well past the seven bytes a word can inline",
2235        );
2236        set(&mut a, 9_000_000_000_000, b"a long way up the index space");
2237        let back = round_trip(&a);
2238        assert_eq!(read(&back, 0).as_deref(), Some(&b"12345"[..]));
2239        assert_eq!(read(&back, 1).as_deref(), Some(&b"1.5"[..]));
2240        assert_eq!(read(&back, 2).as_deref(), Some(&b"short"[..]));
2241        assert_eq!(
2242            read(&back, 3).as_deref(),
2243            Some(&b"a value well past the seven bytes a word can inline"[..])
2244        );
2245        assert_eq!(
2246            read(&back, 9_000_000_000_000).as_deref(),
2247            Some(&b"a long way up the index space"[..])
2248        );
2249        assert_eq!(read(&back, 4), None, "and a hole is still a hole");
2250        assert_eq!(back.get(0), Some(Element::Int(12345)), "still an integer");
2251        assert_eq!(back.get(1), Some(Element::Float(1.5)), "still a double");
2252
2253        round_trip(&Array::new());
2254    }
2255
2256    #[test]
2257    fn both_layouts_come_back_in_the_layout_they_left_in() {
2258        // Dense, which is eleven consecutive positions.
2259        let mut dense = Array::new();
2260        for i in 0..=SPARSE_MAX as u64 {
2261            set(&mut dense, i, b"x");
2262        }
2263        assert!(matches!(dense.slices[0].1.layout, Layout::Dense { .. }));
2264        round_trip(&dense);
2265
2266        // Dense with holes punched in the middle, which is the case a rebuild
2267        // through `set` would have brought back sparse.
2268        let mut holed = dense.clone();
2269        for i in 2..5 {
2270            holed.del(i);
2271        }
2272        assert!(matches!(holed.slices[0].1.layout, Layout::Dense { .. }));
2273        assert_eq!(holed.count(), 8);
2274        let back = round_trip(&holed);
2275        assert_eq!(read(&back, 1).as_deref(), Some(&b"x"[..]));
2276        assert_eq!(read(&back, 4), None);
2277
2278        // Sparse, which is elements too far apart to be worth a window.
2279        let mut sparse = Array::new();
2280        for i in 0..40 {
2281            set(&mut sparse, i * 100, b"x");
2282        }
2283        assert!(matches!(sparse.slices[0].1.layout, Layout::Sparse { .. }));
2284        round_trip(&sparse);
2285    }
2286
2287    #[test]
2288    fn freezing_an_array_leaves_the_dead_blob_bytes_behind() {
2289        let mut a = Array::new();
2290        let long = vec![b'v'; 200];
2291        // Written and overwritten enough times that most of the blob is dead,
2292        // and under the floor that would have compacted it in place.
2293        for _ in 0..8 {
2294            set(&mut a, 0, &long);
2295        }
2296        assert!(a.dead > 0, "there is dead space to leave behind");
2297        let mut buf = Vec::new();
2298        a.freeze(&mut buf);
2299        let back = Array::thaw(&buf).expect("what freeze wrote");
2300        assert_eq!(back.dead, 0, "a demotion is a compaction");
2301        assert_eq!(back.blob.len(), a.blob.len() - a.dead);
2302        assert_eq!(read(&back, 0).as_deref(), Some(&long[..]));
2303        assert!(
2304            buf.len() < a.blob.len(),
2305            "and the dead bytes never went out"
2306        );
2307    }
2308
2309    #[test]
2310    fn a_frozen_array_keeps_the_insert_cursor() {
2311        let mut a = Array::new();
2312        append(&mut a, &[b"x", b"y", b"z"]).expect("room");
2313        let mut back = round_trip(&a);
2314        assert_eq!(back.next_index(), Some(3));
2315        assert_eq!(append(&mut back, &[b"w"]).expect("room"), 3);
2316
2317        // And an array that nothing has appended to comes back without one, so
2318        // its first append still lands at zero.
2319        let mut untouched = Array::new();
2320        set(&mut untouched, 99, b"x");
2321        let mut back = round_trip(&untouched);
2322        assert_eq!(back.next_index(), Some(0), "a cursor nothing has moved");
2323        assert_eq!(append(&mut back, &[b"first"]).expect("room"), 0);
2324    }
2325
2326    #[test]
2327    fn a_frozen_array_that_arrives_damaged_is_an_error_and_not_a_panic() {
2328        // The population is only here to give the freeze several slices and a
2329        // blob to point into. The sweep below is over the first 96 bytes of what
2330        // comes out and does not care how much came after them.
2331        let mut a = Array::new();
2332        for i in 0..many(200u64) {
2333            set(
2334                &mut a,
2335                i * 7,
2336                format!("value:{i:04} and enough bytes to reach the blob").as_bytes(),
2337            );
2338        }
2339        let mut buf = Vec::new();
2340        a.freeze(&mut buf);
2341        assert!(Array::thaw(&buf).is_ok(), "the body it wrote reads back");
2342
2343        assert!(Array::thaw(&[]).is_err(), "nothing at all");
2344        assert!(Array::thaw(&[99]).is_err(), "a form nobody wrote");
2345        for cut in 1..buf.len().min(96) {
2346            assert!(Array::thaw(&buf[..cut]).is_err(), "cut at {cut}");
2347        }
2348        // Every single byte flipped in the header and the first slice, which is
2349        // where a length, a layout byte and a word all live.
2350        for at in 0..buf.len().min(96) {
2351            for bit in 0..8 {
2352                let mut bad = buf.clone();
2353                bad[at] ^= 1 << bit;
2354                // Whatever it decides, it decides without reading off the end of
2355                // the blob and without a subtraction going backwards.
2356                let _ = Array::thaw(&bad);
2357            }
2358        }
2359    }
2360}