Protocolo Búfer
***************

Ciertos objetos disponibles en Python ajustan el acceso a un arreglo
de memoria subyacente o *buffer*. Dichos objetos incluyen el
incorporado "bytes" y "bytearray", y algunos tipos de extensión como
"array.array". Las bibliotecas de terceros pueden definir sus propios
tipos para fines especiales, como el procesamiento de imágenes o el
análisis numérico.

Si bien cada uno de estos tipos tiene su propia semántica, comparten
la característica común de estar respaldados por un búfer de memoria
posiblemente grande. Es deseable, en algunas situaciones, acceder a
ese búfer directamente y sin copia intermedia.

Python proporciona una instalación de este tipo en el nivel C en la
forma de protocolo búfer. Este protocolo tiene dos lados:

* en el lado del productor, un tipo puede exportar una "interfaz de
  búfer" que permite a los objetos de ese tipo exponer información
  sobre su búfer subyacente. Esta interfaz se describe en la sección
  Estructuras de Objetos Búfer;

* en el lado del consumidor, hay varios medios disponibles para
  obtener un puntero a los datos subyacentes sin procesar de un objeto
  (por ejemplo, un parámetro de método).

Los objetos simples como "bytes" y "bytearray" exponen su búfer
subyacente en forma orientada a bytes. Otras formas son posibles; por
ejemplo, los elementos expuestos por un "array.array" pueden ser
valores de varios bytes.

Un consumidor de ejemplo de la interfaz del búfer es el método
"write()" de objetos de archivo: cualquier objeto que pueda exportar
una serie de bytes a través de la interfaz del búfer puede escribirse
en un archivo. Mientras que "write()" solo necesita acceso de solo
lectura a los contenidos internos del objeto que se le pasa, otros
métodos como "readinto()" necesitan acceso de escritura a los
contenidos de su argumento. La interfaz del búfer permite que los
objetos permitan o rechacen selectivamente la exportación de búferes
de lectura-escritura y solo lectura.

Hay dos formas para que un consumidor de la interfaz del búfer
adquiera un búfer sobre un objeto de destino:

* llamar "PyObject_GetBuffer()" con los parámetros correctos;

* llamar "PyArg_ParseTuple()" (o uno de sus hermanos) con uno de los
  "y*", "w*" o "s*" códigos de formato.

En ambos casos, se debe llamar a "PyBuffer_Release()" cuando ya no se
necesita el búfer. De lo contrario, podrían surgir varios problemas,
como pérdidas de recursos.


Estructura de búfer
===================

Las estructuras de búfer (o simplemente "búferes") son útiles como una
forma de exponer los datos binarios de otro objeto al programador de
Python. También se pueden usar como un mecanismo de corte de copia
cero. Usando su capacidad para hacer referencia a un bloque de
memoria, es posible exponer cualquier información al programador
Python con bastante facilidad. La memoria podría ser una matriz grande
y constante en una extensión C, podría ser un bloque de memoria sin
procesar para su manipulación antes de pasar a una biblioteca del
sistema operativo, o podría usarse para pasar datos estructurados en
su formato nativo en memoria .

Contrariamente a la mayoría de los tipos de datos expuestos por el
intérprete de Python, los búferes no son punteros "PyObject" sino
estructuras C simples. Esto les permite ser creados y copiados de
manera muy simple. Cuando se necesita un contenedor genérico alrededor
de un búfer, un objeto memoryview puede ser creado.

Para obtener instrucciones breves sobre cómo escribir un objeto de
exportación, consulte Estructuras de objetos búfer. Para obtener un
búfer, consulte "PyObject_GetBuffer()".

