CINXE.COM

PEP 393 – Flexible String Representation | peps.python.org

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="color-scheme" content="light dark"> <title>PEP 393 – Flexible String Representation | peps.python.org</title> <link rel="shortcut icon" href="../_static/py.png"> <link rel="canonical" href="https://peps.python.org/pep-0393/"> <link rel="stylesheet" href="../_static/style.css" type="text/css"> <link rel="stylesheet" href="../_static/mq.css" type="text/css"> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light"> <link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark"> <link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss"> <meta property="og:title" content='PEP 393 – Flexible String Representation | peps.python.org'> <meta property="og:description" content="The Unicode string type is changed to support multiple internal representations, depending on the character with the largest Unicode ordinal (1, 2, or 4 bytes). This will allow a space-efficient representation in common cases, but give access to full UC..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://peps.python.org/pep-0393/"> <meta property="og:site_name" content="Python Enhancement Proposals (PEPs)"> <meta property="og:image" content="https://peps.python.org/_static/og-image.png"> <meta property="og:image:alt" content="Python PEPs"> <meta property="og:image:width" content="200"> <meta property="og:image:height" content="200"> <meta name="description" content="The Unicode string type is changed to support multiple internal representations, depending on the character with the largest Unicode ordinal (1, 2, or 4 bytes). This will allow a space-efficient representation in common cases, but give access to full UC..."> <meta name="theme-color" content="#3776ab"> </head> <body> <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all"> <title>Following system colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="9"></circle> <path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path> </svg> </symbol> <symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all"> <title>Selected dark colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path> <path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path> </svg> </symbol> <symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all"> <title>Selected light colour scheme</title> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="5"></circle> <line x1="12" y1="1" x2="12" y2="3"></line> <line x1="12" y1="21" x2="12" y2="23"></line> <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line> <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line> <line x1="1" y1="12" x2="3" y2="12"></line> <line x1="21" y1="12" x2="23" y2="12"></line> <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line> <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line> </svg> </symbol> </svg> <script> document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto" </script> <section id="pep-page-section"> <header> <h1>Python Enhancement Proposals</h1> <ul class="breadcrumbs"> <li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li> <li><a href="../pep-0000/">PEP Index</a> &raquo; </li> <li>PEP 393</li> </ul> <button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())"> <svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg> <svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg> <span class="visually-hidden">Toggle light / dark / auto colour theme</span> </button> </header> <article> <section id="pep-content"> <h1 class="page-title">PEP 393 – Flexible String Representation</h1> <dl class="rfc2822 field-list simple"> <dt class="field-odd">Author<span class="colon">:</span></dt> <dd class="field-odd">Martin von Löwis &lt;martin&#32;&#97;t&#32;v.loewis.de&gt;</dd> <dt class="field-even">Status<span class="colon">:</span></dt> <dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd> <dt class="field-odd">Type<span class="colon">:</span></dt> <dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd> <dt class="field-even">Created<span class="colon">:</span></dt> <dd class="field-even">24-Jan-2010</dd> <dt class="field-odd">Python-Version<span class="colon">:</span></dt> <dd class="field-odd">3.3</dd> <dt class="field-even">Post-History<span class="colon">:</span></dt> <dd class="field-even"><p></p></dd> </dl> <hr class="docutils" /> <section id="contents"> <details><summary>Table of Contents</summary><ul class="simple"> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#string-creation">String Creation</a></li> <li><a class="reference internal" href="#string-access">String Access</a></li> <li><a class="reference internal" href="#new-api">New API</a></li> <li><a class="reference internal" href="#stable-abi">Stable ABI</a></li> <li><a class="reference internal" href="#gdb-debugging-hooks">GDB Debugging Hooks</a></li> <li><a class="reference internal" href="#deprecations-removals-and-incompatibilities">Deprecations, Removals, and Incompatibilities</a></li> </ul> </li> <li><a class="reference internal" href="#discussion">Discussion</a><ul> <li><a class="reference internal" href="#performance">Performance</a></li> </ul> </li> <li><a class="reference internal" href="#porting-guidelines">Porting Guidelines</a></li> <li><a class="reference internal" href="#references">References</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> </details></section> <section id="abstract"> <h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2> <p>The Unicode string type is changed to support multiple internal representations, depending on the character with the largest Unicode ordinal (1, 2, or 4 bytes). This will allow a space-efficient representation in common cases, but give access to full UCS-4 on all systems. For compatibility with existing APIs, several representations may exist in parallel; over time, this compatibility should be phased out. The distinction between narrow and wide Unicode builds is dropped. An implementation of this PEP is available at <a class="footnote-reference brackets" href="#id3" id="id1">[1]</a>.</p> </section> <section id="rationale"> <h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2> <p>There are two classes of complaints about the current implementation of the unicode type: on systems only supporting UTF-16, users complain that non-BMP characters are not properly supported. On systems using UCS-4 internally (and also sometimes on systems using UCS-2), there is a complaint that Unicode strings take up too much memory - especially compared to Python 2.x, where the same code would often use ASCII strings (i.e. ASCII-encoded byte strings). With the proposed approach, ASCII-only Unicode strings will again use only one byte per character; while still allowing efficient indexing of strings containing non-BMP characters (as strings containing them will use 4 bytes per character).</p> <p>One problem with the approach is support for existing applications (e.g. extension modules). For compatibility, redundant representations may be computed. Applications are encouraged to phase out reliance on a specific internal representation if possible. As interaction with other libraries will often require some sort of internal representation, the specification chooses UTF-8 as the recommended way of exposing strings to C code.</p> <p>For many strings (e.g. ASCII), multiple representations may actually share memory (e.g. the shortest form may be shared with the UTF-8 form if all characters are ASCII). With such sharing, the overhead of compatibility representations is reduced. If representations do share data, it is also possible to omit structure fields, reducing the base size of string objects.</p> </section> <section id="specification"> <h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2> <p>Unicode structures are now defined as a hierarchy of structures, namely:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">PyObject_HEAD</span> <span class="n">Py_ssize_t</span> <span class="n">length</span><span class="p">;</span> <span class="n">Py_hash_t</span> <span class="nb">hash</span><span class="p">;</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">unsigned</span> <span class="nb">int</span> <span class="n">interned</span><span class="p">:</span><span class="mi">2</span><span class="p">;</span> <span class="n">unsigned</span> <span class="nb">int</span> <span class="n">kind</span><span class="p">:</span><span class="mi">2</span><span class="p">;</span> <span class="n">unsigned</span> <span class="nb">int</span> <span class="n">compact</span><span class="p">:</span><span class="mi">1</span><span class="p">;</span> <span class="n">unsigned</span> <span class="nb">int</span> <span class="n">ascii</span><span class="p">:</span><span class="mi">1</span><span class="p">;</span> <span class="n">unsigned</span> <span class="nb">int</span> <span class="n">ready</span><span class="p">:</span><span class="mi">1</span><span class="p">;</span> <span class="p">}</span> <span class="n">state</span><span class="p">;</span> <span class="n">wchar_t</span> <span class="o">*</span><span class="n">wstr</span><span class="p">;</span> <span class="p">}</span> <span class="n">PyASCIIObject</span><span class="p">;</span> <span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">PyASCIIObject</span> <span class="n">_base</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="n">utf8_length</span><span class="p">;</span> <span class="n">char</span> <span class="o">*</span><span class="n">utf8</span><span class="p">;</span> <span class="n">Py_ssize_t</span> <span class="n">wstr_length</span><span class="p">;</span> <span class="p">}</span> <span class="n">PyCompactUnicodeObject</span><span class="p">;</span> <span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span> <span class="n">PyCompactUnicodeObject</span> <span class="n">_base</span><span class="p">;</span> <span class="n">union</span> <span class="p">{</span> <span class="n">void</span> <span class="o">*</span><span class="nb">any</span><span class="p">;</span> <span class="n">Py_UCS1</span> <span class="o">*</span><span class="n">latin1</span><span class="p">;</span> <span class="n">Py_UCS2</span> <span class="o">*</span><span class="n">ucs2</span><span class="p">;</span> <span class="n">Py_UCS4</span> <span class="o">*</span><span class="n">ucs4</span><span class="p">;</span> <span class="p">}</span> <span class="n">data</span><span class="p">;</span> <span class="p">}</span> <span class="n">PyUnicodeObject</span><span class="p">;</span> </pre></div> </div> <p>Objects for which both size and maximum character are known at creation time are called “compact” unicode objects; character data immediately follow the base structure. If the maximum character is less than 128, they use the PyASCIIObject structure, and the UTF-8 data, the UTF-8 length and the wstr length are the same as the length of the ASCII data. For non-ASCII strings, the PyCompactObject structure is used. Resizing compact objects is not supported.</p> <p>Objects for which the maximum character is not given at creation time are called “legacy” objects, created through PyUnicode_FromStringAndSize(NULL, length). They use the PyUnicodeObject structure. Initially, their data is only in the wstr pointer; when PyUnicode_READY is called, the data pointer (union) is allocated. Resizing is possible as long PyUnicode_READY has not been called.</p> <p>The fields have the following interpretations:</p> <ul class="simple"> <li>length: number of code points in the string (result of sq_length)</li> <li>interned: interned-state (SSTATE_*) as in 3.2</li> <li>kind: form of string<ul> <li>00 =&gt; str is not initialized (data are in wstr)</li> <li>01 =&gt; 1 byte (Latin-1)</li> <li>10 =&gt; 2 byte (UCS-2)</li> <li>11 =&gt; 4 byte (UCS-4);</li> </ul> </li> <li>compact: the object uses one of the compact representations (implies ready)</li> <li>ascii: the object uses the PyASCIIObject representation (implies compact and ready)</li> <li>ready: the canonical representation is ready to be accessed through PyUnicode_DATA and PyUnicode_GET_LENGTH. This is set either if the object is compact, or the data pointer and length have been initialized.</li> <li>wstr_length, wstr: representation in platform’s wchar_t (null-terminated). If wchar_t is 16-bit, this form may use surrogate pairs (in which cast wstr_length differs form length). wstr_length differs from length only if there are surrogate pairs in the representation.</li> <li>utf8_length, utf8: UTF-8 representation (null-terminated).</li> <li>data: shortest-form representation of the unicode string. The string is null-terminated (in its respective representation).</li> </ul> <p>All three representations are optional, although the data form is considered the canonical representation which can be absent only while the string is being created. If the representation is absent, the pointer is NULL, and the corresponding length field may contain arbitrary data.</p> <p>The Py_UNICODE type is still supported but deprecated. It is always defined as a typedef for wchar_t, so the wstr representation can double as Py_UNICODE representation.</p> <p>The data and utf8 pointers point to the same memory if the string uses only ASCII characters (using only Latin-1 is not sufficient). The data and wstr pointers point to the same memory if the string happens to fit exactly to the wchar_t type of the platform (i.e. uses some BMP-not-Latin-1 characters if sizeof(wchar_t) is 2, and uses some non-BMP characters if sizeof(wchar_t) is 4).</p> <section id="string-creation"> <h3><a class="toc-backref" href="#string-creation" role="doc-backlink">String Creation</a></h3> <p>The recommended way to create a Unicode object is to use the function PyUnicode_New:</p> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject</span><span class="o">*</span> <span class="n">PyUnicode_New</span><span class="p">(</span><span class="n">Py_ssize_t</span> <span class="n">size</span><span class="p">,</span> <span class="n">Py_UCS4</span> <span class="n">maxchar</span><span class="p">);</span> </pre></div> </div> <p>Both parameters must denote the eventual size/range of the strings. In particular, codecs using this API must compute both the number of characters and the maximum character in advance. A string is allocated according to the specified size and character range and is null-terminated; the actual characters in it may be uninitialized.</p> <p>PyUnicode_FromString and PyUnicode_FromStringAndSize remain supported for processing UTF-8 input; the input is decoded, and the UTF-8 representation is not yet set for the string.</p> <p>PyUnicode_FromUnicode remains supported but is deprecated. If the Py_UNICODE pointer is non-null, the data representation is set. If the pointer is NULL, a properly-sized wstr representation is allocated, which can be modified until PyUnicode_READY() is called (explicitly or implicitly). Resizing a Unicode string remains possible until it is finalized.</p> <p>PyUnicode_READY() converts a string containing only a wstr representation into the canonical representation. Unless wstr and data can share the memory, the wstr representation is discarded after the conversion. The macro returns 0 on success and -1 on failure, which happens in particular if the memory allocation fails.</p> </section> <section id="string-access"> <h3><a class="toc-backref" href="#string-access" role="doc-backlink">String Access</a></h3> <p>The canonical representation can be accessed using two macros PyUnicode_Kind and PyUnicode_Data. PyUnicode_Kind gives one of the values PyUnicode_WCHAR_KIND (0), PyUnicode_1BYTE_KIND (1), PyUnicode_2BYTE_KIND (2), or PyUnicode_4BYTE_KIND (3). PyUnicode_DATA gives the void pointer to the data. Access to individual characters should use PyUnicode_{READ|WRITE}[_CHAR]:</p> <ul class="simple"> <li>PyUnicode_READ(kind, data, index)</li> <li>PyUnicode_WRITE(kind, data, index, value)</li> <li>PyUnicode_READ_CHAR(unicode, index)</li> </ul> <p>All these macros assume that the string is in canonical form; callers need to ensure this by calling PyUnicode_READY.</p> <p>A new function PyUnicode_AsUTF8 is provided to access the UTF-8 representation. It is thus identical to the existing _PyUnicode_AsString, which is removed. The function will compute the utf8 representation when first called. Since this representation will consume memory until the string object is released, applications should use the existing PyUnicode_AsUTF8String where possible (which generates a new string object every time). APIs that implicitly converts a string to a char* (such as the ParseTuple functions) will use PyUnicode_AsUTF8 to compute a conversion.</p> </section> <section id="new-api"> <h3><a class="toc-backref" href="#new-api" role="doc-backlink">New API</a></h3> <p>This section summarizes the API additions.</p> <p>Macros to access the internal representation of a Unicode object (read-only):</p> <ul class="simple"> <li>PyUnicode_IS_COMPACT_ASCII(o), PyUnicode_IS_COMPACT(o), PyUnicode_IS_READY(o)</li> <li>PyUnicode_GET_LENGTH(o)</li> <li>PyUnicode_KIND(o), PyUnicode_CHARACTER_SIZE(o), PyUnicode_MAX_CHAR_VALUE(o)</li> <li>PyUnicode_DATA(o), PyUnicode_1BYTE_DATA(o), PyUnicode_2BYTE_DATA(o), PyUnicode_4BYTE_DATA(o)</li> </ul> <p>Character access macros:</p> <ul class="simple"> <li>PyUnicode_READ(kind, data, index), PyUnicode_READ_CHAR(o, index)</li> <li>PyUnicode_WRITE(kind, data, index, value)</li> </ul> <p>Other macros:</p> <ul class="simple"> <li>PyUnicode_READY(o)</li> <li>PyUnicode_CONVERT_BYTES(from_type, to_type, begin, end, to)</li> </ul> <p>String creation functions:</p> <ul class="simple"> <li>PyUnicode_New(size, maxchar)</li> <li>PyUnicode_FromKindAndData(kind, data, size)</li> <li>PyUnicode_Substring(o, start, end)</li> </ul> <p>Character access utility functions:</p> <ul class="simple"> <li>PyUnicode_GetLength(o), PyUnicode_ReadChar(o, index), PyUnicode_WriteChar(o, index, character)</li> <li>PyUnicode_CopyCharacters(to, to_start, from, from_start, how_many)</li> <li>PyUnicode_FindChar(str, ch, start, end, direction)</li> </ul> <p>Representation conversion:</p> <ul class="simple"> <li>PyUnicode_AsUCS4(o, buffer, buflen)</li> <li>PyUnicode_AsUCS4Copy(o)</li> <li>PyUnicode_AsUnicodeAndSize(o, size_out)</li> <li>PyUnicode_AsUTF8(o)</li> <li>PyUnicode_AsUTF8AndSize(o, size_out)</li> </ul> <p>UCS4 utility functions:</p> <ul class="simple"> <li>Py_UCS4_{strlen, strcpy, strcat, strncpy, strcmp, strncpy, strcmp, strncmp, strchr, strrchr}</li> </ul> </section> <section id="stable-abi"> <h3><a class="toc-backref" href="#stable-abi" role="doc-backlink">Stable ABI</a></h3> <p>The following functions are added to the stable ABI (<a class="pep reference internal" href="../pep-0384/" title="PEP 384 – Defining a Stable ABI">PEP 384</a>), as they are independent of the actual representation of Unicode objects: PyUnicode_New, PyUnicode_Substring, PyUnicode_GetLength, PyUnicode_ReadChar, PyUnicode_WriteChar, PyUnicode_Find, PyUnicode_FindChar.</p> </section> <section id="gdb-debugging-hooks"> <h3><a class="toc-backref" href="#gdb-debugging-hooks" role="doc-backlink">GDB Debugging Hooks</a></h3> <p>Tools/gdb/libpython.py contains debugging hooks that embed knowledge about the internals of CPython’s data types, include PyUnicodeObject instances. It has been updated to track the change.</p> </section> <section id="deprecations-removals-and-incompatibilities"> <h3><a class="toc-backref" href="#deprecations-removals-and-incompatibilities" role="doc-backlink">Deprecations, Removals, and Incompatibilities</a></h3> <p>While the Py_UNICODE representation and APIs are deprecated with this PEP, no removal of the respective APIs is scheduled. The APIs should remain available at least five years after the PEP is accepted; before they are removed, existing extension modules should be studied to find out whether a sufficient majority of the open-source code on PyPI has been ported to the new API. A reasonable motivation for using the deprecated API even in new code is for code that shall work both on Python 2 and Python 3.</p> <p>The following macros and functions are deprecated:</p> <ul class="simple"> <li>PyUnicode_FromUnicode</li> <li>PyUnicode_GET_SIZE, PyUnicode_GetSize, PyUnicode_GET_DATA_SIZE,</li> <li>PyUnicode_AS_UNICODE, PyUnicode_AsUnicode, PyUnicode_AsUnicodeAndSize</li> <li>PyUnicode_COPY, PyUnicode_FILL, PyUnicode_MATCH</li> <li>PyUnicode_Encode, PyUnicode_EncodeUTF7, PyUnicode_EncodeUTF8, PyUnicode_EncodeUTF16, PyUnicode_EncodeUTF32, PyUnicode_EncodeUnicodeEscape, PyUnicode_EncodeRawUnicodeEscape, PyUnicode_EncodeLatin1, PyUnicode_EncodeASCII, PyUnicode_EncodeCharmap, PyUnicode_TranslateCharmap, PyUnicode_EncodeMBCS, PyUnicode_EncodeDecimal, PyUnicode_TransformDecimalToASCII</li> <li>Py_UNICODE_{strlen, strcat, strcpy, strcmp, strchr, strrchr}</li> <li>PyUnicode_AsUnicodeCopy</li> <li>PyUnicode_GetMax</li> </ul> <p>_PyUnicode_AsDefaultEncodedString is removed. It previously returned a borrowed reference to an UTF-8-encoded bytes object. Since the unicode object cannot anymore cache such a reference, implementing it without leaking memory is not possible. No deprecation phase is provided, since it was an API for internal use only.</p> <p>Extension modules using the legacy API may inadvertently call PyUnicode_READY, by calling some API that requires that the object is ready, and then continue accessing the (now invalid) Py_UNICODE pointer. Such code will break with this PEP. The code was already flawed in 3.2, as there is was no explicit guarantee that the PyUnicode_AS_UNICODE result would stay valid after an API call (due to the possibility of string resizing). Modules that face this issue need to re-fetch the Py_UNICODE pointer after API calls; doing so will continue to work correctly in earlier Python versions.</p> </section> </section> <section id="discussion"> <h2><a class="toc-backref" href="#discussion" role="doc-backlink">Discussion</a></h2> <p>Several concerns have been raised about the approach presented here:</p> <p>It makes the implementation more complex. That’s true, but considered worth it given the benefits.</p> <p>The Py_UNICODE representation is not instantaneously available, slowing down applications that request it. While this is also true, applications that care about this problem can be rewritten to use the data representation.</p> <section id="performance"> <h3><a class="toc-backref" href="#performance" role="doc-backlink">Performance</a></h3> <p>Performance of this patch must be considered for both memory consumption and runtime efficiency. For memory consumption, the expectation is that applications that have many large strings will see a reduction in memory usage. For small strings, the effects depend on the pointer size of the system, and the size of the Py_UNICODE/wchar_t type. The following table demonstrates this for various small ASCII and Latin-1 string sizes and platforms.</p> <table class="docutils align-default"> <tbody> <tr class="row-odd"><td rowspan="3">string size</td> <td colspan="4">Python 3.2</td> <td colspan="4">This PEP</td> </tr> <tr class="row-even"><td colspan="2">16-bit wchar_t</td> <td colspan="2">32-bit wchar_t</td> <td colspan="2">ASCII</td> <td colspan="2">Latin-1</td> </tr> <tr class="row-odd"><td>32-bit</td> <td>64-bit</td> <td>32-bit</td> <td>64-bit</td> <td>32-bit</td> <td>64-bit</td> <td>32-bit</td> <td>64-bit</td> </tr> <tr class="row-even"><td>1</td> <td>32</td> <td>64</td> <td>40</td> <td>64</td> <td>32</td> <td>56</td> <td>40</td> <td>80</td> </tr> <tr class="row-odd"><td>2</td> <td>40</td> <td>64</td> <td>40</td> <td>72</td> <td>32</td> <td>56</td> <td>40</td> <td>80</td> </tr> <tr class="row-even"><td>3</td> <td>40</td> <td>64</td> <td>48</td> <td>72</td> <td>32</td> <td>56</td> <td>40</td> <td>80</td> </tr> <tr class="row-odd"><td>4</td> <td>40</td> <td>72</td> <td>48</td> <td>80</td> <td>32</td> <td>56</td> <td>48</td> <td>80</td> </tr> <tr class="row-even"><td>5</td> <td>40</td> <td>72</td> <td>56</td> <td>80</td> <td>32</td> <td>56</td> <td>48</td> <td>80</td> </tr> <tr class="row-odd"><td>6</td> <td>48</td> <td>72</td> <td>56</td> <td>88</td> <td>32</td> <td>56</td> <td>48</td> <td>80</td> </tr> <tr class="row-even"><td>7</td> <td>48</td> <td>72</td> <td>64</td> <td>88</td> <td>32</td> <td>56</td> <td>48</td> <td>80</td> </tr> <tr class="row-odd"><td>8</td> <td>48</td> <td>80</td> <td>64</td> <td>96</td> <td>40</td> <td>64</td> <td>48</td> <td>88</td> </tr> </tbody> </table> <p>The runtime effect is significantly affected by the API being used. After porting the relevant pieces of code to the new API, the iobench, stringbench, and json benchmarks see typically slowdowns of 1% to 30%; for specific benchmarks, speedups may happen as may happen significantly larger slowdowns.</p> <p>In actual measurements of a Django application (<a class="footnote-reference brackets" href="#id4" id="id2">[2]</a>), significant reductions of memory usage could be found. For example, the storage for Unicode objects reduced to 2216807 bytes, down from 6378540 bytes for a wide Unicode build, and down from 3694694 bytes for a narrow Unicode build (all on a 32-bit system). This reduction came from the prevalence of ASCII strings in this application; out of 36,000 strings (with 1,310,000 chars), 35713 where ASCII strings (with 1,300,000 chars). The sources for these strings where not further analysed; many of them likely originate from identifiers in the library, and string constants in Django’s source code.</p> <p>In comparison to Python 2, both Unicode and byte strings need to be accounted. In the test application, Unicode and byte strings combined had a length of 2,046,000 units (bytes/chars) in 2.x, and 2,200,000 units in 3.x. On a 32-bit system, where the 2.x build used 32-bit wchar_t/Py_UNICODE, the 2.x test used 3,620,000 bytes, and the 3.x build 3,340,000 bytes. This reduction in 3.x using the PEP compared to 2.x only occurs when comparing with a wide unicode build.</p> </section> </section> <section id="porting-guidelines"> <h2><a class="toc-backref" href="#porting-guidelines" role="doc-backlink">Porting Guidelines</a></h2> <p>Only a small fraction of C code is affected by this PEP, namely code that needs to look “inside” unicode strings. That code doesn’t necessarily need to be ported to this API, as the existing API will continue to work correctly. In particular, modules that need to support both Python 2 and Python 3 might get too complicated when simultaneously supporting this new API and the old Unicode API.</p> <p>In order to port modules to the new API, try to eliminate the use of these API elements:</p> <ul class="simple"> <li>the Py_UNICODE type,</li> <li>PyUnicode_AS_UNICODE and PyUnicode_AsUnicode,</li> <li>PyUnicode_GET_SIZE and PyUnicode_GetSize, and</li> <li>PyUnicode_FromUnicode.</li> </ul> <p>When iterating over an existing string, or looking at specific characters, use indexing operations rather than pointer arithmetic; indexing works well for PyUnicode_READ(_CHAR) and PyUnicode_WRITE. Use void* as the buffer type for characters to let the compiler detect invalid dereferencing operations. If you do want to use pointer arithmetics (e.g. when converting existing code), use (unsigned) char* as the buffer type, and keep the element size (1, 2, or 4) in a variable. Notice that (1&lt;&lt;(kind-1)) will produce the element size given a buffer kind.</p> <p>When creating new strings, it was common in Python to start of with a heuristical buffer size, and then grow or shrink if the heuristics failed. With this PEP, this is now less practical, as you need not only a heuristics for the length of the string, but also for the maximum character.</p> <p>In order to avoid heuristics, you need to make two passes over the input: once to determine the output length, and the maximum character; then allocate the target string with PyUnicode_New and iterate over the input a second time to produce the final output. While this may sound expensive, it could actually be cheaper than having to copy the result again as in the following approach.</p> <p>If you take the heuristical route, avoid allocating a string meant to be resized, as resizing strings won’t work for their canonical representation. Instead, allocate a separate buffer to collect the characters, and then construct a unicode object from that using PyUnicode_FromKindAndData. One option is to use Py_UCS4 as the buffer element, assuming for the worst case in character ordinals. This will allow for pointer arithmetics, but may require a lot of memory. Alternatively, start with a 1-byte buffer, and increase the element size as you encounter larger characters. In any case, PyUnicode_FromKindAndData will scan over the buffer to verify the maximum character.</p> <p>For common tasks, direct access to the string representation may not be necessary: PyUnicode_Find, PyUnicode_FindChar, PyUnicode_Ord, and PyUnicode_CopyCharacters help in analyzing and creating string objects, operating on indexes instead of data pointers.</p> </section> <section id="references"> <h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2> <aside class="footnote-list brackets"> <aside class="footnote brackets" id="id3" role="doc-footnote"> <dt class="label" id="id3">[<a href="#id1">1</a>]</dt> <dd>PEP 393 branch <a class="reference external" href="https://bitbucket.org/t0rsten/pep-393">https://bitbucket.org/t0rsten/pep-393</a></aside> <aside class="footnote brackets" id="id4" role="doc-footnote"> <dt class="label" id="id4">[<a href="#id2">2</a>]</dt> <dd>Django measurement results <a class="reference external" href="https://web.archive.org/web/20160911215951/http://www.dcl.hpi.uni-potsdam.de/home/loewis/djmemprof/">https://web.archive.org/web/20160911215951/http://www.dcl.hpi.uni-potsdam.de/home/loewis/djmemprof/</a></aside> </aside> </section> <section id="copyright"> <h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2> <p>This document has been placed in the public domain.</p> </section> </section> <hr class="docutils" /> <p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0393.rst">https://github.com/python/peps/blob/main/peps/pep-0393.rst</a></p> <p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0393.rst">2023-09-09 17:39:29 GMT</a></p> </article> <nav id="pep-sidebar"> <h2>Contents</h2> <ul> <li><a class="reference internal" href="#abstract">Abstract</a></li> <li><a class="reference internal" href="#rationale">Rationale</a></li> <li><a class="reference internal" href="#specification">Specification</a><ul> <li><a class="reference internal" href="#string-creation">String Creation</a></li> <li><a class="reference internal" href="#string-access">String Access</a></li> <li><a class="reference internal" href="#new-api">New API</a></li> <li><a class="reference internal" href="#stable-abi">Stable ABI</a></li> <li><a class="reference internal" href="#gdb-debugging-hooks">GDB Debugging Hooks</a></li> <li><a class="reference internal" href="#deprecations-removals-and-incompatibilities">Deprecations, Removals, and Incompatibilities</a></li> </ul> </li> <li><a class="reference internal" href="#discussion">Discussion</a><ul> <li><a class="reference internal" href="#performance">Performance</a></li> </ul> </li> <li><a class="reference internal" href="#porting-guidelines">Porting Guidelines</a></li> <li><a class="reference internal" href="#references">References</a></li> <li><a class="reference internal" href="#copyright">Copyright</a></li> </ul> <br> <a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0393.rst">Page Source (GitHub)</a> </nav> </section> <script src="../_static/colour_scheme.js"></script> <script src="../_static/wrap_tables.js"></script> <script src="../_static/sticky_banner.js"></script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10