/* Bytes object implementation */ /* XXX TO DO: optimizations */ #define PY_SSIZE_T_CLEAN #include "Python.h" /* Direct API functions */ PyObject * PyBytes_FromStringAndSize(const char *sval, Py_ssize_t size) { PyBytesObject *new; if (size != 0) { assert(sval != NULL); assert(size > 0); } new = PyObject_New(PyBytesObject, &PyBytes_Type); if (new == NULL) return NULL; if (size > 0) { new->ob_sval = PyMem_Malloc(size); if (new->ob_sval == NULL) { Py_DECREF(new); return NULL; } memcpy(new->ob_sval, sval, size); new->ob_size = size; } return (PyObject *)new; } Py_ssize_t PyBytes_Size(PyObject *self) { assert(self != NULL); assert(PyBytes_Check(self)); return ((PyBytesObject *)self)->ob_size; } char * PyBytes_AsString(PyObject *self) { assert(self != NULL); assert(PyBytes_Check(self)); return ((PyBytesObject *)self)->ob_sval; } int PyBytes_Resize(PyObject *self, Py_ssize_t size) { void *sval; assert(self != NULL); assert(PyBytes_Check(self)); assert(size >= 0); sval = PyMem_Realloc(((PyBytesObject *)self)->ob_sval, size); if (sval == NULL) { PyErr_NoMemory(); return -1; } ((PyBytesObject *)self)->ob_sval = sval; ((PyBytesObject *)self)->ob_size = size; return 0; } /* Functions stuffed into the type object */ static Py_ssize_t bytes_length(PyBytesObject *self) { return self->ob_size; } static PyObject * bytes_getitem(PyBytesObject *self, Py_ssize_t i) { if (i < 0) i += self->ob_size; if (i < 0 || i >= self->ob_size) { PyErr_SetString(PyExc_IndexError, "bytes index out of range"); return NULL; } return PyInt_FromLong((unsigned char)(self->ob_sval[i])); } static long bytes_nohash(PyObject *self) { PyErr_SetString(PyExc_TypeError, "bytes objects are unhashable"); return -1; } static int bytes_init(PyBytesObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = {"sequence", 0}; PyObject *arg = NULL; PyObject *it; /* iter(arg) */ PyObject *(*iternext)(PyObject *); if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:bytes", kwlist, &arg)) return -1; /* Verify list invariants established by PyType_GenericAlloc() */ if (self->ob_size != 0) { assert(self->ob_sval != NULL); assert(self->ob_size > 0); } /* Empty previous contents */ if (PyBytes_Resize((PyObject *)self, 0) < 0) return -1; /* Quick check if we're done */ if (arg == 0) return 0; /* XXX Optimize this if the arguments is a list, tuple, or bytes */ /* Get the iterator */ it = PyObject_GetIter(arg); if (it == NULL) return -1; iternext = *it->ob_type->tp_iternext; /* Run the iterator to exhaustion */ for (;;) { PyObject *item; Py_ssize_t value; /* Get the next item */ item = iternext(it); if (item == NULL) { if (PyErr_Occurred()) { if (!PyErr_ExceptionMatches(PyExc_StopIteration)) goto error; PyErr_Clear(); } break; } /* Interpret it as an int (__index__) */ value = PyNumber_Index(item); Py_DECREF(item); if (value == -1 && PyErr_Occurred()) goto error; /* Range check */ if (value < 0 || value >= 256) { PyErr_SetString(PyExc_ValueError, "bytes must be in range(0, 256)"); goto error; } /* Append the byte */ /* XXX Speed this up */ if (PyBytes_Resize((PyObject *)self, self->ob_size+1) < 0) goto error; self->ob_sval[self->ob_size-1] = value; } /* Clean up and return success */ Py_DECREF(it); return 0; error: /* Error handling when it != NULL */ Py_DECREF(it); return -1; } static PyObject * bytes_repr(PyBytesObject *self) { PyObject *list; PyObject *str; PyObject *result; int err; int i; if (self->ob_size == 0) return PyString_FromString("bytes()"); list = PyList_New(0); if (list == NULL) return NULL; str = PyString_FromString("bytes(["); if (str == NULL) goto error; err = PyList_Append(list, str); Py_DECREF(str); if (err < 0) goto error; for (i = 0; i < self->ob_size; i++) { char buffer[20]; sprintf(buffer, ", 0x%02x", (unsigned char) (self->ob_sval[i])); str = PyString_FromString((i == 0) ? buffer+2 : buffer); if (str == NULL) goto error; err = PyList_Append(list, str); Py_DECREF(str); if (err < 0) goto error; } str = PyString_FromString("])"); if (str == NULL) goto error; err = PyList_Append(list, str); Py_DECREF(str); if (err < 0) goto error; str = PyString_FromString(""); if (str == NULL) goto error; result = _PyString_Join(str, list); Py_DECREF(str); Py_DECREF(list); return result; error: /* Error handling when list != NULL */ Py_DECREF(list); return NULL; } static PyObject * bytes_richcompare(PyBytesObject *self, PyBytesObject *other, int op) { PyObject *res; int minsize; int cmp; if (!PyBytes_Check(self) || !PyBytes_Check(other)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (self->ob_size != other->ob_size && (op == Py_EQ || op == Py_NE)) { /* Shortcut: if the lengths differ, the objects differ */ cmp = (op == Py_NE); } else { minsize = self->ob_size; if (other->ob_size < minsize) minsize = other->ob_size; cmp = memcmp(self->ob_sval, other->ob_sval, minsize); /* In ISO C, memcmp() guarantees to use unsigned bytes! */ if (cmp == 0) { if (self->ob_size < other->ob_size) cmp = -1; else if (self->ob_size > other->ob_size) cmp = 1; } 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; Py_INCREF(res); return res; } static void bytes_dealloc(PyBytesObject *self) { if (self->ob_sval != 0) { PyMem_Free(self->ob_sval); } self->ob_type->tp_free((PyObject *)self); } static PySequenceMethods bytes_as_sequence = { (lenfunc)bytes_length, /*sq_length*/ (binaryfunc)0, /*sq_concat*/ (ssizeargfunc)0, /*sq_repeat*/ (ssizeargfunc)bytes_getitem, /*sq_item*/ (ssizessizeargfunc)0, /*sq_slice*/ 0, /*sq_ass_item*/ 0, /*sq_ass_slice*/ (objobjproc)0, /*sq_contains*/ }; static PyMappingMethods bytes_as_mapping = { (lenfunc)bytes_length, (binaryfunc)0, 0, }; static PyBufferProcs bytes_as_buffer = { /* (readbufferproc)bytes_buffer_getreadbuf, (writebufferproc)bytes_buffer_getwritebuf, (segcountproc)bytes_buffer_getsegcount, (charbufferproc)bytes_buffer_getcharbuf, */ }; static PyMethodDef bytes_methods[] = { {NULL, NULL} }; PyDoc_STRVAR(bytes_doc, "bytes([iterable]) -> new array of bytes.\n\ \n\ If an argument is given it must be an iterable yielding ints in range(256)."); PyTypeObject PyBytes_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "bytes", sizeof(PyBytesObject), 0, (destructor)bytes_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ (reprfunc)bytes_repr, /* tp_repr */ 0, /* tp_as_number */ &bytes_as_sequence, /* tp_as_sequence */ &bytes_as_mapping, /* tp_as_mapping */ bytes_nohash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ &bytes_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ /* bytes is 'final' or 'sealed' */ bytes_doc, /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ (richcmpfunc)bytes_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ bytes_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)bytes_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ PyObject_Del, /* tp_free */ };