버퍼 프로토콜
*************

파이썬에서 사용할 수 있는 어떤 객체는 하부 메모리 배열 또는 *버퍼*에
대한 액세스를 감쌉니다. 이러한 객체에는 내장 "bytes" 와 "bytearray",
그리고 "array.array"와 같은 일부 확장형이 포함됩니다. 제삼자 라이브러
리도 이미지 처리나 수치 해석과 같은 특수한 용도로 자체 형을 정의할 수
있습니다.

이러한 형은 각각 고유의 의미가 있지만, (아마도) 큰 메모리 버퍼에 의해
뒷받침되는 공통된 특징을 공유합니다. 어떤 상황에서는 중간 복사 없이 직
접 버퍼에 액세스하는 것이 바람직합니다.

파이썬은 C 수준에서 버퍼 프로토콜 형식으로 이러한 기능을 제공합니다.
이 프로토콜에는 두 가지 측면이 있습니다:

* 생산자 측에서는, 형이 "버퍼 인터페이스"를 내보낼 수 있는데, 그 형의
  객체가 하부 버퍼의 정보를 노출할 수 있게 합니다. 이 인터페이스는
  Buffer Object Structures 절에서 설명됩니다.

* 소비자 측에서는, 객체의 원시 하부 데이터에 대한 포인터를 얻기 위해
  여러 가지 방법을 사용할 수 있습니다 (예를 들어 메서드 매개 변수).

"bytes" 와 "bytearray"와 같은 간단한 객체는 하부 버퍼를 바이트 지향 형
식으로 노출합니다. 다른 형태도 가능합니다; 예를 들어, "array.array"에
의해 노출되는 요소는 멀티 바이트 값이 될 수 있습니다.

버퍼 인터페이스의 소비자 예는 파일 객체의 "write()" 메서드입니다: 버퍼
인터페이스를 통해 일련의 바이트를 내보낼 수 있는 모든 객체는 파일에 기
록될 수 있습니다. "write()"가 전달된 객체의 내부 내용에 대한 읽기 전용
액세스만 필요하지만, "readinto()"와 같은 다른 메서드는 인자의 내용에
쓰기 액세스가 필요합니다. 버퍼 인터페이스는 객체가 읽기-쓰기와 읽기 전
용 버퍼를 선택적으로 허용하거나 거부할 수 있도록 합니다.

버퍼 인터페이스의 소비자가 대상 객체에 대해 버퍼를 얻는 방법에는 두 가
지가 있습니다:

* 올바른 매개 변수로 "PyObject_GetBuffer()"를 호출합니다;

* "y*", "w*" 또는 "s*" 형식 코드 중 하나를 사용하여
  "PyArg_ParseTuple()"(또는 그 형제 중 하나)을 호출합니다.

두 경우 모두, 버퍼가 더는 필요하지 않으면 "PyBuffer_Release()"를 호출
해야 합니다. 그렇게 하지 않으면 자원 누수와 같은 다양한 문제가 발생할
수 있습니다.


버퍼 구조체
===========

버퍼 구조체(또는 단순히 "버퍼")는 다른 객체의 바이너리 데이터를 파이썬
프로그래머에게 노출하는 방법으로 유용합니다. 또한, 복사 없는(zero-
copy) 슬라이싱 메커니즘으로 사용할 수 있습니다. 메모리 블록을 참조하는
능력을 사용해서, 임의의 데이터를 파이썬 프로그래머에게 아주 쉽게 노출
할 수 있습니다. 메모리는 C 확장의 큰 상수 배열일 수 있으며, 운영 체제
라이브러리로 전달되기 전에 조작하기 위한 원시 메모리 블록일 수도 있고,
네이티브 인 메모리(in-memory) 형식으로 구조화된 데이터를 전달하는 데
사용될 수도 있습니다.

파이썬 인터프리터가 노출하는 대부분의 데이터형과 달리, 버퍼는
"PyObject" 포인터가 아니라 단순한 C 구조체입니다. 이를 통해 매우 간단
하게 만들고 복사할 수 있습니다. 버퍼를 감싸는 일반 래퍼가 필요할 때는,
메모리 뷰 객체를 만들 수 있습니다.

제공하는(exporting) 객체를 작성하는 간단한 지침은 버퍼 객체 구조체를
참조하십시오. 버퍼를 얻으려면, "PyObject_GetBuffer()"를 참조하십시오.

