@@ -6,8 +6,18 @@ use crate::{
66 tables:: { DualKey , SingleKey } ,
77} ;
88use core:: marker:: PhantomData ;
9+ use std:: borrow:: Cow ;
910use std:: ops:: { Range , RangeInclusive } ;
1011
12+ /// Raw k2-value pair: (key2, value).
13+ ///
14+ /// This is returned by `iter_k2()` on dual-keyed tables. The caller already
15+ /// knows k1 (they passed it in), so we don't return it redundantly.
16+ pub type RawK2Value < ' a > = ( Cow < ' a , [ u8 ] > , RawValue < ' a > ) ;
17+
18+ /// Typed k2-value pair for a dual-keyed table.
19+ pub type K2Value < T > = ( <T as DualKey >:: Key2 , <T as crate :: tables:: Table >:: Value ) ;
20+
1121/// Trait for traversing key-value pairs in the database.
1222pub trait KvTraverse < E : HotKvReadError > {
1323 /// Set position to the first key-value pair in the database, and return
@@ -212,30 +222,29 @@ pub trait DualKeyTraverse<E: HotKvReadError> {
212222 Ok ( RawDualKeyIter { cursor : self , done : false , _marker : PhantomData } )
213223 }
214224
215- /// Iterate k2 entries within a single k1.
225+ /// Iterate all k2 entries within a single k1.
216226 ///
217- /// The iterator starts at the first entry with (k1, k2) >= the specified
218- /// keys and stops when k1 changes or the table is exhausted.
227+ /// The iterator yields `(k2, value)` pairs for the specified k1, starting
228+ /// from the first k2 value, and stops when k1 changes or the table is
229+ /// exhausted.
230+ ///
231+ /// Note: k1 is not included in the output since the caller already knows
232+ /// it (they passed it in). This avoids redundant allocations.
219233 fn iter_k2 < ' a > (
220234 & ' a mut self ,
221235 k1 : & [ u8 ] ,
222- start_k2 : & [ u8 ] ,
223- ) -> Result < impl Iterator < Item = Result < RawDualKeyValue < ' a > , E > > + ' a , E >
236+ ) -> Result < impl Iterator < Item = Result < RawK2Value < ' a > , E > > + ' a , E >
224237 where
225238 Self : Sized ,
226239 {
227- let entry = self . next_dual_above ( k1, start_k2) ?;
240+ // Position at first entry for this k1 (using empty slice as minimum k2)
241+ let entry = self . next_dual_above ( k1, & [ ] ) ?;
228242 let Some ( ( found_k1, _, _) ) = entry else {
229- return Ok ( RawDualKeyK2Iter {
230- cursor : self ,
231- k1 : Vec :: new ( ) ,
232- done : true ,
233- _marker : PhantomData ,
234- } ) ;
243+ return Ok ( RawDualKeyK2Iter { cursor : self , done : true , _marker : PhantomData } ) ;
235244 } ;
236245 // If the found k1 doesn't match, we're done
237246 let done = found_k1. as_ref ( ) != k1;
238- Ok ( RawDualKeyK2Iter { cursor : self , k1 : k1 . to_vec ( ) , done, _marker : PhantomData } )
247+ Ok ( RawDualKeyK2Iter { cursor : self , done, _marker : PhantomData } )
239248 }
240249}
241250
@@ -520,15 +529,18 @@ pub trait DualTableTraverse<T: DualKey, E: HotKvReadError>: DualKeyTraverse<E> {
520529 where
521530 T :: Key : PartialEq ;
522531
523- /// Iterate k2 entries within a single k1.
532+ /// Iterate all k2 entries within a single k1.
524533 ///
525- /// The iterator starts at the first entry with (k1, k2) >= the specified
526- /// keys and stops when k1 changes or the table is exhausted.
534+ /// The iterator yields `(k2, value)` pairs for the specified k1, starting
535+ /// from the first k2 value, and stops when k1 changes or the table is
536+ /// exhausted.
537+ ///
538+ /// Note: k1 is not included in the output since the caller already knows
539+ /// it (they passed it in). This avoids redundant allocations.
527540 fn iter_k2 (
528541 & mut self ,
529542 k1 : & T :: Key ,
530- start_k2 : & T :: Key2 ,
531- ) -> Result < impl Iterator < Item = Result < DualKeyValue < T > , E > > + ' _ , E >
543+ ) -> Result < impl Iterator < Item = Result < K2Value < T > , E > > + ' _ , E >
532544 where
533545 T :: Key : PartialEq ;
534546}
@@ -607,29 +619,26 @@ where
607619 fn iter_k2 (
608620 & mut self ,
609621 k1 : & T :: Key ,
610- start_k2 : & T :: Key2 ,
611- ) -> Result < impl Iterator < Item = Result < DualKeyValue < T > , E > > + ' _ , E >
622+ ) -> Result < impl Iterator < Item = Result < K2Value < T > , E > > + ' _ , E >
612623 where
613624 T :: Key : PartialEq ,
614625 {
615- // Position cursor and get the target k1 for comparison
616- let entry = DualTableTraverse :: < T , E > :: next_dual_above ( self , k1, start_k2) ?;
626+ // Position cursor at first entry for this k1 using raw interface with empty k2
627+ let mut key1_buf = [ 0u8 ; MAX_KEY_SIZE ] ;
628+ let key1_bytes = k1. encode_key ( & mut key1_buf) ;
629+ let entry = DualKeyTraverse :: next_dual_above ( self , key1_bytes, & [ ] ) ?;
617630 let Some ( ( found_k1, _, _) ) = entry else {
618631 return Ok ( DualTableK2Iter :: < ' _ , C , T , E > {
619632 cursor : self ,
620- k1 : k1. clone ( ) ,
621633 done : true ,
622634 _marker : PhantomData ,
623635 } ) ;
624636 } ;
637+ // Decode the found k1 to check if it matches
638+ let decoded_k1 = T :: decode_key ( found_k1) ?;
625639 // If the found k1 doesn't match, we're done
626- let done = found_k1 != * k1;
627- Ok ( DualTableK2Iter :: < ' _ , C , T , E > {
628- cursor : self ,
629- k1 : found_k1,
630- done,
631- _marker : PhantomData ,
632- } )
640+ let done = decoded_k1 != * k1;
641+ Ok ( DualTableK2Iter :: < ' _ , C , T , E > { cursor : self , done, _marker : PhantomData } )
633642 }
634643}
635644
@@ -811,13 +820,13 @@ where
811820 }
812821}
813822
814- /// Default forward iterator over raw dual-keyed entries within a single k1.
823+ /// Default forward iterator over raw k2-value entries within a single k1.
815824///
816825/// This iterator wraps a cursor implementing `DualKeyTraverse` and yields
817- /// entries while k1 remains unchanged.
826+ /// `(k2, value)` pairs while k1 remains unchanged. The iterator stops when k1
827+ /// changes or the table is exhausted.
818828pub struct RawDualKeyK2Iter < ' a , C , E > {
819829 cursor : & ' a mut C ,
820- k1 : Vec < u8 > ,
821830 done : bool ,
822831 _marker : PhantomData < fn ( ) -> E > ,
823832}
@@ -827,7 +836,7 @@ where
827836 C : DualKeyTraverse < E > ,
828837 E : HotKvReadError ,
829838{
830- type Item = Result < RawDualKeyValue < ' a > , E > ;
839+ type Item = Result < RawK2Value < ' a > , E > ;
831840
832841 fn next ( & mut self ) -> Option < Self :: Item > {
833842 if self . done {
@@ -838,17 +847,10 @@ where
838847 std:: mem:: transmute :: <
839848 Result < Option < RawDualKeyValue < ' _ > > , E > ,
840849 Result < Option < RawDualKeyValue < ' a > > , E > ,
841- > ( self . cursor . read_next ( ) )
850+ > ( self . cursor . next_k2 ( ) )
842851 } ;
843852 match result {
844- Ok ( Some ( ( k1, k2, v) ) ) => {
845- if k1. as_ref ( ) != self . k1 . as_slice ( ) {
846- self . done = true ;
847- None
848- } else {
849- Some ( Ok ( ( k1, k2, v) ) )
850- }
851- }
853+ Ok ( Some ( ( _k1, k2, v) ) ) => Some ( Ok ( ( k2, v) ) ) ,
852854 Ok ( None ) => {
853855 self . done = true ;
854856 None
@@ -865,42 +867,34 @@ where
865867// Typed Iterator Structs (for extension traits)
866868// ============================================================================
867869
868- /// Forward iterator over k2 entries within a single k1.
870+ /// Forward iterator over k2-value pairs within a single k1.
869871///
870- /// This iterator wraps a cursor and yields entries while k1 remains equal
871- /// to the initial k1 value.
872+ /// This iterator wraps a cursor and yields `(k2, value)` pairs by calling
873+ /// `next_k2()` on each iteration. The iterator stops when there are no more
874+ /// k2 entries for the current k1 or when an error occurs.
872875pub struct DualTableK2Iter < ' a , C , T , E >
873876where
874877 T : DualKey ,
875878{
876879 cursor : & ' a mut C ,
877- k1 : T :: Key ,
878880 done : bool ,
879- _marker : PhantomData < fn ( ) -> E > ,
881+ _marker : PhantomData < fn ( ) -> ( T , E ) > ,
880882}
881883
882884impl < ' a , C , T , E > Iterator for DualTableK2Iter < ' a , C , T , E >
883885where
884886 C : DualKeyTraverse < E > ,
885887 T : DualKey ,
886- T :: Key : PartialEq ,
887888 E : HotKvReadError ,
888889{
889- type Item = Result < DualKeyValue < T > , E > ;
890+ type Item = Result < K2Value < T > , E > ;
890891
891892 fn next ( & mut self ) -> Option < Self :: Item > {
892893 if self . done {
893894 return None ;
894895 }
895- match DualTableTraverse :: < T , E > :: read_next ( self . cursor ) {
896- Ok ( Some ( ( k1, k2, v) ) ) => {
897- if k1 != self . k1 {
898- self . done = true ;
899- None
900- } else {
901- Some ( Ok ( ( k1, k2, v) ) )
902- }
903- }
896+ match DualTableTraverse :: < T , E > :: next_k2 ( self . cursor ) {
897+ Ok ( Some ( ( _k1, k2, v) ) ) => Some ( Ok ( ( k2, v) ) ) ,
904898 Ok ( None ) => {
905899 self . done = true ;
906900 None
@@ -1161,16 +1155,19 @@ where
11611155 DualTableTraverse :: < T , E > :: iter ( & mut self . inner )
11621156 }
11631157
1164- /// Iterate k2 entries within a single k1.
1158+ /// Iterate all k2 entries within a single k1.
11651159 ///
1166- /// The iterator starts at the first entry with (k1, k2) >= the specified
1167- /// keys and stops when k1 changes or the table is exhausted.
1160+ /// The iterator yields `(k2, value)` pairs for the specified k1, starting
1161+ /// from the first k2 value, and stops when k1 changes or the table is
1162+ /// exhausted.
1163+ ///
1164+ /// Note: k1 is not included in the output since the caller already knows
1165+ /// it (they passed it in). This avoids redundant allocations.
11681166 pub fn iter_k2 (
11691167 & mut self ,
11701168 k1 : & T :: Key ,
1171- start_k2 : & T :: Key2 ,
1172- ) -> Result < impl Iterator < Item = Result < DualKeyValue < T > , E > > + ' _ , E > {
1173- DualTableTraverse :: < T , E > :: iter_k2 ( & mut self . inner , k1, start_k2)
1169+ ) -> Result < impl Iterator < Item = Result < K2Value < T > , E > > + ' _ , E > {
1170+ DualTableTraverse :: < T , E > :: iter_k2 ( & mut self . inner , k1)
11741171 }
11751172}
11761173
0 commit comments