# Protocolo de Buffer

Certos objetos disponíveis em Python envolvem o acesso a uma matriz de memória subjacente ou * buffer *. Esses objetos incluem o built-in: class: bytes e: class:` bytearray`, e alguns tipos de extensão como: class: array.array. As bibliotecas de terceiros podem definir seus próprios tipos para fins especiais, como processamento de imagem ou análise numérica.

Embora cada um desses tipos tenha sua própria semântica, eles compartilham a característica comum de serem suportados por um buffer de memória possivelmente grande. É desejável, em algumas situações, acessar esse buffer diretamente e sem cópia intermediária.

Python fornece essa facilidade no nível C sob a forma de: ref: `buffer protocol 1 <bufferobjects>’. Este protocolo tem dois lados:

- do lado do produtor, um tipo pode exportar uma “interface de buffer” que permite que objetos desse tipo exponham informações sobre o buffer subjacente. Esta interface é descrita na seção: ref: buffer-structs;

- do lado do consumidor, vários meios estão disponíveis para obter o ponteiro para os dados subjacentes de um objeto (por exemplo, um parâmetro de método).

Objetos simples como `bytes` e `bytearray` expõem seu buffer subjacente em forma de byte-oriented. Outras formas são possíveis; por exemplo, os elementos expostos por uma `array.array` podem ser valores de vários bytes.

Um exemplo de consumidor da interface de buffer é o método: meth:~io.BufferedIOBase.write de objetos de arquivo: qualquer objeto que pode exportar uma série de bytes através da interface de buffer pode ser gravado em um arquivo. Enquanto: `write()` só precisa de acesso somente leitura aos conteúdos internos do objeto passado, outros métodos, tais como `readinto()` precisam de acesso de gravação ao conteúdo de seu argumento. A interface de buffer permite aos objetos permitir ou rejeitar seletivamente a exportação de buffers de leitura-gravação e somente leitura.

Existem duas maneiras para um usuário da interface de buffer adquirir um buffer em um objeto alvo:

- chamada `PyObject_GetBuffer()` com os parâmetros certos;

- chamada `PyArg_ParseTuple()` (ou um dos seus irmãos) com um dos `y*`, `w*` or `s*` format codes 1.

Em ambos os casos, `PyBuffer_Release()` deve ser chamado quando o buffer não é mais necessário. A falta de tal pode levar a várias questões, tais como vazamentos de recursos.

## Estrutura de Buffer

As estruturas de buffer (ou simplesmente “buffers”) são úteis como uma maneira de expor os dados binários de outro objeto para o programador Python. Eles também podem ser usados como um mecanismo de cópia silenciosa. Usando sua capacidade de fazer referência a um bloco de memória, é possível expor facilmente qualquer dado ao programador Python. A memória pode ser uma matriz grande e constante em uma extensão C, pode ser um bloco bruto de memória para manipulação antes de passar para uma biblioteca do sistema operacional, ou pode ser usado para transmitir dados estruturados no formato nativo e formato de memória .

Ao contrário da maioria dos tipos de dados expostos pelo intérprete Python, os buffers não são ponteiros `PyObject` mas sim estruturas C simples. Isso permite que eles sejam criados e copiados de forma muito simples. Quando um invólucro genérico em torno de um buffer é necessário, um objeto memoryview 1 pode ser criado.

Para obter instruções curtas sobre como escrever um objeto exportador, consulte Buffer Object Structures 1. Para obter um buffer, veja `PyObject_GetBuffer()`.

`Py_buffer`
  void *`buf`

    Um ponteiro para o início da estrutura lógica descrita pelos campos do buffer. Este pode ser qualquer local dentro do bloco de memória física subjacente do exportador. Por exemplo, com negativo `strides` o valor pode apontar para o final do bloco de memória.

    Para: termo: matrizes ‘contíguas’, o valor aponta para o início do bloco de memória.

  void *`obj`

    Uma nova referência ao objeto exportador. A referência é de propriedade do consumidor e automaticamente diminuída e definida como NULL por `PyBuffer_Release()`. O campo é o equivalente ao valor de retorno de qualquer função C-API padrão.

    Como um caso especial, para buffers * temporários * que são envolvidos por `PyMemoryView_FromBuffer()` ou `PyBuffer_FillInfo()` este campo é NULL. Em geral, a exportação de objetos NÃO DEVE usar este esquema.

  Py_ssize_t `len`

    `product(shape) * itemsize`. Para matrizes contíguas, este é o comprimento do bloco de memória subjacente. Para matrizes não contíguas, é o comprimento que a estrutura lógica teria se fosse copiado para uma representação contígua.

    Acessando `((char *)buf)[0] up to ((char *)buf)[len-1]` só é válido se o buffer tiver sido obtido por uma solicitação que garanta a contiguidade. Na maioria dos casos, esse pedido será `PyBUF_SIMPLE` ou `PyBUF_WRITABLE`.

  int `readonly`

    Um indicador de se o buffer é somente leitura. Este campo é controlado pelo sinalizador `PyBUF_WRITABLE`.

  Py_ssize_t `itemsize`

    Tamanho do item em bytes de um único elemento. Igual ao valor de:func:struct.calcsize chamado valores não-NULL `format`.

    Exceção importante: se um consumidor solicitar um buffer sem a `PyBUF_FORMAT` flag, `format` será definido como NULL, mas `itemsize` ainda tem o valor para o formato original.

    Se `shape` está presente, a igualdade `product(shape) * itemsize == len` ainda é válida e o usuário pode usar `itemsize` para navegar o buffer.

    Se:c:member:~Py_buffer.shape é NULL como resultado de uma `PyBUF_SIMPLE` ou uma solicitação `PyBUF_WRITABLE`, o consumidor deve ignorar `itemsize` e assume `itemsize == 1`.

  const char *`format`

    A NUL terminou string em :mod: sintaxe de estilo de módulo struct descrevendo o conteúdo de um único item. Se isso for NULL, `"B"` (bytes não assinados) é assumido.

    Este campo é controlado pelo sinalizador `PyBUF_FORMAT`.

  int `ndim`

    O número de dimensões que a memória representa como uma matriz n-dimensional. Se for `0`, `buf` aponta para um único item que representa um escalar. Neste caso, :c:membro:`~Py_buffer.shape`, `strides` e :c:member:`~Py_buffer.suboffsets DEVE ser NULL.

    A macro `PyBUF_MAX_NDIM` limita o número máximo de dimensões a 64. Os exportadores DEVEM respeitar esse limite, os consumidores de buffers multidimensionais DEVEM ser capazes de lidar com dimensões `PyBUF_MAX_NDIM`.

  Py_ssize_t *`shape`

    Uma matriz de `Py_ssize_t` do comprimento `ndim` indicando a forma da memória como uma matriz n-dimensional. Observe que a forma `shape[0] * ... * shape[ndim-1] * itemsize` DEVE ser igual a `len`.

    Os valores da forma são restritos a `shape[n] >= 0`. The case `shape[n] == 0` requer atenção especial. Veja arrays complexos para mais informações.

    A forma de acesso a matriz é de somente leitura para o usuário.

  Py_ssize_t *`strides`

    Uma matriz de `Py_ssize_t` de comprimento:c:member:~Py_buffer.ndim dando o número de bytes para saltar para obter um novo elemento em cada dimensão.

    Os valores de Stride podem ser qualquer número inteiro. Para arrays regulares, os passos são geralmente positivos, mas um consumidor DEVE ser capaz de lidar com o caso `strides[n] <= 0`. Veja complex arrays para mais informações.

    A matriz de passos é somente leitura para o consumidor.

  Py_ssize_t *`suboffsets`

    Uma matriz de `Py_ssize_t` de comprimento `ndim`. If `suboffsets[n] >= 0`, os valores armazenados ao longo da n-ésima dimensão são ponteiros e o valor suboffset determina quantos bytes para adicionar a cada ponteiro após desreferenciar. Um valor de subconjunto que é negativo indica que não deve ocorrer desratização (caminhar em um bloco de memória contíguo).

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

    Esse tipo de representação de matriz é usado pela Python Imaging Library (PIL). Veja complex arrays para obter mais informações sobre como acessar elementos dessa matriz.a matriz.

    A matriz de subconjuntos é somente leitura para o consumidor.

  void *`internal`

    Isso é para uso interno pelo objeto exportador. Por exemplo, isso pode ser re-moldado como um número inteiro pelo exportador e usado para armazenar bandeiras sobre se os conjuntos de forma, passos e suboffsets devem ou não ser liberados quando o buffer é liberado. O consumidor NÃO DEVE alterar esse valor.

