Commit faf1de5e authored by Gregory P. Smith's avatar Gregory P. Smith

For PEP3137: Adds missing methods to the mutable PyBytes object (soon

to be called a buffer).  Shares code with stringobject when possible.
Adds unit tests with common code that should be usable to test the PEPs
mutable buffer() and immutable bytes() types.

 http://bugs.python.org/issue1261
parent eecded1b
#ifndef Py_BYTES_CTYPE_H
#define Py_BYTES_CTYPE_H
/*
* The internal implementation behind PyString (bytes) and PyBytes (buffer)
* methods of the given names, they operate on ASCII byte strings.
*/
extern PyObject* _Py_bytes_isspace(const char *cptr, Py_ssize_t len);
extern PyObject* _Py_bytes_isalpha(const char *cptr, Py_ssize_t len);
extern PyObject* _Py_bytes_isalnum(const char *cptr, Py_ssize_t len);
extern PyObject* _Py_bytes_isdigit(const char *cptr, Py_ssize_t len);
extern PyObject* _Py_bytes_islower(const char *cptr, Py_ssize_t len);
extern PyObject* _Py_bytes_isupper(const char *cptr, Py_ssize_t len);
extern PyObject* _Py_bytes_istitle(const char *cptr, Py_ssize_t len);
/* These store their len sized answer in the given preallocated *result arg. */
extern void _Py_bytes_lower(char *result, const char *cptr, Py_ssize_t len);
extern void _Py_bytes_upper(char *result, const char *cptr, Py_ssize_t len);
extern void _Py_bytes_title(char *result, char *s, Py_ssize_t len);
extern void _Py_bytes_capitalize(char *result, char *s, Py_ssize_t len);
extern void _Py_bytes_swapcase(char *result, char *s, Py_ssize_t len);
/* Shared __doc__ strings. */
extern const char _Py_isspace__doc__[];
extern const char _Py_isalpha__doc__[];
extern const char _Py_isalnum__doc__[];
extern const char _Py_isdigit__doc__[];
extern const char _Py_islower__doc__[];
extern const char _Py_isupper__doc__[];
extern const char _Py_istitle__doc__[];
extern const char _Py_lower__doc__[];
extern const char _Py_upper__doc__[];
extern const char _Py_title__doc__[];
extern const char _Py_capitalize__doc__[];
extern const char _Py_swapcase__doc__[];
#define FLAG_LOWER 0x01
#define FLAG_UPPER 0x02
#define FLAG_ALPHA (FLAG_LOWER|FLAG_UPPER)
#define FLAG_DIGIT 0x04
#define FLAG_ALNUM (FLAG_ALPHA|FLAG_DIGIT)
#define FLAG_SPACE 0x08
#define FLAG_XDIGIT 0x10
extern const unsigned int _Py_ctype_table[256];
#define ISLOWER(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_LOWER)
#define ISUPPER(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_UPPER)
#define ISALPHA(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_ALPHA)
#define ISDIGIT(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_DIGIT)
#define ISXDIGIT(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_XDIGIT)
#define ISALNUM(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_ALNUM)
#define ISSPACE(c) (_Py_ctype_table[Py_CHARMASK(c)] & FLAG_SPACE)
#undef islower
#define islower(c) undefined_islower(c)
#undef isupper
#define isupper(c) undefined_isupper(c)
#undef isalpha
#define isalpha(c) undefined_isalpha(c)
#undef isdigit
#define isdigit(c) undefined_isdigit(c)
#undef isxdigit
#define isxdigit(c) undefined_isxdigit(c)
#undef isalnum
#define isalnum(c) undefined_isalnum(c)
#undef isspace
#define isspace(c) undefined_isspace(c)
extern const unsigned char _Py_ctype_tolower[256];
extern const unsigned char _Py_ctype_toupper[256];
#define TOLOWER(c) (_Py_ctype_tolower[Py_CHARMASK(c)])
#define TOUPPER(c) (_Py_ctype_toupper[Py_CHARMASK(c)])
#undef tolower
#define tolower(c) undefined_tolower(c)
#undef toupper
#define toupper(c) undefined_toupper(c)
/* this is needed because some docs are shared from the .o, not static */
#define PyDoc_STRVAR_shared(name,str) const char name[] = PyDoc_STR(str)
#endif /* !Py_BYTES_CTYPE_H */
This diff is collapsed.
...@@ -8,6 +8,7 @@ import tempfile ...@@ -8,6 +8,7 @@ import tempfile
import unittest import unittest
import test.test_support import test.test_support
import test.string_tests import test.string_tests
import test.buffer_tests
class BytesTest(unittest.TestCase): class BytesTest(unittest.TestCase):
...@@ -454,17 +455,18 @@ class BytesTest(unittest.TestCase): ...@@ -454,17 +455,18 @@ class BytesTest(unittest.TestCase):
def test_fromhex(self): def test_fromhex(self):
self.assertRaises(TypeError, bytes.fromhex) self.assertRaises(TypeError, bytes.fromhex)
self.assertRaises(TypeError, bytes.fromhex, 1) self.assertRaises(TypeError, bytes.fromhex, 1)
self.assertEquals(bytes.fromhex(''), bytes()) self.assertEquals(bytes.fromhex(b''), bytes())
b = bytes([0x1a, 0x2b, 0x30]) b = bytes([0x1a, 0x2b, 0x30])
self.assertEquals(bytes.fromhex('1a2B30'), b) self.assertEquals(bytes.fromhex(b'1a2B30'), b)
self.assertEquals(bytes.fromhex(' 1A 2B 30 '), b) self.assertEquals(bytes.fromhex(b' 1A 2B 30 '), b)
self.assertEquals(bytes.fromhex(memoryview(b'')), bytes()) self.assertEquals(bytes.fromhex(memoryview(b'')), bytes())
self.assertEquals(bytes.fromhex(memoryview(b'0000')), bytes([0, 0])) self.assertEquals(bytes.fromhex(memoryview(b'0000')), bytes([0, 0]))
self.assertRaises(ValueError, bytes.fromhex, 'a') self.assertRaises(TypeError, bytes.fromhex, '1B')
self.assertRaises(ValueError, bytes.fromhex, 'rt') self.assertRaises(ValueError, bytes.fromhex, b'a')
self.assertRaises(ValueError, bytes.fromhex, '1a b cd') self.assertRaises(ValueError, bytes.fromhex, b'rt')
self.assertRaises(ValueError, bytes.fromhex, '\x00') self.assertRaises(ValueError, bytes.fromhex, b'1a b cd')
self.assertRaises(ValueError, bytes.fromhex, '12 \x00 34') self.assertRaises(ValueError, bytes.fromhex, b'\x00')
self.assertRaises(ValueError, bytes.fromhex, b'12 \x00 34')
def test_join(self): def test_join(self):
self.assertEqual(b"".join([]), bytes()) self.assertEqual(b"".join([]), bytes())
...@@ -504,11 +506,12 @@ class BytesTest(unittest.TestCase): ...@@ -504,11 +506,12 @@ class BytesTest(unittest.TestCase):
self.assertEqual(b, b'heo') self.assertEqual(b, b'heo')
self.assertRaises(ValueError, lambda: b.remove(ord('l'))) self.assertRaises(ValueError, lambda: b.remove(ord('l')))
self.assertRaises(ValueError, lambda: b.remove(400)) self.assertRaises(ValueError, lambda: b.remove(400))
self.assertRaises(ValueError, lambda: b.remove('e')) self.assertRaises(TypeError, lambda: b.remove('e'))
# remove first and last # remove first and last
b.remove(ord('o')) b.remove(ord('o'))
b.remove(ord('h')) b.remove(ord('h'))
self.assertEqual(b, b'e') self.assertEqual(b, b'e')
self.assertRaises(TypeError, lambda: b.remove(b'e'))
def test_pop(self): def test_pop(self):
b = b'world' b = b'world'
...@@ -542,6 +545,7 @@ class BytesTest(unittest.TestCase): ...@@ -542,6 +545,7 @@ class BytesTest(unittest.TestCase):
b = bytes() b = bytes()
b.append(ord('A')) b.append(ord('A'))
self.assertEqual(len(b), 1) self.assertEqual(len(b), 1)
self.assertRaises(TypeError, lambda: b.append(b'o'))
def test_insert(self): def test_insert(self):
b = b'msssspp' b = b'msssspp'
...@@ -550,6 +554,7 @@ class BytesTest(unittest.TestCase): ...@@ -550,6 +554,7 @@ class BytesTest(unittest.TestCase):
b.insert(-2, ord('i')) b.insert(-2, ord('i'))
b.insert(1000, ord('i')) b.insert(1000, ord('i'))
self.assertEqual(b, b'mississippi') self.assertEqual(b, b'mississippi')
self.assertRaises(TypeError, lambda: b.insert(0, b'1'))
def test_startswith(self): def test_startswith(self):
b = b'hello' b = b'hello'
...@@ -734,6 +739,29 @@ class BytesTest(unittest.TestCase): ...@@ -734,6 +739,29 @@ class BytesTest(unittest.TestCase):
# Unfortunately they are all bundled with tests that # Unfortunately they are all bundled with tests that
# are not appropriate for bytes # are not appropriate for bytes
# I've started porting some of those into buffer_tests.py, we should port
# the rest that make sense (the code can be cleaned up to use modern
# unittest methods at the same time).
class BufferPEP3137Test(unittest.TestCase,
test.buffer_tests.MixinBytesBufferCommonTests):
def marshal(self, x):
return bytes(x)
# TODO this should become:
#return buffer(x)
# once the bytes -> buffer and str8 -> bytes rename happens
def test_returns_new_copy(self):
val = self.marshal(b'1234')
# On immutable types these MAY return a reference to themselves
# but on mutable types like buffer they MUST return a new copy.
for methname in ('zfill', 'rjust', 'ljust', 'center'):
method = getattr(val, methname)
newval = method(3)
self.assertEqual(val, newval)
self.assertTrue(val is not newval,
methname+' returned self on a mutable object')
class BytesAsStringTest(test.string_tests.BaseTest): class BytesAsStringTest(test.string_tests.BaseTest):
type2test = bytes type2test = bytes
...@@ -759,7 +787,7 @@ class BytesAsStringTest(test.string_tests.BaseTest): ...@@ -759,7 +787,7 @@ class BytesAsStringTest(test.string_tests.BaseTest):
def test_main(): def test_main():
test.test_support.run_unittest(BytesTest) test.test_support.run_unittest(BytesTest)
test.test_support.run_unittest(BytesAsStringTest) test.test_support.run_unittest(BytesAsStringTest)
test.test_support.run_unittest(BufferPEP3137Test)
if __name__ == "__main__": if __name__ == "__main__":
##test_main() ##test_main()
......
...@@ -285,6 +285,7 @@ PYTHON_OBJS= \ ...@@ -285,6 +285,7 @@ PYTHON_OBJS= \
OBJECT_OBJS= \ OBJECT_OBJS= \
Objects/abstract.o \ Objects/abstract.o \
Objects/boolobject.o \ Objects/boolobject.o \
Objects/bytes_methods.o \
Objects/bytesobject.o \ Objects/bytesobject.o \
Objects/cellobject.o \ Objects/cellobject.o \
Objects/classobject.o \ Objects/classobject.o \
...@@ -507,6 +508,18 @@ Python/importdl.o: $(srcdir)/Python/importdl.c ...@@ -507,6 +508,18 @@ Python/importdl.o: $(srcdir)/Python/importdl.c
Objects/unicodectype.o: $(srcdir)/Objects/unicodectype.c \ Objects/unicodectype.o: $(srcdir)/Objects/unicodectype.c \
$(srcdir)/Objects/unicodetype_db.h $(srcdir)/Objects/unicodetype_db.h
BYTESTR_DEPS = Include/bytes_methods.h \
$(srcdir)/Objects/stringlib/fastsearch.h \
$(srcdir)/Objects/stringlib/count.h \
$(srcdir)/Objects/stringlib/find.h \
$(srcdir)/Objects/stringlib/partition.h \
$(srcdir)/Objects/stringlib/ctype.h \
$(srcdir)/Objects/stringlib/transmogrify.h
Objects/stringobject.o: $(srcdir)/Objects/stringobject.c $(BYTESTR_DEPS)
Objects/bytesobject.o: $(srcdir)/Objects/bytesobject.c $(BYTESTR_DEPS)
Objects/unicodeobject.o: $(srcdir)/Objects/unicodeobject.c \ Objects/unicodeobject.o: $(srcdir)/Objects/unicodeobject.c \
$(srcdir)/Objects/stringlib/string_format.h \ $(srcdir)/Objects/stringlib/string_format.h \
$(srcdir)/Objects/stringlib/unicodedefs.h \ $(srcdir)/Objects/stringlib/unicodedefs.h \
......
This diff is collapsed.
This diff is collapsed.
...@@ -32,3 +32,12 @@ STRINGLIB_CHAR* STRINGLIB_STR(PyObject*) ...@@ -32,3 +32,12 @@ STRINGLIB_CHAR* STRINGLIB_STR(PyObject*)
returns the pointer to the character data for the given string returns the pointer to the character data for the given string
object (which must be of the right type) object (which must be of the right type)
int STRINGLIB_CHECK_EXACT(PyObject *)
returns true if the object is an instance of our type, not a subclass.
STRINGLIB_MUTABLE
Must be 0 or 1 to tell the cpp macros in stringlib code if the object
being operated on is mutable or not.
/* NOTE: this API is -ONLY- for use with single byte character strings. */
/* Do not use it with Unicode. */
#include "bytes_methods.h"
static PyObject*
stringlib_isspace(PyObject *self)
{
return _Py_bytes_isspace(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
static PyObject*
stringlib_isalpha(PyObject *self)
{
return _Py_bytes_isalpha(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
static PyObject*
stringlib_isalnum(PyObject *self)
{
return _Py_bytes_isalnum(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
static PyObject*
stringlib_isdigit(PyObject *self)
{
return _Py_bytes_isdigit(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
static PyObject*
stringlib_islower(PyObject *self)
{
return _Py_bytes_islower(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
static PyObject*
stringlib_isupper(PyObject *self)
{
return _Py_bytes_isupper(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
static PyObject*
stringlib_istitle(PyObject *self)
{
return _Py_bytes_istitle(STRINGLIB_STR(self), STRINGLIB_LEN(self));
}
/* functions that return a new object partially translated by ctype funcs: */
static PyObject*
stringlib_lower(PyObject *self)
{
PyObject* newobj;
newobj = STRINGLIB_NEW(NULL, STRINGLIB_LEN(self));
if (!newobj)
return NULL;
_Py_bytes_lower(STRINGLIB_STR(newobj), STRINGLIB_STR(self),
STRINGLIB_LEN(self));
return newobj;
}
static PyObject*
stringlib_upper(PyObject *self)
{
PyObject* newobj;
newobj = STRINGLIB_NEW(NULL, STRINGLIB_LEN(self));
if (!newobj)
return NULL;
_Py_bytes_upper(STRINGLIB_STR(newobj), STRINGLIB_STR(self),
STRINGLIB_LEN(self));
return newobj;
}
static PyObject*
stringlib_title(PyObject *self)
{
PyObject* newobj;
newobj = STRINGLIB_NEW(NULL, STRINGLIB_LEN(self));
if (!newobj)
return NULL;
_Py_bytes_title(STRINGLIB_STR(newobj), STRINGLIB_STR(self),
STRINGLIB_LEN(self));
return newobj;
}
static PyObject*
stringlib_capitalize(PyObject *self)
{
PyObject* newobj;
newobj = STRINGLIB_NEW(NULL, STRINGLIB_LEN(self));
if (!newobj)
return NULL;
_Py_bytes_capitalize(STRINGLIB_STR(newobj), STRINGLIB_STR(self),
STRINGLIB_LEN(self));
return newobj;
}
static PyObject*
stringlib_swapcase(PyObject *self)
{
PyObject* newobj;
newobj = STRINGLIB_NEW(NULL, STRINGLIB_LEN(self));
if (!newobj)
return NULL;
_Py_bytes_swapcase(STRINGLIB_STR(newobj), STRINGLIB_STR(self),
STRINGLIB_LEN(self));
return newobj;
}
...@@ -90,7 +90,7 @@ stringlib_rfind_slice(const STRINGLIB_CHAR* str, Py_ssize_t str_len, ...@@ -90,7 +90,7 @@ stringlib_rfind_slice(const STRINGLIB_CHAR* str, Py_ssize_t str_len,
return stringlib_rfind(str + start, end - start, sub, sub_len, start); return stringlib_rfind(str + start, end - start, sub, sub_len, start);
} }
#ifdef STRINGLIB_STR #ifdef STRINGLIB_WANT_CONTAINS_OBJ
Py_LOCAL_INLINE(int) Py_LOCAL_INLINE(int)
stringlib_contains_obj(PyObject* str, PyObject* sub) stringlib_contains_obj(PyObject* str, PyObject* sub)
......
/* NOTE: this API is -ONLY- for use with single byte character strings. */
/* Do not use it with Unicode. */
#include "bytes_methods.h"
#ifndef STRINGLIB_MUTABLE
#warning "STRINGLIB_MUTABLE not defined before #include, assuming 0"
#define STRINGLIB_MUTABLE 0
#endif
/* the more complicated methods. parts of these should be pulled out into the
shared code in bytes_methods.c to cut down on duplicate code bloat. */
PyDoc_STRVAR(expandtabs__doc__,
"B.expandtabs([tabsize]) -> modified copy of B\n\
\n\
Return a copy of B where all tab characters are expanded using spaces.\n\
If tabsize is not given, a tab size of 8 characters is assumed.");
static PyObject*
stringlib_expandtabs(PyObject *self, PyObject *args)
{
const char *e, *p;
char *q;
Py_ssize_t i, j, old_j;
PyObject *u;
int tabsize = 8;
if (!PyArg_ParseTuple(args, "|i:expandtabs", &tabsize))
return NULL;
/* First pass: determine size of output string */
i = j = old_j = 0;
e = STRINGLIB_STR(self) + STRINGLIB_LEN(self);
for (p = STRINGLIB_STR(self); p < e; p++)
if (*p == '\t') {
if (tabsize > 0) {
j += tabsize - (j % tabsize);
/* XXX: this depends on a signed integer overflow to < 0 */
/* C compilers, including gcc, do -NOT- guarantee this. */
if (old_j > j) {
PyErr_SetString(PyExc_OverflowError,
"result is too long");
return NULL;
}
old_j = j;
}
}
else {
j++;
if (*p == '\n' || *p == '\r') {
i += j;
old_j = j = 0;
/* XXX: this depends on a signed integer overflow to < 0 */
/* C compilers, including gcc, do -NOT- guarantee this. */
if (i < 0) {
PyErr_SetString(PyExc_OverflowError,
"result is too long");
return NULL;
}
}
}
if ((i + j) < 0) {
/* XXX: this depends on a signed integer overflow to < 0 */
/* C compilers, including gcc, do -NOT- guarantee this. */
PyErr_SetString(PyExc_OverflowError, "result is too long");
return NULL;
}
/* Second pass: create output string and fill it */
u = STRINGLIB_NEW(NULL, i + j);
if (!u)
return NULL;
j = 0;
q = STRINGLIB_STR(u);
for (p = STRINGLIB_STR(self); p < e; p++)
if (*p == '\t') {
if (tabsize > 0) {
i = tabsize - (j % tabsize);
j += i;
while (i--)
*q++ = ' ';
}
}
else {
j++;
*q++ = *p;
if (*p == '\n' || *p == '\r')
j = 0;
}
return u;
}
Py_LOCAL_INLINE(PyObject *)
pad(PyObject *self, Py_ssize_t left, Py_ssize_t right, char fill)
{
PyObject *u;
if (left < 0)
left = 0;
if (right < 0)
right = 0;
if (left == 0 && right == 0 && STRINGLIB_CHECK_EXACT(self)) {
#if STRINGLIB_MUTABLE
/* We're defined as returning a copy; If the object is mutable
* that means we must make an identical copy. */
return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self));
#else
Py_INCREF(self);
return (PyObject *)self;
#endif /* STRINGLIB_MUTABLE */
}
u = STRINGLIB_NEW(NULL,
left + STRINGLIB_LEN(self) + right);
if (u) {
if (left)
memset(STRINGLIB_STR(u), fill, left);
Py_MEMCPY(STRINGLIB_STR(u) + left,
STRINGLIB_STR(self),
STRINGLIB_LEN(self));
if (right)
memset(STRINGLIB_STR(u) + left + STRINGLIB_LEN(self),
fill, right);
}
return u;
}
PyDoc_STRVAR(ljust__doc__,
"B.ljust(width[, fillchar]) -> modified copy of B\n"
"\n"
"Return B left justified in a string of length width. Padding is\n"
"done using the specified fill character (default is a space).");
static PyObject *
stringlib_ljust(PyObject *self, PyObject *args)
{
Py_ssize_t width;
char fillchar = ' ';
if (!PyArg_ParseTuple(args, "n|c:ljust", &width, &fillchar))
return NULL;
if (STRINGLIB_LEN(self) >= width && STRINGLIB_CHECK_EXACT(self)) {
#if STRINGLIB_MUTABLE
/* We're defined as returning a copy; If the object is mutable
* that means we must make an identical copy. */
return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self));
#else
Py_INCREF(self);
return (PyObject*) self;
#endif
}
return pad(self, 0, width - STRINGLIB_LEN(self), fillchar);
}
PyDoc_STRVAR(rjust__doc__,
"B.rjust(width[, fillchar]) -> modified copy of B\n"
"\n"
"Return B right justified in a string of length width. Padding is\n"
"done using the specified fill character (default is a space)");
static PyObject *
stringlib_rjust(PyObject *self, PyObject *args)
{
Py_ssize_t width;
char fillchar = ' ';
if (!PyArg_ParseTuple(args, "n|c:rjust", &width, &fillchar))
return NULL;
if (STRINGLIB_LEN(self) >= width && STRINGLIB_CHECK_EXACT(self)) {
#if STRINGLIB_MUTABLE
/* We're defined as returning a copy; If the object is mutable
* that means we must make an identical copy. */
return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self));
#else
Py_INCREF(self);
return (PyObject*) self;
#endif
}
return pad(self, width - STRINGLIB_LEN(self), 0, fillchar);
}
PyDoc_STRVAR(center__doc__,
"B.center(width[, fillchar]) -> modified copy of B\n"
"\n"
"Return B centered in a string of length width. Padding is\n"
"done using the specified fill character (default is a space)");
static PyObject *
stringlib_center(PyObject *self, PyObject *args)
{
Py_ssize_t marg, left;
Py_ssize_t width;
char fillchar = ' ';
if (!PyArg_ParseTuple(args, "n|c:center", &width, &fillchar))
return NULL;
if (STRINGLIB_LEN(self) >= width && STRINGLIB_CHECK_EXACT(self)) {
#if STRINGLIB_MUTABLE
/* We're defined as returning a copy; If the object is mutable
* that means we must make an identical copy. */
return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self));
#else
Py_INCREF(self);
return (PyObject*) self;
#endif
}
marg = width - STRINGLIB_LEN(self);
left = marg / 2 + (marg & width & 1);
return pad(self, left, marg - left, fillchar);
}
PyDoc_STRVAR(zfill__doc__,
"B.zfill(width) -> modified copy of B\n"
"\n"
"Pad a numeric string B with zeros on the left, to fill a field\n"
"of the specified width. B is never truncated.");
static PyObject *
stringlib_zfill(PyObject *self, PyObject *args)
{
Py_ssize_t fill;
PyObject *s;
char *p;
Py_ssize_t width;
if (!PyArg_ParseTuple(args, "n:zfill", &width))
return NULL;
if (STRINGLIB_LEN(self) >= width) {
if (STRINGLIB_CHECK_EXACT(self)) {
#if STRINGLIB_MUTABLE
/* We're defined as returning a copy; If the object is mutable
* that means we must make an identical copy. */
return STRINGLIB_NEW(STRINGLIB_STR(self), STRINGLIB_LEN(self));
#else
Py_INCREF(self);
return (PyObject*) self;
#endif
}
else
return STRINGLIB_NEW(
STRINGLIB_STR(self),
STRINGLIB_LEN(self)
);
}
fill = width - STRINGLIB_LEN(self);
s = pad(self, fill, 0, '0');
if (s == NULL)
return NULL;
p = STRINGLIB_STR(s);
if (p[fill] == '+' || p[fill] == '-') {
/* move sign to beginning of string */
p[0] = p[fill];
p[fill] = '0';
}
return (PyObject*) s;
}
#define _STRINGLIB_SPLIT_APPEND(data, left, right) \
str = STRINGLIB_NEW((data) + (left), \
(right) - (left)); \
if (str == NULL) \
goto onError; \
if (PyList_Append(list, str)) { \
Py_DECREF(str); \
goto onError; \
} \
else \
Py_DECREF(str);
PyDoc_STRVAR(splitlines__doc__,
"B.splitlines([keepends]) -> list of lines\n\
\n\
Return a list of the lines in B, breaking at line boundaries.\n\
Line breaks are not included in the resulting list unless keepends\n\
is given and true.");
static PyObject*
stringlib_splitlines(PyObject *self, PyObject *args)
{
register Py_ssize_t i;
register Py_ssize_t j;
Py_ssize_t len;
int keepends = 0;
PyObject *list;
PyObject *str;
char *data;
if (!PyArg_ParseTuple(args, "|i:splitlines", &keepends))
return NULL;
data = STRINGLIB_STR(self);
len = STRINGLIB_LEN(self);
/* This does not use the preallocated list because splitlines is
usually run with hundreds of newlines. The overhead of
switching between PyList_SET_ITEM and append causes about a
2-3% slowdown for that common case. A smarter implementation
could move the if check out, so the SET_ITEMs are done first
and the appends only done when the prealloc buffer is full.
That's too much work for little gain.*/
list = PyList_New(0);
if (!list)
goto onError;
for (i = j = 0; i < len; ) {
Py_ssize_t eol;
/* Find a line and append it */
while (i < len && data[i] != '\n' && data[i] != '\r')
i++;
/* Skip the line break reading CRLF as one line break */
eol = i;
if (i < len) {
if (data[i] == '\r' && i + 1 < len &&
data[i+1] == '\n')
i += 2;
else
i++;
if (keepends)
eol = i;
}
_STRINGLIB_SPLIT_APPEND(data, j, eol);
j = i;
}
if (j < len) {
_STRINGLIB_SPLIT_APPEND(data, j, len);
}
return list;
onError:
Py_XDECREF(list);
return NULL;
}
#undef _STRINGLIB_SPLIT_APPEND
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#define STRINGLIB_CHECK PyUnicode_Check #define STRINGLIB_CHECK PyUnicode_Check
#define STRINGLIB_TOSTR PyObject_Unicode #define STRINGLIB_TOSTR PyObject_Unicode
#define STRINGLIB_WANT_CONTAINS_OBJ 1
/* STRINGLIB_CMP was defined as: /* STRINGLIB_CMP was defined as:
Py_LOCAL_INLINE(int) Py_LOCAL_INLINE(int)
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment