diff options
Diffstat (limited to 'Objects/bytearrayobject.c')
-rw-r--r-- | Objects/bytearrayobject.c | 2708 |
1 files changed, 1660 insertions, 1048 deletions
diff --git a/Objects/bytearrayobject.c b/Objects/bytearrayobject.c index c9bf11b..5824832 100644 --- a/Objects/bytearrayobject.c +++ b/Objects/bytearrayobject.c @@ -1,22 +1,23 @@ -/* PyByteArray (bytearray) implementation */ +/* PyBytes (bytearray) implementation */ #define PY_SSIZE_T_CLEAN #include "Python.h" -#include "pycore_object.h" -#include "pycore_pymem.h" -#include "pycore_pystate.h" #include "structmember.h" #include "bytes_methods.h" -#include "bytesobject.h" -#include "pystrhex.h" - -/*[clinic input] -class bytearray "PyByteArrayObject *" "&PyByteArray_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=5535b77c37a119e0]*/ char _PyByteArray_empty_string[] = ""; +void +PyByteArray_Fini(void) +{ +} + +int +PyByteArray_Init(void) +{ + return 1; +} + /* end nullbytes support */ /* Helpers */ @@ -26,12 +27,24 @@ _getbytevalue(PyObject* arg, int *value) { long face_value; - if (PyLong_Check(arg)) { + if (PyBytes_CheckExact(arg)) { + if (Py_SIZE(arg) != 1) { + PyErr_SetString(PyExc_ValueError, "string must be of size 1"); + return 0; + } + *value = Py_CHARMASK(((PyBytesObject*)arg)->ob_sval[0]); + return 1; + } + else if (_PyAnyInt_Check(arg)) { face_value = PyLong_AsLong(arg); - } else { + } + else { PyObject *index = PyNumber_Index(arg); if (index == NULL) { - *value = -1; + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Format(PyExc_TypeError, + "an integer or string of size 1 is required"); + } return 0; } face_value = PyLong_AsLong(index); @@ -41,7 +54,6 @@ _getbytevalue(PyObject* arg, int *value) if (face_value < 0 || face_value >= 256) { /* this includes the OverflowError in case the long is too large */ PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); - *value = -1; return 0; } @@ -49,20 +61,65 @@ _getbytevalue(PyObject* arg, int *value) return 1; } +static Py_ssize_t +bytearray_buffer_getreadbuf(PyByteArrayObject *self, Py_ssize_t index, const void **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent bytes segment"); + return -1; + } + *ptr = (void *)PyByteArray_AS_STRING(self); + return Py_SIZE(self); +} + +static Py_ssize_t +bytearray_buffer_getwritebuf(PyByteArrayObject *self, Py_ssize_t index, const void **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent bytes segment"); + return -1; + } + *ptr = (void *)PyByteArray_AS_STRING(self); + return Py_SIZE(self); +} + +static Py_ssize_t +bytearray_buffer_getsegcount(PyByteArrayObject *self, Py_ssize_t *lenp) +{ + if ( lenp ) + *lenp = Py_SIZE(self); + return 1; +} + +static Py_ssize_t +bytearray_buffer_getcharbuf(PyByteArrayObject *self, Py_ssize_t index, const char **ptr) +{ + if ( index != 0 ) { + PyErr_SetString(PyExc_SystemError, + "accessing non-existent bytes segment"); + return -1; + } + *ptr = PyByteArray_AS_STRING(self); + return Py_SIZE(self); +} + static int bytearray_getbuffer(PyByteArrayObject *obj, Py_buffer *view, int flags) { + int ret; void *ptr; if (view == NULL) { - PyErr_SetString(PyExc_BufferError, - "bytearray_getbuffer: view==NULL argument is obsolete"); - return -1; + obj->ob_exports++; + return 0; } ptr = (void *) PyByteArray_AS_STRING(obj); - /* cannot fail if view != NULL and readonly == 0 */ - (void)PyBuffer_FillInfo(view, (PyObject*)obj, ptr, Py_SIZE(obj), 0, flags); - obj->ob_exports++; - return 0; + ret = PyBuffer_FillInfo(view, (PyObject*)obj, ptr, Py_SIZE(obj), 0, flags); + if (ret >= 0) { + obj->ob_exports++; + } + return ret; } static void @@ -71,6 +128,24 @@ bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view) obj->ob_exports--; } +static Py_ssize_t +_getbuffer(PyObject *obj, Py_buffer *view) +{ + PyBufferProcs *buffer = Py_TYPE(obj)->tp_as_buffer; + + if (buffer == NULL || buffer->bf_getbuffer == NULL) + { + PyErr_Format(PyExc_TypeError, + "Type %.100s doesn't support the buffer API", + Py_TYPE(obj)->tp_name); + return -1; + } + + if (buffer->bf_getbuffer(obj, view, PyBUF_SIMPLE) < 0) + return -1; + return view->len; +} + static int _canresize(PyByteArrayObject *self) { @@ -82,14 +157,13 @@ _canresize(PyByteArrayObject *self) return 1; } -#include "clinic/bytearrayobject.c.h" - /* Direct API functions */ PyObject * PyByteArray_FromObject(PyObject *input) { - return _PyObject_CallOneArg((PyObject *)&PyByteArray_Type, input); + return PyObject_CallFunctionObjArgs((PyObject *)&PyByteArray_Type, + input, NULL); } static PyObject * @@ -124,11 +198,6 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size) return NULL; } - /* Prevent buffer overflow when setting alloc to size+1. */ - if (size == PY_SSIZE_T_MAX) { - return PyErr_NoMemory(); - } - new = PyObject_New(PyByteArrayObject, &PyByteArray_Type); if (new == NULL) return NULL; @@ -139,7 +208,7 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size) } else { alloc = size + 1; - new->ob_bytes = PyObject_Malloc(alloc); + new->ob_bytes = PyMem_Malloc(alloc); if (new->ob_bytes == NULL) { Py_DECREF(new); return PyErr_NoMemory(); @@ -150,7 +219,6 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size) } Py_SIZE(new) = size; new->ob_alloc = alloc; - new->ob_start = new->ob_bytes; new->ob_exports = 0; return (PyObject *)new; @@ -175,80 +243,51 @@ PyByteArray_AsString(PyObject *self) } int -PyByteArray_Resize(PyObject *self, Py_ssize_t requested_size) +PyByteArray_Resize(PyObject *self, Py_ssize_t size) { void *sval; - PyByteArrayObject *obj = ((PyByteArrayObject *)self); - /* All computations are done unsigned to avoid integer overflows - (see issue #22335). */ - size_t alloc = (size_t) obj->ob_alloc; - size_t logical_offset = (size_t) (obj->ob_start - obj->ob_bytes); - size_t size = (size_t) requested_size; + Py_ssize_t alloc = ((PyByteArrayObject *)self)->ob_alloc; assert(self != NULL); assert(PyByteArray_Check(self)); - assert(logical_offset <= alloc); - assert(requested_size >= 0); + assert(size >= 0); - if (requested_size == Py_SIZE(self)) { + if (size == Py_SIZE(self)) { return 0; } - if (!_canresize(obj)) { + if (!_canresize((PyByteArrayObject *)self)) { return -1; } - if (size + logical_offset + 1 <= alloc) { - /* Current buffer is large enough to host the requested size, - decide on a strategy. */ - if (size < alloc / 2) { - /* Major downsize; resize down to exact size */ - alloc = size + 1; - } - else { - /* Minor downsize; quick exit */ - Py_SIZE(self) = size; - PyByteArray_AS_STRING(self)[size] = '\0'; /* Trailing null */ - return 0; - } + if (size < alloc / 2) { + /* Major downsize; resize down to exact size */ + alloc = size + 1; + } + else if (size < alloc) { + /* Within allocated size; quick exit */ + Py_SIZE(self) = size; + ((PyByteArrayObject *)self)->ob_bytes[size] = '\0'; /* Trailing null */ + return 0; + } + else if (size <= alloc * 1.125) { + /* Moderate upsize; overallocate similar to list_resize() */ + alloc = size + (size >> 3) + (size < 9 ? 3 : 6); } else { - /* Need growing, decide on a strategy */ - if (size <= alloc * 1.125) { - /* Moderate upsize; overallocate similar to list_resize() */ - alloc = size + (size >> 3) + (size < 9 ? 3 : 6); - } - else { - /* Major upsize; resize up to exact size */ - alloc = size + 1; - } + /* Major upsize; resize up to exact size */ + alloc = size + 1; } - if (alloc > PY_SSIZE_T_MAX) { + + sval = PyMem_Realloc(((PyByteArrayObject *)self)->ob_bytes, alloc); + if (sval == NULL) { PyErr_NoMemory(); return -1; } - if (logical_offset > 0) { - sval = PyObject_Malloc(alloc); - if (sval == NULL) { - PyErr_NoMemory(); - return -1; - } - memcpy(sval, PyByteArray_AS_STRING(self), - Py_MIN((size_t)requested_size, (size_t)Py_SIZE(self))); - PyObject_Free(obj->ob_bytes); - } - else { - sval = PyObject_Realloc(obj->ob_bytes, alloc); - if (sval == NULL) { - PyErr_NoMemory(); - return -1; - } - } - - obj->ob_bytes = obj->ob_start = sval; + ((PyByteArrayObject *)self)->ob_bytes = sval; Py_SIZE(self) = size; - obj->ob_alloc = alloc; - obj->ob_bytes[size] = '\0'; /* Trailing null byte */ + ((PyByteArrayObject *)self)->ob_alloc = alloc; + ((PyByteArrayObject *)self)->ob_bytes[size] = '\0'; /* Trailing null byte */ return 0; } @@ -261,10 +300,10 @@ PyByteArray_Concat(PyObject *a, PyObject *b) va.len = -1; vb.len = -1; - if (PyObject_GetBuffer(a, &va, PyBUF_SIMPLE) != 0 || - PyObject_GetBuffer(b, &vb, PyBUF_SIMPLE) != 0) { + if (_getbuffer(a, &va) < 0 || + _getbuffer(b, &vb) < 0) { PyErr_Format(PyExc_TypeError, "can't concat %.100s to %.100s", - Py_TYPE(b)->tp_name, Py_TYPE(a)->tp_name); + Py_TYPE(a)->tp_name, Py_TYPE(b)->tp_name); goto done; } @@ -299,25 +338,31 @@ bytearray_length(PyByteArrayObject *self) static PyObject * bytearray_iconcat(PyByteArrayObject *self, PyObject *other) { + Py_ssize_t mysize; Py_ssize_t size; Py_buffer vo; - if (PyObject_GetBuffer(other, &vo, PyBUF_SIMPLE) != 0) { + if (_getbuffer(other, &vo) < 0) { PyErr_Format(PyExc_TypeError, "can't concat %.100s to %.100s", Py_TYPE(other)->tp_name, Py_TYPE(self)->tp_name); return NULL; } - size = Py_SIZE(self); - if (size > PY_SSIZE_T_MAX - vo.len) { + mysize = Py_SIZE(self); + if (mysize > PY_SSIZE_T_MAX - vo.len) { PyBuffer_Release(&vo); return PyErr_NoMemory(); } - if (PyByteArray_Resize((PyObject *)self, size + vo.len) < 0) { + size = mysize + vo.len; + if (size < self->ob_alloc) { + Py_SIZE(self) = size; + self->ob_bytes[Py_SIZE(self)] = '\0'; /* Trailing null byte */ + } + else if (PyByteArray_Resize((PyObject *)self, size) < 0) { PyBuffer_Release(&vo); return NULL; } - memcpy(PyByteArray_AS_STRING(self) + size, vo.buf, vo.len); + memcpy(self->ob_bytes + mysize, vo.buf, vo.len); PyBuffer_Release(&vo); Py_INCREF(self); return (PyObject *)self; @@ -333,7 +378,7 @@ bytearray_repeat(PyByteArrayObject *self, Py_ssize_t count) if (count < 0) count = 0; mysize = Py_SIZE(self); - if (count > 0 && mysize > PY_SSIZE_T_MAX / count) + if (count != 0 && mysize > PY_SSIZE_T_MAX / count) return PyErr_NoMemory(); size = mysize * count; result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); @@ -354,24 +399,26 @@ bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count) { Py_ssize_t mysize; Py_ssize_t size; - char *buf; if (count < 0) count = 0; mysize = Py_SIZE(self); - if (count > 0 && mysize > PY_SSIZE_T_MAX / count) + if (count != 0 && mysize > PY_SSIZE_T_MAX / count) return PyErr_NoMemory(); size = mysize * count; - if (PyByteArray_Resize((PyObject *)self, size) < 0) + if (size < self->ob_alloc) { + Py_SIZE(self) = size; + self->ob_bytes[Py_SIZE(self)] = '\0'; /* Trailing null byte */ + } + else if (PyByteArray_Resize((PyObject *)self, size) < 0) return NULL; - buf = PyByteArray_AS_STRING(self); if (mysize == 1) - memset(buf, buf[0], size); + memset(self->ob_bytes, self->ob_bytes[0], size); else { Py_ssize_t i; for (i = 1; i < count; i++) - memcpy(buf + i*mysize, buf, mysize); + memcpy(self->ob_bytes + i*mysize, self->ob_bytes, mysize); } Py_INCREF(self); @@ -381,11 +428,13 @@ bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count) static PyObject * bytearray_getitem(PyByteArrayObject *self, Py_ssize_t i) { + if (i < 0) + i += Py_SIZE(self); if (i < 0 || i >= Py_SIZE(self)) { PyErr_SetString(PyExc_IndexError, "bytearray index out of range"); return NULL; } - return PyLong_FromLong((unsigned char)(PyByteArray_AS_STRING(self)[i])); + return PyInt_FromLong((unsigned char)(self->ob_bytes[i])); } static PyObject * @@ -404,137 +453,50 @@ bytearray_subscript(PyByteArrayObject *self, PyObject *index) PyErr_SetString(PyExc_IndexError, "bytearray index out of range"); return NULL; } - return PyLong_FromLong((unsigned char)(PyByteArray_AS_STRING(self)[i])); + return PyInt_FromLong((unsigned char)(self->ob_bytes[i])); } else if (PySlice_Check(index)) { - Py_ssize_t start, stop, step, slicelength, i; - size_t cur; - if (PySlice_Unpack(index, &start, &stop, &step) < 0) { + Py_ssize_t start, stop, step, slicelength, cur, i; + if (_PySlice_Unpack(index, &start, &stop, &step) < 0) { return NULL; } - slicelength = PySlice_AdjustIndices(PyByteArray_GET_SIZE(self), + slicelength = _PySlice_AdjustIndices(PyByteArray_GET_SIZE(self), &start, &stop, step); if (slicelength <= 0) return PyByteArray_FromStringAndSize("", 0); else if (step == 1) { - return PyByteArray_FromStringAndSize( - PyByteArray_AS_STRING(self) + start, slicelength); + return PyByteArray_FromStringAndSize(self->ob_bytes + start, + slicelength); } else { char *source_buf = PyByteArray_AS_STRING(self); - char *result_buf; + char *result_buf = (char *)PyMem_Malloc(slicelength); PyObject *result; - result = PyByteArray_FromStringAndSize(NULL, slicelength); - if (result == NULL) - return NULL; + if (result_buf == NULL) + return PyErr_NoMemory(); - result_buf = PyByteArray_AS_STRING(result); for (cur = start, i = 0; i < slicelength; cur += step, i++) { result_buf[i] = source_buf[cur]; } + result = PyByteArray_FromStringAndSize(result_buf, slicelength); + PyMem_Free(result_buf); return result; } } else { - PyErr_Format(PyExc_TypeError, - "bytearray indices must be integers or slices, not %.200s", - Py_TYPE(index)->tp_name); + PyErr_SetString(PyExc_TypeError, "bytearray indices must be integers"); return NULL; } } static int -bytearray_setslice_linear(PyByteArrayObject *self, - Py_ssize_t lo, Py_ssize_t hi, - char *bytes, Py_ssize_t bytes_len) -{ - Py_ssize_t avail = hi - lo; - char *buf = PyByteArray_AS_STRING(self); - Py_ssize_t growth = bytes_len - avail; - int res = 0; - assert(avail >= 0); - - if (growth < 0) { - if (!_canresize(self)) - return -1; - - if (lo == 0) { - /* Shrink the buffer by advancing its logical start */ - self->ob_start -= growth; - /* - 0 lo hi old_size - | |<----avail----->|<-----tail------>| - | |<-bytes_len->|<-----tail------>| - 0 new_lo new_hi new_size - */ - } - else { - /* - 0 lo hi old_size - | |<----avail----->|<-----tomove------>| - | |<-bytes_len->|<-----tomove------>| - 0 lo new_hi new_size - */ - memmove(buf + lo + bytes_len, buf + hi, - Py_SIZE(self) - hi); - } - if (PyByteArray_Resize((PyObject *)self, - Py_SIZE(self) + growth) < 0) { - /* Issue #19578: Handling the memory allocation failure here is - tricky here because the bytearray object has already been - modified. Depending on growth and lo, the behaviour is - different. - - If growth < 0 and lo != 0, the operation is completed, but a - MemoryError is still raised and the memory block is not - shrunk. Otherwise, the bytearray is restored in its previous - state and a MemoryError is raised. */ - if (lo == 0) { - self->ob_start += growth; - return -1; - } - /* memmove() removed bytes, the bytearray object cannot be - restored in its previous state. */ - Py_SIZE(self) += growth; - res = -1; - } - buf = PyByteArray_AS_STRING(self); - } - else if (growth > 0) { - if (Py_SIZE(self) > (Py_ssize_t)PY_SSIZE_T_MAX - growth) { - PyErr_NoMemory(); - return -1; - } - - if (PyByteArray_Resize((PyObject *)self, - Py_SIZE(self) + growth) < 0) { - return -1; - } - buf = PyByteArray_AS_STRING(self); - /* Make the place for the additional bytes */ - /* - 0 lo hi old_size - | |<-avail->|<-----tomove------>| - | |<---bytes_len-->|<-----tomove------>| - 0 lo new_hi new_size - */ - memmove(buf + lo + bytes_len, buf + hi, - Py_SIZE(self) - lo - bytes_len); - } - - if (bytes_len > 0) - memcpy(buf + lo, bytes, bytes_len); - return res; -} - -static int bytearray_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi, PyObject *values) { - Py_ssize_t needed; + Py_ssize_t avail, needed; void *bytes; Py_buffer vbytes; int res = 0; @@ -557,14 +519,14 @@ bytearray_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi, needed = 0; } else { - if (PyObject_GetBuffer(values, &vbytes, PyBUF_SIMPLE) != 0) { - PyErr_Format(PyExc_TypeError, - "can't set bytearray slice from %.100s", - Py_TYPE(values)->tp_name); - return -1; - } - needed = vbytes.len; - bytes = vbytes.buf; + if (_getbuffer(values, &vbytes) < 0) { + PyErr_Format(PyExc_TypeError, + "can't set bytearray slice from %.100s", + Py_TYPE(values)->tp_name); + return -1; + } + needed = vbytes.len; + bytes = vbytes.buf; } if (lo < 0) @@ -574,9 +536,50 @@ bytearray_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi, if (hi > Py_SIZE(self)) hi = Py_SIZE(self); - res = bytearray_setslice_linear(self, lo, hi, bytes, needed); + avail = hi - lo; + if (avail < 0) + lo = hi = avail = 0; + + if (avail != needed) { + if (avail > needed) { + if (!_canresize(self)) { + res = -1; + goto finish; + } + /* + 0 lo hi old_size + | |<----avail----->|<-----tomove------>| + | |<-needed->|<-----tomove------>| + 0 lo new_hi new_size + */ + memmove(self->ob_bytes + lo + needed, self->ob_bytes + hi, + Py_SIZE(self) - hi); + } + /* XXX(nnorwitz): need to verify this can't overflow! */ + if (PyByteArray_Resize((PyObject *)self, + Py_SIZE(self) + needed - avail) < 0) { + res = -1; + goto finish; + } + if (avail < needed) { + /* + 0 lo hi old_size + | |<-avail->|<-----tomove------>| + | |<----needed---->|<-----tomove------>| + 0 lo new_hi new_size + */ + memmove(self->ob_bytes + lo + needed, self->ob_bytes + hi, + Py_SIZE(self) - lo - needed); + } + } + + if (needed > 0) + memcpy(self->ob_bytes + lo, bytes, needed); + + + finish: if (vbytes.len != -1) - PyBuffer_Release(&vbytes); + PyBuffer_Release(&vbytes); return res; } @@ -599,7 +602,7 @@ bytearray_setitem(PyByteArrayObject *self, Py_ssize_t i, PyObject *value) if (!_getbytevalue(value, &ival)) return -1; - PyByteArray_AS_STRING(self)[i] = ival; + self->ob_bytes[i] = ival; return 0; } @@ -607,8 +610,7 @@ static int bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values) { Py_ssize_t start, stop, step, slicelen, needed; - char *buf, *bytes; - buf = PyByteArray_AS_STRING(self); + char *bytes; if (PyIndex_Check(index)) { Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError); @@ -635,21 +637,19 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu int ival; if (!_getbytevalue(values, &ival)) return -1; - buf[i] = (char)ival; + self->ob_bytes[i] = (char)ival; return 0; } } else if (PySlice_Check(index)) { - if (PySlice_Unpack(index, &start, &stop, &step) < 0) { + if (_PySlice_Unpack(index, &start, &stop, &step) < 0) { return -1; } - slicelen = PySlice_AdjustIndices(PyByteArray_GET_SIZE(self), &start, + slicelen = _PySlice_AdjustIndices(PyByteArray_GET_SIZE(self), &start, &stop, step); } else { - PyErr_Format(PyExc_TypeError, - "bytearray indices must be integers or slices, not %.200s", - Py_TYPE(index)->tp_name); + PyErr_SetString(PyExc_TypeError, "bytearray indices must be integer"); return -1; } @@ -675,7 +675,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu } else { assert(PyByteArray_Check(values)); - bytes = PyByteArray_AS_STRING(values); + bytes = ((PyByteArrayObject *)values)->ob_bytes; needed = Py_SIZE(values); } /* Make sure b[5:2] = ... inserts before 5, not before 2. */ @@ -683,7 +683,38 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu (step > 0 && start > stop)) stop = start; if (step == 1) { - return bytearray_setslice_linear(self, start, stop, bytes, needed); + if (slicelen != needed) { + if (!_canresize(self)) + return -1; + if (slicelen > needed) { + /* + 0 start stop old_size + | |<---slicelen--->|<-----tomove------>| + | |<-needed->|<-----tomove------>| + 0 lo new_hi new_size + */ + memmove(self->ob_bytes + start + needed, self->ob_bytes + stop, + Py_SIZE(self) - stop); + } + if (PyByteArray_Resize((PyObject *)self, + Py_SIZE(self) + needed - slicelen) < 0) + return -1; + if (slicelen < needed) { + /* + 0 lo hi old_size + | |<-avail->|<-----tomove------>| + | |<----needed---->|<-----tomove------>| + 0 lo new_hi new_size + */ + memmove(self->ob_bytes + start + needed, self->ob_bytes + stop, + Py_SIZE(self) - start - needed); + } + } + + if (needed > 0) + memcpy(self->ob_bytes + start, bytes, needed); + + return 0; } else { if (needed == 0) { @@ -693,11 +724,6 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu if (!_canresize(self)) return -1; - - if (slicelen == 0) - /* Nothing to do here. */ - return 0; - if (step < 0) { stop = start + 1; start = stop + step * (slicelen - 1) - 1; @@ -710,14 +736,14 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu if (cur + step >= (size_t)PyByteArray_GET_SIZE(self)) lim = PyByteArray_GET_SIZE(self) - cur - 1; - memmove(buf + cur - i, - buf + cur + 1, lim); + memmove(self->ob_bytes + cur - i, + self->ob_bytes + cur + 1, lim); } /* Move the tail of the bytes, in one chunk */ - cur = start + (size_t)slicelen*step; + cur = start + slicelen*step; if (cur < (size_t)PyByteArray_GET_SIZE(self)) { - memmove(buf + cur - slicelen, - buf + cur, + memmove(self->ob_bytes + cur - slicelen, + self->ob_bytes + cur, PyByteArray_GET_SIZE(self) - cur); } if (PyByteArray_Resize((PyObject *)self, @@ -728,8 +754,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu } else { /* Assign slice */ - Py_ssize_t i; - size_t cur; + Py_ssize_t cur, i; if (needed != slicelen) { PyErr_Format(PyExc_ValueError, @@ -739,7 +764,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu return -1; } for (cur = start, i = 0; i < slicelen; cur += step, i++) - buf[cur] = bytes[i]; + self->ob_bytes[cur] = bytes[i]; return 0; } } @@ -771,23 +796,42 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) if (arg == NULL) { if (encoding != NULL || errors != NULL) { PyErr_SetString(PyExc_TypeError, - encoding != NULL ? - "encoding without a string argument" : - "errors without a string argument"); + "encoding or errors without sequence argument"); return -1; } return 0; } + if (PyBytes_Check(arg)) { + PyObject *new, *encoded; + if (encoding != NULL) { + encoded = _PyCodec_EncodeText(arg, encoding, errors); + if (encoded == NULL) + return -1; + assert(PyBytes_Check(encoded)); + } + else { + encoded = arg; + Py_INCREF(arg); + } + new = bytearray_iconcat(self, arg); + Py_DECREF(encoded); + if (new == NULL) + return -1; + Py_DECREF(new); + return 0; + } + +#ifdef Py_USING_UNICODE if (PyUnicode_Check(arg)) { /* Encode via the codec registry */ PyObject *encoded, *new; if (encoding == NULL) { PyErr_SetString(PyExc_TypeError, - "string argument without an encoding"); + "unicode argument without an encoding"); return -1; } - encoded = PyUnicode_AsEncodedString(arg, encoding, errors); + encoded = _PyCodec_EncodeText(arg, encoding, errors); if (encoded == NULL) return -1; assert(PyBytes_Check(encoded)); @@ -798,36 +842,33 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) Py_DECREF(new); return 0; } +#endif /* If it's not unicode, there can't be encoding or errors */ if (encoding != NULL || errors != NULL) { PyErr_SetString(PyExc_TypeError, - encoding != NULL ? - "encoding without a string argument" : - "errors without a string argument"); + "encoding or errors without a string argument"); return -1; } /* Is it an int? */ - if (PyIndex_Check(arg)) { - count = PyNumber_AsSsize_t(arg, PyExc_OverflowError); - if (count == -1 && PyErr_Occurred()) { - if (!PyErr_ExceptionMatches(PyExc_TypeError)) - return -1; - PyErr_Clear(); /* fall through */ - } - else { - if (count < 0) { - PyErr_SetString(PyExc_ValueError, "negative count"); + count = PyNumber_AsSsize_t(arg, PyExc_OverflowError); + if (count == -1 && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_TypeError)) + return -1; + PyErr_Clear(); + } + else if (count < 0) { + PyErr_SetString(PyExc_ValueError, "negative count"); + return -1; + } + else { + if (count > 0) { + if (PyByteArray_Resize((PyObject *)self, count)) return -1; - } - if (count > 0) { - if (PyByteArray_Resize((PyObject *)self, count)) - return -1; - memset(PyByteArray_AS_STRING(self), 0, count); - } - return 0; + memset(self->ob_bytes, 0, count); } + return 0; } /* Use the buffer API */ @@ -838,9 +879,8 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) return -1; size = view.len; if (PyByteArray_Resize((PyObject *)self, size) < 0) goto fail; - if (PyBuffer_ToContiguous(PyByteArray_AS_STRING(self), - &view, size, 'C') < 0) - goto fail; + if (PyBuffer_ToContiguous(self->ob_bytes, &view, size, 'C') < 0) + goto fail; PyBuffer_Release(&view); return 0; fail: @@ -852,14 +892,8 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) /* Get the iterator */ it = PyObject_GetIter(arg); - if (it == NULL) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "cannot convert '%.200s' object to bytearray", - arg->ob_type->tp_name); - } + if (it == NULL) return -1; - } iternext = *Py_TYPE(it)->tp_iternext; /* Run the iterator to exhaustion */ @@ -891,7 +925,7 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) } else if (PyByteArray_Resize((PyObject *)self, Py_SIZE(self)+1) < 0) goto error; - PyByteArray_AS_STRING(self)[Py_SIZE(self)-1] = value; + self->ob_bytes[Py_SIZE(self)-1] = value; } /* Clean up and return success */ @@ -909,101 +943,91 @@ bytearray_init(PyByteArrayObject *self, PyObject *args, PyObject *kwds) static PyObject * bytearray_repr(PyByteArrayObject *self) { - const char *className = _PyType_Name(Py_TYPE(self)); - const char *quote_prefix = "(b"; + static const char *hexdigits = "0123456789abcdef"; + const char *quote_prefix = "bytearray(b"; const char *quote_postfix = ")"; Py_ssize_t length = Py_SIZE(self); - /* 6 == strlen(quote_prefix) + 2 + strlen(quote_postfix) + 1 */ - Py_ssize_t newsize; + /* 14 == strlen(quote_prefix) + 2 + strlen(quote_postfix) */ + size_t newsize; PyObject *v; - Py_ssize_t i; - char *bytes; - char c; - char *p; - int quote; - char *test, *start; - char *buffer; - - newsize = strlen(className); - if (length > (PY_SSIZE_T_MAX - 6 - newsize) / 4) { + if (length > (PY_SSIZE_T_MAX - 14) / 4) { PyErr_SetString(PyExc_OverflowError, "bytearray object is too large to make repr"); return NULL; } - - newsize += 6 + length * 4; - buffer = PyObject_Malloc(newsize); - if (buffer == NULL) { - PyErr_NoMemory(); + newsize = 14 + 4 * length; + v = PyString_FromStringAndSize(NULL, newsize); + if (v == NULL) { return NULL; } + else { + register Py_ssize_t i; + register char c; + register char *p; + int quote; + + /* Figure out which quote to use; single is preferred */ + quote = '\''; + { + char *test, *start; + start = PyByteArray_AS_STRING(self); + for (test = start; test < start+length; ++test) { + if (*test == '"') { + quote = '\''; /* back to single */ + goto decided; + } + else if (*test == '\'') + quote = '"'; + } + decided: + ; + } - /* Figure out which quote to use; single is preferred */ - quote = '\''; - start = PyByteArray_AS_STRING(self); - for (test = start; test < start+length; ++test) { - if (*test == '"') { - quote = '\''; /* back to single */ - break; + p = PyString_AS_STRING(v); + while (*quote_prefix) + *p++ = *quote_prefix++; + *p++ = quote; + + for (i = 0; i < length; i++) { + /* There's at least enough room for a hex escape + and a closing quote. */ + assert(newsize - (p - PyString_AS_STRING(v)) >= 5); + c = self->ob_bytes[i]; + if (c == '\'' || c == '\\') + *p++ = '\\', *p++ = c; + else if (c == '\t') + *p++ = '\\', *p++ = 't'; + else if (c == '\n') + *p++ = '\\', *p++ = 'n'; + else if (c == '\r') + *p++ = '\\', *p++ = 'r'; + else if (c == 0) + *p++ = '\\', *p++ = 'x', *p++ = '0', *p++ = '0'; + else if (c < ' ' || c >= 0x7f) { + *p++ = '\\'; + *p++ = 'x'; + *p++ = hexdigits[(c & 0xf0) >> 4]; + *p++ = hexdigits[c & 0xf]; + } + else + *p++ = c; } - else if (*test == '\'') - quote = '"'; - } - - p = buffer; - while (*className) - *p++ = *className++; - while (*quote_prefix) - *p++ = *quote_prefix++; - *p++ = quote; - - bytes = PyByteArray_AS_STRING(self); - for (i = 0; i < length; i++) { - /* There's at least enough room for a hex escape - and a closing quote. */ - assert(newsize - (p - buffer) >= 5); - c = bytes[i]; - if (c == '\'' || c == '\\') - *p++ = '\\', *p++ = c; - else if (c == '\t') - *p++ = '\\', *p++ = 't'; - else if (c == '\n') - *p++ = '\\', *p++ = 'n'; - else if (c == '\r') - *p++ = '\\', *p++ = 'r'; - else if (c == 0) - *p++ = '\\', *p++ = 'x', *p++ = '0', *p++ = '0'; - else if (c < ' ' || c >= 0x7f) { - *p++ = '\\'; - *p++ = 'x'; - *p++ = Py_hexdigits[(c & 0xf0) >> 4]; - *p++ = Py_hexdigits[c & 0xf]; + assert(newsize - (p - PyString_AS_STRING(v)) >= 1); + *p++ = quote; + while (*quote_postfix) { + *p++ = *quote_postfix++; } - else - *p++ = c; - } - assert(newsize - (p - buffer) >= 1); - *p++ = quote; - while (*quote_postfix) { - *p++ = *quote_postfix++; + *p = '\0'; + /* v is cleared on error */ + (void)_PyString_Resize(&v, (p - PyString_AS_STRING(v))); + return v; } - - v = PyUnicode_FromStringAndSize(buffer, p - buffer); - PyObject_Free(buffer); - return v; } static PyObject * bytearray_str(PyObject *op) { - PyConfig *config = &_PyInterpreterState_GET_UNSAFE()->config; - if (config->bytes_warning) { - if (PyErr_WarnEx(PyExc_BytesWarning, - "str() on a bytearray instance", 1)) { - return NULL; - } - } - return bytearray_repr((PyByteArrayObject*)op); + return PyBytes_FromStringAndSize(((PyByteArrayObject*)op)->ob_bytes, Py_SIZE(op)); } static PyObject * @@ -1011,61 +1035,80 @@ bytearray_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t self_size, other_size; Py_buffer self_bytes, other_bytes; + PyObject *res; + Py_ssize_t minsize; int cmp, rc; /* Bytes can be compared to anything that supports the (binary) buffer API. Except that a comparison with Unicode is always an error, even if the comparison is for equality. */ +#ifdef Py_USING_UNICODE rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type); if (!rc) rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type); if (rc < 0) return NULL; if (rc) { - PyConfig *config = &_PyInterpreterState_GET_UNSAFE()->config; - if (config->bytes_warning && (op == Py_EQ || op == Py_NE)) { + if (Py_BytesWarningFlag && op == Py_EQ) { if (PyErr_WarnEx(PyExc_BytesWarning, - "Comparison between bytearray and string", 1)) + "Comparison between bytearray and unicode", 1)) return NULL; } - Py_RETURN_NOTIMPLEMENTED; + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } +#endif - if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) { + self_size = _getbuffer(self, &self_bytes); + if (self_size < 0) { PyErr_Clear(); - Py_RETURN_NOTIMPLEMENTED; + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } - self_size = self_bytes.len; - if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) { + other_size = _getbuffer(other, &other_bytes); + if (other_size < 0) { PyErr_Clear(); PyBuffer_Release(&self_bytes); - Py_RETURN_NOTIMPLEMENTED; + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; } - other_size = other_bytes.len; if (self_size != other_size && (op == Py_EQ || op == Py_NE)) { /* Shortcut: if the lengths differ, the objects differ */ - PyBuffer_Release(&self_bytes); - PyBuffer_Release(&other_bytes); - return PyBool_FromLong((op == Py_NE)); + cmp = (op == Py_NE); } else { - cmp = memcmp(self_bytes.buf, other_bytes.buf, - Py_MIN(self_size, other_size)); - /* In ISO C, memcmp() guarantees to use unsigned bytes! */ + minsize = self_size; + if (other_size < minsize) + minsize = other_size; - PyBuffer_Release(&self_bytes); - PyBuffer_Release(&other_bytes); + cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize); + /* In ISO C, memcmp() guarantees to use unsigned bytes! */ - if (cmp != 0) { - Py_RETURN_RICHCOMPARE(cmp, 0, op); + if (cmp == 0) { + if (self_size < other_size) + cmp = -1; + else if (self_size > other_size) + cmp = 1; } - Py_RETURN_RICHCOMPARE(self_size, other_size, op); + switch (op) { + case Py_LT: cmp = cmp < 0; break; + case Py_LE: cmp = cmp <= 0; break; + case Py_EQ: cmp = cmp == 0; break; + case Py_NE: cmp = cmp != 0; break; + case Py_GT: cmp = cmp > 0; break; + case Py_GE: cmp = cmp >= 0; break; + } } + res = cmp ? Py_True : Py_False; + PyBuffer_Release(&self_bytes); + PyBuffer_Release(&other_bytes); + Py_INCREF(res); + return res; } static void @@ -1077,7 +1120,7 @@ bytearray_dealloc(PyByteArrayObject *self) PyErr_Print(); } if (self->ob_bytes != 0) { - PyObject_Free(self->ob_bytes); + PyMem_Free(self->ob_bytes); } Py_TYPE(self)->tp_free((PyObject *)self); } @@ -1086,10 +1129,7 @@ bytearray_dealloc(PyByteArrayObject *self) /* -------------------------------------------------------------------- */ /* Methods */ -#define FASTSEARCH fastsearch -#define STRINGLIB(F) stringlib_##F #define STRINGLIB_CHAR char -#define STRINGLIB_SIZEOF_CHAR 1 #define STRINGLIB_LEN PyByteArray_GET_SIZE #define STRINGLIB_STR PyByteArray_AS_STRING #define STRINGLIB_NEW PyByteArray_FromStringAndSize @@ -1101,124 +1141,340 @@ bytearray_dealloc(PyByteArrayObject *self) #include "stringlib/fastsearch.h" #include "stringlib/count.h" #include "stringlib/find.h" -#include "stringlib/join.h" #include "stringlib/partition.h" #include "stringlib/split.h" #include "stringlib/ctype.h" #include "stringlib/transmogrify.h" -static PyObject * -bytearray_find(PyByteArrayObject *self, PyObject *args) +/* The following Py_LOCAL_INLINE and Py_LOCAL functions +were copied from the old char* style string object. */ + +/* helper macro to fixup start/end slice values */ +#define ADJUST_INDICES(start, end, len) \ + if (end > len) \ + end = len; \ + else if (end < 0) { \ + end += len; \ + if (end < 0) \ + end = 0; \ + } \ + if (start < 0) { \ + start += len; \ + if (start < 0) \ + start = 0; \ + } + +Py_LOCAL_INLINE(Py_ssize_t) +bytearray_find_internal(PyByteArrayObject *self, PyObject *args, int dir) { - return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + PyObject *subobj; + Py_buffer subbuf; + Py_ssize_t start=0, end=PY_SSIZE_T_MAX; + Py_ssize_t res; + + if (!stringlib_parse_args_finds("find/rfind/index/rindex", + args, &subobj, &start, &end)) + return -2; + if (_getbuffer(subobj, &subbuf) < 0) + return -2; + if (dir > 0) + res = stringlib_find_slice( + PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), + subbuf.buf, subbuf.len, start, end); + else + res = stringlib_rfind_slice( + PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), + subbuf.buf, subbuf.len, start, end); + PyBuffer_Release(&subbuf); + return res; } +PyDoc_STRVAR(find__doc__, +"B.find(sub [,start [,end]]) -> int\n\ +\n\ +Return the lowest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + static PyObject * -bytearray_count(PyByteArrayObject *self, PyObject *args) +bytearray_find(PyByteArrayObject *self, PyObject *args) { - return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + Py_ssize_t result = bytearray_find_internal(self, args, +1); + if (result == -2) + return NULL; + return PyInt_FromSsize_t(result); } -/*[clinic input] -bytearray.clear - -Remove all items from the bytearray. -[clinic start generated code]*/ +PyDoc_STRVAR(count__doc__, +"B.count(sub [,start [,end]]) -> int\n\ +\n\ +Return the number of non-overlapping occurrences of subsection sub in\n\ +bytes B[start:end]. Optional arguments start and end are interpreted\n\ +as in slice notation."); static PyObject * -bytearray_clear_impl(PyByteArrayObject *self) -/*[clinic end generated code: output=85c2fe6aede0956c input=ed6edae9de447ac4]*/ +bytearray_count(PyByteArrayObject *self, PyObject *args) { - if (PyByteArray_Resize((PyObject *)self, 0) < 0) + PyObject *sub_obj; + const char *str = PyByteArray_AS_STRING(self); + Py_ssize_t start = 0, end = PY_SSIZE_T_MAX; + Py_buffer vsub; + PyObject *count_obj; + + if (!stringlib_parse_args_finds("count", args, &sub_obj, &start, &end)) return NULL; - Py_RETURN_NONE; -} -/*[clinic input] -bytearray.copy + if (_getbuffer(sub_obj, &vsub) < 0) + return NULL; -Return a copy of B. -[clinic start generated code]*/ + ADJUST_INDICES(start, end, PyByteArray_GET_SIZE(self)); -static PyObject * -bytearray_copy_impl(PyByteArrayObject *self) -/*[clinic end generated code: output=68cfbcfed484c132 input=6597b0c01bccaa9e]*/ -{ - return PyByteArray_FromStringAndSize(PyByteArray_AS_STRING((PyObject *)self), - PyByteArray_GET_SIZE(self)); + count_obj = PyInt_FromSsize_t( + stringlib_count(str + start, end - start, vsub.buf, vsub.len, PY_SSIZE_T_MAX) + ); + PyBuffer_Release(&vsub); + return count_obj; } + +PyDoc_STRVAR(index__doc__, +"B.index(sub [,start [,end]]) -> int\n\ +\n\ +Like B.find() but raise ValueError when the subsection is not found."); + static PyObject * bytearray_index(PyByteArrayObject *self, PyObject *args) { - return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + Py_ssize_t result = bytearray_find_internal(self, args, +1); + if (result == -2) + return NULL; + if (result == -1) { + PyErr_SetString(PyExc_ValueError, + "subsection not found"); + return NULL; + } + return PyInt_FromSsize_t(result); } + +PyDoc_STRVAR(rfind__doc__, +"B.rfind(sub [,start [,end]]) -> int\n\ +\n\ +Return the highest index in B where subsection sub is found,\n\ +such that sub is contained within B[start,end]. Optional\n\ +arguments start and end are interpreted as in slice notation.\n\ +\n\ +Return -1 on failure."); + static PyObject * bytearray_rfind(PyByteArrayObject *self, PyObject *args) { - return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + Py_ssize_t result = bytearray_find_internal(self, args, -1); + if (result == -2) + return NULL; + return PyInt_FromSsize_t(result); } + +PyDoc_STRVAR(rindex__doc__, +"B.rindex(sub [,start [,end]]) -> int\n\ +\n\ +Like B.rfind() but raise ValueError when the subsection is not found."); + static PyObject * bytearray_rindex(PyByteArrayObject *self, PyObject *args) { - return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + Py_ssize_t result = bytearray_find_internal(self, args, -1); + if (result == -2) + return NULL; + if (result == -1) { + PyErr_SetString(PyExc_ValueError, + "subsection not found"); + return NULL; + } + return PyInt_FromSsize_t(result); } + static int bytearray_contains(PyObject *self, PyObject *arg) { - return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg); + Py_ssize_t ival = PyNumber_AsSsize_t(arg, PyExc_ValueError); + if (ival == -1 && PyErr_Occurred()) { + Py_buffer varg; + int pos; + PyErr_Clear(); + if (_getbuffer(arg, &varg) < 0) + return -1; + pos = stringlib_find(PyByteArray_AS_STRING(self), Py_SIZE(self), + varg.buf, varg.len, 0); + PyBuffer_Release(&varg); + return pos >= 0; + } + if (ival < 0 || ival >= 256) { + PyErr_SetString(PyExc_ValueError, "byte must be in range(0, 256)"); + return -1; + } + + return memchr(PyByteArray_AS_STRING(self), ival, Py_SIZE(self)) != NULL; } -static PyObject * -bytearray_startswith(PyByteArrayObject *self, PyObject *args) + +/* Matches the end (direction >= 0) or start (direction < 0) of self + * against substr, using the start and end arguments. Returns + * -1 on error, 0 if not found and 1 if found. + */ +Py_LOCAL(int) +_bytearray_tailmatch(PyByteArrayObject *self, PyObject *substr, Py_ssize_t start, + Py_ssize_t end, int direction) { - return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + Py_ssize_t len = PyByteArray_GET_SIZE(self); + const char* str; + Py_buffer vsubstr; + int rv = 0; + + str = PyByteArray_AS_STRING(self); + + if (_getbuffer(substr, &vsubstr) < 0) + return -1; + + ADJUST_INDICES(start, end, len); + + if (direction < 0) { + /* startswith */ + if (start+vsubstr.len > len) { + goto done; + } + } else { + /* endswith */ + if (end-start < vsubstr.len || start > len) { + goto done; + } + + if (end-vsubstr.len > start) + start = end - vsubstr.len; + } + if (end-start >= vsubstr.len) + rv = ! memcmp(str+start, vsubstr.buf, vsubstr.len); + +done: + PyBuffer_Release(&vsubstr); + return rv; } + +PyDoc_STRVAR(startswith__doc__, +"B.startswith(prefix [,start [,end]]) -> bool\n\ +\n\ +Return True if B starts with the specified prefix, False otherwise.\n\ +With optional start, test B beginning at that position.\n\ +With optional end, stop comparing B at that position.\n\ +prefix can also be a tuple of strings to try."); + static PyObject * -bytearray_endswith(PyByteArrayObject *self, PyObject *args) +bytearray_startswith(PyByteArrayObject *self, PyObject *args) { - return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args); + Py_ssize_t start = 0; + Py_ssize_t end = PY_SSIZE_T_MAX; + PyObject *subobj; + int result; + + if (!stringlib_parse_args_finds("startswith", args, &subobj, &start, &end)) + return NULL; + if (PyTuple_Check(subobj)) { + Py_ssize_t i; + for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { + result = _bytearray_tailmatch(self, + PyTuple_GET_ITEM(subobj, i), + start, end, -1); + if (result == -1) + return NULL; + else if (result) { + Py_RETURN_TRUE; + } + } + Py_RETURN_FALSE; + } + result = _bytearray_tailmatch(self, subobj, start, end, -1); + if (result == -1) + return NULL; + else + return PyBool_FromLong(result); } +PyDoc_STRVAR(endswith__doc__, +"B.endswith(suffix [,start [,end]]) -> bool\n\ +\n\ +Return True if B ends with the specified suffix, False otherwise.\n\ +With optional start, test B beginning at that position.\n\ +With optional end, stop comparing B at that position.\n\ +suffix can also be a tuple of strings to try."); -/*[clinic input] -bytearray.translate +static PyObject * +bytearray_endswith(PyByteArrayObject *self, PyObject *args) +{ + Py_ssize_t start = 0; + Py_ssize_t end = PY_SSIZE_T_MAX; + PyObject *subobj; + int result; - table: object - Translation table, which must be a bytes object of length 256. - / - delete as deletechars: object(c_default="NULL") = b'' + if (!stringlib_parse_args_finds("endswith", args, &subobj, &start, &end)) + return NULL; + if (PyTuple_Check(subobj)) { + Py_ssize_t i; + for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) { + result = _bytearray_tailmatch(self, + PyTuple_GET_ITEM(subobj, i), + start, end, +1); + if (result == -1) + return NULL; + else if (result) { + Py_RETURN_TRUE; + } + } + Py_RETURN_FALSE; + } + result = _bytearray_tailmatch(self, subobj, start, end, +1); + if (result == -1) + return NULL; + else + return PyBool_FromLong(result); +} -Return a copy with each character mapped by the given translation table. -All characters occurring in the optional argument delete are removed. -The remaining characters are mapped through the given translation table. -[clinic start generated code]*/ +PyDoc_STRVAR(translate__doc__, +"B.translate(table[, deletechars]) -> bytearray\n\ +\n\ +Return a copy of B, where all characters occurring in the\n\ +optional argument deletechars are removed, and the remaining\n\ +characters have been mapped through the given translation\n\ +table, which must be a bytes object of length 256."); static PyObject * -bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, - PyObject *deletechars) -/*[clinic end generated code: output=b6a8f01c2a74e446 input=cfff956d4d127a9b]*/ +bytearray_translate(PyByteArrayObject *self, PyObject *args) { - char *input, *output; - const char *table_chars; - Py_ssize_t i, c; + register char *input, *output; + register const char *table; + register Py_ssize_t i, c; PyObject *input_obj = (PyObject*)self; const char *output_start; Py_ssize_t inlen; PyObject *result = NULL; int trans_table[256]; + PyObject *tableobj = NULL, *delobj = NULL; Py_buffer vtable, vdel; - if (table == Py_None) { - table_chars = NULL; + if (!PyArg_UnpackTuple(args, "translate", 1, 2, + &tableobj, &delobj)) + return NULL; + + if (tableobj == Py_None) { table = NULL; - } else if (PyObject_GetBuffer(table, &vtable, PyBUF_SIMPLE) != 0) { + tableobj = NULL; + } else if (_getbuffer(tableobj, &vtable) < 0) { return NULL; } else { if (vtable.len != 256) { @@ -1227,12 +1483,12 @@ bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, PyBuffer_Release(&vtable); return NULL; } - table_chars = (const char*)vtable.buf; + table = (const char*)vtable.buf; } - if (deletechars != NULL) { - if (PyObject_GetBuffer(deletechars, &vdel, PyBUF_SIMPLE) != 0) { - if (table != NULL) + if (delobj != NULL) { + if (_getbuffer(delobj, &vdel) < 0) { + if (tableobj != NULL) PyBuffer_Release(&vtable); return NULL; } @@ -1246,24 +1502,24 @@ bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, result = PyByteArray_FromStringAndSize((char *)NULL, inlen); if (result == NULL) goto done; - output_start = output = PyByteArray_AS_STRING(result); + output_start = output = PyByteArray_AsString(result); input = PyByteArray_AS_STRING(input_obj); - if (vdel.len == 0 && table_chars != NULL) { + if (vdel.len == 0 && table != NULL) { /* If no deletions are required, use faster code */ for (i = inlen; --i >= 0; ) { c = Py_CHARMASK(*input++); - *output++ = table_chars[c]; + *output++ = table[c]; } goto done; } - if (table_chars == NULL) { + if (table == NULL) { for (i = 0; i < 256; i++) trans_table[i] = Py_CHARMASK(i); } else { for (i = 0; i < 256; i++) - trans_table[i] = Py_CHARMASK(table_chars[i]); + trans_table[i] = Py_CHARMASK(table[i]); } for (i = 0; i < vdel.len; i++) @@ -1272,106 +1528,568 @@ bytearray_translate_impl(PyByteArrayObject *self, PyObject *table, for (i = inlen; --i >= 0; ) { c = Py_CHARMASK(*input++); if (trans_table[c] != -1) - *output++ = (char)trans_table[c]; + if (Py_CHARMASK(*output++ = (char)trans_table[c]) == c) + continue; } /* Fix the size of the resulting string */ if (inlen > 0) - if (PyByteArray_Resize(result, output - output_start) < 0) { - Py_CLEAR(result); - goto done; - } + PyByteArray_Resize(result, output - output_start); done: - if (table != NULL) + if (tableobj != NULL) PyBuffer_Release(&vtable); - if (deletechars != NULL) + if (delobj != NULL) PyBuffer_Release(&vdel); return result; } -/*[clinic input] +/* find and count characters and substrings */ -@staticmethod -bytearray.maketrans +#define findchar(target, target_len, c) \ + ((char *)memchr((const void *)(target), c, target_len)) - frm: Py_buffer - to: Py_buffer - / -Return a translation table useable for the bytes or bytearray translate method. +/* Bytes ops must return a string, create a copy */ +Py_LOCAL(PyByteArrayObject *) +return_self(PyByteArrayObject *self) +{ + return (PyByteArrayObject *)PyByteArray_FromStringAndSize( + PyByteArray_AS_STRING(self), + PyByteArray_GET_SIZE(self)); +} -The returned table will be one where each byte in frm is mapped to the byte at -the same position in to. +Py_LOCAL_INLINE(Py_ssize_t) +countchar(const char *target, Py_ssize_t target_len, char c, Py_ssize_t maxcount) +{ + Py_ssize_t count=0; + const char *start=target; + const char *end=target+target_len; -The bytes objects frm and to must be of the same length. -[clinic start generated code]*/ + while ( (start=findchar(start, end-start, c)) != NULL ) { + count++; + if (count >= maxcount) + break; + start += 1; + } + return count; +} -static PyObject * -bytearray_maketrans_impl(Py_buffer *frm, Py_buffer *to) -/*[clinic end generated code: output=1df267d99f56b15e input=5925a81d2fbbf151]*/ + +/* Algorithms for different cases of string replacement */ + +/* len(self)>=1, from="", len(to)>=1, maxcount>=1 */ +Py_LOCAL(PyByteArrayObject *) +replace_interleave(PyByteArrayObject *self, + const char *to_s, Py_ssize_t to_len, + Py_ssize_t maxcount) { - return _Py_bytes_maketrans(frm, to); + char *self_s, *result_s; + Py_ssize_t self_len, result_len; + Py_ssize_t count, i; + PyByteArrayObject *result; + + self_len = PyByteArray_GET_SIZE(self); + + /* 1 at the end plus 1 after every character; + count = min(maxcount, self_len + 1) */ + if (maxcount <= self_len) { + count = maxcount; + } + else { + /* Can't overflow: self_len + 1 <= maxcount <= PY_SSIZE_T_MAX. */ + count = self_len + 1; + } + + /* Check for overflow */ + /* result_len = count * to_len + self_len; */ + assert(count > 0); + if (to_len > (PY_SSIZE_T_MAX - self_len) / count) { + PyErr_SetString(PyExc_OverflowError, + "replace bytes is too long"); + return NULL; + } + result_len = count * to_len + self_len; + if (! (result = (PyByteArrayObject *) + PyByteArray_FromStringAndSize(NULL, result_len)) ) + return NULL; + + self_s = PyByteArray_AS_STRING(self); + result_s = PyByteArray_AS_STRING(result); + + /* TODO: special case single character, which doesn't need memcpy */ + + /* Lay the first one down (guaranteed this will occur) */ + Py_MEMCPY(result_s, to_s, to_len); + result_s += to_len; + count -= 1; + + for (i=0; i<count; i++) { + *result_s++ = *self_s++; + Py_MEMCPY(result_s, to_s, to_len); + result_s += to_len; + } + + /* Copy the rest of the original string */ + Py_MEMCPY(result_s, self_s, self_len-i); + + return result; } +/* Special case for deleting a single character */ +/* len(self)>=1, len(from)==1, to="", maxcount>=1 */ +Py_LOCAL(PyByteArrayObject *) +replace_delete_single_character(PyByteArrayObject *self, + char from_c, Py_ssize_t maxcount) +{ + char *self_s, *result_s; + char *start, *next, *end; + Py_ssize_t self_len, result_len; + Py_ssize_t count; + PyByteArrayObject *result; -/*[clinic input] -bytearray.replace + self_len = PyByteArray_GET_SIZE(self); + self_s = PyByteArray_AS_STRING(self); - old: Py_buffer - new: Py_buffer - count: Py_ssize_t = -1 - Maximum number of occurrences to replace. - -1 (the default value) means replace all occurrences. - / + count = countchar(self_s, self_len, from_c, maxcount); + if (count == 0) { + return return_self(self); + } -Return a copy with all occurrences of substring old replaced by new. + result_len = self_len - count; /* from_len == 1 */ + assert(result_len>=0); -If the optional argument count is given, only the first count occurrences are -replaced. -[clinic start generated code]*/ + if ( (result = (PyByteArrayObject *) + PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) + return NULL; + result_s = PyByteArray_AS_STRING(result); -static PyObject * -bytearray_replace_impl(PyByteArrayObject *self, Py_buffer *old, - Py_buffer *new, Py_ssize_t count) -/*[clinic end generated code: output=d39884c4dc59412a input=aa379d988637c7fb]*/ + start = self_s; + end = self_s + self_len; + while (count-- > 0) { + next = findchar(start, end-start, from_c); + if (next == NULL) + break; + Py_MEMCPY(result_s, start, next-start); + result_s += (next-start); + start = next+1; + } + Py_MEMCPY(result_s, start, end-start); + + return result; +} + +/* len(self)>=1, len(from)>=2, to="", maxcount>=1 */ + +Py_LOCAL(PyByteArrayObject *) +replace_delete_substring(PyByteArrayObject *self, + const char *from_s, Py_ssize_t from_len, + Py_ssize_t maxcount) +{ + char *self_s, *result_s; + char *start, *next, *end; + Py_ssize_t self_len, result_len; + Py_ssize_t count, offset; + PyByteArrayObject *result; + + self_len = PyByteArray_GET_SIZE(self); + self_s = PyByteArray_AS_STRING(self); + + count = stringlib_count(self_s, self_len, + from_s, from_len, + maxcount); + + if (count == 0) { + /* no matches */ + return return_self(self); + } + + result_len = self_len - (count * from_len); + assert (result_len>=0); + + if ( (result = (PyByteArrayObject *) + PyByteArray_FromStringAndSize(NULL, result_len)) == NULL ) + return NULL; + + result_s = PyByteArray_AS_STRING(result); + + start = self_s; + end = self_s + self_len; + while (count-- > 0) { + offset = stringlib_find(start, end-start, + from_s, from_len, + 0); + if (offset == -1) + break; + next = start + offset; + + Py_MEMCPY(result_s, start, next-start); + + result_s += (next-start); + start = next+from_len; + } + Py_MEMCPY(result_s, start, end-start); + return result; +} + +/* len(self)>=1, len(from)==len(to)==1, maxcount>=1 */ +Py_LOCAL(PyByteArrayObject *) +replace_single_character_in_place(PyByteArrayObject *self, + char from_c, char to_c, + Py_ssize_t maxcount) +{ + char *self_s, *result_s, *start, *end, *next; + Py_ssize_t self_len; + PyByteArrayObject *result; + + /* The result string will be the same size */ + self_s = PyByteArray_AS_STRING(self); + self_len = PyByteArray_GET_SIZE(self); + + next = findchar(self_s, self_len, from_c); + + if (next == NULL) { + /* No matches; return the original bytes */ + return return_self(self); + } + + /* Need to make a new bytes */ + result = (PyByteArrayObject *) PyByteArray_FromStringAndSize(NULL, self_len); + if (result == NULL) + return NULL; + result_s = PyByteArray_AS_STRING(result); + Py_MEMCPY(result_s, self_s, self_len); + + /* change everything in-place, starting with this one */ + start = result_s + (next-self_s); + *start = to_c; + start++; + end = result_s + self_len; + + while (--maxcount > 0) { + next = findchar(start, end-start, from_c); + if (next == NULL) + break; + *next = to_c; + start = next+1; + } + + return result; +} + +/* len(self)>=1, len(from)==len(to)>=2, maxcount>=1 */ +Py_LOCAL(PyByteArrayObject *) +replace_substring_in_place(PyByteArrayObject *self, + const char *from_s, Py_ssize_t from_len, + const char *to_s, Py_ssize_t to_len, + Py_ssize_t maxcount) +{ + char *result_s, *start, *end; + char *self_s; + Py_ssize_t self_len, offset; + PyByteArrayObject *result; + + /* The result bytes will be the same size */ + + self_s = PyByteArray_AS_STRING(self); + self_len = PyByteArray_GET_SIZE(self); + + offset = stringlib_find(self_s, self_len, + from_s, from_len, + 0); + if (offset == -1) { + /* No matches; return the original bytes */ + return return_self(self); + } + + /* Need to make a new bytes */ + result = (PyByteArrayObject *) PyByteArray_FromStringAndSize(NULL, self_len); + if (result == NULL) + return NULL; + result_s = PyByteArray_AS_STRING(result); + Py_MEMCPY(result_s, self_s, self_len); + + /* change everything in-place, starting with this one */ + start = result_s + offset; + Py_MEMCPY(start, to_s, from_len); + start += from_len; + end = result_s + self_len; + + while ( --maxcount > 0) { + offset = stringlib_find(start, end-start, + from_s, from_len, + 0); + if (offset==-1) + break; + Py_MEMCPY(start+offset, to_s, from_len); + start += offset+from_len; + } + + return result; +} + +/* len(self)>=1, len(from)==1, len(to)>=2, maxcount>=1 */ +Py_LOCAL(PyByteArrayObject *) +replace_single_character(PyByteArrayObject *self, + char from_c, + const char *to_s, Py_ssize_t to_len, + Py_ssize_t maxcount) +{ + char *self_s, *result_s; + char *start, *next, *end; + Py_ssize_t self_len, result_len; + Py_ssize_t count; + PyByteArrayObject *result; + + self_s = PyByteArray_AS_STRING(self); + self_len = PyByteArray_GET_SIZE(self); + + count = countchar(self_s, self_len, from_c, maxcount); + if (count == 0) { + /* no matches, return unchanged */ + return return_self(self); + } + + /* use the difference between current and new, hence the "-1" */ + /* result_len = self_len + count * (to_len-1) */ + assert(count > 0); + if (to_len - 1 > (PY_SSIZE_T_MAX - self_len) / count) { + PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); + return NULL; + } + result_len = self_len + count * (to_len - 1); + + if ( (result = (PyByteArrayObject *) + PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) + return NULL; + result_s = PyByteArray_AS_STRING(result); + + start = self_s; + end = self_s + self_len; + while (count-- > 0) { + next = findchar(start, end-start, from_c); + if (next == NULL) + break; + + if (next == start) { + /* replace with the 'to' */ + Py_MEMCPY(result_s, to_s, to_len); + result_s += to_len; + start += 1; + } else { + /* copy the unchanged old then the 'to' */ + Py_MEMCPY(result_s, start, next-start); + result_s += (next-start); + Py_MEMCPY(result_s, to_s, to_len); + result_s += to_len; + start = next+1; + } + } + /* Copy the remainder of the remaining bytes */ + Py_MEMCPY(result_s, start, end-start); + + return result; +} + +/* len(self)>=1, len(from)>=2, len(to)>=2, maxcount>=1 */ +Py_LOCAL(PyByteArrayObject *) +replace_substring(PyByteArrayObject *self, + const char *from_s, Py_ssize_t from_len, + const char *to_s, Py_ssize_t to_len, + Py_ssize_t maxcount) +{ + char *self_s, *result_s; + char *start, *next, *end; + Py_ssize_t self_len, result_len; + Py_ssize_t count, offset; + PyByteArrayObject *result; + + self_s = PyByteArray_AS_STRING(self); + self_len = PyByteArray_GET_SIZE(self); + + count = stringlib_count(self_s, self_len, + from_s, from_len, + maxcount); + + if (count == 0) { + /* no matches, return unchanged */ + return return_self(self); + } + + /* Check for overflow */ + /* result_len = self_len + count * (to_len-from_len) */ + assert(count > 0); + if (to_len - from_len > (PY_SSIZE_T_MAX - self_len) / count) { + PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); + return NULL; + } + result_len = self_len + count * (to_len - from_len); + + if ( (result = (PyByteArrayObject *) + PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) + return NULL; + result_s = PyByteArray_AS_STRING(result); + + start = self_s; + end = self_s + self_len; + while (count-- > 0) { + offset = stringlib_find(start, end-start, + from_s, from_len, + 0); + if (offset == -1) + break; + next = start+offset; + if (next == start) { + /* replace with the 'to' */ + Py_MEMCPY(result_s, to_s, to_len); + result_s += to_len; + start += from_len; + } else { + /* copy the unchanged old then the 'to' */ + Py_MEMCPY(result_s, start, next-start); + result_s += (next-start); + Py_MEMCPY(result_s, to_s, to_len); + result_s += to_len; + start = next+from_len; + } + } + /* Copy the remainder of the remaining bytes */ + Py_MEMCPY(result_s, start, end-start); + + return result; +} + + +Py_LOCAL(PyByteArrayObject *) +replace(PyByteArrayObject *self, + const char *from_s, Py_ssize_t from_len, + const char *to_s, Py_ssize_t to_len, + Py_ssize_t maxcount) { - return stringlib_replace((PyObject *)self, - (const char *)old->buf, old->len, - (const char *)new->buf, new->len, count); + if (maxcount < 0) { + maxcount = PY_SSIZE_T_MAX; + } else if (maxcount == 0 || PyByteArray_GET_SIZE(self) == 0) { + /* nothing to do; return the original bytes */ + return return_self(self); + } + + if (maxcount == 0 || + (from_len == 0 && to_len == 0)) { + /* nothing to do; return the original bytes */ + return return_self(self); + } + + /* Handle zero-length special cases */ + + if (from_len == 0) { + /* insert the 'to' bytes everywhere. */ + /* >>> "Python".replace("", ".") */ + /* '.P.y.t.h.o.n.' */ + return replace_interleave(self, to_s, to_len, maxcount); + } + + /* Except for "".replace("", "A") == "A" there is no way beyond this */ + /* point for an empty self bytes to generate a non-empty bytes */ + /* Special case so the remaining code always gets a non-empty bytes */ + if (PyByteArray_GET_SIZE(self) == 0) { + return return_self(self); + } + + if (to_len == 0) { + /* delete all occurrences of 'from' bytes */ + if (from_len == 1) { + return replace_delete_single_character( + self, from_s[0], maxcount); + } else { + return replace_delete_substring(self, from_s, from_len, maxcount); + } + } + + /* Handle special case where both bytes have the same length */ + + if (from_len == to_len) { + if (from_len == 1) { + return replace_single_character_in_place( + self, + from_s[0], + to_s[0], + maxcount); + } else { + return replace_substring_in_place( + self, from_s, from_len, to_s, to_len, maxcount); + } + } + + /* Otherwise use the more generic algorithms */ + if (from_len == 1) { + return replace_single_character(self, from_s[0], + to_s, to_len, maxcount); + } else { + /* len('from')>=2, len('to')>=1 */ + return replace_substring(self, from_s, from_len, to_s, to_len, maxcount); + } } -/*[clinic input] -bytearray.split - sep: object = None - The delimiter according which to split the bytearray. - None (the default value) means split on ASCII whitespace characters - (space, tab, return, newline, formfeed, vertical tab). - maxsplit: Py_ssize_t = -1 - Maximum number of splits to do. - -1 (the default value) means no limit. +PyDoc_STRVAR(replace__doc__, +"B.replace(old, new[, count]) -> bytes\n\ +\n\ +Return a copy of B with all occurrences of subsection\n\ +old replaced by new. If the optional argument count is\n\ +given, only the first count occurrences are replaced."); + +static PyObject * +bytearray_replace(PyByteArrayObject *self, PyObject *args) +{ + Py_ssize_t count = -1; + PyObject *from, *to, *res; + Py_buffer vfrom, vto; + + if (!PyArg_ParseTuple(args, "OO|n:replace", &from, &to, &count)) + return NULL; + + if (_getbuffer(from, &vfrom) < 0) + return NULL; + if (_getbuffer(to, &vto) < 0) { + PyBuffer_Release(&vfrom); + return NULL; + } + + res = (PyObject *)replace((PyByteArrayObject *) self, + vfrom.buf, vfrom.len, + vto.buf, vto.len, count); -Return a list of the sections in the bytearray, using sep as the delimiter. -[clinic start generated code]*/ + PyBuffer_Release(&vfrom); + PyBuffer_Release(&vto); + return res; +} + +PyDoc_STRVAR(split__doc__, +"B.split([sep[, maxsplit]]) -> list of bytearray\n\ +\n\ +Return a list of the sections in B, using sep as the delimiter.\n\ +If sep is not given, B is split on ASCII whitespace characters\n\ +(space, tab, return, newline, formfeed, vertical tab).\n\ +If maxsplit is given, at most maxsplit splits are done."); static PyObject * -bytearray_split_impl(PyByteArrayObject *self, PyObject *sep, - Py_ssize_t maxsplit) -/*[clinic end generated code: output=833e2cf385d9a04d input=24f82669f41bf523]*/ +bytearray_split(PyByteArrayObject *self, PyObject *args) { Py_ssize_t len = PyByteArray_GET_SIZE(self), n; + Py_ssize_t maxsplit = -1; const char *s = PyByteArray_AS_STRING(self), *sub; - PyObject *list; + PyObject *list, *subobj = Py_None; Py_buffer vsub; + if (!PyArg_ParseTuple(args, "|On:split", &subobj, &maxsplit)) + return NULL; if (maxsplit < 0) maxsplit = PY_SSIZE_T_MAX; - if (sep == Py_None) + if (subobj == Py_None) return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit); - if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) + if (_getbuffer(subobj, &vsub) < 0) return NULL; sub = vsub.buf; n = vsub.len; @@ -1383,29 +2101,19 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep, return list; } -/*[clinic input] -bytearray.partition - - sep: object - / - -Partition the bytearray into three parts using the given separator. - -This will search for the separator sep in the bytearray. If the separator is -found, returns a 3-tuple containing the part before the separator, the -separator itself, and the part after it as new bytearray objects. - -If the separator is not found, returns a 3-tuple containing the copy of the -original bytearray object and two empty bytearray objects. -[clinic start generated code]*/ +PyDoc_STRVAR(partition__doc__, +"B.partition(sep) -> (head, sep, tail)\n\ +\n\ +Searches for the separator sep in B, and returns the part before it,\n\ +the separator itself, and the part after it. If the separator is not\n\ +found, returns B and two empty bytearray objects."); static PyObject * -bytearray_partition(PyByteArrayObject *self, PyObject *sep) -/*[clinic end generated code: output=45d2525ddd35f957 input=8f644749ee4fc83a]*/ +bytearray_partition(PyByteArrayObject *self, PyObject *sep_obj) { PyObject *bytesep, *result; - bytesep = _PyByteArray_FromBufferObject(sep); + bytesep = _PyByteArray_FromBufferObject(sep_obj); if (! bytesep) return NULL; @@ -1420,30 +2128,20 @@ bytearray_partition(PyByteArrayObject *self, PyObject *sep) return result; } -/*[clinic input] -bytearray.rpartition - - sep: object - / - -Partition the bytearray into three parts using the given separator. - -This will search for the separator sep in the bytearray, starting at the end. -If the separator is found, returns a 3-tuple containing the part before the -separator, the separator itself, and the part after it as new bytearray -objects. - -If the separator is not found, returns a 3-tuple containing two empty bytearray -objects and the copy of the original bytearray object. -[clinic start generated code]*/ +PyDoc_STRVAR(rpartition__doc__, +"B.rpartition(sep) -> (head, sep, tail)\n\ +\n\ +Searches for the separator sep in B, starting at the end of B,\n\ +and returns the part before it, the separator itself, and the\n\ +part after it. If the separator is not found, returns two empty\n\ +bytearray objects and B."); static PyObject * -bytearray_rpartition(PyByteArrayObject *self, PyObject *sep) -/*[clinic end generated code: output=440de3c9426115e8 input=7e3df3e6cb8fa0ac]*/ +bytearray_rpartition(PyByteArrayObject *self, PyObject *sep_obj) { PyObject *bytesep, *result; - bytesep = _PyByteArray_FromBufferObject(sep); + bytesep = _PyByteArray_FromBufferObject(sep_obj); if (! bytesep) return NULL; @@ -1458,31 +2156,33 @@ bytearray_rpartition(PyByteArrayObject *self, PyObject *sep) return result; } -/*[clinic input] -bytearray.rsplit = bytearray.split - -Return a list of the sections in the bytearray, using sep as the delimiter. - -Splitting is done starting at the end of the bytearray and working to the front. -[clinic start generated code]*/ +PyDoc_STRVAR(rsplit__doc__, +"B.rsplit(sep[, maxsplit]) -> list of bytearray\n\ +\n\ +Return a list of the sections in B, using sep as the delimiter,\n\ +starting at the end of B and working to the front.\n\ +If sep is not given, B is split on ASCII whitespace characters\n\ +(space, tab, return, newline, formfeed, vertical tab).\n\ +If maxsplit is given, at most maxsplit splits are done."); static PyObject * -bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep, - Py_ssize_t maxsplit) -/*[clinic end generated code: output=a55e0b5a03cb6190 input=a68286e4dd692ffe]*/ +bytearray_rsplit(PyByteArrayObject *self, PyObject *args) { Py_ssize_t len = PyByteArray_GET_SIZE(self), n; + Py_ssize_t maxsplit = -1; const char *s = PyByteArray_AS_STRING(self), *sub; - PyObject *list; + PyObject *list, *subobj = Py_None; Py_buffer vsub; + if (!PyArg_ParseTuple(args, "|On:rsplit", &subobj, &maxsplit)) + return NULL; if (maxsplit < 0) maxsplit = PY_SSIZE_T_MAX; - if (sep == Py_None) + if (subobj == Py_None) return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit); - if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) + if (_getbuffer(subobj, &vsub) < 0) return NULL; sub = vsub.buf; n = vsub.len; @@ -1494,21 +2194,18 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep, return list; } -/*[clinic input] -bytearray.reverse - -Reverse the order of the values in B in place. -[clinic start generated code]*/ - +PyDoc_STRVAR(reverse__doc__, +"B.reverse() -> None\n\ +\n\ +Reverse the order of the values in B in place."); static PyObject * -bytearray_reverse_impl(PyByteArrayObject *self) -/*[clinic end generated code: output=9f7616f29ab309d3 input=543356319fc78557]*/ +bytearray_reverse(PyByteArrayObject *self, PyObject *unused) { char swap, *head, *tail; Py_ssize_t i, j, n = Py_SIZE(self); j = n / 2; - head = PyByteArray_AS_STRING(self); + head = self->ob_bytes; tail = head + n - 1; for (i = 0; i < j; i++) { swap = *head; @@ -1519,72 +2216,55 @@ bytearray_reverse_impl(PyByteArrayObject *self) Py_RETURN_NONE; } - -/*[python input] -class bytesvalue_converter(CConverter): - type = 'int' - converter = '_getbytevalue' -[python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=29c2e7c26c212812]*/ - - -/*[clinic input] -bytearray.insert - - index: Py_ssize_t - The index where the value is to be inserted. - item: bytesvalue - The item to be inserted. - / - -Insert a single item into the bytearray before the given index. -[clinic start generated code]*/ - +PyDoc_STRVAR(insert__doc__, +"B.insert(index, int) -> None\n\ +\n\ +Insert a single item into the bytearray before the given index."); static PyObject * -bytearray_insert_impl(PyByteArrayObject *self, Py_ssize_t index, int item) -/*[clinic end generated code: output=76c775a70e7b07b7 input=b2b5d07e9de6c070]*/ +bytearray_insert(PyByteArrayObject *self, PyObject *args) { - Py_ssize_t n = Py_SIZE(self); - char *buf; + PyObject *value; + int ival; + Py_ssize_t where, n = Py_SIZE(self); + + if (!PyArg_ParseTuple(args, "nO:insert", &where, &value)) + return NULL; if (n == PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "cannot add more objects to bytearray"); return NULL; } + if (!_getbytevalue(value, &ival)) + return NULL; if (PyByteArray_Resize((PyObject *)self, n + 1) < 0) return NULL; - buf = PyByteArray_AS_STRING(self); - if (index < 0) { - index += n; - if (index < 0) - index = 0; + if (where < 0) { + where += n; + if (where < 0) + where = 0; } - if (index > n) - index = n; - memmove(buf + index + 1, buf + index, n - index); - buf[index] = item; + if (where > n) + where = n; + memmove(self->ob_bytes + where + 1, self->ob_bytes + where, n - where); + self->ob_bytes[where] = ival; Py_RETURN_NONE; } -/*[clinic input] -bytearray.append - - item: bytesvalue - The item to be appended. - / - -Append a single item to the end of the bytearray. -[clinic start generated code]*/ - +PyDoc_STRVAR(append__doc__, +"B.append(int) -> None\n\ +\n\ +Append a single item to the end of B."); static PyObject * -bytearray_append_impl(PyByteArrayObject *self, int item) -/*[clinic end generated code: output=a154e19ed1886cb6 input=20d6bec3d1340593]*/ +bytearray_append(PyByteArrayObject *self, PyObject *arg) { + int value; Py_ssize_t n = Py_SIZE(self); + if (! _getbytevalue(arg, &value)) + return NULL; if (n == PY_SSIZE_T_MAX) { PyErr_SetString(PyExc_OverflowError, "cannot add more objects to bytearray"); @@ -1593,24 +2273,18 @@ bytearray_append_impl(PyByteArrayObject *self, int item) if (PyByteArray_Resize((PyObject *)self, n + 1) < 0) return NULL; - PyByteArray_AS_STRING(self)[n] = item; + self->ob_bytes[n] = value; Py_RETURN_NONE; } -/*[clinic input] -bytearray.extend - - iterable_of_ints: object - The iterable of items to append. - / - -Append all the items from the iterator or sequence to the end of the bytearray. -[clinic start generated code]*/ - +PyDoc_STRVAR(extend__doc__, +"B.extend(iterable int) -> None\n\ +\n\ +Append all the elements from the iterator or sequence to the\n\ +end of B."); static PyObject * -bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints) -/*[clinic end generated code: output=98155dbe249170b1 input=c617b3a93249ba28]*/ +bytearray_extend(PyByteArrayObject *self, PyObject *arg) { PyObject *it, *item, *bytearray_obj; Py_ssize_t buf_size = 0, len = 0; @@ -1618,25 +2292,19 @@ bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints) char *buf; /* bytearray_setslice code only accepts something supporting PEP 3118. */ - if (PyObject_CheckBuffer(iterable_of_ints)) { - if (bytearray_setslice(self, Py_SIZE(self), Py_SIZE(self), iterable_of_ints) == -1) + if (PyObject_CheckBuffer(arg)) { + if (bytearray_setslice(self, Py_SIZE(self), Py_SIZE(self), arg) == -1) return NULL; Py_RETURN_NONE; } - it = PyObject_GetIter(iterable_of_ints); - if (it == NULL) { - if (PyErr_ExceptionMatches(PyExc_TypeError)) { - PyErr_Format(PyExc_TypeError, - "can't extend bytearray with %.100s", - iterable_of_ints->ob_type->tp_name); - } + it = PyObject_GetIter(arg); + if (it == NULL) return NULL; - } /* Try to determine the length of the argument. 32 is arbitrary. */ - buf_size = PyObject_LengthHint(iterable_of_ints, 32); + buf_size = _PyObject_LengthHint(arg, 32); if (buf_size == -1) { Py_DECREF(it); return NULL; @@ -1695,83 +2363,68 @@ bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints) } Py_DECREF(bytearray_obj); - if (PyErr_Occurred()) { - return NULL; - } - Py_RETURN_NONE; } -/*[clinic input] -bytearray.pop - - index: Py_ssize_t = -1 - The index from where to remove the item. - -1 (the default value) means remove the last item. - / - -Remove and return a single item from B. - -If no index argument is given, will pop the last item. -[clinic start generated code]*/ - +PyDoc_STRVAR(pop__doc__, +"B.pop([index]) -> int\n\ +\n\ +Remove and return a single item from B. If no index\n\ +argument is given, will pop the last value."); static PyObject * -bytearray_pop_impl(PyByteArrayObject *self, Py_ssize_t index) -/*[clinic end generated code: output=e0ccd401f8021da8 input=3591df2d06c0d237]*/ +bytearray_pop(PyByteArrayObject *self, PyObject *args) { int value; - Py_ssize_t n = Py_SIZE(self); - char *buf; + Py_ssize_t where = -1, n = Py_SIZE(self); + + if (!PyArg_ParseTuple(args, "|n:pop", &where)) + return NULL; if (n == 0) { PyErr_SetString(PyExc_IndexError, "pop from empty bytearray"); return NULL; } - if (index < 0) - index += Py_SIZE(self); - if (index < 0 || index >= Py_SIZE(self)) { + if (where < 0) + where += Py_SIZE(self); + if (where < 0 || where >= Py_SIZE(self)) { PyErr_SetString(PyExc_IndexError, "pop index out of range"); return NULL; } if (!_canresize(self)) return NULL; - buf = PyByteArray_AS_STRING(self); - value = buf[index]; - memmove(buf + index, buf + index + 1, n - index); + value = self->ob_bytes[where]; + memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where); if (PyByteArray_Resize((PyObject *)self, n - 1) < 0) return NULL; - return PyLong_FromLong((unsigned char)value); + return PyInt_FromLong((unsigned char)value); } -/*[clinic input] -bytearray.remove - - value: bytesvalue - The value to remove. - / - -Remove the first occurrence of a value in the bytearray. -[clinic start generated code]*/ - +PyDoc_STRVAR(remove__doc__, +"B.remove(int) -> None\n\ +\n\ +Remove the first occurrence of a value in B."); static PyObject * -bytearray_remove_impl(PyByteArrayObject *self, int value) -/*[clinic end generated code: output=d659e37866709c13 input=121831240cd51ddf]*/ +bytearray_remove(PyByteArrayObject *self, PyObject *arg) { - Py_ssize_t where, n = Py_SIZE(self); - char *buf = PyByteArray_AS_STRING(self); + int value; + Py_ssize_t n = Py_SIZE(self); + char *where; - where = stringlib_find_char(buf, n, value); - if (where < 0) { + if (! _getbytevalue(arg, &value)) + return NULL; + + where = memchr(self->ob_bytes, value, n); + if (!where) { PyErr_SetString(PyExc_ValueError, "value not found in bytearray"); return NULL; } if (!_canresize(self)) return NULL; - memmove(buf + where, buf + where + 1, n - where); + memmove(where, where + 1, self->ob_bytes + n - where); if (PyByteArray_Resize((PyObject *)self, n - 1) < 0) return NULL; @@ -1781,356 +2434,367 @@ bytearray_remove_impl(PyByteArrayObject *self, int value) /* XXX These two helpers could be optimized if argsize == 1 */ static Py_ssize_t -lstrip_helper(const char *myptr, Py_ssize_t mysize, - const void *argptr, Py_ssize_t argsize) +lstrip_helper(unsigned char *myptr, Py_ssize_t mysize, + void *argptr, Py_ssize_t argsize) { Py_ssize_t i = 0; - while (i < mysize && memchr(argptr, (unsigned char) myptr[i], argsize)) + while (i < mysize && memchr(argptr, myptr[i], argsize)) i++; return i; } static Py_ssize_t -rstrip_helper(const char *myptr, Py_ssize_t mysize, - const void *argptr, Py_ssize_t argsize) +rstrip_helper(unsigned char *myptr, Py_ssize_t mysize, + void *argptr, Py_ssize_t argsize) { Py_ssize_t i = mysize - 1; - while (i >= 0 && memchr(argptr, (unsigned char) myptr[i], argsize)) + while (i >= 0 && memchr(argptr, myptr[i], argsize)) i--; return i + 1; } -/*[clinic input] -bytearray.strip - - bytes: object = None - / - -Strip leading and trailing bytes contained in the argument. - -If the argument is omitted or None, strip leading and trailing ASCII whitespace. -[clinic start generated code]*/ - +PyDoc_STRVAR(strip__doc__, +"B.strip([bytes]) -> bytearray\n\ +\n\ +Strip leading and trailing bytes contained in the argument.\n\ +If the argument is omitted, strip ASCII whitespace."); static PyObject * -bytearray_strip_impl(PyByteArrayObject *self, PyObject *bytes) -/*[clinic end generated code: output=760412661a34ad5a input=ef7bb59b09c21d62]*/ +bytearray_strip(PyByteArrayObject *self, PyObject *args) { - Py_ssize_t left, right, mysize, byteslen; - char *myptr; - const char *bytesptr; - Py_buffer vbytes; - - if (bytes == Py_None) { - bytesptr = "\t\n\r\f\v "; - byteslen = 6; + Py_ssize_t left, right, mysize, argsize; + void *myptr, *argptr; + PyObject *arg = Py_None; + Py_buffer varg; + if (!PyArg_ParseTuple(args, "|O:strip", &arg)) + return NULL; + if (arg == Py_None) { + argptr = "\t\n\r\f\v "; + argsize = 6; } else { - if (PyObject_GetBuffer(bytes, &vbytes, PyBUF_SIMPLE) != 0) + if (_getbuffer(arg, &varg) < 0) return NULL; - bytesptr = (const char *) vbytes.buf; - byteslen = vbytes.len; + argptr = varg.buf; + argsize = varg.len; } - myptr = PyByteArray_AS_STRING(self); + myptr = self->ob_bytes; mysize = Py_SIZE(self); - left = lstrip_helper(myptr, mysize, bytesptr, byteslen); + left = lstrip_helper(myptr, mysize, argptr, argsize); if (left == mysize) right = left; else - right = rstrip_helper(myptr, mysize, bytesptr, byteslen); - if (bytes != Py_None) - PyBuffer_Release(&vbytes); - return PyByteArray_FromStringAndSize(myptr + left, right - left); + right = rstrip_helper(myptr, mysize, argptr, argsize); + if (arg != Py_None) + PyBuffer_Release(&varg); + return PyByteArray_FromStringAndSize(self->ob_bytes + left, right - left); } -/*[clinic input] -bytearray.lstrip - - bytes: object = None - / - -Strip leading bytes contained in the argument. - -If the argument is omitted or None, strip leading ASCII whitespace. -[clinic start generated code]*/ - +PyDoc_STRVAR(lstrip__doc__, +"B.lstrip([bytes]) -> bytearray\n\ +\n\ +Strip leading bytes contained in the argument.\n\ +If the argument is omitted, strip leading ASCII whitespace."); static PyObject * -bytearray_lstrip_impl(PyByteArrayObject *self, PyObject *bytes) -/*[clinic end generated code: output=d005c9d0ab909e66 input=80843f975dd7c480]*/ +bytearray_lstrip(PyByteArrayObject *self, PyObject *args) { - Py_ssize_t left, right, mysize, byteslen; - char *myptr; - const char *bytesptr; - Py_buffer vbytes; - - if (bytes == Py_None) { - bytesptr = "\t\n\r\f\v "; - byteslen = 6; + Py_ssize_t left, right, mysize, argsize; + void *myptr, *argptr; + PyObject *arg = Py_None; + Py_buffer varg; + if (!PyArg_ParseTuple(args, "|O:lstrip", &arg)) + return NULL; + if (arg == Py_None) { + argptr = "\t\n\r\f\v "; + argsize = 6; } else { - if (PyObject_GetBuffer(bytes, &vbytes, PyBUF_SIMPLE) != 0) + if (_getbuffer(arg, &varg) < 0) return NULL; - bytesptr = (const char *) vbytes.buf; - byteslen = vbytes.len; + argptr = varg.buf; + argsize = varg.len; } - myptr = PyByteArray_AS_STRING(self); + myptr = self->ob_bytes; mysize = Py_SIZE(self); - left = lstrip_helper(myptr, mysize, bytesptr, byteslen); + left = lstrip_helper(myptr, mysize, argptr, argsize); right = mysize; - if (bytes != Py_None) - PyBuffer_Release(&vbytes); - return PyByteArray_FromStringAndSize(myptr + left, right - left); + if (arg != Py_None) + PyBuffer_Release(&varg); + return PyByteArray_FromStringAndSize(self->ob_bytes + left, right - left); } -/*[clinic input] -bytearray.rstrip - - bytes: object = None - / - -Strip trailing bytes contained in the argument. - -If the argument is omitted or None, strip trailing ASCII whitespace. -[clinic start generated code]*/ - +PyDoc_STRVAR(rstrip__doc__, +"B.rstrip([bytes]) -> bytearray\n\ +\n\ +Strip trailing bytes contained in the argument.\n\ +If the argument is omitted, strip trailing ASCII whitespace."); static PyObject * -bytearray_rstrip_impl(PyByteArrayObject *self, PyObject *bytes) -/*[clinic end generated code: output=030e2fbd2f7276bd input=e728b994954cfd91]*/ +bytearray_rstrip(PyByteArrayObject *self, PyObject *args) { - Py_ssize_t right, mysize, byteslen; - char *myptr; - const char *bytesptr; - Py_buffer vbytes; - - if (bytes == Py_None) { - bytesptr = "\t\n\r\f\v "; - byteslen = 6; + Py_ssize_t left, right, mysize, argsize; + void *myptr, *argptr; + PyObject *arg = Py_None; + Py_buffer varg; + if (!PyArg_ParseTuple(args, "|O:rstrip", &arg)) + return NULL; + if (arg == Py_None) { + argptr = "\t\n\r\f\v "; + argsize = 6; } else { - if (PyObject_GetBuffer(bytes, &vbytes, PyBUF_SIMPLE) != 0) + if (_getbuffer(arg, &varg) < 0) return NULL; - bytesptr = (const char *) vbytes.buf; - byteslen = vbytes.len; + argptr = varg.buf; + argsize = varg.len; } - myptr = PyByteArray_AS_STRING(self); + myptr = self->ob_bytes; mysize = Py_SIZE(self); - right = rstrip_helper(myptr, mysize, bytesptr, byteslen); - if (bytes != Py_None) - PyBuffer_Release(&vbytes); - return PyByteArray_FromStringAndSize(myptr, right); + left = 0; + right = rstrip_helper(myptr, mysize, argptr, argsize); + if (arg != Py_None) + PyBuffer_Release(&varg); + return PyByteArray_FromStringAndSize(self->ob_bytes + left, right - left); } -/*[clinic input] -bytearray.decode - - encoding: str(c_default="NULL") = 'utf-8' - The encoding with which to decode the bytearray. - errors: str(c_default="NULL") = 'strict' - The error handling scheme to use for the handling of decoding errors. - The default is 'strict' meaning that decoding errors raise a - UnicodeDecodeError. Other possible values are 'ignore' and 'replace' - as well as any other name registered with codecs.register_error that - can handle UnicodeDecodeErrors. - -Decode the bytearray using the codec registered for encoding. -[clinic start generated code]*/ +PyDoc_STRVAR(decode_doc, +"B.decode([encoding[, errors]]) -> unicode object.\n\ +\n\ +Decodes B using the codec registered for encoding. encoding defaults\n\ +to the default encoding. errors may be given to set a different error\n\ +handling scheme. Default is 'strict' meaning that encoding errors raise\n\ +a UnicodeDecodeError. Other possible values are 'ignore' and 'replace'\n\ +as well as any other name registered with codecs.register_error that is\n\ +able to handle UnicodeDecodeErrors."); static PyObject * -bytearray_decode_impl(PyByteArrayObject *self, const char *encoding, - const char *errors) -/*[clinic end generated code: output=f57d43f4a00b42c5 input=f28d8f903020257b]*/ +bytearray_decode(PyObject *self, PyObject *args, PyObject *kwargs) { - if (encoding == NULL) + const char *encoding = NULL; + const char *errors = NULL; + static char *kwlist[] = {"encoding", "errors", 0}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ss:decode", kwlist, &encoding, &errors)) + return NULL; + if (encoding == NULL) { +#ifdef Py_USING_UNICODE encoding = PyUnicode_GetDefaultEncoding(); - return PyUnicode_FromEncodedObject((PyObject*)self, encoding, errors); +#else + PyErr_SetString(PyExc_ValueError, "no encoding specified"); + return NULL; +#endif + } + return _PyCodec_DecodeText(self, encoding, errors); } PyDoc_STRVAR(alloc_doc, "B.__alloc__() -> int\n\ \n\ -Return the number of bytes actually allocated."); +Returns the number of bytes actually allocated."); static PyObject * -bytearray_alloc(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored)) +bytearray_alloc(PyByteArrayObject *self) { - return PyLong_FromSsize_t(self->ob_alloc); + return PyInt_FromSsize_t(self->ob_alloc); } -/*[clinic input] -bytearray.join +PyDoc_STRVAR(join_doc, +"B.join(iterable_of_bytes) -> bytes\n\ +\n\ +Concatenates any number of bytearray objects, with B in between each pair."); - iterable_of_bytes: object - / +static PyObject * +bytearray_join(PyByteArrayObject *self, PyObject *it) +{ + PyObject *seq; + Py_ssize_t mysize = Py_SIZE(self); + Py_ssize_t i; + Py_ssize_t n; + PyObject **items; + Py_ssize_t totalsize = 0; + PyObject *result; + char *dest; -Concatenate any number of bytes/bytearray objects. + seq = PySequence_Fast(it, "can only join an iterable"); + if (seq == NULL) + return NULL; + n = PySequence_Fast_GET_SIZE(seq); + items = PySequence_Fast_ITEMS(seq); + + /* Compute the total size, and check that they are all bytes */ + /* XXX Shouldn't we use _getbuffer() on these items instead? */ + for (i = 0; i < n; i++) { + PyObject *obj = items[i]; + if (!PyByteArray_Check(obj) && !PyBytes_Check(obj)) { + PyErr_Format(PyExc_TypeError, + "can only join an iterable of bytes " + "(item %ld has type '%.100s')", + /* XXX %ld isn't right on Win64 */ + (long)i, Py_TYPE(obj)->tp_name); + goto error; + } + if (i > 0) + totalsize += mysize; + totalsize += Py_SIZE(obj); + if (totalsize < 0) { + PyErr_NoMemory(); + goto error; + } + } -The bytearray whose method is called is inserted in between each pair. + /* Allocate the result, and copy the bytes */ + result = PyByteArray_FromStringAndSize(NULL, totalsize); + if (result == NULL) + goto error; + dest = PyByteArray_AS_STRING(result); + for (i = 0; i < n; i++) { + PyObject *obj = items[i]; + Py_ssize_t size = Py_SIZE(obj); + char *buf; + if (PyByteArray_Check(obj)) + buf = PyByteArray_AS_STRING(obj); + else + buf = PyBytes_AS_STRING(obj); + if (i) { + memcpy(dest, self->ob_bytes, mysize); + dest += mysize; + } + memcpy(dest, buf, size); + dest += size; + } -The result is returned as a new bytearray object. -[clinic start generated code]*/ + /* Done */ + Py_DECREF(seq); + return result; -static PyObject * -bytearray_join(PyByteArrayObject *self, PyObject *iterable_of_bytes) -/*[clinic end generated code: output=a8516370bf68ae08 input=aba6b1f9b30fcb8e]*/ -{ - return stringlib_bytes_join((PyObject*)self, iterable_of_bytes); + /* Error handling */ + error: + Py_DECREF(seq); + return NULL; } -/*[clinic input] -bytearray.splitlines - - keepends: bool(accept={int}) = False +PyDoc_STRVAR(splitlines__doc__, +"B.splitlines(keepends=False) -> 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."); -Return a list of the lines in the bytearray, breaking at line boundaries. +static PyObject* +bytearray_splitlines(PyObject *self, PyObject *args) +{ + int keepends = 0; -Line breaks are not included in the resulting list unless keepends is given and -true. -[clinic start generated code]*/ + if (!PyArg_ParseTuple(args, "|i:splitlines", &keepends)) + return NULL; -static PyObject * -bytearray_splitlines_impl(PyByteArrayObject *self, int keepends) -/*[clinic end generated code: output=4223c94b895f6ad9 input=99a27ad959b9cf6b]*/ -{ return stringlib_splitlines( (PyObject*) self, PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), keepends ); } -/*[clinic input] -@classmethod -bytearray.fromhex - - string: unicode - / - -Create a bytearray object from a string of hexadecimal numbers. - -Spaces between two numbers are accepted. -Example: bytearray.fromhex('B9 01EF') -> bytearray(b'\\xb9\\x01\\xef') -[clinic start generated code]*/ +PyDoc_STRVAR(fromhex_doc, +"bytearray.fromhex(string) -> bytearray\n\ +\n\ +Create a bytearray object from a string of hexadecimal numbers.\n\ +Spaces between two numbers are accepted.\n\ +Example: bytearray.fromhex('B9 01EF') -> bytearray(b'\\xb9\\x01\\xef')."); -static PyObject * -bytearray_fromhex_impl(PyTypeObject *type, PyObject *string) -/*[clinic end generated code: output=8f0f0b6d30fb3ba0 input=f033a16d1fb21f48]*/ +static int +hex_digit_to_int(char c) { - PyObject *result = _PyBytes_FromHex(string, type == &PyByteArray_Type); - if (type != &PyByteArray_Type && result != NULL) { - Py_SETREF(result, _PyObject_CallOneArg((PyObject *)type, result)); + if (Py_ISDIGIT(c)) + return c - '0'; + else { + if (Py_ISUPPER(c)) + c = Py_TOLOWER(c); + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; } - return result; -} - -/*[clinic input] -bytearray.hex - - sep: object = NULL - An optional single character or byte to separate hex bytes. - bytes_per_sep: int = 1 - How many bytes between separators. Positive values count from the - right, negative values count from the left. - -Create a str of hexadecimal numbers from a bytearray object. - -Example: ->>> value = bytearray([0xb9, 0x01, 0xef]) ->>> value.hex() -'b901ef' ->>> value.hex(':') -'b9:01:ef' ->>> value.hex(':', 2) -'b9:01ef' ->>> value.hex(':', -2) -'b901:ef' -[clinic start generated code]*/ - -static PyObject * -bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep) -/*[clinic end generated code: output=29c4e5ef72c565a0 input=814c15830ac8c4b5]*/ -{ - char* argbuf = PyByteArray_AS_STRING(self); - Py_ssize_t arglen = PyByteArray_GET_SIZE(self); - return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep); + return -1; } static PyObject * -_common_reduce(PyByteArrayObject *self, int proto) +bytearray_fromhex(PyObject *cls, PyObject *args) { - PyObject *dict; - _Py_IDENTIFIER(__dict__); + PyObject *newbytes; char *buf; + char *hex; + Py_ssize_t hexlen, byteslen, i, j; + int top, bot; - if (_PyObject_LookupAttrId((PyObject *)self, &PyId___dict__, &dict) < 0) { + if (!PyArg_ParseTuple(args, "s#:fromhex", &hex, &hexlen)) return NULL; - } - if (dict == NULL) { - dict = Py_None; - Py_INCREF(dict); - } - - buf = PyByteArray_AS_STRING(self); - if (proto < 3) { - /* use str based reduction for backwards compatibility with Python 2.x */ - PyObject *latin1; - if (Py_SIZE(self)) - latin1 = PyUnicode_DecodeLatin1(buf, Py_SIZE(self), NULL); - else - latin1 = PyUnicode_FromString(""); - return Py_BuildValue("(O(Ns)N)", Py_TYPE(self), latin1, "latin-1", dict); - } - else { - /* use more efficient byte based reduction */ - if (Py_SIZE(self)) { - return Py_BuildValue("(O(y#)N)", Py_TYPE(self), buf, Py_SIZE(self), dict); - } - else { - return Py_BuildValue("(O()N)", Py_TYPE(self), dict); + byteslen = hexlen/2; /* This overestimates if there are spaces */ + newbytes = PyByteArray_FromStringAndSize(NULL, byteslen); + if (!newbytes) + return NULL; + buf = PyByteArray_AS_STRING(newbytes); + for (i = j = 0; i < hexlen; i += 2) { + /* skip over spaces in the input */ + while (hex[i] == ' ') + i++; + if (i >= hexlen) + break; + top = hex_digit_to_int(hex[i]); + bot = hex_digit_to_int(hex[i+1]); + if (top == -1 || bot == -1) { + PyErr_Format(PyExc_ValueError, + "non-hexadecimal number found in " + "fromhex() arg at position %zd", i); + goto error; } + buf[j++] = (top << 4) + bot; } -} - -/*[clinic input] -bytearray.__reduce__ as bytearray_reduce + if (PyByteArray_Resize(newbytes, j) < 0) + goto error; + return newbytes; -Return state information for pickling. -[clinic start generated code]*/ - -static PyObject * -bytearray_reduce_impl(PyByteArrayObject *self) -/*[clinic end generated code: output=52bf304086464cab input=44b5737ada62dd3f]*/ -{ - return _common_reduce(self, 2); + error: + Py_DECREF(newbytes); + return NULL; } -/*[clinic input] -bytearray.__reduce_ex__ as bytearray_reduce_ex - - proto: int = 0 - / - -Return state information for pickling. -[clinic start generated code]*/ +PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyObject * -bytearray_reduce_ex_impl(PyByteArrayObject *self, int proto) -/*[clinic end generated code: output=52eac33377197520 input=f129bc1a1aa151ee]*/ -{ - return _common_reduce(self, proto); -} +bytearray_reduce(PyByteArrayObject *self) +{ + PyObject *latin1, *dict; + if (self->ob_bytes) +#ifdef Py_USING_UNICODE + latin1 = PyUnicode_DecodeLatin1(self->ob_bytes, + Py_SIZE(self), NULL); +#else + latin1 = PyString_FromStringAndSize(self->ob_bytes, Py_SIZE(self)); +#endif + else +#ifdef Py_USING_UNICODE + latin1 = PyUnicode_FromString(""); +#else + latin1 = PyString_FromString(""); +#endif -/*[clinic input] -bytearray.__sizeof__ as bytearray_sizeof + dict = PyObject_GetAttrString((PyObject *)self, "__dict__"); + if (dict == NULL) { + PyErr_Clear(); + dict = Py_None; + Py_INCREF(dict); + } -Returns the size of the bytearray object in memory, in bytes. -[clinic start generated code]*/ + return Py_BuildValue("(O(Ns)N)", Py_TYPE(self), latin1, "latin-1", dict); +} +PyDoc_STRVAR(sizeof_doc, +"B.__sizeof__() -> int\n\ + \n\ +Returns the size of B in memory, in bytes"); static PyObject * -bytearray_sizeof_impl(PyByteArrayObject *self) -/*[clinic end generated code: output=738abdd17951c427 input=e27320fd98a4bc5a]*/ +bytearray_sizeof(PyByteArrayObject *self) { Py_ssize_t res; res = _PyObject_SIZE(Py_TYPE(self)) + self->ob_alloc * sizeof(char); - return PyLong_FromSsize_t(res); + return PyInt_FromSsize_t(res); } static PySequenceMethods bytearray_as_sequence = { @@ -2153,6 +2817,10 @@ static PyMappingMethods bytearray_as_mapping = { }; static PyBufferProcs bytearray_as_buffer = { + (readbufferproc)bytearray_buffer_getreadbuf, + (writebufferproc)bytearray_buffer_getwritebuf, + (segcountproc)bytearray_buffer_getsegcount, + (charbufferproc)bytearray_buffer_getcharbuf, (getbufferproc)bytearray_getbuffer, (releasebufferproc)bytearray_releasebuffer, }; @@ -2160,102 +2828,83 @@ static PyBufferProcs bytearray_as_buffer = { static PyMethodDef bytearray_methods[] = { {"__alloc__", (PyCFunction)bytearray_alloc, METH_NOARGS, alloc_doc}, - BYTEARRAY_REDUCE_METHODDEF - BYTEARRAY_REDUCE_EX_METHODDEF - BYTEARRAY_SIZEOF_METHODDEF - BYTEARRAY_APPEND_METHODDEF - {"capitalize", stringlib_capitalize, METH_NOARGS, + {"__reduce__", (PyCFunction)bytearray_reduce, METH_NOARGS, reduce_doc}, + {"__sizeof__", (PyCFunction)bytearray_sizeof, METH_NOARGS, sizeof_doc}, + {"append", (PyCFunction)bytearray_append, METH_O, append__doc__}, + {"capitalize", (PyCFunction)stringlib_capitalize, METH_NOARGS, _Py_capitalize__doc__}, - STRINGLIB_CENTER_METHODDEF - BYTEARRAY_CLEAR_METHODDEF - BYTEARRAY_COPY_METHODDEF - {"count", (PyCFunction)bytearray_count, METH_VARARGS, - _Py_count__doc__}, - BYTEARRAY_DECODE_METHODDEF - {"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS, - _Py_endswith__doc__}, - STRINGLIB_EXPANDTABS_METHODDEF - BYTEARRAY_EXTEND_METHODDEF - {"find", (PyCFunction)bytearray_find, METH_VARARGS, - _Py_find__doc__}, - BYTEARRAY_FROMHEX_METHODDEF - BYTEARRAY_HEX_METHODDEF - {"index", (PyCFunction)bytearray_index, METH_VARARGS, _Py_index__doc__}, - BYTEARRAY_INSERT_METHODDEF - {"isalnum", stringlib_isalnum, METH_NOARGS, + {"center", (PyCFunction)stringlib_center, METH_VARARGS, center__doc__}, + {"count", (PyCFunction)bytearray_count, METH_VARARGS, count__doc__}, + {"decode", (PyCFunction)bytearray_decode, METH_VARARGS | METH_KEYWORDS, decode_doc}, + {"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS, endswith__doc__}, + {"expandtabs", (PyCFunction)stringlib_expandtabs, METH_VARARGS, + expandtabs__doc__}, + {"extend", (PyCFunction)bytearray_extend, METH_O, extend__doc__}, + {"find", (PyCFunction)bytearray_find, METH_VARARGS, find__doc__}, + {"fromhex", (PyCFunction)bytearray_fromhex, METH_VARARGS|METH_CLASS, + fromhex_doc}, + {"index", (PyCFunction)bytearray_index, METH_VARARGS, index__doc__}, + {"insert", (PyCFunction)bytearray_insert, METH_VARARGS, insert__doc__}, + {"isalnum", (PyCFunction)stringlib_isalnum, METH_NOARGS, _Py_isalnum__doc__}, - {"isalpha", stringlib_isalpha, METH_NOARGS, + {"isalpha", (PyCFunction)stringlib_isalpha, METH_NOARGS, _Py_isalpha__doc__}, - {"isascii", stringlib_isascii, METH_NOARGS, - _Py_isascii__doc__}, - {"isdigit", stringlib_isdigit, METH_NOARGS, + {"isdigit", (PyCFunction)stringlib_isdigit, METH_NOARGS, _Py_isdigit__doc__}, - {"islower", stringlib_islower, METH_NOARGS, + {"islower", (PyCFunction)stringlib_islower, METH_NOARGS, _Py_islower__doc__}, - {"isspace", stringlib_isspace, METH_NOARGS, + {"isspace", (PyCFunction)stringlib_isspace, METH_NOARGS, _Py_isspace__doc__}, - {"istitle", stringlib_istitle, METH_NOARGS, + {"istitle", (PyCFunction)stringlib_istitle, METH_NOARGS, _Py_istitle__doc__}, - {"isupper", stringlib_isupper, METH_NOARGS, + {"isupper", (PyCFunction)stringlib_isupper, METH_NOARGS, _Py_isupper__doc__}, - BYTEARRAY_JOIN_METHODDEF - STRINGLIB_LJUST_METHODDEF - {"lower", stringlib_lower, METH_NOARGS, _Py_lower__doc__}, - BYTEARRAY_LSTRIP_METHODDEF - BYTEARRAY_MAKETRANS_METHODDEF - BYTEARRAY_PARTITION_METHODDEF - BYTEARRAY_POP_METHODDEF - BYTEARRAY_REMOVE_METHODDEF - BYTEARRAY_REPLACE_METHODDEF - BYTEARRAY_REVERSE_METHODDEF - {"rfind", (PyCFunction)bytearray_rfind, METH_VARARGS, _Py_rfind__doc__}, - {"rindex", (PyCFunction)bytearray_rindex, METH_VARARGS, _Py_rindex__doc__}, - STRINGLIB_RJUST_METHODDEF - BYTEARRAY_RPARTITION_METHODDEF - BYTEARRAY_RSPLIT_METHODDEF - BYTEARRAY_RSTRIP_METHODDEF - BYTEARRAY_SPLIT_METHODDEF - BYTEARRAY_SPLITLINES_METHODDEF + {"join", (PyCFunction)bytearray_join, METH_O, join_doc}, + {"ljust", (PyCFunction)stringlib_ljust, METH_VARARGS, ljust__doc__}, + {"lower", (PyCFunction)stringlib_lower, METH_NOARGS, _Py_lower__doc__}, + {"lstrip", (PyCFunction)bytearray_lstrip, METH_VARARGS, lstrip__doc__}, + {"partition", (PyCFunction)bytearray_partition, METH_O, partition__doc__}, + {"pop", (PyCFunction)bytearray_pop, METH_VARARGS, pop__doc__}, + {"remove", (PyCFunction)bytearray_remove, METH_O, remove__doc__}, + {"replace", (PyCFunction)bytearray_replace, METH_VARARGS, replace__doc__}, + {"reverse", (PyCFunction)bytearray_reverse, METH_NOARGS, reverse__doc__}, + {"rfind", (PyCFunction)bytearray_rfind, METH_VARARGS, rfind__doc__}, + {"rindex", (PyCFunction)bytearray_rindex, METH_VARARGS, rindex__doc__}, + {"rjust", (PyCFunction)stringlib_rjust, METH_VARARGS, rjust__doc__}, + {"rpartition", (PyCFunction)bytearray_rpartition, METH_O, rpartition__doc__}, + {"rsplit", (PyCFunction)bytearray_rsplit, METH_VARARGS, rsplit__doc__}, + {"rstrip", (PyCFunction)bytearray_rstrip, METH_VARARGS, rstrip__doc__}, + {"split", (PyCFunction)bytearray_split, METH_VARARGS, split__doc__}, + {"splitlines", (PyCFunction)bytearray_splitlines, METH_VARARGS, + splitlines__doc__}, {"startswith", (PyCFunction)bytearray_startswith, METH_VARARGS , - _Py_startswith__doc__}, - BYTEARRAY_STRIP_METHODDEF - {"swapcase", stringlib_swapcase, METH_NOARGS, + startswith__doc__}, + {"strip", (PyCFunction)bytearray_strip, METH_VARARGS, strip__doc__}, + {"swapcase", (PyCFunction)stringlib_swapcase, METH_NOARGS, _Py_swapcase__doc__}, - {"title", stringlib_title, METH_NOARGS, _Py_title__doc__}, - BYTEARRAY_TRANSLATE_METHODDEF - {"upper", stringlib_upper, METH_NOARGS, _Py_upper__doc__}, - STRINGLIB_ZFILL_METHODDEF + {"title", (PyCFunction)stringlib_title, METH_NOARGS, _Py_title__doc__}, + {"translate", (PyCFunction)bytearray_translate, METH_VARARGS, + translate__doc__}, + {"upper", (PyCFunction)stringlib_upper, METH_NOARGS, _Py_upper__doc__}, + {"zfill", (PyCFunction)stringlib_zfill, METH_VARARGS, zfill__doc__}, {NULL} }; -static PyObject * -bytearray_mod(PyObject *v, PyObject *w) -{ - if (!PyByteArray_Check(v)) - Py_RETURN_NOTIMPLEMENTED; - return _PyBytes_FormatEx(PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1); -} - -static PyNumberMethods bytearray_as_number = { - 0, /*nb_add*/ - 0, /*nb_subtract*/ - 0, /*nb_multiply*/ - bytearray_mod, /*nb_remainder*/ -}; - PyDoc_STRVAR(bytearray_doc, -"bytearray(iterable_of_ints) -> bytearray\n\ -bytearray(string, encoding[, errors]) -> bytearray\n\ -bytearray(bytes_or_buffer) -> mutable copy of bytes_or_buffer\n\ -bytearray(int) -> bytes array of size given by the parameter initialized with null bytes\n\ -bytearray() -> empty bytes array\n\ +"bytearray(iterable_of_ints) -> bytearray.\n\ +bytearray(string, encoding[, errors]) -> bytearray.\n\ +bytearray(bytes_or_bytearray) -> mutable copy of bytes_or_bytearray.\n\ +bytearray(memory_view) -> bytearray.\n\ \n\ Construct a mutable bytearray object from:\n\ - an iterable yielding integers in range(256)\n\ - a text string encoded using the specified encoding\n\ - - a bytes or a buffer object\n\ + - a bytes or a bytearray object\n\ - any object implementing the buffer API.\n\ - - an integer"); +\n\ +bytearray(int) -> bytearray.\n\ +\n\ +Construct a zero-initialized bytearray of the given length."); static PyObject *bytearray_iter(PyObject *seq); @@ -2266,12 +2915,12 @@ PyTypeObject PyByteArray_Type = { sizeof(PyByteArrayObject), 0, (destructor)bytearray_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_compare */ (reprfunc)bytearray_repr, /* tp_repr */ - &bytearray_as_number, /* tp_as_number */ + 0, /* tp_as_number */ &bytearray_as_sequence, /* tp_as_sequence */ &bytearray_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ @@ -2280,7 +2929,8 @@ PyTypeObject PyByteArray_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &bytearray_as_buffer, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */ bytearray_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -2338,8 +2988,8 @@ bytearrayiter_next(bytesiterobject *it) assert(PyByteArray_Check(seq)); if (it->it_index < PyByteArray_GET_SIZE(seq)) { - item = PyLong_FromLong( - (unsigned char)PyByteArray_AS_STRING(seq)[it->it_index]); + item = PyInt_FromLong( + (unsigned char)seq->ob_bytes[it->it_index]); if (item != NULL) ++it->it_index; return item; @@ -2351,58 +3001,20 @@ bytearrayiter_next(bytesiterobject *it) } static PyObject * -bytearrayiter_length_hint(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) +bytesarrayiter_length_hint(bytesiterobject *it) { Py_ssize_t len = 0; - if (it->it_seq) { + if (it->it_seq) len = PyByteArray_GET_SIZE(it->it_seq) - it->it_index; - if (len < 0) { - len = 0; - } - } - return PyLong_FromSsize_t(len); + return PyInt_FromSsize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); -static PyObject * -bytearrayiter_reduce(bytesiterobject *it, PyObject *Py_UNUSED(ignored)) -{ - _Py_IDENTIFIER(iter); - if (it->it_seq != NULL) { - return Py_BuildValue("N(O)n", _PyEval_GetBuiltinId(&PyId_iter), - it->it_seq, it->it_index); - } else { - return Py_BuildValue("N(())", _PyEval_GetBuiltinId(&PyId_iter)); - } -} - -static PyObject * -bytearrayiter_setstate(bytesiterobject *it, PyObject *state) -{ - Py_ssize_t index = PyLong_AsSsize_t(state); - if (index == -1 && PyErr_Occurred()) - return NULL; - if (it->it_seq != NULL) { - if (index < 0) - index = 0; - else if (index > PyByteArray_GET_SIZE(it->it_seq)) - index = PyByteArray_GET_SIZE(it->it_seq); /* iterator exhausted */ - it->it_index = index; - } - Py_RETURN_NONE; -} - -PyDoc_STRVAR(setstate_doc, "Set state information for unpickling."); - static PyMethodDef bytearrayiter_methods[] = { - {"__length_hint__", (PyCFunction)bytearrayiter_length_hint, METH_NOARGS, + {"__length_hint__", (PyCFunction)bytesarrayiter_length_hint, METH_NOARGS, length_hint_doc}, - {"__reduce__", (PyCFunction)bytearrayiter_reduce, METH_NOARGS, - bytearray_reduce__doc__}, - {"__setstate__", (PyCFunction)bytearrayiter_setstate, METH_O, - setstate_doc}, {NULL, NULL} /* sentinel */ }; @@ -2413,10 +3025,10 @@ PyTypeObject PyByteArrayIter_Type = { 0, /* tp_itemsize */ /* methods */ (destructor)bytearrayiter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ + 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ |