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
This commit is contained in:
parent
4c967e7041
commit
1b6a1e98a8
@ -10,7 +10,6 @@
|
||||
|
||||
//! Readers and Writers for in-memory buffers
|
||||
|
||||
use cmp::max;
|
||||
use cmp::min;
|
||||
use container::Container;
|
||||
use option::None;
|
||||
@ -20,6 +19,25 @@ use io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult};
|
||||
use vec;
|
||||
use vec::{Vector, ImmutableVector, MutableVector, OwnedCloneableVector};
|
||||
|
||||
fn combine(seek: SeekStyle, cur: uint, end: uint, offset: i64) -> IoResult<u64> {
|
||||
// compute offset as signed and clamp to prevent overflow
|
||||
let pos = match seek {
|
||||
SeekSet => 0,
|
||||
SeekEnd => end,
|
||||
SeekCur => cur,
|
||||
} as i64;
|
||||
|
||||
if offset + pos < 0 {
|
||||
Err(IoError {
|
||||
kind: io::InvalidInput,
|
||||
desc: "invalid seek to a negative offset",
|
||||
detail: None
|
||||
})
|
||||
} else {
|
||||
Ok((offset + pos) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes to an owned, growable byte vector
|
||||
///
|
||||
/// # Example
|
||||
@ -92,19 +110,11 @@ impl Writer for MemWriter {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#10432)
|
||||
impl Seek for MemWriter {
|
||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
||||
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||
// compute offset as signed and clamp to prevent overflow
|
||||
let offset = match style {
|
||||
SeekSet => { 0 }
|
||||
SeekEnd => { self.buf.len() }
|
||||
SeekCur => { self.pos }
|
||||
} as i64;
|
||||
|
||||
self.pos = max(0, offset+pos) as uint;
|
||||
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
|
||||
self.pos = new as uint;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -139,7 +149,7 @@ impl MemReader {
|
||||
/// Tests whether this reader has read all bytes in its buffer.
|
||||
///
|
||||
/// If `true`, then this will no longer return bytes from `read`.
|
||||
pub fn eof(&self) -> bool { self.pos == self.buf.len() }
|
||||
pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
|
||||
|
||||
/// Acquires an immutable reference to the underlying buffer of this
|
||||
/// `MemReader`.
|
||||
@ -172,7 +182,11 @@ impl Reader for MemReader {
|
||||
|
||||
impl Seek for MemReader {
|
||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
||||
fn seek(&mut self, _pos: i64, _style: SeekStyle) -> IoResult<()> { fail!() }
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
|
||||
self.pos = new as uint;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer for MemReader {
|
||||
@ -236,24 +250,15 @@ impl<'a> Writer for BufWriter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(#10432)
|
||||
impl<'a> Seek for BufWriter<'a> {
|
||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
||||
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||
// compute offset as signed and clamp to prevent overflow
|
||||
let offset = match style {
|
||||
SeekSet => { 0 }
|
||||
SeekEnd => { self.buf.len() }
|
||||
SeekCur => { self.pos }
|
||||
} as i64;
|
||||
|
||||
self.pos = max(0, offset+pos) as uint;
|
||||
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
|
||||
self.pos = new as uint;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Reads from a fixed-size byte slice
|
||||
///
|
||||
/// # Example
|
||||
@ -284,7 +289,7 @@ impl<'a> BufReader<'a> {
|
||||
/// Tests whether this reader has read all bytes in its buffer.
|
||||
///
|
||||
/// If `true`, then this will no longer return bytes from `read`.
|
||||
pub fn eof(&self) -> bool { self.pos == self.buf.len() }
|
||||
pub fn eof(&self) -> bool { self.pos >= self.buf.len() }
|
||||
}
|
||||
|
||||
impl<'a> Reader for BufReader<'a> {
|
||||
@ -307,7 +312,11 @@ impl<'a> Reader for BufReader<'a> {
|
||||
|
||||
impl<'a> Seek for BufReader<'a> {
|
||||
fn tell(&self) -> IoResult<u64> { Ok(self.pos as u64) }
|
||||
fn seek(&mut self, _pos: i64, _style: SeekStyle) -> IoResult<()> { fail!() }
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
|
||||
let new = if_ok!(combine(style, self.pos, self.buf.len(), pos));
|
||||
self.pos = new as uint;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Buffer for BufReader<'a> {
|
||||
@ -506,4 +515,42 @@ mod test {
|
||||
Err(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_past_end() {
|
||||
let buf = [0xff];
|
||||
let mut r = BufReader::new(buf);
|
||||
r.seek(10, SeekSet).unwrap();
|
||||
assert!(r.read(&mut []).is_err());
|
||||
|
||||
let mut r = MemReader::new(~[10]);
|
||||
r.seek(10, SeekSet).unwrap();
|
||||
assert!(r.read(&mut []).is_err());
|
||||
|
||||
let mut r = MemWriter::new();
|
||||
r.seek(10, SeekSet).unwrap();
|
||||
assert!(r.write([3]).is_ok());
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = BufWriter::new(buf);
|
||||
r.seek(10, SeekSet).unwrap();
|
||||
assert!(r.write([3]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_before_0() {
|
||||
let buf = [0xff];
|
||||
let mut r = BufReader::new(buf);
|
||||
assert!(r.seek(-1, SeekSet).is_err());
|
||||
|
||||
let mut r = MemReader::new(~[10]);
|
||||
assert!(r.seek(-1, SeekSet).is_err());
|
||||
|
||||
let mut r = MemWriter::new();
|
||||
assert!(r.seek(-1, SeekSet).is_err());
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = BufWriter::new(buf);
|
||||
assert!(r.seek(-1, SeekSet).is_err());
|
||||
}
|
||||
}
|
||||
|
@ -1192,19 +1192,21 @@ pub enum SeekStyle {
|
||||
SeekCur,
|
||||
}
|
||||
|
||||
/// # FIXME
|
||||
/// * Are `u64` and `i64` the right choices?
|
||||
pub trait Seek {
|
||||
/// Return position of file cursor in the stream
|
||||
fn tell(&self) -> IoResult<u64>;
|
||||
|
||||
/// Seek to an offset in a stream
|
||||
///
|
||||
/// A successful seek clears the EOF indicator.
|
||||
/// A successful seek clears the EOF indicator. Seeking beyond EOF is
|
||||
/// allowed, but seeking before position 0 is not allowed.
|
||||
///
|
||||
/// # FIXME
|
||||
/// # Errors
|
||||
///
|
||||
/// * What is the behavior when seeking past the end of a stream?
|
||||
/// * Seeking to a negative offset is considered an error
|
||||
/// * Seeking past the end of the stream does not modify the underlying
|
||||
/// stream, but the next write may cause the previous data to be filled in
|
||||
/// with a bit pattern.
|
||||
fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user