1use 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
32const NO_KEY: &str = "no such key";
36
37const OUT_OF_RANGE: &str = "index out of range";
39
40const 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum End {
55 Left,
57 Right,
59}
60
61impl End {
62 #[inline]
64 #[must_use]
65 pub const fn is_left(self) -> bool {
66 matches!(self, End::Left)
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum Order {
80 OneByOne,
84 Bulk,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100pub struct Movem {
101 pub from: End,
103 pub to: End,
105 pub count: usize,
107 pub exactly: bool,
109 pub order: Order,
111}
112
113impl Keyspace {
114 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn lmove(&mut self, src: &[u8], dst: &[u8], from: End, to: End) -> Result<Option<&[u8]>> {
465 self.list_slot(dst)?;
469 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 pub fn lmovem<F>(&mut self, src: &[u8], dst: &[u8], b: Movem, f: F) -> Result<usize>
521 where
522 F: FnMut(&[u8]),
523 {
524 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 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 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 #[inline]
617 fn list_slot(&mut self, key: &[u8]) -> Result<Option<u32>> {
618 self.live_slot(key, Kind::List)
619 }
620
621 #[inline]
626 fn list_at(&self, at: u32) -> &List {
627 self.lists.get(at).expect("the record points at its body")
628 }
629
630 fn new_list(&mut self, key: &[u8]) -> u32 {
636 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
649impl Db {
650 pub fn lmove<F>(&self, src: &[u8], dst: &[u8], from: End, to: End, f: F) -> Result<bool>
662 where
663 F: FnOnce(&[u8]),
664 {
665 let (home, onto) = (self.stripe_of(src), self.stripe_of(dst));
666 if home == onto {
667 return match self.hold_stripe(home).lmove(src, dst, from, to)? {
668 Some(moved) => {
669 f(moved);
670 Ok(true)
671 }
672 None => Ok(false),
673 };
674 }
675 self.hold_stripe(onto).list_slot(dst)?;
678 let mut spare = self.spare();
681 let buf = &mut spare.bytes;
682 buf.clear();
683 let moved = self
684 .hold_stripe(home)
685 .pop_into(src, from, 1, |e| e.write_to(buf))?;
686 if moved == 0 {
687 return Ok(false);
688 }
689 self.hold_stripe(onto)
690 .push(dst, to, std::iter::once(buf.as_slice()))?;
691 f(buf);
692 Ok(true)
693 }
694
695 pub fn lmovem<F>(&self, src: &[u8], dst: &[u8], b: Movem, f: F) -> Result<usize>
708 where
709 F: FnMut(&[u8]),
710 {
711 let (home, onto) = (self.stripe_of(src), self.stripe_of(dst));
712 if home == onto {
713 return self.hold_stripe(home).lmovem(src, dst, b, f);
714 }
715 self.hold_stripe(onto).list_slot(dst)?;
718 let have = self.hold_stripe(home).llen(src)?;
719 if have == 0 || (b.exactly && have < b.count) {
720 return Ok(0);
721 }
722 let mut spare = self.spare();
725 let spare = &mut *spare;
726 let (buf, ends) = (&mut spare.bytes, &mut spare.rows);
727 buf.clear();
728 ends.clear();
729 let moved = self.hold_stripe(home).pop_into(src, b.from, b.count, |e| {
730 e.write_to(buf);
731 ends.push(buf.len());
732 })?;
733 self.hold_stripe(onto)
734 .push_block(dst, b, moved, buf, ends, f)?;
735 Ok(moved)
736 }
737}
738
739#[inline]
745fn at(index: i64, len: usize) -> Option<usize> {
746 let i = if index < 0 { len as i64 + index } else { index };
747 (i >= 0 && (i as usize) < len).then_some(i as usize)
748}
749
750#[inline]
756fn window(start: i64, stop: i64, len: usize) -> (usize, usize) {
757 let n = len as i64;
758 let from = if start < 0 { (n + start).max(0) } else { start };
759 let to = if stop < 0 { n + stop } else { stop.min(n - 1) };
760 if from > to || from >= n || to < 0 {
761 return (0, 0);
762 }
763 (from as usize, (to - from + 1) as usize)
764}
765
766#[cfg(test)]
767mod tests {
768 use super::*;
769 use crate::Clock;
770 use crate::list::Encoding;
771 use yo_common::Code;
772
773 fn db() -> Keyspace {
774 Keyspace::with_clock(Clock::fixed(1_000))
775 }
776
777 fn rpush(d: &mut Keyspace, key: &[u8], values: &[&[u8]]) -> usize {
778 d.push(key, End::Right, values.iter().copied())
779 .expect("a list")
780 }
781
782 fn all(d: &mut Keyspace, key: &[u8]) -> Vec<String> {
783 d.lrange(key, 0, -1)
784 .expect("a list")
785 .map(|e| String::from_utf8(e.to_vec()).expect("utf8 in these tests"))
786 .collect()
787 }
788
789 #[test]
790 fn pushing_to_a_key_that_is_not_there_makes_it() {
791 let mut d = db();
792 assert_eq!(rpush(&mut d, b"l", &[b"a", b"b", b"c"]), 3);
793 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
794 assert_eq!(d.llen(b"l").expect("a list"), 3);
795 }
796
797 #[test]
798 fn lpush_puts_the_last_element_in_front() {
799 let mut d = db();
800 d.push(b"l", End::Left, [b"a".as_slice(), b"b", b"c"].into_iter())
801 .expect("a list");
802 assert_eq!(all(&mut d, b"l"), ["c", "b", "a"]);
803 }
804
805 #[test]
806 fn pushing_nothing_does_not_make_a_key() {
807 let mut d = db();
808 let none: [&[u8]; 0] = [];
809 assert_eq!(d.push(b"l", End::Left, none.into_iter()).expect("ok"), 0);
810 assert_eq!(d.kind_of(b"l"), None);
811 }
812
813 #[test]
814 fn pushx_only_pushes_to_a_list_that_is_there() {
815 let mut d = db();
816 assert_eq!(
817 d.pushx(b"l", End::Right, [b"a".as_slice()].into_iter())
818 .expect("ok"),
819 0
820 );
821 assert_eq!(d.kind_of(b"l"), None);
822 rpush(&mut d, b"l", &[b"a"]);
823 assert_eq!(
824 d.pushx(b"l", End::Right, [b"b".as_slice()].into_iter())
825 .expect("ok"),
826 2
827 );
828 }
829
830 #[test]
831 fn popping_the_last_element_takes_the_key_with_it() {
832 let mut d = db();
833 rpush(&mut d, b"l", &[b"only"]);
834 assert_eq!(
835 d.pop(b"l", End::Left).expect("ok").as_deref(),
836 Some(&b"only"[..])
837 );
838 assert_eq!(d.kind_of(b"l"), None);
839 assert_eq!(d.pop(b"l", End::Left).expect("ok"), None);
840 }
841
842 #[test]
843 fn a_count_pop_takes_from_the_end_it_was_asked_for() {
844 let mut d = db();
845 rpush(&mut d, b"l", &[b"a", b"b", b"c", b"d"]);
846 let mut got = Vec::new();
847 d.pop_into(b"l", End::Right, 2, |e| got.push(e.to_vec()))
848 .expect("a list");
849 assert_eq!(got, [b"d".to_vec(), b"c".to_vec()]);
850 assert_eq!(all(&mut d, b"l"), ["a", "b"]);
851 }
852
853 #[test]
854 fn a_count_pop_larger_than_the_list_empties_it() {
855 let mut d = db();
856 rpush(&mut d, b"l", &[b"a", b"b"]);
857 let mut n = 0;
858 assert_eq!(
859 d.pop_into(b"l", End::Left, 99, |_| n += 1).expect("a list"),
860 2
861 );
862 assert_eq!(n, 2);
863 assert_eq!(d.kind_of(b"l"), None);
864 }
865
866 #[test]
867 fn lrange_clamps_at_both_ends() {
868 let mut d = db();
869 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
870 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
871 let got: Vec<_> = d
872 .lrange(b"l", -100, 100)
873 .expect("a list")
874 .map(|e| e.to_vec())
875 .collect();
876 assert_eq!(got.len(), 3);
877 assert_eq!(d.lrange(b"l", 2, 1).expect("a list").count(), 0);
878 assert_eq!(d.lrange(b"l", 5, 9).expect("a list").count(), 0);
879 assert_eq!(d.lrange(b"nope", 0, -1).expect("no key").count(), 0);
880 }
881
882 #[test]
883 fn lindex_counts_from_the_back_when_it_is_negative() {
884 let mut d = db();
885 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
886 assert_eq!(
887 d.lindex(b"l", 0).expect("ok").map(|e| e.to_vec()),
888 Some(b"a".to_vec())
889 );
890 assert_eq!(
891 d.lindex(b"l", -1).expect("ok").map(|e| e.to_vec()),
892 Some(b"c".to_vec())
893 );
894 assert!(d.lindex(b"l", 3).expect("ok").is_none());
895 assert!(d.lindex(b"l", -4).expect("ok").is_none());
896 assert!(d.lindex(b"nope", 0).expect("ok").is_none());
897 }
898
899 #[test]
900 fn lset_says_which_of_the_two_ways_it_missed() {
901 let mut d = db();
902 let e = d.lset(b"nope", 0, b"x").expect_err("no key");
903 assert_eq!(e.message(), NO_KEY);
904 rpush(&mut d, b"l", &[b"a", b"b"]);
905 d.lset(b"l", -1, b"z").expect("in range");
906 assert_eq!(all(&mut d, b"l"), ["a", "z"]);
907 let e = d.lset(b"l", 9, b"x").expect_err("out of range");
908 assert_eq!(e.message(), OUT_OF_RANGE);
909 }
910
911 #[test]
912 fn linsert_has_three_answers() {
913 let mut d = db();
914 assert_eq!(d.linsert(b"nope", true, b"a", b"x").expect("ok"), 0);
915 rpush(&mut d, b"l", &[b"a", b"c"]);
916 assert_eq!(d.linsert(b"l", true, b"c", b"b").expect("ok"), 3);
917 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
918 assert_eq!(d.linsert(b"l", false, b"zz", b"x").expect("ok"), -1);
919 }
920
921 #[test]
922 fn lrem_counts_from_the_end_the_sign_says() {
923 let mut d = db();
924 rpush(&mut d, b"l", &[b"a", b"x", b"b", b"x", b"c", b"x"]);
925 assert_eq!(d.lrem(b"l", 1, b"x").expect("ok"), 1);
926 assert_eq!(all(&mut d, b"l"), ["a", "b", "x", "c", "x"]);
927 assert_eq!(d.lrem(b"l", -1, b"x").expect("ok"), 1);
928 assert_eq!(all(&mut d, b"l"), ["a", "b", "x", "c"]);
929 assert_eq!(d.lrem(b"l", 0, b"x").expect("ok"), 1);
930 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
931 }
932
933 #[test]
934 fn removing_everything_takes_the_key() {
935 let mut d = db();
936 rpush(&mut d, b"l", &[b"x", b"x"]);
937 assert_eq!(d.lrem(b"l", 0, b"x").expect("ok"), 2);
938 assert_eq!(d.kind_of(b"l"), None);
939 }
940
941 #[test]
942 fn ltrim_keeps_the_window() {
943 let mut d = db();
944 rpush(&mut d, b"l", &[b"a", b"b", b"c", b"d", b"e"]);
945 d.ltrim(b"l", 1, -2).expect("ok");
946 assert_eq!(all(&mut d, b"l"), ["b", "c", "d"]);
947 }
948
949 #[test]
950 fn an_empty_window_deletes_the_key() {
951 let mut d = db();
952 rpush(&mut d, b"l", &[b"a", b"b"]);
953 d.ltrim(b"l", 1, 0).expect("ok");
954 assert_eq!(d.kind_of(b"l"), None);
955 d.ltrim(b"nope", 0, -1).expect("no key is not an error");
956 }
957
958 #[test]
959 fn lpos_answers_where_and_how_many() {
960 let mut d = db();
961 rpush(&mut d, b"l", &[b"a", b"b", b"c", b"b", b"b"]);
962 let mut out = Vec::new();
963 d.lpos(b"l", b"b", 1, 0, 0, &mut out).expect("ok");
964 assert_eq!(out, [1, 3, 4]);
965 d.lpos(b"l", b"b", -1, 2, 0, &mut out).expect("ok");
966 assert_eq!(out, [4, 3]);
967 d.lpos(b"l", b"b", 1, 0, 2, &mut out).expect("ok");
968 assert_eq!(out, [1]);
969 d.lpos(b"l", b"zz", 1, 0, 0, &mut out).expect("ok");
970 assert!(out.is_empty());
971 d.lpos(b"nope", b"b", 1, 0, 0, &mut out).expect("ok");
972 assert!(out.is_empty());
973 }
974
975 #[test]
976 fn a_rank_of_zero_is_an_error() {
977 let mut d = db();
978 rpush(&mut d, b"l", &[b"a"]);
979 let e = d
980 .lpos(b"l", b"a", 0, 0, 0, &mut Vec::new())
981 .expect_err("zero");
982 assert_eq!(e.message(), ZERO_RANK);
983 }
984
985 #[test]
992 fn lmovem_orders_the_block_four_ways() {
993 use End::{Left, Right};
994 use Order::{Bulk, OneByOne};
995 for (from, to, order, want, left) in [
996 (Left, Right, OneByOne, ["a", "b"], ["c", "d", "e"]),
997 (Left, Right, Bulk, ["a", "b"], ["c", "d", "e"]),
998 (Left, Left, OneByOne, ["b", "a"], ["c", "d", "e"]),
999 (Left, Left, Bulk, ["a", "b"], ["c", "d", "e"]),
1000 (Right, Left, OneByOne, ["d", "e"], ["a", "b", "c"]),
1001 (Right, Left, Bulk, ["d", "e"], ["a", "b", "c"]),
1002 (Right, Right, OneByOne, ["e", "d"], ["a", "b", "c"]),
1003 (Right, Right, Bulk, ["d", "e"], ["a", "b", "c"]),
1004 ] {
1005 let mut d = db();
1006 rpush(&mut d, b"s", &[b"a", b"b", b"c", b"d", b"e"]);
1007 let mut got = Vec::new();
1008 let b = Movem {
1009 from,
1010 to,
1011 count: 2,
1012 exactly: false,
1013 order,
1014 };
1015 let n = d
1016 .lmovem(b"s", b"t", b, |v| {
1017 got.push(String::from_utf8(v.to_vec()).expect("utf8 in these tests"));
1018 })
1019 .expect("two lists");
1020 let how = format!("{from:?} {to:?} {order:?}");
1021 assert_eq!(n, 2, "{how}");
1022 assert_eq!(got, want, "the reply for {how}");
1023 assert_eq!(all(&mut d, b"t"), want, "the destination for {how}");
1024 assert_eq!(all(&mut d, b"s"), left, "what is left for {how}");
1025 }
1026 }
1027
1028 fn block(from: End, to: End, count: usize, exactly: bool) -> Movem {
1031 Movem {
1032 from,
1033 to,
1034 count,
1035 exactly,
1036 order: Order::Bulk,
1037 }
1038 }
1039
1040 #[test]
1043 fn lmovem_exactly_takes_all_of_them_or_none() {
1044 let mut d = db();
1045 rpush(&mut d, b"s", &[b"a", b"b", b"c"]);
1046 let all_of_four = block(End::Left, End::Right, 4, true);
1047 let n = d
1048 .lmovem(b"s", b"t", all_of_four, |_| {
1049 panic!("nothing should have moved")
1050 })
1051 .expect("two lists");
1052 assert_eq!(n, 0);
1053 assert_eq!(
1054 all(&mut d, b"s"),
1055 ["a", "b", "c"],
1056 "the source is untouched"
1057 );
1058 assert_eq!(d.llen(b"t").expect("a list"), 0, "and nothing was made");
1059
1060 let mut got = Vec::new();
1062 let up_to_four = block(End::Left, End::Right, 4, false);
1063 let n = d
1064 .lmovem(b"s", b"t", up_to_four, |v| got.push(v.to_vec()))
1065 .expect("two lists");
1066 assert_eq!(n, 3);
1067 assert_eq!(got.len(), 3);
1068 assert!(!d.exists(b"s"), "an emptied source goes");
1069 }
1070
1071 #[test]
1074 fn lmovem_onto_itself_rotates_by_the_count() {
1075 let mut d = db();
1076 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
1077 d.lmovem(b"l", b"l", block(End::Left, End::Right, 2, false), |_| {})
1078 .expect("a list");
1079 assert_eq!(all(&mut d, b"l"), ["c", "a", "b"]);
1080
1081 let mut d = db();
1082 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
1083 d.lmovem(b"l", b"l", block(End::Right, End::Left, 2, false), |_| {})
1084 .expect("a list");
1085 assert_eq!(all(&mut d, b"l"), ["b", "c", "a"]);
1086 }
1087
1088 #[test]
1091 fn lmovem_checks_the_destination_before_taking_anything() {
1092 let mut d = db();
1093 rpush(&mut d, b"s", &[b"a", b"b"]);
1094 d.set_plain(b"str", b"v").expect("room");
1095 let two = block(End::Left, End::Right, 2, false);
1096 let e = d
1097 .lmovem(b"s", b"str", two, |_| {})
1098 .expect_err("the destination is a string");
1099 assert_eq!(e.code(), Code::WrongType);
1100 assert_eq!(all(&mut d, b"s"), ["a", "b"], "the source is untouched");
1101
1102 let n = d
1103 .lmovem(b"nope", b"t", two, |_| {})
1104 .expect("a source that is not there is not an error");
1105 assert_eq!(n, 0);
1106 }
1107
1108 #[test]
1109 fn lmove_between_two_keys_makes_the_second_one() {
1110 let mut d = db();
1111 rpush(&mut d, b"src", &[b"a", b"b"]);
1112 let got = d.lmove(b"src", b"dst", End::Right, End::Left).expect("ok");
1113 assert_eq!(got, Some(&b"b"[..]));
1114 assert_eq!(all(&mut d, b"src"), ["a"]);
1115 assert_eq!(all(&mut d, b"dst"), ["b"]);
1116 }
1117
1118 #[test]
1119 fn lmove_onto_itself_rotates() {
1120 let mut d = db();
1121 rpush(&mut d, b"l", &[b"a", b"b", b"c"]);
1122 d.lmove(b"l", b"l", End::Right, End::Left).expect("ok");
1123 assert_eq!(all(&mut d, b"l"), ["c", "a", "b"]);
1124 d.lmove(b"l", b"l", End::Left, End::Right).expect("ok");
1125 assert_eq!(all(&mut d, b"l"), ["a", "b", "c"]);
1126 }
1127
1128 #[test]
1129 fn lmove_from_a_key_that_is_not_there_does_nothing() {
1130 let mut d = db();
1131 assert_eq!(
1132 d.lmove(b"nope", b"dst", End::Left, End::Left).expect("ok"),
1133 None
1134 );
1135 assert_eq!(d.kind_of(b"dst"), None);
1136 }
1137
1138 #[test]
1142 fn lmove_stops_allocating_once_its_buffer_is_grown() {
1143 let mut d = db();
1144 rpush(&mut d, b"q", &[b"a", b"b", b"c"]);
1145 d.lmove(b"q", b"q", End::Right, End::Left).expect("ok");
1148 let (_, allocs) = crate::tally::counted(|| {
1149 for _ in 0..100 {
1150 d.lmove(b"q", b"q", End::Right, End::Left).expect("ok");
1151 }
1152 });
1153 assert_eq!(allocs, 0, "lmove allocated {allocs} times in a hundred");
1154 assert_eq!(all(&mut d, b"q"), ["b", "c", "a"]);
1155 }
1156
1157 #[test]
1161 fn lrem_does_not_allocate_to_remove_a_handful() {
1162 let mut d = db();
1163 let many: Vec<&[u8]> = (0..100).map(|_| b"gone".as_slice()).collect();
1167 rpush(&mut d, b"l", &many);
1168 rpush(&mut d, b"l", &[b"keep"]);
1169 let (_, allocs) = crate::tally::counted(|| {
1170 for _ in 0..100 {
1171 assert_eq!(d.lrem(b"l", 1, b"gone").expect("a list"), 1);
1172 }
1173 });
1174 assert_eq!(allocs, 0, "lrem allocated {allocs} times in a hundred");
1175 assert_eq!(all(&mut d, b"l"), ["keep"]);
1176 }
1177
1178 #[test]
1180 fn lrem_with_more_hits_than_fit_inline_still_removes_all_of_them() {
1181 let mut d = db();
1182 let many: Vec<&[u8]> = (0..40).map(|_| b"x".as_slice()).collect();
1183 rpush(&mut d, b"l", &many);
1184 rpush(&mut d, b"l", &[b"keep"]);
1185 assert_eq!(d.lrem(b"l", 0, b"x").expect("a list"), 40);
1186 assert_eq!(all(&mut d, b"l"), ["keep"]);
1187 }
1188
1189 #[test]
1190 fn lmove_checks_the_destination_before_taking_anything() {
1191 let mut d = db();
1192 rpush(&mut d, b"src", &[b"a"]);
1193 d.set_plain(b"dst", b"a string").expect("room");
1194 let e = d
1195 .lmove(b"src", b"dst", End::Left, End::Left)
1196 .expect_err("the destination is a string");
1197 assert_eq!(e.code(), Code::WrongType);
1198 assert_eq!(all(&mut d, b"src"), ["a"]);
1199 }
1200
1201 #[test]
1202 fn every_command_says_wrongtype_against_a_string() {
1203 let mut d = db();
1204 d.set_plain(b"s", b"a string").expect("room");
1205 assert_eq!(
1206 d.push(b"s", End::Left, [b"x".as_slice()].into_iter())
1207 .expect_err("a string")
1208 .code(),
1209 Code::WrongType
1210 );
1211 assert_eq!(d.llen(b"s").expect_err("a string").code(), Code::WrongType);
1212 assert_eq!(
1213 d.pop(b"s", End::Left).expect_err("a string").code(),
1214 Code::WrongType
1215 );
1216 assert_eq!(
1217 d.lset(b"s", 0, b"x").expect_err("a string").code(),
1218 Code::WrongType
1219 );
1220 assert_eq!(
1221 d.ltrim(b"s", 0, -1).expect_err("a string").code(),
1222 Code::WrongType
1223 );
1224 }
1225
1226 #[test]
1227 fn a_list_is_a_list_to_the_rest_of_the_keyspace() {
1228 let mut d = db();
1229 rpush(&mut d, b"l", &[b"a"]);
1230 assert_eq!(d.kind_of(b"l").map(|k| k.name()), Some("list"));
1231 assert_eq!(d.encoding_name(b"l"), Some(Encoding::Listpack.name()));
1232 assert!(d.exists(b"l"));
1233 assert!(d.drop_key(b"l"));
1234 assert_eq!(d.kind_of(b"l"), None);
1235 }
1236
1237 #[test]
1238 fn a_list_can_be_given_a_deadline_and_reaped() {
1239 let mut d = Keyspace::with_clock(Clock::fixed(1_000));
1240 rpush(&mut d, b"l", &[b"a", b"b"]);
1241 assert!(d.set_expiry(b"l", Some(1_500)));
1242 assert_eq!(all(&mut d, b"l"), ["a", "b"]);
1243 d.clock().advance(1_000);
1244 assert_eq!(d.llen(b"l").expect("gone"), 0);
1245 assert_eq!(d.kind_of(b"l"), None);
1246 }
1247
1248 #[test]
1249 fn a_big_list_is_chunked_and_still_answers_the_same() {
1250 let mut d = db();
1251 let value = vec![b'x'; 400];
1252 for i in 0..40 {
1253 let mut v = value.clone();
1254 v.extend_from_slice(format!("{i}").as_bytes());
1255 d.push(b"l", End::Right, [v.as_slice()].into_iter())
1256 .expect("a list");
1257 }
1258 assert_eq!(d.encoding_name(b"l"), Some(Encoding::Quicklist.name()));
1259 assert_eq!(d.llen(b"l").expect("a list"), 40);
1260 let last = d.lindex(b"l", -1).expect("ok").expect("in range").to_vec();
1261 assert!(last.ends_with(b"39"));
1262 d.ltrim(b"l", 0, 0).expect("ok");
1263 assert_eq!(d.llen(b"l").expect("a list"), 1);
1264 assert_eq!(d.encoding_name(b"l"), Some(Encoding::Listpack.name()));
1265 }
1266}