## Tipos de solicitação do buffer

Os buffers geralmente são obtidos enviando uma solicitação de buffer para um objeto exportador via `PyObject_GetBuffer()`. Uma vez que a complexidade da estrutura lógica da memória pode variar drasticamente, o consumidor usa o argumento flags para especificar o tipo de buffer exato que pode manipular.

Todos `Py_buffer` são inequivocamente definidos pelo tipo de solicitação.

### campos independentes do pedido

Os seguintes campos não são influenciados por flags e devem sempre ser preenchidos com os valores corretos: `obj`, `buf`, `len`, `itemsize`, `ndim`.

### apenas em formato

> `PyBUF_WRITABLE`

>   Controla o campo `readonly`. Se configurado, o exportador DEVE fornecer um buffer gravável ou então reportar falha. Caso contrário, o exportador pode fornecer um buffer de somente leitura ou gravável, mas a escolha DEVE ser consistente para todos os consumidores.

> `PyBUF_FORMAT`

>   Controla o campo `format`. Se configurado, este campo DEVE ser preenchido corretamente. Caso contrário, este campo DEVE ser NULL.

:`PyBUF_WRITABLE` pode ser |’d para qualquer um dos sinalizadores na próxima seção. Uma vez que `PyBUF_WRITABLE` é definido como 0, `PyBUF_WRITABLE` pode ser usado como uma bandeira autônoma para solicitar um buffer simples gravável.

