Protocolo de buffer
*******************

Certos objetos disponíveis no Python envolvem o acesso a um vetor ou
*buffer* de memória subjacente. Esses objetos incluem as classes
"bytes" e "bytearray" embutidas, e alguns tipos de extensão como
"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 protocolo de
buffer. 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 Buffer Object
  Structures;

* 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 uma forma orientada a byte. 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 "write()"
de objetos 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 e escrita e de
somente leitura.

Existem duas maneiras para um consumidor da interface de buffer
adquirir um buffer em um objeto alvo:

* chamar "PyObject_GetBuffer()" com os parâmetros certos;

* chamar "PyArg_ParseTuple()" (ou um dos seus irmãos) com um dos
  códigos de formatação "y*", "w*" ou "s*".

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ários
problemas, 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 interpretador
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 pode ser criado.

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

type 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 vetores *contíguos*, o valor aponta para o início do bloco
      de memória.

   PyObject *obj

      Uma nova referência ao objeto sendo exporta. A referência
      pertence ao consumidor e é automaticamente liberada (por
      exemplo, a contagem de referências é decrementada) e é atribuída
      para "NULL"  por "PyBuffer_Release()". O campo equivale ao valor
      de retorno de qualquer função padrão da API C.

      Como um caso especial, para buffers *temporários* que são
      encapsulados por "PyMemoryView_FromBuffer()" ou
      "PyBuffer_FillInfo()" esse campo é "NULL". Em geral, objetos
      exportadores NÃO DEVEM usar esse 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

      O tamanho do item em bytes de um único elemento. O mesmo que o
      valor de "struct.calcsize()" chamado em valores não "NULL" de
      "format".

      Exceção importante: Se um consumidor requisita um buffer sem
      sinalizador "PyBUF_FORMAT", "format" será definido como "NULL",
      mas "itemsize" ainda terá seu 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 "shape" é "NULL" como resultado de uma "PyBUF_SIMPLE" ou uma
      requisição "PyBUF_WRITABLE", o consumidor deve ignorar
      "itemsize" e presumir "itemsize == 1".

   const char *format

      Uma string terminada por *NUL* no estilo de sintaxe de módulo
      "struct" descrevendo os conteúdos de um único item. Se isso é
      "NULL", ""B"" (unsigned bytes) é assumido.

      Este campo é controlado pelo sinalizador "PyBUF_FORMAT".

   int ndim

      O número de dimensões que a memória representa como um vetor
      n-dimensional. Se é "0", "buf" aponta para um único item
      representando um escalar. Neste caso, "shape", "strides" e
      "suboffsets" DEVEM 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 complex arrays
      para mais informações.

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

   Py_ssize_t *strides

      Um vetor de "Py_ssize_t" de comprimento "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". Se
      "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 suboffset que é negativo indica que não deve ocorrer
      desreferenciação (caminhando em um bloco de memória contíguo).

      Se todos os subconjuntos forem negativos (ou seja, não é
      necessário fazer referência), então este campo deve ser "NULL"
      (o valor padrão).

      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   | subconjuntos |
|===============================|=========|===========|==============|
| 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   | subconjuntos | 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* significa contiguidade indefinida. O consumidor
deve chamar "PyBuffer_IsContiguous()" para determinar a contiguidade.

+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| Solicitação                     | Forma   | Avanços   | subconjuntos | contig   | readonly   | format   |
|=================================|=========|===========|==============|==========|============|==========|
| PyBUF_FULL                      | sim     | sim       | se           | U        | 0          | sim      |
|                                 |         |           | necessário   |          |            |          |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_FULL_RO                   | sim     | sim       | se           | U        | 1 ou 0     | sim      |
|                                 |         |           | necessário   |          |            |          |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| 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     |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+


Vetores Complexos
=================


Estilo NumPy: forma e avanços
-----------------------------

A estrutura lógica de vetores do estilo NumPy é definida por
"itemsize", "ndim", "shape" e "strides".

Se "ndim == 0", a localização da memória apontada para "buf" é
interpretada como um escalar de tamanho "itemsize". Nesse caso, ambos
"shape" e "strides" são "NULL".

Se "strides" é "NULL", o vetor é interpretado como um vetor C
n-dimensional padrão. Caso contrário, o consumidor deve acessar um
vetor n-dimensional como a seguir:

   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.

