| /* NOTE: this API is -ONLY- for use with single byte character strings. */ |
| /* Do not use it with Unicode. */ |
| |
| #include "bytes_methods.h" |
| |
| #ifndef STRINGLIB_MUTABLE |
| #warning "STRINGLIB_MUTABLE not defined before #include, assuming 0" |
| #define STRINGLIB_MUTABLE 0 |
| #endif |
| |
| /* the more complicated methods. parts of these should be pulled out into the |
| shared code in bytes_methods.c to cut down on duplicate code bloat. */ |
| |
| PyDoc_STRVAR(expandtabs__doc__, |
| "B.expandtabs([tabsize]) -> copy of B\n\ |
| \n\ |
| Return a copy of B where all tab characters are expanded using spaces.\n\ |
| If tabsize is not given, a tab size of 8 characters is assumed."); |
| |
| static PyObject* |
| stringlib_expandtabs(PyObject *self, PyObject *args) |
| { |
| const char *e, *p; |
| char *q; |
| size_t i, j; |
| PyObject *u; |
| int tabsize = 8; |
| |
| if (!PyArg_ParseTuple(args, "|i:expandtabs", &tabsize)) |
| return NULL; |
| |
| /* First pass: determine size of output string */ |
| i = j = 0; |
| e = STRINGLIB_STR(self) + STRINGLIB_LEN(self); |
| for (p = STRINGLIB_STR(self); p < e; p++) |
| if (*p == '\t') { |
| if (tabsize > 0) { |
| j += tabsize - (j % tabsize); |
| if (j > PY_SSIZE_T_MAX) { |
| PyErr_SetString(PyExc_OverflowError, |
| "result is too long"); |
| return NULL; |
| } |
| } |
| } |
| else { |
| j++; |
| if (*p == '\n' || *p == '\r') { |
| i += j; |
| j = 0; |
| if (i > PY_SSIZE_T_MAX) { |
| PyErr_SetString(PyExc_OverflowError, |
| "result is too long"); |
| return NULL; |
| } |
| } |
| } |
| |
| if ((i + j) > PY_SSIZE_T_MAX) { |
| PyErr_SetString(PyExc_OverflowError, "result is too long"); |
| return NULL; |
| } |
| |
| /* Second pass: create output string and fill it */ |
| u = STRINGLIB_NEW(NULL, i + j); |
| if (!u) |
| return NULL; |
| |
| j = 0; |
| q = STRINGLIB_STR(u); |
| |
| for (p = STRINGLIB_STR(self); p < e; p++) |
| if (*p == '\t') { |
| if (tabsize > 0) { |
| i = tabsize - (j % tabsize); |
| j += i; |
| while (i--) |
| *q++ = ' '; |
| } |
| } |
| else { |
| j++; |
| *q++ = *p; |
| if (*p == '\n' || *p == '\r') |
| j = 0; |
| } |
| |
| return u; |
| } |
| |
| Py_LOCAL_INLINE(PyObject *) |
| pad(PyObject *self, Py_ssize_t left, Py_ssize_t right, char fill) |
| { |
| PyObject *u; |
| |
| if (left < 0) |
| left = 0; |
| if (right < 0) |
| right = 0; |
| |
| if (left == 0 && right == 0 && STRINGLIB_CHECK_EXACT(self)) { |
| #if STRINGLIB_MUTABLE |
| /* We're defined as returning a copy; If the object is mutable |
| * that means we must make an identical copy. */ |
| return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self)); |
| #else |
| Py_INCREF(self); |
| return (PyObject *)self; |
| #endif /* STRINGLIB_MUTABLE */ |
| } |
| |
| u = STRINGLIB_NEW(NULL, |
| left + STRINGLIB_LEN(self) + right); |
| if (u) { |
| if (left) |
| memset(STRINGLIB_STR(u), fill, left); |
| Py_MEMCPY(STRINGLIB_STR(u) + left, |
| STRINGLIB_STR(self), |
| STRINGLIB_LEN(self)); |
| if (right) |
| memset(STRINGLIB_STR(u) + left + STRINGLIB_LEN(self), |
| fill, right); |
| } |
| |
| return u; |
| } |
| |
| PyDoc_STRVAR(ljust__doc__, |
| "B.ljust(width[, fillchar]) -> copy of B\n" |
| "\n" |
| "Return B left justified in a string of length width. Padding is\n" |
| "done using the specified fill character (default is a space)."); |
| |
| static PyObject * |
| stringlib_ljust(PyObject *self, PyObject *args) |
| { |
| Py_ssize_t width; |
| char fillchar = ' '; |
| |
| if (!PyArg_ParseTuple(args, "n|c:ljust", &width, &fillchar)) |
| return NULL; |
| |
| if (STRINGLIB_LEN(self) >= width && STRINGLIB_CHECK_EXACT(self)) { |
| #if STRINGLIB_MUTABLE |
| /* We're defined as returning a copy; If the object is mutable |
| * that means we must make an identical copy. */ |
| return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self)); |
| #else |
| Py_INCREF(self); |
| return (PyObject*) self; |
| #endif |
| } |
| |
| return pad(self, 0, width - STRINGLIB_LEN(self), fillchar); |
| } |
| |
| |
| PyDoc_STRVAR(rjust__doc__, |
| "B.rjust(width[, fillchar]) -> copy of B\n" |
| "\n" |
| "Return B right justified in a string of length width. Padding is\n" |
| "done using the specified fill character (default is a space)"); |
| |
| static PyObject * |
| stringlib_rjust(PyObject *self, PyObject *args) |
| { |
| Py_ssize_t width; |
| char fillchar = ' '; |
| |
| if (!PyArg_ParseTuple(args, "n|c:rjust", &width, &fillchar)) |
| return NULL; |
| |
| if (STRINGLIB_LEN(self) >= width && STRINGLIB_CHECK_EXACT(self)) { |
| #if STRINGLIB_MUTABLE |
| /* We're defined as returning a copy; If the object is mutable |
| * that means we must make an identical copy. */ |
| return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self)); |
| #else |
| Py_INCREF(self); |
| return (PyObject*) self; |
| #endif |
| } |
| |
| return pad(self, width - STRINGLIB_LEN(self), 0, fillchar); |
| } |
| |
| |
| PyDoc_STRVAR(center__doc__, |
| "B.center(width[, fillchar]) -> copy of B\n" |
| "\n" |
| "Return B centered in a string of length width. Padding is\n" |
| "done using the specified fill character (default is a space)."); |
| |
| static PyObject * |
| stringlib_center(PyObject *self, PyObject *args) |
| { |
| Py_ssize_t marg, left; |
| Py_ssize_t width; |
| char fillchar = ' '; |
| |
| if (!PyArg_ParseTuple(args, "n|c:center", &width, &fillchar)) |
| return NULL; |
| |
| if (STRINGLIB_LEN(self) >= width && STRINGLIB_CHECK_EXACT(self)) { |
| #if STRINGLIB_MUTABLE |
| /* We're defined as returning a copy; If the object is mutable |
| * that means we must make an identical copy. */ |
| return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self)); |
| #else |
| Py_INCREF(self); |
| return (PyObject*) self; |
| #endif |
| } |
| |
| marg = width - STRINGLIB_LEN(self); |
| left = marg / 2 + (marg & width & 1); |
| |
| return pad(self, left, marg - left, fillchar); |
| } |
| |
| PyDoc_STRVAR(zfill__doc__, |
| "B.zfill(width) -> copy of B\n" |
| "\n" |
| "Pad a numeric string B with zeros on the left, to fill a field\n" |
| "of the specified width. B is never truncated."); |
| |
| static PyObject * |
| stringlib_zfill(PyObject *self, PyObject *args) |
| { |
| Py_ssize_t fill; |
| PyObject *s; |
| char *p; |
| Py_ssize_t width; |
| |
| if (!PyArg_ParseTuple(args, "n:zfill", &width)) |
| return NULL; |
| |
| if (STRINGLIB_LEN(self) >= width) { |
| if (STRINGLIB_CHECK_EXACT(self)) { |
| #if STRINGLIB_MUTABLE |
| /* We're defined as returning a copy; If the object is mutable |
| * that means we must make an identical copy. */ |
| return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self)); |
| #else |
| Py_INCREF(self); |
| return (PyObject*) self; |
| #endif |
| } |
| else |
| return STRINGLIB_NEW( |
| STRINGLIB_STR(self), |
| STRINGLIB_LEN(self) |
| ); |
| } |
| |
| fill = width - STRINGLIB_LEN(self); |
| |
| s = pad(self, fill, 0, '0'); |
| |
| if (s == NULL) |
| return NULL; |
| |
| p = STRINGLIB_STR(s); |
| if (p[fill] == '+' || p[fill] == '-') { |
| /* move sign to beginning of string */ |
| p[0] = p[fill]; |
| p[fill] = '0'; |
| } |
| |
| return (PyObject*) s; |
| } |
| |
| |
| #define _STRINGLIB_SPLIT_APPEND(data, left, right) \ |
| str = STRINGLIB_NEW((data) + (left), \ |
| (right) - (left)); \ |
| if (str == NULL) \ |
| goto onError; \ |
| if (PyList_Append(list, str)) { \ |
| Py_DECREF(str); \ |
| goto onError; \ |
| } \ |
| else \ |
| Py_DECREF(str); |
| |
| PyDoc_STRVAR(splitlines__doc__, |
| "B.splitlines([keepends]) -> list of lines\n\ |
| \n\ |
| Return a list of the lines in B, breaking at line boundaries.\n\ |
| Line breaks are not included in the resulting list unless keepends\n\ |
| is given and true."); |
| |
| static PyObject* |
| stringlib_splitlines(PyObject *self, PyObject *args) |
| { |
| register Py_ssize_t i; |
| register Py_ssize_t j; |
| Py_ssize_t len; |
| int keepends = 0; |
| PyObject *list; |
| PyObject *str; |
| char *data; |
| |
| if (!PyArg_ParseTuple(args, "|i:splitlines", &keepends)) |
| return NULL; |
| |
| data = STRINGLIB_STR(self); |
| len = STRINGLIB_LEN(self); |
| |
| /* This does not use the preallocated list because splitlines is |
| usually run with hundreds of newlines. The overhead of |
| switching between PyList_SET_ITEM and append causes about a |
| 2-3% slowdown for that common case. A smarter implementation |
| could move the if check out, so the SET_ITEMs are done first |
| and the appends only done when the prealloc buffer is full. |
| That's too much work for little gain.*/ |
| |
| list = PyList_New(0); |
| if (!list) |
| goto onError; |
| |
| for (i = j = 0; i < len; ) { |
| Py_ssize_t eol; |
| |
| /* Find a line and append it */ |
| while (i < len && data[i] != '\n' && data[i] != '\r') |
| i++; |
| |
| /* Skip the line break reading CRLF as one line break */ |
| eol = i; |
| if (i < len) { |
| if (data[i] == '\r' && i + 1 < len && |
| data[i+1] == '\n') |
| i += 2; |
| else |
| i++; |
| if (keepends) |
| eol = i; |
| } |
| _STRINGLIB_SPLIT_APPEND(data, j, eol); |
| j = i; |
| } |
| if (j < len) { |
| _STRINGLIB_SPLIT_APPEND(data, j, len); |
| } |
| |
| return list; |
| |
| onError: |
| Py_XDECREF(list); |
| return NULL; |
| } |
| |
| #undef _STRINGLIB_SPLIT_APPEND |
| |