Skip to content

Commit 87d61fa

Browse files
committed
gh-158451: Add _PyUnicodeWriter_WriteLatin1String() test (#158464)
Fix _PyUnicodeWriter_WriteLatin1String() when the writer buffer kind is UCS-2 or UCS-4. (cherry picked from commit 26d3f5d)
1 parent 21aae35 commit 87d61fa

4 files changed

Lines changed: 81 additions & 56 deletions

File tree

‎Lib/test/test_capi/test_unicode.py‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,24 @@ def test_ascii(self):
18751875
writer.write_ascii(b"Python! <truncated>", 6)
18761876
self.assertEqual(writer.finish(), "Hello Python")
18771877

1878+
def test_write_latin1(self):
1879+
# Test _PyUnicodeWriter_WriteLatin1String()
1880+
writer = self.create_writer(0)
1881+
# Start with ASCII buffer
1882+
writer.write_latin1(b"abc IGNORED", 3)
1883+
writer.write_latin1(b"IGNORED", 0)
1884+
# Change buffer kind to UCS-1
1885+
writer.write_latin1(b"\xe9", 1)
1886+
# Change buffer kind to UCS-2
1887+
writer.write_str('[\u20ac]')
1888+
writer.write_latin1(b"def\xa0", 4)
1889+
# Change buffer kind to UCS-4
1890+
writer.write_str('[\U0010ffff]')
1891+
writer.write_latin1(b"ghi\xff.", 5)
1892+
writer.write_latin1(b"IGNORED", 0)
1893+
self.assertEqual(writer.finish(),
1894+
"abc\xe9[\u20ac]def\xa0[\U0010ffff]ghi\xff.")
1895+
18781896
def test_invalid_utf8(self):
18791897
writer = self.create_writer(0)
18801898
with self.assertRaises(UnicodeDecodeError):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :c:func:`!_PyUnicodeWriter_WriteLatin1String` when the writer buffer
2+
kind is UCS-2 or UCS-4. Patch by Victor Stinner.

‎Modules/_testcapi/unicode.c‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,32 @@ writer_write_substring(PyObject *self_raw, PyObject *args)
677677
}
678678

679679