Esta é uma função que retorna um ponteiro para o elemento em uma
matriz N-D apontada por um índice N-dimensional onde existem ambos
passos e subconjuntos não-"NULL":

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


Funções relacionadas ao Buffer
==============================

int PyObject_CheckBuffer(PyObject *obj)

   Retorna "1" se *obj* oferece suporte à interface de buffer, se não,
   "0". Quando "1" é retornado, isso não garante que
   "PyObject_GetBuffer()" será bem sucedida. Esta função é sempre bem
   sucedida.

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

   Envia uma requisição para o *exporter* para preencher *view* como
   especificado por *flags*. Se o exportador não consegue prover um
   buffer do mesmo tipo, ele DEVE levantar "PyExc_BufferError",
   definir "view->obj" para "NULL" e retornar "-1".

   Em caso de sucesso, preenche *view*, define "view->obj" para uma
   nova referência para *exporter* e retorna 0. No caso de provedores
   de buffer encadeados que redirecionam requisições para um único
   objeto, "view->obj" DEVE se referir a este objeto em vez de
   *exporter* (Veja Buffer Object Structures).

   Chamadas bem sucedidas para "PyObject_GetBuffer()" devem ser
   emparelhadas a chamadas para  "PyBuffer_Release()", similar para
   "malloc()" e "free()". Assim, após o consumidor terminar com o
   buffer, "PyBuffer_Release()" deve ser chamado exatamente uma vez.

void PyBuffer_Release(Py_buffer *view)

   Libera o buffer de *view* e libera o *strong reference* (por
   exemplo, decrementa o contador de referências) para o objeto de
   suporte da view, "view->obj". Esta função DEVE ser chamada quando o
   buffer não estiver mais sendo usado, ou o vazamento de referências
   pode acontecer.

   É um erro chamar essa função em um buffer que não foi obtido via
   "PyObject_GetBuffer()".

Py_ssize_t PyBuffer_SizeFromFormat(const char *format)

   Retorna o implícito "itemsize" de "format". Se houver erro, levanta
   uma exceção e retorna -1.

   Novo na versão 3.9.

int PyBuffer_IsContiguous(Py_buffer *view, char order)

   Retorna "1" se a memória definida pela *view* é *contígua* no
   estilo C (*order* é "'C'") ou no estilo Fortran (*order* é "'F'")
   ou qualquer outra (*order* é "'A'"). Retorna "0" caso contrário.
   Essa função é sempre bem sucedida.

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

   Recebe a área de memória apontada pelos *indices* dentro da *view*
   dada. *indices* deve apontar para um array de "view->ndim" índices.

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

   Copia *len* bytes contíguos de *buf* para *view*. *fort* pode ser
   "'C'" ou "'F'" (para ordenação estilo C ou estilo Fortran). Retorna
   "0" em caso de sucesso e "-1" em caso de erro.

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

   Copia *len* bytes de *src* para sua representação contígua em
   *buf*. *order* pode ser "'C'" ou "'F'" ou "'A'" (para ordenação
   estilo C, Fortran ou qualquer uma). O retorno é "0" em caso de
   sucesso e "-1" em caso de falha.

   Esta função falha se *len* != *src->len*.

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

   Preenche o array *strides* com byte-strides de um array *contíguo*
   (estilo C se *order* é "'C'" ou estilo Fortran se *order* for
   "'F'") da forma dada com o número dado de bytes por elemento.

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

   Manipula requisições de buffer para um exportador que quer expor
   *buf* de tamanho *len* com capacidade de escrita definida de acordo
   com *readonly*. *buf* é interpretada como uma sequência de bytes
   sem sinal.

   O argumento *flags* indica o tipo de requisição. Esta função sempre
   preenche *view* como especificado por *flags*, a não ser que *buf*
   seja designado como somente leitura e "PyBUF_WRITABLE" esteja
   definido em *flags*.

   Em caso de sucesso, define "view->obj" para uma nova referência
   para *exporter* e retorna 0. Caso contrário, levanta
   "PyExc_BufferError", define "view->obj" para "NULL" e retorna "-1";

   Se esta função é usada como parte de um getbufferproc, *exporter*
   DEVE ser definida para o objeto de exportação e *flags* deve ser
   passado sem modificações. Caso contrário, *exporter* DEVE ser
   "NULL".
