@@ -82,3 +82,126 @@ impl SizeHint {
8282 self . upper = Some ( value) ;
8383 }
8484}
85+
86+ /// Perfectly adds two `SizeHint'`s
87+ impl core:: ops:: Add for SizeHint {
88+ type Output = SizeHint ;
89+
90+ fn add ( self , rhs : Self ) -> Self :: Output {
91+ SizeHint {
92+ lower : self . lower ( ) + rhs. lower ( ) ,
93+ upper : self
94+ . upper ( )
95+ . and_then ( |this| rhs. upper ( ) . map ( |rhs| this + rhs) ) ,
96+ }
97+ }
98+ }
99+
100+ /// Asserts that SizeHint addition is perfect with a basic proof
101+ #[ test]
102+ fn size_hint_addition_proof ( ) {
103+ /// Converts a SizeHint to a tuple for equality checks and matching
104+ fn to_parts ( s : SizeHint ) -> ( u64 , Option < u64 > ) {
105+ ( s. lower ( ) , s. upper ( ) )
106+ }
107+
108+ // assuming addition itself is perfect, there are 3 distinct states:
109+ // (_, Some(_)) + (_, Some(_)) => (_ + _, Some(_ + _))
110+ // (_, Some(_)) + (_, None) => (_ + _, None)
111+ // (_, None) + (_, None) => (_ + _, None)
112+ //
113+ // we can assert this in the typesystem! (and name them for our tests)
114+ match ( to_parts ( SizeHint :: new ( ) ) , to_parts ( SizeHint :: new ( ) ) ) {
115+ ( ( _, Some ( _) ) , ( _, Some ( _) ) ) => { } // 1
116+ ( ( _, None ) , ( _, None ) ) => { } // 2
117+
118+ // note that these cases are identical if we can prove lhs + rhs is equivalent to rhs + lhs
119+ // see below, we do prove that!
120+ ( ( _, Some ( _) ) , ( _, None ) ) => { } // 3
121+ ( ( _, None ) , ( _, Some ( _) ) ) => { }
122+ }
123+ //
124+ // Additionally, we assert a with_exact remains intact if we add two with_exact's together
125+ //
126+ // Additionally, we assert that all operations are equivalent if we do a + b vs b + a
127+
128+ // asserts a + b == b + a == eq
129+ macro_rules! reciprocal_add_eq {
130+ ( $a: expr, $b: expr, $eq: expr) => {
131+ assert_eq!( to_parts( ( $a. clone( ) + $b. clone( ) ) ) , $eq) ;
132+ assert_eq!( to_parts( ( $b. clone( ) + $a. clone( ) ) ) , $eq) ;
133+ } ;
134+ }
135+
136+ // note that we use increasing powers of two every time we fetch a number, this ensures all
137+ // numbers will add uniquely
138+
139+ let exact_1 = SizeHint :: with_exact ( 1 ) ;
140+ let exact_2 = SizeHint :: with_exact ( 2 ) ;
141+
142+ // with_exact
143+ reciprocal_add_eq ! ( exact_1, exact_2, to_parts( SizeHint :: with_exact( 1 + 2 ) ) ) ;
144+
145+ let some_lhs = SizeHint {
146+ lower : 4 ,
147+ upper : Some ( 8 ) ,
148+ } ;
149+
150+ let some_rhs = SizeHint {
151+ lower : 16 ,
152+ upper : Some ( 32 ) ,
153+ } ;
154+
155+ // case 1
156+ reciprocal_add_eq ! ( some_lhs, some_rhs, ( 4 + 16 , Some ( 8 + 32 ) ) ) ;
157+
158+ let none_lhs = SizeHint {
159+ lower : 64 ,
160+ upper : None ,
161+ } ;
162+
163+ let none_rhs = SizeHint {
164+ lower : 128 ,
165+ upper : None ,
166+ } ;
167+
168+ // case 2
169+ reciprocal_add_eq ! ( none_lhs, none_rhs, ( 64 + 128 , None ) ) ;
170+
171+ // case 3
172+ reciprocal_add_eq ! ( some_lhs, none_rhs, ( 4 + 128 , None ) ) ;
173+ }
174+
175+ /// Asserts that some "real data" gets passed through without issue
176+ #[ test]
177+ fn size_hint_addition_basic ( ) {
178+ let exact_l = SizeHint :: with_exact ( 20 ) ;
179+ let exact_r = SizeHint :: with_exact ( 5 ) ;
180+
181+ assert_eq ! ( Some ( 25 ) , ( exact_l. clone( ) + exact_r) . exact( ) ) ;
182+
183+ let inexact_l = SizeHint {
184+ lower : 25 ,
185+ upper : None ,
186+ } ;
187+ let inexact_r = SizeHint {
188+ lower : 10 ,
189+ upper : Some ( 50 ) ,
190+ } ;
191+
192+ let inexact = inexact_l + inexact_r. clone ( ) ;
193+
194+ assert_eq ! ( inexact. lower( ) , 35 ) ;
195+ assert_eq ! ( inexact. upper( ) , None ) ;
196+
197+ let exact_inexact = exact_l. clone ( ) + inexact_r. clone ( ) ;
198+
199+ assert_eq ! ( exact_inexact. lower( ) , 30 ) ;
200+ assert_eq ! ( exact_inexact. upper( ) , Some ( 70 ) ) ;
201+
202+ // same as previous but reversed operation order
203+ let inexact_exact = inexact_r + exact_l;
204+
205+ assert_eq ! ( inexact_exact. lower( ) , 30 ) ;
206+ assert_eq ! ( inexact_exact. upper( ) , Some ( 70 ) ) ;
207+ }
0 commit comments