# バッファプロトコル (buffer Protocol)

Pythonで利用可能ないくつかのオブジェクトは、下層にあるメモリ配列または buffer へのアクセスを提供します。このようなオブジェクトとして、組み込みの `bytes` や `bytearray` 、 `array.array` のようないくつかの拡張型が挙げられます。サードバーティのライブラリは画像処理や数値解析のような特別な目的のために、それら自身の型を定義することができます。

それぞれの型はそれ自身のセマンティクスを持ちますが、おそらく大きなメモリバッファからなるという共通の特徴を共有します。いくつかの状況では仲介するコピーを行うことなく直接バッファにアクセスすることが望まれます。

Pythonは buffer protocol の形式で C レベルの仕組みを提供します。このプロトコルには二つの側面があります:

- 提供する側では、ある型は、そのオブジェクトの下層にあるバッファに関する情報を提供できる "buffer インタフェース" をエクスポートすることができます。このインタフェースは バッファオブジェクト構造体 (buffer object structure) の節で説明します。

- 利用する側では、オブジェクトの下層にある生データへのポインタを得るいくつかの手段が利用できます（たとえばメソッド引数）。

`bytes` や `bytearray` などのシンプルなオブジェクトは、内部のバッファーをバイト列の形式で公開します。 バイト列以外の形式も利用可能です。例えば、 `array.array` が公開する要素はマルチバイト値になることがあります。

bufferインタフェースの利用者の一例は、ファイルオブジェクトの `write()` メソッドです: bufferインタフェースを通して一連のバイト列を提供できるどんなオブジェクトでもファイルに書き込むことができます。 `write()` は、その引数として渡されたオブジェクトの内部要素に対する読み出し専用アクセスのみを必要としますが、 `readinto()` のような他のメソッドでは、その引数の内容に対する書き込みアクセスが必要です。bufferインタフェースにより、オブジェクトは読み書き両方、読み出し専用バッファへのアクセスを許可するかそれとも拒否するか選択することができます。

bufferインタフェースの利用者には、対象となるオブジェクトのバッファを得る二つの方法があります:

- 正しい引数で `PyObject_GetBuffer()` を呼び出す;

- `PyArg_ParseTuple()` (またはその同族のひとつ) を `y*` 、 `w*` または `s*` format codes のいずれかとともに呼び出す。

どちらのケースでも、bufferが必要なくなった時に `PyBuffer_Release()` を呼び出さなければなりません。これを怠ると、リソースリークのような様々な問題につながる恐れがあります。

## buffer 構造体

バッファ構造体（または単純に "buffers"）は別のオブジェクトのバイナリデータをPythonプログラマに提供するのに便利です。これはまた、ゼロコピースライシング機構としても使用できます。このメモリブロックを参照する機能を使うことで、どんなデータでもとても簡単にPythonプログラマに提供することができます。メモリは、C 拡張の大きな配列定数かもしれませんし、オペレーティングシステムライブラリに渡す前のメモリブロックかもしれませんし、構造化データをネイティブのインメモリ形式受け渡すのに使用されるかもしれません。

Pythonインタプリタによって提供される多くのデータ型とは異なり、バッファは `PyObject` ポインタではなく、シンプルなC 構造体です。そのため、作成とコピーが非常に簡単に行えます。バッファの一般的なラッパーが必要なときは、 memoryview オブジェクトが作成されます。

エクスポートされるオブジェクトを書く方法の短い説明には、 Buffer Object Structures を参照してください。バッファを取得するには、 `PyObject_GetBuffer()` を参照してください。

