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