buffer: almacenamiento de bytes ampliable

Buffer Describe las reglas de inicialización, adición, consulta, capacidad y liberación.

Wave Foundation

Significado de Buffer

Buffer en std::buffer::types tiene data: ptr<u8>, len: i64 y cap: i64. len es el número de bytes inicializados y en uso, y cap es el número total de bytes asignados. Mantenga siempre 0 <= len <= cap. La cadena NUL no garantiza automáticamente la terminación.

Básico API

módulo declaración significado
std::buffer::alloc buffer_init(out_buf: ptr<Buffer>, capacity: i64) -> i64 Inicialice el nuevo repositorio. No recuperar en buffers que ya se poseen
mismo módulo buffer_free(buf: ptr<Buffer>) -> i64 Desasignar. Vacío si tiene éxito
mismo módulo buffer_reserve(buf: ptr<Buffer>, required_cap: i64) -> i64 Asegúrese de tener una capacidad total mínima. len Mantenido
mismo módulo buffer_clear(buf: ptr<Buffer>) -> i64 Mantener capacidad y len=0
mismo módulo buffer_resize(buf: ptr<Buffer>, new_len: i64, value: u8) -> i64 Cambie la longitud, complete nuevos bytes con value
std::buffer::write buffer_push(buf: ptr<Buffer>, value: u8) -> i64 agregar un byte
mismo módulo buffer_append(buf: ptr<Buffer>, src: ptr<u8>, size: i64) -> i64 sizeCopiar y agregar byte
mismo módulo buffer_append_str(buf: ptr<Buffer>, s: str) -> i64 Agregue bytes de cadena excluyendo NUL
std::buffer::read buffer_get(buf: ptr<Buffer>, index: i64, out_value: ptr<u8>) -> i64 Leer un byte en el rango

El retorno de estado API devuelve BUFFER_OK(0) en caso de éxito. Distinga entre los errores INVALID, BOUNDS, OVERFLOW y ALLOC de std::buffer::error. El número no se interpreta como OS errno. buffer_new representa un error de asignación como un Buffer vacío, por lo que cuando necesite distinguir entre errores, utilice buffer_init.

Ejemplo de ejecución

import("std::buffer::alloc")::{
    Buffer, buffer_init, buffer_free
};
import("std::buffer::write")::{
    buffer_append_str, buffer_push
};
import("std::buffer::read")::{
    buffer_get
};

fun main() -> i32 {
    var data: Buffer;
    if (buffer_init(&data, 0) < 0) {
        return 1;
    }
    if (buffer_append_str(&data, "Hi") < 0 || buffer_push(&data, 33) < 0) {
        buffer_free(&data);
        return 2;
    }

    var value: u8 = 0;
    if (buffer_get(&data, 2, &value) < 0) {
        buffer_free(&data);
        return 3;
    }

    println("{} {}", data.len, value);
    if (buffer_free(&data) < 0) {
        return 4;
    }

    return 0;
}

Resultado de la ejecución:

3 33

Guárdelo como main.wave y ejecútelo como wavec run main.wave. Una capacidad inicial de 0 no es un error, sino un búfer vacío válido. Libera espacio durante el procesamiento posterior.

Esperanza de vida y fracaso

Hacer crecer el búfer puede cambiar los datos. No utilice una dirección prestada anteriormente después de una operación que pueda reasignarla. Copiar la estructura Buffer no duplica su asignación, así que asigne a esa asignación un único propietario.

buffer_get no cambia los argumentos de salida si falla. Por otro lado, la función de conveniencia buffer_at también representa errores como 0, así que use buffer_get para distinguir entre 0 bytes reales y fallas. Evite crear len/cap no válido cambiando directamente los campos públicos.

Memoria API · Practica leer un archivo como Buffer

Observar la longitud y la capacidad por separado

