Skip to main content

yo_kv/
lists.rs

1//! The list commands.
2//!
3//! One method per Redis command on [`Keyspace`], the same arrangement the set
4//! and hash commands use and for the same reason: a key belongs to the database
5//! and not to a type, so `LPUSH` against a string has to be able to see that it
6//! is a string. The list itself, and the choice between the two representations
7//! it can be in, is [`crate::list`]. This file is what the wire and the embedded
8//! API both call.
9//!
10//! # Indexes
11//!
12//! Every index a client sends is signed and counts from the back when it is
13//! negative, and every range is inclusive at both ends. Both of those are Redis
14//! rules that no structure below here should have to know about, so they are
15//! turned into an offset and a count in `at` and `window` and the list sees
16//! nothing but `usize`.
17//!
18//! # An empty list is not a list
19//!
20//! The key goes when the last element does, whether that was `LPOP`, `LREM` or
21//! `LTRIM`. Redis has the same rule for every collection, and it is the reason
22//! `EXISTS` answers zero after a list is emptied rather than one.
23
24use yo_common::{Code, Error, Result};
25
26use crate::db::Db;
27use crate::keyspace::Keyspace;
28use crate::list::{Element, List};
29use crate::strings;
30use crate::value::{self, Kind};
31
32/// What `LSET` and `LINSERT` say about a key that is not there.
33///
34/// Redis's words, because they go on the wire verbatim.
35const NO_KEY: &str = "no such key";
36
37/// What `LSET` says about an index past the end.
38const OUT_OF_RANGE: &str = "index out of range";
39
40/// What `LPOS` says about a rank of zero.
41///
42/// Read off a running 8.8 rather than written from memory, because the older
43/// wording of this message is still all over the internet and clients match on
44/// the text. Zero is the one rank with no reading: 1 is the first match from
45/// the front, -1 the first from the back, and 0 would have to mean neither.
46const ZERO_RANK: &str = "RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start from the end of the list";
47
48/// Which end a command works from.
49///
50/// `LPUSH` and `RPUSH` are one method, and so are `LPOP` and `RPOP`, because
51/// the difference between each pair is this and nothing else. `LMOVE` needs two
52/// of them and would need two flags whichever way this was written.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum End {
55    /// The head, which is where `LPUSH` puts things and `LPOP` takes them from.
56    Left,
57    /// The tail.
58    Right,
59}
60
61impl End {
62    /// Whether this is the head.
63    #[inline]
64    #[must_use]
65    pub const fn is_left(self) -> bool {
66        matches!(self, End::Left)
67    }
68}
69
70/// Which order `LMOVEM` leaves the elements it moved in.
71///
72/// It only makes a difference when both ends are the same one, which is worth
73/// knowing before reading any further. Taking from the left and putting on the
74/// right hands the block over in the order it was in either way, and so does
75/// taking from the right and putting on the left. It is `LEFT LEFT` and `RIGHT
76/// RIGHT` where the two answers come apart, because those are the cases where
77/// each element lands in front of the one before it.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum Order {
80    /// `OBO`. Exactly as if `LMOVE` had been sent that many times, so a
81    /// destination end that puts each element in front of the last leaves the
82    /// block reversed.
83    OneByOne,
84    /// `BULK`. The block keeps the order it had in the source whatever the two
85    /// ends are.
86    Bulk,
87}
88
89/// Everything `LMOVEM` needs to know beyond the two keys.
90///
91/// The five of them travel together because they are all parsed out of one
92/// command and none of them means anything without the others. Keeping them in
93/// a struct is also what stops the call from being eight positional arguments
94/// where three of them are ends and flags that read the same at the call site.
95///
96/// Named after the command rather than called `Block`, which is what it holds,
97/// because `BLMOVEM` puts one of these inside the structure the blocking layer
98/// already calls a block and one of those two names had to go.
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct Movem {
101    /// The end of the source to take from.
102    pub from: End,
103    /// The end of the destination to put on.
104    pub to: End,
105    /// How many to move. The 5 argument form of the command means one.
106    pub count: usize,
107    /// `EXACTLY` rather than `COUNT`: all of them or none of them.
108    pub exactly: bool,
109    /// Which order they are left in.
110    pub order: Order,
111}
112
113impl Keyspace {
114    /// `LPUSH key element [element ...]` and `RPUSH`. Answers the new length.
115    ///
116    /// The elements arrive as an iterator rather than a slice, the same as
117    /// `SADD`, because the wire layer has them as positions in the connection's
118    /// read buffer and collecting them into a slice would be an allocation on a
119    /// shard thread.
120    ///
121    /// They go in one at a time, so `LPUSH k a b c` leaves the list holding
122    /// `c b a`. That reads like a bug and is not: each element in turn is put
123    /// at the head, and the last one sent ends up in front.
124    pub fn push<'v>(
125        &mut self,
126        key: &[u8],
127        end: End,
128        values: impl Iterator<Item = &'v [u8]> + Clone,
129    ) -> Result<usize> {
130        for v in values.clone() {
131            strings::check_len(key, v.len())?;
132        }
133        let at = match self.list_slot(key)? {
134            Some(at) => at,
135            None => {
136                // `LPUSH k` with no elements does not make a key. The wire
137                // parser rejects it on arity before it gets here, but the
138                // embedded API has no parser in front of it and an empty list
139                // left behind would be a key that exists and holds nothing.
140                if values.clone().next().is_none() {
141                    return Ok(0);
142                }
143                self.new_list(key)
144            }
145        };
146        let limits = self.list_limits;
147        let list = self
148            .lists
149            .get_mut(at)
150            .expect("the record points at its body");
151        for v in values {
152            if end.is_left() {
153                list.push_front(v, &limits);
154            } else {
155                list.push_back(v, &limits);
156            }
157        }
158        Ok(list.len())
159    }
160
161    /// `LPUSHX key element [element ...]` and `RPUSHX`.
162    ///
163    /// The same as [`Keyspace::push`] except that a key which is not there
164    /// stays not there, and the answer is zero.
165    pub fn pushx<'v>(
166        &mut self,
167        key: &[u8],
168        end: End,
169        values: impl Iterator<Item = &'v [u8]> + Clone,
170    ) -> Result<usize> {
171        if self.list_slot(key)?.is_none() {
172            return Ok(0);
173        }
174        self.push(key, end, values)
175    }
176
177    /// `LPOP key` and `RPOP key`, one element.
178    ///
179    /// This allocates, because the element it answers with is the element it
180    /// just took out of the structure that was holding it, the same bind
181    /// `SPOP` is in. [`Keyspace::pop_into`] is the version the wire uses, which
182    /// copies straight into the reply buffer instead.
183    pub fn pop(&mut self, key: &[u8], end: End) -> Result<Option<Vec<u8>>> {
184        let Some(at) = self.list_slot(key)? else {
185            return Ok(None);
186        };
187        let limits = self.list_limits;
188        let list = self
189            .lists
190            .get_mut(at)
191            .expect("the record points at its body");
192        let got = if end.is_left() {
193            list.pop_front(&limits)
194        } else {
195            list.pop_back(&limits)
196        };
197        if list.is_empty() {
198            self.drop_key(key);
199        }
200        Ok(got)
201    }
202
203    /// `LPOP key count` and `RPOP key count`, straight into the reply.
204    ///
205    /// Answers how many were taken. `f` is called with each element in the
206    /// order the reply wants them, which for `LPOP` with a count is head first
207    /// and for `RPOP` is tail first, and it is called before the element is
208    /// dropped so nothing has to be copied to a `Vec` on the way (Y18).
209    ///
210    /// A count larger than the list takes the whole list, and the key goes with
211    /// it.
212    pub fn pop_into<F>(&mut self, key: &[u8], end: End, count: usize, mut f: F) -> Result<usize>
213    where
214        F: FnMut(Element<'_>),
215    {
216        let Some(at) = self.list_slot(key)? else {
217            return Ok(0);
218        };
219        let limits = self.list_limits;
220        let list = self
221            .lists
222            .get_mut(at)
223            .expect("the record points at its body");
224        let take = count.min(list.len());
225        for _ in 0..take {
226            // The element is handed over while it is still in the list and
227            // dropped right after, which is why this is a loop of read then
228            // drop rather than a loop of `pop_front`. A `pop_front` would build
229            // a `Vec` per element for no reason other than to hand it back.
230            let e = if end.is_left() {
231                list.front()
232            } else {
233                list.back()
234            };
235            f(e.expect("a list shorter than it says it is"));
236            if end.is_left() {
237                list.drop_front(&limits);
238            } else {
239                list.drop_back(&limits);
240            }
241        }
242        if list.is_empty() {
243            self.drop_key(key);
244        }
245        Ok(take)
246    }
247
248    /// `LLEN key`. Zero for a key that is not there.
249    pub fn llen(&mut self, key: &[u8]) -> Result<usize> {
250        Ok(match self.list_slot(key)? {
251            Some(at) => self.list_at(at).len(),
252            None => 0,
253        })
254    }
255
256    /// `LINDEX key index`, counting from the back when the index is negative.
257    pub fn lindex(&mut self, key: &[u8], index: i64) -> Result<Option<Element<'_>>> {
258        let Some(slot) = self.list_slot(key)? else {
259            return Ok(None);
260        };
261        let list = self.list_at(slot);
262        Ok(at(index, list.len()).and_then(|i| list.get(i)))
263    }
264
265    /// `LRANGE key start stop`, both ends inclusive and both able to be
266    /// negative.
267    ///
268    /// The answer borrows the database for as long as it is alive, so the
269    /// caller walks it straight into the reply rather than collecting it (Y18).
270    /// A key that is not there is an empty range and not a nil, which is what
271    /// Redis replies and is the one place a list differs from a set.
272    pub fn lrange(
273        &mut self,
274        key: &[u8],
275        start: i64,
276        stop: i64,
277    ) -> Result<impl Iterator<Item = Element<'_>>> {
278        let slot = self.list_slot(key)?;
279        let list = slot.map(|at| self.list_at(at));
280        let (from, count) = match list {
281            Some(l) => window(start, stop, l.len()),
282            None => (0, 0),
283        };
284        Ok(list
285            .into_iter()
286            .flat_map(move |l| l.range(from, count))
287            .take(count))
288    }
289
290    /// `LSET key index element`.
291    ///
292    /// Two errors and no boolean, because both of them are errors on the wire:
293    /// `no such key` for a missing key and `index out of range` for an index
294    /// the list does not reach. A list is never empty, so those really are the
295    /// only two ways to miss.
296    pub fn lset(&mut self, key: &[u8], index: i64, value: &[u8]) -> Result<()> {
297        strings::check_len(key, value.len())?;
298        let Some(slot) = self.list_slot(key)? else {
299            return Err(Error::new(Code::Invalid, NO_KEY));
300        };
301        let limits = self.list_limits;
302        let list = self
303            .lists
304            .get_mut(slot)
305            .expect("the record points at its body");
306        let Some(i) = at(index, list.len()) else {
307            return Err(Error::new(Code::Invalid, OUT_OF_RANGE));
308        };
309        if !list.set(i, value, &limits) {
310            return Err(Error::new(Code::Invalid, OUT_OF_RANGE));
311        }
312        Ok(())
313    }
314
315    /// `LINSERT key BEFORE|AFTER pivot element`.
316    ///
317    /// The new length, or `-1` when the pivot is not in the list, or `0` when
318    /// the key is not there. Three answers in one signed number is Redis's
319    /// choice and it is a bad one, but it is on the wire and cannot be changed.
320    pub fn linsert(&mut self, key: &[u8], before: bool, pivot: &[u8], value: &[u8]) -> Result<i64> {
321        strings::check_len(key, value.len())?;
322        let Some(slot) = self.list_slot(key)? else {
323            return Ok(0);
324        };
325        let limits = self.list_limits;
326        let list = self
327            .lists
328            .get_mut(slot)
329            .expect("the record points at its body");
330        Ok(match list.insert_at_pivot(pivot, value, before, &limits) {
331            Some(len) => len as i64,
332            None => -1,
333        })
334    }
335
336    /// `LREM key count element`. Answers how many went.
337    ///
338    /// A positive count removes that many from the front, a negative one that
339    /// many from the back, and zero removes all of them. The key goes if the
340    /// list ends up empty.
341    pub fn lrem(&mut self, key: &[u8], count: i64, value: &[u8]) -> Result<usize> {
342        let Some(slot) = self.list_slot(key)? else {
343            return Ok(0);
344        };
345        let limits = self.list_limits;
346        let list = self
347            .lists
348            .get_mut(slot)
349            .expect("the record points at its body");
350        let gone = list.remove(count, value, &limits);
351        if list.is_empty() {
352            self.drop_key(key);
353        }
354        Ok(gone)
355    }
356
357    /// `LTRIM key start stop`, keeping the window and throwing the rest away.
358    ///
359    /// A window that selects nothing deletes the key, which is what an empty
360    /// range means here: `LTRIM k 1 0` is the documented way to empty a list.
361    pub fn ltrim(&mut self, key: &[u8], start: i64, stop: i64) -> Result<()> {
362        let Some(slot) = self.list_slot(key)? else {
363            return Ok(());
364        };
365        let limits = self.list_limits;
366        let list = self
367            .lists
368            .get_mut(slot)
369            .expect("the record points at its body");
370        let (from, count) = window(start, stop, list.len());
371        list.trim(from, count, &limits);
372        if list.is_empty() {
373            self.drop_key(key);
374        }
375        Ok(())
376    }
377
378    /// `LPOS key element [RANK rank] [COUNT count] [MAXLEN len]`.
379    ///
380    /// The positions land in `out`, which the caller supplies and which is
381    /// cleared first, because this runs on a shard thread and a shard thread
382    /// that allocates aborts. `count` of zero means every match and `maxlen` of
383    /// zero means no limit on how far to look, both of which are Redis's
384    /// spellings for no limit.
385    ///
386    /// # Errors
387    ///
388    /// A rank of zero, which has no reading. Everything else about a missing
389    /// key or a missing element is an empty answer rather than an error.
390    pub fn lpos(
391        &mut self,
392        key: &[u8],
393        value: &[u8],
394        rank: i64,
395        count: usize,
396        maxlen: usize,
397        out: &mut Vec<usize>,
398    ) -> Result<()> {
399        out.clear();
400        if rank == 0 {
401            return Err(Error::new(Code::Invalid, ZERO_RANK));
402        }
403        self.lpos_into(key, value, rank, count, maxlen, |at| out.push(at))?;
404        Ok(())
405    }
406
407    /// `LPOS`, with each position handed over as it is found.
408    ///
409    /// This is what the wire calls. The positions go straight into the reply
410    /// buffer as they are discovered, so a `LPOS key x COUNT 0` over a list
411    /// with ten thousand matches never builds a list of ten thousand numbers
412    /// anywhere (Y18). Answers how many there were.
413    ///
414    /// # Errors
415    ///
416    /// A rank of zero, and `WRONGTYPE` for a key that is not a list.
417    pub fn lpos_into<F>(
418        &mut self,
419        key: &[u8],
420        value: &[u8],
421        rank: i64,
422        count: usize,
423        maxlen: usize,
424        mut found: F,
425    ) -> Result<usize>
426    where
427        F: FnMut(usize),
428    {
429        if rank == 0 {
430            return Err(Error::new(Code::Invalid, ZERO_RANK));
431        }
432        let Some(slot) = self.list_slot(key)? else {
433            return Ok(0);
434        };
435        Ok(self
436            .list_at(slot)
437            .positions(value, rank, count, maxlen, &mut found))
438    }
439
440    /// `LMOVE src dst LEFT|RIGHT LEFT|RIGHT`, and `RPOPLPUSH` under it.
441    ///
442    /// Answers the element that moved, or nothing when the source is empty or
443    /// missing. The destination is made if it is not there, and the source key
444    /// goes if that was its last element.
445    ///
446    /// `src` and `dst` being the same key is not a special case to work around,
447    /// it is `LMOVE k k LEFT RIGHT`, which is the documented way to rotate a
448    /// list and is what a round robin scheduler is built out of. It falls out
449    /// of taking the element before deciding where to put it.
450    ///
451    /// This is the one list command that has to copy an element, for the reason
452    /// `SPOP` gives: the value it answers with no longer has a structure to
453    /// borrow from. Moving the bytes from one list to the other without the
454    /// copy would need both bodies borrowed at once, and the destination may be
455    /// the source.
456    ///
457    /// The copy is not an allocation, though, which is the difference between
458    /// this and the first version of it. The element goes into the database's
459    /// one scratch buffer and the answer borrows that, so a queue that runs
460    /// `RPOPLPUSH` in a loop does no allocator work at all after the first
461    /// call, where before it did a malloc and a free per element. The answer
462    /// borrows the database until the caller is done with it, which is what
463    /// both callers want anyway: they write it to the reply and drop it.
464    pub fn lmove(&mut self, src: &[u8], dst: &[u8], from: End, to: End) -> Result<Option<&[u8]>> {
465        // The destination's type is checked before anything is taken, so that
466        // `LMOVE list string LEFT LEFT` is a `WRONGTYPE` with the source
467        // untouched rather than an element that has gone nowhere.
468        self.list_slot(dst)?;
469        // Taken out of the database and put back at the end of every path, so
470        // that `pop_into` and `push` can have `&mut self` while the bytes are
471        // in hand. The buffer is empty for the duration and nothing else looks
472        // at it, so a path that returns early leaves it exactly as it found it.
473        let mut buf = std::mem::take(&mut self.scratch);
474        buf.clear();
475        let took = self.pop_into(src, from, 1, |e| e.write_to(&mut buf));
476        let moved = match took {
477            Ok(n) => n,
478            Err(e) => {
479                self.scratch = buf;
480                return Err(e);
481            }
482        };
483        if moved == 0 {
484            self.scratch = buf;
485            return Ok(None);
486        }
487        let pushed = self.push(dst, to, std::iter::once(buf.as_slice()));
488        self.scratch = buf;
489        pushed?;
490        Ok(Some(&self.scratch))
491    }
492
493    /// `LMOVEM src dst LEFT|RIGHT LEFT|RIGHT [COUNT|EXACTLY n OBO|BULK]`.
494    ///
495    /// [`Keyspace::lmove`] for more than one element at a time, which Redis
496    /// 8.10 added. `f` gets each element that moved, in the order it now sits in
497    /// the destination, which is the order the reply wants. The count that comes
498    /// back is how many that was, and zero means the reply is a nil rather than
499    /// an empty array.
500    ///
501    /// [`Movem::exactly`] is the `EXACTLY` spelling against the `COUNT` one: all
502    /// of them or none of them, so a source shorter than the count moves nothing
503    /// and answers zero. That is the whole difference and it is checked before
504    /// anything is taken, which is the only way to make it true.
505    ///
506    /// # Why everything is taken before anything is put
507    ///
508    /// Because the destination is allowed to be the source. `LMOVEM k k LEFT
509    /// RIGHT COUNT 2 BULK` is a rotation by two and has to work, the same way
510    /// `LMOVE k k LEFT RIGHT` is a rotation by one. Popping the whole block
511    /// first and pushing it afterwards gets that for nothing, where anything
512    /// that interleaved the two would be reading a list it was writing.
513    ///
514    /// The block goes through the same scratch buffer [`Keyspace::lmove`] uses,
515    /// with the row buffer next to it holding where each element ends in it, so
516    /// moving a hundred elements is two buffers that were already there rather
517    /// than a `Vec` per element. Both are taken out of the database for the
518    /// duration, because the bytes have to be in hand while `push` has
519    /// `&mut self`.
520    pub fn lmovem<F>(&mut self, src: &[u8], dst: &[u8], b: Movem, f: F) -> Result<usize>
521    where
522        F: FnMut(&[u8]),
523    {
524        // Both types settled before a single element moves, so a WRONGTYPE
525        // anywhere leaves both lists as they were. `llen` checks the source and
526        // is wanted for `EXACTLY` anyway.
527        self.list_slot(dst)?;
528        let have = self.llen(src)?;
529        if b.exactly && have < b.count {
530            return Ok(0);
531        }
532
533        let mut buf = std::mem::take(&mut self.scratch);
534        let mut ends = std::mem::take(&mut self.rows);
535        buf.clear();
536        ends.clear();
537        let took = self.pop_into(src, b.from, b.count, |e| {
538            e.write_to(&mut buf);
539            ends.push(buf.len());
540        });
541        let moved = match took {
542            Ok(n) => n,
543            Err(e) => {
544                self.scratch = buf;
545                self.rows = ends;
546                return Err(e);
547            }
548        };
549        if moved == 0 {
550            self.scratch = buf;
551            self.rows = ends;
552            return Ok(0);
553        }
554
555        let pushed = self.push_block(dst, b, moved, &buf, &ends, f);
556        self.scratch = buf;
557        self.rows = ends;
558        pushed?;
559        Ok(moved)
560    }
561
562    /// The elements a move took, pushed onto the destination in the order the
563    /// destination wants them, and then handed to `f` in the order the reply
564    /// wants them.
565    ///
566    /// `buf` holds them end to end in the order they came off the source and
567    /// `ends` says where each one stops. Its own method because a move across
568    /// two stripes takes the elements out of one stripe and puts them into
569    /// another, and this half is the part that belongs to the destination.
570    ///
571    /// # Where the destination order comes from
572    ///
573    /// `pop_into` hands them over in the order it took them, which is source
574    /// order from the left and reversed source order from the right. Bulk wants
575    /// source order, so it turns them back over when they came off the right.
576    /// One by one wants whatever repeated pushes at that end would have left,
577    /// and a push at the head puts each in front of the last, so it turns them
578    /// over when the destination end is the head. Those two conditions agree
579    /// exactly when the two ends differ, which is why the spelling only matters
580    /// when they are the same.
581    pub(crate) fn push_block<F>(
582        &mut self,
583        dst: &[u8],
584        b: Movem,
585        moved: usize,
586        buf: &[u8],
587        ends: &[usize],
588        mut f: F,
589    ) -> Result<()>
590    where
591        F: FnMut(&[u8]),
592    {
593        let flip = match b.order {
594            Order::Bulk => !b.from.is_left(),
595            Order::OneByOne => b.to.is_left(),
596        };
597        let at = |i: usize| {
598            let end = ends[i];
599            let start = if i == 0 { 0 } else { ends[i - 1] };
600            &buf[start..end]
601        };
602        let placed = |i: usize| if flip { moved - 1 - i } else { i };
603        // And pushing in that order needs the same turn again at the head, for
604        // the same reason: `push` is `LPUSH`, so the last one sent ends up in
605        // front.
606        let sent = |i: usize| placed(if b.to.is_left() { moved - 1 - i } else { i });
607
608        self.push(dst, b.to, (0..moved).map(|i| at(sent(i))))?;
609        for i in 0..moved {
610            f(at(placed(i)));
611        }
612        Ok(())
613    }
614
615    /// The slot `key`'s list is in, or `None` if there is no such key.
616    #[inline]
617    fn list_slot(&mut self, key: &[u8]) -> Result<Option<u32>> {
618        self.live_slot(key, Kind::List)
619    }
620
621    /// The body in a slot the record pointed at.
622    ///
623    /// Panicking here means a record outlived its body, the same bug
624    /// [`Keyspace::set_at`] is watching for.
625    #[inline]
626    fn list_at(&self, at: u32) -> &List {
627        self.lists.get(at).expect("the record points at its body")
628    }
629
630    /// Make an empty list under `key` and answer which slot it went in.
631    ///
632    /// No hint, unlike a set. A list starts packed whatever is going into it,
633    /// because the band it belongs in is decided by bytes rather than by count
634    /// and the first push finds that out for free.
635    fn new_list(&mut self, key: &[u8]) -> u32 {
636        // The body and, every so often, the slab that holds it. See
637        // `yo_alloc::first_touch` for why this is the one allocation a command
638        // is allowed to make.
639        let at = yo_alloc::first_touch(|| self.lists.insert(List::new()));
640        let len = value::slot_record_len(false);
641        self.write_rec(key, len, |out| {
642            value::write_slot_record(out, Kind::List, at, None);
643        });
644        self.bodies += 1;
645        at
646    }
647
648    /// Put a list of `values` under `key`, replacing whatever was there.
649    ///
650    /// What `SORT ... STORE` writes, and the same shape [`Keyspace::put_set`]
651    /// has for the store forms of the set commands: the value under the name
652    /// changes and the name stays where it stands. That is not the same thing
653    /// as taking the key away and putting a new one there under the same
654    /// spelling, which is what [`Keyspace::import`] does, and a client watching
655    /// for keys that were not there before can tell the difference.
656    ///
657    /// The destination is allowed to be the key that was sorted, because the
658    /// caller has already read out everything it needs. Any deadline the
659    /// destination carried goes with the value, which is what every store form
660    /// does and for the reason [`Keyspace::put_set`] gives.
661    pub(crate) fn put_list<'v>(
662        &mut self,
663        key: &[u8],
664        values: impl Iterator<Item = &'v [u8]> + Clone,
665    ) -> Result<usize> {
666        for v in values.clone() {
667            strings::check_len(key, v.len())?;
668        }
669        self.replacing(key, Kind::List);
670        self.free_body(key);
671        let at = self.new_list(key);
672        let limits = self.list_limits;
673        let list = self
674            .lists
675            .get_mut(at)
676            .expect("the record points at its body");
677        for v in values {
678            list.push_back(v, &limits);
679        }
680        Ok(list.len())
681    }
682}
683
684impl Db {
685    /// `LMOVE`, and `RPOPLPUSH` with it, over a database of any width.
686    ///
687    /// Two keys on one stripe is the whole command handed to that stripe, which
688    /// is what a width one database always does and what makes `LMOVE k k LEFT
689    /// RIGHT` a rotation here as much as it is there.
690    ///
691    /// Two keys on two stripes is the same three steps with the element passing
692    /// through this database's own buffer rather than a stripe's. The element
693    /// has to be copied for the reason the one stripe version gives, that it has
694    /// no structure to borrow from once it has moved, and the copy costs no
695    /// allocation after the first call for the same reason too.
696    pub fn lmove<F>(&self, src: &[u8], dst: &[u8], from: End, to: End, f: F) -> Result<bool>
697    where
698        F: FnOnce(&[u8]),
699    {
700        let (home, onto) = (self.stripe_of(src), self.stripe_of(dst));
701        if home == onto {
702            return match self.hold_stripe(home).lmove(src, dst, from, to)? {
703                Some(moved) => {
704                    f(moved);
705                    Ok(true)
706                }
707                None => Ok(false),
708            };
709        }
710        // The destination's type before anything is taken, which is the order
711        // that makes `LMOVE list string LEFT LEFT` leave the source alone.
712        self.hold_stripe(onto).list_slot(dst)?;
713        // The buffers before the stripes, which is the order every command that
714        // wants both takes them in.
715        let mut spare = self.spare();
716        let buf = &mut spare.bytes;
717        buf.clear();
718        let moved = self
719            .hold_stripe(home)
720            .pop_into(src, from, 1, |e| e.write_to(buf))?;
721        if moved == 0 {
722            return Ok(false);
723        }
724        self.hold_stripe(onto)
725            .push(dst, to, std::iter::once(buf.as_slice()))?;
726        f(buf);
727        Ok(true)
728    }
729
730    /// `LMOVEM src dst LEFT|RIGHT LEFT|RIGHT [COUNT|EXACTLY n OBO|BULK]`.
731    ///
732    /// [`Db::lmove`] for a block of elements, and everything is taken out of the
733    /// source before anything is put in the destination, which is what the one
734    /// stripe version does and for the same reason: the destination is allowed
735    /// to be the source.
736    ///
737    /// The ordering the elements end up in is worked out by
738    /// [`Keyspace::lmovem`], and rather than write it out a second time this
739    /// hands the block to that method on the destination's stripe. The source
740    /// stripe has already given the elements up by then, so what is left is a
741    /// push into one stripe, which is a move whose source is not there.
742    pub fn lmovem<F>(&self, src: &[u8], dst: &[u8], b: Movem, f: F) -> Result<usize>
743    where
744        F: FnMut(&[u8]),
745    {
746        let (home, onto) = (self.stripe_of(src), self.stripe_of(dst));
747        if home == onto {
748            return self.hold_stripe(home).lmovem(src, dst, b, f);
749        }
750        // Both types settled before a single element moves, and the length of
751        // the source is wanted for `EXACTLY` anyway.
752        self.hold_stripe(onto).list_slot(dst)?;
753        let have = self.hold_stripe(home).llen(src)?;
754        if have == 0 || (b.exactly && have < b.count) {
755            return Ok(0);
756        }
757        // The buffers before the stripes, which is the order every command that
758        // wants both takes them in.
759        let mut spare = self.spare();
760        let spare = &mut *spare;
761        let (buf, ends) = (&mut spare.bytes, &mut spare.rows);
762        buf.clear();
763        ends.clear();
764        let moved = self.hold_stripe(home).pop_into(src, b.from, b.count, |e| {
765            e.write_to(buf);
766            ends.push(buf.len());
767        })?;
768        self.hold_stripe(onto)
769            .push_block(dst, b, moved, buf, ends, f)?;
770        Ok(moved)
771    }
772}
773
774/// Turn a signed index into an offset from the front, or nothing if it misses.
775///
776/// A negative index counts from the back, so -1 is the last element. An index
777/// that is still negative after that, or that reaches past the end, is a miss,
778/// and the two are the same answer because `LINDEX` replies nil to both.
779#[inline]
780fn at(index: i64, len: usize) -> Option<usize> {
781    let i = if index < 0 { len as i64 + index } else { index };
782    (i >= 0 && (i as usize) < len).then_some(i as usize)
783}
784
785/// Turn an inclusive `start` and `stop` into an offset and a count.
786///
787/// Every out of range case clamps rather than erroring, which is Redis's rule
788/// for `LRANGE` and `LTRIM` both: a start before the front is the front, a stop
789/// past the end is the end, and a start after the stop is nothing at all.
790#[inline]
791fn window(start: i64, stop: i64, len: usize) -> (usize, usize) {
792    let n = len as i64;
793    let from = if start < 0 { (n + start).max(0) } else { start };
794    let to = if stop < 0 { n + stop } else { stop.min(n - 1) };
795    if from > to || from >= n || to < 0 {
796        return (0, 0);
797    }
798    (from as usize, (to - from + 1) as usize)
799}
800
801#[cfg(test)]
802mod tests {
803    use super::*;
804    use crate::Clock;
805    use crate::list::Encoding;
806    use yo_common::Code;
807
808    fn db() -> Keyspace {
809        Keyspace::with_clock(Clock::fixed(1_000))
810    }
811
812    fn rpush(d: &mut Keyspace, key: &[u8], values: &[&[u8]]) -> usize {
813        d.push(key, End::Right, values.iter().copied())
814            .expect("a list")
815    }
816
817    fn all(d: &mut Keyspace, key: &[u8]) -> Vec<String> {
818        d.lrange(key, 0, -1)
819            .expect("a list")
820            .map(|e| String::from_utf8(e.to_vec()).expect("utf8 in these tests"))
821            .collect()
822    }
823
824    #[test]
825    fn pushing_to_a_key_that_is_not_there_makes_it() {
826        let mut d = db();
827        assert_eq!(rpush(&mut d, b"l", &[b"a", b"b", b"c"]), 3);
828        assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
829        assert_eq!(d.llen(b"l").expect("a list"), 3);
830    }
831
832    #[test]
833    fn lpush_puts_the_last_element_in_front() {
834        let mut d = db();
835        d.push(b"l", End::Left, [b"a".as_slice(), b"b", b"c"].into_iter())
836            .expect("a list");
837        assert_eq!(all(&mut d, b"l"), ["c", "b", "a"]);
838    }
839
840    #[test]
841    fn pushing_nothing_does_not_make_a_key() {
842        let mut d = db();
843        let none: [&[u8]; 0] = [];
844        assert_eq!(d.push(b"l", End::Left, none.into_iter()).expect("ok"), 0);
845        assert_eq!(d.kind_of(b"l"), None);
846    }
847
848    #[test]
849    fn pushx_only_pushes_to_a_list_that_is_there() {
850        let mut d = db();
851        assert_eq!(
852            d.pushx(b"l", End::Right, [b"a".as_slice()].into_iter())
853                .expect("ok"),
854            0
855        );
856        assert_eq!(d.kind_of(b"l"), None);
857        rpush(&mut d, b"l", &[b"a"]);
858        assert_eq!(
859            d.pushx(b"l", End::Right, [b"b".as_slice()].into_iter())
860                .expect("ok"),
861            2
862        );
863    }
864
865    #[test]
866    fn popping_the_last_element_takes_the_key_with_it() {
867        let mut d = db();
868        rpush(&mut d, b"l", &[b"only"]);
869        assert_eq!(
870            d.pop(b"l", End::Left).expect("ok").as_deref(),
871            Some(&b"only"[..])
872        );
873        assert_eq!(d.kind_of(b"l"), None);
874        assert_eq!(d.pop(b"l", End::Left).expect("ok"), None);
875    }
876
877    #[test]
878    fn a_count_pop_takes_from_the_end_it_was_asked_for() {
879        let mut d = db();
880        rpush(&mut d, b"l", &[b"a", b"b", b"c", b"d"]);
881        let mut got = Vec::new();
882        d.pop_into(b"l", End::Right, 2, |e| got.push(e.to_vec()))
883            .expect("a list");
884        assert_eq!(got, [b"d".to_vec(), b"c".to_vec()]);
885        assert_eq!(all(&mut d, b"l"), ["a", "b"]);
886    }
887
888    #[test]
889    fn a_count_pop_larger_than_the_list_empties_it() {
890        let mut d = db();
891        rpush(&mut d, b"l", &[b"a", b"b"]);
892        let mut n = 0;
893        assert_eq!(
894            d.pop_into(b"l", End::Left, 99, |_| n += 1).expect("a list"),
895            2
896        );
897        assert_eq!(n, 2);
898        assert_eq!(d.kind_of(b"l"), None);
899    }
900
901    #[test]
902    fn lrange_clamps_at_both_ends() {
903        let mut d = db();
904        rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
905        assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
906        let got: Vec<_> = d
907            .lrange(b"l", -100, 100)
908            .expect("a list")
909            .map(|e| e.to_vec())
910            .collect();
911        assert_eq!(got.len(), 3);
912        assert_eq!(d.lrange(b"l", 2, 1).expect("a list").count(), 0);
913        assert_eq!(d.lrange(b"l", 5, 9).expect("a list").count(), 0);
914        assert_eq!(d.lrange(b"nope", 0, -1).expect("no key").count(), 0);
915    }
916
917    #[test]
918    fn lindex_counts_from_the_back_when_it_is_negative() {
919        let mut d = db();
920        rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
921        assert_eq!(
922            d.lindex(b"l", 0).expect("ok").map(|e| e.to_vec()),
923            Some(b"a".to_vec())
924        );
925        assert_eq!(
926            d.lindex(b"l", -1).expect("ok").map(|e| e.to_vec()),
927            Some(b"c".to_vec())
928        );
929        assert!(d.lindex(b"l", 3).expect("ok").is_none());
930        assert!(d.lindex(b"l", -4).expect("ok").is_none());
931        assert!(d.lindex(b"nope", 0).expect("ok").is_none());
932    }
933
934    #[test]
935    fn lset_says_which_of_the_two_ways_it_missed() {
936        let mut d = db();
937        let e = d.lset(b"nope", 0, b"x").expect_err("no key");
938        assert_eq!(e.message(), NO_KEY);
939        rpush(&mut d, b"l", &[b"a", b"b"]);
940        d.lset(b"l", -1, b"z").expect("in range");
941        assert_eq!(all(&mut d, b"l"), ["a", "z"]);
942        let e = d.lset(b"l", 9, b"x").expect_err("out of range");
943        assert_eq!(e.message(), OUT_OF_RANGE);
944    }
945
946    #[test]
947    fn linsert_has_three_answers() {
948        let mut d = db();
949        assert_eq!(d.linsert(b"nope", true, b"a", b"x").expect("ok"), 0);
950        rpush(&mut d, b"l", &[b"a", b"c"]);
951        assert_eq!(d.linsert(b"l", true, b"c", b"b").expect("ok"), 3);
952        assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
953        assert_eq!(d.linsert(b"l", false, b"zz", b"x").expect("ok"), -1);
954    }
955
956    #[test]
957    fn lrem_counts_from_the_end_the_sign_says() {
958        let mut d = db();
959        rpush(&mut d, b"l", &[b"a", b"x", b"b", b"x", b"c", b"x"]);
960        assert_eq!(d.lrem(b"l", 1, b"x").expect("ok"), 1);
961        assert_eq!(all(&mut d, b"l"), ["a", "b", "x", "c", "x"]);
962        assert_eq!(d.lrem(b"l", -1, b"x").expect("ok"), 1);
963        assert_eq!(all(&mut d, b"l"), ["a", "b", "x", "c"]);
964        assert_eq!(d.lrem(b"l", 0, b"x").expect("ok"), 1);
965        assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
966    }
967
968    #[test]
969    fn removing_everything_takes_the_key() {
970        let mut d = db();
971        rpush(&mut d, b"l", &[b"x", b"x"]);
972        assert_eq!(d.lrem(b"l", 0, b"x").expect("ok"), 2);
973        assert_eq!(d.kind_of(b"l"), None);
974    }
975
976    #[test]
977    fn ltrim_keeps_the_window() {
978        let mut d = db();
979        rpush(&mut d, b"l", &[b"a", b"b", b"c", b"d", b"e"]);
980        d.ltrim(b"l", 1, -2).expect("ok");
981        assert_eq!(all(&mut d, b"l"), ["b", "c", "d"]);
982    }
983
984    #[test]
985    fn an_empty_window_deletes_the_key() {
986        let mut d = db();
987        rpush(&mut d, b"l", &[b"a", b"b"]);
988        d.ltrim(b"l", 1, 0).expect("ok");
989        assert_eq!(d.kind_of(b"l"), None);
990        d.ltrim(b"nope", 0, -1).expect("no key is not an error");
991    }
992
993    #[test]
994    fn lpos_answers_where_and_how_many() {
995        let mut d = db();
996        rpush(&mut d, b"l", &[b"a", b"b", b"c", b"b", b"b"]);
997        let mut out = Vec::new();
998        d.lpos(b"l", b"b", 1, 0, 0, &mut out).expect("ok");
999        assert_eq!(out, [1, 3, 4]);
1000        d.lpos(b"l", b"b", -1, 2, 0, &mut out).expect("ok");
1001        assert_eq!(out, [4, 3]);
1002        d.lpos(b"l", b"b", 1, 0, 2, &mut out).expect("ok");
1003        assert_eq!(out, [1]);
1004        d.lpos(b"l", b"zz", 1, 0, 0, &mut out).expect("ok");
1005        assert!(out.is_empty());
1006        d.lpos(b"nope", b"b", 1, 0, 0, &mut out).expect("ok");
1007        assert!(out.is_empty());
1008    }
1009
1010    #[test]
1011    fn a_rank_of_zero_is_an_error() {
1012        let mut d = db();
1013        rpush(&mut d, b"l", &[b"a"]);
1014        let e = d
1015            .lpos(b"l", b"a", 0, 0, 0, &mut Vec::new())
1016            .expect_err("zero");
1017        assert_eq!(e.message(), ZERO_RANK);
1018    }
1019
1020    /// The whole of `LMOVEM`'s ordering, which is the part of it that is not
1021    /// obvious. Every row was read off a live 8.10.1 before it was written here.
1022    ///
1023    /// The two spellings agree on the two rows where the ends differ and come
1024    /// apart on the two where they are the same, and that is the entire content
1025    /// of `OBO` against `BULK`.
1026    #[test]
1027    fn lmovem_orders_the_block_four_ways() {
1028        use End::{Left, Right};
1029        use Order::{Bulk, OneByOne};
1030        for (from, to, order, want, left) in [
1031            (Left, Right, OneByOne, ["a", "b"], ["c", "d", "e"]),
1032            (Left, Right, Bulk, ["a", "b"], ["c", "d", "e"]),
1033            (Left, Left, OneByOne, ["b", "a"], ["c", "d", "e"]),
1034            (Left, Left, Bulk, ["a", "b"], ["c", "d", "e"]),
1035            (Right, Left, OneByOne, ["d", "e"], ["a", "b", "c"]),
1036            (Right, Left, Bulk, ["d", "e"], ["a", "b", "c"]),
1037            (Right, Right, OneByOne, ["e", "d"], ["a", "b", "c"]),
1038            (Right, Right, Bulk, ["d", "e"], ["a", "b", "c"]),
1039        ] {
1040            let mut d = db();
1041            rpush(&mut d, b"s", &[b"a", b"b", b"c", b"d", b"e"]);
1042            let mut got = Vec::new();
1043            let b = Movem {
1044                from,
1045                to,
1046                count: 2,
1047                exactly: false,
1048                order,
1049            };
1050            let n = d
1051                .lmovem(b"s", b"t", b, |v| {
1052                    got.push(String::from_utf8(v.to_vec()).expect("utf8 in these tests"));
1053                })
1054                .expect("two lists");
1055            let how = format!("{from:?} {to:?} {order:?}");
1056            assert_eq!(n, 2, "{how}");
1057            assert_eq!(got, want, "the reply for {how}");
1058            assert_eq!(all(&mut d, b"t"), want, "the destination for {how}");
1059            assert_eq!(all(&mut d, b"s"), left, "what is left for {how}");
1060        }
1061    }
1062
1063    /// A block for the tests that are not about ordering, which all want the
1064    /// same one and only care about the ends and the count.
1065    fn block(from: End, to: End, count: usize, exactly: bool) -> Movem {
1066        Movem {
1067            from,
1068            to,
1069            count,
1070            exactly,
1071            order: Order::Bulk,
1072        }
1073    }
1074
1075    /// `EXACTLY` is all of them or none, and the check happens before anything
1076    /// is taken, which is the only place it can happen.
1077    #[test]
1078    fn lmovem_exactly_takes_all_of_them_or_none() {
1079        let mut d = db();
1080        rpush(&mut d, b"s", &[b"a", b"b", b"c"]);
1081        let all_of_four = block(End::Left, End::Right, 4, true);
1082        let n = d
1083            .lmovem(b"s", b"t", all_of_four, |_| {
1084                panic!("nothing should have moved")
1085            })
1086            .expect("two lists");
1087        assert_eq!(n, 0);
1088        assert_eq!(
1089            all(&mut d, b"s"),
1090            ["a", "b", "c"],
1091            "the source is untouched"
1092        );
1093        assert_eq!(d.llen(b"t").expect("a list"), 0, "and nothing was made");
1094
1095        // The same count with `COUNT` takes what there is.
1096        let mut got = Vec::new();
1097        let up_to_four = block(End::Left, End::Right, 4, false);
1098        let n = d
1099            .lmovem(b"s", b"t", up_to_four, |v| got.push(v.to_vec()))
1100            .expect("two lists");
1101        assert_eq!(n, 3);
1102        assert_eq!(got.len(), 3);
1103        assert!(!d.exists(b"s"), "an emptied source goes");
1104    }
1105
1106    /// The destination is allowed to be the source, the same way it is for
1107    /// `LMOVE`, and there it is a rotation by more than one.
1108    #[test]
1109    fn lmovem_onto_itself_rotates_by_the_count() {
1110        let mut d = db();
1111        rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
1112        d.lmovem(b"l", b"l", block(End::Left, End::Right, 2, false), |_| {})
1113            .expect("a list");
1114        assert_eq!(all(&mut d, b"l"), ["c", "a", "b"]);
1115
1116        let mut d = db();
1117        rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
1118        d.lmovem(b"l", b"l", block(End::Right, End::Left, 2, false), |_| {})
1119            .expect("a list");
1120        assert_eq!(all(&mut d, b"l"), ["b", "c", "a"]);
1121    }
1122
1123    /// A source that is not there moves nothing and is not an error, and the
1124    /// destination's type is settled before anything is taken.
1125    #[test]
1126    fn lmovem_checks_the_destination_before_taking_anything() {
1127        let mut d = db();
1128        rpush(&mut d, b"s", &[b"a", b"b"]);
1129        d.set_plain(b"str", b"v").expect("room");
1130        let two = block(End::Left, End::Right, 2, false);
1131        let e = d
1132            .lmovem(b"s", b"str", two, |_| {})
1133            .expect_err("the destination is a string");
1134        assert_eq!(e.code(), Code::WrongType);
1135        assert_eq!(all(&mut d, b"s"), ["a", "b"], "the source is untouched");
1136
1137        let n = d
1138            .lmovem(b"nope", b"t", two, |_| {})
1139            .expect("a source that is not there is not an error");
1140        assert_eq!(n, 0);
1141    }
1142
1143    #[test]
1144    fn lmove_between_two_keys_makes_the_second_one() {
1145        let mut d = db();
1146        rpush(&mut d, b"src", &[b"a", b"b"]);
1147        let got = d.lmove(b"src", b"dst", End::Right, End::Left).expect("ok");
1148        assert_eq!(got, Some(&b"b"[..]));
1149        assert_eq!(all(&mut d, b"src"), ["a"]);
1150        assert_eq!(all(&mut d, b"dst"), ["b"]);
1151    }
1152
1153    #[test]
1154    fn lmove_onto_itself_rotates() {
1155        let mut d = db();
1156        rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
1157        d.lmove(b"l", b"l", End::Right, End::Left).expect("ok");
1158        assert_eq!(all(&mut d, b"l"), ["c", "a", "b"]);
1159        d.lmove(b"l", b"l", End::Left, End::Right).expect("ok");
1160        assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
1161    }
1162
1163    #[test]
1164    fn lmove_from_a_key_that_is_not_there_does_nothing() {
1165        let mut d = db();
1166        assert_eq!(
1167            d.lmove(b"nope", b"dst", End::Left, End::Left).expect("ok"),
1168            None
1169        );
1170        assert_eq!(d.kind_of(b"dst"), None);
1171    }
1172
1173    /// `RPOPLPUSH` in a loop is what a work queue is, so the element that moves
1174    /// must not cost a malloc and a free every time round. The first call is
1175    /// allowed to grow the scratch buffer and everything after it is not.
1176    #[test]
1177    fn lmove_stops_allocating_once_its_buffer_is_grown() {
1178        let mut d = db();
1179        rpush(&mut d, b"q", &[b"a", b"b", b"c"]);
1180        // Warm up. This one may grow the scratch buffer, and on a fresh
1181        // database it also makes the destination.
1182        d.lmove(b"q", b"q", End::Right, End::Left).expect("ok");
1183        let (_, allocs) = crate::tally::counted(|| {
1184            for _ in 0..100 {
1185                d.lmove(b"q", b"q", End::Right, End::Left).expect("ok");
1186            }
1187        });
1188        assert_eq!(allocs, 0, "lmove allocated {allocs} times in a hundred");
1189        assert_eq!(all(&mut d, b"q"), ["b", "c", "a"]);
1190    }
1191
1192    /// `LREM` used to build a `Vec` to hold the indices it was about to remove,
1193    /// and the count it is given is one in almost every use of it, so that was
1194    /// an allocation to hold a single `usize`.
1195    #[test]
1196    fn lrem_does_not_allocate_to_remove_a_handful() {
1197        let mut d = db();
1198        // Built up front, so the loop below is a hundred `LREM` calls and
1199        // nothing else. Pushing inside it would make the key over and over,
1200        // and making a key is an allocation this is not asking about.
1201        let many: Vec<&[u8]> = (0..100).map(|_| b"gone".as_slice()).collect();
1202        rpush(&mut d, b"l", &many);
1203        rpush(&mut d, b"l", &[b"keep"]);
1204        let (_, allocs) = crate::tally::counted(|| {
1205            for _ in 0..100 {
1206                assert_eq!(d.lrem(b"l", 1, b"gone").expect("a list"), 1);
1207            }
1208        });
1209        assert_eq!(allocs, 0, "lrem allocated {allocs} times in a hundred");
1210        assert_eq!(all(&mut d, b"l"), ["keep"]);
1211    }
1212
1213    /// And it still answers when the hits do not fit on the stack.
1214    #[test]
1215    fn lrem_with_more_hits_than_fit_inline_still_removes_all_of_them() {
1216        let mut d = db();
1217        let many: Vec<&[u8]> = (0..40).map(|_| b"x".as_slice()).collect();
1218        rpush(&mut d, b"l", &many);
1219        rpush(&mut d, b"l", &[b"keep"]);
1220        assert_eq!(d.lrem(b"l", 0, b"x").expect("a list"), 40);
1221        assert_eq!(all(&mut d, b"l"), ["keep"]);
1222    }
1223
1224    #[test]
1225    fn lmove_checks_the_destination_before_taking_anything() {
1226        let mut d = db();
1227        rpush(&mut d, b"src", &[b"a"]);
1228        d.set_plain(b"dst", b"a string").expect("room");
1229        let e = d
1230            .lmove(b"src", b"dst", End::Left, End::Left)
1231            .expect_err("the destination is a string");
1232        assert_eq!(e.code(), Code::WrongType);
1233        assert_eq!(all(&mut d, b"src"), ["a"]);
1234    }
1235
1236    #[test]
1237    fn every_command_says_wrongtype_against_a_string() {
1238        let mut d = db();
1239        d.set_plain(b"s", b"a string").expect("room");
1240        assert_eq!(
1241            d.push(b"s", End::Left, [b"x".as_slice()].into_iter())
1242                .expect_err("a string")
1243                .code(),
1244            Code::WrongType
1245        );
1246        assert_eq!(d.llen(b"s").expect_err("a string").code(), Code::WrongType);
1247        assert_eq!(
1248            d.pop(b"s", End::Left).expect_err("a string").code(),
1249            Code::WrongType
1250        );
1251        assert_eq!(
1252            d.lset(b"s", 0, b"x").expect_err("a string").code(),
1253            Code::WrongType
1254        );
1255        assert_eq!(
1256            d.ltrim(b"s", 0, -1).expect_err("a string").code(),
1257            Code::WrongType
1258        );
1259    }
1260
1261    #[test]
1262    fn a_list_is_a_list_to_the_rest_of_the_keyspace() {
1263        let mut d = db();
1264        rpush(&mut d, b"l", &[b"a"]);
1265        assert_eq!(d.kind_of(b"l").map(|k| k.name()), Some("list"));
1266        assert_eq!(d.encoding_name(b"l"), Some(Encoding::Listpack.name()));
1267        assert!(d.exists(b"l"));
1268        assert!(d.drop_key(b"l"));
1269        assert_eq!(d.kind_of(b"l"), None);
1270    }
1271
1272    #[test]
1273    fn a_list_can_be_given_a_deadline_and_reaped() {
1274        let mut d = Keyspace::with_clock(Clock::fixed(1_000));
1275        rpush(&mut d, b"l", &[b"a", b"b"]);
1276        assert!(d.set_expiry(b"l", Some(1_500)));
1277        assert_eq!(all(&mut d, b"l"), ["a", "b"]);
1278        d.clock().advance(1_000);
1279        assert_eq!(d.llen(b"l").expect("gone"), 0);
1280        assert_eq!(d.kind_of(b"l"), None);
1281    }
1282
1283    #[test]
1284    fn a_big_list_is_chunked_and_still_answers_the_same() {
1285        let mut d = db();
1286        let value = vec![b'x'; 400];
1287        for i in 0..40 {
1288            let mut v = value.clone();
1289            v.extend_from_slice(format!("{i}").as_bytes());
1290            d.push(b"l", End::Right, [v.as_slice()].into_iter())
1291                .expect("a list");
1292        }
1293        assert_eq!(d.encoding_name(b"l"), Some(Encoding::Quicklist.name()));
1294        assert_eq!(d.llen(b"l").expect("a list"), 40);
1295        let last = d.lindex(b"l", -1).expect("ok").expect("in range").to_vec();
1296        assert!(last.ends_with(b"39"));
1297        d.ltrim(b"l", 0, 0).expect("ok");
1298        assert_eq!(d.llen(b"l").expect("a list"), 1);
1299        assert_eq!(d.encoding_name(b"l"), Some(Encoding::Listpack.name()));
1300    }
1301}