Fixed Buffer Allocator em Zig — O que é e Como Usar

Fixed Buffer Allocator em Zig — O que é e Como Usar

Definição

O Fixed Buffer Allocator (std.heap.FixedBufferAllocator) em Zig é um alocador que distribui memória a partir de um buffer pré-alocado de tamanho fixo. Não faz nenhuma chamada ao sistema operacional — toda memória vem do buffer fornecido na inicialização. Quando o buffer acaba, qualquer nova alocação retorna error.OutOfMemory.

É o alocador ideal para sistemas embarcados, contextos sem SO e situações onde alocação dinâmica do SO não é desejável.

Por que Fixed Buffer Allocator Importa

  1. Zero syscalls: Não depende do sistema operacional para memória.
  2. Determinístico: Tempo de alocação previsível — importante para real-time.
  3. Embarcados: Funciona em plataformas freestanding sem heap.
  4. Stack-based: O buffer pode viver na stack, eliminando uso de heap completamente.

Exemplo Prático

Uso Básico

const std = @import("std");

pub fn main() !void {
    // Buffer na stack — sem heap!
    var buffer: [4096]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);
    const allocator = fba.allocator();

    // Alocações vêm do buffer de 4096 bytes
    const dados = try allocator.alloc(u8, 100);
    const numeros = try allocator.alloc(u32, 50);

    dados[0] = 'Z';
    numeros[0] = 42;

    std.debug.print("Dados: {c}, Números: {}\n", .{ dados[0], numeros[0] });

    // Quando o buffer acabar:
    // const grande = try allocator.alloc(u8, 10000); // error.OutOfMemory
}

Para Funções que Precisam de Allocator

const std = @import("std");

fn formatar(allocator: std.mem.Allocator, nome: []const u8, idade: u32) ![]u8 {
    return std.fmt.allocPrint(allocator, "{s} tem {} anos", .{ nome, idade });
}

pub fn main() !void {
    var buffer: [256]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);

    const texto = try formatar(fba.allocator(), "Maria", 25);
    std.debug.print("{s}\n", .{texto});
}

Em Sistemas Embarcados

const std = @import("std");

// Buffer global para embarcados (sem heap disponível)
var memoria_global: [8192]u8 = undefined;
var alocador_global = std.heap.FixedBufferAllocator.init(&memoria_global);

pub fn setup() !void {
    const alloc = alocador_global.allocator();

    var sensores = try alloc.alloc(u16, 10);
    _ = sensores;

    // Processamento com memória limitada e previsível
}

Reset para Reutilização

fn processarMensagens(mensagens: []const Mensagem) !void {
    var buffer: [1024]u8 = undefined;
    var fba = std.heap.FixedBufferAllocator.init(&buffer);

    for (mensagens) |msg| {
        // Reset "zera" o alocador — permite reutilizar todo o buffer
        fba.reset();

        const alloc = fba.allocator();
        const processada = try processar(alloc, msg);
        try enviar(processada);
    }
}

Combinando com Arena

var buffer: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);

// Arena sobre FBA — melhor gerenciamento de alocações múltiplas
var arena = std.heap.ArenaAllocator.init(fba.allocator());
defer arena.deinit();

Armadilhas Comuns

  • OutOfMemory inesperado: O buffer tem tamanho fixo. Se as alocações excederem o tamanho, OutOfMemory é retornado. Dimensione o buffer adequadamente.
  • Fragmentação: free individual pode criar lacunas que não podem ser reaproveitadas facilmente. Use reset() quando possível.
  • Buffer na stack: Buffers muito grandes na stack causam stack overflow. Para buffers grandes, use variáveis globais ou comptime.
  • Esquecer o reset: Em loops, esquecer de resetar o FBA faz o buffer esgotar rapidamente.
  • Alinhamento: O FBA respeita requisitos de alinhamento, o que pode desperdiçar bytes entre alocações.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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