`Py_buffer`
  void *`buf`

    バッファフィールドが表している論理構造の先頭を指すポインタ。バッファを提供するオブジェクトの下層物理メモリブロック中のどの位置にもなりえます。たとえば、たとえば `strides` が負だと、この値はメモリブロックの末尾かもしれません。

    連続 配列の場合この値はメモリブロックの先頭を指します。

  void *`obj`

    エクスポートされているオブジェクトの新しい参照。参照は消費者によって所有され、`PyBuffer_Release()` によって自動的にデクリメントされて NULL に設定されます。このフィールドは標準的なC-API 関数の戻り値と等価です。

    `PyMemoryView_FromBuffer()` または `PyBuffer_FillInfo()` によってラップされた 一時的な バッファである特別なケースでは、このフィールドは NULL です。一般的に、エクスポートオブジェクトはこの方式を使用してはなりません。

  Py_ssize_t `len`

    `product(shape) * itemsize`。contiguous配列では、下層のメモリブロックの長さになります。非contiguous 配列では、contiguous表現にコピーされた場合に論理構造がもつ長さです。

    `((char *)buf)[0]` から `((char *)buf)[len-1]` の範囲へのアクセスは、連続性 (contiguity) を保証するリクエストによって取得されたバッファに対してのみ許されます。 多くの場合に、そのようなリクエストは `PyBUF_SIMPLE` または `PyBUF_WRITABLE` です。

  int `readonly`

    バッファが読み出し専用であるか示します。このフィールドは `PyBUF_WRITABLE` フラグで制御できます。

  Py_ssize_t `itemsize`

    要素一つ分のbyte単位のサイズ。 `struct.calcsize()` を非NULLの `format` 値に対して呼び出した結果と同じです。

    重要な例外: 消費者が `PyBUF_FORMAT` フラグを設定することなくバッファを要求した場合、 `format` は NULL に設定されます。 しかし `itemsize` は元のフォーマットに従った値を保持します。

    `shape` が存在する場合、 `product(shape) * itemsize == len` の等式が守られ、利用者は `itemsize` を buffer を読むために利用できます。

    `PyBUF_SIMPLE` または `PyBUF_WRITABLE` で要求した結果、 `shape` が NULL であれば、消費者は `itemsize` を無視して `itemsize == 1` と見なさなければなりません。

  const char *`format`

    要素一つ分の内容を指定する、 `struct` モジュールスタイル文法の、 NUL 終端文字列。 このポインタの値が NULL なら、 `"B"` (符号無しバイト) として扱われます。

    このフィールドは `PyBUF_FORMAT` フラグによって制御されます。

  int `ndim`

    メモリがN次元配列を表している時の次元数。 `0` の場合、 `buf` はスカラ値を表す1つの要素を指しています。 この場合、 `shape`, `strides`, `suboffsets` は NULL でなければなりません。

    `PyBUF_MAX_NDIM` は次元数の最大値を64に制限しています。 提供側はこの制限を尊重しなければなりません。 多次元配列の消費側は `PyBUF_MAX_NDIM` 次元までを扱えるようにするべきです。

  Py_ssize_t *`shape`

    メモリ上のN次元配列の形を示す、長さが `ndim` である `Py_ssize_t` の配列です。 `shape[0] * ... * shape[ndim-1] * itemsize` は `len` と等しくなければなりません。

    shape の値は `shape[n] >= 0` に制限されます。 `shape[n] == 0` の場合に特に注意が必要です。 詳細は complex arrays を参照してください。

    shepe (形状) 配列は利用者からは読み出し専用です。

  Py_ssize_t *`strides`

    各次元において新しい値を得るためにスキップするバイト数を示す、長さ `ndim` の `Py_ssize_t` の配列。

    ストライド値は、任意の整数を指定できます。規定の配列では、ストライドは通常でいけば有効です。しかし利用者は、`strides[n] <= 0` のケースを処理することができる必要があります。詳細については complex arrays を参照してください。

    消費者にとって、この strides 配列は読み出し専用です。

  Py_ssize_t *`suboffsets`

    `Py_ssize_t` 型の要素を持つ長さ `ndim` の配列。 `suboffsets[n] >= 0` の場合は、 n 番目の次元に沿って保存されている値はポインタで、 suboffset 値は各ポインタの参照を解決した後に何バイト加えればいいかを示しています。 suboffset の値が負の数の場合は、ポインタの参照解決は不要 (連続したメモリブロック内に直接配置されいる) ということになります。

    全ての suboffset が負数の場合 (つまり参照解決が不要) な場合、このフィールドは NULL (デフォルト値) でなければなりません。

    この種の配列表現は Python Imaging Library (PIL) で使われています。 このような配列で要素にアクセスする方法についてさらに詳しことは complex arrays を参照してください。

    消費者にとって、suboffsets 配列は読み出し専用です。

  void *`internal`

    バッファを提供する側のオブジェクトが内部的に利用するための変数です。例えば、提供側はこの変数に整数型をキャストして、shape, strides, suboffsets といった配列をバッファを開放するときに同時に解放するべきかどうかを管理するフラグに使うことができるでしょう。バッファを受け取る側は、この値を決して変更してはなりません。

