88#ifdef MYPYC_EXPERIMENTAL
99
1010static PyObject *
11- b64decode_handle_invalid (PyObject * out_bytes , char * outbuf , size_t max_out , const char * src , size_t srclen );
11+ b64decode_handle_invalid_input (
12+ PyObject * out_bytes , char * outbuf , size_t max_out , const char * src , size_t srclen );
1213
1314#define BASE64_MAXBIN ((PY_SSIZE_T_MAX - 3) / 2)
1415
@@ -132,7 +133,8 @@ b64decode_internal(PyObject *arg) {
132133
133134 if (ret != 1 ) {
134135 if (ret == 0 ) {
135- return b64decode_handle_invalid (out_bytes , outbuf , max_out , src , srclen );
136+ // Slow path: handle non-base64 input
137+ return b64decode_handle_invalid_input (out_bytes , outbuf , max_out , src , srclen );
136138 }
137139 Py_DECREF (out_bytes );
138140 if (ret == -1 ) {
@@ -150,36 +152,39 @@ b64decode_internal(PyObject *arg) {
150152 return NULL ;
151153 }
152154
153- #ifndef Py_LIMITED_API
154155 // Shrink in place to the actual decoded length
155156 if (_PyBytes_Resize (& out_bytes , (Py_ssize_t )outlen ) < 0 ) {
156157 // _PyBytes_Resize sets an exception and may free the old object
157158 return NULL ;
158159 }
159160 return out_bytes ;
160- #else
161- // PEP 384 limited-API fallback: copy into a right-sized bytes object
162- PyObject * res = PyBytes_FromStringAndSize (outbuf , (Py_ssize_t )outlen );
163- Py_DECREF (out_bytes );
164- return res ; // may be NULL if allocation failed (exception set)
165- #endif
166161}
167162
163+ // Process non-base64 input by ignoring non-base64 characters, for compatiblity
164+ // with stdlib b64decode.
168165static PyObject *
169- b64decode_handle_invalid (PyObject * out_bytes , char * outbuf , size_t max_out , const char * src , size_t srclen )
166+ b64decode_handle_invalid_input (
167+ PyObject * out_bytes , char * outbuf , size_t max_out , const char * src , size_t srclen )
170168{
171- size_t i ;
172- char * newbuf = PyMem_Malloc ( srclen );
169+ // Copy input to a temporary buffer, with non-base64 characters and extra suffix
170+ // characters removed
173171 size_t newbuf_len = 0 ;
174- for (i = 0 ; i < srclen ; i ++ ) {
172+ char * newbuf = PyMem_Malloc (srclen );
173+ if (newbuf == NULL ) {
174+ Py_DECREF (out_bytes );
175+ return PyErr_NoMemory ();
176+ }
177+
178+ // Copy base64 characters and some padding to the new buffer
179+ for (size_t i = 0 ; i < srclen ; i ++ ) {
175180 char c = src [i ];
176181 if (is_valid_base64_char (c , false)) {
177182 newbuf [newbuf_len ++ ] = c ;
178183 } else if (c == '=' ) {
179- // Copy necessary amount of padding
184+ // Copy a necessary amount of padding
180185 int remainder = newbuf_len % 4 ;
181186 if (remainder == 0 ) {
182- // No padding needed -- ignore padding
187+ // No padding needed
183188 break ;
184189 }
185190 int numpad = 4 - remainder ;
@@ -192,6 +197,7 @@ b64decode_handle_invalid(PyObject *out_bytes, char *outbuf, size_t max_out, cons
192197 newbuf [newbuf_len ++ ] = '=' ;
193198 i ++ ;
194199 numpad -- ;
200+ // Skip non-base64 alphabet characters within padding
195201 while (i < srclen && !is_valid_base64_char (src [i ], true)) {
196202 i ++ ;
197203 }
@@ -200,7 +206,10 @@ b64decode_handle_invalid(PyObject *out_bytes, char *outbuf, size_t max_out, cons
200206 }
201207 }
202208
209+ // Stdlib always performs a non-strict padding check
203210 if (newbuf_len % 4 != 0 ) {
211+ Py_DECREF (out_bytes );
212+ PyMem_Free (newbuf );
204213 PyErr_SetString (PyExc_ValueError , "Incorrect padding" );
205214 return NULL ;
206215 }
0 commit comments