@@ -1503,6 +1503,126 @@ impl LineWrap {
15031503 }
15041504}
15051505
1506+ /// Stack-backed encoded Base64 output.
1507+ ///
1508+ /// This type is intended for short values where heap allocation would be
1509+ /// unnecessary but manually sizing and passing a separate output slice is
1510+ /// noisy. Its visible bytes are produced by crate encoders, so [`Self::as_str`]
1511+ /// can return `&str` without exposing a fallible UTF-8 conversion to callers.
1512+ ///
1513+ /// The backing array is cleared when the value is dropped. This is best-effort
1514+ /// data-retention reduction and is not a formal zeroization guarantee.
1515+ pub struct EncodedBuffer < const CAP : usize > {
1516+ bytes : [ u8 ; CAP ] ,
1517+ len : usize ,
1518+ }
1519+
1520+ impl < const CAP : usize > EncodedBuffer < CAP > {
1521+ /// Creates an empty encoded buffer.
1522+ #[ must_use]
1523+ pub const fn new ( ) -> Self {
1524+ Self {
1525+ bytes : [ 0u8 ; CAP ] ,
1526+ len : 0 ,
1527+ }
1528+ }
1529+
1530+ /// Returns the number of visible encoded bytes.
1531+ #[ must_use]
1532+ pub const fn len ( & self ) -> usize {
1533+ self . len
1534+ }
1535+
1536+ /// Returns whether the buffer has no visible encoded bytes.
1537+ #[ must_use]
1538+ pub const fn is_empty ( & self ) -> bool {
1539+ self . len == 0
1540+ }
1541+
1542+ /// Returns the stack capacity in bytes.
1543+ #[ must_use]
1544+ pub const fn capacity ( & self ) -> usize {
1545+ CAP
1546+ }
1547+
1548+ /// Returns the visible encoded bytes.
1549+ #[ must_use]
1550+ pub fn as_bytes ( & self ) -> & [ u8 ] {
1551+ & self . bytes [ ..self . len ]
1552+ }
1553+
1554+ /// Returns the visible encoded bytes as UTF-8.
1555+ ///
1556+ /// # Panics
1557+ ///
1558+ /// Panics only if the crate's internal invariant is broken and the buffer
1559+ /// contains non-UTF-8 bytes.
1560+ #[ must_use]
1561+ pub fn as_str ( & self ) -> & str {
1562+ match core:: str:: from_utf8 ( self . as_bytes ( ) ) {
1563+ Ok ( output) => output,
1564+ Err ( _) => unreachable ! ( "base64 encoder produced non-UTF-8 output" ) ,
1565+ }
1566+ }
1567+
1568+ /// Clears the visible bytes and the full backing array.
1569+ pub fn clear ( & mut self ) {
1570+ self . bytes . fill ( 0 ) ;
1571+ self . len = 0 ;
1572+ }
1573+
1574+ /// Clears bytes after the visible prefix.
1575+ pub fn clear_tail ( & mut self ) {
1576+ self . bytes [ self . len ..] . fill ( 0 ) ;
1577+ }
1578+ }
1579+
1580+ impl < const CAP : usize > AsRef < [ u8 ] > for EncodedBuffer < CAP > {
1581+ fn as_ref ( & self ) -> & [ u8 ] {
1582+ self . as_bytes ( )
1583+ }
1584+ }
1585+
1586+ impl < const CAP : usize > Clone for EncodedBuffer < CAP > {
1587+ fn clone ( & self ) -> Self {
1588+ let mut output = Self :: new ( ) ;
1589+ output. bytes [ ..self . len ] . copy_from_slice ( self . as_bytes ( ) ) ;
1590+ output. len = self . len ;
1591+ output
1592+ }
1593+ }
1594+
1595+ impl < const CAP : usize > core:: fmt:: Debug for EncodedBuffer < CAP > {
1596+ fn fmt ( & self , formatter : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
1597+ formatter
1598+ . debug_struct ( "EncodedBuffer" )
1599+ . field ( "bytes" , & "<redacted>" )
1600+ . field ( "len" , & self . len )
1601+ . field ( "capacity" , & CAP )
1602+ . finish ( )
1603+ }
1604+ }
1605+
1606+ impl < const CAP : usize > Default for EncodedBuffer < CAP > {
1607+ fn default ( ) -> Self {
1608+ Self :: new ( )
1609+ }
1610+ }
1611+
1612+ impl < const CAP : usize > Drop for EncodedBuffer < CAP > {
1613+ fn drop ( & mut self ) {
1614+ self . clear ( ) ;
1615+ }
1616+ }
1617+
1618+ impl < const CAP : usize > Eq for EncodedBuffer < CAP > { }
1619+
1620+ impl < const CAP : usize > PartialEq for EncodedBuffer < CAP > {
1621+ fn eq ( & self , other : & Self ) -> bool {
1622+ self . as_bytes ( ) == other. as_bytes ( )
1623+ }
1624+ }
1625+
15061626/// A named Base64 profile with an engine and optional strict line wrapping.
15071627///
15081628/// Profiles are convenience values for protocol-shaped Base64. They keep the
@@ -1589,6 +1709,27 @@ where
15891709 }
15901710 }
15911711
1712+ /// Encodes `input` into a stack-backed buffer.
1713+ ///
1714+ /// This is useful for short values where heap allocation is unnecessary.
1715+ /// If encoding fails, the internal backing array is cleared before the
1716+ /// error is returned.
1717+ pub fn encode_buffer < const CAP : usize > (
1718+ & self ,
1719+ input : & [ u8 ] ,
1720+ ) -> Result < EncodedBuffer < CAP > , EncodeError > {
1721+ let mut output = EncodedBuffer :: new ( ) ;
1722+ let written = match self . encode_slice_clear_tail ( input, & mut output. bytes ) {
1723+ Ok ( written) => written,
1724+ Err ( err) => {
1725+ output. clear ( ) ;
1726+ return Err ( err) ;
1727+ }
1728+ } ;
1729+ output. len = written;
1730+ Ok ( output)
1731+ }
1732+
15921733 /// Decodes `input` into `output` according to this profile.
15931734 pub fn decode_slice ( & self , input : & [ u8 ] , output : & mut [ u8 ] ) -> Result < usize , DecodeError > {
15941735 match self . wrap {
@@ -2657,6 +2798,36 @@ where
26572798 Ok ( written)
26582799 }
26592800
2801+ /// Encodes `input` into a stack-backed buffer.
2802+ ///
2803+ /// This helper is useful for short values where callers want the
2804+ /// convenience of an owned result without enabling `alloc`.
2805+ ///
2806+ /// # Examples
2807+ ///
2808+ /// ```
2809+ /// use base64_ng::STANDARD;
2810+ ///
2811+ /// let encoded = STANDARD.encode_buffer::<8>(b"hello").unwrap();
2812+ ///
2813+ /// assert_eq!(encoded.as_str(), "aGVsbG8=");
2814+ /// ```
2815+ pub fn encode_buffer < const CAP : usize > (
2816+ & self ,
2817+ input : & [ u8 ] ,
2818+ ) -> Result < EncodedBuffer < CAP > , EncodeError > {
2819+ let mut output = EncodedBuffer :: new ( ) ;
2820+ let written = match self . encode_slice_clear_tail ( input, & mut output. bytes ) {
2821+ Ok ( written) => written,
2822+ Err ( err) => {
2823+ output. clear ( ) ;
2824+ return Err ( err) ;
2825+ }
2826+ } ;
2827+ output. len = written;
2828+ Ok ( output)
2829+ }
2830+
26602831 /// Encodes `input` into a newly allocated byte vector.
26612832 #[ cfg( feature = "alloc" ) ]
26622833 pub fn encode_vec ( & self , input : & [ u8 ] ) -> Result < alloc:: vec:: Vec < u8 > , EncodeError > {
0 commit comments