reserve libera espacio de almacenamiento, pero no aumenta len. resize cambia la longitud real utilizada e inicializa la parte extendida al byte especificado. clear establece solo la longitud utilizada en 0, lo que permite reutilizar la asignación.

Guarde el siguiente programa como main.wave y ejecútelo. No depende del múltiplo de crecimiento exacto de la capacidad; simplemente garantiza que tenga el espacio que necesita.

import("std::buffer::alloc")::{
    Buffer,
    buffer_init,
    buffer_reserve,
    buffer_resize,
    buffer_clear,
    buffer_free
};

fun main() -> i32 {
    var data: Buffer;

    if (buffer_init(&data, 0) < 0) {
        return 1;
    }

    if (buffer_reserve(&data, 20) < 0) {
        buffer_free(&data);
        return 2;
    }

    println("reserved length={}", data.len);

    if (buffer_resize(&data, 3, 7) < 0) {
        buffer_free(&data);
        return 3;
    }

    println("resized length={} first={}", data.len, deref data.data[0]);

    if (buffer_clear(&data) < 0) {
        buffer_free(&data);
        return 4;
    }

    println("cleared length={}", data.len);

    if (buffer_free(&data) < 0) {
        return 5;
    }

    return 0;
}

Resultado de la ejecución:

reserved length=0
resized length=3 first=7
cleared length=0

Al aumentar a resize, pasamos value=7, por lo que los tres bytes nuevos que vemos son 7. El espacio solo asegurado con reserve no se lee como datos inicializados. cap permanecerá después de clear y se puede agregar nuevamente al mismo Buffer.

Distinguir entre cero bytes y errores de búsqueda

buffer_get devuelve el estado y escribe los bytes reales como argumentos de salida. Incluso si los datos son 0, es un éxito normal.

import("std::buffer::alloc")::{Buffer, buffer_init, buffer_free};
import("std::buffer::write")::{buffer_push};
import("std::buffer::read")::{buffer_get};
import("std::buffer::error")::{BUFFER_ERR_BOUNDS};

fun main() -> i32 {
    var data: Buffer;

    if (buffer_init(&data, 0) < 0) {
        return 1;
    }

    if (buffer_push(&data, 0) < 0) {
        buffer_free(&data);
        return 2;
    }

    var value: u8 = 99;

    if (buffer_get(&data, 0, &value) < 0) {
        buffer_free(&data);
        return 3;
    }

    println("stored={}", value);
    value = 99;

    if (buffer_get(&data, 1, &value) == BUFFER_ERR_BOUNDS) {
        println("outside, preserved={}", value);
    }

    if (buffer_free(&data) < 0) {
        return 4;
    }

    return 0;
}

Resultado de la ejecución:

stored=0
outside, preserved=99

El primer acierto es un éxito con lectura 0, el segundo acierto es un fallo fuera de límites. Incluso si value=99 permanece después de una falla, no significa que sea el valor leído del búfer. Asegúrese de verificar el estado juntos.

Solución práctica: acumulación de bytes

Para sumar números del 0 al 9, repita buffer_push y verifique cada resultado. Guarde la suma en i64 y lea solo el rango 0 <= index < data.len. Después de manejar el búfer, llamamos a buffer_free tanto en la ruta de éxito como en la de error.

import("std::buffer::alloc")::{Buffer, buffer_init, buffer_free};
import("std::buffer::write")::{buffer_push};

fun main() -> i32 {
    var data: Buffer;

    if (buffer_init(&data, 0) < 0) {
        return 1;
    }

    for (var value: i32 = 0; value < 10; value += 1) {
        if (buffer_push(&data, value as u8) < 0) {
            buffer_free(&data);
            return 2;
        }
    }

    var total: i64 = 0;

    for (var index: i64 = 0; index < data.len; index += 1) {
        total += deref data.data[index] as i64;
    }

    println("sum={}", total);

    if (buffer_free(&data) < 0) {
        return 3;
    }

    return 0;
}

Resultado de la ejecución:

sum=45