diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -627,6 +627,7 @@ BYTESTR_DEPS = \ $(srcdir)/Objects/stringlib/eq.h \ $(srcdir)/Objects/stringlib/fastsearch.h \ $(srcdir)/Objects/stringlib/find.h \ + $(srcdir)/Objects/stringlib/findwidth.h \ $(srcdir)/Objects/stringlib/partition.h \ $(srcdir)/Objects/stringlib/split.h \ $(srcdir)/Objects/stringlib/stringdefs.h \ diff --git a/Objects/stringlib/findwidth.h b/Objects/stringlib/findwidth.h new file mode 100644 --- /dev/null +++ b/Objects/stringlib/findwidth.h @@ -0,0 +1,124 @@ +/* Finding the optimal width of unicode characters in a buffer */ + +#if STRINGLIB_IS_UNICODE + +/* Mask to check or force alignment of a pointer to C 'long' boundaries */ +#define LONG_PTR_MASK (size_t) (SIZEOF_LONG - 1) + +/* Mask to quickly check whether a C 'long' contains a + non-ASCII, UTF8-encoded char. */ +#if (SIZEOF_LONG == 8) +# define UCS1_ASCII_CHAR_MASK 0x8080808080808080L +#elif (SIZEOF_LONG == 4) +# define UCS1_ASCII_CHAR_MASK 0x80808080L +#else +# error C 'long' size should be either 4 or 8! +#endif + +#if STRINGLIB_SIZEOF_CHAR == 1 + +Py_LOCAL_INLINE(Py_UCS4) +STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end) +{ + const unsigned char *p = (const unsigned char *) begin; + const unsigned char *aligned_end = (const unsigned char *) ((size_t) end & ~LONG_PTR_MASK); + + while (p < end) { + if (!((size_t) p & LONG_PTR_MASK)) { + /* Help register allocation */ + register const unsigned char *_p = p; + while (_p < aligned_end) { + unsigned long value = *(unsigned long *) _p; + if (value & UCS1_ASCII_CHAR_MASK) + return 255; + _p += SIZEOF_LONG; + } + p = _p; + if (p == end) + break; + } + if (*p++ & 0x80) + return 255; + } + return 127; +} + +#undef LONG_PTR_MASK +#undef ASCII_CHAR_MASK + +#else /* STRINGLIB_SIZEOF_CHAR == 1 */ + +#define MASK_ASCII 0xFFFFFF80 +#define MASK_UCS1 0xFFFFFF00 +#define MASK_UCS2 0xFFFF0000 + +Py_LOCAL_INLINE(Py_UCS4) +STRINGLIB(find_max_char)(const STRINGLIB_CHAR *begin, const STRINGLIB_CHAR *end) +{ +#if STRINGLIB_SIZEOF_CHAR == 2 + const Py_UCS4 mask_limit = MASK_UCS1; +#elif STRINGLIB_SIZEOF_CHAR == 4 + const Py_UCS4 mask_limit = MASK_UCS2; +#else +#error Invalid STRINGLIB_SIZEOF_CHAR (must be 1, 2 or 4) +#endif + register Py_UCS4 mask = MASK_ASCII; + Py_UCS4 ret; + + const STRINGLIB_CHAR *p = begin; + Py_ssize_t n = end - begin; + const STRINGLIB_CHAR *unrolled_end = begin + (n & ~ (Py_ssize_t) 7); + while (p < unrolled_end) { + Py_UCS4 bits = p[0] | p[1] | p[2] | p[3]; +_recheck: + if (bits & mask) { + if (mask == mask_limit) { + /* Limit reached */ + ret = ~mask + 1; + goto _end; + } + if (mask == MASK_ASCII) { + mask = MASK_UCS1; + } + else { + /* mask can't be MASK_UCS2 because of mask_limit above */ + assert(mask == MASK_UCS1); + mask = MASK_UCS2; + } + goto _recheck; + /* We check the new mask on the same chars in the next iteration */ + continue; + } + p += 4; + } + while (p < end) { + if (p[0] & mask) { + if (mask == mask_limit) { + /* Limit reached */ + ret = ~mask + 1; + goto _end; + } + if (mask == MASK_ASCII) { + mask = MASK_UCS1; + } + else { + /* mask can't be MASK_UCS2 because of mask_limit above */ + assert(mask == MASK_UCS1); + mask = MASK_UCS2; + } + /* We check the new mask on the same chars in the next iteration */ + continue; + } + p++; + } + ret = ~mask; +_end: + if (ret >= 0x10FFFF) + return 0x10FFFF; + else + return ret; +} + +#endif /* STRINGLIB_SIZEOF_CHAR == 1 */ +#endif /* STRINGLIB_IS_UNICODE */ + diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -260,6 +260,15 @@ static int unicode_is_singleton(PyObject #endif static PyObject * +unicode_fromascii(const unsigned char *s, Py_ssize_t size); +static PyObject * +_PyUnicode_FromUCS1(const unsigned char *s, Py_ssize_t size); +static PyObject * +_PyUnicode_FromUCS2(const Py_UCS2 *s, Py_ssize_t size); +static PyObject * +_PyUnicode_FromUCS4(const Py_UCS4 *s, Py_ssize_t size); + +static PyObject * unicode_encode_call_errorhandler(const char *errors, PyObject **errorHandler,const char *encoding, const char *reason, const Py_UNICODE *unicode, Py_ssize_t size, PyObject **exceptionObject, @@ -468,6 +477,48 @@ make_bloom_mask(int kind, void* ptr, Py_ (BLOOM(mask, chr) \ && (PyUnicode_FindChar(str, chr, 0, PyUnicode_GET_LENGTH(str), 1) >= 0)) +/* Compilation of templated routines */ + +#include "stringlib/asciilib.h" +#include "stringlib/fastsearch.h" +#include "stringlib/partition.h" +#include "stringlib/split.h" +#include "stringlib/count.h" +#include "stringlib/find.h" +#include "stringlib/findwidth.h" +#include "stringlib/localeutil.h" +#include "stringlib/undef.h" + +#include "stringlib/ucs1lib.h" +#include "stringlib/fastsearch.h" +#include "stringlib/partition.h" +#include "stringlib/split.h" +#include "stringlib/count.h" +#include "stringlib/find.h" +#include "stringlib/findwidth.h" +#include "stringlib/localeutil.h" +#include "stringlib/undef.h" + +#include "stringlib/ucs2lib.h" +#include "stringlib/fastsearch.h" +#include "stringlib/partition.h" +#include "stringlib/split.h" +#include "stringlib/count.h" +#include "stringlib/find.h" +#include "stringlib/findwidth.h" +#include "stringlib/localeutil.h" +#include "stringlib/undef.h" + +#include "stringlib/ucs4lib.h" +#include "stringlib/fastsearch.h" +#include "stringlib/partition.h" +#include "stringlib/split.h" +#include "stringlib/count.h" +#include "stringlib/find.h" +#include "stringlib/findwidth.h" +#include "stringlib/localeutil.h" +#include "stringlib/undef.h" + /* --- Unicode Object ----------------------------------------------------- */ static PyObject * @@ -1689,17 +1740,11 @@ _PyUnicode_FromUCS1(const unsigned char* { PyObject *res; unsigned char max_char = 127; - Py_ssize_t i; assert(size >= 0); if (size == 1) return get_latin1_char(u[0]); - for (i = 0; i < size; i++) { - if (u[i] & 0x80) { - max_char = 255; - break; - } - } + max_char = ucs1lib_find_max_char(u, u + size); res = PyUnicode_New(size, max_char); if (!res) return NULL; @@ -1713,26 +1758,20 @@ _PyUnicode_FromUCS2(const Py_UCS2 *u, Py { PyObject *res; Py_UCS2 max_char = 0; - Py_ssize_t i; assert(size >= 0); if (size == 1 && u[0] < 256) return get_latin1_char((unsigned char)u[0]); - for (i = 0; i < size; i++) { - if (u[i] > max_char) { - max_char = u[i]; - if (max_char >= 256) - break; - } - } + max_char = ucs2lib_find_max_char(u, u + size); res = PyUnicode_New(size, max_char); if (!res) return NULL; if (max_char >= 256) memcpy(PyUnicode_2BYTE_DATA(res), u, sizeof(Py_UCS2)*size); - else - for (i = 0; i < size; i++) - PyUnicode_1BYTE_DATA(res)[i] = (Py_UCS1)u[i]; + else { + _PyUnicode_CONVERT_BYTES( + Py_UCS2, Py_UCS1, u, u + size, PyUnicode_1BYTE_DATA(res)); + } assert(_PyUnicode_CheckConsistency(res, 1)); return res; } @@ -1742,18 +1781,11 @@ _PyUnicode_FromUCS4(const Py_UCS4 *u, Py { PyObject *res; Py_UCS4 max_char = 0; - Py_ssize_t i; assert(size >= 0); if (size == 1 && u[0] < 256) return get_latin1_char(u[0]); - for (i = 0; i < size; i++) { - if (u[i] > max_char) { - max_char = u[i]; - if (max_char >= 0x10000) - break; - } - } + max_char = ucs4lib_find_max_char(u, u + size); res = PyUnicode_New(size, max_char); if (!res) return NULL; @@ -1794,7 +1826,7 @@ unicode_adjust_maxchar(PyObject **p_unic { PyObject *unicode, *copy; Py_UCS4 max_char; - Py_ssize_t i, len; + Py_ssize_t len; unsigned int kind; assert(p_unicode != NULL); @@ -1807,37 +1839,23 @@ unicode_adjust_maxchar(PyObject **p_unic kind = PyUnicode_KIND(unicode); if (kind == PyUnicode_1BYTE_KIND) { const Py_UCS1 *u = PyUnicode_1BYTE_DATA(unicode); - for (i = 0; i < len; i++) { - if (u[i] & 0x80) - return; - } - max_char = 127; + max_char = ucs1lib_find_max_char(u, u + len); + if (max_char >= 128) + return; } else if (kind == PyUnicode_2BYTE_KIND) { const Py_UCS2 *u = PyUnicode_2BYTE_DATA(unicode); - max_char = 0; - for (i = 0; i < len; i++) { - if (u[i] > max_char) { - max_char = u[i]; - if (max_char >= 256) - return; - } - } + max_char = ucs2lib_find_max_char(u, u + len); + if (max_char >= 256) + return; } else { - const Py_UCS4 *u; + const Py_UCS4 *u = PyUnicode_4BYTE_DATA(unicode); assert(kind == PyUnicode_4BYTE_KIND); - u = PyUnicode_4BYTE_DATA(unicode); - max_char = 0; - for (i = 0; i < len; i++) { - if (u[i] > max_char) { - max_char = u[i]; - if (max_char >= 0x10000) - return; - } - } - } - assert(max_char < PyUnicode_MAX_CHAR_VALUE(unicode)); + max_char = ucs4lib_find_max_char(u, u + len); + if (max_char >= 0x10000) + return; + } copy = PyUnicode_New(len, max_char); copy_characters(copy, 0, unicode, 0, len); Py_DECREF(unicode); @@ -8508,42 +8526,6 @@ PyUnicode_EncodeDecimal(Py_UNICODE *s, /* --- Helpers ------------------------------------------------------------ */ -#include "stringlib/asciilib.h" -#include "stringlib/fastsearch.h" -#include "stringlib/partition.h" -#include "stringlib/split.h" -#include "stringlib/count.h" -#include "stringlib/find.h" -#include "stringlib/localeutil.h" -#include "stringlib/undef.h" - -#include "stringlib/ucs1lib.h" -#include "stringlib/fastsearch.h" -#include "stringlib/partition.h" -#include "stringlib/split.h" -#include "stringlib/count.h" -#include "stringlib/find.h" -#include "stringlib/localeutil.h" -#include "stringlib/undef.h" - -#include "stringlib/ucs2lib.h" -#include "stringlib/fastsearch.h" -#include "stringlib/partition.h" -#include "stringlib/split.h" -#include "stringlib/count.h" -#include "stringlib/find.h" -#include "stringlib/localeutil.h" -#include "stringlib/undef.h" - -#include "stringlib/ucs4lib.h" -#include "stringlib/fastsearch.h" -#include "stringlib/partition.h" -#include "stringlib/split.h" -#include "stringlib/count.h" -#include "stringlib/find.h" -#include "stringlib/localeutil.h" -#include "stringlib/undef.h" - static Py_ssize_t any_find_slice(int direction, PyObject* s1, PyObject* s2, Py_ssize_t start,