Saltar al contenido principal

Módulo Bytes

El módulo Bytes crea bloques de memoria de tamaño fijo, que se leen y se escriben byte a byte o en grupos de 16 y 32 bits. Sirven para los datos binarios que otros módulos esperan así: los píxeles de draw.bytes, las muestras de Sound.loadSample y la música de Sound.loadMusic, o una trama para mandar a un aparato.

use Bytes

Funciona igual en Windows y en todas las placas. Los valores de 16 y 32 bits se guardan con el byte bajo primero (little endian), que es como los guardan el PC y los chips ESP32.

Funciones​

FunciónQué haceDevuelve
Bytes.new(n)Crea un bloque de n bytesEl bloque
Bytes.clone(bloque)Copia un bloqueUn bloque nuevo, con el mismo contenido

new(n)​

Crea un bloque de n bytes. n tiene que ser un entero; con 0, el bloque no tiene ningún byte.

El contenido de un bloque nuevo no está a cero: puede traer cualquier cosa. Llama a fill(0) antes de usarlo.

ErrorCuándo
ERR_TYPE_MISMATCHn no es un entero.
ERR_INVALID_VALUEn es negativo.
ERR_INVALID_ARG_NUMBERNo le pasas un argumento.
use Bytes

var bloque = Bytes.new(64) // 64 bytes
bloque.fill(0) // a cero
println(bloque.size()) // 64
Un tamaño demasiado grande cierra la VM

Si no hay memoria para tantos bytes, la VM se cierra (en Windows, con el mensaje ReallocAlloc failed), y try no lo evita. Comprueba el tamaño antes de crear el bloque.

clone(bloque)​

Devuelve un bloque nuevo, del mismo tamaño y con el mismo contenido que bloque. Cambiar uno no cambia el otro. Si bloque no es un bloque de Bytes, lanza ERR_TYPE_MISMATCH.

use Bytes

var original = Bytes.new(4)
original.write(0, 0xAB)

var copia = Bytes.clone(original)
copia.write(0, 0) // no cambia el original

println(original.read(0)) // 171
println(copia.read(0)) // 0

Objetos que devuelve​

Bytes.new y Bytes.clone devuelven un bloque, con estos métodos:

MétodoQué haceDevuelve
fill(v)Pone todos los bytes a vnil
size()Cuántos bytes tiene el bloqueUn entero
read(i)Lee el byte iDe 0 a 255
write(i, v)Escribe el byte inil
read16(i)Lee el valor de 16 bits número iDe 0 a 65535
write16(i, v)Escribe el valor de 16 bits número inil
read32(i)Lee el valor de 32 bits número iUn entero con signo
write32(i, v)Escribe el valor de 32 bits número inil

El índice cuenta en unidades del tamaño que lees o escribes, no en bytes:

MétodosUnidad del índiceBytes que toca el índice iÍndices de un bloque de n bytes
read y write1 byteiDe 0 a n - 1
read16 y write162 bytes2i y 2i + 1De 0 a n div 2 - 1
read32 y write324 bytesDe 4i a 4i + 3De 0 a n div 4 - 1

Así, read16(1) lee los bytes 2 y 3, no el byte 1. En un bloque de tamaño impar, el último byte solo se alcanza con read y write.

Errores de todos los métodos de lectura y escritura (excepciones, que se capturan con try):

  • ERR_INDEX_OUT_OF_RANGE si el índice se sale del bloque (también si es negativo).
  • ERR_TYPE_MISMATCH si el índice no es un entero.
  • ERR_INVALID_ARG_NUMBER si no les pasas los argumentos que piden.

fill(v)​

Pone todos los bytes del bloque a v. v tiene que ser un entero (si no, ERR_TYPE_MISMATCH), y se queda su byte bajo: fill(300) pone 44 en cada byte.

use Bytes

var bloque = Bytes.new(8)
bloque.fill(0xFF) // todos a 255
bloque.fill(0) // todos a 0

size()​

Devuelve cuántos bytes tiene el bloque.

read(i)​

Devuelve el byte i, de 0 a 255.

use Bytes

var bloque = Bytes.new(4)
bloque.write(2, 0x42)
println(bloque.read(2)) // 66

write(i, v)​

Escribe v en el byte i. Se queda el byte bajo de v: 300 se guarda como 44, y -1, como 255.

v no se comprueba: tiene que ser un entero. Un decimal, true, nil o una cadena no dan error y escriben un byte sin sentido. Ojo con /, que siempre da un decimal: usa div o int() (ver «Trampas y problemas conocidos»).

use Bytes

var bloque = Bytes.new(4)
bloque.write(0, 0xDE)
bloque.write(1, 0xAD)
bloque.write(2, 300)
println(bloque.read(2)) // 44

read16(i)​

Devuelve el valor de 16 bits que forman los bytes 2i (el bajo) y 2i + 1 (el alto), de 0 a 65535.

