Skip to content

Commit 65fd305

Browse files
committed
Clean up implementation
1 parent 62666f4 commit 65fd305

File tree

1 file changed

+24
-15
lines changed

1 file changed

+24
-15
lines changed

mypyc/lib-rt/librt_base64.c

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
#ifdef MYPYC_EXPERIMENTAL
99

1010
static 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.
168165
static 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

Comments
 (0)