インターナルPython ー Pythonの文字列とPyUnicodeObject構造体

前回の続きとなる.
インターナルPython ー PythonのオブジェクトとPyObject構造体の関係 - NO!と言えるようになりたい

今回は,UTF-8エンコードされたバイト列から,文字列オブジェクトに変換するまでの,大まかな流れについて見てみる.

PyUnicodeObject構造体

Python 3.xから,文字列の型であるstr型は,Python 2.xで言うところのunicode型となっており,内部的にはUCS-2(もしくはUCS-4)で保持している.str型を表す構造体は,以下のように定義されている.

/* Include/unicodeobject.h */
 437 typedef struct {
 438     PyObject_HEAD
 439     Py_ssize_t length;          /* Length of raw Unicode data in buffer */
 440     Py_UNICODE *str;            /* Raw Unicode buffer */
 441     long hash;                  /* Hash value; -1 if not set */
 442     int state;                  /* != 0 if interned. In this case the two
 443                                  * references from the dictionary to this object
 444                                  * are *not* counted in ob_refcnt. */
 445     PyObject *defenc;           /* (Default) Encoded version as Python
 446                                    string, or NULL; this is used for
 447                                    implementing the buffer protocol */
 448 } PyUnicodeObject;

PyObject_HEADはマクロとなっており,実態はPyObject構造体の変数を宣言しているだけである.

/* Include/object.h */
  80 /* PyObject_HEAD defines the initial segment of every PyObject. */
  81 #define PyObject_HEAD                   PyObject ob_base;

PyUnicodeObject構造体のインスタンス生成は,典型的にはPyUnicode_FromStringマクロを用いて行われる.例として,readlineモジュールを読み込んでいる部分を見てみる.readlineモジュールは,Pythonインタプリタ起動時に読み込まれ,該当箇所では次のようになっている.

/* Modules/main.c */
 510                 PyObject *v;
 511                 v = PyImport_ImportModule("readline");

また,PyImport_ImportModule()関数は以下のように定義されている.

/* Python/import.c */
2045 /* Import a module, either built-in, frozen, or external, and return
2046    its module object WITH INCREMENTED REFERENCE COUNT */
2047 
2048 PyObject *
2049 PyImport_ImportModule(const char *name)
2050 {
2051         PyObject *pname;
2052         PyObject *result;
2053 
2054         pname = PyUnicode_FromString(name);
2055         if (pname == NULL)
2056                 return NULL;
2057         result = PyImport_Import(pname);
2058         Py_DECREF(pname);
2059         return result;
2060 }

2054行目は,PyUnicode_FromString("readline")と呼び出されているに等しい.このPyUnicode_FromStringマクロでは,文字列オブジェクトを生成し,生成に失敗したらNULLポインタが返る.また,2058行目では必要なくなったオブジェクトの解放が,Py_DECREFマクロで行われている.

文字列オブジェクトの生成

では,文字列オブジェクトの確保についてを順追って見ていく.まずは,PyUnicode_FromString()関数の定義であるが,これは以下のようになっている.

 556 PyObject *PyUnicode_FromString(const char *u)
 557 {
 558     size_t size = strlen(u);
 559     if (size > PY_SSIZE_T_MAX) {
 560         PyErr_SetString(PyExc_OverflowError, "input too long");
 561         return NULL;
 562     }
 563 
 564     return PyUnicode_FromStringAndSize(u, size);
 565 }
 566 

引数のconst char *uは変換する文字列へのポインタであり,この文字列のエンコーディングUTF-8でなければならない.

558行目で入力文字列のバイト長が計算され,559行目でそれがPY_SSIZE_T_MAXを超えていないかがチェックされている.もし超えていたなら,560行目でエラーコードのための文字列がセットされて,561行目でNULLポインタが返される.

なお,PY_SSIZE_T_MAXの定義は以下のようになっている.

/* Include/pyport.h */
 177 /* Largest positive value of type Py_ssize_t. */
 178 #define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1))

-1は0xFFFFFFFFであり,これを1ビット右にシフトすると0x7FFFFFFFとなるので,つまりこれは,符号付きlongの最大値を表していることになる.

