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