@@ -693,7 +693,9 @@ where
693693 /// Create a new `TopNComputer`.
694694 /// Internally it will allocate a buffer of size `2 * top_n`.
695695 pub fn new ( top_n : usize ) -> Self {
696- let vec_cap = top_n. max ( 1 ) * 2 ;
696+ // We ensure that there is always enough space to include an entire block in the buffer if
697+ // need be, so that `push_block_lazy` can avoid checking capacity inside its loop.
698+ let vec_cap = ( top_n. max ( 1 ) * 2 ) + crate :: COLLECT_BLOCK_BUFFER_LEN ;
697699 TopNComputer {
698700 buffer : Vec :: with_capacity ( vec_cap) ,
699701 top_n,
@@ -775,6 +777,12 @@ where TScore: PartialOrd + Clone
775777 else {
776778 return ;
777779 } ;
780+
781+ if self . buffer . len ( ) == self . buffer . capacity ( ) {
782+ let median = self . truncate_top_n ( ) ;
783+ self . threshold = Some ( median) ;
784+ }
785+
778786 push_assuming_capacity (
779787 ComparableDoc {
780788 sort_key : feature,
@@ -789,13 +797,62 @@ where TScore: PartialOrd + Clone
789797 self . push ( feature, doc) ;
790798 return ;
791799 }
800+
801+ #[ inline( always) ]
802+ pub ( crate ) fn push_block_lazy <
803+ TSegmentSortKeyComputer : SegmentSortKeyComputer < SegmentSortKey = TScore > ,
804+ > (
805+ & mut self ,
806+ docs : & [ DocId ] ,
807+ score_tweaker : & mut TSegmentSortKeyComputer ,
808+ ) {
809+ // If the addition of this block might push us over capacity, start by truncating: our
810+ // capacity is larger than 2*n + COLLECT_BLOCK_BUFFER_LEN, so this always makes enough room
811+ // for the entire block (although some of the block might be eliminated).
812+ if self . buffer . len ( ) + docs. len ( ) > self . buffer . capacity ( ) {
813+ let median = self . truncate_top_n ( ) ;
814+ self . threshold = Some ( median) ;
815+ }
816+
817+ if let Some ( last_median) = self . threshold . clone ( ) {
818+ if TSegmentSortKeyComputer :: is_lazy ( ) {
819+ // We validated at the top of the method that we have capacity.
820+ score_tweaker. accept_sort_key_block_lazy :: < REVERSE_ORDER > ( docs, & last_median, & mut self . buffer ) ;
821+ return ;
822+ }
823+
824+ // Eagerly push, with a threshold to compare to.
825+ for & doc in docs {
826+ let sort_key = score_tweaker. sort_key ( doc, 0.0 ) ;
827+
828+ if !REVERSE_ORDER && sort_key > last_median {
829+ continue ;
830+ }
831+ if REVERSE_ORDER && sort_key < last_median {
832+ continue ;
833+ }
834+
835+ // We validated at the top of the method that we have capacity.
836+ let comparable_doc = ComparableDoc { doc, sort_key } ;
837+ push_assuming_capacity ( comparable_doc, & mut self . buffer ) ;
838+ }
839+ } else {
840+ // Eagerly push, without a threshold to compare to.
841+ for & doc in docs {
842+ let sort_key = score_tweaker. sort_key ( doc, 0.0 ) ;
843+ // We validated at the top of the method that we have capacity.
844+ let comparable_doc = ComparableDoc { doc, sort_key } ;
845+ push_assuming_capacity ( comparable_doc, & mut self . buffer ) ;
846+ }
847+ }
848+ }
792849}
793850
794851// Push an element provided there is enough capacity to do so.
795852//
796853// Panics if there is not enough capacity to add an element.
797854#[ inline( always) ]
798- fn push_assuming_capacity < T > ( el : T , buf : & mut Vec < T > ) {
855+ pub fn push_assuming_capacity < T > ( el : T , buf : & mut Vec < T > ) {
799856 let prev_len = buf. len ( ) ;
800857 assert ! ( prev_len < buf. capacity( ) ) ;
801858 // This is mimicking the current (non-stabilized) implementation in std.
@@ -1509,11 +1566,11 @@ mod tests {
15091566 #[ test]
15101567 fn test_top_field_collect_string_prop(
15111568 order in prop_oneof!( Just ( Order :: Desc ) , Just ( Order :: Asc ) ) ,
1512- limit in 1 ..256_usize ,
1513- offset in 0 ..256_usize ,
1569+ limit in 1 ..32_usize ,
1570+ offset in 0 ..32_usize ,
15141571 segments_terms in
15151572 proptest:: collection:: vec(
1516- proptest:: collection:: vec( 0 ..32_u8 , 1 ..32_usize ) ,
1573+ proptest:: collection:: vec( 0 ..64_u8 , 1 ..256_usize ) ,
15171574 0 ..8_usize ,
15181575 )
15191576 ) {
0 commit comments