qubit_io/std_io/ext/read_seek_ext.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9//! Position-preserving extension operations for seekable standard readers.
10
11use std::io::{
12 Read,
13 Result,
14 Seek,
15 SeekFrom,
16};
17
18use crate::std_io::{
19 ReadSeek,
20 ext::internal::read_ext_impl,
21};
22
23/// Extension methods for values that implement both [`Read`] and [`Seek`].
24///
25/// `ReadSeekExt` provides position-preserving read helpers for common
26/// inspection use cases such as file signature checks, MIME detection, and
27/// random-offset probing.
28pub trait ReadSeekExt: Read + Seek {
29 /// Reads from the current position and restores the original position.
30 ///
31 /// This method has the same partial-EOF semantics as
32 /// [`crate::std_io::ext::ReadExt::read_exact_or_eof`], but it leaves the
33 /// stream positioned where it was before the call when restoration
34 /// succeeds.
35 ///
36 /// # Parameters
37 /// - `buffer`: Destination buffer to fill.
38 ///
39 /// # Returns
40 /// The number of bytes written into `buffer`.
41 ///
42 /// # Errors
43 /// Returns an error when reading the current position, reading bytes, or
44 /// restoring the original position fails. If both reading and restoration
45 /// fail, the restoration error is returned because the caller's stream
46 /// position contract was not preserved.
47 fn peek_exact_or_eof(&mut self, buffer: &mut [u8]) -> Result<usize>;
48
49 /// Reads from `offset` and restores the original position.
50 ///
51 /// This method seeks to `offset`, reads until `buffer` is full or EOF is
52 /// reached, and then restores the position that was current before the
53 /// call.
54 ///
55 /// # Parameters
56 /// - `offset`: Absolute byte offset from the start of the stream.
57 /// - `buffer`: Destination buffer to fill.
58 ///
59 /// # Returns
60 /// The number of bytes written into `buffer`.
61 ///
62 /// # Errors
63 /// Returns an error when reading the current position, seeking to `offset`,
64 /// reading bytes, or restoring the original position fails. If restoration
65 /// fails, the restoration error is returned.
66 fn read_exact_or_eof_at(
67 &mut self,
68 offset: u64,
69 buffer: &mut [u8],
70 ) -> Result<usize>;
71}
72
73/// Implements a position-preserving read through a type-erased stream.
74///
75/// # Parameters
76/// - `reader`: Stream to read and restore.
77/// - `buffer`: Destination buffer.
78///
79/// # Returns
80/// The number of bytes written into `buffer`.
81///
82/// # Errors
83/// Returns an error when reading, querying, or restoring the stream position
84/// fails.
85fn peek_exact_or_eof_impl(
86 reader: &mut dyn ReadSeek,
87 buffer: &mut [u8],
88) -> Result<usize> {
89 let position = reader.stream_position()?;
90 let read_result = read_ext_impl::read_exact_or_eof(reader, buffer);
91 let restore_result = reader.seek(SeekFrom::Start(position));
92 match (read_result, restore_result) {
93 (Ok(count), Ok(_)) => Ok(count),
94 (Err(error), Ok(_)) => Err(error),
95 (_, Err(error)) => Err(error),
96 }
97}
98
99/// Implements an offset read through a type-erased stream.
100///
101/// # Parameters
102/// - `reader`: Stream to seek, read, and restore.
103/// - `offset`: Absolute byte offset to read from.
104/// - `buffer`: Destination buffer.
105///
106/// # Returns
107/// The number of bytes written into `buffer`.
108///
109/// # Errors
110/// Returns an error when querying, changing, or restoring the stream position,
111/// or when reading fails.
112fn read_exact_or_eof_at_impl(
113 reader: &mut dyn ReadSeek,
114 offset: u64,
115 buffer: &mut [u8],
116) -> Result<usize> {
117 let position = reader.stream_position()?;
118 let read_result = match reader.seek(SeekFrom::Start(offset)) {
119 Ok(_) => read_ext_impl::read_exact_or_eof(reader, buffer),
120 Err(error) => Err(error),
121 };
122 let restore_result = reader.seek(SeekFrom::Start(position));
123 match (read_result, restore_result) {
124 (Ok(count), Ok(_)) => Ok(count),
125 (Err(error), Ok(_)) => Err(error),
126 (_, Err(error)) => Err(error),
127 }
128}
129
130impl<T> ReadSeekExt for T
131where
132 T: Read + Seek + ?Sized,
133{
134 /// Reads from the current position and restores it afterward.
135 ///
136 /// # Parameters
137 /// - `buffer`: Destination buffer.
138 ///
139 /// # Returns
140 /// The number of bytes written into `buffer`.
141 ///
142 /// # Errors
143 /// Returns an error when reading, querying, or restoring the stream
144 /// position fails.
145 #[inline(always)]
146 fn peek_exact_or_eof(&mut self, buffer: &mut [u8]) -> Result<usize> {
147 let mut reader = self;
148 peek_exact_or_eof_impl(&mut reader, buffer)
149 }
150
151 /// Reads from an absolute offset and restores the original position.
152 ///
153 /// # Parameters
154 /// - `offset`: Absolute byte offset to read from.
155 /// - `buffer`: Destination buffer.
156 ///
157 /// # Returns
158 /// The number of bytes written into `buffer`.
159 ///
160 /// # Errors
161 /// Returns an error when querying, changing, or restoring the stream
162 /// position, or when reading fails.
163 #[inline(always)]
164 fn read_exact_or_eof_at(
165 &mut self,
166 offset: u64,
167 buffer: &mut [u8],
168 ) -> Result<usize> {
169 let mut reader = self;
170 read_exact_or_eof_at_impl(&mut reader, offset, buffer)
171 }
172}