Skip to main content

omp_core/encoding/
base_n.rs

1//! Power-of-2 base encodings using bit manipulation.
2//!
3//! This module implements encodings where the base is a power of 2 (e.g.,
4//! Base64, Base32, Hex). These encodings use efficient bit manipulation
5//! algorithms instead of division.
6
7use core::{fmt, mem::MaybeUninit, slice, str};
8use std::{cmp::Ordering, intrinsics, io, iter::FusedIterator};
9
10use bytes::{BufMut, Bytes};
11
12use super::{error::*, fixed_arr::*, opt};
13
14/// Encoding lookup table mapping 8-bit values to output characters.
15pub type ETable = [u8; 256];
16/// Decoding lookup table mapping ASCII characters to base-N values.
17pub type DTable = [u8; 256];
18
19/// A compile-time dictionary for base-N encoding/decoding.
20///
21/// # Type Parameters
22/// - `N`: The base (must be a power of 2 and <= 256)
23/// - `PAD`: Optional padding character (0 means no padding)
24#[derive(Clone, Copy, Debug)]
25pub struct Encoding<const N: usize> {
26	dtable: DTable,
27	etable: ETable,
28	pad:    u8,
29}
30
31impl<const N: usize> Encoding<N> {
32	/// The number of bits encoded per character.
33	pub const BITS_PER_CHAR: u32 = N.trailing_zeros();
34	/// The input group size (number of bytes per encoding group).
35	pub const GROUP_IN: usize = (Self::GROUP_OUT * Self::BITS_PER_CHAR as usize) / 8;
36	/// The output group size (number of characters per encoding group).
37	pub const GROUP_OUT: usize = 8 / Self::const_gcd(8, Self::BITS_PER_CHAR as usize);
38	/// The bit mask for extracting character values.
39	pub const MASK: u8 = (N - 1) as u8;
40
41	/// Const GCD for computing group sizes.
42	const fn const_gcd(mut a: usize, mut b: usize) -> usize {
43		while b != 0 {
44			let t = a % b;
45			a = b;
46			b = t;
47		}
48		a
49	}
50
51	/// Creates a new encoding from an alphabet.
52	pub const fn new(alphabet: &[u8; N], pad: Option<u8>) -> Self {
53		assert!(N.is_power_of_two(), "base must be power of 2");
54		assert!(N <= 256, "base must be <= 256");
55
56		let mut dtable: DTable = [0xff; _];
57		let mut etable: ETable = [0; _];
58		let mut i = 0;
59		while i < N {
60			let ch = alphabet[i] as usize;
61			assert!(dtable[ch] == 0xff, "duplicate character in alphabet");
62			dtable[ch] = i as u8;
63			etable[i] = ch as u8;
64			i += 1;
65		}
66		while i < 256 {
67			etable[i] = alphabet[i % N];
68			i += 1;
69		}
70
71		let pad = if let Some(pad) = pad {
72			assert!(pad != 0, "padding char must not be 0");
73			assert!(dtable[pad as usize] == 0xff, "padding char must not be in alphabet");
74			// Use 0xfe to mark padding (0xff is for invalid chars)
75			dtable[pad as usize] = 0xfe;
76			pad
77		} else {
78			0
79		};
80
81		Self { etable, dtable, pad }
82	}
83
84	/// Add lowercase aliases for [A-Z] if present in alphabet.
85	pub const fn with_lowercase(mut self) -> Self {
86		let mut c = b'A';
87		while c <= b'Z' {
88			let idx = self.dtable[c as usize];
89			if idx != 0xff {
90				self.dtable[(c | 0x20) as usize] = idx;
91			}
92			c += 1;
93		}
94		self
95	}
96
97	/// Returns the bit mask for extracting character values.
98	#[inline(always)]
99	pub const fn mask(&self) -> u8 {
100		Self::MASK
101	}
102
103	/// Returns the output group size (number of characters per encoding group).
104	#[inline(always)]
105	pub const fn group_size_out(&self) -> usize {
106		Self::GROUP_OUT
107	}
108
109	/// Returns the input group size (number of bytes per encoding group).
110	#[inline(always)]
111	pub const fn group_size_in(&self) -> usize {
112		Self::GROUP_IN
113	}
114
115	/// Returns the padding character if padding is enabled.
116	#[inline(always)]
117	pub const fn padding(&self) -> Option<u8> {
118		if self.pad == 0 { None } else { Some(self.pad) }
119	}
120
121	/// Returns the bits per character.
122	#[inline(always)]
123	pub const fn bits_per_char(&self) -> u32 {
124		Self::BITS_PER_CHAR
125	}
126
127	/// Returns the exact encoded length for a given source byte length.
128	#[inline]
129	pub const fn encode_len(&self, src_len: usize) -> usize {
130		let bits = Self::BITS_PER_CHAR as usize;
131		let chars = (src_len * 8).div_ceil(bits);
132		if self.pad == 0 {
133			chars
134		} else {
135			let group = Self::GROUP_OUT;
136			chars.div_ceil(group) * group
137		}
138	}
139
140	/// Returns the decoded length for a given number of characters (excluding
141	/// padding).
142	#[inline]
143	pub const fn decode_len(&self, chars_len_upto_pad: usize) -> usize {
144		let bits = Self::BITS_PER_CHAR as usize;
145		(chars_len_upto_pad * bits) / 8
146	}
147
148	/// Encodes a single value (masked to valid range) to its character.
149	#[inline(always)]
150	pub const fn encode(&self, value: u8) -> u8 {
151		self.etable[value as usize]
152	}
153
154	/// Decodes a single character to its value, returning `None` for invalid
155	/// characters.
156	#[inline(always)]
157	pub const fn decode(&self, ch: u8) -> Option<u8> {
158		let val = self.dtable[ch as usize];
159		if val >= 0xfe {
160			std::hint::cold_path();
161			None
162		} else {
163			Some(val)
164		}
165	}
166
167	/// Encodes bytes into a mutable buffer, returning the number of characters
168	/// written. Routes to optimized implementation at runtime or const
169	/// fallback at compile time.
170	#[inline]
171	pub const fn encode_mut(&self, src: &[u8], dst: &mut [u8]) -> usize {
172		intrinsics::const_eval_select((self, src, dst), Self::encode_const, Self::encode_opt)
173	}
174
175	/// Decodes characters into a mutable buffer, returning the number of bytes
176	/// written. Routes to optimized implementation at runtime or const
177	/// fallback at compile time.
178	#[inline]
179	pub const fn decode_mut(&self, src: &[u8], dst: &mut [u8]) -> Result<usize> {
180		intrinsics::const_eval_select((self, src, dst), Self::decode_const, Self::decode_opt)
181	}
182
183	/// Encodes a fixed-size byte array, returning an `ArrayStr` wrapper.
184	#[inline]
185	pub const fn encode_n<const L: usize>(&self, src: &[u8; L]) -> ArrayStr<L> {
186		let mut out = [[0u8; 2]; L];
187		// Safety guard: ArrayStr<L> has 2*L bytes of capacity.
188		// Ensure the encoding actually fits to avoid silent truncation (e.g. Base64
189		// padded, L==1).
190		assert!(
191			self.encode_len(L) <= (L * 2),
192			"encode_n: capacity (2*L) insufficient for this encoding"
193		);
194		let len = self.encode_mut(src, out.as_flattened_mut());
195		ArrayStr::new(out, len)
196	}
197
198	/// Decodes a fixed-size character array, returning an `Array` wrapper.
199	#[inline]
200	pub const fn decode_n<const L: usize>(&self, src: &[u8; L]) -> Option<Array<L>> {
201		let mut out = [0u8; L];
202		let Ok(len) = self.decode_mut(src, &mut out) else {
203			return None;
204		};
205		Some(Array::new(out, len))
206	}
207
208	/// Creates an encoding writer that wraps an `io::Write` and encodes bytes
209	/// before writing them.
210	///
211	/// The returned writer buffers input, encodes it using this encoding, and
212	/// writes the encoded output to the inner writer.
213	///
214	/// # Examples
215	/// ```
216	/// use std::io::Write;
217	///
218	/// use omp_core::base64;
219	///
220	/// let mut output = Vec::new();
221	/// let mut writer = base64::STD.encode_writer(&mut output);
222	/// writer.write_all(b"Hello").unwrap();
223	/// writer.flush().unwrap();
224	/// ```
225	#[inline]
226	pub const fn encode_writer<W: io::Write>(&self, inner: W) -> EncodeWriter<W, N> {
227		EncodeWriter::new(inner, *self)
228	}
229
230	/// Creates a decoding writer that wraps an `io::Write` and decodes bytes
231	/// before writing them.
232	///
233	/// The returned writer buffers encoded input, decodes it using this
234	/// encoding, and writes the decoded output to the inner writer.
235	///
236	/// # Examples
237	/// ```
238	/// use std::io::Write;
239	///
240	/// use omp_core::base64;
241	///
242	/// let mut output = Vec::new();
243	/// {
244	/// 	let mut writer = base64::STD.decode_writer(&mut output);
245	/// 	writer.write_all(b"SGVsbG8=").unwrap();
246	/// 	writer.flush().unwrap();
247	/// }
248	/// assert_eq!(output, b"Hello");
249	/// ```
250	#[inline]
251	pub const fn decode_writer<W: io::Write>(&self, inner: W) -> DecodeWriter<W, N> {
252		DecodeWriter::new(inner, *self)
253	}
254
255	/// Fast runtime encoder (non-const) - routes to specialized paths.
256	#[doc(hidden)]
257	#[inline(always)]
258	pub fn encode_opt(this: &Self, src: &[u8], dst: &mut [u8]) -> usize {
259		if const { N == 64 } {
260			opt::enc64(src, &this.etable, dst, this.pad)
261		} else if const { N == 32 } {
262			opt::enc32(src, &this.etable, dst, this.pad)
263		} else {
264			Self::encode_const(this, src, dst)
265		}
266	}
267
268	/// Fast runtime decoder (non-const) - routes to specialized paths.
269	#[doc(hidden)]
270	#[inline(always)]
271	pub fn decode_opt(this: &Self, src: &[u8], dst: &mut [u8]) -> Result<usize> {
272		if const { N == 64 } {
273			opt::dec64(src, &this.dtable, dst, this.pad)
274		} else if const { N == 32 } {
275			opt::dec32(src, &this.dtable, dst, this.pad)
276		} else {
277			Self::decode_const(this, src, dst)
278		}
279	}
280
281	/// Regular compile-time encoder.
282	#[doc(hidden)]
283	#[inline(always)]
284	pub const fn encode_const(this: &Self, src: &[u8], dst: &mut [u8]) -> usize {
285		let bits = Self::BITS_PER_CHAR;
286		let mask = Self::MASK;
287		let group_in = Self::GROUP_IN;
288		let group_out = Self::GROUP_OUT;
289		let pad = this.pad;
290
291		let mut src_idx = 0;
292		let mut dst_idx = 0;
293
294		// Process full groups
295		while src_idx + group_in <= src.len() && dst_idx + group_out <= dst.len() {
296			let mut buffer = 0u64;
297			let mut buffer_bits = 0u32;
298
299			let mut i = 0;
300			while i < group_in {
301				buffer = (buffer << 8) | (src[src_idx] as u64);
302				buffer_bits += 8;
303				src_idx += 1;
304				i += 1;
305			}
306
307			let mut j = 0;
308			while j < group_out {
309				buffer_bits -= bits;
310				let val = ((buffer >> buffer_bits) & (mask as u64)) as u8;
311				dst[dst_idx] = this.encode(val);
312				dst_idx += 1;
313				j += 1;
314			}
315		}
316
317		// Handle remaining bytes
318		if src_idx < src.len() {
319			let mut buffer = 0u64;
320			let mut buffer_bits = 0u32;
321
322			while src_idx < src.len() {
323				buffer = (buffer << 8) | (src[src_idx] as u64);
324				buffer_bits += 8;
325				src_idx += 1;
326			}
327
328			while buffer_bits >= bits && dst_idx < dst.len() {
329				buffer_bits -= bits;
330				let val = ((buffer >> buffer_bits) & (mask as u64)) as u8;
331				dst[dst_idx] = this.encode(val);
332				dst_idx += 1;
333			}
334
335			if buffer_bits > 0 && dst_idx < dst.len() {
336				let val = ((buffer << (bits - buffer_bits)) & (mask as u64)) as u8;
337				dst[dst_idx] = this.encode(val);
338				dst_idx += 1;
339			}
340
341			// Add padding
342			if pad != 0 {
343				let expected = (src.len() * 8).div_ceil(bits as usize).div_ceil(group_out) * group_out;
344				while dst_idx < expected && dst_idx < dst.len() {
345					dst[dst_idx] = pad;
346					dst_idx += 1;
347				}
348			}
349		}
350
351		dst_idx
352	}
353
354	/// Regular compile-time decoder.
355	#[doc(hidden)]
356	#[inline(always)]
357	pub const fn decode_const(this: &Self, src: &[u8], dst: &mut [u8]) -> Result<usize> {
358		let bits = Self::BITS_PER_CHAR;
359		let pad = this.pad;
360
361		let mut src_idx = 0;
362		let mut dst_idx = 0;
363		let mut buffer = 0u64;
364		let mut buffer_bits = 0u32;
365
366		while src_idx < src.len() {
367			let ch = src[src_idx];
368			src_idx += 1;
369
370			if pad != 0 && ch == pad {
371				break;
372			}
373
374			let Some(val) = this.decode(ch) else {
375				return Err(DecodeError::InvalidCharacter(ch));
376			};
377
378			buffer = (buffer << bits) | (val as u64);
379			buffer_bits += bits;
380
381			while buffer_bits >= 8 && dst_idx < dst.len() {
382				buffer_bits -= 8;
383				dst[dst_idx] = ((buffer >> buffer_bits) & 0xff) as u8;
384				dst_idx += 1;
385			}
386		}
387
388		Ok(dst_idx)
389	}
390}
391
392// ============================================================================
393// DECODER
394// ============================================================================
395
396/// A streaming base-N decoder that converts encoded characters to bytes.
397///
398/// # Examples
399/// ```
400/// use omp_core::base64;
401/// let encoded = b"SGVsbG8=";
402/// let decoded = base64::decode(encoded).into_vec().unwrap();
403/// assert_eq!(decoded, b"Hello");
404/// ```
405#[derive(Debug, Clone)]
406pub struct Decoder<'a, const N: usize> {
407	/// Remaining source bytes
408	src:              &'a [u8],
409	/// Dictionary for decoding
410	enc:              &'a Encoding<N>,
411	/// Bit accumulator
412	buffer:           u64,
413	/// Number of bits in buffer
414	buffer_bits:      u8,
415	/// Cached bits per character
416	bits:             u8,
417	/// Remaining non-padding chars to decode (decremented as we consume)
418	remaining_nonpad: usize,
419}
420
421impl<'a, const N: usize> Decoder<'a, N> {
422	/// Creates a new decoder from a byte slice.
423	pub const fn new(src: &'a [u8], enc: &'a Encoding<N>) -> Self {
424		let bits = Encoding::<N>::BITS_PER_CHAR as u8;
425
426		// Find padding position once for O(1) len()
427		let remaining_nonpad = if enc.pad == 0 {
428			src.len()
429		} else {
430			let mut i = 0;
431			while i < src.len() {
432				if src[i] == enc.pad {
433					break;
434				}
435				i += 1;
436			}
437			i
438		};
439
440		Self { src, enc, buffer: 0, buffer_bits: 0, bits, remaining_nonpad }
441	}
442
443	/// Collects the decoded bytes into a `Vec<u8>`.
444	pub fn into_vec(self) -> Result<Vec<u8>> {
445		let cap = self.enc.decode_len(self.remaining_nonpad);
446		let mut buf = vec![0u8; cap];
447		let written = self.enc.decode_mut(self.src, &mut buf)?;
448		buf.truncate(written);
449		Ok(buf)
450	}
451
452	/// Collects the decoded bytes into a `Bytes`.
453	pub fn into_bytes(self) -> Result<Bytes> {
454		self.into_vec().map(Bytes::from)
455	}
456
457	/// Collects the decoded bytes into a slice.
458	#[inline]
459	pub const fn into_slice(self, buf: &mut [u8]) -> Result<usize> {
460		self.enc.decode_mut(self.src, buf)
461	}
462
463	/// Collects the decoded bytes into a `[u8; N]`.
464	pub fn into_array<const K: usize>(self) -> Result<[u8; K]> {
465		let mut buf = [0u8; K];
466		let n = self.into_slice(&mut buf)?;
467		if n != K {
468			return Err(DecodeError::InputTooShort);
469		}
470		Ok(buf)
471	}
472
473	/// Collects the decoded bytes into a `BufMut`.
474	pub fn into_buf<B: BufMut>(mut self, mut buf: B) -> Result<B> {
475		loop {
476			let mut n = 0;
477			// SAFETY: We do not read the uninitialized bytes, only write to them.
478			let chunk = unsafe { buf.chunk_mut().as_uninit_slice_mut() };
479
480			// If no space available, we're done (caller must provide enough capacity)
481			if chunk.is_empty() {
482				break Ok(buf);
483			}
484
485			for (b, dst) in self.by_ref().zip(&mut *chunk) {
486				dst.write(b?);
487				n += 1;
488			}
489			let exhausted = n < chunk.len();
490
491			// SAFETY: We've written `n` bytes to the buffer.
492			unsafe { buf.advance_mut(n) };
493
494			if exhausted {
495				break Ok(buf);
496			}
497		}
498	}
499
500	/// Extends an existing collection with the decoded bytes.
501	pub fn extend_into<E: Extend<u8> + ?Sized>(self, buf: &mut E) -> Result<usize> {
502		let mut n = 0;
503		for byte in self {
504			buf.extend_one(byte?);
505			n += 1;
506		}
507		Ok(n)
508	}
509
510	/// Writes the decoded bytes to an `io::Write`.
511	pub fn write_into<W: io::Write + ?Sized>(mut self, writer: &mut W) -> io::Result<usize> {
512		let mut tmp = [MaybeUninit::<u8>::uninit(); 512];
513		let mut total = 0;
514		loop {
515			let mut i = 0;
516			for dst in &mut tmp {
517				if let Some(b) = self.next() {
518					dst.write(b.map_err(io::Error::other)?);
519					i += 1;
520				} else {
521					break;
522				}
523			}
524			if i == 0 {
525				break;
526			}
527			// SAFETY: We've written exactly `i` bytes to `tmp` via MaybeUninit::write,
528			// fully initializing tmp[0..i]. slice::from_raw_parts creates a view of
529			// these initialized bytes for writing.
530			unsafe {
531				writer.write_all(slice::from_raw_parts(tmp.as_ptr().cast(), i))?;
532			}
533			total += i;
534		}
535		Ok(total)
536	}
537}
538
539impl<const N: usize> Iterator for Decoder<'_, N> {
540	type Item = Result<u8>;
541
542	fn next(&mut self) -> Option<Self::Item> {
543		let bits = self.bits;
544
545		loop {
546			// If we have enough bits, emit a byte
547			if self.buffer_bits >= 8 {
548				let shift = self.buffer_bits - 8;
549				let byte = ((self.buffer >> shift) & 0xff) as u8;
550				self.buffer_bits -= 8;
551				// No need to mask - just track buffer_bits
552				return Some(Ok(byte));
553			}
554
555			// Try to get next character
556			let ch = *self.src.split_off_first()?;
557
558			// Break on first padding (consistent with const decode_mut)
559			if self.enc.pad != 0 && ch == self.enc.pad {
560				return None;
561			}
562
563			// Decode character
564			let Some(val) = self.enc.decode(ch) else {
565				return Some(Err(DecodeError::InvalidCharacter(ch)));
566			};
567
568			// Decrement remaining non-padding count
569			self.remaining_nonpad = self.remaining_nonpad.saturating_sub(1);
570
571			// Add to buffer (pack into high end)
572			self.buffer = (self.buffer << bits) | (val as u64);
573			self.buffer_bits += bits;
574		}
575	}
576
577	fn size_hint(&self) -> (usize, Option<usize>) {
578		let n = self.len();
579		(n, Some(n))
580	}
581}
582
583impl<const N: usize> ExactSizeIterator for Decoder<'_, N> {
584	#[inline]
585	fn len(&self) -> usize {
586		// O(1) using cached remaining_nonpad
587		let bits = self.bits as usize;
588		((self.remaining_nonpad * bits) + self.buffer_bits as usize) / 8
589	}
590}
591
592impl<const N: usize> FusedIterator for Decoder<'_, N> {}
593
594impl<const N: usize, const K: usize> TryFrom<Decoder<'_, N>> for [u8; K] {
595	type Error = DecodeError;
596
597	fn try_from(decoder: Decoder<'_, N>) -> Result<Self> {
598		decoder.into_array()
599	}
600}
601
602impl<const N: usize> fmt::Display for Decoder<'_, N> {
603	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604		for byte in self.clone() {
605			if let Ok(byte) = byte {
606				write!(f, "{byte:02x}")?;
607			} else {
608				write!(f, "??")?;
609			}
610		}
611		Ok(())
612	}
613}
614
615impl<const N: usize> PartialEq<[u8]> for Decoder<'_, N> {
616	fn eq(&self, other: &[u8]) -> bool {
617		self.clone().eq(other.iter().map(|&b| Ok(b)))
618	}
619}
620
621impl<const N: usize> PartialOrd<[u8]> for Decoder<'_, N> {
622	fn partial_cmp(&self, other: &[u8]) -> Option<Ordering> {
623		Some(self.clone().cmp(other.iter().map(|&b| Ok(b))))
624	}
625}
626
627// ============================================================================
628// ENCODER
629// ============================================================================
630
631/// Iterator that encodes bytes as individual base-N ASCII characters.
632///
633/// # Examples
634/// ```
635/// use omp_core::base64;
636/// let data = b"Hello";
637/// let encoded = base64::encode(data).into_string();
638/// assert_eq!(encoded, "SGVsbG8=");
639/// ```
640#[derive(Debug, Clone)]
641pub struct Encoder<'a, const N: usize> {
642	/// Source bytes
643	src:           &'a [u8],
644	/// Dictionary for encoding
645	enc:           &'a Encoding<N>,
646	/// Bit accumulator
647	buffer:        u64,
648	/// Number of bits in buffer
649	buffer_bits:   usize,
650	/// Characters emitted so far
651	chars_emitted: usize,
652	/// Pending padding characters
653	padding_count: usize,
654	/// Whether we're done with source
655	done:          bool,
656}
657
658impl<'a, const N: usize> Encoder<'a, N> {
659	/// Creates a new encoder.
660	pub const fn new(src: &'a [u8], enc: &'a Encoding<N>) -> Self {
661		Self { src, enc, buffer: 0, buffer_bits: 0, chars_emitted: 0, padding_count: 0, done: false }
662	}
663
664	/// Collects into a `Vec<u8>`.
665	pub fn into_vec(self) -> Vec<u8> {
666		let out_len = self.enc.encode_len(self.src.len());
667		let mut buf = vec![0u8; out_len];
668		let written = self.enc.encode_mut(self.src, &mut buf);
669		buf.truncate(written);
670		buf
671	}
672
673	/// Collects into a `Bytes`.
674	pub fn into_bytes(self) -> Bytes {
675		Bytes::from(self.into_vec())
676	}
677
678	/// Collects into a String.
679	pub fn into_string(self) -> String {
680		ascii_to_str_owned(self.into_vec())
681	}
682
683	/// Extends into an existing buffer.
684	pub fn extend_into<E: Extend<u8> + ?Sized>(self, buf: &mut E) {
685		buf.extend(self);
686	}
687
688	/// Collects into a `BufMut`.
689	pub fn into_buf<B: BufMut>(mut self, mut buf: B) -> B {
690		loop {
691			let mut n = 0;
692			// SAFETY: We do not read the uninitialized bytes, only write to them.
693			let chunk = unsafe { buf.chunk_mut().as_uninit_slice_mut() };
694
695			// If no space available, we're done (caller must provide enough capacity)
696			if chunk.is_empty() {
697				break buf;
698			}
699
700			for (b, dst) in self.by_ref().zip(&mut *chunk) {
701				dst.write(b);
702				n += 1;
703			}
704			let exhausted = n < chunk.len();
705			// SAFETY: We've written `n` bytes to the buffer.
706			unsafe { buf.advance_mut(n) };
707			if exhausted {
708				break buf;
709			}
710		}
711	}
712
713	/// Writes to an `io::Write`.
714	pub fn write_into<W: io::Write + ?Sized>(mut self, writer: &mut W) -> io::Result<usize> {
715		let mut tmp = [MaybeUninit::<u8>::uninit(); 512];
716		let mut total = 0;
717		loop {
718			let mut i = 0;
719			for dst in &mut tmp {
720				if let Some(b) = self.next() {
721					dst.write(b);
722					i += 1;
723				} else {
724					break;
725				}
726			}
727			if i == 0 {
728				break;
729			}
730			// SAFETY: We've written exactly `i` bytes to `tmp` via MaybeUninit::write,
731			// fully initializing tmp[0..i]. slice::from_raw_parts creates a view of
732			// these initialized bytes for writing.
733			unsafe {
734				writer.write_all(slice::from_raw_parts(tmp.as_ptr().cast(), i))?;
735			}
736			total += i;
737		}
738		Ok(total)
739	}
740
741	/// Writes to a `fmt::Write`.
742	pub fn format_into<W: fmt::Write + ?Sized>(self, writer: &mut W) -> fmt::Result {
743		for byte in self {
744			writer.write_char(byte as char)?;
745		}
746		Ok(())
747	}
748}
749
750impl<const N: usize> From<Encoder<'_, N>> for String {
751	fn from(encoder: Encoder<'_, N>) -> Self {
752		encoder.into_string()
753	}
754}
755
756impl<const N: usize> From<Encoder<'_, N>> for Bytes {
757	fn from(encoder: Encoder<'_, N>) -> Self {
758		encoder.into_bytes()
759	}
760}
761
762impl<const N: usize> From<Encoder<'_, N>> for Vec<u8> {
763	fn from(encoder: Encoder<'_, N>) -> Self {
764		encoder.into_vec()
765	}
766}
767
768impl<const N: usize> Iterator for Encoder<'_, N> {
769	type Item = u8;
770
771	fn next(&mut self) -> Option<Self::Item> {
772		let bits = Encoding::<N>::BITS_PER_CHAR as usize;
773		let mask = Encoding::<N>::MASK;
774
775		loop {
776			// Emit padding if any
777			if self.padding_count > 0 {
778				self.padding_count -= 1;
779				return self.enc.padding();
780			}
781
782			// Emit character if we have enough bits
783			if self.buffer_bits >= bits {
784				let shift = self.buffer_bits - bits;
785				let val = ((self.buffer >> shift) as u8) & mask;
786				self.buffer_bits -= bits;
787				self.chars_emitted += 1;
788				return Some(self.enc.encode(val));
789			}
790
791			// Refill from source
792			if let Some(byte) = self.src.split_off_first() {
793				self.buffer = (self.buffer << 8) | (*byte as u64);
794				self.buffer_bits += 8;
795				continue;
796			}
797
798			// Source exhausted
799			if !self.done {
800				self.done = true;
801
802				// Emit final partial character
803				if self.buffer_bits > 0 {
804					let val = ((self.buffer << (bits - self.buffer_bits)) as u8) & mask;
805					self.buffer_bits = 0;
806					let total_chars = self.chars_emitted + 1;
807
808					if self.enc.pad != 0 {
809						let group_out = Encoding::<N>::GROUP_OUT;
810						let rem = total_chars % group_out;
811						if rem > 0 {
812							self.padding_count = group_out - rem;
813						}
814					}
815
816					self.chars_emitted += 1;
817					return Some(self.enc.encode(val));
818				}
819
820				// Calculate padding for exact multiples
821				if self.enc.pad != 0 && self.chars_emitted > 0 {
822					let group_out = Encoding::<N>::GROUP_OUT;
823					let rem = self.chars_emitted % group_out;
824					if rem > 0 {
825						self.padding_count = group_out - rem;
826						continue;
827					}
828				}
829			}
830
831			return None;
832		}
833	}
834
835	fn size_hint(&self) -> (usize, Option<usize>) {
836		let n = self.len();
837		(n, Some(n))
838	}
839}
840
841impl<const N: usize> ExactSizeIterator for Encoder<'_, N> {
842	#[inline]
843	fn len(&self) -> usize {
844		let bits = Encoding::<N>::BITS_PER_CHAR as usize;
845		let total_bits = (self.src.len() * 8) + self.buffer_bits;
846		let chars = total_bits.div_ceil(bits) + self.padding_count;
847		if self.enc.pad != 0 {
848			let group = Encoding::<N>::GROUP_OUT;
849			chars.div_ceil(group) * group
850		} else {
851			chars
852		}
853	}
854}
855
856impl<const N: usize> FusedIterator for Encoder<'_, N> {}
857
858impl<const N: usize> serde::Serialize for Encoder<'_, N> {
859	fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
860		let len = self.enc.encode_len(self.src.len());
861		serialize(serializer, len, |buffer| self.enc.encode_mut(self.src, buffer))
862	}
863}
864
865impl<const N: usize> fmt::Display for Encoder<'_, N> {
866	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
867		format_with_precision(self.clone(), f)
868	}
869}
870
871impl<const N: usize> Eq for Encoder<'_, N> {}
872
873impl<'b, const N: usize, const M: usize> PartialEq<Encoder<'b, M>> for Encoder<'_, N> {
874	fn eq(&self, other: &Encoder<'b, M>) -> bool {
875		self.clone().eq(other.clone())
876	}
877}
878impl<const N: usize> Ord for Encoder<'_, N> {
879	fn cmp(&self, other: &Self) -> Ordering {
880		self.clone().cmp(other.clone())
881	}
882}
883impl<'b, const N: usize, const M: usize> PartialOrd<Encoder<'b, M>> for Encoder<'_, N> {
884	fn partial_cmp(&self, other: &Encoder<'b, M>) -> Option<Ordering> {
885		Some(self.clone().cmp(other.clone()))
886	}
887}
888
889impl<const N: usize> PartialEq<[u8]> for Encoder<'_, N> {
890	fn eq(&self, other: &[u8]) -> bool {
891		self.clone().eq(other.iter().copied())
892	}
893}
894
895impl<const N: usize> PartialEq<str> for Encoder<'_, N> {
896	fn eq(&self, other: &str) -> bool {
897		self.clone().eq(other.as_bytes().iter().copied())
898	}
899}
900
901impl<const N: usize> PartialOrd<[u8]> for Encoder<'_, N> {
902	fn partial_cmp(&self, other: &[u8]) -> Option<Ordering> {
903		Some(self.clone().cmp(other.iter().copied()))
904	}
905}
906
907impl<const N: usize> PartialOrd<str> for Encoder<'_, N> {
908	fn partial_cmp(&self, other: &str) -> Option<Ordering> {
909		Some(self.clone().cmp(other.as_bytes().iter().copied()))
910	}
911}
912
913// ============================================================================
914// IO WRITERS
915// ============================================================================
916
917/// Buffer size for input data (raw bytes for encoding, encoded chars for
918/// decoding).
919const INPUT_BUFFER_SIZE: usize = 768;
920/// Buffer size for output data (encoded chars for encoding, raw bytes for
921/// decoding).
922const OUTPUT_BUFFER_SIZE: usize = 2048;
923
924/// A buffered writer that encodes raw bytes before writing them to an inner
925/// writer.
926///
927/// This writer buffers input bytes, encodes them using the specified
928/// `Encoding<N>`, and writes the encoded output to the wrapped writer. It
929/// implements `io::Write`, allowing transparent encoding of data streams.
930///
931/// # Examples
932/// ```
933/// use std::io::Write;
934///
935/// use omp_core::encoding::{EncodeWriter, base64};
936///
937/// let mut output = Vec::new();
938/// let mut writer = base64::encode_writer(&mut output);
939/// writer.write_all(b"Hello, World!").unwrap();
940/// writer.flush().unwrap();
941/// // output now contains base64-encoded data
942/// ```
943pub struct EncodeWriter<W: io::Write, const N: usize> {
944	inner:      Option<W>,
945	enc:        Encoding<N>,
946	input_buf:  [MaybeUninit<u8>; INPUT_BUFFER_SIZE],
947	input_len:  usize,
948	output_buf: [u8; OUTPUT_BUFFER_SIZE],
949}
950
951impl<W: io::Write, const N: usize> EncodeWriter<W, N> {
952	/// Creates a new encoding writer that wraps the given writer.
953	pub const fn new(inner: W, enc: Encoding<N>) -> Self {
954		Self {
955			inner: Some(inner),
956			enc,
957			input_buf: [MaybeUninit::uninit(); INPUT_BUFFER_SIZE],
958			input_len: 0,
959			output_buf: [0u8; OUTPUT_BUFFER_SIZE],
960		}
961	}
962
963	/// Consumes this writer, flushing any buffered data and returning the inner
964	/// writer.
965	///
966	/// # Errors
967	/// Returns an error if flushing fails.
968	pub fn into_inner(mut self) -> io::Result<W> {
969		io::Write::flush(&mut self)?;
970		let inner = self.inner.take().expect("EncodeWriter already consumed");
971		// Prevent Drop from running since we're consuming the writer
972		core::mem::forget(self);
973		Ok(inner)
974	}
975
976	/// Encodes and flushes the input buffer to the inner writer.
977	fn flush_input(&mut self) -> io::Result<()> {
978		if self.input_len == 0 {
979			return Ok(());
980		}
981
982		// SAFETY: We have initialized input_buf[0..input_len] via writes in the write()
983		// method. The pointer cast from MaybeUninit<u8> to u8 is valid because
984		// MaybeUninit<u8> has the same layout as u8, and we've initialized these
985		// bytes.
986		let input_slice =
987			unsafe { slice::from_raw_parts(self.input_buf.as_ptr().cast::<u8>(), self.input_len) };
988
989		let encoded_len = self.enc.encode_mut(input_slice, &mut self.output_buf);
990		let encoded_data = &self.output_buf[..encoded_len];
991
992		// Write the encoded data to the inner writer
993		let inner = self.inner.as_mut().expect("EncodeWriter inner is None");
994		inner.write_all(encoded_data)?;
995
996		// Reset input buffer
997		self.input_len = 0;
998		Ok(())
999	}
1000}
1001
1002impl<W: io::Write, const N: usize> io::Write for EncodeWriter<W, N> {
1003	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1004		let mut bytes_written = 0;
1005
1006		while bytes_written < buf.len() {
1007			let available = INPUT_BUFFER_SIZE - self.input_len;
1008			if available == 0 {
1009				self.flush_input()?;
1010				continue;
1011			}
1012
1013			let to_copy = (buf.len() - bytes_written).min(available);
1014
1015			// SAFETY: We're writing to input_buf[input_len..input_len+to_copy], which is
1016			// within bounds since input_len + to_copy <= INPUT_BUFFER_SIZE. The
1017			// source pointer is valid for `to_copy` bytes. We cast from *const u8
1018			// to *mut u8 for the destination, which is safe because
1019			// MaybeUninit<u8> has the same layout as u8.
1020			unsafe {
1021				let dst = self.input_buf.as_mut_ptr().add(self.input_len).cast::<u8>();
1022				let src = buf.as_ptr().add(bytes_written);
1023				core::ptr::copy_nonoverlapping(src, dst, to_copy);
1024			}
1025
1026			self.input_len += to_copy;
1027			bytes_written += to_copy;
1028		}
1029
1030		Ok(bytes_written)
1031	}
1032
1033	fn flush(&mut self) -> io::Result<()> {
1034		self.flush_input()?;
1035		self
1036			.inner
1037			.as_mut()
1038			.expect("EncodeWriter inner is None")
1039			.flush()
1040	}
1041}
1042
1043impl<W: io::Write, const N: usize> Drop for EncodeWriter<W, N> {
1044	fn drop(&mut self) {
1045		let _ = io::Write::flush(self);
1046	}
1047}
1048
1049/// A buffered writer that decodes encoded bytes before writing them to an inner
1050/// writer.
1051///
1052/// This writer buffers encoded characters, decodes them using the specified
1053/// `Encoding<N>`, and writes the decoded output to the wrapped writer. It
1054/// implements `io::Write`, allowing transparent decoding of data streams.
1055///
1056/// Only complete encoding groups are decoded during normal writes. Incomplete
1057/// groups are kept in the buffer until flush is called, at which point they are
1058/// decoded (potentially with padding handling for base64/base32).
1059///
1060/// # Examples
1061/// ```
1062/// use std::io::Write;
1063///
1064/// use omp_core::encoding::{DecodeWriter, base64};
1065///
1066/// let mut output = Vec::new();
1067/// let mut writer = base64::decode_writer(&mut output);
1068/// writer.write_all(b"SGVsbG8sIFdvcmxkIQ==").unwrap();
1069/// writer.flush().unwrap();
1070/// drop(writer);
1071/// assert_eq!(output, b"Hello, World!");
1072/// ```
1073pub struct DecodeWriter<W: io::Write, const N: usize> {
1074	inner:      Option<W>,
1075	enc:        Encoding<N>,
1076	input_buf:  [MaybeUninit<u8>; INPUT_BUFFER_SIZE],
1077	input_len:  usize,
1078	output_buf: [u8; OUTPUT_BUFFER_SIZE],
1079}
1080
1081impl<W: io::Write, const N: usize> DecodeWriter<W, N> {
1082	/// Creates a new decoding writer that wraps the given writer.
1083	pub const fn new(inner: W, enc: Encoding<N>) -> Self {
1084		Self {
1085			inner: Some(inner),
1086			enc,
1087			input_buf: [MaybeUninit::uninit(); INPUT_BUFFER_SIZE],
1088			input_len: 0,
1089			output_buf: [0u8; OUTPUT_BUFFER_SIZE],
1090		}
1091	}
1092
1093	/// Consumes this writer, flushing any buffered data and returning the inner
1094	/// writer.
1095	///
1096	/// # Errors
1097	/// Returns an error if flushing or decoding fails.
1098	pub fn into_inner(mut self) -> io::Result<W> {
1099		io::Write::flush(&mut self)?;
1100		let inner = self.inner.take().expect("DecodeWriter already consumed");
1101		// Prevent Drop from running since we're consuming the writer
1102		core::mem::forget(self);
1103		Ok(inner)
1104	}
1105
1106	/// Decodes and flushes the input buffer to the inner writer.
1107	///
1108	/// If `final_flush` is true, all remaining input is decoded, including
1109	/// incomplete groups. Otherwise, only complete groups are decoded and
1110	/// incomplete groups are kept in the buffer.
1111	fn flush_input(&mut self, final_flush: bool) -> io::Result<()> {
1112		if self.input_len == 0 {
1113			return Ok(());
1114		}
1115
1116		let group_out = self.enc.group_size_out();
1117
1118		// Determine how many characters to decode
1119		let to_decode = if final_flush {
1120			// Decode everything, including incomplete groups
1121			self.input_len
1122		} else {
1123			// Only decode complete groups
1124			(self.input_len / group_out) * group_out
1125		};
1126
1127		if to_decode == 0 {
1128			return Ok(());
1129		}
1130
1131		// SAFETY: We have initialized input_buf[0..to_decode] via writes in the write()
1132		// method. The pointer cast from MaybeUninit<u8> to u8 is valid because
1133		// MaybeUninit<u8> has the same layout as u8, and we've initialized these
1134		// bytes.
1135		let input_slice =
1136			unsafe { slice::from_raw_parts(self.input_buf.as_ptr().cast::<u8>(), to_decode) };
1137
1138		let decoded_len = self
1139			.enc
1140			.decode_mut(input_slice, &mut self.output_buf)
1141			.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
1142		let decoded_data = &self.output_buf[..decoded_len];
1143
1144		// Write the decoded data to the inner writer
1145		let inner = self.inner.as_mut().expect("DecodeWriter inner is None");
1146		inner.write_all(decoded_data)?;
1147
1148		// Move remaining bytes to the start of the buffer
1149		let remaining = self.input_len - to_decode;
1150		if remaining > 0 {
1151			// SAFETY: We're moving initialized bytes within the buffer. The source range
1152			// [to_decode..input_len] and destination range [0..remaining] don't overlap
1153			// because remaining < to_decode (when remaining > 0). Both ranges are within
1154			// INPUT_BUFFER_SIZE.
1155			unsafe {
1156				core::ptr::copy(
1157					self.input_buf.as_ptr().add(to_decode),
1158					self.input_buf.as_mut_ptr(),
1159					remaining,
1160				);
1161			}
1162		}
1163		self.input_len = remaining;
1164
1165		Ok(())
1166	}
1167}
1168
1169impl<W: io::Write, const N: usize> io::Write for DecodeWriter<W, N> {
1170	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1171		let mut bytes_written = 0;
1172
1173		while bytes_written < buf.len() {
1174			let available = INPUT_BUFFER_SIZE - self.input_len;
1175			if available == 0 {
1176				self.flush_input(false)?;
1177				continue;
1178			}
1179
1180			let to_copy = (buf.len() - bytes_written).min(available);
1181
1182			// SAFETY: We're writing to input_buf[input_len..input_len+to_copy], which is
1183			// within bounds since input_len + to_copy <= INPUT_BUFFER_SIZE. The
1184			// source pointer is valid for `to_copy` bytes. We cast from *const u8
1185			// to *mut u8 for the destination, which is safe because
1186			// MaybeUninit<u8> has the same layout as u8.
1187			unsafe {
1188				let dst = self.input_buf.as_mut_ptr().add(self.input_len).cast::<u8>();
1189				let src = buf.as_ptr().add(bytes_written);
1190				core::ptr::copy_nonoverlapping(src, dst, to_copy);
1191			}
1192
1193			self.input_len += to_copy;
1194			bytes_written += to_copy;
1195		}
1196
1197		Ok(bytes_written)
1198	}
1199
1200	fn flush(&mut self) -> io::Result<()> {
1201		self.flush_input(true)?;
1202		self
1203			.inner
1204			.as_mut()
1205			.expect("DecodeWriter inner is None")
1206			.flush()
1207	}
1208}
1209
1210impl<W: io::Write, const N: usize> Drop for DecodeWriter<W, N> {
1211	fn drop(&mut self) {
1212		let _ = io::Write::flush(self);
1213	}
1214}
1215
1216// ============================================================================
1217// CONVENIENCE FUNCTIONS
1218// ============================================================================
1219
1220/// Defines a base-N encoding constant and its encode/decode functions.
1221macro_rules! define_encoding {
1222    ($mod_name:ident, $n:literal, $pad:literal, $alphabet:expr, $lowercase:literal) => {
1223         #[doc = concat!(
1224            stringify!($mod_name), " encoding and decoding.\n\n",
1225            "This module provides ", stringify!($mod_name), " encoding/decoding using a ",
1226            stringify!($n), "-character alphabet. Both padded and unpadded variants are available.\n\n",
1227        )]
1228        pub mod $mod_name {
1229            use super::*;
1230
1231            #[doc = concat!(
1232                "Encoding dictionary for ", stringify!($mod_name), ".\n\n",
1233                "Defines the ", stringify!($n), "-character alphabet, padding character, and ",
1234                "encoding/decoding behavior. Use [`STD`] or [`RAW`] for standard configurations."
1235            )]
1236            pub type Encoding = super::Encoding<$n>;
1237
1238            #[doc = concat!(
1239                "Streaming decoder for ", stringify!($mod_name), "-encoded data.\n\n",
1240                "Decodes ", stringify!($mod_name), " characters into bytes. ",
1241                "Create with [`decode()`] or use [`STD`]/[`RAW`] decoder methods."
1242            )]
1243            pub type Decoder<'a> = super::Decoder<'a, $n>;
1244
1245            #[doc = concat!(
1246                "Streaming encoder for ", stringify!($mod_name), " encoding.\n\n",
1247                "Encodes bytes into ", stringify!($mod_name), " characters. ",
1248                "Create with [`encode()`] or use [`STD`]/[`RAW`] encoder methods."
1249            )]
1250            pub type Encoder<'a> = super::Encoder<'a, $n>;
1251
1252            #[doc = concat!(
1253                "Standard ", stringify!($mod_name), " encoding with padding.\n\n",
1254                "Uses a ", stringify!($n), "-character alphabet with `=` padding."
1255            )]
1256            pub const STD: Encoding = {
1257                let enc = Encoding::new($alphabet, Some($pad));
1258                #[allow(unused_mut, reason = "conditional mutation in const context for lowercase flag")]
1259                let mut enc = enc;
1260                if $lowercase {
1261                    enc.with_lowercase()
1262                } else {
1263                    enc
1264                }
1265            };
1266
1267            #[doc = concat!("Padded ", stringify!($mod_name), " encoding (alias of STD).")]
1268            pub const PADDED: Encoding = STD;
1269
1270            #[doc = concat!(
1271                "Raw ", stringify!($mod_name), " encoding without padding.\n\n",
1272                "Uses a ", stringify!($n), "-character alphabet with no padding characters."
1273            )]
1274            pub const RAW: Encoding = {
1275                let enc = Encoding::new($alphabet, None);
1276                #[allow(unused_mut, reason = "conditional mutation in const context for lowercase flag")]
1277                let mut enc = enc;
1278                if $lowercase {
1279                    enc.with_lowercase()
1280                } else {
1281                    enc
1282                }
1283            };
1284
1285            #[doc = concat!(
1286                "Encodes a byte slice to ", stringify!($mod_name), " with padding.\n\n",
1287                "# Arguments\n\n",
1288                "* `src` - The byte slice to encode\n\n",
1289                "# Returns\n\n",
1290                "An iterator that yields encoded bytes.\n\n",
1291                "# Example\n\n",
1292                "```\n",
1293                "use omp_core::", stringify!($mod_name), ";\n",
1294                "let encoded = ", stringify!($mod_name), "::encode(b\"hello\").into_string();\n",
1295                "```"
1296            )]
1297            pub fn encode<S: AsRef<[u8]> + ?Sized>(src: &S) -> Encoder {
1298                Encoder::new(src.as_ref(), &STD)
1299            }
1300
1301            #[doc = concat!(
1302                "Decodes ", stringify!($mod_name), " encoded bytes with padding.\n\n",
1303                "# Arguments\n\n",
1304                "* `src` - The encoded byte slice to decode\n\n",
1305                "# Returns\n\n",
1306                "An iterator that yields decoded bytes.\n\n",
1307                "# Example\n\n",
1308                "```\n",
1309                "use omp_core::", stringify!($mod_name), ";\n",
1310                "let encoded = ", stringify!($mod_name), "::encode(b\"hello\").into_string();\n",
1311                "let decoded = ", stringify!($mod_name), "::decode(&encoded).into_vec().unwrap();\n",
1312                "assert_eq!(decoded, b\"hello\");\n",
1313                "```"
1314            )]
1315            pub fn decode<S: AsRef<[u8]> + ?Sized>(src: &S) -> Decoder {
1316                Decoder::new(src.as_ref(), &STD)
1317            }
1318
1319            #[doc = concat!(
1320                "Encodes a fixed-size byte array to ", stringify!($mod_name), " with padding.\n\n",
1321                "# Arguments\n\n",
1322                "* `src` - The byte array to encode\n\n",
1323                "# Returns\n\n",
1324                "An `ArrayStr` containing the encoded output.\n\n",
1325                "# Example\n\n",
1326                "```\n",
1327                "use omp_core::", stringify!($mod_name), ";\n",
1328                "let data = [0x48u8, 0x65, 0x6c, 0x6c, 0x6f];\n",
1329                "let encoded = ", stringify!($mod_name), "::encode_n(&data);\n",
1330                "```"
1331            )]
1332            pub const fn encode_n<const N: usize>(src: &[u8; N]) -> ArrayStr<N> {
1333                STD.encode_n(src)
1334            }
1335
1336            #[doc = concat!(
1337                "Decodes a fixed-size ", stringify!($mod_name), " array with padding.\n\n",
1338                "# Arguments\n\n",
1339                "* `src` - The encoded byte array to decode\n\n",
1340                "# Returns\n\n",
1341                "A `Result` containing the decoded byte array or an error.\n\n",
1342                "# Example\n\n",
1343                "```\n",
1344                "use omp_core::", stringify!($mod_name), ";\n",
1345                "let data = [0x48u8, 0x65, 0x6c, 0x6c, 0x6f];\n",
1346                "let encoded = ", stringify!($mod_name), "::encode(&data).into_vec();\n",
1347                "let decoded = ", stringify!($mod_name), "::decode(&encoded).into_vec().unwrap();\n",
1348                "assert_eq!(&decoded, &data);\n",
1349                "```"
1350            )]
1351            pub const fn decode_n<const N: usize>(src: &[u8; N]) -> Option<Array<N>> {
1352                STD.decode_n(src)
1353            }
1354
1355            #[doc = concat!(
1356                "Encodes a byte slice to ", stringify!($mod_name), " without padding.\n\n",
1357                "# Arguments\n\n",
1358                "* `src` - The byte slice to encode\n\n",
1359                "# Returns\n\n",
1360                "An iterator that yields encoded bytes without padding characters.\n\n",
1361                "# Example\n\n",
1362                "```\n",
1363                "use omp_core::", stringify!($mod_name), ";\n",
1364                "let encoded = ", stringify!($mod_name), "::encode_raw(b\"hello\").into_string();\n",
1365                "```"
1366            )]
1367            pub fn encode_raw<S: AsRef<[u8]> + ?Sized>(src: &S) -> Encoder {
1368                Encoder::new(src.as_ref(), &RAW)
1369            }
1370
1371            #[doc = concat!(
1372                "Decodes ", stringify!($mod_name), " encoded bytes without padding.\n\n",
1373                "# Arguments\n\n",
1374                "* `src` - The encoded byte slice to decode\n\n",
1375                "# Returns\n\n",
1376                "An iterator that yields decoded bytes.\n\n",
1377                "# Example\n\n",
1378                "```\n",
1379                "use omp_core::", stringify!($mod_name), ";\n",
1380                "let encoded = ", stringify!($mod_name), "::encode_raw(b\"hello\").into_string();\n",
1381                "let decoded = ", stringify!($mod_name), "::decode_raw(&encoded).into_vec().unwrap();\n",
1382                "assert_eq!(decoded, b\"hello\");\n",
1383                "```"
1384            )]
1385            pub fn decode_raw<S: AsRef<[u8]> + ?Sized>(src: &S) -> Decoder {
1386                Decoder::new(src.as_ref(), &RAW)
1387            }
1388
1389            #[doc = concat!(
1390                "Encodes a fixed-size byte array to ", stringify!($mod_name), " without padding.\n\n",
1391                "# Arguments\n\n",
1392                "* `src` - The byte array to encode\n\n",
1393                "# Returns\n\n",
1394                "An `ArrayStr` containing the encoded output without padding.\n\n",
1395                "# Example\n\n",
1396                "```\n",
1397                "use omp_core::", stringify!($mod_name), ";\n",
1398                "let data = [0x48u8, 0x65, 0x6c, 0x6c, 0x6f];\n",
1399                "let encoded = ", stringify!($mod_name), "::encode_raw_n(&data);\n",
1400                "```"
1401            )]
1402            pub const fn encode_raw_n<const N: usize>(src: &[u8; N]) -> ArrayStr<N> {
1403                RAW.encode_n(src)
1404            }
1405
1406            #[doc = concat!(
1407                "Decodes a fixed-size ", stringify!($mod_name), " array without padding.\n\n",
1408                "# Arguments\n\n",
1409                "* `src` - The encoded byte array to decode\n\n",
1410                "# Returns\n\n",
1411                "A `Result` containing the decoded byte array or an error.\n\n",
1412                "# Example\n\n",
1413                "```\n",
1414                "use omp_core::", stringify!($mod_name), ";\n",
1415                "let data = [0x48u8, 0x65, 0x6c, 0x6c, 0x6f];\n",
1416                "let encoded = ", stringify!($mod_name), "::encode_raw(&data).into_vec();\n",
1417                "let decoded = ", stringify!($mod_name), "::decode_raw(&encoded).into_vec().unwrap();\n",
1418                "assert_eq!(&decoded, &data);\n",
1419                "```"
1420            )]
1421            pub const fn decode_raw_n<const N: usize>(src: &[u8; N]) -> Option<Array<N>> {
1422                RAW.decode_n(src)
1423            }
1424
1425            #[doc = concat!(
1426                "Encodes bytes into a mutable buffer with padding.\n\n",
1427                "# Arguments\n\n",
1428                "* `src` - The byte slice to encode\n",
1429                "* `dst` - The mutable buffer to write encoded characters to\n\n",
1430                "# Returns\n\n",
1431                "The number of characters written to `dst`.\n\n",
1432                "# Example\n\n",
1433                "```\n",
1434                "use omp_core::", stringify!($mod_name), ";\n",
1435                "let mut buf = [0u8; 16];\n",
1436                "let n = ", stringify!($mod_name), "::encode_mut(b\"hello\", &mut buf);\n",
1437                "```"
1438            )]
1439            pub const fn encode_mut(src: &[u8], dst: &mut [u8]) -> usize {
1440                STD.encode_mut(src, dst)
1441            }
1442
1443            #[doc = concat!(
1444                "Decodes ", stringify!($mod_name), " encoded bytes into a mutable buffer with padding.\n\n",
1445                "# Arguments\n\n",
1446                "* `src` - The encoded byte slice to decode\n",
1447                "* `dst` - The mutable buffer to write decoded bytes to\n\n",
1448                "# Returns\n\n",
1449                "The number of bytes written to `dst`, or an error if decoding failed.\n\n",
1450                "# Example\n\n",
1451                "```\n",
1452                "use omp_core::", stringify!($mod_name), ";\n",
1453                "let mut buf = [0u8; 10];\n",
1454                "let encoded = ", stringify!($mod_name), "::encode(b\"hello\").into_vec();\n",
1455                "let n = ", stringify!($mod_name), "::decode_mut(&encoded, &mut buf).unwrap();\n",
1456                "```"
1457            )]
1458            pub const fn decode_mut(src: &[u8], dst: &mut [u8]) -> Result<usize> {
1459                STD.decode_mut(src, dst)
1460            }
1461
1462            #[doc = concat!(
1463                "Encodes bytes into a mutable buffer without padding.\n\n",
1464                "# Arguments\n\n",
1465                "* `src` - The byte slice to encode\n",
1466                "* `dst` - The mutable buffer to write encoded characters to\n\n",
1467                "# Returns\n\n",
1468                "The number of characters written to `dst`.\n\n",
1469                "# Example\n\n",
1470                "```\n",
1471                "use omp_core::", stringify!($mod_name), ";\n",
1472                "let mut buf = [0u8; 16];\n",
1473                "let n = ", stringify!($mod_name), "::encode_raw_mut(b\"hello\", &mut buf);\n",
1474                "```"
1475            )]
1476            pub const fn encode_raw_mut(src: &[u8], dst: &mut [u8]) -> usize {
1477                RAW.encode_mut(src, dst)
1478            }
1479
1480            #[doc = concat!(
1481                "Decodes ", stringify!($mod_name), " encoded bytes into a mutable buffer without padding.\n\n",
1482                "# Arguments\n\n",
1483                "* `src` - The encoded byte slice to decode\n",
1484                "* `dst` - The mutable buffer to write decoded bytes to\n\n",
1485                "# Returns\n\n",
1486                "The number of bytes written to `dst`, or an error if decoding failed.\n\n",
1487                "# Example\n\n",
1488                "```\n",
1489                "use omp_core::", stringify!($mod_name), ";\n",
1490                "let mut buf = [0u8; 10];\n",
1491                "let encoded = ", stringify!($mod_name), "::encode_raw(b\"hello\").into_vec();\n",
1492                "let n = ", stringify!($mod_name), "::decode_raw_mut(&encoded, &mut buf).unwrap();\n",
1493                "```"
1494            )]
1495            pub const fn decode_raw_mut(src: &[u8], dst: &mut [u8]) -> Result<usize> {
1496                RAW.decode_mut(src, dst)
1497            }
1498
1499            #[doc = concat!(
1500                "Returns the encoded length including padding.\n\n",
1501                "Calculates how many ", stringify!($mod_name), " characters (including padding) ",
1502                "are needed to encode `src_len` bytes.\n\n",
1503                "# Arguments\n\n",
1504                "* `src_len` - Number of source bytes\n\n",
1505                "# Returns\n\n",
1506                "Number of ", stringify!($mod_name), " characters in padded output\n\n",
1507                "# Example\n\n",
1508                "```\n",
1509                "use omp_core::", stringify!($mod_name), ";\n",
1510                "// 5 bytes encodes to a specific character count\n",
1511                "let output_len = ", stringify!($mod_name), "::encode_len(5);\n",
1512                "let encoded = ", stringify!($mod_name), "::encode(&[0u8; 5]).into_vec();\n",
1513                "assert_eq!(encoded.len(), output_len);\n",
1514                "```"
1515            )]
1516            #[inline]
1517            pub const fn encode_len(src_len: usize) -> usize {
1518                STD.encode_len(src_len)
1519            }
1520
1521            #[doc = concat!(
1522                "Returns the decoded output length for padded input.\n\n",
1523                "Given the number of non-padding ", stringify!($mod_name), " characters, ",
1524                "calculates how many bytes will result from decoding.\n\n",
1525                "# Arguments\n\n",
1526                "* `src_len` - Number of encoded characters (padding characters not counted)\n\n",
1527                "# Returns\n\n",
1528                "Number of decoded bytes\n\n",
1529                "# Example\n\n",
1530                "```\n",
1531                "use omp_core::", stringify!($mod_name), ";\n",
1532                "// Encoding 5 bytes produces some chars; decoding those chars yields 5 bytes\n",
1533                "let data = [0u8; 5];\n",
1534                "let encoded = ", stringify!($mod_name), "::encode(&data).into_vec();\n",
1535                "let non_padding_len = encoded.iter().position(|&b| b == b'=').unwrap_or(encoded.len());\n",
1536                "assert_eq!(", stringify!($mod_name), "::decode_len(non_padding_len), 5);\n",
1537                "```"
1538            )]
1539            #[inline]
1540            pub const fn decode_len(src_len: usize) -> usize {
1541                STD.decode_len(src_len)
1542            }
1543
1544            #[doc = concat!(
1545                "Returns the encoded length without padding.\n\n",
1546                "Calculates how many ", stringify!($mod_name), " characters (no padding) ",
1547                "are needed to encode `src_len` bytes.\n\n",
1548                "# Arguments\n\n",
1549                "* `src_len` - Number of source bytes\n\n",
1550                "# Returns\n\n",
1551                "Number of ", stringify!($mod_name), " characters in unpadded output\n\n",
1552                "# Example\n\n",
1553                "```\n",
1554                "use omp_core::", stringify!($mod_name), ";\n",
1555                "// 5 bytes encodes to a specific character count (no padding)\n",
1556                "let output_len = ", stringify!($mod_name), "::encode_raw_len(5);\n",
1557                "let encoded = ", stringify!($mod_name), "::encode_raw(&[0u8; 5]).into_vec();\n",
1558                "assert_eq!(encoded.len(), output_len);\n",
1559                "```"
1560            )]
1561            #[inline]
1562            pub const fn encode_raw_len(src_len: usize) -> usize {
1563                RAW.encode_len(src_len)
1564            }
1565
1566            #[doc = concat!(
1567                "Returns the decoded output length for unpadded input.\n\n",
1568                "Given the number of ", stringify!($mod_name), " characters (no padding), ",
1569                "calculates how many bytes will result from decoding.\n\n",
1570                "# Arguments\n\n",
1571                "* `src_len` - Number of unpadded encoded characters\n\n",
1572                "# Returns\n\n",
1573                "Number of decoded bytes\n\n",
1574                "# Example\n\n",
1575                "```\n",
1576                "use omp_core::", stringify!($mod_name), ";\n",
1577                "// Encoding 5 bytes without padding produces chars; decoding yields 5 bytes\n",
1578                "let data = [0u8; 5];\n",
1579                "let encoded = ", stringify!($mod_name), "::encode_raw(&data).into_vec();\n",
1580                "assert_eq!(", stringify!($mod_name), "::decode_raw_len(encoded.len()), 5);\n",
1581                "```"
1582            )]
1583            #[inline]
1584            pub const fn decode_raw_len(src_len: usize) -> usize {
1585                RAW.decode_len(src_len)
1586            }
1587
1588            #[doc = concat!(
1589                "Creates an encoding writer that wraps an `io::Write`.\\n\\n",
1590                "The writer buffers raw bytes, encodes them to ", stringify!($mod_name), " with padding, ",
1591                "and writes the encoded output to the inner writer.\\n\\n",
1592                "# Examples\\n\\n",
1593                "```\\n",
1594                "use omp_core::", stringify!($mod_name), ";\\n",
1595                "use std::io::Write;\\n",
1596                "\\n",
1597                "let mut output = Vec::new();\\n",
1598                "let mut writer = ", stringify!($mod_name), "::encode_writer(&mut output);\\n",
1599                "writer.write_all(b\\\"Hello\\\").unwrap();\\n",
1600                "writer.flush().unwrap();\\n",
1601                "```\\n"
1602            )]
1603            pub const fn encode_writer<W: std::io::Write>(inner: W) -> super::EncodeWriter<W, $n> {
1604                super::EncodeWriter::new(inner, STD)
1605            }
1606
1607            #[doc = concat!(
1608                "Creates a decoding writer that wraps an `io::Write`.\\n\\n",
1609                "The writer buffers ", stringify!($mod_name), " encoded bytes with padding, ",
1610                "decodes them, and writes the raw output to the inner writer.\\n\\n",
1611                "# Examples\\n\\n",
1612                "```\\n",
1613                "use omp_core::", stringify!($mod_name), ";\\n",
1614                "use std::io::Write;\\n",
1615                "\\n",
1616                "let mut output = Vec::new();\\n",
1617                "let encoded = ", stringify!($mod_name), "::encode(b\\\"Hello\\\").into_vec();\\n",
1618                "let mut writer = ", stringify!($mod_name), "::decode_writer(&mut output);\\n",
1619                "writer.write_all(&encoded).unwrap();\\n",
1620                "writer.flush().unwrap();\\n",
1621                "assert_eq!(output, b\\\"Hello\\\");\\n",
1622                "```\\n"
1623            )]
1624            pub const fn decode_writer<W: std::io::Write>(inner: W) -> super::DecodeWriter<W, $n> {
1625                super::DecodeWriter::new(inner, STD)
1626            }
1627
1628            #[doc = concat!(
1629                "Creates an encoding writer for unpadded ", stringify!($mod_name), ".\\n\\n",
1630                "The writer buffers raw bytes, encodes them to ", stringify!($mod_name), " without padding, ",
1631                "and writes the encoded output to the inner writer.\\n\\n",
1632                "# Examples\\n\\n",
1633                "```\\n",
1634                "use omp_core::", stringify!($mod_name), ";\\n",
1635                "use std::io::Write;\\n",
1636                "\\n",
1637                "let mut output = Vec::new();\\n",
1638                "let mut writer = ", stringify!($mod_name), "::encode_writer_raw(&mut output);\\n",
1639                "writer.write_all(b\\\"Hello\\\").unwrap();\\n",
1640                "writer.flush().unwrap();\\n",
1641                "```\\n"
1642            )]
1643            pub const fn encode_writer_raw<W: std::io::Write>(inner: W) -> super::EncodeWriter<W, $n> {
1644                super::EncodeWriter::new(inner, RAW)
1645            }
1646
1647            #[doc = concat!(
1648                "Creates a decoding writer for unpadded ", stringify!($mod_name), ".\\n\\n",
1649                "The writer buffers ", stringify!($mod_name), " encoded bytes without padding, ",
1650                "decodes them, and writes the raw output to the inner writer.\\n\\n",
1651                "# Examples\\n\\n",
1652                "```\\n",
1653                "use omp_core::", stringify!($mod_name), ";\\n",
1654                "use std::io::Write;\\n",
1655                "\\n",
1656                "let mut output = Vec::new();\\n",
1657                "let encoded = ", stringify!($mod_name), "::encode_raw(b\\\"Hello\\\").into_vec();\\n",
1658                "let mut writer = ", stringify!($mod_name), "::decode_writer_raw(&mut output);\\n",
1659                "writer.write_all(&encoded).unwrap();\\n",
1660                "writer.flush().unwrap();\\n",
1661                "assert_eq!(output, b\\\"Hello\\\");\\n",
1662                "```\\n"
1663            )]
1664            pub const fn decode_writer_raw<W: std::io::Write>(inner: W) -> super::DecodeWriter<W, $n> {
1665                super::DecodeWriter::new(inner, RAW)
1666            }
1667        }
1668    };
1669}
1670
1671// Base64 variants
1672define_encoding!(
1673	base64,
1674	64,
1675	b'=',
1676	b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
1677	false
1678);
1679define_encoding!(
1680	base64_url,
1681	64,
1682	b'=',
1683	b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
1684	false
1685);
1686
1687// Base32 variants
1688define_encoding!(base32, 32, b'=', b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", false);
1689define_encoding!(base32_hex, 32, b'=', b"0123456789ABCDEFGHIJKLMNOPQRSTUV", false);
1690define_encoding!(base32_dns, 32, b'=', b"0123456789abcdefghijklmnopqrstuv", false);