## バッファリクエストのタイプ

バッファは通常、 `PyObject_GetBuffer()` を使うことで、エクスポートするオブジェクトにバッファリクエストを送ることで得られます。メモリの論理的な構造の複雑性は多岐にわたるため、消費者は flags 引数を使って、自身が扱えるバッファの種類を指定します。

`Py_buffer` の全フィールドは、リクエストの種類によって曖昧さを残さずに定義されます。

### リクエストに依存しないフィールド

下記のフィールドは flags の影響を受けずに、常に正しい値で設定されます。: `obj`, `buf`, `len`, `itemsize`, `ndim`.

### readonly, format

> `PyBUF_WRITABLE`

>   `readonly` フィールドを制御します。もしこのフラグが設定されている場合、exporter は、書き込み可能なバッファを提供するか、さもなければ失敗を報告しなければなりません。フラグが設定されていない場合、exporterは、読み出し専用と書き込み可能なバッファのどちらを提供しても構いませんが、どちらで提供するかどうかは全ての消費者に対して一貫性がなければなりません。

> `PyBUF_FORMAT`

>   `format` フィールドを制御します。もしフラグが設定されていれば、このフィールドを正しく埋めなければなりません。フラグが設定されていなければ、このフィールドを NULL に設定しなければなりません。

`PyBUF_WRITABLE` は、次の節に出てくるどのフラグとも | を取ってかまいません。 `PyBUF_SIMPLE` は 0 と定義されているので、 `PyBUF_WRITABLE` は単純な書き込み可能なバッファを要求する単独のフラグとして使えます。

`PyBUF_FORMAT` は、`PyBUF_SIMPLE` 以外のどのフラグとも | を取ってかまいません。 後者のフラグは `B` (符号なしバイト) フォーマットを既に指示しています。

### shape, strides, suboffsets

このフラグは、以下で複雑性が大きい順に並べたメモリの論理的な構造を制御します。個々のフラグは、それより下に記載されたフラグのすべてのビットを含むことに注意してください。

リクエスト | shape | strides | suboffsets
--- | --- | --- | ---

`PyBUF_INDIRECT`

yes

yes

必要な場合

`PyBUF_STRIDES`

yes

yes

NULL

`PyBUF_ND`

yes

NULL

NULL

`PyBUF_SIMPLE`

NULL

NULL

NULL

### 隣接性のリクエスト

ストライドの情報があってもなくても、C または Fortran の 連続性 がはっきりと要求される可能性があります。 ストライド情報なしに、バッファーは C と隣接している必要があります。

リクエスト | shape | strides | suboffsets | contig
--- | --- | --- | --- | ---

`PyBUF_C_CONTIGUOUS`

yes

yes

NULL

C

`PyBUF_F_CONTIGUOUS`

yes

yes

NULL

F

`PyBUF_ANY_CONTIGUOUS`

yes

yes

NULL

C か F

`PyBUF_ND`

yes

NULL

NULL

C

### 複合リクエスト

有り得る全てのリクエストの値は、前の節でのフラグの組み合わせで網羅的に定義されています。 便利なように、バッファープロトコルでは頻繁に使用される組み合わせを単一のフラグとして提供してます。

