@@ -18,7 +18,7 @@ use io::prelude::*;
18
18
use cmp;
19
19
use error;
20
20
use fmt;
21
- use io:: { self , DEFAULT_BUF_SIZE , Error , ErrorKind } ;
21
+ use io:: { self , DEFAULT_BUF_SIZE , Error , ErrorKind , SeekFrom } ;
22
22
use ptr;
23
23
use iter;
24
24
@@ -120,6 +120,51 @@ impl<R> fmt::Debug for BufReader<R> where R: fmt::Debug {
120
120
}
121
121
}
122
122
123
+ impl < R : Seek > Seek for BufReader < R > {
124
+ /// Seek to an offset, in bytes, in the underlying reader.
125
+ ///
126
+ /// The position used for seeking with `SeekFrom::Current(_)` is the
127
+ /// position the underlying reader would be at if the `BufReader` had no
128
+ /// internal buffer.
129
+ ///
130
+ /// Seeking always discards the internal buffer, even if the seek position
131
+ /// would otherwise fall within it. This guarantees that calling
132
+ /// `.unwrap()` immediately after a seek yields the underlying reader at
133
+ /// the same position.
134
+ ///
135
+ /// See `std::io::Seek` for more details.
136
+ ///
137
+ /// Note: In the edge case where you're seeking with `SeekFrom::Current(n)`
138
+ /// where `n` minus the internal buffer length underflows an `i64`, two
139
+ /// seeks will be performed instead of one. If the second seek returns
140
+ /// `Err`, the underlying reader will be left at the same position it would
141
+ /// have if you seeked to `SeekFrom::Current(0)`.
142
+ fn seek ( & mut self , pos : SeekFrom ) -> io:: Result < u64 > {
143
+ let result: u64 ;
144
+ if let SeekFrom :: Current ( n) = pos {
145
+ let remainder = ( self . cap - self . pos ) as i64 ;
146
+ // it should be safe to assume that remainder fits within an i64 as the alternative
147
+ // means we managed to allocate 8 ebibytes and that's absurd.
148
+ // But it's not out of the realm of possibility for some weird underlying reader to
149
+ // support seeking by i64::min_value() so we need to handle underflow when subtracting
150
+ // remainder.
151
+ if let Some ( offset) = n. checked_sub ( remainder) {
152
+ result = try!( self . inner . seek ( SeekFrom :: Current ( offset) ) ) ;
153
+ } else {
154
+ // seek backwards by our remainder, and then by the offset
155
+ try!( self . inner . seek ( SeekFrom :: Current ( -remainder) ) ) ;
156
+ self . pos = self . cap ; // empty the buffer
157
+ result = try!( self . inner . seek ( SeekFrom :: Current ( n) ) ) ;
158
+ }
159
+ } else {
160
+ // Seeking with Start/End doesn't care about our buffer length.
161
+ result = try!( self . inner . seek ( pos) ) ;
162
+ }
163
+ self . pos = self . cap ; // empty the buffer
164
+ Ok ( result)
165
+ }
166
+ }
167
+
123
168
/// Wraps a Writer and buffers output to it
124
169
///
125
170
/// It can be excessively inefficient to work directly with a `Write`. For
@@ -478,7 +523,7 @@ impl<S: Write> fmt::Debug for BufStream<S> where S: fmt::Debug {
478
523
mod tests {
479
524
use prelude:: v1:: * ;
480
525
use io:: prelude:: * ;
481
- use io:: { self , BufReader , BufWriter , BufStream , Cursor , LineWriter } ;
526
+ use io:: { self , BufReader , BufWriter , BufStream , Cursor , LineWriter , SeekFrom } ;
482
527
use test;
483
528
484
529
/// A dummy reader intended at testing short-reads propagation.
@@ -533,6 +578,67 @@ mod tests {
533
578
assert_eq ! ( reader. read( & mut buf) . unwrap( ) , 0 ) ;
534
579
}
535
580
581
+ #[ test]
582
+ fn test_buffered_reader_seek ( ) {
583
+ let inner: & [ u8 ] = & [ 5 , 6 , 7 , 0 , 1 , 2 , 3 , 4 ] ;
584
+ let mut reader = BufReader :: with_capacity ( 2 , io:: Cursor :: new ( inner) ) ;
585
+
586
+ assert_eq ! ( reader. seek( SeekFrom :: Start ( 3 ) ) . ok( ) , Some ( 3 ) ) ;
587
+ assert_eq ! ( reader. fill_buf( ) . ok( ) , Some ( & [ 0 , 1 ] [ ..] ) ) ;
588
+ assert_eq ! ( reader. seek( SeekFrom :: Current ( 0 ) ) . ok( ) , Some ( 3 ) ) ;
589
+ assert_eq ! ( reader. fill_buf( ) . ok( ) , Some ( & [ 0 , 1 ] [ ..] ) ) ;
590
+ assert_eq ! ( reader. seek( SeekFrom :: Current ( 1 ) ) . ok( ) , Some ( 4 ) ) ;
591
+ assert_eq ! ( reader. fill_buf( ) . ok( ) , Some ( & [ 1 , 2 ] [ ..] ) ) ;
592
+ reader. consume ( 1 ) ;
593
+ assert_eq ! ( reader. seek( SeekFrom :: Current ( -2 ) ) . ok( ) , Some ( 3 ) ) ;
594
+ }
595
+
596
+ #[ test]
597
+ fn test_buffered_reader_seek_underflow ( ) {
598
+ // gimmick reader that yields its position modulo 256 for each byte
599
+ struct PositionReader {
600
+ pos : u64
601
+ }
602
+ impl Read for PositionReader {
603
+ fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
604
+ let len = buf. len ( ) ;
605
+ for x in buf {
606
+ * x = self . pos as u8 ;
607
+ self . pos = self . pos . wrapping_add ( 1 ) ;
608
+ }
609
+ Ok ( len)
610
+ }
611
+ }
612
+ impl Seek for PositionReader {
613
+ fn seek ( & mut self , pos : SeekFrom ) -> io:: Result < u64 > {
614
+ match pos {
615
+ SeekFrom :: Start ( n) => {
616
+ self . pos = n;
617
+ }
618
+ SeekFrom :: Current ( n) => {
619
+ self . pos = self . pos . wrapping_add ( n as u64 ) ;
620
+ }
621
+ SeekFrom :: End ( n) => {
622
+ self . pos = u64:: max_value ( ) . wrapping_add ( n as u64 ) ;
623
+ }
624
+ }
625
+ Ok ( self . pos )
626
+ }
627
+ }
628
+
629
+ let mut reader = BufReader :: with_capacity ( 5 , PositionReader { pos : 0 } ) ;
630
+ assert_eq ! ( reader. fill_buf( ) . ok( ) , Some ( & [ 0 , 1 , 2 , 3 , 4 ] [ ..] ) ) ;
631
+ assert_eq ! ( reader. seek( SeekFrom :: End ( -5 ) ) . ok( ) , Some ( u64 :: max_value( ) -5 ) ) ;
632
+ assert_eq ! ( reader. fill_buf( ) . ok( ) . map( |s| s. len( ) ) , Some ( 5 ) ) ;
633
+ // the following seek will require two underlying seeks
634
+ let expected = 9223372036854775802 ;
635
+ assert_eq ! ( reader. seek( SeekFrom :: Current ( i64 :: min_value( ) ) ) . ok( ) , Some ( expected) ) ;
636
+ assert_eq ! ( reader. fill_buf( ) . ok( ) . map( |s| s. len( ) ) , Some ( 5 ) ) ;
637
+ // seeking to 0 should empty the buffer.
638
+ assert_eq ! ( reader. seek( SeekFrom :: Current ( 0 ) ) . ok( ) , Some ( expected) ) ;
639
+ assert_eq ! ( reader. get_ref( ) . pos, expected) ;
640
+ }
641
+
536
642
#[ test]
537
643
fn test_buffered_writer ( ) {
538
644
let inner = Vec :: new ( ) ;
0 commit comments