1use std::cell::RefCell;
44use std::collections::VecDeque;
45
46use yo_common::Small;
47
48use crate::chunk::{CHUNK_BYTES, Chunk};
49use crate::frozen::{self, Broken};
50use crate::listpack::{Entry, Listpack};
51
52const HITS: usize = 8;
58
59const FORM_PACKED: u8 = 1;
61const FORM_CHUNKS: u8 = 2;
63
64pub type Element<'a> = Entry<'a>;
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct Limits {
75 pub max_packed_bytes: usize,
77 pub max_packed_entries: Option<usize>,
85}
86
87impl Default for Limits {
88 fn default() -> Limits {
92 Limits {
93 max_packed_bytes: CHUNK_BYTES,
94 max_packed_entries: None,
95 }
96 }
97}
98
99impl Limits {
100 #[must_use]
107 pub fn of(fill: i32) -> Limits {
108 if fill >= 0 {
109 return Limits {
110 max_packed_bytes: CHUNK_BYTES,
111 max_packed_entries: Some((fill as usize).max(1)),
112 };
113 }
114 const SIZES: [usize; 5] = [4096, 8192, 16384, 32768, 65536];
116 let at = ((-fill) as usize - 1).min(SIZES.len() - 1);
117 Limits {
118 max_packed_bytes: SIZES[at],
119 max_packed_entries: None,
120 }
121 }
122
123 #[must_use]
125 fn exceeded(&self, bytes: usize, entries: usize) -> bool {
126 match self.max_packed_entries {
127 Some(cap) => bytes > CHUNK_BYTES || entries > cap,
128 None => bytes > self.max_packed_bytes,
129 }
130 }
131
132 #[must_use]
134 fn exceeded_halved(&self, bytes: usize, entries: usize) -> bool {
135 match self.max_packed_entries {
136 Some(cap) => bytes > CHUNK_BYTES / 2 || entries > cap / 2,
137 None => bytes > self.max_packed_bytes / 2,
138 }
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum Encoding {
145 Listpack,
147 Quicklist,
149}
150
151impl Encoding {
152 #[must_use]
154 pub const fn name(self) -> &'static str {
155 match self {
156 Encoding::Listpack => "listpack",
157 Encoding::Quicklist => "quicklist",
158 }
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
164enum Body {
165 Packed(Listpack),
166 Chunks(Deque),
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
171pub struct List {
172 body: Body,
173}
174
175impl Default for List {
176 fn default() -> List {
177 List::new()
178 }
179}
180
181impl List {
182 #[must_use]
184 pub fn new() -> List {
185 List {
186 body: Body::Packed(Listpack::new()),
187 }
188 }
189
190 #[must_use]
192 #[inline]
193 pub fn len(&self) -> usize {
194 match &self.body {
195 Body::Packed(lp) => lp.len(),
196 Body::Chunks(d) => d.len(),
197 }
198 }
199
200 #[must_use]
206 #[inline]
207 pub fn is_empty(&self) -> bool {
208 self.len() == 0
209 }
210
211 pub fn freeze(&self, out: &mut Vec<u8>) {
230 match &self.body {
231 Body::Packed(lp) => {
232 out.push(FORM_PACKED);
233 out.extend_from_slice(lp.as_bytes());
234 }
235 Body::Chunks(d) => {
236 out.push(FORM_CHUNKS);
237 frozen::put_uint(out, d.chunks.len() as u64);
238 for c in &d.chunks {
239 frozen::put_uint(out, c.len() as u64);
240 frozen::put_bytes(out, c.entries());
241 }
242 }
243 }
244 }
245
246 pub fn thaw(bytes: &[u8]) -> Result<List, Broken> {
253 let mut cut = frozen::Cut::new(bytes);
254 match cut.byte()? {
255 FORM_PACKED => Ok(List {
256 body: Body::Packed(Listpack::from_bytes(cut.rest()).map_err(|_| Broken::Body)?),
257 }),
258 FORM_CHUNKS => {
259 let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
260 if n > cut.rest().len() {
263 return Err(Broken::Body);
264 }
265 let mut d = Deque::new();
266 d.chunks.reserve(n);
267 for _ in 0..n {
268 let count = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
269 let entries = cut.bytes()?;
270 if count > entries.len() {
271 return Err(Broken::Body);
274 }
275 d.len += count;
276 d.chunks.push_back(Chunk::adopt(entries, count));
277 }
278 Ok(List {
279 body: Body::Chunks(d),
280 })
281 }
282 _ => Err(Broken::Form),
283 }
284 }
285
286 #[must_use]
288 pub const fn encoding(&self) -> Encoding {
289 match &self.body {
290 Body::Packed(_) => Encoding::Listpack,
291 Body::Chunks(_) => Encoding::Quicklist,
292 }
293 }
294
295 #[must_use]
297 pub fn memory_bytes(&self) -> usize {
298 match &self.body {
299 Body::Packed(lp) => lp.byte_len(),
300 Body::Chunks(d) => d.memory_bytes(),
301 }
302 }
303
304 #[must_use]
306 pub fn get(&self, index: usize) -> Option<Element<'_>> {
307 match &self.body {
308 Body::Packed(lp) => lp.get(index),
309 Body::Chunks(d) => d.get(index),
310 }
311 }
312
313 #[must_use]
315 pub fn front(&self) -> Option<Element<'_>> {
316 match &self.body {
317 Body::Packed(lp) => lp.get(0),
318 Body::Chunks(d) => d.front(),
319 }
320 }
321
322 #[must_use]
327 pub fn back(&self) -> Option<Element<'_>> {
328 match &self.body {
329 Body::Packed(lp) => lp.get_back(0),
330 Body::Chunks(d) => d.back(),
331 }
332 }
333
334 pub fn iter(&self) -> impl Iterator<Item = Element<'_>> {
336 let (packed, chunks) = match &self.body {
339 Body::Packed(lp) => (Some(lp.iter()), None),
340 Body::Chunks(d) => (None, Some(d.iter())),
341 };
342 packed
343 .into_iter()
344 .flatten()
345 .chain(chunks.into_iter().flatten())
346 }
347
348 pub fn iter_back(&self) -> impl Iterator<Item = Element<'_>> {
353 let (packed, chunks) = match &self.body {
354 Body::Packed(lp) => (Some(lp.iter_back()), None),
355 Body::Chunks(d) => (None, Some(d.iter_back())),
356 };
357 packed
358 .into_iter()
359 .flatten()
360 .chain(chunks.into_iter().flatten())
361 }
362
363 pub fn range(&self, start: usize, count: usize) -> impl Iterator<Item = Element<'_>> {
376 let (packed, chunks) = match &self.body {
377 Body::Packed(lp) => (Some(lp.iter_from(start).take(count)), None),
378 Body::Chunks(d) => (None, Some(d.range(start, count))),
379 };
380 packed
381 .into_iter()
382 .flatten()
383 .chain(chunks.into_iter().flatten())
384 }
385
386 pub fn push_front(&mut self, value: &[u8], limits: &Limits) {
388 self.grow_by(value, limits);
389 match &mut self.body {
390 Body::Packed(lp) => lp.insert(0, value),
391 Body::Chunks(d) => d.push_front(value),
392 }
393 }
394
395 pub fn push_back(&mut self, value: &[u8], limits: &Limits) {
397 self.grow_by(value, limits);
398 match &mut self.body {
399 Body::Packed(lp) => lp.push(value),
400 Body::Chunks(d) => d.push_back(value),
401 }
402 }
403
404 pub fn insert(&mut self, index: usize, value: &[u8], limits: &Limits) -> bool {
409 if index > self.len() {
410 return false;
411 }
412 self.grow_by(value, limits);
413 match &mut self.body {
414 Body::Packed(lp) => {
415 lp.insert(index, value);
416 true
417 }
418 Body::Chunks(d) => d.insert_at(index, value),
419 }
420 }
421
422 pub fn insert_at_pivot(
427 &mut self,
428 pivot: &[u8],
429 value: &[u8],
430 before: bool,
431 limits: &Limits,
432 ) -> Option<usize> {
433 let at = self.find(pivot)?;
434 let at = if before { at } else { at + 1 };
435 self.insert(at, value, limits).then(|| self.len())
436 }
437
438 pub fn set(&mut self, index: usize, value: &[u8], limits: &Limits) -> bool {
440 if index >= self.len() {
441 return false;
442 }
443 if let Body::Packed(lp) = &self.body {
448 let old = lp.get(index).map_or(0, |e| e.byte_len());
449 let after = lp.byte_len() + crate::listpack::entry_len(value) - old;
450 self.grow_to(after, self.len(), limits);
451 }
452 match &mut self.body {
453 Body::Packed(lp) => lp.replace(index, value),
454 Body::Chunks(d) => d.replace_at(index, value),
455 }
456 }
457
458 #[must_use]
466 pub fn find(&self, value: &[u8]) -> Option<usize> {
467 let as_int = yo_common::num::parse_i64(value);
468 match &self.body {
469 Body::Packed(lp) => lp.find_parsed(value, as_int, 1),
470 Body::Chunks(d) => d.find(value, as_int),
471 }
472 }
473
474 pub fn positions(
501 &self,
502 value: &[u8],
503 rank: i64,
504 count: usize,
505 maxlen: usize,
506 found: &mut dyn FnMut(usize),
507 ) -> usize {
508 if rank == 0 {
509 return 0;
510 }
511 let as_int = yo_common::num::parse_i64(value);
512 let len = self.len();
513 let mut skip = rank.unsigned_abs() as usize - 1;
514 let mut hits = 0usize;
515 {
516 let mut take = |at: usize| -> bool {
520 if skip > 0 {
521 skip -= 1;
522 return true;
523 }
524 found(at);
525 hits += 1;
526 count == 0 || hits < count
527 };
528 if rank > 0 {
529 match &self.body {
530 Body::Packed(lp) => lp.find_each(value, as_int, maxlen, &mut take),
531 Body::Chunks(d) => d.find_each(value, as_int, maxlen, &mut take),
532 };
533 } else {
534 let mut back = |at: usize| take(len - at - 1);
538 match &self.body {
539 Body::Packed(lp) => lp.find_each_back(value, as_int, maxlen, &mut back),
540 Body::Chunks(d) => d.find_each_back(value, as_int, maxlen, &mut back),
541 };
542 }
543 }
544 hits
545 }
546
547 pub fn remove(&mut self, count: i64, value: &[u8], limits: &Limits) -> usize {
552 let as_int = yo_common::num::parse_i64(value);
553 let want = if count == 0 {
554 usize::MAX
555 } else {
556 count.unsigned_abs() as usize
557 };
558 let mut hits: Small<usize, HITS> = Small::new();
567 if count >= 0 {
568 match &self.body {
569 Body::Packed(lp) => lp.find_each(value, as_int, 0, &mut |at| {
570 hits.push(at);
571 hits.len() < want
572 }),
573 Body::Chunks(d) => d.find_each(value, as_int, 0, &mut |at| {
574 hits.push(at);
575 hits.len() < want
576 }),
577 };
578 hits.reverse();
579 } else {
580 let len = self.len();
581 match &self.body {
582 Body::Packed(lp) => lp.find_each_back(value, as_int, 0, &mut |at| {
583 hits.push(len - at - 1);
584 hits.len() < want
585 }),
586 Body::Chunks(d) => d.find_each_back(value, as_int, 0, &mut |at| {
587 hits.push(len - at - 1);
588 hits.len() < want
589 }),
590 };
591 }
592 for at in &hits {
593 self.remove_at(*at);
594 }
595 self.shrunk(limits);
596 hits.len()
597 }
598
599 fn remove_at(&mut self, index: usize) -> bool {
605 match &mut self.body {
606 Body::Packed(lp) => lp.delete(index, 1),
607 Body::Chunks(d) => d.remove_at(index),
608 }
609 }
610
611 pub fn trim(&mut self, start: usize, count: usize, limits: &Limits) {
617 let len = self.len();
618 let start = start.min(len);
619 let keep = count.min(len - start);
620 match &mut self.body {
621 Body::Packed(lp) => {
622 lp.delete(start + keep, len - start - keep);
623 lp.delete(0, start);
624 }
625 Body::Chunks(d) => d.trim(start, keep),
626 }
627 self.shrunk(limits);
628 }
629
630 pub fn drop_front(&mut self, limits: &Limits) -> bool {
636 let gone = match &mut self.body {
637 Body::Packed(lp) => lp.delete(0, 1),
638 Body::Chunks(d) => d.drop_front(),
639 };
640 self.shrunk(limits);
641 gone
642 }
643
644 pub fn drop_back(&mut self, limits: &Limits) -> bool {
646 let gone = match &mut self.body {
647 Body::Packed(lp) => {
648 let last = lp.len().checked_sub(1);
649 last.is_some_and(|at| lp.delete(at, 1))
650 }
651 Body::Chunks(d) => d.drop_back(),
652 };
653 self.shrunk(limits);
654 gone
655 }
656
657 pub fn pop_front(&mut self, limits: &Limits) -> Option<Vec<u8>> {
662 let out = self.front()?.to_vec();
663 self.drop_front(limits);
664 Some(out)
665 }
666
667 pub fn pop_back(&mut self, limits: &Limits) -> Option<Vec<u8>> {
669 let out = self.back()?.to_vec();
670 self.drop_back(limits);
671 Some(out)
672 }
673
674 fn shrunk(&mut self, limits: &Limits) {
681 let Body::Chunks(d) = &self.body else {
682 return;
683 };
684 if d.chunks.len() != 1 {
685 return;
686 }
687 let only = &d.chunks[0];
688 if limits.exceeded_halved(only.live_bytes(), only.len()) {
689 return;
690 }
691 let mut lp = Listpack::new();
692 for e in only.iter() {
693 match e {
694 Entry::Int(n) => {
695 let mut digits = Vec::new();
696 Entry::Int(n).write_to(&mut digits);
697 lp.push(&digits);
698 }
699 Entry::Str(s) => lp.push(s),
700 }
701 }
702 self.body = Body::Packed(lp);
703 }
704
705 fn grow_by(&mut self, value: &[u8], limits: &Limits) {
711 let Body::Packed(lp) = &self.body else {
712 return;
713 };
714 let after = lp.byte_len() + crate::listpack::entry_len(value);
715 self.grow_to(after, lp.len() + 1, limits);
716 }
717
718 fn grow_to(&mut self, bytes: usize, entries: usize, limits: &Limits) {
720 if !matches!(self.body, Body::Packed(_)) || !limits.exceeded(bytes, entries) {
721 return;
722 }
723 let Body::Packed(lp) = std::mem::replace(&mut self.body, Body::Chunks(Deque::new())) else {
724 unreachable!("just matched a packed body");
725 };
726 let Body::Chunks(d) = &mut self.body else {
727 unreachable!("just put a chunked body there");
728 };
729 d.adopt(&lp);
730 }
731}
732
733fn lone(value: &[u8], front: bool) -> Chunk {
740 if crate::listpack::entry_len(value) > CHUNK_BYTES {
741 return Chunk::plain(value);
742 }
743 let mut c = if front {
744 Chunk::for_front()
745 } else {
746 Chunk::for_back()
747 };
748 let put = if front {
749 c.push_front(value)
750 } else {
751 c.push_back(value)
752 };
753 debug_assert!(put, "an empty chunk refused the only element in it");
754 c
755}
756
757#[derive(Debug, Clone)]
763struct Deque {
764 chunks: VecDeque<Chunk>,
765 len: usize,
766 starts: RefCell<VecDeque<i64>>,
773}
774
775impl PartialEq for Deque {
780 fn eq(&self, other: &Deque) -> bool {
781 self.len == other.len && self.chunks == other.chunks
782 }
783}
784
785impl Eq for Deque {}
786
787impl Deque {
788 fn new() -> Deque {
790 Deque {
791 chunks: VecDeque::new(),
792 len: 0,
793 starts: RefCell::new(VecDeque::new()),
794 }
795 }
796
797 #[inline]
799 const fn len(&self) -> usize {
800 self.len
801 }
802
803 fn adopt(&mut self, lp: &Listpack) {
805 self.len = lp.len();
806 self.chunks.push_back(Chunk::adopt(lp.entries(), lp.len()));
807 self.tail_added();
808 }
809
810 fn memory_bytes(&self) -> usize {
819 let spare = self.chunks.capacity() - self.chunks.len();
820 self.chunks.iter().map(Chunk::memory_bytes).sum::<usize>()
821 + spare * size_of::<Chunk>()
822 + self.starts.borrow().capacity() * size_of::<i64>()
823 }
824
825 fn front(&self) -> Option<Element<'_>> {
827 self.chunks.front()?.front()
828 }
829
830 fn back(&self) -> Option<Element<'_>> {
832 self.chunks.back()?.back()
833 }
834
835 fn get(&self, index: usize) -> Option<Element<'_>> {
837 let (i, within) = self.locate(index)?;
838 self.chunks[i].get(within)
839 }
840
841 fn iter(&self) -> impl Iterator<Item = Element<'_>> {
843 self.chunks.iter().flat_map(Chunk::iter)
844 }
845
846 fn find(&self, value: &[u8], as_int: Option<i64>) -> Option<usize> {
853 let mut base = 0usize;
854 for c in &self.chunks {
855 if let Some(at) = c.find(value, as_int) {
856 return Some(base + at);
857 }
858 base += c.len();
859 }
860 None
861 }
862
863 fn find_each(
869 &self,
870 value: &[u8],
871 as_int: Option<i64>,
872 limit: usize,
873 hit: &mut dyn FnMut(usize) -> bool,
874 ) -> usize {
875 let mut base = 0usize;
876 let mut looked = 0usize;
877 for c in &self.chunks {
878 if limit != 0 && looked >= limit {
879 break;
880 }
881 let at = base;
882 let mut on = true;
883 looked += c.find_each(value, as_int, limit.saturating_sub(looked), &mut |i| {
884 on = hit(at + i);
885 on
886 });
887 base += c.len();
888 if !on {
889 break;
890 }
891 }
892 looked
893 }
894
895 fn find_each_back(
897 &self,
898 value: &[u8],
899 as_int: Option<i64>,
900 limit: usize,
901 hit: &mut dyn FnMut(usize) -> bool,
902 ) -> usize {
903 let mut base = 0usize;
904 let mut looked = 0usize;
905 for c in self.chunks.iter().rev() {
906 if limit != 0 && looked >= limit {
907 break;
908 }
909 let at = base;
910 let mut on = true;
911 looked += c.find_each_back(value, as_int, limit.saturating_sub(looked), &mut |i| {
912 on = hit(at + i);
913 on
914 });
915 base += c.len();
916 if !on {
917 break;
918 }
919 }
920 looked
921 }
922
923 fn range(&self, start: usize, count: usize) -> impl Iterator<Item = Element<'_>> {
932 let (chunk, within) = self.locate(start).unwrap_or((self.chunks.len(), 0));
933 let first = self.chunks.get(chunk).map(|c| c.iter_from(within));
934 first
935 .into_iter()
936 .flatten()
937 .chain(self.chunks.iter().skip(chunk + 1).flat_map(Chunk::iter))
938 .take(count)
939 }
940
941 fn iter_back(&self) -> impl Iterator<Item = Element<'_>> {
943 self.chunks.iter().rev().flat_map(Chunk::iter_back)
944 }
945
946 fn locate(&self, index: usize) -> Option<(usize, usize)> {
972 if index >= self.len {
973 return None;
974 }
975 let head = self.chunks.front()?.len();
982 if index < head {
983 return Some((0, index));
984 }
985 let last = self.chunks.len() - 1;
986 let before_tail = self.len - self.chunks[last].len();
987 if index >= before_tail {
988 return Some((last, index - before_tail));
989 }
990 let mut starts = self.starts.borrow_mut();
991 if starts.is_empty() {
992 starts.push_back(0);
993 }
994 let want = starts[0] + index as i64;
999 loop {
1000 let last = starts.len() - 1;
1001 let end = starts[last] + self.chunks[last].len() as i64;
1002 if end > want || starts.len() == self.chunks.len() {
1003 break;
1004 }
1005 starts.push_back(end);
1006 }
1007 let at = starts.partition_point(|&s| s <= want) - 1;
1011 Some((at, (want - starts[at]) as usize))
1012 }
1013
1014 #[inline]
1019 fn cut(&mut self, from: usize) {
1020 let keep = from.min(self.chunks.len());
1021 let starts = self.starts.get_mut();
1022 if starts.len() > keep {
1023 starts.truncate(keep);
1024 }
1025 }
1026
1027 #[inline]
1032 fn head_moved(&mut self, by: i64) {
1033 if let Some(first) = self.starts.get_mut().front_mut() {
1034 *first += by;
1035 }
1036 }
1037
1038 #[inline]
1040 fn head_added(&mut self, len: usize) {
1041 let starts = self.starts.get_mut();
1042 if let Some(&first) = starts.front() {
1043 starts.push_front(first - len as i64);
1044 }
1045 }
1046
1047 #[inline]
1049 fn head_dropped(&mut self) {
1050 self.starts.get_mut().pop_front();
1051 }
1052
1053 #[inline]
1060 fn tail_added(&mut self) {
1061 let n = self.chunks.len();
1062 let before = if n >= 2 { self.chunks[n - 2].len() } else { 0 };
1063 let starts = self.starts.get_mut();
1064 if n == 1 && starts.is_empty() {
1065 starts.push_back(0);
1066 } else if starts.len() + 1 == n {
1067 let last = starts[n - 2];
1068 starts.push_back(last + before as i64);
1069 }
1070 }
1071
1072 fn push_front(&mut self, value: &[u8]) {
1074 if let Some(head) = self.chunks.front_mut()
1075 && head.push_front(value)
1076 {
1077 self.len += 1;
1078 self.head_moved(-1);
1079 return;
1080 }
1081 if self.chunks.front().is_some_and(Chunk::is_empty) {
1085 self.chunks.pop_front();
1086 self.head_dropped();
1087 } else if let Some(head) = self.chunks.front_mut() {
1088 head.seal();
1089 }
1090 self.chunks.push_front(lone(value, true));
1091 self.len += 1;
1092 self.head_added(1);
1093 }
1094
1095 fn push_back(&mut self, value: &[u8]) {
1097 if let Some(tail) = self.chunks.back_mut()
1098 && tail.push_back(value)
1099 {
1100 self.len += 1;
1101 return;
1102 }
1103 if self.chunks.back().is_some_and(Chunk::is_empty) {
1104 self.chunks.pop_back();
1105 self.cut(self.chunks.len());
1106 } else if let Some(tail) = self.chunks.back_mut() {
1107 tail.seal();
1108 }
1109 self.chunks.push_back(lone(value, false));
1110 self.len += 1;
1111 self.tail_added();
1112 }
1113
1114 fn insert_at(&mut self, index: usize, value: &[u8]) -> bool {
1121 if index > self.len {
1122 return false;
1123 }
1124 if index == 0 {
1125 self.push_front(value);
1126 return true;
1127 }
1128 if index == self.len {
1129 self.push_back(value);
1130 return true;
1131 }
1132 let Some((i, within)) = self.locate(index) else {
1133 return false;
1134 };
1135 if self.chunks[i].insert_at(within, value) {
1136 self.len += 1;
1137 self.cut(i + 1);
1139 return true;
1140 }
1141 let mut rest = self.chunks[i].split_off(within);
1142 let put = self.chunks[i].push_back(value) || rest.push_front(value);
1144 self.chunks.insert(i + 1, rest);
1145 if !put {
1146 self.chunks.insert(i + 1, lone(value, false));
1147 }
1148 self.len += 1;
1149 self.cut(i + 1);
1150 true
1151 }
1152
1153 fn remove_at(&mut self, index: usize) -> bool {
1155 let Some((i, within)) = self.locate(index) else {
1156 return false;
1157 };
1158 if !self.chunks[i].remove_at(within) {
1159 return false;
1160 }
1161 self.len -= 1;
1162 if self.chunks[i].is_empty() && self.chunks.len() > 1 {
1163 self.chunks.remove(i);
1164 self.cut(i);
1168 } else {
1169 self.cut(i + 1);
1170 }
1171 true
1172 }
1173
1174 fn replace_at(&mut self, index: usize, value: &[u8]) -> bool {
1179 let Some((i, within)) = self.locate(index) else {
1180 return false;
1181 };
1182 if self.chunks[i].replace_at(within, value) {
1183 return true;
1186 }
1187 let mut rest = self.chunks[i].split_off(within);
1188 rest.drop_front();
1189 let put = self.chunks[i].push_back(value) || rest.push_front(value);
1190 if !rest.is_empty() {
1191 self.chunks.insert(i + 1, rest);
1192 }
1193 if !put {
1194 self.chunks.insert(i + 1, lone(value, false));
1195 }
1196 if self.chunks[i].is_empty() && self.chunks.len() > 1 {
1197 self.chunks.remove(i);
1198 }
1199 self.cut(i);
1200 true
1201 }
1202
1203 fn trim(&mut self, start: usize, keep: usize) {
1210 let mut front = start;
1211 while front > 0 {
1212 let Some(held) = self.chunks.front().map(Chunk::len) else {
1213 break;
1214 };
1215 if held <= front && self.chunks.len() > 1 {
1216 front -= held;
1217 self.len -= held;
1218 self.chunks.pop_front();
1219 self.head_dropped();
1220 } else {
1221 let took = self.chunks[0].drop_front_n(front);
1222 self.len -= took;
1223 front -= took;
1224 self.head_moved(took as i64);
1225 if took == 0 {
1226 break;
1227 }
1228 }
1229 }
1230 let mut back = self.len - keep.min(self.len);
1231 while back > 0 {
1232 let Some(held) = self.chunks.back().map(Chunk::len) else {
1233 break;
1234 };
1235 if held <= back && self.chunks.len() > 1 {
1236 back -= held;
1237 self.len -= held;
1238 self.chunks.pop_back();
1239 self.cut(self.chunks.len());
1240 } else {
1241 let last = self.chunks.len() - 1;
1242 let took = self.chunks[last].drop_back_n(back);
1243 self.len -= took;
1244 back -= took;
1245 if took == 0 {
1246 break;
1247 }
1248 }
1249 }
1250 }
1251
1252 fn drop_front(&mut self) -> bool {
1254 let Some(head) = self.chunks.front_mut() else {
1255 return false;
1256 };
1257 if !head.drop_front() {
1258 return false;
1259 }
1260 let gone = head.is_empty();
1261 self.len -= 1;
1262 self.head_moved(1);
1263 if gone && self.chunks.len() > 1 {
1264 self.chunks.pop_front();
1265 self.head_dropped();
1266 }
1267 true
1268 }
1269
1270 fn drop_back(&mut self) -> bool {
1272 let Some(tail) = self.chunks.back_mut() else {
1273 return false;
1274 };
1275 if !tail.drop_back() {
1276 return false;
1277 }
1278 let gone = tail.is_empty();
1279 self.len -= 1;
1280 if gone && self.chunks.len() > 1 {
1281 self.chunks.pop_back();
1282 self.cut(self.chunks.len());
1283 }
1284 true
1285 }
1286
1287 #[cfg(test)]
1294 fn index_is_true(&self) {
1295 let starts = self.starts.borrow();
1296 assert!(
1297 starts.len() <= self.chunks.len(),
1298 "the index describes {} chunks and the ring holds {}",
1299 starts.len(),
1300 self.chunks.len()
1301 );
1302 let Some(&base) = starts.front() else {
1303 return;
1304 };
1305 let mut real = 0usize;
1306 for (i, &s) in starts.iter().enumerate() {
1307 assert_eq!(
1308 s - base,
1309 real as i64,
1310 "chunk {i} is indexed at {} and starts at {real}",
1311 s - base
1312 );
1313 real += self.chunks[i].len();
1314 }
1315 }
1316}
1317
1318#[cfg(test)]
1319mod tests {
1320 use super::*;
1321 use crate::many;
1322
1323 fn all(l: &List) -> Vec<Vec<u8>> {
1324 l.iter().map(|e| e.to_vec()).collect()
1325 }
1326
1327 fn both_bands(n: usize) -> [List; 2] {
1334 let limits = Limits::default();
1335 let mut packed = List::new();
1336 let mut chunks = List::new();
1337 for i in 0..n {
1338 packed.push_back(format!("e{i}").as_bytes(), &limits);
1339 chunks.push_back(format!("e{i}:{}", "p".repeat(400)).as_bytes(), &limits);
1340 }
1341 assert_eq!(packed.encoding(), Encoding::Listpack);
1342 assert_eq!(chunks.encoding(), Encoding::Quicklist);
1343 [packed, chunks]
1344 }
1345
1346 fn chunked(n: usize) -> List {
1349 let mut l = List::new();
1350 let limits = Limits::default();
1351 for i in 0..n {
1352 l.push_back(format!("value:{i:0>60}").as_bytes(), &limits);
1353 }
1354 assert_eq!(l.encoding(), Encoding::Quicklist, "{n} did not promote");
1355 l
1356 }
1357
1358 #[test]
1359 fn a_new_list_is_empty_and_packed() {
1360 let l = List::new();
1361 assert!(l.is_empty());
1362 assert_eq!(l.len(), 0);
1363 assert_eq!(l.encoding(), Encoding::Listpack);
1364 assert!(l.front().is_none());
1365 assert!(l.back().is_none());
1366 assert!(l.get(0).is_none());
1367 }
1368
1369 #[test]
1370 fn pushing_at_both_ends_puts_the_elements_in_order() {
1371 let mut l = List::new();
1372 let limits = Limits::default();
1373 l.push_back(b"b", &limits);
1374 l.push_back(b"c", &limits);
1375 l.push_front(b"a", &limits);
1376 assert_eq!(all(&l), vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]);
1377 assert_eq!(l.front().unwrap().to_vec(), b"a");
1378 assert_eq!(l.back().unwrap().to_vec(), b"c");
1379 assert_eq!(l.get(1).unwrap().to_vec(), b"b");
1380 assert_eq!(l.len(), 3);
1381 }
1382
1383 #[test]
1384 fn popping_takes_from_the_end_it_says() {
1385 let mut l = List::new();
1386 let limits = Limits::default();
1387 for m in [b"a", b"b", b"c"] {
1388 l.push_back(m, &limits);
1389 }
1390 assert_eq!(l.pop_front(&limits).unwrap(), b"a");
1391 assert_eq!(l.pop_back(&limits).unwrap(), b"c");
1392 assert_eq!(all(&l), vec![b"b".to_vec()]);
1393 assert_eq!(l.pop_front(&limits).unwrap(), b"b");
1394 assert!(l.pop_front(&limits).is_none());
1395 assert!(l.pop_back(&limits).is_none());
1396 assert!(l.is_empty());
1397 }
1398
1399 #[test]
1402 fn a_thousand_short_elements_stay_packed() {
1403 let mut l = List::new();
1404 let limits = Limits::default();
1405 for i in 0..1000 {
1406 l.push_back(i.to_string().as_bytes(), &limits);
1407 }
1408 assert_eq!(l.encoding(), Encoding::Listpack);
1409 assert_eq!(l.len(), 1000);
1410 }
1411
1412 #[test]
1413 fn enough_bytes_promotes_and_keeps_every_element() {
1414 let l = chunked(300);
1415 assert_eq!(l.len(), 300);
1416 for i in 0..300 {
1417 assert_eq!(
1418 l.get(i).unwrap().to_vec(),
1419 format!("value:{i:0>60}").into_bytes(),
1420 "element {i} after promotion"
1421 );
1422 }
1423 }
1424
1425 #[test]
1426 fn a_chunked_list_pushes_and_pops_at_both_ends() {
1427 let mut l = chunked(300);
1428 let limits = Limits::default();
1429 l.push_front(b"first", &limits);
1430 l.push_back(b"last", &limits);
1431 assert_eq!(l.len(), 302);
1432 assert_eq!(l.front().unwrap().to_vec(), b"first");
1433 assert_eq!(l.back().unwrap().to_vec(), b"last");
1434 assert_eq!(l.pop_front(&limits).unwrap(), b"first");
1435 assert_eq!(l.pop_back(&limits).unwrap(), b"last");
1436 assert_eq!(l.len(), 300);
1437 assert_eq!(
1438 l.front().unwrap().to_vec(),
1439 format!("value:{:0>60}", 0).into_bytes()
1440 );
1441 }
1442
1443 #[test]
1447 fn a_queue_drains_in_the_order_it_filled() {
1448 let mut l = List::new();
1449 let limits = Limits::default();
1450 let n = many(5000);
1451 for i in 0..n {
1452 l.push_back(format!("job:{i:0>40}").as_bytes(), &limits);
1453 }
1454 for i in 0..n {
1455 assert_eq!(
1456 l.pop_front(&limits).unwrap(),
1457 format!("job:{i:0>40}").into_bytes(),
1458 "job {i} came back in the wrong place"
1459 );
1460 }
1461 assert!(l.is_empty());
1462 }
1463
1464 #[test]
1467 fn a_stack_comes_back_in_reverse() {
1468 let mut l = List::new();
1469 let limits = Limits::default();
1470 let n = many(2000);
1471 for i in 0..n {
1472 l.push_front(format!("frame:{i:0>40}").as_bytes(), &limits);
1473 }
1474 for i in (0..n).rev() {
1475 assert_eq!(
1476 l.pop_front(&limits).unwrap(),
1477 format!("frame:{i:0>40}").into_bytes()
1478 );
1479 }
1480 assert!(l.is_empty());
1481 }
1482
1483 #[test]
1484 fn indexing_agrees_with_the_walk_from_both_ends() {
1485 let l = chunked(if cfg!(miri) { 300 } else { 1000 });
1486 let walked = all(&l);
1487 for (i, want) in walked.iter().enumerate() {
1488 assert_eq!(&l.get(i).unwrap().to_vec(), want, "at {i}");
1489 }
1490 assert!(l.get(walked.len()).is_none());
1491 }
1492
1493 #[test]
1496 fn a_list_that_shrinks_far_enough_goes_back_to_one_blob() {
1497 let mut l = chunked(300);
1498 let limits = Limits::default();
1499 while l.len() > 200 {
1500 l.drop_back(&limits);
1501 }
1502 assert_eq!(
1503 l.encoding(),
1504 Encoding::Quicklist,
1505 "under the limit is not under half of it"
1506 );
1507 while l.len() > 50 {
1508 l.drop_back(&limits);
1509 }
1510 assert_eq!(l.encoding(), Encoding::Listpack);
1511 assert_eq!(l.len(), 50);
1512 for i in 0..50 {
1513 assert_eq!(
1514 l.get(i).unwrap().to_vec(),
1515 format!("value:{i:0>60}").into_bytes(),
1516 "element {i} survived the demotion"
1517 );
1518 }
1519 }
1520
1521 #[test]
1524 fn a_demoted_list_promotes_again() {
1525 let mut l = chunked(300);
1526 let limits = Limits::default();
1527 while l.len() > 20 {
1528 l.drop_back(&limits);
1529 }
1530 assert_eq!(l.encoding(), Encoding::Listpack);
1531 for i in 0..300 {
1532 l.push_back(format!("again:{i:0>60}").as_bytes(), &limits);
1533 }
1534 assert_eq!(l.encoding(), Encoding::Quicklist);
1535 assert_eq!(l.len(), 320);
1536 assert_eq!(
1537 l.get(19).unwrap().to_vec(),
1538 format!("value:{:0>60}", 19).into_bytes(),
1539 "the last of the elements that survived the demotion"
1540 );
1541 assert_eq!(
1542 l.get(20).unwrap().to_vec(),
1543 format!("again:{:0>60}", 0).into_bytes(),
1544 "the first of the elements pushed after it"
1545 );
1546 }
1547
1548 #[test]
1551 fn integers_stay_integers_across_the_band_change() {
1552 let mut l = List::new();
1553 let limits = Limits::default();
1554 for i in 0..300 {
1555 l.push_back(i.to_string().as_bytes(), &limits);
1556 l.push_back(vec![b'x'; 100].as_slice(), &limits);
1557 }
1558 assert_eq!(l.encoding(), Encoding::Quicklist);
1559 assert_eq!(l.get(0), Some(Entry::Int(0)));
1560 assert_eq!(l.get(2), Some(Entry::Int(1)));
1561 assert_eq!(l.len(), 600);
1562 }
1563
1564 #[test]
1568 fn a_count_limit_promotes_on_the_count() {
1569 let limits = Limits::of(4);
1570 let mut l = List::new();
1571 for i in 0..4 {
1572 l.push_back(i.to_string().as_bytes(), &limits);
1573 }
1574 assert_eq!(l.encoding(), Encoding::Listpack);
1575 l.push_back(b"5", &limits);
1576 assert_eq!(l.encoding(), Encoding::Quicklist);
1577 assert_eq!(l.len(), 5);
1578 }
1579
1580 #[test]
1581 fn the_limits_are_redis_node_limits() {
1582 assert_eq!(Limits::of(-1).max_packed_bytes, 4096);
1583 assert_eq!(Limits::of(-2).max_packed_bytes, 8192);
1584 assert_eq!(Limits::of(-5).max_packed_bytes, 65536);
1585 assert_eq!(Limits::of(-9).max_packed_bytes, 65536);
1586 assert_eq!(Limits::of(128).max_packed_entries, Some(128));
1587 assert_eq!(Limits::of(0).max_packed_entries, Some(1));
1588 assert_eq!(Limits::of(-2), Limits::default());
1589 }
1590
1591 #[test]
1592 fn memory_is_counted_in_both_bands() {
1593 let mut l = List::new();
1594 let limits = Limits::default();
1595 assert!(l.memory_bytes() > 0);
1596 for i in 0..300 {
1597 l.push_back(format!("value:{i:0>60}").as_bytes(), &limits);
1598 }
1599 let held = l.memory_bytes();
1602 assert!(held > 300 * 66, "{held} is less than the elements");
1603 assert!(held < 300 * 66 * 3, "{held} is three times the elements");
1604 }
1605
1606 #[test]
1616 #[ignore = "a measurement, run it by name"]
1617 fn measure_bytes_per_element() {
1618 let limits = Limits::default();
1619 for len in [8usize, 16, 64] {
1620 for n in [128usize, 10_000, 1_000_000] {
1621 let (l, payload) = weighed(n, len, &limits);
1622 let total = l.memory_bytes();
1623 println!(
1624 "n={n:<9} elem={len:<4} band={:<9} total={total:<11} payload={payload:<11} over_per_element={:.2}",
1625 l.encoding().name(),
1626 (total as f64 - payload as f64) / n as f64
1627 );
1628 }
1629 }
1630 }
1631
1632 fn weighed(n: usize, len: usize, limits: &Limits) -> (List, usize) {
1634 let mut l = List::new();
1635 let mut payload = 0usize;
1636 for i in 0..n {
1637 let v = format!("e{i:0>w$}", w = len - 1);
1641 debug_assert_eq!(v.len(), len);
1642 payload += v.len();
1643 l.push_back(v.as_bytes(), limits);
1644 }
1645 (l, payload)
1646 }
1647
1648 #[test]
1655 fn a_long_list_does_not_hold_much_more_than_it_stores() {
1656 let limits = Limits::default();
1657 let n = 100_000;
1658 let (l, payload) = weighed(n, 16, &limits);
1659 assert_eq!(l.encoding(), Encoding::Quicklist);
1660 let total = l.memory_bytes();
1661 assert!(
1662 total < payload + n * 4,
1663 "{total} bytes for {payload} of elements, which is {:.2} an element over",
1664 (total as f64 - payload as f64) / n as f64
1665 );
1666 }
1667
1668 #[test]
1672 fn an_element_too_big_for_a_chunk_gets_one_of_its_own() {
1673 let mut l = List::new();
1674 let limits = Limits::default();
1675 let huge = vec![b'h'; 20_000];
1676 l.push_back(&huge, &limits);
1677 assert_eq!(l.len(), 1);
1678 assert_eq!(l.encoding(), Encoding::Quicklist);
1679 assert_eq!(l.front().unwrap().to_vec(), huge);
1680 l.push_back(b"after", &limits);
1681 l.push_front(b"before", &limits);
1682 assert_eq!(l.len(), 3);
1683 assert_eq!(l.get(1).unwrap().to_vec(), huge);
1684 assert_eq!(l.back().unwrap().to_vec(), b"after");
1685 assert_eq!(l.front().unwrap().to_vec(), b"before");
1686 assert_eq!(l.pop_front(&limits).unwrap(), b"before");
1687 assert_eq!(l.pop_front(&limits).unwrap(), huge);
1688 }
1689
1690 #[test]
1691 fn the_walk_backward_is_the_walk_forward_reversed() {
1692 for mut l in [List::new(), chunked(400)] {
1693 let limits = Limits::default();
1694 l.push_back(b"tail", &limits);
1695 let mut want = all(&l);
1696 want.reverse();
1697 let got: Vec<Vec<u8>> = l.iter_back().map(|e| e.to_vec()).collect();
1698 assert_eq!(got, want, "{:?}", l.encoding());
1699 }
1700 }
1701
1702 #[test]
1703 fn a_range_is_the_window_it_was_asked_for() {
1704 for l in both_bands(50) {
1705 let all_of_it = all(&l);
1706 for (start, count) in [(0, 0), (0, 5), (3, 4), (48, 9), (50, 3), (0, 50)] {
1707 let got: Vec<Vec<u8>> = l.range(start, count).map(|e| e.to_vec()).collect();
1708 let want = &all_of_it[start.min(50)..(start + count).min(50)];
1709 assert_eq!(got, want, "{start} for {count} in {:?}", l.encoding());
1710 }
1711 }
1712 }
1713
1714 #[test]
1719 fn a_window_lands_in_the_right_place_whatever_chunk_it_starts_in() {
1720 let limits = Limits::default();
1721 let mut l = List::new();
1722 let n = if cfg!(miri) { 100usize } else { 500 };
1728 let counts = if cfg!(miri) {
1729 [0usize, 1, 7, 26, 100]
1730 } else {
1731 [0, 1, 7, 130, 500]
1732 };
1733 for i in 0..n {
1734 l.push_back(format!("e{i}:{}", "p".repeat(200)).as_bytes(), &limits);
1735 }
1736 assert_eq!(l.encoding(), Encoding::Quicklist);
1737 let all_of_it = all(&l);
1738
1739 for start in 0..=n {
1740 for count in counts {
1741 let got: Vec<Vec<u8>> = l.range(start, count).map(|e| e.to_vec()).collect();
1742 let want = &all_of_it[start.min(n)..(start + count).min(n)];
1743 assert_eq!(got, want, "{count} from {start}");
1744 }
1745 }
1746 }
1747
1748 #[test]
1755 fn a_packed_window_lands_in_the_right_place_from_either_end() {
1756 let limits = Limits::default();
1757 let mut l = List::new();
1758 let n = if cfg!(miri) { 100usize } else { 400 };
1762 let counts = if cfg!(miri) {
1763 [0usize, 1, 7, 33, 100]
1764 } else {
1765 [0, 1, 7, 130, 400]
1766 };
1767 for i in 0..n {
1768 l.push_back(format!("e{i:0>9}").as_bytes(), &limits);
1769 }
1770 assert_eq!(l.encoding(), Encoding::Listpack);
1771 let all_of_it = all(&l);
1772
1773 for start in 0..=n {
1774 assert_eq!(
1775 l.get(start).map(|e| e.to_vec()).as_ref(),
1776 all_of_it.get(start),
1777 "element {start}"
1778 );
1779 for count in counts {
1780 let got: Vec<Vec<u8>> = l.range(start, count).map(|e| e.to_vec()).collect();
1781 let want = &all_of_it[start.min(n)..(start + count).min(n)];
1782 assert_eq!(got, want, "{count} from {start}");
1783 }
1784 }
1785 }
1786
1787 #[test]
1794 fn reading_the_middle_survives_a_head_that_keeps_moving() {
1795 let limits = Limits::default();
1796 let mut l = List::new();
1797 let mut want: Vec<Vec<u8>> = Vec::new();
1798 for i in 0..many(2000) {
1799 let v = format!("e{i}:{}", "p".repeat(100)).into_bytes();
1800 l.push_back(&v, &limits);
1801 want.push(v);
1802 }
1803 assert_eq!(l.encoding(), Encoding::Quicklist);
1804
1805 for round in 0..many(400) {
1808 if round % 3 == 0 {
1811 for k in 0..7 {
1812 let v = format!("h{round}:{k}:{}", "q".repeat(100)).into_bytes();
1813 l.push_front(&v, &limits);
1814 want.insert(0, v);
1815 }
1816 } else {
1817 for _ in 0..5 {
1818 assert_eq!(l.pop_front(&limits), Some(want.remove(0)));
1819 }
1820 }
1821 assert_eq!(l.len(), want.len(), "length after round {round}");
1822 for at in [0, 1, want.len() / 3, want.len() / 2, want.len() - 1] {
1823 assert_eq!(
1824 l.get(at).map(|e| e.to_vec()).as_ref(),
1825 Some(&want[at]),
1826 "element {at} after round {round}"
1827 );
1828 }
1829 let mid = want.len() / 2;
1830 let got: Vec<Vec<u8>> = l.range(mid, 30).map(|e| e.to_vec()).collect();
1831 assert_eq!(got, want[mid..mid + 30], "the window after round {round}");
1832 let Body::Chunks(d) = &l.body else {
1833 panic!("the list left the chunked band");
1834 };
1835 d.index_is_true();
1836 }
1837 }
1838
1839 #[test]
1840 fn setting_an_element_replaces_only_that_one() {
1841 for mut l in both_bands(50) {
1842 let limits = Limits::default();
1843 let before = all(&l);
1844 let band = l.encoding();
1845 for at in [0usize, 1, 25, 49] {
1846 let mut want = before.clone();
1847 for value in [
1848 &b"z"[..],
1849 &b"a much longer element than the one there"[..],
1850 b"42",
1851 ] {
1852 assert!(l.set(at, value, &limits), "setting {at} in {band:?}");
1853 want[at] = value.to_vec();
1854 assert_eq!(all(&l), want, "setting {at} to {value:?} in {band:?}");
1855 }
1856 l.set(at, &before[at], &limits);
1857 }
1858 assert!(!l.set(50, b"z", &limits), "past the end is not a set");
1859 assert_eq!(all(&l), before);
1860 }
1861 }
1862
1863 #[test]
1875 fn a_pivot_is_found_at_the_right_position_across_a_ring_of_chunks() {
1876 let limits = Limits::default();
1877 let mut l = List::new();
1878 let mut want: Vec<Vec<u8>> = Vec::new();
1879 for i in 0..4_000usize {
1880 let v = match i % 4 {
1884 0 => i.to_string().into_bytes(),
1885 1 => (i64::MAX - i as i64).to_string().into_bytes(),
1886 2 => format!("v{i:0width$}", width = 1 + i % 30).into_bytes(),
1887 _ => format!("value:{i}:{}", "x".repeat(40 + i % 90)).into_bytes(),
1888 };
1889 l.push_back(&v, &limits);
1890 want.push(v);
1891 }
1892 assert_eq!(l.encoding(), Encoding::Quicklist, "this needs the ring");
1893 assert_eq!(l.len(), want.len());
1894 for (at, v) in want.iter().enumerate() {
1895 assert_eq!(l.find(v), Some(at), "element {at} is not where it is");
1896 }
1897 assert_eq!(l.find(b"not in here at all"), None);
1898 assert_eq!(l.find(b"v2000000000000000000000000000002"), None);
1902 }
1903
1904 #[test]
1911 fn positions_agree_with_the_element_walk_across_a_ring_of_chunks() {
1912 let limits = Limits::default();
1913 let mut l = List::new();
1914 let n = many(4_000usize);
1915 for i in 0..n {
1916 let v = if i % 7 == 0 {
1917 b"wanted".to_vec()
1918 } else {
1919 format!("element:{i:0width$}", width = 8 + i % 40).into_bytes()
1920 };
1921 l.push_back(&v, &limits);
1922 }
1923 assert_eq!(l.encoding(), Encoding::Quicklist, "this needs the ring");
1924 let want: Vec<usize> = (0..n).filter(|i| i % 7 == 0).collect();
1925
1926 let mut got = Vec::new();
1927 assert_eq!(
1928 l.positions(b"wanted", 1, 0, 0, &mut |at| got.push(at)),
1929 want.len()
1930 );
1931 assert_eq!(got, want);
1932
1933 let mut got = Vec::new();
1936 l.positions(b"wanted", -1, 0, 0, &mut |at| got.push(at));
1937 got.reverse();
1938 assert_eq!(got, want, "the same matches, found the other way round");
1939
1940 let mut got = Vec::new();
1943 l.positions(b"wanted", 4, 3, 0, &mut |at| got.push(at));
1944 assert_eq!(got, want[3..6].to_vec());
1945 let mut got = Vec::new();
1946 l.positions(b"wanted", -4, 3, 0, &mut |at| got.push(at));
1947 let mut tail = want[want.len() - 6..want.len() - 3].to_vec();
1948 tail.reverse();
1949 assert_eq!(got, tail);
1950
1951 let budget = n / 4;
1955 let mut got = Vec::new();
1956 l.positions(b"wanted", 1, 0, budget, &mut |at| got.push(at));
1957 assert_eq!(
1958 got,
1959 want.iter()
1960 .copied()
1961 .filter(|&i| i < budget)
1962 .collect::<Vec<_>>()
1963 );
1964 let mut got = Vec::new();
1965 l.positions(b"wanted", -1, 0, budget, &mut |at| got.push(at));
1966 got.reverse();
1967 assert_eq!(
1968 got,
1969 want.iter()
1970 .copied()
1971 .filter(|&i| i >= n - budget)
1972 .collect::<Vec<_>>()
1973 );
1974 }
1975
1976 #[test]
1980 fn removing_across_a_ring_takes_out_exactly_what_was_asked_for() {
1981 let limits = Limits::default();
1982 let n: usize = if cfg!(miri) { 600 } else { 3_000 };
1985 let build = || {
1986 let mut l = List::new();
1987 for i in 0..n {
1988 let v = if i % 5 == 0 {
1989 b"gone".to_vec()
1990 } else {
1991 format!("element:{i:0width$}", width = 8 + i % 30).into_bytes()
1992 };
1993 l.push_back(&v, &limits);
1994 }
1995 assert_eq!(l.encoding(), Encoding::Quicklist, "this needs the ring");
1996 l
1997 };
1998 let kept: Vec<Vec<u8>> = (0..n)
1999 .filter(|i| i % 5 != 0)
2000 .map(|i| format!("element:{i:0width$}", width = 8 + i % 30).into_bytes())
2001 .collect();
2002
2003 let mut l = build();
2004 assert_eq!(l.remove(0, b"gone", &limits), n / 5);
2005 assert_eq!(all(&l), kept);
2006
2007 let mut l = build();
2009 assert_eq!(l.remove(10, b"gone", &limits), 10);
2010 assert_eq!(l.len(), n - 10);
2011 assert_eq!(l.find(b"gone"), Some(40), "the eleventh was at 50");
2012
2013 let mut l = build();
2015 assert_eq!(l.remove(-10, b"gone", &limits), 10);
2016 assert_eq!(l.len(), n - 10);
2017 let mut last = 0usize;
2018 l.positions(b"gone", -1, 1, 0, &mut |at| last = at);
2019 assert_eq!(last, n - 55);
2022 }
2023
2024 #[test]
2025 fn an_insert_goes_where_the_pivot_is() {
2026 for mut l in both_bands(50) {
2027 let limits = Limits::default();
2028 let before = all(&l);
2029 let band = l.encoding();
2030 let pivot = before[25].clone();
2031 assert_eq!(
2032 l.insert_at_pivot(&pivot, b"before", true, &limits),
2033 Some(51)
2034 );
2035 assert_eq!(
2036 l.insert_at_pivot(&pivot, b"after", false, &limits),
2037 Some(52)
2038 );
2039 assert_eq!(l.get(25).unwrap().to_vec(), b"before", "{band:?}");
2040 assert_eq!(l.get(26).unwrap().to_vec(), pivot, "{band:?}");
2041 assert_eq!(l.get(27).unwrap().to_vec(), b"after", "{band:?}");
2042 assert_eq!(l.len(), 52);
2043 assert_eq!(
2044 l.insert_at_pivot(b"nothing like it", b"x", true, &limits),
2045 None
2046 );
2047 assert_eq!(l.len(), 52);
2048 }
2049 }
2050
2051 #[test]
2052 fn an_insert_by_index_takes_both_ends_and_the_middle() {
2053 let n: usize = if cfg!(miri) { 200 } else { 400 };
2054 for at in [0usize, 1, n / 2, n - 1, n] {
2055 let mut l = chunked(n);
2056 let limits = Limits::default();
2057 let mut want = all(&l);
2058 assert!(l.insert(at, b"new", &limits), "inserting at {at}");
2059 want.insert(at, b"new".to_vec());
2060 assert_eq!(all(&l), want, "inserting at {at}");
2061 assert_eq!(l.len(), n + 1);
2062 }
2063 let mut l = chunked(n);
2064 assert!(!l.insert(n + 1, b"new", &Limits::default()));
2065 }
2066
2067 #[test]
2068 fn removing_by_value_counts_from_the_end_it_was_told_to() {
2069 let build = || {
2070 let mut l = List::new();
2071 let limits = Limits::default();
2072 for i in 0..40 {
2073 l.push_back(
2074 if i % 3 == 0 {
2075 b"x".to_vec()
2076 } else {
2077 format!("e{i}").into_bytes()
2078 }
2079 .as_slice(),
2080 &limits,
2081 );
2082 }
2083 l
2084 };
2085 let limits = Limits::default();
2086
2087 let mut l = build();
2088 assert_eq!(l.remove(0, b"x", &limits), 14, "every one of them");
2089 assert!(!all(&l).contains(&b"x".to_vec()));
2090 assert_eq!(l.len(), 26);
2091
2092 let mut l = build();
2093 assert_eq!(l.remove(2, b"x", &limits), 2);
2094 assert_eq!(l.len(), 38);
2095 assert_eq!(l.get(0).unwrap().to_vec(), b"e1", "the first two went");
2096
2097 let mut l = build();
2098 assert_eq!(l.remove(-2, b"x", &limits), 2);
2099 assert_eq!(l.get(0).unwrap().to_vec(), b"x", "the last two went");
2100 assert_eq!(l.back().unwrap().to_vec(), b"e38");
2101
2102 let mut l = build();
2103 assert_eq!(l.remove(99, b"x", &limits), 14, "more than there are");
2104 assert_eq!(l.remove(1, b"nothing like it", &limits), 0);
2105 }
2106
2107 #[test]
2108 fn a_trim_keeps_the_window_and_nothing_else() {
2109 let n: usize = if cfg!(miri) { 200 } else { 400 };
2112 for (start, count) in [
2113 (0usize, n),
2114 (0, 10),
2115 (n - 10, 10),
2116 (n / 4, n / 2),
2117 (0, 0),
2118 (n - 1, 1),
2119 ] {
2120 let mut l = chunked(n);
2121 let limits = Limits::default();
2122 let want = all(&l)[start..start + count].to_vec();
2123 l.trim(start, count, &limits);
2124 assert_eq!(all(&l), want, "keeping {count} from {start}");
2125 assert_eq!(l.len(), count);
2126 }
2127 }
2128
2129 #[test]
2132 fn a_trim_that_leaves_a_handful_goes_back_to_one_blob() {
2133 let mut l = chunked(400);
2134 let limits = Limits::default();
2135 l.trim(10, 5, &limits);
2136 assert_eq!(l.encoding(), Encoding::Listpack);
2137 assert_eq!(l.len(), 5);
2138 assert_eq!(
2139 l.get(0).unwrap().to_vec(),
2140 format!("value:{:0>60}", 10).into_bytes()
2141 );
2142 }
2143
2144 #[test]
2145 fn a_position_is_counted_from_the_end_the_rank_asked_for() {
2146 for mut l in [List::new(), chunked(300)] {
2147 let limits = Limits::default();
2148 let band = l.encoding();
2149 for m in [b"a", b"b", b"a", b"c", b"a"] {
2150 l.push_back(m, &limits);
2151 }
2152 let base = l.len() - 5;
2153 let mut out = Vec::new();
2154
2155 l.positions(b"a", 1, 1, 0, &mut |at| out.push(at));
2156 assert_eq!(out, vec![base], "{band:?}");
2157
2158 out.clear();
2159 l.positions(b"a", 2, 1, 0, &mut |at| out.push(at));
2160 assert_eq!(out, vec![base + 2], "the second from the front");
2161
2162 out.clear();
2163 l.positions(b"a", -1, 1, 0, &mut |at| out.push(at));
2164 assert_eq!(out, vec![base + 4], "the first from the back");
2165
2166 out.clear();
2167 l.positions(b"a", -2, 1, 0, &mut |at| out.push(at));
2168 assert_eq!(out, vec![base + 2], "the second from the back");
2169
2170 out.clear();
2171 l.positions(b"a", 1, 0, 0, &mut |at| out.push(at));
2172 assert_eq!(out, vec![base, base + 2, base + 4], "all of them");
2173
2174 out.clear();
2175 l.positions(b"a", -1, 0, 0, &mut |at| out.push(at));
2176 assert_eq!(out, vec![base + 4, base + 2, base], "all of them backward");
2177
2178 out.clear();
2179 l.positions(b"a", 1, 2, 0, &mut |at| out.push(at));
2180 assert_eq!(out, vec![base, base + 2], "two of them");
2181
2182 out.clear();
2183 l.positions(b"nothing like it", 1, 0, 0, &mut |at| out.push(at));
2184 assert!(out.is_empty());
2185
2186 out.clear();
2187 l.positions(b"a", 0, 0, 0, &mut |at| out.push(at));
2188 assert!(out.is_empty(), "a rank of zero is not a rank");
2189 }
2190 }
2191
2192 #[test]
2195 fn maxlen_stops_the_walk_rather_than_the_answers() {
2196 let mut l = List::new();
2197 let limits = Limits::default();
2198 for i in 0..20 {
2199 l.push_back(
2200 if i == 15 {
2201 b"x".to_vec()
2202 } else {
2203 format!("e{i}").into_bytes()
2204 }
2205 .as_slice(),
2206 &limits,
2207 );
2208 }
2209 let mut out = Vec::new();
2210 l.positions(b"x", 1, 0, 10, &mut |at| out.push(at));
2211 assert!(out.is_empty(), "ten comparisons do not reach the sixteenth");
2212 out.clear();
2213 l.positions(b"x", 1, 0, 16, &mut |at| out.push(at));
2214 assert_eq!(out, vec![15]);
2215 out.clear();
2216 l.positions(b"x", -1, 0, 5, &mut |at| out.push(at));
2217 assert_eq!(out, vec![15], "five from the back does reach it");
2218 }
2219
2220 #[test]
2225 fn a_long_run_of_operations_agrees_with_a_vec() {
2226 let (_, chunked) = model_run(&Limits::default());
2232 assert_eq!(chunked, 0, "the default limits should not chunk this list");
2233 let (packed, chunked) = model_run(&Limits::of(8));
2234 assert!(packed > many(200), "{packed} rounds packed");
2237 assert!(chunked > many(200), "{chunked} rounds chunked");
2238 }
2239
2240 fn model_run(limits: &Limits) -> (usize, usize) {
2243 let mut l = List::new();
2244 let mut want: Vec<Vec<u8>> = Vec::new();
2245 let mut seed = 0x2064_u64;
2248 let mut next = move || {
2249 seed = seed.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
2250 (seed >> 33) as usize
2251 };
2252 let (mut packed, mut chunked) = (0, 0);
2253 for round in 0..many(4000) {
2254 let n = next();
2255 let value = match n % 4 {
2256 0 => format!("{}", n % 97).into_bytes(),
2257 1 => vec![b'a' + (n % 26) as u8; 1 + n % 40],
2258 2 => vec![b'z'; 200 + n % 400],
2259 _ => format!("e{round}").into_bytes(),
2260 };
2261 match n % 9 {
2262 0 => {
2263 l.push_front(&value, limits);
2264 want.insert(0, value);
2265 }
2266 1 | 2 => {
2267 l.push_back(&value, limits);
2268 want.push(value);
2269 }
2270 3 if !want.is_empty() => {
2271 let at = n % want.len();
2272 assert!(l.insert(at, &value, limits));
2273 want.insert(at, value);
2274 }
2275 4 if !want.is_empty() => {
2276 let at = n % want.len();
2277 assert!(l.set(at, &value, limits));
2278 want[at] = value;
2279 }
2280 5 if !want.is_empty() => {
2281 assert_eq!(l.pop_front(limits), Some(want.remove(0)));
2282 }
2283 6 if !want.is_empty() => {
2284 assert_eq!(l.pop_back(limits), want.pop());
2285 }
2286 7 if want.len() > 4 => {
2287 let start = n % (want.len() - 2);
2288 let keep = 1 + n % (want.len() - start);
2289 l.trim(start, keep, limits);
2290 want = want[start..start + keep].to_vec();
2291 }
2292 8 if !want.is_empty() => {
2293 let needle = want[n % want.len()].clone();
2294 let count = [0i64, 1, -1, 3][n % 4];
2295 let gone = l.remove(count, &needle, limits);
2296 let mut hits: Vec<usize> = want
2297 .iter()
2298 .enumerate()
2299 .filter(|(_, m)| **m == needle)
2300 .map(|(i, _)| i)
2301 .collect();
2302 if count < 0 {
2303 hits.reverse();
2304 }
2305 if count != 0 {
2306 hits.truncate(count.unsigned_abs() as usize);
2307 }
2308 assert_eq!(gone, hits.len(), "round {round}");
2309 hits.sort_unstable();
2310 for at in hits.iter().rev() {
2311 want.remove(*at);
2312 }
2313 }
2314 _ => {}
2315 }
2316 assert_eq!(l.len(), want.len(), "length after round {round}");
2317 if !want.is_empty() {
2323 for at in [0, want.len() / 2, want.len() - 1] {
2324 assert_eq!(
2325 l.get(at).map(|e| e.to_vec()).as_ref(),
2326 Some(&want[at]),
2327 "element {at} after round {round}"
2328 );
2329 }
2330 }
2331 if let Body::Chunks(d) = &l.body {
2332 d.index_is_true();
2333 }
2334 match l.encoding() {
2335 Encoding::Listpack => packed += 1,
2336 Encoding::Quicklist => chunked += 1,
2337 }
2338 if round % 25 == 0 {
2339 assert_eq!(all(&l), want, "contents after round {round}");
2340 let mut back = all(&l);
2341 back.reverse();
2342 let walked: Vec<Vec<u8>> = l.iter_back().map(|e| e.to_vec()).collect();
2343 assert_eq!(walked, back, "the backward walk after round {round}");
2344 if let Some(first) = want.first() {
2345 assert_eq!(l.front().unwrap().to_vec(), *first);
2346 assert_eq!(l.back().unwrap().to_vec(), *want.last().unwrap());
2347 assert_eq!(l.find(first), Some(0));
2348 }
2349 }
2350 }
2351 assert_eq!(all(&l), want);
2352 (packed, chunked)
2353 }
2354
2355 fn round_trip(l: &List) -> List {
2357 let mut out = Vec::new();
2358 l.freeze(&mut out);
2359 let back = List::thaw(&out).expect("it came back");
2360 assert_eq!(back.len(), l.len(), "the length");
2361 assert_eq!(back.encoding(), l.encoding(), "the band");
2362 assert_eq!(all(&back), all(l), "the elements");
2363 let mut backward: Vec<Vec<u8>> = back.iter_back().map(|e| e.to_vec()).collect();
2364 backward.reverse();
2365 assert_eq!(backward, all(l), "and the walk the other way");
2366 back
2367 }
2368
2369 #[test]
2370 fn a_frozen_list_comes_back_in_the_band_it_left() {
2371 round_trip(&List::new());
2372 for l in both_bands(40) {
2373 round_trip(&l);
2374 }
2375 for l in both_bands(200) {
2376 round_trip(&l);
2377 }
2378 }
2379
2380 #[test]
2386 fn a_ring_comes_back_with_the_same_chunk_boundaries() {
2387 let l = chunked(if cfg!(miri) { 500 } else { 2000 });
2390 let Body::Chunks(before) = &l.body else {
2391 unreachable!("chunked built a ring");
2392 };
2393 let want: Vec<usize> = before.chunks.iter().map(Chunk::len).collect();
2394 assert!(want.len() > 2, "{} chunks is not a ring", want.len());
2395
2396 let back = round_trip(&l);
2397 let Body::Chunks(after) = &back.body else {
2398 unreachable!("it came back a ring");
2399 };
2400 let got: Vec<usize> = after.chunks.iter().map(Chunk::len).collect();
2401 assert_eq!(got, want);
2402 }
2403
2404 #[test]
2405 fn a_list_that_came_back_takes_more_elements_at_both_ends() {
2406 let limits = Limits::default();
2407 for l in both_bands(200) {
2408 let mut back = round_trip(&l);
2409 back.push_front(b"first", &limits);
2410 back.push_back(b"last", &limits);
2411 assert_eq!(back.len(), l.len() + 2);
2412 assert_eq!(back.front().expect("a front").to_vec(), b"first".to_vec());
2413 assert_eq!(back.back().expect("a back").to_vec(), b"last".to_vec());
2414 assert_eq!(back.get(1).expect("the old front").to_vec(), all(&l)[0]);
2415 }
2416 }
2417
2418 #[test]
2419 fn an_element_too_big_for_a_chunk_survives_the_trip() {
2420 let limits = Limits::default();
2421 let mut l = List::new();
2422 l.push_back(b"before", &limits);
2423 l.push_back(&vec![b'x'; CHUNK_BYTES * 2], &limits);
2424 l.push_back(b"after", &limits);
2425 assert_eq!(l.encoding(), Encoding::Quicklist);
2426 let back = round_trip(&l);
2427 assert_eq!(
2428 back.get(1).expect("the big one").to_vec().len(),
2429 CHUNK_BYTES * 2
2430 );
2431 }
2432
2433 #[test]
2434 fn a_frozen_list_that_arrives_damaged_is_an_error_and_not_a_panic() {
2435 for l in both_bands(40) {
2436 let mut out = Vec::new();
2437 l.freeze(&mut out);
2438 for cut in 0..out.len() {
2439 let _ = List::thaw(&out[..cut]);
2440 }
2441 }
2442 assert_eq!(List::thaw(&[]).err(), Some(Broken::Short));
2443 assert_eq!(List::thaw(&[9]).err(), Some(Broken::Form));
2444 assert_eq!(List::thaw(&[FORM_PACKED, 1, 2]).err(), Some(Broken::Body));
2445 assert_eq!(
2448 List::thaw(&[FORM_CHUNKS, 0xff, 0xff, 0x7f, 0]).err(),
2449 Some(Broken::Body)
2450 );
2451 assert_eq!(
2452 List::thaw(&[FORM_CHUNKS, 1, 9, 2, b'a', b'b']).err(),
2453 Some(Broken::Body)
2454 );
2455 }
2456}