Directly implement Seek::stream_position
This commit is contained in:
1 parent
5b8a2832c3
commit
006f0b0fa4
1 file changed
+43
-4
+43
-4
@@ -25,7 +25,7 @@ enum Variant {
|
|||||||
// the length relative to the base so we can seek relative without querying the position.
|
// the length relative to the base so we can seek relative without querying the position.
|
||||||
value_bytes: u64,
|
value_bytes: u64,
|
||||||
remainder: u64,
|
remainder: u64,
|
||||||
base: IfdPointer,
|
base: u64,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ impl<'lt, R> EntryBytesReader<'lt, R> {
|
|||||||
variant: Variant::InFile {
|
variant: Variant::InFile {
|
||||||
value_bytes,
|
value_bytes,
|
||||||
remainder: value_bytes,
|
remainder: value_bytes,
|
||||||
base: pointer,
|
base: pointer.0,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -177,7 +177,7 @@ impl<R: Read + Seek> Seek for EntryBytesReader<'_, R> {
|
|||||||
// We checked this against overflow on construction. Ignore the offset we get.
|
// We checked this against overflow on construction. Ignore the offset we get.
|
||||||
self.inner
|
self.inner
|
||||||
.inner()
|
.inner()
|
||||||
.seek(io::SeekFrom::Start(base.0 + offset))?;
|
.seek(io::SeekFrom::Start(base + offset))?;
|
||||||
|
|
||||||
self.variant = Variant::InFile {
|
self.variant = Variant::InFile {
|
||||||
value_bytes,
|
value_bytes,
|
||||||
@@ -189,6 +189,34 @@ impl<R: Read + Seek> Seek for EntryBytesReader<'_, R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stream_position(&mut self) -> io::Result<u64> {
|
||||||
|
Ok(match &self.variant {
|
||||||
|
Variant::Immediate { data, value_bytes } => {
|
||||||
|
// Recall the value is at the end of the buffer.
|
||||||
|
let from_end = OFFSET_BUFFER_LEN as u64 - data.position();
|
||||||
|
u64::from(*value_bytes) - from_end
|
||||||
|
}
|
||||||
|
// Note: we might think about using the inner `stream_position` here. However that
|
||||||
|
// exposes something interesting: If the internal stream is modified concurrently from
|
||||||
|
// another instance (e.g. a shared `File`) then we may lose track of it anyways.
|
||||||
|
// Especially the `Read` implementation is not bounded on `Seek` so it could not
|
||||||
|
// acquire the position. And it should not since that is just a different form of race
|
||||||
|
// where we risk the position being modified by a different thread just as well.
|
||||||
|
//
|
||||||
|
// So in the interest of least surprise we just assume that we can track the position
|
||||||
|
// accurately here and that the inner reader uses for instance `read_at` consistently.
|
||||||
|
// We own the value (or mutable reference) it after all.
|
||||||
|
//
|
||||||
|
// Also this one MUST be compatible with `seek(SeekFrom::Current(0))` as per trait
|
||||||
|
// defintion—so at least keep those consistent if changing anything.
|
||||||
|
&Variant::InFile {
|
||||||
|
remainder,
|
||||||
|
value_bytes,
|
||||||
|
..
|
||||||
|
} => value_bytes - remainder,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -214,6 +242,8 @@ mod tests {
|
|||||||
.find_tag_buf(tags::Tag::StripByteCounts, &mut buffer)
|
.find_tag_buf(tags::Tag::StripByteCounts, &mut buffer)
|
||||||
.expect("some strip byte count is present");
|
.expect("some strip byte count is present");
|
||||||
|
|
||||||
|
let bytes_of_value = buffer.as_bytes().len() as u64;
|
||||||
|
|
||||||
let mut reader = decoder
|
let mut reader = decoder
|
||||||
.read_tag(tags::Tag::StripByteCounts)?
|
.read_tag(tags::Tag::StripByteCounts)?
|
||||||
.expect("some strip byte count is present");
|
.expect("some strip byte count is present");
|
||||||
@@ -222,18 +252,22 @@ mod tests {
|
|||||||
reader.read_to_end(&mut data)?;
|
reader.read_to_end(&mut data)?;
|
||||||
|
|
||||||
// `ValueBuffer` also stores its data in the file endianess until converted.
|
// `ValueBuffer` also stores its data in the file endianess until converted.
|
||||||
|
assert_eq!(reader.stream_position()?, bytes_of_value);
|
||||||
assert_eq!(buffer.as_bytes(), data);
|
assert_eq!(buffer.as_bytes(), data);
|
||||||
|
|
||||||
reader.seek(SeekFrom::Start(0))?;
|
reader.seek(SeekFrom::Start(0))?;
|
||||||
|
assert_eq!(reader.stream_position()?, 0);
|
||||||
reader.read_to_end(&mut data)?;
|
reader.read_to_end(&mut data)?;
|
||||||
|
assert_eq!(reader.stream_position()?, bytes_of_value);
|
||||||
|
|
||||||
let (first, second) = data.split_at(data.len() / 2);
|
let (first, second) = data.split_at(bytes_of_value as usize);
|
||||||
assert_eq!(first, second);
|
assert_eq!(first, second);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[allow(clippy::seek_from_current)] // We want to test that functionality.
|
||||||
fn seek_from_end() -> Result<(), crate::error::TiffError> {
|
fn seek_from_end() -> Result<(), crate::error::TiffError> {
|
||||||
const ARTIST: &str = "99 little bugs in the code";
|
const ARTIST: &str = "99 little bugs in the code";
|
||||||
|
|
||||||
@@ -265,6 +299,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(reader.seek(SeekFrom::End(0))?, ARTIST.len() as u64 + 1);
|
assert_eq!(reader.seek(SeekFrom::End(0))?, ARTIST.len() as u64 + 1);
|
||||||
assert!(reader.seek(SeekFrom::End(1)).is_err());
|
assert!(reader.seek(SeekFrom::End(1)).is_err());
|
||||||
|
assert_eq!(reader.stream_position()?, ARTIST.len() as u64 + 1);
|
||||||
|
|
||||||
// Check relative seeks by comparing against the "code"
|
// Check relative seeks by comparing against the "code"
|
||||||
let code_offset = reader.seek(SeekFrom::End(-5))?;
|
let code_offset = reader.seek(SeekFrom::End(-5))?;
|
||||||
@@ -273,11 +308,15 @@ mod tests {
|
|||||||
assert_eq!(is_code, *value.last_chunk::<4>().unwrap());
|
assert_eq!(is_code, *value.last_chunk::<4>().unwrap());
|
||||||
|
|
||||||
assert_eq!(reader.seek(SeekFrom::Start(0))?, 0);
|
assert_eq!(reader.seek(SeekFrom::Start(0))?, 0);
|
||||||
|
assert_eq!(reader.stream_position()?, 0);
|
||||||
assert_eq!(reader.seek(SeekFrom::Current(0))?, 0);
|
assert_eq!(reader.seek(SeekFrom::Current(0))?, 0);
|
||||||
|
assert_eq!(reader.stream_position()?, 0);
|
||||||
assert!(reader.seek(SeekFrom::Current(-1)).is_err());
|
assert!(reader.seek(SeekFrom::Current(-1)).is_err());
|
||||||
|
assert_eq!(reader.stream_position()?, 0);
|
||||||
|
|
||||||
reader.seek(SeekFrom::Start(code_offset))?;
|
reader.seek(SeekFrom::Start(code_offset))?;
|
||||||
reader.read_exact(&mut is_code)?;
|
reader.read_exact(&mut is_code)?;
|
||||||
|
assert_eq!(reader.stream_position()?, ARTIST.len() as u64);
|
||||||
assert_eq!(is_code, *value.last_chunk::<4>().unwrap());
|
assert_eq!(is_code, *value.last_chunk::<4>().unwrap());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in new issue
Block a user