Py_buffer

   void *buf

      Un puntero al inicio de la estructura lógica descrita por los
      campos del búfer. Puede ser cualquier ubicación dentro del
      bloque de memoria física subyacente del exportador. Por ejemplo,
      con negativo "strides" el valor puede apuntar al final del
      bloque de memoria.

      Para arreglos *contiguous*, el valor apunta al comienzo del
      bloque de memoria.

   void *obj

      Una nueva referencia al objeto exportador. La referencia es
      propiedad del consumidor y automáticamente disminuye y se
      establece en "NULL" por "PyBuffer_Release()". El campo es el
      equivalente del valor de retorno de cualquier función estándar
      de C-API.

      Como un caso especial, para los búferes *temporary* que están
      envueltos por "PyMemoryView_FromBuffer()" o
      "PyBuffer_FillInfo()" este campo es "NULL". En general, los
      objetos de exportación NO DEBEN usar este esquema.

   Py_ssize_t len

      "product(shape) * itemize". Para arreglos contiguos, esta es la
      longitud del bloque de memoria subyacente. Para arreglos no
      contiguos, es la longitud que tendría la estructura lógica si se
      copiara en una representación contigua.

      Accede a "((char *)buf)[0] hasta ((char *)buf)[len-1]" solo es
      válido si el búfer se ha obtenido mediante una solicitud que
      garantiza la contigüidad. En la mayoría de los casos, dicha
      solicitud será "PyBUF_SIMPLE" o "PyBUF_WRITABLE".

   int readonly

      Un indicador de si el búfer es de solo lectura. Este campo está
      controlado por el indicador "PyBUF_WRITABLE".

   Py_ssize_t itemsize

      Tamaño del elemento en bytes de un solo elemento. Igual que el
      valor de "struct.calcsize()" invocado en valores no "NULL"
      "format".

      Excepción importante: si un consumidor solicita un búfer sin el
      indicador "PyBUF_FORMAT", "format" se establecerá en "NULL",
      pero "itemsize" todavía tiene el valor para el formato original.

      Si "shape" está presente, la igualdad "product(shape) * itemsize
      == len" aún se mantiene y el consumidor puede usar "itemsize"
      para navegar el búfer.

      Si "shape" es "NULL" como resultado de un "PyBUF_SIMPLE" o un
      "PyBUF_WRITABLE", el consumidor debe ignorar "itemsize" y asume
      "itemsize == 1".

   const char *format

      Una cadena de caracteres terminada en *NUL* en sintaxis de
      estilo del modulo "struct" que describe el contenido de un solo
      elemento. Si esto es "NULL", se supone ""B"" (bytes sin signo).

      Este campo está controlado por el indicador "PyBUF_FORMAT".

   int ndim

      El número de dimensiones que representa la memoria como un
      arreglo n-dimensional. Si es `` 0``, "buf" apunta a un solo
      elemento que representa un escalar. En este caso, "shape",
      "strides" y "suboffsets" DEBE ser "NULL".

      La macro "PyBUF_MAX_NDIM" limita el número máximo de dimensiones
      a 64. Los exportadores DEBEN respetar este límite, los
      consumidores de búfer multidimensionales DEBEN poder manejar
      hasta dimensiones "PyBUF_MAX_NDIM".

   Py_ssize_t *shape

      Un arreglo de "Py_ssize_t" de longitud "ndim" que indica la
      forma de la memoria como un arreglo n-dimensional. Tenga en
      cuenta que "shape[0] * ... * shape[ndim-1] * itemsize" DEBE ser
      igual a "len".

      Los valores de forma están restringidos a "shape[n] >= 0". El
      caso "shape[n] == 0" requiere atención especial. Vea arreglos
      complejos (complex arrays) para más información.

      El arreglo de formas es de sólo lectura para el consumidor.

   Py_ssize_t *strides

      Un arreglo de "Py_ssize_t" de longitud "ndim" que proporciona el
      número de bytes que se omiten para llegar a un nuevo elemento en
      cada dimensión.

      Los valores de *stride* pueden ser cualquier número entero. Para
      los arreglos regulares, los pasos son generalmente positivos,
      pero un consumidor DEBE ser capaz de manejar el caso "strides[n]
      <= 0". Ver complex arrays para más información.

      El arreglo *strides* es de sólo lectura para el consumidor.

   Py_ssize_t *suboffsets

      Un arreglo de "Py_ssize_t" de longitud "ndim". Si "suboffsets[n]
      >= 0", los valores almacenados a lo largo de la enésima
      dimensión son punteros y el valor del *suboffsets* dicta cuántos
      bytes agregar a cada puntero después de desreferenciarlos. Un
      valor de *suboffsets* negativo indica que no debe producirse una
      desreferenciación (*striding* en un bloque de memoria contiguo).

      Si todos los *suboffsets* son negativos (es decir, no se
      necesita desreferenciar), entonces este campo debe ser "NULL"
      (el valor predeterminado).

      *Python Imaging Library (PIL)* utiliza este tipo de
      representación de arreglos. Consulte complex arrays para obtener
      más información sobre cómo acceder a los elementos de dicho
      arreglo.

      El arreglo de *suboffsets* es de sólo lectura para el
      consumidor.

   void *internal

      Esto es para uso interno del objeto exportador. Por ejemplo, el
      exportador podría volver a emitirlo como un número entero y
      utilizarlo para almacenar indicadores sobre si las matrices de
      forma, *strides* y *suboffsets* deben liberarse cuando se libera
      el búfer. El consumidor NO DEBE alterar este valor.