バイト長がPY_SSIZE_T_MAXを超えていなかったら,PyUnicode_FromStringAndSize()関数が呼ばれる.PyUnicode_FromStringAndSize()関数の定義は次のようになる.

/* Objects/unicodeobject.c */
 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
 510 {
 511     PyUnicodeObject *unicode;
 512 
 513     if (size < 0) {
 514         PyErr_SetString(PyExc_SystemError,
 515                         "Negative size passed to PyUnicode_FromStringAndSize");
 516         return NULL;
 517     }
 518 
 519     /* If the Unicode data is known at construction time, we can apply
 520        some optimizations which share commonly used objects.
 521        Also, this means the input must be UTF-8, so fall back to the
 522        UTF-8 decoder at the end. */
 523     if (u != NULL) {
 524 
 525         /* Optimization for empty strings */
 526         if (size == 0 && unicode_empty != NULL) {
 527             Py_INCREF(unicode_empty);
 528             return (PyObject *)unicode_empty;
 529         }
 530 
 531         /* Single characters are shared when using this constructor.
 532            Restrict to ASCII, since the input must be UTF-8. */
 533         if (size == 1 && Py_CHARMASK(*u) < 128) {
 534             unicode = unicode_latin1[Py_CHARMASK(*u)];
 535             if (!unicode) {
 536                 unicode = _PyUnicode_New(1);
 537                 if (!unicode)
 538                     return NULL;
 539                 unicode->str[0] = Py_CHARMASK(*u);
 540                 unicode_latin1[Py_CHARMASK(*u)] = unicode;
 541             }
 542             Py_INCREF(unicode);
 543             return (PyObject *)unicode;
 544         }
 545 
 546         return PyUnicode_DecodeUTF8(u, size, NULL);
 547     }
 548 
 549     unicode = _PyUnicode_New(size);
 550     if (!unicode)
 551         return NULL;
 552 
 553     return (PyObject *)unicode;
 554 }

513〜517行目では,バイト長を表す変数sizeが0より大きかをチェックしており,0未満であるならエラーを返している.文字列のオブジェクトの生成は,526行目以降のif文内で行っている.

から文字と,1文字ASCIIの扱い

Pythonでは効率化のため,から文字と,一文字のASCII文字を表す文字列オブジェクトのインスタンスは,一つずつしか生成されない.つまり,Pythonで以下のように文字列を作成しすると,実際に作成されるオブジェクトの総数はから文字が1つと,'a'が1つ,'aa'が2つとなる.

$ python
>>> a, b = '', ''
>>> c, d, e = 'a', 'a', 'a'
>>> f, g = 'aa', 'aa'

Python内部では,から文字の文字列オブジェクトはunicode_emptyに,1文字ASCIIの文字列オブジェクトは,unicode_latin1に保存している.

unicode_emptyとunicode_latin1の宣言は以下の通りとなっている.

/* Objects/unicodeobject.c */
 110 /* The empty Unicode object is shared to improve performance. */
 111 static PyUnicodeObject *unicode_empty;
 112 
 113 /* Single character Unicode strings in the Latin-1 range are being
 114    shared as well. */
 115 static PyUnicodeObject *unicode_latin1[256];

また,これらの初期化は,_PyUnicode_Init()関数内で,以下のように行われている.

/* Objects/unicodeobject.c */
9631 void _PyUnicode_Init(void)
9632 {
9633     int i;
9634 
... /* 省略 */
9646 
9647     /* Init the implementation */
9648     free_list = NULL;
9649     numfree = 0;
9650     unicode_empty = _PyUnicode_New(0);
9651     if (!unicode_empty)
9652         return;
9653 
9654     for (i = 0; i < 256; i++)
9655         unicode_latin1[i] = NULL;
9656     if (PyType_Ready(&PyUnicode_Type) < 0)
9657         Py_FatalError("Can't initialize 'unicode'");
9658 
... /* 省略 */
9665 }
9666 

9650行目で,長さ0の文字列オブジェクトを生成してunicode_emptyに保存しており,9654 - 9655行目で,unicode_latin1[]配列をNULLで初期化していることが分かる.

先程のPythonプログラムを実行したあとの状態を絵で表すと,大体こんな感じになる.

ob_refcntはリファレンスカウントで,strは実際の文字列となる.

