1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::{cmp, io};
use std::io::SeekFrom;
use std::io::{Seek,Read,Write};

pub struct T<S>(pub S);

impl<S: Read + Seek> Read for T<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let pos = try!(self.0.seek(SeekFrom::Current(0)));
        let t_dist = -(cmp::min(buf.len(), pos as usize) as i64);
        let n_pos = try!(self.0.seek(SeekFrom::Current(t_dist)));
        let dist = (pos - n_pos) as usize;
        let ct = try!(self.0.read(&mut buf[..dist]));
        /* XXX: we have the data at this point, even if this seek fails. Should we ignore a Seek
         * failure? */
        try!(self.0.seek(SeekFrom::Start(n_pos)));
        buf[..ct].reverse();
        Ok(ct)
    }
}

impl<S: Write + io::Seek> Write for T<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let pos = try!(self.0.seek(SeekFrom::Current(0)));
        let seek_goal = cmp::min(buf.len(), pos as usize) as i64;
        let new_pos = try!(self.0.seek(SeekFrom::Current(-seek_goal)));
        let seek_dist = (pos - new_pos) as usize;
        /* XXX: it may make sense for dist != buf.len() being an error, and not performing a
         * partial write */
        let mut r_buf = buf[..seek_dist].to_vec();
        r_buf.reverse();
        /* Note that we must use write_all(), and even then partial writes are problematic as they
         * go the wrong way.
         */
        try!(self.0.write_all(&r_buf));
        try!(self.0.seek(SeekFrom::Start(new_pos)));
        Ok(seek_dist)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl<S: io::Seek> io::Seek for T<S> {
    fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
        let real_pos = match pos {
            /* TODO: consider whether we should allow seeking before the start */
            SeekFrom::Current(x) => SeekFrom::Current(-x),
            SeekFrom::Start(x) => {
                assert!(x <= std::i64::MAX as u64);
                SeekFrom::End(-(x as i64))
            },
            SeekFrom::End(x) => {
                assert!(x >= 0);
                SeekFrom::Start(x as u64)
            },
        };

        let u_res = try!(self.0.seek(real_pos));

        let cur = try!(self.0.seek(SeekFrom::Current(0)));
        let end = try!(self.0.seek(SeekFrom::End(0)));
        try!(self.0.seek(SeekFrom::Start(cur)));
        Ok(end - u_res)
    }
}

#[test]
fn seek() {
    use std::io::Cursor;
    let mut c = T(Cursor::new(vec![4u8, 6, 2]));
    assert_eq!(c.seek(SeekFrom::Start(0)).unwrap(), 0);
    assert_eq!(c.seek(SeekFrom::Start(1)).unwrap(), 1);
    assert_eq!(c.seek(SeekFrom::End(0)).unwrap(), 3);
    assert_eq!(c.seek(SeekFrom::Current(-1)).unwrap(), 2);
}

#[test]
fn read() {
    use std::io::Cursor;
    let t = vec![4u8, 6, 2];
    let r : &mut[_] = &mut t.clone();
    r.reverse();

    let mut c = T(Cursor::new(t));
    let b : &mut[_] = &mut [0; 3];
    assert_eq!(c.seek(SeekFrom::Start(0)).unwrap(), 0);
    assert_eq!(c.read(b).unwrap(), 3);
    assert_eq!(b, r);
    assert_eq!(c.seek(SeekFrom::Current(0)).unwrap(), 3);
    assert_eq!(c.read(b).unwrap(), 0);
    assert_eq!(b, r);
}

#[test]
fn write() {
    use std::io::Cursor;
    let t = vec![4u8, 6, 2];

    let mut c = T(Cursor::new(t));
    let b = [5, 2, 6];
    let mut r = b;
    r.reverse();
    assert_eq!(c.seek(SeekFrom::Start(0)).unwrap(), 0);
    assert_eq!(c.write(&b).unwrap(), 3);
    assert_eq!(&c.0.get_ref()[..], &r);
    assert_eq!(c.write(&b).unwrap(), 0);
    assert_eq!(c.seek(SeekFrom::Start(0)).unwrap(), 0);
    assert_eq!(c.write(&b[..2]).unwrap(), 2);
    assert_eq!(c.write(&b[..2]).unwrap(), 1);
}