Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1c24bd0

Browse files
author
Victor Stinner
committed
Issue #8870: PyUnicode_AsWideCharString() doesn't count the trailing nul character
And write unit tests for PyUnicode_AsWideChar() and PyUnicode_AsWideCharString().
1 parent 5a2da3b commit 1c24bd0

4 files changed

Lines changed: 105 additions & 11 deletions

File tree

Doc/c-api/unicode.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@ wchar_t support for platforms which support it:
466466

467467
Convert the Unicode object to a wide character string. The output string
468468
always ends with a nul character. If *size* is not *NULL*, write the number
469-
of wide characters (including the nul character) into *\*size*.
469+
of wide characters (excluding the trailing 0-termination character) into
470+
*\*size*.
470471

471472
Returns a buffer allocated by :cfunc:`PyMem_Alloc` (use :cfunc:`PyMem_Free`
472473
to free it) on success. On error, returns *NULL*, *\*size* is undefined and

Lib/test/test_unicode.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,45 @@ def test_from_format(self):
13941394
'string, got a non-ASCII byte: 0xe9$',
13951395
format_unicode, b'unicode\xe9=%s', 'ascii')
13961396

1397+
# Test PyUnicode_AsWideChar()
1398+
def test_aswidechar(self):
1399+
from _testcapi import test_aswidechar
1400+
from ctypes import c_wchar, sizeof
1401+
1402+
wchar, size = test_aswidechar('abcdef', 2)
1403+
self.assertEquals(size, 2)
1404+
self.assertEquals(wchar, 'ab')
1405+
1406+
wchar, size = test_aswidechar('abc', 3)
1407+
self.assertEquals(size, 3)
1408+
self.assertEquals(wchar, 'abc')
1409+
1410+
wchar, size = test_aswidechar('abc', 4)
1411+
self.assertEquals(size, 3)
1412+
self.assertEquals(wchar, 'abc\0')
1413+
1414+
wchar, size = test_aswidechar('abc', 10)
1415+
self.assertEquals(size, 3)
1416+
self.assertEquals(wchar, 'abc\0')
1417+
1418+
wchar, size = test_aswidechar('abc\0def', 20)
1419+
self.assertEquals(size, 7)
1420+
self.assertEquals(wchar, 'abc\0def\0')
1421+
1422+
# Test PyUnicode_AsWideCharString()
1423+
def test_aswidecharstring(self):
1424+
from _testcapi import test_aswidecharstring
1425+
from ctypes import c_wchar, sizeof
1426+
1427+
wchar, size = test_aswidecharstring('abc')
1428+
self.assertEquals(size, 3)
1429+
self.assertEquals(wchar, 'abc\0')
1430+
1431+
wchar, size = test_aswidecharstring('abc\0def')
1432+
self.assertEquals(size, 7)
1433+
self.assertEquals(wchar, 'abc\0def\0')
1434+
1435+
13971436
def test_main():
13981437
support.run_unittest(__name__)
13991438

Modules/_testcapimodule.c

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,58 @@ test_widechar(PyObject *self)
13851385
Py_RETURN_NONE;
13861386
}
13871387

