| #include "bytes_extra_ops.h" | |
| PyObject *CPyBytes_Translate(PyObject *bytes, PyObject *table) { | |
| // Fast path: exact bytes object with exact bytes table | |
| if (PyBytes_CheckExact(bytes) && PyBytes_CheckExact(table)) { | |
| Py_ssize_t table_len = PyBytes_GET_SIZE(table); | |
| if (table_len != 256) { | |
| PyErr_SetString(PyExc_ValueError, | |
| "translation table must be 256 characters long"); | |
| return NULL; | |
| } | |
| Py_ssize_t len = PyBytes_GET_SIZE(bytes); | |
| const char *input = PyBytes_AS_STRING(bytes); | |
| const char *trans_table = PyBytes_AS_STRING(table); | |
| PyObject *result = PyBytes_FromStringAndSize(NULL, len); | |
| if (result == NULL) { | |
| return NULL; | |
| } | |
| char *output = PyBytes_AS_STRING(result); | |
| bool changed = false; | |
| // Without a loop unrolling hint performance can be worse than CPython | |
| CPY_UNROLL_LOOP(4) | |
| for (Py_ssize_t i = len; --i >= 0;) { | |
| char c = *input++; | |
| if ((*output++ = trans_table[(unsigned char)c]) != c) | |
| changed = true; | |
| } | |
| // If nothing changed, discard result and return the original object | |
| if (!changed) { | |
| Py_DECREF(result); | |
| Py_INCREF(bytes); | |
| return bytes; | |
| } | |
| return result; | |
| } | |
| // Fallback to Python method call for non-exact types or non-standard tables | |
| return PyObject_CallMethodOneArg(bytes, mypyc_interned_str.translate, table); | |
| } |