Internos

Funções e estruturas não exportadas, documentadas para quem quiser estender o pacote (ou entender o pipeline). Nada aqui é API estável.

Pipeline de leitura

MicroSUS.canal_registrosFunction
canal_registros(caminho, cab; lote = 4096) -> Channel{Vector{Vector{UInt8}}}

Produz lotes de registros brutos (cada um com tamanho_registro bytes, já sem registros deletados), lendo .dbc (descompressão em task separada) ou .dbf (leitura direta). A montagem lida com registros que atravessam a fronteira dos chunks de 4 KiB da janela DCL.

source
MicroSUS.abre_dbcFunction
abre_dbc(caminho) -> (io, CabecalhoDBF)

Abre um .dbc, lê o cabeçalho DBF em claro e posiciona io no início do fluxo comprimido (cabeçalho + 4 bytes de CRC).

source
MicroSUS.le_cabecalho_dbfFunction
le_cabecalho_dbf(bytes::Vector{UInt8}) -> CabecalhoDBF

Interpreta os primeiros bytes de um DBF (ou o cabeçalho em claro de um .dbc): contagem de registros, tamanhos, language driver e descritores de campo (32 bytes cada, terminados por 0x0D).

source
MicroSUS.RegistroDBFType
RegistroDBF

Visão leve sobre os bytes de um registro. r[:CAMPO] devolve o texto do campo (trim + transcodificação), parseando só o que for pedido — é o objeto passado ao filtro de ler.

source

O fluxo completo: MicroSUS.abre_dbc lê o cabeçalho em claro e posiciona o IO no fluxo DCL; dcl_descomprime emite chunks de ≤ 4 KiB; MicroSUS.canal_registros monta registros de largura fixa atravessando fronteiras de chunk e emite lotes de registros brutos por um Channel; MicroSUS._canal_lotes (em src/tables.jl) aplica filtro e parse e emite NamedTuples prontos.

Helpers de padronização

Usados por process_sim, process_sinasc e process_sih; são o que você reaproveita ao escrever a rotina de uma fonte ainda não coberta.

MicroSUS.processar_fonteFunction
processar_fonte(id::Symbol, df::DataFrame; verbose = true) -> DataFrame

Despacha para a rotina de padronização da fonte, se existir. Fontes sem rotina implementada devolvem o DataFrame inalterado (com um aviso).

source
MicroSUS.rotular!Function
rotular!(df, col, labels) -> df

Substitui os códigos da coluna col pelos rótulos do dicionário labels. Códigos ausentes do dicionário (ex.: "9" = ignorado) viram missing. Não faz nada se a coluna não existir no DataFrame — o layout dos arquivos do DATASUS varia entre anos.

source
MicroSUS.para_data!Function
para_data!(df, col; formato = dateformat"ddmmyyyy") -> df

Converte uma coluna de datas em texto ("01072026") para Date. Valores inválidos, vazios ou zerados viram missing. Colunas que o leitor já tipou como Date (schemas :data_ddmmyyyy/:data_yyyymmdd) passam intactas.

source
MicroSUS.para_int!Function
para_int!(df, col) / para_float!(df, col) -> df

Converte colunas numéricas armazenadas como texto. Valores já numéricos passam intactos; texto inválido vira missing.

source

rotular! converte código não mapeado em missing. É deliberado — "9", "99" e afins significam "ignorado" —, mas implica que um dicionário incompleto apaga dados válidos em silêncio. Ao escrever um dicionário novo, cubra o domínio inteiro ou deixe a coluna crua.

Encoding

MicroSUS.decodifica_textoFunction
decodifica_texto(bytes, lo, hi, encoding::Symbol) -> String

Converte bytes[lo:hi] para String UTF-8, removendo espaços à direita. encoding ∈ (:cp850, :latin1, :cp1252, :utf8, :raw). Fast path: se todos os bytes forem ASCII (caso da imensa maioria dos campos do DATASUS), não há tabela envolvida.

source
MicroSUS.encoding_do_ldidFunction
encoding_do_ldid(ldid::UInt8) -> Symbol

Encoding a partir do language driver ID (byte 29 do cabeçalho DBF). 0x00 (não especificado) cai em :cp850, que é a prática do DATASUS.

source

As tabelas MicroSUS._CP850_ALTA (128 caracteres da metade alta do CP850) e MicroSUS._CP1252_80_9F vivem em src/encoding.jl. O fast path ASCII evita qualquer lookup quando o campo não tem byte ≥ 0x80.

O descompressor DCL

O porte do blast.c está em src/dcl.jl, isolado do resto — pode ser usado para qualquer fluxo PKWare DCL, não só DATASUS:

  • MicroSUS._Huffman / MicroSUS._constroi: tabelas canônicas a partir da representação compacta do formato (byte = repetições − 1 no nibble alto, comprimento no baixo);
  • MicroSUS._decodifica: decodificação bit a bit com os códigos invertidos (peculiaridade do PKWare);
  • MicroSUS._bits: buffer de bits LSB-first;
  • janela circular de 4 KiB (_MAXWIN) com flush ao sink — a razão da memória O(1).

Os parâmetros do formato: lit (literais crus vs. codificados), dict ∈ 4..6 (log₂ do dicionário − 6), comprimentos via _LEN_BASE/_LEN_EXTRA, fim de fluxo em len == 519.

Parsers de bytes

Em src/schema.jl: MicroSUS._parse_int, MicroSUS._parse_float e MicroSUS._parse_data operam direto no Vector{UInt8} do registro (offsets do CampoDBF), sem String intermediária. Vazio, não-dígito ou data impossível → missing, nunca exceção.

O compressor dos testes

test/runtests.jl contém um compressor DCL mínimo (comprime_dcl): literais crus, matches e o código de fim, com os códigos canônicos computados das mesmas tabelas do descompressor e emitidos MSB-first invertidos. Ele existe só para permitir round-trip real nos testes — não é um compressor de verdade (não procura matches), e por isso vive fora de src/.

Invariantes úteis para PRs

  • Nenhum estágio do pipeline pode reter mais que O(tamanho_lote).
  • filtro nunca deve disparar parse de campo não consultado.
  • Campo fora do schema precisa continuar legível (tipagem do DBF).
  • ] test MicroSUS roda sem rede.