Py_buffer

   void *buf

      버퍼 필드에 의해 기술된 논리적 구조의 시작을 가리키는 포인터. 이
      것은 제공자(exporter)의 하부 물리적 메모리 블록 내의 모든 위치일
      수 있습니다. 예를 들어, 음의 "strides"를 사용하면 값이 메모리 블
      록의 끝을 가리킬 수 있습니다.

      *연속* 배열의 경우, 값은 메모리 블록의 시작을 가리킵니다.

   void *obj

      A new reference to the exporting object. The reference is owned
      by the consumer and automatically decremented and set to "NULL"
      by "PyBuffer_Release()". The field is the equivalent of the
      return value of any standard C-API function.

      As a special case, for *temporary* buffers that are wrapped by
      "PyMemoryView_FromBuffer()" or "PyBuffer_FillInfo()" this field
      is "NULL". In general, exporting objects MUST NOT use this
      scheme.

   Py_ssize_t len

      "product(shape) * itemsize". 연속 배열의 경우, 하부 메모리 블록
      의 길이입니다. 불연속 배열의 경우, 연속 표현으로 복사된다면 논리
      적 구조체가 갖게 될 길이입니다.

      "((char *)buf)[0] 에서 ((char *)buf)[len-1]" 범위의 액세스는 연
      속성을 보장하는 요청으로 버퍼가 확보된 경우에만 유효합니다. 대부
      분 이러한 요청은 "PyBUF_SIMPLE" 또는 "PyBUF_WRITABLE"입니다.

   int readonly

      버퍼가 읽기 전용인지를 나타내는 표시기입니다. 이 필드는
      "PyBUF_WRITABLE" 플래그로 제어됩니다.

   Py_ssize_t itemsize

      Item size in bytes of a single element. Same as the value of
      "struct.calcsize()" called on non-"NULL" "format" values.

      Important exception: If a consumer requests a buffer without the
      "PyBUF_FORMAT" flag, "format" will be set to  "NULL",  but
      "itemsize" still has the value for the original format.

      "shape"이 있으면, "product(shape) * itemsize == len" 동치가 계속
      성립하고 소비자는 "itemsize"를 사용하여 버퍼를 탐색할 수 있습니
      다.

      If "shape" is "NULL" as a result of a "PyBUF_SIMPLE" or a
      "PyBUF_WRITABLE" request, the consumer must disregard "itemsize"
      and assume "itemsize == 1".

   const char *format

      A *NUL* terminated string in "struct" module style syntax
      describing the contents of a single item. If this is "NULL",
      ""B"" (unsigned bytes) is assumed.

      이 필드는 "PyBUF_FORMAT" 플래그로 제어됩니다.

   int ndim

      The number of dimensions the memory represents as an
      n-dimensional array. If it is "0", "buf" points to a single item
      representing a scalar. In this case, "shape", "strides" and
      "suboffsets" MUST be "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[n] >= 0"로 제한됩니다. "shape[n] == 0"인 경우는
      특별한 주의가 필요합니다. 자세한 정보는 복잡한 배열을 참조하십시
      오.

      shape 배열은 소비자에게 읽기 전용입니다.

   Py_ssize_t *strides

      각 차원에서 새 요소를 가져오기 위해 건너뛸 바이트 수를 제공하는
      길이 "ndim"의 "Py_ssize_t" 배열.

      스트라이드 값은 임의의 정수일 수 있습니다. 일반 배열의 경우, 스
      트라이드는 보통 양수이지만, 소비자는 "strides[n] <= 0"인 경우를
      처리할 수 있어야 합니다. 자세한 내용은 복잡한 배열을 참조하십시
      오.

      strides 배열은 소비자에게 읽기 전용입니다.

   Py_ssize_t *suboffsets

      길이 "ndim"의 "Py_ssize_t" 배열. "suboffsets[n] >= 0" 면, n 번째
      차원을 따라 저장된 값은 포인터이고 서브 오프셋 값은 역참조(de-
      referencing) 후 각 포인터에 더할 바이트 수를 나타냅니다. 음의 서
      브 오프셋 값은 역참조(de-referencing)가 발생하지 않아야 함을 나
      타냅니다 (연속 메모리 블록에서의 스트라이드).

      If all suboffsets are negative (i.e. no de-referencing is
      needed), then this field must be "NULL" (the default value).

      이 유형의 배열 표현은 파이썬 이미징 라이브러리(PIL)에서 사용됩니
      다. 이러한 배열 요소에 액세스하는 방법에 대한 자세한 내용은 복잡
      한 배열을 참조하십시오.

      suboffsets 배열은 소비자에게 읽기 전용입니다.

   void *internal

      이것은 제공하는(exporting) 객체에 의해 내부적으로 사용됩니다. 예
      를 들어, 이것은 제공자(exporter)가 정수로 다시 캐스팅할 수 있으
      며, 버퍼가 해제될 때 shape, strides 및 suboffsets 배열을 해제해
      야 하는지에 대한 플래그를 저장하는 데 사용됩니다. 소비자가 이 값
      을 변경해서는 안 됩니다.