Tipos de solicitud búfer
========================

Los búferes obtienen generalmente enviando una solicitud de búfer a un
objeto de exportación a través de "PyObject_GetBuffer()". Dado que la
complejidad de la estructura lógica de la memoria puede variar
drásticamente, el consumidor usa el argumento *flags* para especificar
el tipo de búfer exacto que puede manejar.

Todos los campos "Py_buffer" están definidos inequívocamente por el
tipo de solicitud.


campos independientes de solicitud
----------------------------------

Los siguientes campos no están influenciados por *flags* y siempre
deben completarse con los valores correctos: "obj", "buf", "len",
"itemsize", "ndim".


formato de sólo lectura
-----------------------

   PyBUF_WRITABLE

      Controla el campo "readonly". Si se establece, el exportador
      DEBE proporcionar un búfer de escritura o, de lo contrario,
      informar de un error. De lo contrario, el exportador PUEDE
      proporcionar un búfer de solo lectura o de escritura, pero la
      elección DEBE ser coherente para todos los consumidores.

   PyBUF_FORMAT

      Controla el campo "format". Si se establece, este campo DEBE
      completarse correctamente. De lo contrario, este campo DEBE ser
      "NULL".

"PyBUF_WRITABLE" puede ser |'d a cualquiera de las banderas en la
siguiente sección. Dado que "PyBUF_SIMPLE" se define como 0,
"PyBUF_WRITABLE" puede usarse como un indicador independiente para
solicitar un búfer de escritura simple.

"PyBUF_FORMAT" puede ser |'d para cualquiera de las banderas excepto
"PyBUF_SIMPLE". Este último ya implica el formato "B" (bytes sin
signo).


formas, *strides*, *suboffsets*
-------------------------------

Las banderas que controlan la estructura lógica de la memoria se
enumeran en orden decreciente de complejidad. Tenga en cuenta que cada
bandera contiene todos los bits de las banderas debajo de ella.

+-------------------------------+---------+-----------+--------------+
| Solicitud                     | forma   | *strides* | *suboffsets* |
|===============================|=========|===========|==============|
| PyBUF_INDIRECT                | sí      | sí        | si es        |
|                               |         |           | necesario    |
+-------------------------------+---------+-----------+--------------+
| PyBUF_STRIDES                 | sí      | sí        | NULL         |
+-------------------------------+---------+-----------+--------------+
| PyBUF_ND                      | sí      | NULL      | NULL         |
+-------------------------------+---------+-----------+--------------+
| PyBUF_SIMPLE                  | NULL    | NULL      | NULL         |
+-------------------------------+---------+-----------+--------------+


solicitudes de contigüidad
--------------------------

La *contigüidad* C o Fortran se puede solicitar explícitamente, con y
sin información de paso. Sin información de paso, el búfer debe ser
C-contiguo.

