1use yo_common::{Code, Error, Result};
25
26use crate::keyspace::Keyspace;
27use crate::list::{Element, List};
28use crate::strings;
29use crate::value::{self, Kind};
30
31const NO_KEY: &str = "no such key";
35
36const OUT_OF_RANGE: &str = "index out of range";
38
39const 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";
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum End {
54 Left,
56 Right,
58}
59
60impl End {
61 #[inline]
63 #[must_use]
64 pub const fn is_left(self) -> bool {
65 matches!(self, End::Left)
66 }
67}
68
69impl Keyspace {
70 pub fn push<'v>(
81 &mut self,
82 key: &[u8],
83 end: End,
84 values: impl Iterator<Item = &'v [u8]> + Clone,
85 ) -> Result<usize> {
86 for v in values.clone() {
87 strings::check_len(key, v.len())?;
88 }
89 let at = match self.list_slot(key)? {
90 Some(at) => at,
91 None => {
92 if values.clone().next().is_none() {
97 return Ok(0);
98 }
99 self.new_list(key)
100 }
101 };
102 let limits = self.list_limits;
103 let list = self
104 .lists
105 .get_mut(at)
106 .expect("the record points at its body");
107 for v in values {
108 if end.is_left() {
109 list.push_front(v, &limits);
110 } else {
111 list.push_back(v, &limits);
112 }
113 }
114 Ok(list.len())
115 }
116
117 pub fn pushx<'v>(
122 &mut self,
123 key: &[u8],
124 end: End,
125 values: impl Iterator<Item = &'v [u8]> + Clone,
126 ) -> Result<usize> {
127 if self.list_slot(key)?.is_none() {
128 return Ok(0);
129 }
130 self.push(key, end, values)
131 }
132
133 pub fn pop(&mut self, key: &[u8], end: End) -> Result<Option<Vec<u8>>> {
140 let Some(at) = self.list_slot(key)? else {
141 return Ok(None);
142 };
143 let limits = self.list_limits;
144 let list = self
145 .lists
146 .get_mut(at)
147 .expect("the record points at its body");
148 let got = if end.is_left() {
149 list.pop_front(&limits)
150 } else {
151 list.pop_back(&limits)
152 };
153 if list.is_empty() {
154 self.drop_key(key);
155 }
156 Ok(got)
157 }
158
159 pub fn pop_into<F>(&mut self, key: &[u8], end: End, count: usize, mut f: F) -> Result<usize>
169 where
170 F: FnMut(Element<'_>),
171 {
172 let Some(at) = self.list_slot(key)? else {
173 return Ok(0);
174 };
175 let limits = self.list_limits;
176 let list = self
177 .lists
178 .get_mut(at)
179 .expect("the record points at its body");
180 let take = count.min(list.len());
181 for _ in 0..take {
182 let e = if end.is_left() {
187 list.front()
188 } else {
189 list.back()
190 };
191 f(e.expect("a list shorter than it says it is"));
192 if end.is_left() {
193 list.drop_front(&limits);
194 } else {
195 list.drop_back(&limits);
196 }
197 }
198 if list.is_empty() {
199 self.drop_key(key);
200 }
201 Ok(take)
202 }
203
204 pub fn llen(&mut self, key: &[u8]) -> Result<usize> {
206 Ok(match self.list_slot(key)? {
207 Some(at) => self.list_at(at).len(),
208 None => 0,
209 })
210 }
211
212 pub fn lindex(&mut self, key: &[u8], index: i64) -> Result<Option<Element<'_>>> {
214 let Some(slot) = self.list_slot(key)? else {
215 return Ok(None);
216 };
217 let list = self.list_at(slot);
218 Ok(at(index, list.len()).and_then(|i| list.get(i)))
219 }
220
221 pub fn lrange(
229 &mut self,
230 key: &[u8],
231 start: i64,
232 stop: i64,
233 ) -> Result<impl Iterator<Item = Element<'_>>> {
234 let slot = self.list_slot(key)?;
235 let list = slot.map(|at| self.list_at(at));
236 let (from, count) = match list {
237 Some(l) => window(start, stop, l.len()),
238 None => (0, 0),
239 };
240 Ok(list
241 .into_iter()
242 .flat_map(move |l| l.range(from, count))
243 .take(count))
244 }
245
246 pub fn lset(&mut self, key: &[u8], index: i64, value: &[u8]) -> Result<()> {
253 strings::check_len(key, value.len())?;
254 let Some(slot) = self.list_slot(key)? else {
255 return Err(Error::new(Code::Invalid, NO_KEY));
256 };
257 let limits = self.list_limits;
258 let list = self
259 .lists
260 .get_mut(slot)
261 .expect("the record points at its body");
262 let Some(i) = at(index, list.len()) else {
263 return Err(Error::new(Code::Invalid, OUT_OF_RANGE));
264 };
265 if !list.set(i, value, &limits) {
266 return Err(Error::new(Code::Invalid, OUT_OF_RANGE));
267 }
268 Ok(())
269 }
270
271 pub fn linsert(&mut self, key: &[u8], before: bool, pivot: &[u8], value: &[u8]) -> Result<i64> {
277 strings::check_len(key, value.len())?;
278 let Some(slot) = self.list_slot(key)? else {
279 return Ok(0);
280 };
281 let limits = self.list_limits;
282 let list = self
283 .lists
284 .get_mut(slot)
285 .expect("the record points at its body");
286 Ok(match list.insert_at_pivot(pivot, value, before, &limits) {
287 Some(len) => len as i64,
288 None => -1,
289 })
290 }
291
292 pub fn lrem(&mut self, key: &[u8], count: i64, value: &[u8]) -> Result<usize> {
298 let Some(slot) = self.list_slot(key)? else {
299 return Ok(0);
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 gone = list.remove(count, value, &limits);
307 if list.is_empty() {
308 self.drop_key(key);
309 }
310 Ok(gone)
311 }
312
313 pub fn ltrim(&mut self, key: &[u8], start: i64, stop: i64) -> Result<()> {
318 let Some(slot) = self.list_slot(key)? else {
319 return Ok(());
320 };
321 let limits = self.list_limits;
322 let list = self
323 .lists
324 .get_mut(slot)
325 .expect("the record points at its body");
326 let (from, count) = window(start, stop, list.len());
327 list.trim(from, count, &limits);
328 if list.is_empty() {
329 self.drop_key(key);
330 }
331 Ok(())
332 }
333
334 pub fn lpos(
347 &mut self,
348 key: &[u8],
349 value: &[u8],
350 rank: i64,
351 count: usize,
352 maxlen: usize,
353 out: &mut Vec<usize>,
354 ) -> Result<()> {
355 out.clear();
356 if rank == 0 {
357 return Err(Error::new(Code::Invalid, ZERO_RANK));
358 }
359 self.lpos_into(key, value, rank, count, maxlen, |at| out.push(at))?;
360 Ok(())
361 }
362
363 pub fn lpos_into<F>(
374 &mut self,
375 key: &[u8],
376 value: &[u8],
377 rank: i64,
378 count: usize,
379 maxlen: usize,
380 mut found: F,
381 ) -> Result<usize>
382 where
383 F: FnMut(usize),
384 {
385 if rank == 0 {
386 return Err(Error::new(Code::Invalid, ZERO_RANK));
387 }
388 let Some(slot) = self.list_slot(key)? else {
389 return Ok(0);
390 };
391 Ok(self
392 .list_at(slot)
393 .positions(value, rank, count, maxlen, &mut found))
394 }
395
396 pub fn lmove(&mut self, src: &[u8], dst: &[u8], from: End, to: End) -> Result<Option<&[u8]>> {
421 self.list_slot(dst)?;
425 let mut buf = std::mem::take(&mut self.scratch);
430 buf.clear();
431 let took = self.pop_into(src, from, 1, |e| e.write_to(&mut buf));
432 let moved = match took {
433 Ok(n) => n,
434 Err(e) => {
435 self.scratch = buf;
436 return Err(e);
437 }
438 };
439 if moved == 0 {
440 self.scratch = buf;
441 return Ok(None);
442 }
443 let pushed = self.push(dst, to, std::iter::once(buf.as_slice()));
444 self.scratch = buf;
445 pushed?;
446 Ok(Some(&self.scratch))
447 }
448
449 #[inline]
451 fn list_slot(&mut self, key: &[u8]) -> Result<Option<u32>> {
452 self.live_slot(key, Kind::List)
453 }
454
455 #[inline]
460 fn list_at(&self, at: u32) -> &List {
461 self.lists.get(at).expect("the record points at its body")
462 }
463
464 fn new_list(&mut self, key: &[u8]) -> u32 {
470 let at = yo_alloc::first_touch(|| self.lists.insert(List::new()));
474 let len = value::slot_record_len(false);
475 self.write_rec(key, len, |out| {
476 value::write_slot_record(out, Kind::List, at, None);
477 });
478 self.bodies += 1;
479 at
480 }
481}
482
483#[inline]
489fn at(index: i64, len: usize) -> Option<usize> {
490 let i = if index < 0 { len as i64 + index } else { index };
491 (i >= 0 && (i as usize) < len).then_some(i as usize)
492}
493
494#[inline]
500fn window(start: i64, stop: i64, len: usize) -> (usize, usize) {
501 let n = len as i64;
502 let from = if start < 0 { (n + start).max(0) } else { start };
503 let to = if stop < 0 { n + stop } else { stop.min(n - 1) };
504 if from > to || from >= n || to < 0 {
505 return (0, 0);
506 }
507 (from as usize, (to - from + 1) as usize)
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513 use crate::Clock;
514 use crate::list::Encoding;
515 use yo_common::Code;
516
517 fn db() -> Keyspace {
518 Keyspace::with_clock(Clock::fixed(1_000))
519 }
520
521 fn rpush(d: &mut Keyspace, key: &[u8], values: &[&[u8]]) -> usize {
522 d.push(key, End::Right, values.iter().copied())
523 .expect("a list")
524 }
525
526 fn all(d: &mut Keyspace, key: &[u8]) -> Vec<String> {
527 d.lrange(key, 0, -1)
528 .expect("a list")
529 .map(|e| String::from_utf8(e.to_vec()).expect("utf8 in these tests"))
530 .collect()
531 }
532
533 #[test]
534 fn pushing_to_a_key_that_is_not_there_makes_it() {
535 let mut d = db();
536 assert_eq!(rpush(&mut d, b"l", &[b"a", b"b", b"c"]), 3);
537 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
538 assert_eq!(d.llen(b"l").expect("a list"), 3);
539 }
540
541 #[test]
542 fn lpush_puts_the_last_element_in_front() {
543 let mut d = db();
544 d.push(b"l", End::Left, [b"a".as_slice(), b"b", b"c"].into_iter())
545 .expect("a list");
546 assert_eq!(all(&mut d, b"l"), ["c", "b", "a"]);
547 }
548
549 #[test]
550 fn pushing_nothing_does_not_make_a_key() {
551 let mut d = db();
552 let none: [&[u8]; 0] = [];
553 assert_eq!(d.push(b"l", End::Left, none.into_iter()).expect("ok"), 0);
554 assert_eq!(d.kind_of(b"l"), None);
555 }
556
557 #[test]
558 fn pushx_only_pushes_to_a_list_that_is_there() {
559 let mut d = db();
560 assert_eq!(
561 d.pushx(b"l", End::Right, [b"a".as_slice()].into_iter())
562 .expect("ok"),
563 0
564 );
565 assert_eq!(d.kind_of(b"l"), None);
566 rpush(&mut d, b"l", &[b"a"]);
567 assert_eq!(
568 d.pushx(b"l", End::Right, [b"b".as_slice()].into_iter())
569 .expect("ok"),
570 2
571 );
572 }
573
574 #[test]
575 fn popping_the_last_element_takes_the_key_with_it() {
576 let mut d = db();
577 rpush(&mut d, b"l", &[b"only"]);
578 assert_eq!(
579 d.pop(b"l", End::Left).expect("ok").as_deref(),
580 Some(&b"only"[..])
581 );
582 assert_eq!(d.kind_of(b"l"), None);
583 assert_eq!(d.pop(b"l", End::Left).expect("ok"), None);
584 }
585
586 #[test]
587 fn a_count_pop_takes_from_the_end_it_was_asked_for() {
588 let mut d = db();
589 rpush(&mut d, b"l", &[b"a", b"b", b"c", b"d"]);
590 let mut got = Vec::new();
591 d.pop_into(b"l", End::Right, 2, |e| got.push(e.to_vec()))
592 .expect("a list");
593 assert_eq!(got, [b"d".to_vec(), b"c".to_vec()]);
594 assert_eq!(all(&mut d, b"l"), ["a", "b"]);
595 }
596
597 #[test]
598 fn a_count_pop_larger_than_the_list_empties_it() {
599 let mut d = db();
600 rpush(&mut d, b"l", &[b"a", b"b"]);
601 let mut n = 0;
602 assert_eq!(
603 d.pop_into(b"l", End::Left, 99, |_| n += 1).expect("a list"),
604 2
605 );
606 assert_eq!(n, 2);
607 assert_eq!(d.kind_of(b"l"), None);
608 }
609
610 #[test]
611 fn lrange_clamps_at_both_ends() {
612 let mut d = db();
613 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
614 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
615 let got: Vec<_> = d
616 .lrange(b"l", -100, 100)
617 .expect("a list")
618 .map(|e| e.to_vec())
619 .collect();
620 assert_eq!(got.len(), 3);
621 assert_eq!(d.lrange(b"l", 2, 1).expect("a list").count(), 0);
622 assert_eq!(d.lrange(b"l", 5, 9).expect("a list").count(), 0);
623 assert_eq!(d.lrange(b"nope", 0, -1).expect("no key").count(), 0);
624 }
625
626 #[test]
627 fn lindex_counts_from_the_back_when_it_is_negative() {
628 let mut d = db();
629 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
630 assert_eq!(
631 d.lindex(b"l", 0).expect("ok").map(|e| e.to_vec()),
632 Some(b"a".to_vec())
633 );
634 assert_eq!(
635 d.lindex(b"l", -1).expect("ok").map(|e| e.to_vec()),
636 Some(b"c".to_vec())
637 );
638 assert!(d.lindex(b"l", 3).expect("ok").is_none());
639 assert!(d.lindex(b"l", -4).expect("ok").is_none());
640 assert!(d.lindex(b"nope", 0).expect("ok").is_none());
641 }
642
643 #[test]
644 fn lset_says_which_of_the_two_ways_it_missed() {
645 let mut d = db();
646 let e = d.lset(b"nope", 0, b"x").expect_err("no key");
647 assert_eq!(e.message(), NO_KEY);
648 rpush(&mut d, b"l", &[b"a", b"b"]);
649 d.lset(b"l", -1, b"z").expect("in range");
650 assert_eq!(all(&mut d, b"l"), ["a", "z"]);
651 let e = d.lset(b"l", 9, b"x").expect_err("out of range");
652 assert_eq!(e.message(), OUT_OF_RANGE);
653 }
654
655 #[test]
656 fn linsert_has_three_answers() {
657 let mut d = db();
658 assert_eq!(d.linsert(b"nope", true, b"a", b"x").expect("ok"), 0);
659 rpush(&mut d, b"l", &[b"a", b"c"]);
660 assert_eq!(d.linsert(b"l", true, b"c", b"b").expect("ok"), 3);
661 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
662 assert_eq!(d.linsert(b"l", false, b"zz", b"x").expect("ok"), -1);
663 }
664
665 #[test]
666 fn lrem_counts_from_the_end_the_sign_says() {
667 let mut d = db();
668 rpush(&mut d, b"l", &[b"a", b"x", b"b", b"x", b"c", b"x"]);
669 assert_eq!(d.lrem(b"l", 1, b"x").expect("ok"), 1);
670 assert_eq!(all(&mut d, b"l"), ["a", "b", "x", "c", "x"]);
671 assert_eq!(d.lrem(b"l", -1, b"x").expect("ok"), 1);
672 assert_eq!(all(&mut d, b"l"), ["a", "b", "x", "c"]);
673 assert_eq!(d.lrem(b"l", 0, b"x").expect("ok"), 1);
674 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
675 }
676
677 #[test]
678 fn removing_everything_takes_the_key() {
679 let mut d = db();
680 rpush(&mut d, b"l", &[b"x", b"x"]);
681 assert_eq!(d.lrem(b"l", 0, b"x").expect("ok"), 2);
682 assert_eq!(d.kind_of(b"l"), None);
683 }
684
685 #[test]
686 fn ltrim_keeps_the_window() {
687 let mut d = db();
688 rpush(&mut d, b"l", &[b"a", b"b", b"c", b"d", b"e"]);
689 d.ltrim(b"l", 1, -2).expect("ok");
690 assert_eq!(all(&mut d, b"l"), ["b", "c", "d"]);
691 }
692
693 #[test]
694 fn an_empty_window_deletes_the_key() {
695 let mut d = db();
696 rpush(&mut d, b"l", &[b"a", b"b"]);
697 d.ltrim(b"l", 1, 0).expect("ok");
698 assert_eq!(d.kind_of(b"l"), None);
699 d.ltrim(b"nope", 0, -1).expect("no key is not an error");
700 }
701
702 #[test]
703 fn lpos_answers_where_and_how_many() {
704 let mut d = db();
705 rpush(&mut d, b"l", &[b"a", b"b", b"c", b"b", b"b"]);
706 let mut out = Vec::new();
707 d.lpos(b"l", b"b", 1, 0, 0, &mut out).expect("ok");
708 assert_eq!(out, [1, 3, 4]);
709 d.lpos(b"l", b"b", -1, 2, 0, &mut out).expect("ok");
710 assert_eq!(out, [4, 3]);
711 d.lpos(b"l", b"b", 1, 0, 2, &mut out).expect("ok");
712 assert_eq!(out, [1]);
713 d.lpos(b"l", b"zz", 1, 0, 0, &mut out).expect("ok");
714 assert!(out.is_empty());
715 d.lpos(b"nope", b"b", 1, 0, 0, &mut out).expect("ok");
716 assert!(out.is_empty());
717 }
718
719 #[test]
720 fn a_rank_of_zero_is_an_error() {
721 let mut d = db();
722 rpush(&mut d, b"l", &[b"a"]);
723 let e = d
724 .lpos(b"l", b"a", 0, 0, 0, &mut Vec::new())
725 .expect_err("zero");
726 assert_eq!(e.message(), ZERO_RANK);
727 }
728
729 #[test]
730 fn lmove_between_two_keys_makes_the_second_one() {
731 let mut d = db();
732 rpush(&mut d, b"src", &[b"a", b"b"]);
733 let got = d.lmove(b"src", b"dst", End::Right, End::Left).expect("ok");
734 assert_eq!(got, Some(&b"b"[..]));
735 assert_eq!(all(&mut d, b"src"), ["a"]);
736 assert_eq!(all(&mut d, b"dst"), ["b"]);
737 }
738
739 #[test]
740 fn lmove_onto_itself_rotates() {
741 let mut d = db();
742 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
743 d.lmove(b"l", b"l", End::Right, End::Left).expect("ok");
744 assert_eq!(all(&mut d, b"l"), ["c", "a", "b"]);
745 d.lmove(b"l", b"l", End::Left, End::Right).expect("ok");
746 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
747 }
748
749 #[test]
750 fn lmove_from_a_key_that_is_not_there_does_nothing() {
751 let mut d = db();
752 assert_eq!(
753 d.lmove(b"nope", b"dst", End::Left, End::Left).expect("ok"),
754 None
755 );
756 assert_eq!(d.kind_of(b"dst"), None);
757 }
758
759 #[test]
763 fn lmove_stops_allocating_once_its_buffer_is_grown() {
764 let mut d = db();
765 rpush(&mut d, b"q", &[b"a", b"b", b"c"]);
766 d.lmove(b"q", b"q", End::Right, End::Left).expect("ok");
769 let (_, allocs) = crate::tally::counted(|| {
770 for _ in 0..100 {
771 d.lmove(b"q", b"q", End::Right, End::Left).expect("ok");
772 }
773 });
774 assert_eq!(allocs, 0, "lmove allocated {allocs} times in a hundred");
775 assert_eq!(all(&mut d, b"q"), ["b", "c", "a"]);
776 }
777
778 #[test]
782 fn lrem_does_not_allocate_to_remove_a_handful() {
783 let mut d = db();
784 let many: Vec<&[u8]> = (0..100).map(|_| b"gone".as_slice()).collect();
788 rpush(&mut d, b"l", &many);
789 rpush(&mut d, b"l", &[b"keep"]);
790 let (_, allocs) = crate::tally::counted(|| {
791 for _ in 0..100 {
792 assert_eq!(d.lrem(b"l", 1, b"gone").expect("a list"), 1);
793 }
794 });
795 assert_eq!(allocs, 0, "lrem allocated {allocs} times in a hundred");
796 assert_eq!(all(&mut d, b"l"), ["keep"]);
797 }
798
799 #[test]
801 fn lrem_with_more_hits_than_fit_inline_still_removes_all_of_them() {
802 let mut d = db();
803 let many: Vec<&[u8]> = (0..40).map(|_| b"x".as_slice()).collect();
804 rpush(&mut d, b"l", &many);
805 rpush(&mut d, b"l", &[b"keep"]);
806 assert_eq!(d.lrem(b"l", 0, b"x").expect("a list"), 40);
807 assert_eq!(all(&mut d, b"l"), ["keep"]);
808 }
809
810 #[test]
811 fn lmove_checks_the_destination_before_taking_anything() {
812 let mut d = db();
813 rpush(&mut d, b"src", &[b"a"]);
814 d.set_plain(b"dst", b"a string").expect("room");
815 let e = d
816 .lmove(b"src", b"dst", End::Left, End::Left)
817 .expect_err("the destination is a string");
818 assert_eq!(e.code(), Code::WrongType);
819 assert_eq!(all(&mut d, b"src"), ["a"]);
820 }
821
822 #[test]
823 fn every_command_says_wrongtype_against_a_string() {
824 let mut d = db();
825 d.set_plain(b"s", b"a string").expect("room");
826 assert_eq!(
827 d.push(b"s", End::Left, [b"x".as_slice()].into_iter())
828 .expect_err("a string")
829 .code(),
830 Code::WrongType
831 );
832 assert_eq!(d.llen(b"s").expect_err("a string").code(), Code::WrongType);
833 assert_eq!(
834 d.pop(b"s", End::Left).expect_err("a string").code(),
835 Code::WrongType
836 );
837 assert_eq!(
838 d.lset(b"s", 0, b"x").expect_err("a string").code(),
839 Code::WrongType
840 );
841 assert_eq!(
842 d.ltrim(b"s", 0, -1).expect_err("a string").code(),
843 Code::WrongType
844 );
845 }
846
847 #[test]
848 fn a_list_is_a_list_to_the_rest_of_the_keyspace() {
849 let mut d = db();
850 rpush(&mut d, b"l", &[b"a"]);
851 assert_eq!(d.kind_of(b"l").map(|k| k.name()), Some("list"));
852 assert_eq!(d.encoding_name(b"l"), Some(Encoding::Listpack.name()));
853 assert!(d.exists(b"l"));
854 assert!(d.drop_key(b"l"));
855 assert_eq!(d.kind_of(b"l"), None);
856 }
857
858 #[test]
859 fn a_list_can_be_given_a_deadline_and_reaped() {
860 let mut d = Keyspace::with_clock(Clock::fixed(1_000));
861 rpush(&mut d, b"l", &[b"a", b"b"]);
862 assert!(d.set_expiry(b"l", Some(1_500)));
863 assert_eq!(all(&mut d, b"l"), ["a", "b"]);
864 d.clock_mut().advance(1_000);
865 assert_eq!(d.llen(b"l").expect("gone"), 0);
866 assert_eq!(d.kind_of(b"l"), None);
867 }
868
869 #[test]
870 fn a_big_list_is_chunked_and_still_answers_the_same() {
871 let mut d = db();
872 let value = vec![b'x'; 400];
873 for i in 0..40 {
874 let mut v = value.clone();
875 v.extend_from_slice(format!("{i}").as_bytes());
876 d.push(b"l", End::Right, [v.as_slice()].into_iter())
877 .expect("a list");
878 }
879 assert_eq!(d.encoding_name(b"l"), Some(Encoding::Quicklist.name()));
880 assert_eq!(d.llen(b"l").expect("a list"), 40);
881 let last = d.lindex(b"l", -1).expect("ok").expect("in range").to_vec();
882 assert!(last.ends_with(b"39"));
883 d.ltrim(b"l", 0, 0).expect("ok");
884 assert_eq!(d.llen(b"l").expect("a list"), 1);
885 assert_eq!(d.encoding_name(b"l"), Some(Encoding::Listpack.name()));
886 }
887}