1use std::{cell::RefCell, collections::HashMap, rc::Rc};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
20pub enum LazyLayoutKey {
21 User(u64),
23 Index(usize),
25}
26
27impl LazyLayoutKey {
28 const USER_TAG: u64 = 0b00 << 62;
29 const INDEX_TAG: u64 = 0b01 << 62;
30 const VALUE_MASK: u64 = (1u64 << 62) - 1;
31
32 #[inline]
48 pub fn to_slot_id(self) -> u64 {
49 match self {
50 LazyLayoutKey::User(k) => {
51 let value = Self::normalize_value(k, "User");
52 Self::USER_TAG | value
53 }
54 LazyLayoutKey::Index(i) => {
55 let value = Self::normalize_value(i as u64, "Index");
56 Self::INDEX_TAG | value
57 }
58 }
59 }
60
61 #[inline]
62 fn normalize_value(value: u64, kind: &'static str) -> u64 {
63 if value <= Self::VALUE_MASK {
64 value
65 } else {
66 log::warn!(
67 "LazyList {kind} key {value:#018x} exceeds 62 bits; mixing to 62 bits to avoid overflow"
68 );
69 Self::mix_to_value_bits(value)
70 }
71 }
72
73 #[inline]
74 fn mix_to_value_bits(mut value: u64) -> u64 {
75 value ^= value >> 33;
76 value = value.wrapping_mul(0xff51afd7ed558ccd);
77 value ^= value >> 33;
78 value = value.wrapping_mul(0xc4ceb9fe1a85ec53);
79 value ^= value >> 33;
80 value & Self::VALUE_MASK
81 }
82
83 #[inline]
85 pub fn is_user_key(self) -> bool {
86 matches!(self, LazyLayoutKey::User(_))
87 }
88}
89
90#[doc(hidden)]
91pub struct LazyScopeMarker;
92
93#[derive(Clone, Default)]
108pub struct LazyItems {
109 count: usize,
110 key: Option<Rc<dyn Fn(usize) -> u64>>,
111 content_type: Option<Rc<dyn Fn(usize) -> u64>>,
112}
113
114impl LazyItems {
115 pub fn new(count: usize) -> Self {
117 Self {
118 count,
119 key: None,
120 content_type: None,
121 }
122 }
123
124 pub fn key(mut self, key: impl Fn(usize) -> u64 + 'static) -> Self {
127 self.key = Some(Rc::new(key));
128 self
129 }
130
131 pub fn content_type(mut self, content_type: impl Fn(usize) -> u64 + 'static) -> Self {
134 self.content_type = Some(Rc::new(content_type));
135 self
136 }
137
138 pub fn count(&self) -> usize {
140 self.count
141 }
142
143 pub fn key_fn(&self) -> Option<Rc<dyn Fn(usize) -> u64>> {
145 self.key.clone()
146 }
147
148 pub fn content_type_fn(&self) -> Option<Rc<dyn Fn(usize) -> u64>> {
150 self.content_type.clone()
151 }
152}
153
154impl From<usize> for LazyItems {
155 fn from(count: usize) -> Self {
156 Self::new(count)
157 }
158}
159
160pub trait LazyListScope {
181 fn item<F>(&mut self, content: F)
183 where
184 F: Fn() + 'static,
185 {
186 self.item_keyed(None, None, content);
187 }
188
189 fn item_keyed<F>(&mut self, key: Option<u64>, content_type: Option<u64>, content: F)
194 where
195 F: Fn() + 'static;
196
197 fn items<I, F>(&mut self, items: I, item_content: F)
202 where
203 I: Into<LazyItems>,
204 F: Fn(usize) + 'static;
205}
206
207pub struct LazyListInterval {
212 pub start_index: usize,
214
215 pub count: usize,
217
218 pub key: Option<Rc<dyn Fn(usize) -> u64>>,
221
222 pub content_type: Option<Rc<dyn Fn(usize) -> u64>>,
225
226 pub content: Rc<dyn Fn(usize)>,
229}
230
231impl std::fmt::Debug for LazyListInterval {
232 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233 f.debug_struct("LazyListInterval")
234 .field("start_index", &self.start_index)
235 .field("count", &self.count)
236 .finish_non_exhaustive()
237 }
238}
239
240pub struct LazyListIntervalContent {
244 intervals: Vec<LazyListInterval>,
245 total_count: usize,
246 key_cache: RefCell<Option<HashMap<u64, usize>>>,
247}
248
249impl LazyListIntervalContent {
250 pub fn new() -> Self {
252 Self {
253 intervals: Vec::new(),
254 total_count: 0,
255 key_cache: RefCell::new(None),
256 }
257 }
258
259 fn invalidate_cache(&self) {
260 *self.key_cache.borrow_mut() = None;
261 }
262
263 fn ensure_cache(&self) {
264 let mut cache = self.key_cache.borrow_mut();
265 if cache.is_some() {
266 return;
267 }
268
269 let mut map = HashMap::with_capacity(self.total_count);
270 for index in 0..self.total_count {
271 let slot_id = self.get_key(index).to_slot_id();
272 map.insert(slot_id, index);
273 }
274 *cache = Some(map);
275 }
276
277 pub fn item_count(&self) -> usize {
280 self.total_count
281 }
282
283 pub fn intervals(&self) -> &[LazyListInterval] {
285 &self.intervals
286 }
287
288 pub fn get_key(&self, index: usize) -> LazyLayoutKey {
295 if let Some((interval, local_index)) = self.find_interval(index)
296 && let Some(key_fn) = &interval.key
297 {
298 return LazyLayoutKey::User(key_fn(local_index));
299 }
300 LazyLayoutKey::Index(index)
301 }
302
303 pub fn get_content_type(&self, index: usize) -> Option<u64> {
306 if let Some((interval, local_index)) = self.find_interval(index)
307 && let Some(type_fn) = &interval.content_type
308 {
309 return Some(type_fn(local_index));
310 }
311 None
312 }
313
314 pub fn invoke_content(&self, index: usize) {
319 if let Some((interval, local_index)) = self.find_interval(index) {
320 (interval.content)(local_index);
321 }
322 }
323
324 pub fn with_interval<T, F>(&self, global_index: usize, block: F) -> Option<T>
327 where
328 F: FnOnce(usize, &LazyListInterval) -> T,
329 {
330 self.find_interval(global_index)
331 .map(|(interval, local_index)| block(local_index, interval))
332 }
333
334 #[must_use]
344 pub fn get_index_by_key(&self, key: LazyLayoutKey) -> Option<usize> {
345 let slot_id = key.to_slot_id();
346 self.get_index_by_slot_id(slot_id)
347 }
348
349 pub fn get_index_by_key_in_range(
352 &self,
353 key: LazyLayoutKey,
354 range: std::ops::Range<usize>,
355 ) -> Option<usize> {
356 let start = range.start.min(self.total_count);
357 let end = range.end.min(self.total_count);
358 (start..end).find(|&index| self.get_key(index) == key)
359 }
360
361 const CACHE_THRESHOLD: usize = 64;
362
363 #[must_use]
372 pub fn get_index_by_slot_id(&self, slot_id: u64) -> Option<usize> {
373 if self.total_count <= Self::CACHE_THRESHOLD {
374 return (0..self.total_count)
375 .find(|&index| self.get_key(index).to_slot_id() == slot_id);
376 }
377
378 self.ensure_cache();
379 if let Some(cache) = self.key_cache.borrow().as_ref() {
380 return cache.get(&slot_id).copied();
381 }
382
383 log::warn!(
384 "get_index_by_slot_id: cache unexpectedly missing ({} items), using linear search",
385 self.total_count
386 );
387 (0..self.total_count).find(|&index| self.get_key(index).to_slot_id() == slot_id)
388 }
389
390 pub fn get_index_by_slot_id_in_range(
392 &self,
393 slot_id: u64,
394 range: std::ops::Range<usize>,
395 ) -> Option<usize> {
396 let start = range.start.min(self.total_count);
397 let end = range.end.min(self.total_count);
398 (start..end).find(|&index| self.get_key(index).to_slot_id() == slot_id)
399 }
400
401 fn find_interval(&self, index: usize) -> Option<(&LazyListInterval, usize)> {
402 if self.intervals.is_empty() || index >= self.total_count {
403 return None;
404 }
405
406 let pos = self
407 .intervals
408 .partition_point(|interval| interval.start_index + interval.count <= index);
409
410 if pos < self.intervals.len() {
411 let interval = &self.intervals[pos];
412 if index >= interval.start_index && index < interval.start_index + interval.count {
413 let local_index = index - interval.start_index;
414 return Some((interval, local_index));
415 }
416 }
417 None
418 }
419}
420
421impl Default for LazyListIntervalContent {
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427impl LazyListScope for LazyListIntervalContent {
428 fn item_keyed<F>(&mut self, key: Option<u64>, content_type: Option<u64>, content: F)
429 where
430 F: Fn() + 'static,
431 {
432 self.invalidate_cache();
433 let start_index = self.total_count;
434 self.intervals.push(LazyListInterval {
435 start_index,
436 count: 1,
437 key: key.map(|k| Rc::new(move |_| k) as Rc<dyn Fn(usize) -> u64>),
438 content_type: content_type.map(|t| Rc::new(move |_| t) as Rc<dyn Fn(usize) -> u64>),
439 content: Rc::new(move |_| content()),
440 });
441 self.total_count += 1;
442 }
443
444 fn items<I, F>(&mut self, items: I, item_content: F)
445 where
446 I: Into<LazyItems>,
447 F: Fn(usize) + 'static,
448 {
449 let items = items.into();
450 let count = items.count();
451 if count == 0 {
452 return;
453 }
454
455 self.invalidate_cache();
456 let start_index = self.total_count;
457 self.intervals.push(LazyListInterval {
458 start_index,
459 count,
460 key: items.key_fn(),
461 content_type: items.content_type_fn(),
462 content: Rc::new(item_content),
463 });
464 self.total_count += count;
465 }
466}
467
468use crate::lazy::item_provider::LazyLayoutItemProvider;
469
470impl LazyLayoutItemProvider for LazyListIntervalContent {
474 fn item_count(&self) -> usize {
475 self.total_count
476 }
477
478 fn get_key(&self, index: usize) -> u64 {
479 LazyListIntervalContent::get_key(self, index).to_slot_id()
480 }
481
482 fn get_content_type(&self, index: usize) -> Option<u64> {
483 LazyListIntervalContent::get_content_type(self, index)
484 }
485
486 fn get_index(&self, key: u64) -> Option<usize> {
487 self.get_index_by_slot_id(key)
488 }
489}
490
491pub trait LazyListScopeExt: LazyListScope {
501 fn items_slice<T, F>(&mut self, items: &[T], item_content: F)
529 where
530 T: Clone + 'static,
531 F: Fn(&T) + 'static,
532 {
533 let items_rc: Rc<[T]> = items.to_vec().into();
534 self.items(items.len(), move |index| {
535 if let Some(item) = items_rc.get(index) {
536 item_content(item);
537 }
538 });
539 }
540
541 fn items_vec<T, F>(&mut self, items: Vec<T>, item_content: F)
556 where
557 T: 'static,
558 F: Fn(&T) + 'static,
559 {
560 let len = items.len();
561 let items_rc: Rc<[T]> = Rc::from(items);
562 self.items(len, move |index| {
563 if let Some(item) = items_rc.get(index) {
564 item_content(item);
565 }
566 });
567 }
568
569 fn items_indexed<T, L, F>(&mut self, items: L, item_content: F)
593 where
594 T: 'static,
595 L: Into<Rc<[T]>>,
596 F: Fn(usize, &T) + 'static,
597 {
598 let items_rc: Rc<[T]> = items.into();
599 self.items(items_rc.len(), move |index| {
600 if let Some(item) = items_rc.get(index) {
601 item_content(index, item);
602 }
603 });
604 }
605
606 fn items_slice_rc<T, F>(&mut self, items: Rc<[T]>, item_content: F)
620 where
621 T: 'static,
622 F: Fn(&T) + 'static,
623 {
624 let len = items.len();
625 self.items(len, move |index| {
626 if let Some(item) = items.get(index) {
627 item_content(item);
628 }
629 });
630 }
631
632 fn items_indexed_rc<T, F>(&mut self, items: Rc<[T]>, item_content: F)
646 where
647 T: 'static,
648 F: Fn(usize, &T) + 'static,
649 {
650 let len = items.len();
651 self.items(len, move |index| {
652 if let Some(item) = items.get(index) {
653 item_content(index, item);
654 }
655 });
656 }
657
658 fn items_with_provider<T, P, F>(&mut self, count: usize, provider: P, item_content: F)
680 where
681 T: 'static,
682 P: Fn(usize) -> Option<T> + 'static,
683 F: Fn(T) + 'static,
684 {
685 self.items(count, move |index| {
686 if let Some(item) = provider(index) {
687 item_content(item);
688 }
689 });
690 }
691
692 fn items_indexed_with_provider<T, P, F>(&mut self, count: usize, provider: P, item_content: F)
711 where
712 T: 'static,
713 P: Fn(usize) -> Option<T> + 'static,
714 F: Fn(usize, T) + 'static,
715 {
716 self.items(count, move |index| {
717 if let Some(item) = provider(index) {
718 item_content(index, item);
719 }
720 });
721 }
722}
723
724impl<T: LazyListScope + ?Sized> LazyListScopeExt for T {}
725
726#[cfg(test)]
727mod tests {
728 use std::cell::Cell;
729
730 use super::*;
731
732 #[test]
733 fn key_overflow_warning_suppression_has_no_process_global_state() {
734 let source = include_str!("lazy_list_scope.rs");
735 let user_logged = ["USER_OVERFLOW", "_LOGGED"].concat();
736 let index_logged = ["INDEX_OVERFLOW", "_LOGGED"].concat();
737 let atomic_bool = ["Atomic", "Bool"].concat();
738
739 assert!(
740 !source.contains(&user_logged)
741 && !source.contains(&index_logged)
742 && !source.contains(&atomic_bool),
743 "lazy-list key overflow diagnostics must not use process-global suppression state"
744 );
745 }
746
747 #[test]
748 fn test_single_item() {
749 let mut content = LazyListIntervalContent::new();
750 let called = Rc::new(Cell::new(false));
751 let called_clone = Rc::clone(&called);
752
753 content.item_keyed(Some(42), None, move || {
754 called_clone.set(true);
755 });
756
757 assert_eq!(content.item_count(), 1);
758 assert_eq!(content.get_key(0), LazyLayoutKey::User(42));
759
760 content.invoke_content(0);
761 assert!(called.get());
762 }
763
764 #[test]
765 fn test_multiple_items() {
766 let mut content = LazyListIntervalContent::new();
767
768 content.items(LazyItems::new(5).key(|i| (i * 10) as u64), |_i| {});
769
770 assert_eq!(content.item_count(), 5);
771 assert_eq!(content.get_key(0), LazyLayoutKey::User(0));
772 assert_eq!(content.get_key(1), LazyLayoutKey::User(10));
773 assert_eq!(content.get_key(4), LazyLayoutKey::User(40));
774 }
775
776 #[test]
777 fn test_mixed_intervals() {
778 let mut content = LazyListIntervalContent::new();
779
780 content.item_keyed(Some(100), None, || {});
781
782 content.items(LazyItems::new(3).key(|i| i as u64), |_| {});
783
784 content.item_keyed(Some(200), None, || {});
785
786 assert_eq!(content.item_count(), 5);
787 assert_eq!(content.get_key(0), LazyLayoutKey::User(100));
788 assert_eq!(content.get_key(1), LazyLayoutKey::User(0));
789 assert_eq!(content.get_key(2), LazyLayoutKey::User(1));
790 assert_eq!(content.get_key(3), LazyLayoutKey::User(2));
791 assert_eq!(content.get_key(4), LazyLayoutKey::User(200));
792 }
793
794 #[test]
795 fn test_with_interval() {
796 let mut content = LazyListIntervalContent::new();
797 content.items(5, |_| {});
798
799 let result = content.with_interval(3, |local_idx, interval| (local_idx, interval.count));
800
801 assert_eq!(result, Some((3, 5)));
802 }
803
804 #[test]
805 fn test_user_keys_dont_collide_with_default_keys() {
806 let mut content = LazyListIntervalContent::new();
807
808 content.item_keyed(Some(0), None, || {});
809 content.item(|| {});
810 content.item_keyed(Some(1), None, || {});
811
812 assert_eq!(content.get_key(0), LazyLayoutKey::User(0));
813 assert_eq!(content.get_key(1), LazyLayoutKey::Index(1));
814 assert_eq!(content.get_key(2), LazyLayoutKey::User(1));
815
816 assert_ne!(content.get_key(0), content.get_key(1));
817 assert_ne!(content.get_key(2), content.get_key(1));
818
819 assert_ne!(
820 content.get_key(0).to_slot_id(),
821 content.get_key(1).to_slot_id()
822 );
823 }
824
825 #[test]
826 fn test_slot_id_collision_prevention() {
827 let user_key = LazyLayoutKey::User(0);
828 let index_key = LazyLayoutKey::Index(0);
829
830 assert_ne!(user_key.to_slot_id(), index_key.to_slot_id());
831
832 assert_eq!(user_key.to_slot_id(), 0);
833 assert_eq!(index_key.to_slot_id(), 1u64 << 62);
834
835 assert!(user_key.to_slot_id() < (1u64 << 62));
836 assert!(index_key.to_slot_id() >= (1u64 << 62));
837 assert!(index_key.to_slot_id() < (2u64 << 62));
838
839 let user_max = LazyLayoutKey::User((1u64 << 62) - 1);
840 assert!(
841 user_max.to_slot_id() < (1u64 << 62),
842 "User keys stay in user range"
843 );
844 assert_eq!(user_max.to_slot_id(), (1u64 << 62) - 1);
845
846 let index_large = LazyLayoutKey::Index(((1u64 << 62) - 1) as usize);
847 assert!(
848 index_large.to_slot_id() >= (1u64 << 62),
849 "Index keys stay in index range"
850 );
851 assert!(
852 index_large.to_slot_id() < (2u64 << 62),
853 "Index keys below reserved range"
854 );
855 }
856
857 #[test]
858 fn test_user_key_overflow_is_stable_and_tagged() {
859 let user_max = LazyLayoutKey::User(u64::MAX);
860 let slot = user_max.to_slot_id();
861 assert_eq!(slot, user_max.to_slot_id());
862 assert!(slot < (1u64 << 62));
863 }
864
865 #[test]
866 fn test_index_key_overflow_is_stable_and_tagged() {
867 let index_max = LazyLayoutKey::Index(usize::MAX);
868 let slot = index_max.to_slot_id();
869 assert_eq!(slot, index_max.to_slot_id());
870 assert!(slot >= (1u64 << 62));
871 assert!(slot < (2u64 << 62));
872 }
873
874 #[test]
875 fn test_user_key_high_bits_influence_slot_id() {
876 let key_low = LazyLayoutKey::User(0x0000_0000_0000_0001);
877 let key_high = LazyLayoutKey::User(0x4000_0000_0000_0001);
878 assert_ne!(
879 key_low.to_slot_id(),
880 key_high.to_slot_id(),
881 "High bits are mixed into the slot id to avoid truncation collisions"
882 );
883 }
884
885 #[test]
886 fn test_items_slice() {
887 let mut content = LazyListIntervalContent::new();
888 let data = vec!["Apple", "Banana", "Cherry"];
889 let items_visited = Rc::new(RefCell::new(Vec::new()));
890 let items_clone = items_visited.clone();
891
892 content.items_slice(&data, move |item: &&str| {
893 items_clone.borrow_mut().push((*item).to_string());
894 });
895
896 assert_eq!(content.item_count(), 3);
897
898 for i in 0..3 {
899 content.invoke_content(i);
900 }
901
902 let visited = items_visited.borrow();
903 assert_eq!(*visited, vec!["Apple", "Banana", "Cherry"]);
904 }
905
906 #[test]
907 fn test_items_indexed() {
908 let mut content = LazyListIntervalContent::new();
909 let data = vec![
910 "Apple".to_string(),
911 "Banana".to_string(),
912 "Cherry".to_string(),
913 ];
914 let items_visited = Rc::new(RefCell::new(Vec::new()));
915 let items_clone = items_visited.clone();
916
917 content.items_indexed(data, move |index, item: &String| {
918 items_clone.borrow_mut().push((index, item.clone()));
919 });
920
921 assert_eq!(content.item_count(), 3);
922
923 for i in 0..3 {
924 content.invoke_content(i);
925 }
926
927 let visited = items_visited.borrow();
928 assert_eq!(
929 *visited,
930 vec![
931 (0, "Apple".to_string()),
932 (1, "Banana".to_string()),
933 (2, "Cherry".to_string())
934 ]
935 );
936 }
937
938 #[test]
939 fn test_items_indexed_slice() {
940 let mut content = LazyListIntervalContent::new();
941 let data = vec!["Apple", "Banana", "Cherry"];
942 let items_visited = Rc::new(RefCell::new(Vec::new()));
943 let items_clone = items_visited.clone();
944
945 content.items_indexed(data.as_slice(), move |index, item: &&str| {
946 items_clone.borrow_mut().push((index, (*item).to_string()));
947 });
948
949 assert_eq!(content.item_count(), 3);
950
951 for i in 0..3 {
952 content.invoke_content(i);
953 }
954
955 let visited = items_visited.borrow();
956 assert_eq!(
957 *visited,
958 vec![
959 (0, "Apple".to_string()),
960 (1, "Banana".to_string()),
961 (2, "Cherry".to_string())
962 ]
963 );
964 }
965
966 #[test]
967 fn test_items_slice_rc() {
968 let mut content = LazyListIntervalContent::new();
969 let data: Rc<[String]> = Rc::from(vec!["Apple".into(), "Banana".into()]);
970 let items_visited = Rc::new(RefCell::new(Vec::new()));
971 let items_clone = items_visited.clone();
972
973 content.items_slice_rc(Rc::clone(&data), move |item: &String| {
974 items_clone.borrow_mut().push(item.clone());
975 });
976
977 assert_eq!(content.item_count(), 2);
978
979 for i in 0..2 {
980 content.invoke_content(i);
981 }
982
983 let visited = items_visited.borrow();
984 assert_eq!(*visited, vec!["Apple", "Banana"]);
985 }
986
987 #[test]
988 fn test_items_indexed_rc() {
989 let mut content = LazyListIntervalContent::new();
990 let data: Rc<[String]> = Rc::from(vec!["Apple".into(), "Banana".into()]);
991 let items_visited = Rc::new(RefCell::new(Vec::new()));
992 let items_clone = items_visited.clone();
993
994 content.items_indexed_rc(Rc::clone(&data), move |index, item: &String| {
995 items_clone.borrow_mut().push((index, item.clone()));
996 });
997
998 assert_eq!(content.item_count(), 2);
999
1000 for i in 0..2 {
1001 content.invoke_content(i);
1002 }
1003
1004 let visited = items_visited.borrow();
1005 assert_eq!(
1006 *visited,
1007 vec![(0, "Apple".to_string()), (1, "Banana".to_string())]
1008 );
1009 }
1010
1011 #[test]
1012 fn test_items_with_provider() {
1013 let mut content = LazyListIntervalContent::new();
1014 let data = ["Apple", "Banana", "Cherry"];
1015 let items_visited = Rc::new(RefCell::new(Vec::new()));
1016 let items_clone = items_visited.clone();
1017
1018 content.items_with_provider(
1019 data.len(),
1020 move |index| data.get(index).copied(),
1021 move |item: &str| {
1022 items_clone.borrow_mut().push(item.to_string());
1023 },
1024 );
1025
1026 assert_eq!(content.item_count(), 3);
1027
1028 for i in 0..3 {
1029 content.invoke_content(i);
1030 }
1031
1032 let visited = items_visited.borrow();
1033 assert_eq!(*visited, vec!["Apple", "Banana", "Cherry"]);
1034 }
1035
1036 #[test]
1037 fn test_items_indexed_with_provider() {
1038 let mut content = LazyListIntervalContent::new();
1039 let data = ["Apple", "Banana", "Cherry"];
1040 let items_visited = Rc::new(RefCell::new(Vec::new()));
1041 let items_clone = items_visited.clone();
1042
1043 content.items_indexed_with_provider(
1044 data.len(),
1045 move |index| data.get(index).copied(),
1046 move |index, item: &str| {
1047 items_clone.borrow_mut().push((index, item.to_string()));
1048 },
1049 );
1050
1051 assert_eq!(content.item_count(), 3);
1052
1053 for i in 0..3 {
1054 content.invoke_content(i);
1055 }
1056
1057 let visited = items_visited.borrow();
1058 assert_eq!(
1059 *visited,
1060 vec![
1061 (0, "Apple".to_string()),
1062 (1, "Banana".to_string()),
1063 (2, "Cherry".to_string())
1064 ]
1065 );
1066 }
1067
1068 #[test]
1069 fn test_large_list_cache_works() {
1070 let mut content = LazyListIntervalContent::new();
1071
1072 content.items(LazyItems::new(20_000).key(|i| (i * 7) as u64), |_| {});
1073
1074 let key_19999 = content.get_key(19999);
1075 assert_eq!(key_19999, LazyLayoutKey::User(19999 * 7));
1076
1077 let slot_id = key_19999.to_slot_id();
1078 let found_index = content.get_index_by_slot_id(slot_id);
1079 assert_eq!(found_index, Some(19999));
1080
1081 let key_10000 = content.get_key(10000);
1082 let slot_id_mid = key_10000.to_slot_id();
1083 let found_mid = content.get_index_by_slot_id(slot_id_mid);
1084 assert_eq!(found_mid, Some(10000));
1085 }
1086}