Skip to content

Commit 1b6a1e9

Browse files
committed
Finalize the Seek API
This adopts the rules posted in #10432: 1. If a seek position is negative, then an error is generated 2. Seeks beyond the end-of-file are allowed. Future writes will fill the gap with data and future reads will return errors. 3. Seeks within the bounds of a file are fine. Closes #10432
1 parent 4c967e7 commit 1b6a1e9

File tree

2 files changed

+80
-31
lines changed

2 files changed

+80
-31
lines changed

src/libstd/io/mem.rs

+73-26
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
//! Readers and Writers for in-memory buffers
1212
13-
use cmp::max;
1413
use cmp::min;
1514
use container::Container;
1615
use option::None;
@@ -20,6 +19,25 @@ use io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult};
2019
use vec;
2120
use vec::{Vector, ImmutableVector, MutableVector, OwnedCloneableVector};
2221

22+
fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult<u64> {
23+
// compute offset as signed and clamp to prevent overflow
24+
let pos = match seek {
25+
SeekSet => 0,
26+
SeekEnd => end,
27+
SeekCur => cur,
28+
} as i64;
29+
30+
if offset + pos < 0 {
31+
Err(IoError {
32+
kind: io::InvalidInput,
33+
desc: "invalid seek to a negative offset",
34+
detail: None
35+
})
36+
} else {
37+
Ok((offset + pos) as u64)
38+
}
39+
}
40+
2341
/// Writes to an owned, growable byte vector
2442
///
2543
/// # Example
@@ -92,19 +110,11 @@ impl Writer for MemWriter {
92110
}
93111
}
94112

95-
// FIXME(#10432)
96113
impl Seek for MemWriter {
97114
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
98-
99115
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
100-
// compute offset as signed and clamp to prevent overflow
101-
let offset = match style {
102-
SeekSet => { 0 }
103-
SeekEnd => { self.buf.len() }
104-
SeekCur => { self.pos }
105-
} as i64;
106-
107-
self.pos = max(0, offset+pos) as uint;
116+
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
117+
self.pos = new as uint;
108118
Ok(())
109119
}
110120
}
@@ -139,7 +149,7 @@ impl MemReader {
139149
/// Tests whether this reader has read all bytes in its buffer.
140150
///
141151
/// If `true`, then this will no longer return bytes from `read`.
142-
pub fn eof(&self) -> bool { self.pos == self.buf.len() }
152+
pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
143153

144154
/// Acquires an immutable reference to the underlying buffer of this
145155
/// `MemReader`.
@@ -172,7 +182,11 @@ impl Reader for MemReader {
172182

173183
impl Seek for MemReader {
174184
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
175-
fn seek(&mut self, _pos: i64, _style: SeekStyle) -> IoResult<()> { fail!() }
185+
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
186+
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
187+
self.pos = new as uint;
188+
Ok(())
189+
}
176190
}
177191

178192
impl Buffer for MemReader {
@@ -236,24 +250,15 @@ impl<'a> Writer for BufWriter<'a> {
236250
}
237251
}
238252

239-
// FIXME(#10432)
240253
impl<'a> Seek for BufWriter<'a> {
241254
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
242-
243255
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
244-
// compute offset as signed and clamp to prevent overflow
245-
let offset = match style {
246-
SeekSet => { 0 }
247-
SeekEnd => { self.buf.len() }
248-
SeekCur => { self.pos }
249-
} as i64;
250-
251-
self.pos = max(0, offset+pos) as uint;
256+
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
257+
self.pos = new as uint;
252258
Ok(())
253259
}
254260
}
255261

256-
257262
/// Reads from a fixed-size byte slice
258263
///
259264
/// # Example
@@ -284,7 +289,7 @@ impl<'a> BufReader<'a> {
284289
/// Tests whether this reader has read all bytes in its buffer.
285290
///
286291
/// If `true`, then this will no longer return bytes from `read`.
287-
pub fn eof(&self) -> bool { self.pos == self.buf.len() }
292+
pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
288293
}
289294

290295
impl<'a> Reader for BufReader<'a> {
@@ -307,7 +312,11 @@ impl<'a> Reader for BufReader<'a> {
307312

308313
impl<'a> Seek for BufReader<'a> {
309314
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
310-
fn seek(&mut self, _pos: i64, _style: SeekStyle) -> IoResult<()> { fail!() }
315+
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
316+
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
317+
self.pos = new as uint;
318+
Ok(())
319+
}
311320
}
312321

313322
impl<'a> Buffer for BufReader<'a> {
@@ -506,4 +515,42 @@ mod test {
506515
Err(..) => {}
507516
}
508517
}
518+
519+
#[test]
520+
fn seek_past_end() {
521+
let buf = [0xff];
522+
let mut r = BufReader::new(buf);
523+
r.seek(10, SeekSet).unwrap();
524+
assert!(r.read(&mut []).is_err());
525+
526+
let mut r = MemReader::new(~[10]);
527+
r.seek(10, SeekSet).unwrap();
528+
assert!(r.read(&mut []).is_err());
529+
530+
let mut r = MemWriter::new();
531+
r.seek(10, SeekSet).unwrap();
532+
assert!(r.write([3]).is_ok());
533+
534+
let mut buf = [0];
535+
let mut r = BufWriter::new(buf);
536+
r.seek(10, SeekSet).unwrap();
537+
assert!(r.write([3]).is_err());
538+
}
539+
540+
#[test]
541+
fn seek_before_0() {
542+
let buf = [0xff];
543+
let mut r = BufReader::new(buf);
544+
assert!(r.seek(-1, SeekSet).is_err());
545+
546+
let mut r = MemReader::new(~[10]);
547+
assert!(r.seek(-1, SeekSet).is_err());
548+
549+
let mut r = MemWriter::new();
550+
assert!(r.seek(-1, SeekSet).is_err());
551+
552+
let mut buf = [0];
553+
let mut r = BufWriter::new(buf);
554+
assert!(r.seek(-1, SeekSet).is_err());
555+
}
509556
}

src/libstd/io/mod.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1192,19 +1192,21 @@ pub enum SeekStyle {
11921192
SeekCur,
11931193
}
11941194

1195-
/// # FIXME
1196-
/// * Are `u64` and `i64` the right choices?
11971195
pub trait Seek {
11981196
/// Return position of file cursor in the stream
11991197
fn tell(&self) -> IoResult<u64>;
12001198

12011199
/// Seek to an offset in a stream
12021200
///
1203-
/// A successful seek clears the EOF indicator.
1201+
/// A successful seek clears the EOF indicator. Seeking beyond EOF is
1202+
/// allowed, but seeking before position 0 is not allowed.
12041203
///
1205-
/// # FIXME
1204+
/// # Errors
12061205
///
1207-
/// * What is the behavior when seeking past the end of a stream?
1206+
/// * Seeking to a negative offset is considered an error
1207+
/// * Seeking past the end of the stream does not modify the underlying
1208+
/// stream, but the next write may cause the previous data to be filled in
1209+
/// with a bit pattern.
12081210
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>;
12091211
}
12101212

0 commit comments

Comments
 (0)