次のテーブルの U は連続性が未定義であることを表します。 利用者は `PyBuffer_IsContiguous()` を呼び出して連続性を判定する必要があるでしょう。

リクエスト | shape | strides | suboffsets | contig | readonly | format
--- | --- | --- | --- | --- | --- | ---

`PyBUF_FULL`

yes

yes

必要な場合

U

0

yes

`PyBUF_FULL_RO`

yes

yes

必要な場合

U

1 か 0

yes

`PyBUF_RECORDS`

yes

yes

NULL

U

0

yes

`PyBUF_RECORDS_RO`

yes

yes

NULL

U

1 か 0

yes

`PyBUF_STRIDED`

yes

yes

NULL

U

0

NULL

`PyBUF_STRIDED_RO`

yes

yes

NULL

U

1 か 0

NULL

`PyBUF_CONTIG`

yes

NULL

NULL

C

0

NULL

`PyBUF_CONTIG_RO`

yes

NULL

NULL

C

1 か 0

NULL

## 複雑な配列

### NumPy スタイル: shape, strides

NumPy スタイルの配列の論理的構造は `itemsize`, `ndim`, `shape`, `strides` で定義されます。

`ndim == 0` の場合は、 `buf` が指すメモリの場所は、サイズが `itemsize` のスカラ値として解釈されます。 この場合、 `shape` と `strides` の両方とも NULL です。

`strides` が NULL の場合は、配列は標準の n 次元 C 配列として解釈されます。 そうでない場合は、利用者は次のように n 次元配列にアクセスしなければなりません:

> `ptr = (char *)buf + indices[0] * strides[0] + ... + indices[n-1] * strides[n-1]` `item = *((typeof(item) *)ptr);`

上記のように、 `buf` はメモリブロック内のどの場所でも指すことが可能です。エクスポーターはこの関数を使用することによってバッファの妥当性を確認出来ます。

    def verify_structure(memlen, itemsize, ndim, shape, strides, offset):
        """Verify that the parameters represent a valid array within
           the bounds of the allocated memory:
               char *mem: start of the physical memory block
               memlen: length of the physical memory block
               offset: (char *)buf - mem
        """
        if offset % itemsize:
            return False
        if offset < 0 or offset+itemsize > memlen:
            return False
        if any(v % itemsize for v in strides):
            return False

        if ndim <= 0:
            return ndim == 0 and not shape and not strides
        if 0 in shape:
            return True

        imin = sum(strides[j]*(shape[j]-1) for j in range(ndim)
                   if strides[j] <= 0)
        imax = sum(strides[j]*(shape[j]-1) for j in range(ndim)
                   if strides[j] > 0)

        return 0 <= offset+imin and offset+imax+itemsize <= memlen

### PIL スタイル: shape, strides, suboffsets

PIL スタイルの配列では通常の要素の他に、ある次元の上で次の要素を取得するために辿るポインタを持てます。 例えば、通常の3次元 C 配列 `char v[2][2][3]` は、2次元配列への 2 つのポインタからなる配列 `char (*v[2])[2][3]` と見ることもできます。 suboffset 表現では、これらの 2 つのポインタは `buf` の先頭に埋め込め、メモリのどこにでも配置できる 2 つの `char x[2][3]` 配列を指します。

次の例は、 strides も suboffsets も NULL でない場合の、N 次元インデックスによって指されている N 次元配列内の要素へのポインタを返す関数です。

    void *get_item_pointer(int ndim, void *buf, Py_ssize_t *strides,
                           Py_ssize_t *suboffsets, Py_ssize_t *indices) {
        char *pointer = (char*)buf;
        int i;
        for (i = 0; i < ndim; i++) {
            pointer += strides[i] * indices[i];
            if (suboffsets[i] >=0 ) {
                pointer = *((char**)pointer) + suboffsets[i];
            }
        }
        return (void*)pointer;
    }

