@@ -1431,6 +1431,18 @@ pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
14311431/// URL-safe Base64 engine without padding.
14321432pub const URL_SAFE_NO_PAD : Engine < UrlSafe , false > = Engine :: new ( ) ;
14331433
1434+ /// bcrypt-style Base64 engine without padding.
1435+ ///
1436+ /// This uses the bcrypt alphabet with the crate's normal Base64 bit packing.
1437+ /// It does not parse complete bcrypt password-hash strings.
1438+ pub const BCRYPT_NO_PAD : Engine < Bcrypt , false > = Engine :: new ( ) ;
1439+
1440+ /// Unix `crypt(3)`-style Base64 engine without padding.
1441+ ///
1442+ /// This uses the `crypt(3)` alphabet with the crate's normal Base64 bit
1443+ /// packing. It does not parse complete password-hash strings.
1444+ pub const CRYPT_NO_PAD : Engine < Crypt , false > = Engine :: new ( ) ;
1445+
14341446/// Line ending used by wrapped Base64 output.
14351447#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
14361448pub enum LineEnding {
@@ -1491,6 +1503,164 @@ impl LineWrap {
14911503 }
14921504}
14931505
1506+ /// A named Base64 profile with an engine and optional strict line wrapping.
1507+ ///
1508+ /// Profiles are convenience values for protocol-shaped Base64. They keep the
1509+ /// same strict alphabet, padding, canonical-bit, and output-buffer rules as
1510+ /// [`Engine`], while carrying the wrapping policy for MIME/PEM-like formats.
1511+ #[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
1512+ pub struct Profile < A , const PAD : bool > {
1513+ engine : Engine < A , PAD > ,
1514+ wrap : Option < LineWrap > ,
1515+ }
1516+
1517+ impl < A , const PAD : bool > Profile < A , PAD >
1518+ where
1519+ A : Alphabet ,
1520+ {
1521+ /// Creates a profile from an engine and optional strict line wrapping.
1522+ #[ must_use]
1523+ pub const fn new ( engine : Engine < A , PAD > , wrap : Option < LineWrap > ) -> Self {
1524+ Self { engine, wrap }
1525+ }
1526+
1527+ /// Returns the underlying engine.
1528+ #[ must_use]
1529+ pub const fn engine ( & self ) -> Engine < A , PAD > {
1530+ self . engine
1531+ }
1532+
1533+ /// Returns the strict wrapping policy carried by this profile, if any.
1534+ #[ must_use]
1535+ pub const fn line_wrap ( & self ) -> Option < LineWrap > {
1536+ self . wrap
1537+ }
1538+
1539+ /// Returns the encoded length for this profile.
1540+ pub const fn encoded_len ( & self , input_len : usize ) -> Result < usize , EncodeError > {
1541+ match self . wrap {
1542+ Some ( wrap) => wrapped_encoded_len ( input_len, PAD , wrap) ,
1543+ None => encoded_len ( input_len, PAD ) ,
1544+ }
1545+ }
1546+
1547+ /// Returns the exact decoded length for this profile.
1548+ pub fn decoded_len ( & self , input : & [ u8 ] ) -> Result < usize , DecodeError > {
1549+ match self . wrap {
1550+ Some ( wrap) => self . engine . decoded_len_wrapped ( input, wrap) ,
1551+ None => self . engine . decoded_len ( input) ,
1552+ }
1553+ }
1554+
1555+ /// Validates input according to this profile without writing decoded bytes.
1556+ pub fn validate_result ( & self , input : & [ u8 ] ) -> Result < ( ) , DecodeError > {
1557+ match self . wrap {
1558+ Some ( wrap) => self . engine . validate_wrapped_result ( input, wrap) ,
1559+ None => self . engine . validate_result ( input) ,
1560+ }
1561+ }
1562+
1563+ /// Returns whether `input` is valid for this profile.
1564+ #[ must_use]
1565+ pub fn validate ( & self , input : & [ u8 ] ) -> bool {
1566+ self . validate_result ( input) . is_ok ( )
1567+ }
1568+
1569+ /// Encodes `input` into `output` according to this profile.
1570+ pub fn encode_slice ( & self , input : & [ u8 ] , output : & mut [ u8 ] ) -> Result < usize , EncodeError > {
1571+ match self . wrap {
1572+ Some ( wrap) => self . engine . encode_slice_wrapped ( input, output, wrap) ,
1573+ None => self . engine . encode_slice ( input, output) ,
1574+ }
1575+ }
1576+
1577+ /// Encodes `input` into `output` and clears all bytes after the encoded
1578+ /// prefix.
1579+ pub fn encode_slice_clear_tail (
1580+ & self ,
1581+ input : & [ u8 ] ,
1582+ output : & mut [ u8 ] ,
1583+ ) -> Result < usize , EncodeError > {
1584+ match self . wrap {
1585+ Some ( wrap) => self
1586+ . engine
1587+ . encode_slice_wrapped_clear_tail ( input, output, wrap) ,
1588+ None => self . engine . encode_slice_clear_tail ( input, output) ,
1589+ }
1590+ }
1591+
1592+ /// Decodes `input` into `output` according to this profile.
1593+ pub fn decode_slice ( & self , input : & [ u8 ] , output : & mut [ u8 ] ) -> Result < usize , DecodeError > {
1594+ match self . wrap {
1595+ Some ( wrap) => self . engine . decode_slice_wrapped ( input, output, wrap) ,
1596+ None => self . engine . decode_slice ( input, output) ,
1597+ }
1598+ }
1599+
1600+ /// Decodes `input` into `output` and clears all bytes after the decoded
1601+ /// prefix.
1602+ pub fn decode_slice_clear_tail (
1603+ & self ,
1604+ input : & [ u8 ] ,
1605+ output : & mut [ u8 ] ,
1606+ ) -> Result < usize , DecodeError > {
1607+ match self . wrap {
1608+ Some ( wrap) => self
1609+ . engine
1610+ . decode_slice_wrapped_clear_tail ( input, output, wrap) ,
1611+ None => self . engine . decode_slice_clear_tail ( input, output) ,
1612+ }
1613+ }
1614+
1615+ /// Encodes `input` into a newly allocated byte vector.
1616+ #[ cfg( feature = "alloc" ) ]
1617+ pub fn encode_vec ( & self , input : & [ u8 ] ) -> Result < alloc:: vec:: Vec < u8 > , EncodeError > {
1618+ match self . wrap {
1619+ Some ( wrap) => self . engine . encode_wrapped_vec ( input, wrap) ,
1620+ None => self . engine . encode_vec ( input) ,
1621+ }
1622+ }
1623+
1624+ /// Encodes `input` into a newly allocated UTF-8 string.
1625+ #[ cfg( feature = "alloc" ) ]
1626+ pub fn encode_string ( & self , input : & [ u8 ] ) -> Result < alloc:: string:: String , EncodeError > {
1627+ match self . wrap {
1628+ Some ( wrap) => self . engine . encode_wrapped_string ( input, wrap) ,
1629+ None => self . engine . encode_string ( input) ,
1630+ }
1631+ }
1632+
1633+ /// Decodes `input` into a newly allocated byte vector.
1634+ #[ cfg( feature = "alloc" ) ]
1635+ pub fn decode_vec ( & self , input : & [ u8 ] ) -> Result < alloc:: vec:: Vec < u8 > , DecodeError > {
1636+ match self . wrap {
1637+ Some ( wrap) => self . engine . decode_wrapped_vec ( input, wrap) ,
1638+ None => self . engine . decode_vec ( input) ,
1639+ }
1640+ }
1641+ }
1642+
1643+ /// MIME Base64 profile: standard alphabet, padding, 76-column CRLF wrapping.
1644+ pub const MIME : Profile < Standard , true > = Profile :: new ( STANDARD , Some ( LineWrap :: MIME ) ) ;
1645+
1646+ /// PEM Base64 profile: standard alphabet, padding, 64-column LF wrapping.
1647+ pub const PEM : Profile < Standard , true > = Profile :: new ( STANDARD , Some ( LineWrap :: PEM ) ) ;
1648+
1649+ /// PEM Base64 profile with CRLF line endings.
1650+ pub const PEM_CRLF : Profile < Standard , true > = Profile :: new ( STANDARD , Some ( LineWrap :: PEM_CRLF ) ) ;
1651+
1652+ /// bcrypt-style no-padding Base64 profile.
1653+ ///
1654+ /// This profile carries the bcrypt alphabet and no padding. It does not parse
1655+ /// complete bcrypt password-hash strings.
1656+ pub const BCRYPT : Profile < Bcrypt , false > = Profile :: new ( BCRYPT_NO_PAD , None ) ;
1657+
1658+ /// Unix `crypt(3)`-style no-padding Base64 profile.
1659+ ///
1660+ /// This profile carries the `crypt(3)` alphabet and no padding. It does not
1661+ /// parse complete password-hash strings.
1662+ pub const CRYPT : Profile < Crypt , false > = Profile :: new ( CRYPT_NO_PAD , None ) ;
1663+
14941664/// Returns the encoded length for an input length and padding policy.
14951665///
14961666/// This function returns [`EncodeError::LengthOverflow`] instead of panicking.
@@ -1761,6 +1931,39 @@ impl Alphabet for UrlSafe {
17611931 }
17621932}
17631933
1934+ /// The bcrypt Base64 alphabet.
1935+ ///
1936+ /// This alphabet is commonly used by bcrypt hash strings. It is provided as an
1937+ /// alphabet/profile building block; `base64-ng` does not parse or verify full
1938+ /// bcrypt password-hash records.
1939+ #[ derive( Clone , Copy , Debug , Default , Eq , PartialEq ) ]
1940+ pub struct Bcrypt ;
1941+
1942+ impl Alphabet for Bcrypt {
1943+ const ENCODE : [ u8 ; 64 ] = * b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ;
1944+
1945+ #[ inline]
1946+ fn decode ( byte : u8 ) -> Option < u8 > {
1947+ decode_alphabet_byte ( byte, & Self :: ENCODE )
1948+ }
1949+ }
1950+
1951+ /// The Unix `crypt(3)` Base64 alphabet.
1952+ ///
1953+ /// This alphabet is provided as an explicit legacy interoperability profile.
1954+ /// `base64-ng` does not parse or verify complete password-hash records.
1955+ #[ derive( Clone , Copy , Debug , Default , Eq , PartialEq ) ]
1956+ pub struct Crypt ;
1957+
1958+ impl Alphabet for Crypt {
1959+ const ENCODE : [ u8 ; 64 ] = * b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ;
1960+
1961+ #[ inline]
1962+ fn decode ( byte : u8 ) -> Option < u8 > {
1963+ decode_alphabet_byte ( byte, & Self :: ENCODE )
1964+ }
1965+ }
1966+
17641967#[ inline]
17651968const fn encode_base64_value < A : Alphabet > ( value : u8 ) -> u8 {
17661969 encode_alphabet_value ( value, & A :: ENCODE )
@@ -1984,11 +2187,43 @@ mod backend {
19842187}
19852188
19862189/// A zero-sized Base64 engine parameterized by alphabet and padding policy.
1987- #[ derive( Clone , Copy , Debug , Default , Eq , PartialEq ) ]
19882190pub struct Engine < A , const PAD : bool > {
19892191 alphabet : core:: marker:: PhantomData < A > ,
19902192}
19912193
2194+ impl < A , const PAD : bool > Clone for Engine < A , PAD > {
2195+ fn clone ( & self ) -> Self {
2196+ * self
2197+ }
2198+ }
2199+
2200+ impl < A , const PAD : bool > Copy for Engine < A , PAD > { }
2201+
2202+ impl < A , const PAD : bool > core:: fmt:: Debug for Engine < A , PAD > {
2203+ fn fmt ( & self , formatter : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
2204+ formatter
2205+ . debug_struct ( "Engine" )
2206+ . field ( "padded" , & PAD )
2207+ . finish ( )
2208+ }
2209+ }
2210+
2211+ impl < A , const PAD : bool > Default for Engine < A , PAD > {
2212+ fn default ( ) -> Self {
2213+ Self {
2214+ alphabet : core:: marker:: PhantomData ,
2215+ }
2216+ }
2217+ }
2218+
2219+ impl < A , const PAD : bool > Eq for Engine < A , PAD > { }
2220+
2221+ impl < A , const PAD : bool > PartialEq for Engine < A , PAD > {
2222+ fn eq ( & self , _other : & Self ) -> bool {
2223+ true
2224+ }
2225+ }
2226+
19922227impl < A , const PAD : bool > Engine < A , PAD >
19932228where
19942229 A : Alphabet ,
0 commit comments