`PyBUF_FORMAT` pode ser |’d para qualquer um dos sinalizadores, exceto `PyBUF_SIMPLE`. O último já implica o formato `B` (bytes não assinados).

### forma, avanços, suboffsets

As bandeiras que controlam a estrutura lógica da memória estão listadas em ordem decrescente de complexidade. Observe que cada bandeira contém todos os bits das bandeiras abaixo.

Solicitação | Forma | Avanços | suboffsets
--- | --- | --- | ---

`PyBUF_INDIRECT`

sim

sim

se necessário

`PyBUF_STRIDES`

sim

sim

NULL

`PyBUF_ND`

sim

NULL

NULL

`PyBUF_SIMPLE`

NULL

NULL

NULL

### requisições contíguas

contiguity do C ou Fortran podem ser explicitamente solicitadas, com ou sem informação de avanço. Sem informação de avanço, o buffer deve ser C-contíguo.

Solicitação | Forma | Avanços | suboffsets | contig
--- | --- | --- | --- | ---

`PyBUF_C_CONTIGUOUS`

sim

sim

NULL

C

`PyBUF_F_CONTIGUOUS`

sim

sim

NULL

F

`PyBUF_ANY_CONTIGUOUS`

sim

sim

NULL

C ou F

`PyBUF_ND`

sim

NULL

NULL

C

### requisições compostas

Todas as requisições possíveis foram completamente definidas por alguma combinação dos sinalizadores na seção anterior. Por conveniência, o protocolo do buffer fornece combinações frequentemente utilizadas como sinalizadores únicos.