버퍼 요청 유형
==============

버퍼는 대개 "PyObject_GetBuffer()"를 통해 제공하는(exporting) 객체로
버퍼 요청을 보내서 얻습니다. 메모리의 논리적 구조의 복잡성이 크게 다를
수 있으므로, 소비자는 처리할 수 있는 정확한 버퍼 유형을 지정하기 위해
*flags* 인자를 사용합니다.

모든 "Py_buffer" 필드는 요청 유형에 의해 모호하지 않게 정의됩니다.


요청 독립적 필드
----------------

다음 필드는 *flags*의 영향을 받지 않고 항상 올바른 값으로 채워져야 합
니다: "obj", "buf", "len", "itemsize", "ndim".


readonly, format
----------------

   PyBUF_WRITABLE

      "readonly" 필드를 제어합니다. 설정되면, 제공자는 반드시 쓰기 가
      능한 버퍼를 제공하거나 실패를 보고해야 합니다. 그렇지 않으면, 제
      공자는 읽기 전용 버퍼나 쓰기 가능 버퍼를 제공할 수 있지만, 모든
      소비자에 대해 일관성을 유지해야 합니다.

   PyBUF_FORMAT

      Controls the "format" field. If set, this field MUST be filled
      in correctly. Otherwise, this field MUST be "NULL".

"PyBUF_WRITABLE"은 다음 섹션의 모든 플래그와 | 될 수 있습니다.
"PyBUF_SIMPLE"이 0으로 정의되므로, "PyBUF_WRITABLE"은 독립형 플래그로
사용되어 간단한 쓰기 가능한 버퍼를 요청할 수 있습니다.

"PyBUF_FORMAT"은 "PyBUF_SIMPLE"을 제외한 임의의 플래그와 | 될 수 있습
니다. 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 나 포트란 *연속성*을 명시적으로 요청할 수 있는데, 스트라이드 정보를
포함하기도 그렇지 않기도 합니다. 스트라이드 정보가 없으면, 버퍼는 C-연
속이어야 합니다.

+-------------------------------------+---------+-----------+--------------+----------+
| 요청                                | shape   | strides   | suboffsets   | 연속성   |
|=====================================|=========|===========|==============|==========|
| 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   | 연속성   | 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"로 정의됩니다.

If "ndim == 0", the memory location pointed to by "buf" is interpreted
as a scalar of size "itemsize". In that case, both "shape" and
"strides" are "NULL".

If "strides" is "NULL", the array is interpreted as a standard
n-dimensional C-array. Otherwise, the consumer must access an
n-dimensional array as follows:

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

위에서 언급했듯이, "buf"는 실제 메모리 블록 내의 모든 위치를 가리킬 수
있습니다. 제공자(exporter)는 이 함수로 버퍼의 유효성을 검사 할 수 있습
니다:

   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-차원 배열을 가리키는 2개의 포인터 배열로
볼 수도 있습니다: "char (*v[2])[2][3]". suboffsets 표현에서, 이 두 포
인터는 "buf"의 시작 부분에 임베드 될 수 있는데, 메모리의 어느 위치 에
나 배치될 수 있는 두 개의 "char x[2][3]" 배열을 가리킵니다.

