@@ -4,8 +4,35 @@ import '../../byte.dart';
44import 'base64.dart' ;
55import 'base64_norm.dart' ;
66
7+ /// Strict Base64 Decoder
8+ ///
9+ /// **alphabet**: A–Za–z0–9+/
10+ ///
11+ /// It requires padding and throws [Base64Exception] if the incoming text is
12+ /// ill-formed. For a more relaxed decoder, see [base64DecNorm] .
13+ ///
14+ /// Base64 encoding scheme —
15+ /// [RFC 4648 section 4] (https://datatracker.ietf.org/doc/html/rfc4648#section-4)
16+ const base64Dec = Base64Dec ();
17+
18+ /// Normalizing Base64 Decoder
19+ ///
20+ /// It normalizes the encoded text before trying to decode it.
21+ ///
22+ /// Normalization process:
23+ ///
24+ /// - Unescape any '%' that preceeds '3D' (percent-encoded '=').
25+ /// - Replace '_' or '-' with '/' or '+'.
26+ /// - Add trailing padding '=' if needed.
27+ /// - Only base64 characters (A–Za–z0–9/+).
28+ /// - The total length will always be a multiple of four.
29+ const base64DecNorm = Base64Dec .norm ();
30+
731/// Base64 Decoder.
832///
33+ /// Throws [Base64Exception] when [encoded] does not have the expected base64
34+ /// format and cannot be parsed.
35+ ///
936/// Base64 encoding scheme —
1037/// [RFC 4648 section 4] (https://datatracker.ietf.org/doc/html/rfc4648#section-4)
1138class Base64Dec implements Base64Decoder {
@@ -30,31 +57,32 @@ class Base64Dec implements Base64Decoder {
3057 /// - Add trailing padding '=' if needed.
3158 /// - Only base64 characters (A–Za–z0–9/+).
3259 /// - The total length will always be a multiple of four.
33- const Base64Dec .norm () : this (const Base64Norm () );
60+ const Base64Dec .norm () : this (base64Norm );
3461
3562 // Restores the original bytes.
3663 final _ProperlyPadded _decode;
3764
3865 // The normalization phase.
3966 final Base64NormText _norm;
4067
68+ /// Returns the decoded bytes of [encoded] .
69+ ///
70+ /// Throws [Base64Exception] when [encoded] does not have the expected base64
71+ /// format and cannot be parsed.
4172 @override
4273 Uint8List call (String encoded) => _decode (_norm (encoded));
4374}
4475
4576/// Convenience Base64 decoder implementation over [Base64Dec] .
4677class Base64DecOf implements Bytes {
4778 /// Decoded bytes of [encoded] text.
48- const Base64DecOf (String encoded, [Base64Dec dec = const Base64Dec ()])
49- : _encoded = encoded,
50- _dec = dec;
79+ const Base64DecOf (String encoded) : this ._set (encoded, base64Dec);
5180
5281 /// Normalizes [encoded] before trying to decode it.
53- const Base64DecOf .norm (String encoded)
54- : this (encoded, const Base64Dec .norm ());
82+ const Base64DecOf .norm (String encoded) : this ._set (encoded, base64DecNorm);
5583
56- /// Custom
57- const Base64DecOf .custom (this ._encoded, this ._dec);
84+ /// Sets the encoded text and decoder instance.
85+ const Base64DecOf ._set (this ._encoded, this ._dec);
5886
5987 final String _encoded;
6088 final Base64Dec _dec;
@@ -95,32 +123,38 @@ class _ProperlyPadded {
95123 Uint8List call (String base64) {
96124 final info = _Base64Info (base64);
97125 if (info.totalLength % 4 != 0 ) {
98- throw const FormatException ('Invalid base64 encoding length' );
126+ throw const Base64Exception .length (
127+ message: 'Base64 encoding length must be a multiple of 4.' ,
128+ );
99129 }
100130 switch (info.payloadLength % 4 ) {
101131 case 1 :
102- throw const FormatException ('Invalid base64 payload length' );
132+ throw const Base64Exception .length (
133+ message: 'Invalid base64 payload length' ,
134+ );
103135 case 0 :
104136 // There must be no padding.
105137 if (info.numOfPadChars != 0 ) {
106- throw const FormatException (
107- "Invalid base64 padding: unnecessary trailing '='." ,
138+ throw const Base64Exception . padding (
139+ message : "Invalid base64 padding: unnecessary trailing '='." ,
108140 );
109141 }
110142 break ;
111143 case 2 :
112144 // There must be exactly 2 padding chars.
113145 if (info.numOfPadChars != 2 ) {
114- throw const FormatException (
115- "Invalid base64 padding: missing '==' at the end of the encoded text." ,
146+ throw const Base64Exception .padding (
147+ message:
148+ "Invalid base64 padding: missing '==' at the end of the encoded text." ,
116149 );
117150 }
118151 break ;
119152 case 3 :
120153 // There must be a single padding char at the end.
121154 if (info.numOfPadChars != 1 ) {
122- throw const FormatException (
123- "Invalid base64 padding: there should only be one '=' at the end of the encoded text." ,
155+ throw const Base64Exception .padding (
156+ message:
157+ "Invalid base64 padding: there should be a single '=' at the end of the encoded text." ,
124158 );
125159 }
126160 }
@@ -192,15 +226,11 @@ class _Base64Indexes {
192226 int operator [](int i) {
193227 final code = _base64.codeUnitAt (i);
194228 if (code < 0 || code > 127 ) {
195- throw const FormatException (
196- 'Illegal base64 character: non-ascii character found.' ,
197- );
229+ throw const Base64Exception .nonAscii ();
198230 }
199231 final index = _asciiBase64Indexes[code];
200232 if (index < 0 ) {
201- throw const FormatException (
202- "Invalid base64 character: ascii character other than '[A-Za-z][0-9]+/'." ,
203- );
233+ throw const Base64Exception .illegalAscii ();
204234 }
205235 return index;
206236 }
0 commit comments