std.io.FixedBufferStream em Zig — Referência e Exemplos

std.io.FixedBufferStream — Stream sobre Buffer Fixo

O FixedBufferStream é um tipo que implementa as interfaces Reader e Writer sobre um buffer de tamanho fixo em memória. Ele é ideal para cenários onde você precisa de um writer/reader sem alocação dinâmica, como formatar strings, testes ou construir dados em buffers pré-alocados.

Visão Geral

O FixedBufferStream mantém um cursor de posição que avança conforme dados são lidos ou escritos. Ele funciona sobre um slice de bytes existente, sem fazer nenhuma alocação própria.

const std = @import("std");

// Sobre um buffer mutável (leitura e escrita)
var buf: [1024]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf);

// Sobre um slice constante (somente leitura)
const dados = "Olá, mundo!";
var stream_leitura = std.io.fixedBufferStream(dados);

Tipo Principal

FixedBufferStream

pub fn FixedBufferStream(comptime Buffer: type) type {
    return struct {
        buffer: Buffer,
        pos: usize,

        // Obtém o writer (somente para buffers mutáveis)
        pub fn writer(self: *Self) Writer;

        // Obtém o reader
        pub fn reader(self: *Self) Reader;

        // Retorna o slice de bytes já escritos (do início até pos)
        pub fn getWritten(self: Self) []const u8;

        // Reseta a posição para o início
        pub fn reset(self: *Self) void;

        // Reposiciona o cursor
        pub fn seekTo(self: *Self, pos: u64) void;

        // Move o cursor relativamente à posição atual
        pub fn seekBy(self: *Self, amt: i64) void;

        // Retorna a posição atual
        pub fn getPos(self: Self) u64;

        // Retorna os bytes ainda não lidos/escritos
        pub fn getEndPos(self: Self) u64;
    };
}

Função de Conveniência

pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(@TypeOf(buffer))

Cria um FixedBufferStream a partir de um slice mutável ([]u8) ou constante ([]const u8).

Exemplo 1: Formatação de String sem Alocação

O uso mais comum é formatar strings em um buffer na stack:

const std = @import("std");

fn formatarMensagem(nome: []const u8, pontos: u32) []const u8 {
    var buf: [256]u8 = undefined;
    var stream = std.io.fixedBufferStream(&buf);
    const writer = stream.writer();

    writer.print("Jogador '{s}' marcou {d} pontos!", .{ nome, pontos }) catch
        return "Erro na formatação";

    return stream.getWritten();
}

pub fn main() void {
    const msg = formatarMensagem("Ana", 1500);
    std.debug.print("{s}\n", .{msg});
}

Nota: Cuidado com o tempo de vida do buffer! No exemplo acima, o slice retornado aponta para a stack da função. Em código real, passe o buffer por parâmetro.

Exemplo 2: Construção de Dados em Memória

Usando FixedBufferStream para construir um pacote de protocolo:

const std = @import("std");

const TipoPacote = enum(u8) {
    handshake = 0x01,
    dados = 0x02,
    desconectar = 0x03,
};

fn construirPacote(buf: []u8, tipo: TipoPacote, payload: []const u8) ![]const u8 {
    var stream = std.io.fixedBufferStream(buf);
    const writer = stream.writer();

    // Cabeçalho: magic (2 bytes) + tipo (1 byte) + tamanho payload (4 bytes)
    try writer.writeAll(&[_]u8{ 0xZI, 0x47 }); // magic bytes
    try writer.writeByte(@intFromEnum(tipo));
    try writer.writeInt(u32, @intCast(payload.len), .big);

    // Payload
    try writer.writeAll(payload);

    return stream.getWritten();
}

pub fn main() !void {
    var buf: [1024]u8 = undefined;
    const pacote = try construirPacote(&buf, .dados, "Olá, servidor!");

    std.debug.print("Pacote: {d} bytes\n", .{pacote.len});
    for (pacote) |byte| {
        std.debug.print("{x:0>2} ", .{byte});
    }
    std.debug.print("\n", .{});
}

Exemplo 3: Leitura de Dados de um Buffer

Usando FixedBufferStream como reader sobre dados existentes:

const std = @import("std");

fn parsearCabecalho(dados: []const u8) !void {
    var stream = std.io.fixedBufferStream(dados);
    const reader = stream.reader();

    const versao = try reader.readInt(u16, .big);
    const flags = try reader.readByte();
    const tamanho = try reader.readInt(u32, .big);

    std.debug.print("Versão: {d}\n", .{versao});
    std.debug.print("Flags: 0x{x:0>2}\n", .{flags});
    std.debug.print("Tamanho: {d}\n", .{tamanho});

    // Ler o restante como texto
    var texto_buf: [256]u8 = undefined;
    const n = try reader.read(&texto_buf);
    if (n > 0) {
        std.debug.print("Dados: {s}\n", .{texto_buf[0..n]});
    }
}

pub fn main() !void {
    // Simular dados recebidos
    const dados = [_]u8{
        0x00, 0x01, // versão = 1
        0xFF, // flags
        0x00, 0x00, 0x00, 0x05, // tamanho = 5
        'H', 'e', 'l', 'l', 'o', // dados
    };

    try parsearCabecalho(&dados);
}

Padrões Comuns

Formatação Segura com bufPrint

Para formatação simples, std.fmt.bufPrint é mais direto, mas FixedBufferStream é melhor para escritas incrementais:

// Simples: use bufPrint
const resultado = try std.fmt.bufPrint(&buf, "Valor: {d}", .{42});

// Incremental: use FixedBufferStream
var stream = std.io.fixedBufferStream(&buf);
const w = stream.writer();
try w.writeAll("Início ");
try w.print("meio {d} ", .{42});
try w.writeAll("fim");
const resultado2 = stream.getWritten();

Testes com FixedBufferStream

Ideal para testar funções que escrevem em qualquer writer:

test "função de formatação" {
    var buf: [1024]u8 = undefined;
    var stream = std.io.fixedBufferStream(&buf);

    try minhaFuncaoDeEscrita(stream.writer());

    try std.testing.expectEqualStrings("saída esperada", stream.getWritten());
}

Verificação de Espaço

O FixedBufferStream retorna erro error.NoSpaceLeft se o buffer estiver cheio:

var buf: [10]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf);
const writer = stream.writer();

writer.writeAll("Este texto é longo demais") catch |err| {
    // err == error.NoSpaceLeft
    std.debug.print("Buffer cheio! Escritos: {d} bytes\n", .{stream.getWritten().len});
};

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.