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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/*******************************************************************************
*
* Copyright (c) 2026 Haixing Hu.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0.
*
******************************************************************************/
//! Mutable state for one buffered conversion call.
use core::num::NonZeroUsize;
use super::{
decode_context::DecodeContext,
transcode_progress::TranscodeProgress,
};
/// Mutable state for one buffered conversion call.
///
/// `ConvertState` is an internal cursor helper owned by
/// [`crate::BufferedConvertEngine`]. Hook implementations receive narrower
/// context objects and never own converter cursor state.
pub(crate) struct ConvertState<'a, Input, Output> {
/// Complete input unit slice visible to the converter.
input: &'a [Input],
/// Absolute input index where this call starts.
input_start: usize,
/// Complete output unit slice visible to the converter.
output: &'a mut [Output],
/// Absolute output index where this call starts.
output_start: usize,
/// Absolute input index for the next conversion step.
input_cursor: usize,
/// Absolute output index for the next write.
output_cursor: usize,
}
impl<'a, Input, Output> ConvertState<'a, Input, Output> {
/// Creates mutable conversion state.
///
/// # Parameters
///
/// - `input`: Complete input unit slice visible to the converter.
/// - `input_index`: Absolute input index where conversion starts.
/// - `output`: Complete output unit slice visible to the converter.
/// - `output_index`: Absolute output index where writing starts.
///
/// # Returns
///
/// Returns initialized conversion state with cursors at the requested start
/// positions.
#[must_use]
#[inline(always)]
pub(crate) fn new(input: &'a [Input], input_index: usize, output: &'a mut [Output], output_index: usize) -> Self {
debug_assert!(input_index <= input.len(), "input index must be within the input slice");
Self {
input,
input_start: input_index,
output,
output_start: output_index,
input_cursor: input_index,
output_cursor: output_index,
}
}
/// Returns the complete input slice.
///
/// # Type Parameters
///
/// - `Input`: Source unit type visible to conversion.
/// - `Output`: Target unit type visible to conversion.
///
/// # Returns
///
/// Returns the full input slice.
#[must_use]
#[inline(always)]
pub(crate) fn input(&self) -> &[Input] {
self.input
}
/// Returns the complete output slice mutably.
///
/// # Returns
///
/// Returns the full mutable output slice.
#[inline(always)]
pub(crate) fn output_mut(&mut self) -> &mut [Output] {
self.output
}
/// Returns the current output cursor.
///
/// # Returns
///
/// Returns current output cursor.
#[must_use]
#[inline(always)]
pub(crate) const fn output_cursor(&self) -> usize {
self.output_cursor
}
/// Returns whether there is still input to convert.
///
/// # Returns
///
/// Returns `true` when more input units remain.
#[must_use]
#[inline(always)]
pub(crate) fn has_input(&self) -> bool {
self.input_cursor < self.input.len()
}
/// Returns input units visible from the current input cursor.
///
/// # Returns
///
/// Returns remaining input units visible from `input_cursor`.
#[must_use]
#[inline(always)]
pub(crate) fn available_input(&self) -> usize {
self.input.len() - self.input_cursor
}
/// Returns writable output units visible from the current output cursor.
///
/// # Returns
///
/// Returns remaining writable output capacity from `output_cursor`.
#[must_use]
#[inline(always)]
pub(crate) fn available_output(&self) -> usize {
self.output.len().saturating_sub(self.output_cursor)
}
/// Returns a public decode context snapshot at the current cursors.
///
/// # Returns
///
/// Returns context values suitable for decode-error hook dispatch.
#[must_use]
#[inline(always)]
pub(crate) fn decode_context(&self) -> DecodeContext {
DecodeContext::new(
self.input_start,
self.input_cursor,
self.output_start,
self.output_cursor,
self.available_input(),
)
}
/// Advances the input cursor.
///
/// # Parameters
///
/// - `read`: Number of input units consumed by the conversion step.
#[inline(always)]
pub(crate) fn advance_input(&mut self, read: usize) {
assert!(read <= self.available_input(), "conversion step read beyond input");
self.input_cursor += read;
}
/// Advances the output cursor.
///
/// # Parameters
///
/// - `written`: Number of output units written by the conversion step.
#[inline(always)]
pub(crate) fn advance_output(&mut self, written: usize) {
assert!(
written <= self.available_output(),
"conversion step wrote beyond output",
);
self.output_cursor += written;
}
/// Returns input units consumed since this call started.
///
/// # Returns
///
/// Returns consumed input units relative to `input_start`.
#[must_use]
#[inline(always)]
pub(crate) const fn read(&self) -> usize {
self.input_cursor - self.input_start
}
/// Returns output units written since this call started.
///
/// # Returns
///
/// Returns written output units relative to `output_start`.
#[must_use]
#[inline(always)]
pub(crate) const fn written(&self) -> usize {
self.output_cursor - self.output_start
}
/// Returns completed progress for the current cursors.
///
/// # Returns
///
/// Returns [`TranscodeProgress::complete`]-style state.
#[must_use]
#[inline(always)]
pub(crate) fn complete_progress(&self) -> TranscodeProgress {
TranscodeProgress::complete(self.read(), self.written())
}
/// Returns progress for missing input.
///
/// # Parameters
///
/// - `additional`: Additional input units required to continue.
/// - `available`: Input units currently available at the stop boundary.
///
/// # Returns
///
/// Returns [`TranscodeProgress`] with [`TranscodeStatus::NeedInput`].
#[must_use]
#[inline(always)]
pub(crate) fn need_input_progress(&self, additional: NonZeroUsize, available: usize) -> TranscodeProgress {
TranscodeProgress::need_input(self.input_cursor, additional, available, self.read(), self.written())
}
/// Returns progress for missing output.
///
/// # Parameters
///
/// - `additional`: Additional output units required to continue.
/// - `available`: Output units currently available at the stop boundary.
///
/// # Returns
///
/// Returns [`TranscodeProgress`] with [`TranscodeStatus::NeedOutput`].
#[must_use]
#[inline(always)]
pub(crate) fn need_output_progress(&self, additional: NonZeroUsize, available: usize) -> TranscodeProgress {
TranscodeProgress::need_output(self.output_cursor, additional, available, self.read(), self.written())
}
}