もう一度PyUnicode_FromStringAndSize()関数を見てみる.

/* Objects/unicodeobject.c */
 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
 510 {
... /* 省略 */
 525         /* Optimization for empty strings */
 526         if (size == 0 && unicode_empty != NULL) {
 527             Py_INCREF(unicode_empty);
 528             return (PyObject *)unicode_empty;
 529         }

526行目では,バイト長が0かどうかがチェックされている.もし0であるなら,527行目でunicode_emptyのリファレンスカウントを増加させ,528行目でunicode_emptyを返り値として返している.

次に,入力が1文字のASCIIであった場合の処理部分を見てみる.

 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
 510 {
... /* 省略 */
 531         /* Single characters are shared when using this constructor.
 532            Restrict to ASCII, since the input must be UTF-8. */
 533         if (size == 1 && Py_CHARMASK(*u) < 128) {
 534             unicode = unicode_latin1[Py_CHARMASK(*u)];
 535             if (!unicode) {
 536                 unicode = _PyUnicode_New(1);
 537                 if (!unicode)
 538                     return NULL;
 539                 unicode->str[0] = Py_CHARMASK(*u);
 540                 unicode_latin1[Py_CHARMASK(*u)] = unicode;
 541             }
 542             Py_INCREF(unicode);
 543             return (PyObject *)unicode;
 544         }

533行目では,入力文字が1バイトかつ,ASCIIであるかかどうかがチェックされる.Py_CHARMASKマクロはcharを符号無しの値に変換するマクロであり,定義は以下のようになっている.

/* Include/Python.h */
 132 /* Convert a possibly signed character to a nonnegative int */
 133 /* XXX This assumes characters are 8 bits wide */
 134 #ifdef __CHAR_UNSIGNED__
 135 #define Py_CHARMASK(c)          (c)
 136 #else
 137 #define Py_CHARMASK(c)          ((unsigned char)((c) & 0xff))
 138 #endif

UTF-8では,ASCII文字は128以下の値となるため,Py_CHARMASK(*u) < 128とすることで,ASCIIであるかをチェックできる.もしも入力が1文字のASCIIであったなら,if文内が実行されることになる.

既に述べたが,1文字ASCIIのオブジェクトは,unicode_latin1配列に格納される.つまり,unicode_latin1['a']が"a"という文字列のための文字列オブジェクトとなる.535行目以降では,オブジェクトが存在するかを確認し,存在しなかったら,536行目で新たにオブジェクトを生成し540行目で,生成したオブジェクトをunicode_latin1配列に格納している.もしも既に該当ASCII文字のオブジェクトが存在する場合は,単にリファレンスカウントをインクリメントして,それを返すだけである.

複数バイト文字列の扱い

最後に,入力バイトが1より大きかった場合と,入力文字列を指すポインタがNULLであった場合の処理を見てみる.

/* Objects/unicodeobject.c */
 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
 510 {
... /* 省略 */
 523     if (u != NULL) {
... /* 省略 */
 531         /* Single characters are shared when using this constructor.
 532            Restrict to ASCII, since the input must be UTF-8. */
 533         if (size == 1 && Py_CHARMASK(*u) < 128) {
... /* 省略 */
 543             return (PyObject *)unicode;
 544         }
 545
 546         return PyUnicode_DecodeUTF8(u, size, NULL);
 547     }
 548 
 549     unicode = _PyUnicode_New(size);
 550     if (!unicode)
 551         return NULL;
 552 
 553     return (PyObject *)unicode;
 554 }

546行目はバイト長が1より大きかった場合の処理であり,PyUnicode_DecodeUTF8マクロによりUTF-8からUCS-2へと文字コード変換された文字列オブジェクトが生成されて,それを返り値としている.

549行目以降では,sizeバイトの文字列オブジェクトを_PyUnicode_New()関数で生成して,それを返している.

今回のまとめ

文字列オブジェクト生成までの大体の流れはこんな感じになる.今回の重要な点は,から文字と,1文字のASCIIだけからなる文字列オブジェクトは,複数のインスタンスが生成されないということである.次は,_PyUnicode_New()と,PyUnicode_DecodeUTF8について見ていきたいと思う.