Na seguinte tabela U siginifica contíguidade indefinida. O consumidor deve chamar `PyBuffer_IsContiguous()` para determinar a contíguidade.

Solicitação | Forma | Avanços | suboffsets | contig | readonly | formato
--- | --- | --- | --- | --- | --- | ---

`PyBUF_FULL`

sim

sim

se necessário

U

0

sim

`PyBUF_FULL_RO`

sim

sim

se necessário

U

1 ou 0

sim

`PyBUF_RECORDS`

sim

sim

NULL

U

0

sim

`PyBUF_RECORDS_RO`

sim

sim

NULL

U

1 ou 0

sim

`PyBUF_STRIDED`

sim

sim

NULL

U

0

NULL

`PyBUF_STRIDED_RO`

sim

sim

NULL

U

1 ou 0

NULL

`PyBUF_CONTIG`

sim

NULL

NULL

C

0

NULL

`PyBUF_CONTIG_RO`

sim

NULL

NULL

C

1 ou 0

NULL

## Arrays Complexos

### NumPy-style: forma e avanços

A estrutura lógica de vetores do estilo NumPy é definida por `itemsize`, `ndim`, `shape` e `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);`

Como notado acima, `buf` pode apontar para qualquer localização dentro do bloco de memória em si. Um exportador pode verificar a validade de um buffer com essa função:

    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

### Estilo-PIL: forma, avanços e suboffsets

Além dos itens normais, uma matriz em estilo PIL pode conter ponteiros que devem ser seguidos para se obter o próximo elemento em uma dimensão. Por exemplo, a matriz tridimensional em C `char v[2][2][3]` também pode ser vista como um vetor de 2 ponteiros para duas matrizes bidimensionais: `char (*v[2])[2][3]`. Na representação por suboffsets, esses dois ponteiros podem ser embutidos no início de `buf`, apontando para duas matrizes `char x[2][3]` que podem estar localizadas em qualquer lugar na memória.

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;
    }

## Buffer-related functions

int `PyObject_CheckBuffer`( PyObject *obj)

  Return `1` if obj supports the buffer interface otherwise `0`. When `1` is returned, it doesn’t guarantee that `PyObject_GetBuffer()` will succeed. This function always succeeds.

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`.

  On success, fill in view, set `view->obj` to a new reference to exporter and return 0. In the case of chained buffer providers that redirect requests to a single object, `view->obj` MAY refer to this object instead of exporter (See Buffer Object Structures).

  Successful calls to `PyObject_GetBuffer()` must be paired with calls to `PyBuffer_Release()`, similar to `malloc()` and `free()`. Thus, after the consumer is done with the buffer, `PyBuffer_Release()` must be called exactly once.

void `PyBuffer_Release`( Py_buffer *view)

  Release the buffer view and decrement the reference count for `view->obj`. This function MUST be called when the buffer is no longer being used, otherwise reference leaks may occur.

  It is an error to call this function on a buffer that was not obtained via `PyObject_GetBuffer()`.

Py_ssize_t `PyBuffer_SizeFromFormat`(const char *)

  Return the implied `itemsize` from `format`. This function is not yet implemented.

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

  Return `1` if the memory defined by the view is C-style (order is `'C'`) or Fortran-style (order is `'F'`) contiguous or either one (order is `'A'`). Return `0` otherwise. This function always succeeds.

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'` (for C-style or Fortran-style ordering). `0` is returned on success, `-1` on error.

  This function fails if len != src->len.

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

  Fill the strides array with byte-strides of a contiguous (C-style if order is `'C'` or Fortran-style if order is `'F'`) array of the given shape with the given number of bytes per element.

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

  Handle buffer requests for an exporter that wants to expose buf of size len with writability set according to readonly. buf is interpreted as a sequence of unsigned bytes.

  The flags argument indicates the request type. This function always fills in view as specified by flags, unless buf has been designated as read-only and `PyBUF_WRITABLE` is set in flags.

  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.