use Bytes

var bloque = Bytes.new(4)
bloque.write16(0, 0x1234)
println(bloque.read16(0)) // 4660 (0x1234)
println(bloque.read(0)) // 52 (0x34): el byte bajo va primero
println(bloque.read(1)) // 18 (0x12)

write16(i, v)​

Escribe v en los bytes 2i y 2i + 1, el byte bajo primero. Se quedan los 16 bits bajos: 70000 se guarda como 4464. Como en write, v tiene que ser un entero.

read32(i)​

Devuelve el valor de 32 bits que forman los bytes de 4i a 4i + 3, el bajo primero.

Los enteros de JARU tienen signo: los valores de 0x80000000 en adelante salen negativos (0xDEADBEEF se lee como -559038737). toUnsigned() no lo cambia; para verlo en hexadecimal, usa toHex().

use Bytes

var bloque = Bytes.new(8)
bloque.write32(0, 0xDEADBEEF)
bloque.write32(1, 0x12345678)

println(bloque.read32(0)) // -559038737
println(bloque.read32(0).toHex()) // DEADBEEF
println(bloque.read32(1)) // 305419896

write32(i, v)​

Escribe v en los bytes de 4i a 4i + 3, el bajo primero. Como en write, v tiene que ser un entero.

Un entero de JARU llega hasta 2147483647. Los valores de 32 bits más altos, escríbelos en hexadecimal: 0xFFFFFFFF es un entero (vale -1) y se guarda bien. Escrito en decimal, 4294967295 es un decimal, y write32 guarda un valor sin sentido sin avisar.

use Bytes

var bloque = Bytes.new(16)
bloque.write32(0, 0x00FF00FF)
bloque.write32(1, 0xFFFFFFFF)
println(bloque.read(4)) // 255

Ejemplo completo​

Una trama para mandar a un aparato: una cabecera de 16 bits, la longitud, los datos y una suma de control de un byte.

use Bytes

// Trama: cabecera (2 bytes), longitud (1), datos y suma de control (1)
var datos = [0x2A, 0x7F, 0x10]
var trama = Bytes.new(2 + 1 + datos.size() + 1)
trama.fill(0)

trama.write16(0, 0xAA55) // bytes 0 y 1
trama.write(2, datos.size()) // byte 2
for (var i = 0; i < datos.size(); i++)
trama.write(3 + i, datos[i])
end

// La suma de control: el byte bajo de la suma de todos los anteriores
var suma = 0
for (var i = 0; i < trama.size() - 1; i++)
suma = suma + trama.read(i)
end
trama.write(trama.size() - 1, suma % 256)

for (var i = 0; i < trama.size(); i++)
print(trama.read(i).toHex(), " ")
end
println("")
println(trama.read16(0).toHex()) // la cabecera, otra vez en 16 bits
55 AA 3 2A 7F 10 BB 
AA55

La cabecera 0xAA55 sale como 55 AA: el byte bajo va primero.

Una imagen para draw.bytes​

draw.bytes pinta un bloque como una imagen: un byte por píxel, con el número de su color en una paleta. La paleta es un array {…} de colores 0xRRGGBB; con una lista […] no pinta nada (ver Draw). Este programa pinta un degradado del negro al rojo durante dos segundos:

use Bytes, Display

const ANCHO = 160
const ALTO = 120

Display.open(ANCHO, ALTO)
var draw = Display.draw
Display.fps = 30

// La imagen: un byte por píxel, con el número de su color en la paleta
var imagen = Bytes.new(ANCHO * ALTO)
for (var y = 0; y < ALTO; y++)
for (var x = 0; x < ANCHO; x++)
imagen.write(y * ANCHO + x, (x * 255) div (ANCHO - 1))
end
end

// La paleta: 256 colores, del negro al rojo
var paleta[256]
for (var i = 0; i < 256; i++)
paleta[i] = i << 16
end

var fotogramas = 0
while (fotogramas < 60)
draw.bytes(0, 0, ANCHO, ALTO, imagen, paleta)
Display.update()
fotogramas++
end
Display.close()

Trampas y problemas conocidos​

  • Un bloque nuevo no está a cero. Llama a fill(0) antes de usarlo.
  • write no comprueba el valor. Un decimal se escribe como un byte sin sentido, sin error. / da siempre un decimal (10 / 4 es 2.5), así que bloque.write(0, total / 4) no guarda lo que esperas: usa total div 4 o int(total / 4).
  • read32 tiene signo. Los valores de 0x80000000 en adelante salen negativos.
  • Los valores de 32 bits altos, en hexadecimal. 4294967295 escrito en decimal no es un entero: escribe 0xFFFFFFFF.
  • Índices en unidades. read16(1) y read32(1) no empiezan en el byte 1, sino en el 2 y en el 4.

Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.