680+
static PyObject*
681+
writer_write_latin1(PyObject *self_raw, PyObject *args)
682+
{
683+
WriterObject *self = (WriterObject *)self_raw;
684+
if (writer_check(self) < 0) {
685+
return NULL;
686+
}
687+
688+
const char *str;
689+
Py_ssize_t bsize, size;
690+
if (!PyArg_ParseTuple(args, "z#n", &str, &bsize, &size)) {
691+
return NULL;
692+
}
693+
694+
_PyUnicodeWriter *writer = (_PyUnicodeWriter*)self->writer;
695+
_Py_COMP_DIAG_PUSH
696+
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
697+
if (_PyUnicodeWriter_WriteLatin1String(writer, str, size) < 0) {
698+
return NULL;
699+
}
700+
_Py_COMP_DIAG_POP
701+
702+
Py_RETURN_NONE;
703+
}
704+
705+
680706
static PyObject*
681707
writer_decodeutf8stateful(PyObject *self_raw, PyObject *args)
682708
{
@@ -745,6 +771,7 @@ static PyMethodDef writer_methods[] = {
745771
{"write_str", _PyCFunction_CAST(writer_write_str), METH_O},
746772
{"write_repr", _PyCFunction_CAST(writer_write_repr), METH_O},
747773
{"write_substring", _PyCFunction_CAST(writer_write_substring), METH_VARARGS},
774+
{"write_latin1", _PyCFunction_CAST(writer_write_latin1), METH_VARARGS},
748775
{"decodeutf8stateful", _PyCFunction_CAST(writer_decodeutf8stateful), METH_VARARGS},
749776
{"get_pointer", _PyCFunction_CAST(writer_get_pointer), METH_VARARGS},
750777
{"finish", _PyCFunction_CAST(writer_finish), METH_NOARGS},

‎Objects/unicodeobject.c‎

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,58 +1910,6 @@ PyUnicode_Resize(PyObject **p_unicode, Py_ssize_t length)
19101910
return unicode_resize(p_unicode, length);
19111911
}
19121912

1913-
/* Copy an ASCII or latin1 char* string into a Python Unicode string.
1914-
1915-
WARNING: The function doesn't copy the terminating null character and
1916-
doesn't check the maximum character (may write a latin1 character in an
1917-
ASCII string). */
1918-
static void
1919-
unicode_write_cstr(PyObject *unicode, Py_ssize_t index,
1920-
const char *str, Py_ssize_t len)
1921-
{
1922-
int kind = PyUnicode_KIND(unicode);
1923-
const void *data = PyUnicode_DATA(unicode);
1924-
const char *end = str + len;
1925-
1926-
assert(index + len <= PyUnicode_GET_LENGTH(unicode));
1927-
switch (kind) {
1928-
case PyUnicode_1BYTE_KIND: {
1929-
#ifdef Py_DEBUG
1930-
if (PyUnicode_IS_ASCII(unicode)) {
1931-
Py_UCS4 maxchar = ucs1lib_find_max_char(
1932-
(const Py_UCS1*)str,
1933-
(const Py_UCS1*)str + len);
1934-
assert(maxchar < 128);
1935-
}
1936-
#endif
1937-
memcpy((char *) data + index, str, len);
1938-
break;
1939-
}
1940-
case PyUnicode_2BYTE_KIND: {
1941-
Py_UCS2 *start = (Py_UCS2 *)data + index;
1942-
Py_UCS2 *ucs2 = start;
1943-
1944-
for (; str < end; ++ucs2, ++str)
1945-
*ucs2 = (Py_UCS2)*str;
1946-
1947-
assert((ucs2 - start) <= PyUnicode_GET_LENGTH(unicode));
1948-
break;
1949-
}
1950-
case PyUnicode_4BYTE_KIND: {
1951-
Py_UCS4 *start = (Py_UCS4 *)data + index;
1952-
Py_UCS4 *ucs4 = start;
1953-
1954-
for (; str < end; ++ucs4, ++str)
1955-
*ucs4 = (Py_UCS4)*str;
1956-
1957-
assert((ucs4 - start) <= PyUnicode_GET_LENGTH(unicode));
1958-
break;
1959-
}
1960-
default:
1961-
Py_UNREACHABLE();
1962-
}
1963-
}
1964-
19651913
static PyObject*
19661914
get_latin1_char(Py_UCS1 ch)
19671915
{
@@ -14202,12 +14150,42 @@ int
1420214150
_PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer,
1420314151
const char *str, Py_ssize_t len)
1420414152
{
14205-
Py_UCS4 maxchar;
14153+
if (len == 0) {
14154+
return 0;
14155+
}
1420614156

14207-
maxchar = ucs1lib_find_max_char((const Py_UCS1*)str, (const Py_UCS1*)str + len);
14208-
if (_PyUnicodeWriter_Prepare(writer, len, maxchar) == -1)
14157+
const Py_UCS1 *ucs1 = (const Py_UCS1 *)str;
14158+
Py_UCS4 maxchar = ucs1lib_find_max_char(ucs1, ucs1 + len);
14159+
if (_PyUnicodeWriter_Prepare(writer, len, maxchar) < 0) {
1420914160
return -1;
14210-
unicode_write_cstr(writer->buffer, writer->pos, str, len);
14161+
}
14162+
14163+
Py_ssize_t index = writer->pos;
14164+
switch (writer->kind) {
14165+
case PyUnicode_1BYTE_KIND: {
14166+
memcpy((Py_UCS1 *)writer->data + index, ucs1, len);
14167+
break;
14168+
}
14169+
case PyUnicode_2BYTE_KIND: {
14170+
Py_UCS2 *ucs2 = (Py_UCS2 *)writer->data + index;
14171+
const Py_UCS1 *end = ucs1 + len;
14172+
for (; ucs1 < end; ++ucs2, ++ucs1) {
14173+
*ucs2 = (Py_UCS2)*ucs1;
14174+
}
14175+
break;
14176+
}
14177+
case PyUnicode_4BYTE_KIND: {
14178+
Py_UCS4 *ucs4 = (Py_UCS4 *)writer->data + index;
14179+
const Py_UCS1 *end = ucs1 + len;
14180+
for (; ucs1 < end; ++ucs4, ++ucs1) {
14181+
*ucs4 = (Py_UCS4)*ucs1;
14182+
}
14183+
break;
14184+
}
14185+
default:
14186+
Py_UNREACHABLE();
14187+
}
14188+
1421114189
writer->pos += len;
1421214190
return 0;
1421314191
}

0 commit comments

Comments
 (0)