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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/*******************************************************************************
*
* Copyright (c) 2026 Haixing Hu.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0.
*
******************************************************************************/
//! Buffered converter adapter backed by two low-level codecs.
use core::{
fmt,
hash::{
Hash,
Hasher,
},
};
use super::{
BufferedConvertEngine,
BufferedConverter,
BufferedTranscoder,
FinishError,
TranscodeProgress,
TranscodeStatus,
codec_buffered_convert_hooks::CodecBufferedConvertHooks,
};
use crate::{
CapacityError,
Codec,
CodecConvertError,
};
/// Strict codec-backed converter error type.
type CodecBufferedConvertError<D, E> = CodecConvertError<<D as Codec>::DecodeError, <E as Codec>::EncodeError>;
/// Converts source units to target units through a decoded value by using codecs.
///
/// The converter decodes one source value with the decoder codec, then encodes
/// that value with the encoder codec. If the current output buffer cannot hold
/// the encoded value, the already decoded value is retained by the common
/// converter engine and must be drained before more source input is consumed.
/// Incomplete source tails are left in the caller-provided input slice; callers
/// own input-buffer refill and EOF incomplete-tail policy.
///
/// # Type Parameters
///
/// - `D`: Low-level codec used to decode source units.
/// - `E`: Low-level codec used to encode target units.
pub struct CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
{
/// Common buffered converter engine.
engine: BufferedConvertEngine<D, E, CodecBufferedConvertHooks>,
}
impl<D, E> Clone for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
BufferedConvertEngine<D, E, CodecBufferedConvertHooks>: Clone,
{
/// Clones the wrapped converter engine.
///
/// # Returns
///
/// Returns a cloned converter adapter sharing the same inner engine state.
#[inline(always)]
fn clone(&self) -> Self {
Self {
engine: self.engine.clone(),
}
}
}
impl<D, E> fmt::Debug for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
BufferedConvertEngine<D, E, CodecBufferedConvertHooks>: fmt::Debug,
{
/// Formats the wrapped converter engine for debugging.
///
/// # Parameters
///
/// - `f`: Destination formatter.
///
/// # Returns
///
/// Returns `fmt::Result` from the formatter.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CodecBufferedConverter")
.field("engine", &self.engine)
.finish()
}
}
impl<D, E> Default for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
BufferedConvertEngine<D, E, CodecBufferedConvertHooks>: Default,
{
/// Creates a default codec-backed buffered converter.
///
/// # Returns
///
/// Returns a converter with default codecs and hooks.
#[inline(always)]
fn default() -> Self {
Self {
engine: BufferedConvertEngine::default(),
}
}
}
impl<D, E> Eq for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
BufferedConvertEngine<D, E, CodecBufferedConvertHooks>: Eq,
{
}
impl<D, E> Hash for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
BufferedConvertEngine<D, E, CodecBufferedConvertHooks>: Hash,
{
/// Hashes the wrapped converter engine.
///
/// # Parameters
///
/// - `state`: Output hash state.
///
/// # Returns
///
/// Returns unit `()`.
#[inline(always)]
fn hash<S: Hasher>(&self, state: &mut S) {
self.engine.hash(state);
}
}
impl<D, E> PartialEq for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
BufferedConvertEngine<D, E, CodecBufferedConvertHooks>: PartialEq,
{
/// Compares the wrapped converter engine.
///
/// # Parameters
///
/// - `other`: Another converter to compare with.
///
/// # Returns
///
/// Returns `true` when the wrapped engines are equal.
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.engine == other.engine
}
}
impl<D, E> CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
{
/// Creates a buffered converter backed by decoder and encoder codecs.
///
/// # Parameters
///
/// - `decoder`: Low-level codec used to decode source units.
/// - `encoder`: Low-level codec used to encode target units.
///
/// # Returns
///
/// Returns a buffered converter adapter for the supplied codecs.
#[must_use]
#[inline(always)]
pub fn new(decoder: D, encoder: E) -> Self {
Self {
engine: BufferedConvertEngine::new(decoder, encoder, CodecBufferedConvertHooks::new()),
}
}
/// Returns an upper bound for target units produced from `input_len` units.
///
/// This concrete adapter method is available even when `D::Value` does not
/// implement [`Default`].
///
/// # Parameters
///
/// - `input_len`: Source units the caller plans to convert.
///
/// # Returns
///
/// Returns a conservative upper bound for produced target units.
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
self.engine.max_output_len(input_len)
}
/// Returns the maximum target units emitted by finishing internal state.
///
/// # Returns
///
/// Returns a conservative upper bound for remaining converter-final output.
#[must_use = "capacity planning can fail on overflow"]
#[inline(always)]
pub fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
self.engine.max_finish_output_len()
}
/// Clears retained pending output and hook state.
///
/// # Returns
///
/// Returns unit `()`.
#[inline(always)]
pub fn reset(&mut self) {
self.engine.reset();
}
/// Converts source units into target units.
///
/// This is the main streaming operation and does not require `D::Value` to
/// implement [`Default`].
///
/// # Parameters
///
/// - `input`: Source unit slice.
/// - `input_index`: Absolute source index where conversion starts.
/// - `output`: Target unit slice.
/// - `output_index`: Absolute target index where writing starts.
///
/// # Returns
///
/// Returns conversion progress for consumed/produced counters and stop
/// reason.
///
/// # Errors
///
/// Returns converter error when source or target indices are invalid, or
/// when decoding/encoding fails under current policy.
#[inline(always)]
pub fn transcode(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, CodecBufferedConvertError<D, E>> {
self.engine.transcode(input, input_index, output, output_index)
}
/// Finishes internally retained output after EOF.
///
/// The strict codec-backed converter has no hook-owned final output. Finish
/// drains any retained decoded value through the normal conversion path and
/// then completes without requiring `D::Value: Default`.
///
/// # Parameters
///
/// - `output`: Target unit slice for finalization output.
/// - `output_index`: Absolute target output index where writing starts.
///
/// # Returns
///
/// Returns the number of target units written by finalization.
///
/// # Errors
///
/// Returns a finish error for pending output that cannot be finalized.
#[inline(always)]
pub fn finish(
&mut self,
output: &mut [E::Unit],
output_index: usize,
) -> Result<usize, FinishError<CodecBufferedConvertError<D, E>>> {
let required = self.max_finish_output_len().map_err(FinishError::capacity)?;
FinishError::ensure_output_capacity(output.len(), output_index, required)?;
let empty_input: &[D::Unit] = &[];
let progress = self
.transcode(empty_input, 0, output, output_index)
.map_err(FinishError::source)?;
match progress.status() {
TranscodeStatus::Complete => Ok(progress.written()),
TranscodeStatus::NeedInput { .. } => {
unreachable!("codec converter finish uses empty input and strict no-op decode finish hooks")
}
TranscodeStatus::NeedOutput { .. } => {
unreachable!("codec converter finish reserves the complete pending-output bound before draining")
}
}
}
}
impl<D, E> BufferedTranscoder<D::Unit, E::Unit> for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
{
type Error = CodecConvertError<D::DecodeError, E::EncodeError>;
/// Returns an upper bound for target units produced from `input_len` units.
///
/// # Parameters
///
/// - `input_len`: Source units the caller plans to convert.
///
/// # Returns
///
/// Returns a conservative upper bound for produced target units.
#[inline(always)]
fn max_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
CodecBufferedConverter::max_output_len(self, input_len)
}
/// Returns the maximum target units emitted by finishing internal state.
///
/// # Returns
///
/// Returns a conservative upper bound for remaining converter-final output.
#[inline(always)]
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
CodecBufferedConverter::max_finish_output_len(self)
}
/// Clears retained pending output.
///
/// # Returns
///
/// Returns unit `()`.
#[inline(always)]
fn reset(&mut self) {
CodecBufferedConverter::reset(self);
}
/// Converts source units into target units.
///
/// # Parameters
///
/// - `input`: Source unit slice.
/// - `input_index`: Absolute source index where conversion starts.
/// - `output`: Target unit slice.
/// - `output_index`: Absolute target index where writing starts.
///
/// # Returns
///
/// Returns conversion progress for consumed/produced counters and stop reason.
///
/// # Errors
///
/// Returns converter error when source or target indices are invalid, or
/// when decoding/encoding fails under current policy.
#[inline(always)]
fn transcode(
&mut self,
input: &[D::Unit],
input_index: usize,
output: &mut [E::Unit],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
CodecBufferedConverter::transcode(self, input, input_index, output, output_index)
}
/// Finishes internally retained output after EOF.
///
/// # Parameters
///
/// - `output`: Target unit slice for finalization output.
/// - `output_index`: Absolute target output index where writing starts.
///
/// # Returns
///
/// Returns the number of target units written by finalization.
///
/// # Errors
///
/// Returns a finish error for pending output that cannot be finalized.
#[inline(always)]
fn finish(&mut self, output: &mut [E::Unit], output_index: usize) -> Result<usize, FinishError<Self::Error>> {
CodecBufferedConverter::finish(self, output, output_index)
}
}
impl<D, E> BufferedConverter<D::Unit, E::Unit> for CodecBufferedConverter<D, E>
where
D: Codec,
E: Codec<Value = D::Value>,
{
}