## バッファ関連の関数

int `PyObject_CheckBuffer`( PyObject *obj)

  obj がbuffer インタフェースをサポートしている場合は `1` を返し、そうでない場合は `0` を返します。 `1` を返したとしても、 `PyObject_GetBuffer()` が成功することは保証されません。 この関数は常に成功します。

int `PyObject_GetBuffer`( PyObject *exporter, Py_buffer *view, int flags)

  exporter に flags で指定された方法で view を埋めるように要求します。 もし exporter が指定されたとおりにバッファを提供できない場合、 `PyExc_BufferError` を送出し、 `view->obj` を NULL に設定した上で、 `-1` を返さなければなりません。

  成功したときは、 view を埋め、 `view->obj` に exporter への新しい参照を設定し、0を返します。チェイン状のバッファプロバイダがリクエストを単一のオブジェクトにリダイレクトするケースでは、 `view->obj` は exporter の代わりにこのオブジェクトを参照します (バッファオブジェクト構造体 を参照してください)。

  `malloc()` と `free()` のように、呼び出しに成功した `PyObject_GetBuffer()` と対になる `PyBuffer_Release()` の呼び出しがなけれなればなりません。 従って、バッファの利用が済んだら `PyBuffer_Release()` が厳密に1回だけ呼び出されなければなりません。

void `PyBuffer_Release`( Py_buffer *view)

  バッファ view を解放し、 `view->obj` の参照カウントを1つ減らします。 この関数はバッファが使われることがなくなったときに呼び出さなければならず、そうしないと参照のリークが起こり得ます。

  `PyObject_GetBuffer()` を通して取得していないバッファに対してこの関数を呼び出すのは間違いです。

Py_ssize_t `PyBuffer_SizeFromFormat`(const char *)

  `itemsize` の値を `format` から計算して返します。 この関数はまだ実装されていません。

int `PyBuffer_IsContiguous`( Py_buffer *view, char order)

  view で定義されているメモリが、 C スタイル (order == `'C'`) のときか、 Fortran スタイル (order == `'F'`) 連続 (contiguous) のときか、そのいずれか (order == `'A'`) であれば `1` を返します。 それ以外の場合は `0` を返します。 この関数は常に成功します。

int `PyBuffer_ToContiguous`(void *buf, Py_buffer *src, Py_ssize_t len, char order)

  src から len バイトを連続表現で buf 上にコピーします。 order は `'C'` または `'F'` (C スタイルまたは Fortran スタイルの順序) が指定できます。 成功したら `0` が返り、エラーなら `-1` が返ります。

  len != src->len の場合、この関数は失敗します。

void `PyBuffer_FillContiguousStrides`(int ndims, Py_ssize_t *shape, Py_ssize_t *strides, int itemsize, char order)

  strides 配列を、 itemsize の大きさの要素がバイト単位の、 shape の形をした連続な (contiguous) (order が `'C'` なら C-style 、 `'F'` なら Fortran-style の) 多次元配列として埋める。

int `PyBuffer_FillInfo`( Py_buffer *view, PyObject *exporter, void *buf, Py_ssize_t len, int readonly, int flags)

  サイズが len の buf を readonly に従った書き込み可/不可の設定で公開するバッファリクエストを処理します。 buf は符号無しバイトの列として解釈されます。

  flags 引数はリクエストのタイプを示します。 この関数は、 buf が読み出し専用と指定されていて、 flags に `PyBUF_WRITABLE` が設定されていない限り、常にフラグに指定された通りに view を埋めます。

  成功したときは、 `view->obj` に exporter への新しい参照を設定し、 0 を返します。 失敗したときは、 `PyExc_BufferError` を送出し、 `view->obj` に NULL を設定し、 `-1` を返します;

  この関数を getbufferproc の一部として使う場合には、 exporter はエクスポートするオブジェクトに設定しなければならず、さらに flags は変更せずに渡さなければなりません。 そうでない場合は、 exporter は NULL でなければなりません。