Skip to content

Commit 60dabef

Browse files
committed
Auto merge of #138023 - melrief:97227_impl_Seek_for_Take, r=tgross35
Add `std::io::Seek` instance for `std::io::Take` Library tracking issue [#97227](#97227). ACP: rust-lang/libs-team#555 1. add a `len` field to `Take` to keep track of the original number of bytes that `Take` could read 2. add a `position()` method to return the current position of the cursor inside `Take` 3. implement `std::io::Seek` for `std::io::Take` Closes: rust-lang/libs-team#555
2 parents 59372f2 + c8f5ff8 commit 60dabef

File tree

3 files changed

+173
-1
lines changed

3 files changed

+173
-1
lines changed

library/std/src/io/mod.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1214,7 +1214,7 @@ pub trait Read {
12141214
where
12151215
Self: Sized,
12161216
{
1217-
Take { inner: self, limit }
1217+
Take { inner: self, len: limit, limit }
12181218
}
12191219
}
12201220

@@ -2830,6 +2830,7 @@ impl<T, U> SizeHint for Chain<T, U> {
28302830
#[derive(Debug)]
28312831
pub struct Take<T> {
28322832
inner: T,
2833+
len: u64,
28332834
limit: u64,
28342835
}
28352836

@@ -2864,6 +2865,12 @@ impl<T> Take<T> {
28642865
self.limit
28652866
}
28662867

2868+
/// Returns the number of bytes read so far.
2869+
#[unstable(feature = "seek_io_take_position", issue = "97227")]
2870+
pub fn position(&self) -> u64 {
2871+
self.len - self.limit
2872+
}
2873+
28672874
/// Sets the number of bytes that can be read before this instance will
28682875
/// return EOF. This is the same as constructing a new `Take` instance, so
28692876
/// the amount of bytes read and the previous limit value don't matter when
@@ -2889,6 +2896,7 @@ impl<T> Take<T> {
28892896
/// ```
28902897
#[stable(feature = "take_set_limit", since = "1.27.0")]
28912898
pub fn set_limit(&mut self, limit: u64) {
2899+
self.len = limit;
28922900
self.limit = limit;
28932901
}
28942902

@@ -3076,6 +3084,49 @@ impl<T> SizeHint for Take<T> {
30763084
}
30773085
}
30783086

3087+
#[stable(feature = "seek_io_take", since = "CURRENT_RUSTC_VERSION")]
3088+
impl<T: Seek> Seek for Take<T> {
3089+
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
3090+
let new_position = match pos {
3091+
SeekFrom::Start(v) => Some(v),
3092+
SeekFrom::Current(v) => self.position().checked_add_signed(v),
3093+
SeekFrom::End(v) => self.len.checked_add_signed(v),
3094+
};
3095+
let new_position = match new_position {
3096+
Some(v) if v <= self.len => v,
3097+
_ => return Err(ErrorKind::InvalidInput.into()),
3098+
};
3099+
while new_position != self.position() {
3100+
if let Some(offset) = new_position.checked_signed_diff(self.position()) {
3101+
self.inner.seek_relative(offset)?;
3102+
self.limit = self.limit.wrapping_sub(offset as u64);
3103+
break;
3104+
}
3105+
let offset = if new_position > self.position() { i64::MAX } else { i64::MIN };
3106+
self.inner.seek_relative(offset)?;
3107+
self.limit = self.limit.wrapping_sub(offset as u64);
3108+
}
3109+
Ok(new_position)
3110+
}
3111+
3112+
fn stream_len(&mut self) -> Result<u64> {
3113+
Ok(self.len)
3114+
}
3115+
3116+
fn stream_position(&mut self) -> Result<u64> {
3117+
Ok(self.position())
3118+
}
3119+
3120+
fn seek_relative(&mut self, offset: i64) -> Result<()> {
3121+
if !self.position().checked_add_signed(offset).is_some_and(|p| p <= self.len) {
3122+
return Err(ErrorKind::InvalidInput.into());
3123+
}
3124+
self.inner.seek_relative(offset)?;
3125+
self.limit = self.limit.wrapping_sub(offset as u64);
3126+
Ok(())
3127+
}
3128+
}
3129+
30793130
/// An iterator over `u8` values of a reader.
30803131
///
30813132
/// This struct is generally created by calling [`bytes`] on a reader.

library/std/src/io/tests.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,126 @@ fn seek_position() -> io::Result<()> {
416416
Ok(())
417417
}
418418

419+
#[test]
420+
fn take_seek() -> io::Result<()> {
421+
let mut buf = Cursor::new(b"0123456789");
422+
buf.set_position(2);
423+
let mut take = buf.by_ref().take(4);
424+
let mut buf1 = [0u8; 1];
425+
let mut buf2 = [0u8; 2];
426+
assert_eq!(take.position(), 0);
427+
428+
assert_eq!(take.seek(SeekFrom::Start(0))?, 0);
429+
take.read_exact(&mut buf2)?;
430+
assert_eq!(buf2, [b'2', b'3']);
431+
assert_eq!(take.seek(SeekFrom::Start(1))?, 1);
432+
take.read_exact(&mut buf2)?;
433+
assert_eq!(buf2, [b'3', b'4']);
434+
assert_eq!(take.seek(SeekFrom::Start(2))?, 2);
435+
take.read_exact(&mut buf2)?;
436+
assert_eq!(buf2, [b'4', b'5']);
437+
assert_eq!(take.seek(SeekFrom::Start(3))?, 3);
438+
take.read_exact(&mut buf1)?;
439+
assert_eq!(buf1, [b'5']);
440+
assert_eq!(take.seek(SeekFrom::Start(4))?, 4);
441+
assert_eq!(take.read(&mut buf1)?, 0);
442+
443+
assert_eq!(take.seek(SeekFrom::End(0))?, 4);
444+
assert_eq!(take.seek(SeekFrom::End(-1))?, 3);
445+
take.read_exact(&mut buf1)?;
446+
assert_eq!(buf1, [b'5']);
447+
assert_eq!(take.seek(SeekFrom::End(-2))?, 2);
448+
take.read_exact(&mut buf2)?;
449+
assert_eq!(buf2, [b'4', b'5']);
450+
assert_eq!(take.seek(SeekFrom::End(-3))?, 1);
451+
take.read_exact(&mut buf2)?;
452+
assert_eq!(buf2, [b'3', b'4']);
453+
assert_eq!(take.seek(SeekFrom::End(-4))?, 0);
454+
take.read_exact(&mut buf2)?;
455+
assert_eq!(buf2, [b'2', b'3']);
456+
457+
assert_eq!(take.seek(SeekFrom::Current(0))?, 2);
458+
take.read_exact(&mut buf2)?;
459+
assert_eq!(buf2, [b'4', b'5']);
460+
461+
assert_eq!(take.seek(SeekFrom::Current(-3))?, 1);
462+
take.read_exact(&mut buf2)?;
463+
assert_eq!(buf2, [b'3', b'4']);
464+
465+
assert_eq!(take.seek(SeekFrom::Current(-1))?, 2);
466+
take.read_exact(&mut buf2)?;
467+
assert_eq!(buf2, [b'4', b'5']);
468+
469+
assert_eq!(take.seek(SeekFrom::Current(-4))?, 0);
470+
take.read_exact(&mut buf2)?;
471+
assert_eq!(buf2, [b'2', b'3']);
472+
473+
assert_eq!(take.seek(SeekFrom::Current(2))?, 4);
474+
assert_eq!(take.read(&mut buf1)?, 0);
475+
476+
Ok(())
477+
}
478+
479+
#[test]
480+
fn take_seek_error() {
481+
let buf = Cursor::new(b"0123456789");
482+
let mut take = buf.take(2);
483+
assert!(take.seek(SeekFrom::Start(3)).is_err());
484+
assert!(take.seek(SeekFrom::End(1)).is_err());
485+
assert!(take.seek(SeekFrom::End(-3)).is_err());
486+
assert!(take.seek(SeekFrom::Current(-1)).is_err());
487+
assert!(take.seek(SeekFrom::Current(3)).is_err());
488+
}
489+
490+
struct ExampleHugeRangeOfZeroes {
491+
position: u64,
492+
}
493+
494+
impl Read for ExampleHugeRangeOfZeroes {
495+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
496+
let max = buf.len().min(usize::MAX);
497+
for i in 0..max {
498+
if self.position == u64::MAX {
499+
return Ok(i);
500+
}
501+
self.position += 1;
502+
buf[i] = 0;
503+
}
504+
Ok(max)
505+
}
506+
}
507+
508+
impl Seek for ExampleHugeRangeOfZeroes {
509+
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
510+
match pos {
511+
io::SeekFrom::Start(i) => self.position = i,
512+
io::SeekFrom::End(i) if i >= 0 => self.position = u64::MAX,
513+
io::SeekFrom::End(i) => self.position = self.position - i.unsigned_abs(),
514+
io::SeekFrom::Current(i) => {
515+
self.position = if i >= 0 {
516+
self.position.saturating_add(i.unsigned_abs())
517+
} else {
518+
self.position.saturating_sub(i.unsigned_abs())
519+
};
520+
}
521+
}
522+
Ok(self.position)
523+
}
524+
}
525+
526+
#[test]
527+
fn take_seek_big_offsets() -> io::Result<()> {
528+
let inner = ExampleHugeRangeOfZeroes { position: 1 };
529+
let mut take = inner.take(u64::MAX - 2);
530+
assert_eq!(take.seek(io::SeekFrom::Start(u64::MAX - 2))?, u64::MAX - 2);
531+
assert_eq!(take.inner.position, u64::MAX - 1);
532+
assert_eq!(take.seek(io::SeekFrom::Start(0))?, 0);
533+
assert_eq!(take.inner.position, 1);
534+
assert_eq!(take.seek(io::SeekFrom::End(-1))?, u64::MAX - 3);
535+
assert_eq!(take.inner.position, u64::MAX - 2);
536+
Ok(())
537+
}
538+
419539
// A simple example reader which uses the default implementation of
420540
// read_to_end.
421541
struct ExampleSliceReader<'a> {

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@
325325
#![feature(try_blocks)]
326326
#![feature(try_trait_v2)]
327327
#![feature(type_alias_impl_trait)]
328+
#![feature(unsigned_signed_diff)]
328329
// tidy-alphabetical-end
329330
//
330331
// Library features (core):

0 commit comments

Comments
 (0)