1388+
static PyObject *
1389+
test_aswidechar(PyObject *self, PyObject *args)
1390+
{
1391+
PyObject *unicode, *result;
1392+
Py_ssize_t buflen, size;
1393+
wchar_t *buffer;
1394+
1395+
if (!PyArg_ParseTuple(args, "Un", &unicode, &buflen))
1396+
return NULL;
1397+
buffer = PyMem_Malloc(buflen * sizeof(wchar_t));
1398+
if (buffer == NULL)
1399+
return PyErr_NoMemory();
1400+
1401+
size = PyUnicode_AsWideChar((PyUnicodeObject*)unicode, buffer, buflen);
1402+
if (size == -1) {
1403+
PyMem_Free(buffer);
1404+
return NULL;
1405+
}
1406+
1407+
if (size < buflen)
1408+
buflen = size + 1;
1409+
else
1410+
buflen = size;
1411+
result = PyUnicode_FromWideChar(buffer, buflen);
1412+
PyMem_Free(buffer);
1413+
if (result == NULL)
1414+
return NULL;
1415+
1416+
return Py_BuildValue("(Nn)", result, size);
1417+
}
1418+
1419+
static PyObject *
1420+
test_aswidecharstring(PyObject *self, PyObject *args)
1421+
{
1422+
PyObject *unicode, *result;
1423+
Py_ssize_t size;
1424+
wchar_t *buffer;
1425+
1426+
if (!PyArg_ParseTuple(args, "U", &unicode))
1427+
return NULL;
1428+
1429+
buffer = PyUnicode_AsWideCharString((PyUnicodeObject*)unicode, &size);
1430+
if (buffer == NULL)
1431+
return NULL;
1432+
1433+
result = PyUnicode_FromWideChar(buffer, size + 1);
1434+
PyMem_Free(buffer);
1435+
if (result == NULL)
1436+
return NULL;
1437+
return Py_BuildValue("(Nn)", result, size);
1438+
}
1439+
13881440
static PyObject *
13891441
getargs_w_star(PyObject *self, PyObject *args)
13901442
{
@@ -2262,28 +2314,30 @@ static PyMethodDef TestMethods[] = {
22622314
{"getargs_Z_hash", getargs_Z_hash, METH_VARARGS},
22632315
{"getargs_w_star", getargs_w_star, METH_VARARGS},
22642316
{"codec_incrementalencoder",
2265-
(PyCFunction)codec_incrementalencoder, METH_VARARGS},
2317+
(PyCFunction)codec_incrementalencoder, METH_VARARGS},
22662318
{"codec_incrementaldecoder",
2267-
(PyCFunction)codec_incrementaldecoder, METH_VARARGS},
2319+
(PyCFunction)codec_incrementaldecoder, METH_VARARGS},
22682320
{"test_s_code", (PyCFunction)test_s_code, METH_NOARGS},
22692321
{"test_u_code", (PyCFunction)test_u_code, METH_NOARGS},
22702322
{"test_Z_code", (PyCFunction)test_Z_code, METH_NOARGS},
22712323
{"test_widechar", (PyCFunction)test_widechar, METH_NOARGS},
2324+
{"test_aswidechar", test_aswidechar, METH_VARARGS},
2325+
{"test_aswidecharstring", test_aswidecharstring, METH_VARARGS},
22722326
#ifdef WITH_THREAD
2273-
{"_test_thread_state", test_thread_state, METH_VARARGS},
2327+
{"_test_thread_state", test_thread_state, METH_VARARGS},
22742328
{"_pending_threadfunc", pending_threadfunc, METH_VARARGS},
22752329
#endif
22762330
#ifdef HAVE_GETTIMEOFDAY
2277-
{"profile_int", profile_int, METH_NOARGS},
2331+
{"profile_int", profile_int, METH_NOARGS},
22782332
#endif
2279-
{"traceback_print", traceback_print, METH_VARARGS},
2280-
{"exception_print", exception_print, METH_VARARGS},
2281-
{"argparsing", argparsing, METH_VARARGS},
2282-
{"code_newempty", code_newempty, METH_VARARGS},
2333+
{"traceback_print", traceback_print, METH_VARARGS},
2334+
{"exception_print", exception_print, METH_VARARGS},
2335+
{"argparsing", argparsing, METH_VARARGS},
2336+
{"code_newempty", code_newempty, METH_VARARGS},
22832337
{"make_exception_with_doc", (PyCFunction)make_exception_with_doc,
22842338
METH_VARARGS | METH_KEYWORDS},
22852339
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
2286-
{"format_unicode", format_unicode, METH_VARARGS},
2340+
{"format_unicode", format_unicode, METH_VARARGS},
22872341
{NULL, NULL} /* sentinel */
22882342
};
22892343

Objects/unicodeobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,7 @@ PyUnicode_AsWideCharString(PyUnicodeObject *unicode,
12161216
}
12171217
unicode_aswidechar(unicode, buffer, buflen);
12181218
if (size)
1219-
*size = buflen;
1219+
*size = buflen - 1;
12201220
return buffer;
12211221
}
12221222

0 commit comments

Comments
 (0)