+-------------------------------------+---------+-----------+--------------+----------+
| Solicitud                           | forma   | *strides* | *suboffsets* | contig   |
|=====================================|=========|===========|==============|==========|
| PyBUF_C_CONTIGUOUS                  | sí      | sí        | NULL         | C        |
+-------------------------------------+---------+-----------+--------------+----------+
| PyBUF_F_CONTIGUOUS                  | sí      | sí        | NULL         | F        |
+-------------------------------------+---------+-----------+--------------+----------+
| PyBUF_ANY_CONTIGUOUS                | sí      | sí        | NULL         | C o F    |
+-------------------------------------+---------+-----------+--------------+----------+
| "PyBUF_ND"                          | sí      | NULL      | NULL         | C        |
+-------------------------------------+---------+-----------+--------------+----------+


solicitudes compuestas
----------------------

Todas las solicitudes posibles están completamente definidas por
alguna combinación de las banderas en la sección anterior. Por
conveniencia, el protocolo de memoria intermedia proporciona
combinaciones de uso frecuente como indicadores únicos.

En la siguiente tabla *U* significa contigüidad indefinida. El
consumidor tendría que llamar a "PyBuffer_IsContiguous()" para
determinar la contigüidad.

+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| Solicitud                       | forma   | *strides* | *suboffsets* | contig   | sólo       | formato  |
|                                 |         |           |              |          | lectura    |          |
|=================================|=========|===========|==============|==========|============|==========|
| PyBUF_FULL                      | sí      | sí        | si es        | U        | 0          | sí       |
|                                 |         |           | necesario    |          |            |          |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_FULL_RO                   | sí      | sí        | si es        | U        | 1 o 0      | sí       |
|                                 |         |           | necesario    |          |            |          |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_RECORDS                   | sí      | sí        | NULL         | U        | 0          | sí       |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_RECORDS_RO                | sí      | sí        | NULL         | U        | 1 o 0      | sí       |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_STRIDED                   | sí      | sí        | NULL         | U        | 0          | NULL     |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_STRIDED_RO                | sí      | sí        | NULL         | U        | 1 o 0      | NULL     |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_CONTIG                    | sí      | NULL      | NULL         | C        | 0          | NULL     |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+
| PyBUF_CONTIG_RO                 | sí      | NULL      | NULL         | C        | 1 o 0      | NULL     |
+---------------------------------+---------+-----------+--------------+----------+------------+----------+


Arreglos complejos
==================


Estilo NumPy: forma y *strides*
-------------------------------

La estructura lógica de las matrices de estilo NumPy está definida por
"itemsize", "ndim", "shape" y "strides".

Si "ndim == 0", la ubicación de memoria señalada por "buf" se
interpreta como un escalar de tamaño "itemsize". En ese caso, tanto
"shape" como "strides" son "NULL".

Si "strides" es "NULL", el arreglo se interpreta como un arreglo C
n-dimensional estándar. De lo contrario, el consumidor debe acceder a
un arreglo n-dimensional de la siguiente manera:

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

Como se señaló anteriormente, "buf" puede apuntar a cualquier
ubicación dentro del bloque de memoria real. Un exportador puede
verificar la validez de un búfer con esta función:

   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, *strides* y *suboffsets*
-------------------------------------------

Además de los elementos normales, los arreglos de estilo PIL pueden
contener punteros que deben seguirse para llegar al siguiente elemento
en una dimensión. Por ejemplo, el arreglo C tridimensional regular
"char v[2][2][3]" también se puede ver como un arreglo de 2 punteros a
2 arreglos bidimensionales: "char (*v[2])[2][3]". En la representación
de *suboffsets*, esos dos punteros pueden incrustarse al comienzo de
"buf", apuntando a dos matrices "char x[2][3]" que pueden ubicarse en
cualquier lugar de la memoria.

Aquí hay una función que retorna un puntero al elemento en un arreglo
N-D a la que apunta un índice N-dimensional cuando hay *strides* y
*suboffsets* no "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;
   }


Funciones relacionadas a búfer
==============================