Here is a function that returns a pointer to the element in an N-D
array pointed to by an N-dimensional index when there are both
non-"NULL" strides and suboffsets:

   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*가 버퍼 인터페이스를 지원하면 "1"을 반환하고, 그렇지 않으면
   "0"을 반환합니다. "1"이 반환될 때, "PyObject_GetBuffer()"가 성공할
   것이라고 보장하지는 않습니다. 이 함수는 항상 성공합니다.

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

   Send a request to *exporter* to fill in *view* as specified by
   *flags*. If the exporter cannot provide a buffer of the exact type,
   it MUST raise "PyExc_BufferError", set "view->obj" to "NULL" and
   return "-1".

   성공하면, *view*를 채우고, "view->obj"를 *exporter*에 대한 새 참조
   로 설정하고, 0을 반환합니다. 요청을 단일 객체로 리디렉션하는 연결된
   (chained) 버퍼 공급자의 경우, "view->obj"는 *exporter* 대신 이 객체
   를 참조할 수 있습니다 (버퍼 객체 구조체를 보세요).

   "PyObject_GetBuffer()"에 대한 성공적인 호출은 "PyBuffer_Release()"
   에 대한 호출과 쌍을 이루어야 합니다, "malloc()"과 "free()"와 유사합
   니다. 따라서, 소비자가 버퍼로 작업한 후에는, "PyBuffer_Release()"를
   정확히 한 번 호출해야 합니다.

void PyBuffer_Release(Py_buffer *view)

   버퍼 *view*를 해제하고 "view->obj"에 대한 참조 횟수를 감소시킵니다.
   버퍼가 더는 사용되지 않을 때, 이 함수를 반드시 호출해야 합니다. 그
   렇지 않으면 참조 누수가 발생할 수 있습니다.

   "PyObject_GetBuffer()"를 통해 얻지 않은 버퍼에 이 함수를 호출하는
   것은 에러입니다.

Py_ssize_t PyBuffer_SizeFromFormat(const char *)

   "format"이 암시하는 "itemsize"를 반환합니다. 이 함수는 아직 구현되
   지 않았습니다.

int PyBuffer_IsContiguous(Py_buffer *view, char order)

   *view*로 정의된 메모리가 C 스타일(*order*가 "'C'")이나 포트란 스타
   일(*order*가 "'F'") *연속*이거나 둘 중 하나(*order*가 "'A'")면 "1"
   을 반환합니다. 그렇지 않으면 "0"을 반환합니다. 이 함수는 항상 성공
   합니다.

void* PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices)

   Get the memory area pointed to by the *indices* inside the given
   *view*. *indices* must point to an array of "view->ndim" indices.

int PyBuffer_FromContiguous(Py_buffer *view, void *buf, Py_ssize_t len, char fort)

   Copy contiguous *len* bytes from *buf* to *view*. *fort* can be
   "'C'" or "'F'" (for C-style or Fortran-style ordering). "0" is
   returned on success, "-1" on error.

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

   Copy *len* bytes from *src* to its contiguous representation in
   *buf*. *order* can be "'C'" or "'F'" or "'A'" (for C-style or
   Fortran-style ordering or either one). "0" is returned on success,
   "-1" on error.

   이 함수는 *len* != *src->len*이면 실패합니다.

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

   *strides* 배열을 주어진 요소당 바이트 수와 주어진 shape 으로 *연속*
   (*order*가 "'C'"면 C 스타일, *order*가 "'F'"면 포트란 스타일) 배열
   의 바이트 스트라이드로 채웁니다.

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

   *readonly*에 따라 쓰기 가능성이 설정된 *len* 크기의 *buf*를 노출하
   려는 제공자(exporter)에 대한 버퍼 요청을 처리합니다. *buf*는 부호
   없는 바이트의 시퀀스로 해석됩니다.

   *flags* 인자는 요청 유형을 나타냅니다. 이 함수는 *buf*가 읽기 전용
   으로 지정되고 "PyBUF_WRITABLE"이 *flags*에 설정되어 있지 않으면, 항
   상 플래그가 지정하는 대로 *view*를 채웁니다.

   On success, set "view->obj" to a new reference to *exporter* and
   return 0. Otherwise, raise "PyExc_BufferError", set "view->obj" to
   "NULL" and return "-1";

   If this function is used as part of a getbufferproc, *exporter*
   MUST be set to the exporting object and *flags* must be passed
   unmodified. Otherwise, *exporter* MUST be "NULL".