int PyObject_CheckBuffer(PyObject *obj)

   Retorna "1" si *obj* admite la interfaz de búfer; de lo contrario,
   "0" cuando se retorna "1", no garantiza que "PyObject_GetBuffer()"
   tenga éxito. Esta función siempre finaliza con éxito.

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

   Envía una solicitud al *exporter* para completar la *view* según lo
   especificado por *flags*. Si el exportador no puede proporcionar un
   búfer del tipo exacto, DEBE lanzar "PyExc_BufferError", establecer
   "view->obj" en "NULL" y retornar "-1".

   Si tiene éxito, completa *view*, establece "view->obj" en una nueva
   referencia a *exporter* y retorna 0. En el caso de proveedores de
   búfer encadenados que redirigen las solicitudes a un solo objeto,
   "view->obj" PUEDE referirse a este objeto en lugar de *exporter*
   (Ver Estructuras de objetos de búfer).

   Las llamadas exitosas a "PyObject_GetBuffer()" deben combinarse con
   las llamadas a "PyBuffer_Release()", similar a "malloc()" y
   "free()". Por lo tanto, después de que el consumidor haya terminado
   con el búfer, "PyBuffer_Release()" debe llamarse exactamente una
   vez.

void PyBuffer_Release(Py_buffer *view)

   Libera el búfer *view* y disminuye el conteo de referencias para
   "view->obj". Esta función DEBE llamarse cuando el búfer ya no se
   utiliza, de lo contrario, pueden producirse fugas de referencia.

   Es un error llamar a esta función en un búfer que no se obtuvo a
   través de "PyObject_GetBuffer()".

Py_ssize_t PyBuffer_SizeFromFormat(const char *format)

   Retorna el "itemsize" implícito de "format". En caso de error,
   lanza una excepción y retorna -1.

   Nuevo en la versión 3.9.

int PyBuffer_IsContiguous(Py_buffer *view, char order)

   Retorna "1" si la memoria definida por *view* es de estilo C
   (*order* es "'C'") o de estilo Fortran (*order* es "'F'")
   *contiguous* o uno cualquiera (*order* es "'A'"). Retorna "0" de lo
   contrario. Esta función siempre finaliza con éxito.

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

   Obtiene el área de memoria señalada por los *indices* dentro del
   *view* dado. *indices* deben apuntar a un arreglo de índices
   "view->ndim".

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

   Copia *len* bytes contiguos de *buf* a *view*. *fort* puede ser
   "'C'" o "'F'" (para pedidos al estilo C o al estilo Fortran). "0"
   se retorna en caso de éxito, "-1" en caso de error.

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

   Copia *len* bytes de *src* a su representación contigua en *buf*.
   *order* puede ser "'C'" o "'F'" o "''A'" (para pedidos al estilo C
   o al estilo Fortran o cualquiera) "0" se retorna en caso de éxito,
   "-1" en caso de error.

   Esta función falla si *len* != *src->len*.

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

   Rellena el arreglo *strides* con bytes de paso de un *contiguous*
   (estilo C si *order* es "'C'" o estilo Fortran si *order* es "'F '"
   ) arreglo de la forma dada con el 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)

   Maneje las solicitudes de búfer para un exportador que quiera
   exponer *buf* de tamaño *len* con capacidad de escritura
   establecida de acuerdo con *readonly*. *buf* se interpreta como una
   secuencia de bytes sin signo.

   El argumento *flags* indica el tipo de solicitud. Esta función
   siempre llena *view* según lo especificado por *flags*, a menos que
   *buf* haya sido designado como solo lectura y "PyBUF_WRITABLE" esté
   configurado en *flags*.

   Si tiene éxito, establece "view->obj" en una nueva referencia a
   *exporter* y retorna 0. De lo contrario, aumenta
   "PyExc_BufferError", establece "view->obj" en "NULL" y retorna
   "-1";

   Si esta función se usa como parte de a getbufferproc, *exporter*
   DEBE establecerse en el objeto exportador y *flags* deben pasarse
   sin modificaciones. De lo contrario, *exporter* DEBE ser "NULL".
