Saltar para o conteúdo
Ridgun
Português

27 O Livro do Ridgun

A lib uuid — RFC 9562 UUIDv4/UUIDv7, FNV-1a, Adler-32, CRC-32 e PRNG

27.1. Por que existe a lib uuid? (O problema real e a filosofia Ridgun)

«Por que preciso de uma lib uuid escrita de raiz em Ridgun puro em vez de ligar uma biblioteca C tradicional?»

Essa é a primeira pergunta que qualquer engenheiro de sistemas faz ao chegar à uuid. E a resposta resume toda a filosofia do ridgun-libs:

Sejamos sinceros: usar UUIDv4 totalmente aleatórios como chave primária numa base de dados fragmenta os índices B-Tree, enquanto inventar IDs caseiros sem entropia do kernel acaba em colisões.

A lib uuid implementa UUIDv4 (aleatório com CSPRNG do kernel) e UUIDv7 (ordenado cronologicamente nos primeiros 48 bits segundo a RFC 9562), checksums rápidos (FNV-1a, Adler-32, CRC-32 IEEE 802.3) e um PRNG determinístico com semente.

Num ecossistema tradicional, resolver as tarefas do domínio UUID implica arrastar dependências externas em C, lidar com ponteiros brutos, conversões opacas de tipos, exceções não verificadas ou pausas imprevisíveis do coletor de lixo. Em Ridgun, a lib uuid (src/main/ridgun/lib/uuid.rg) aborda o problema com quatro princípios inegociáveis:

  1. Declare import uuid (ou import uuid as alias) no início do ficheiro: cada lib define o seu espaço de nomes module :: uuid e em modo --strict as funções multi-palavra são sempre invocadas com espaços naturais.
  2. Encadeie os seus pipelines da esquerda para a direita com ->: escreva entrada passo um -> passo dois -> resultado, use máscaras (+ _ _ +) para desestruturar sequências e omita _ = em instruções sequenciais (ou descarte com -> ()).
  3. Recursão de cauda com TCO (Tail-Call Optimization): todas as travessias internas da uuid são compiladas para ciclos nativos em espaço de pilha $O(1)$.
  4. Memória determinística (RMM1 / RMM2): cada estrutura intermédia é libertada no instante exato em que termina a sua última leitura, sem Garbage Collector.

27.2. Artefactos nativos, importação e catálogo completo da API de uuid

No ridgun-libs, uuid é uma lib de primeira classe localizada em src/main/ridgun/lib/uuid.rg (domínio UUID). Ao compilar com stable bundle, é empacotada em três formatos nativos:

ArtefactoCaminhoFinalidade
Bundle Ridguntarget/bundles/uuid.rglibDistribuição e importação direta com import uuid
Ficheiro compiladotarget/uuid.rgcArtefacto intermédio verificado pelo ridgunc
Objeto partilhado ELF64target/libuuid.soBiblioteca dinâmica nativa sem dependências de C nem libc

Cada lib declara o seu próprio espaço de nomes com module :: uuid: basta escrever import uuid (ou import uuid as alias) no topo e invocar as suas funções com espaços naturais (minha funcao x) e pipelines -> (x minha funcao -> resultado).

27.2.1. Resumo das famílias funcionais da uuid

Assinatura / Função / TipoO que faz e quando usar
uuid v4 / uuid v7 / uuid v7 atGera UUIDs canónicos de 36 caracteres RFC 9562 v4 (aleatório) e v7 (ordenado no tempo)
uuid is valid / uuid versionValida o formato 8-4-4-4-12 e os bits de variante RFC 9562 (8, 9, a, b)
fnv1a 32 / adler32 / crc32 / crc32 hexHashes não criptográficos e checksums rápidos de 32 bits
prng next / prng rangePRNG congruencial linear funcional puro para testes e simulações reprodutíveis

27.2.2. Catálogo exaustivo de tipos e assinaturas em src/main/ridgun/lib/uuid.rg

Abaixo encontra-se o inventário completo de tipos algébricos (type), operadores (op ::) e assinaturas de funções (fn ::) definidos em src/main/ridgun/lib/uuid.rg (0 tipos, 0 operadores e 33 funções):

#Função / Assinatura (fn ::)Tipo de Retorno
1fn :: uu hex lut IntString
2fn :: uu u32 IntInt
3fn :: uu bxor loop Int Int Int IntInt
4fn :: uu bxor32 Int IntInt
5fn :: uu fnv loop String Int Int IntInt
6fn :: fnv1a 32 StringInt
7fn :: uu adler loop String Int Int Int IntInt
8fn :: adler32 StringInt
9fn :: uu crc bit loop Int IntInt
10fn :: uu crc byte loop String Int Int IntInt
11fn :: crc32 StringInt
12fn :: uu abs IntInt
13fn :: prng next IntInt
14fn :: uu prng range step Int Int IntInt Int
15fn :: prng range Int Int IntInt Int
16fn :: uu alloc page IntInt
17fn :: uu free page Int Int IntInt
18fn :: uu alloc mfdInt
19fn :: uu peek Int Int IntString
20fn :: uu decode u32 le String IntInt
21fn :: uu decode u64 le String IntInt
22fn :: uu wall time msInt
23fn :: uu bytes to hex loop String Int IntString
24fn :: uu random hex16String
25fn :: uu hex val IntInt
26fn :: uu variant char StringString
27fn :: uuid v4String
28fn :: uu hex48 loop Int IntString
29fn :: uuid v7 at IntString
30fn :: uuid v7String
31fn :: uu is hex range String Int IntBool
32fn :: uuid is valid StringBool
33fn :: uuid version StringInt

27.3. Explorando a uuid a fundo: Exemplos passo a passo de cada funcionalidade

Não basta ver a tabela de assinaturas: vamos explorar cada uma das funcionalidades da lib uuid através de exemplos progressivos e executáveis em Ridgun. Começaremos pelas duas receitas tabulares de uso diário e depois percorreremos em detalhe todos os cenários funcionais, transformações e casos de fronteira verificados em src/test/ridgun/test uuid.rg.

27.3.1. Exemplo 1: Fluxo principal em estilo tabular com uuid

Repare como em Ridgun os sinais = e os passos de transformação com uuid ficam alinhados como uma tabela limpa, sem variáveis mutáveis desnecessárias nem efeitos colaterais ocultos:

import uuid

fn :: generar ids ordenados => String String Bool
      generar ids ordenados =
        1000 uuid v7 at -> id1
        2000 uuid v7 at -> id2
        (uuid is valid id1) and (id1 < id2) -> ok
        (id1, id2, ok)

27.3.2. Exemplo 2: Composição segura e tratamento de casos de fronteira

Todas as funções da lib uuid foram desenhadas para se comporem com guardas booleanas | e com as restantes 32 libs do ridgun-libs:

import uuid

fn :: firmar paquete String => Int Int
      firmar paquete payload =
        payload crc32 -> c32
        payload fnv1a 32 -> fnv
        (c32, fnv)

27.3.3. Exemplo 3: Geração, validação e inspeção de UUIDv4 (CSPRNG) e UUIDv7 ordenáveis por tempo (RFC 9562)

Neste exemplo prático exploramos de forma exaustiva como a uuid se comporta neste bloco funcional, incluindo entradas padrão, combinações de parâmetros e valores limite:

fn :: test uuidv4 and uuidv7 => ()
      test uuidv4 and uuidv7 =
        u4a = uuid v4
        u4b = uuid v4
        assert int equals (str len u4a) 36 "AC1: uuid_v4 length 36"
        assert true (uuid is valid u4a) "AC1: uuid_v4 valid RFC 9562 format"
        assert true (uuid is valid u4b) "AC1: second uuid_v4 valid"
        assert false (u4a == u4b) "AC1: CSPRNG uuid_v4 calls are unique"
        assert int equals (uuid version u4a) 4 "AC1: uuid_v4 version == 4"

        -- RFC 9562 UUIDv7 timestamp encoding & chronological prefix ordering
        u7_zero = uuid v7 at 0
        u7a = uuid v7 at 1700000000000
        u7b = uuid v7 at 1800000000000
        u7_live = uuid v7
        assert true (uuid is valid u7_zero) "AC1: uuid_v7 at epoch 0 valid"
        assert str eq (str slice u7_zero 0 13) "00000000-0000" "AC1: uuid_v7 at epoch 0 zero prefix"
        assert true (uuid is valid u7a) "AC1: uuid_v7 at 1700000000000 valid"
        assert true (uuid is valid u7b) "AC1: uuid_v7 at 1800000000000 valid"
        assert true (uuid is valid u7_live) "AC1: live uuid_v7 valid"
        assert int equals (uuid version u7a) 7 "AC1: uuid_v7 version == 7"
        assert int equals (uuid version u7_live) 7 "AC1: live uuid_v7 version == 7"
        assert str eq (str slice u7a 0 8) "018bcfe5" "AC1: uuid_v7 timestamp prefix 1700000000000"
        assert str eq (str slice u7b 0 8) "01a3185c" "AC1: uuid_v7 timestamp prefix 1800000000000"

        -- Uppercase valid UUID acceptance & RFC 9562 variant nibble (8, 9, a, b) validation
        assert true (uuid is valid "550E8400-E29B-41D4-A716-446655440000") "AC1: uppercase RFC 9562 UUID valid"
        assert false (uuid is valid "") "AC1: empty string invalid"
        assert false (uuid is valid "not-a-uuid") "AC1: short string invalid"
        assert false (uuid is valid "550e8400-e29b-41d4-1716-446655440000") "AC1: variant nibble '1' (< 8) rejected"
        assert false (uuid is valid "550e8400-e29b-41d4-c716-446655440000") "AC1: variant nibble 'c' (> b) rejected"
        assert false (uuid is valid "550e8400Xe29b-41d4-a716-446655440000") "AC1: missing hyphen at index 8 rejected"
        assert false (uuid is valid "550e8400-e29b-41d4-a716-44665544000g") "AC1: non-hex character 'g' rejected"
        assert int equals (uuid version "not-a-uuid") (0 - 1) "AC1: invalid uuid version -> -1"
        ()

27.3.4. Exemplo 4: Somas de verificação FNV-1a, DJB2, Adler-32, CRC32 IEEE 802.3 (0xCBF43926) e PRNG determinístico SplitMix64

Neste exemplo prático exploramos de forma exaustiva como a uuid se comporta neste bloco funcional, incluindo entradas padrão, combinações de parâmetros e valores limite:

fn :: test checksums and prng => ()
      test checksums and prng =
        -- Official FNV-1a 32-bit vectors (isthe.com/chongo/tech/comp/fnv/)
        assert int equals (fnv1a 32 "") 2166136261 "AC2: FNV-1a 32 '' == 0x811c9dc5 (offset basis)"
        assert int equals (fnv1a 32 "a") 3826002220 "AC2: FNV-1a 32 'a' == 0xe40c292c"
        assert int equals (fnv1a 32 "foobar") 3214735720 "AC2: FNV-1a 32 'foobar' == 0xbf9cf968"

        -- Official RFC 1950 Adler-32 vectors
        assert int equals (adler32 "") 1 "AC2: Adler-32 '' == 1"
        assert int equals (adler32 "a") 6422626 "AC2: Adler-32 'a' == 0x00620062"
        assert int equals (adler32 "Wikipedia") 300286872 "AC2: Adler-32 'Wikipedia' == 0x11e60398"

        -- Official IEEE 802.3 / ISO-HDLC CRC-32 vectors
        assert int equals (crc32 "") 0 "AC2: CRC-32 '' == 0"
        assert int equals (crc32 "a") 3904355907 "AC2: CRC-32 'a' == 0xe8b7be43"
        assert int equals (crc32 "123456789") 3421780262 "AC2: CRC-32 '123456789' == 0xcbf43926"

        -- Deterministic 31-bit LCG PRNG
        s1 = prng next 42
        s2 = prng next 42
        s3 = prng next s1
        assert int equals s1 s2 "AC3: prng_next deterministic for same seed"
        assert false (s1 == s3) "AC3: prng_next advances state"
        r1 = prng range 42 10 20
        v1 = prng val r1
        assert true (v1 >= 10 and v1 <= 20) "AC3: prng_range within [10, 20]"
        r_deg = prng range 42 50 10
        assert int equals (prng val r_deg) 50 "AC3: prng_range with hi <= lo clamps to lo"
        ()

27.3.5. Exemplo 5: Rastreabilidade distribuída com UUIDv7 ordenáveis por tempo e integridade de pacotes com CRC32 e FNV-1a (uuid v7, uuid v7 timestamp ms, crc32 hex)

Neste exemplo prático exploramos de forma exaustiva como a uuid se comporta neste bloco funcional, incluindo entradas padrão, combinações de parâmetros e valores limite:

import uuid

fn :: sellar paquete trazado (ahora_ms : Int) (payload : String) => (String, Int, String, Int)
      sellar paquete trazado ahora_ms payload =
        trace_id = uuid v7 ahora_ms
        ts_extra = uuid v7 timestamp ms trace_id
        crc_hex  = crc32 hex payload
        hash_fnv = fnv1a 64 payload
        (trace_id, ts_extra, crc_hex, hash_fnv)

27.4. Debaixo do capô: como a uuid é construída em Ridgun puro (src/main/ridgun/lib/uuid.rg)

Uma das maiores vantagens do ridgun-libs é que não existem caixas-pretas em C. Pode abrir src/main/ridgun/lib/uuid.rg e ler exatamente como cada operação é implementada usando pattern matching, árvores de guardas | / |. e recursão de cauda (TCO). Vejamos as peças-chave da sua implementação real:

27.4.1. Implementação interna: RFC 9562 UUIDv4 & UUIDv7 Generation & Validation (CSPRNG + clock_gettime)

Observe como uuid.rg estrutura primeiro as assinaturas fn :: ... => ... e resolve cada caso através de equações alinhadas e chamadas de cauda:

module :: uuid

fn :: uu alloc page Int => Int
      uu alloc page sz  =
        neg1 = 0 - 1
        sys call 9 0 sz 3 34 neg1 0

fn :: uu free page Int Int Int => Int
      uu free page mfd ptr sz =
        sys close mfd -> ()
        sys call 11 ptr sz 0 0 0 0

fn :: uu alloc mfd => Int
      uu alloc mfd =  sys call 319 4194311 1 0 0 0 0

fn :: uu peek Int Int Int => String
      uu peek mfd ptr len =
        sys call 18 mfd ptr len 0 0 0 -> ()
        sys call 8 mfd 0 0 0 0 0 -> ()
        sys read fd mfd len

fn :: uu decode u32 le String Int => Int
      uu decode u32 le s off =
        b0 = str char at s off
        b1 = str char at s (off + 1)
        b2 = str char at s (off + 2)
        b3 = str char at s (off + 3)
        b0 + (b1 * 256) + (b2 * 65536) + (b3 * 16777216)

fn :: uu decode u64 le String Int => Int
      uu decode u64 le s off =
        uu decode u32 le s off -> lo
        uu decode u32 le s (off + 4) -> hi
        lo + (hi * 4294967296)

fn :: uu wall time ms => Int
      uu wall time ms =
        uu alloc page 4096 -> ptr
        uu alloc mfd -> mfd
        sys call 228 0 ptr 0 0 0 0 -> ()
        uu peek mfd ptr 16 -> raw
        uu free page mfd ptr 4096 -> ()
        uu decode u64 le raw 0 -> sec
        uu decode u64 le raw 8 -> nsec
        (sec * 1000) + (nsec / 1000000)

fn :: uu bytes to hex loop String Int Int => String
      uu bytes to hex loop s idx len =
        | idx >= len = ""
        | otherwise =
          b   = ((str char at s idx) % 256 + 256) % 256
          hi  = b / 16
          lo  = b % 16
          tbl = uu hex lut 0
          (str slice tbl hi (hi + 1)) + (str slice tbl lo (lo + 1)) + (uu bytes to hex loop s (idx + 1) len)

fn :: uu random hex16 => String
      uu random hex16 =
        uu alloc page 4096 -> ptr
        uu alloc mfd -> mfd
        sys call 318 ptr 16 0 0 0 0 -> ()
        uu peek mfd ptr 16 -> raw
        uu free page mfd ptr 4096 -> ()
        uu bytes to hex loop raw 0 16

fn :: uu hex val Int => Int
      uu hex val c   =
        | c >= 48 and c <= 57  = c - 48
        | c >= 65 and c <= 70  = (c - 65) + 10
        | c >= 97 and c <= 102 = (c - 97) + 10
        | otherwise            = 0 - 1

fn :: uu variant char String => String
      uu variant char hex1   =
        v   = uu hex val (str char at hex1 0)
        idx = 8 + (v % 4)
        tbl = uu hex lut 0
        str slice tbl idx (idx + 1)

fn :: uuid v4 => String
      uuid v4 =
        h  = uu random hex16
        p1 = str slice h 0 8
        p2 = str slice h 8 12
        p3 = "4" + (str slice h 13 16)
        p4 = (uu variant char (str slice h 16 17)) + (str slice h 17 20)
        p5 = str slice h 20 32
        "{p1}-{p2}-{p3}-{p4}-{p5}"

fn :: uu hex48 loop Int Int => String
      uu hex48 loop val rem =
        | rem <= 0 = ""
        | otherwise =
          nib = ((val % 16) + 16) % 16
          tbl = uu hex lut 0
          (uu hex48 loop (val / 16) (rem - 1)) + (str slice tbl nib (nib + 1))

fn :: uuid v7 at Int   => String
      uuid v7 at ts_ms =
        eff_ts = uu abs ts_ms
        ts_hex = uu hex48 loop eff_ts 12
        h      = uu random hex16
        p1     = str slice ts_hex 0 8
        p2     = str slice ts_hex 8 12
        p3     = "7" + (str slice h 0 3)
        p4     = (uu variant char (str slice h 3 4)) + (str slice h 4 7)
        p5     = str slice h 7 19
        "{p1}-{p2}-{p3}-{p4}-{p5}"

fn :: uuid v7 => String
      uuid v7 =  uuid v7 at (uu wall time ms)

fn :: uu is hex range String Int Int => Bool
      uu is hex range s idx end_idx =
        | idx >= end_idx                       = True
        | (uu hex val (str char at s idx)) < 0 = False
        | otherwise                            = uu is hex range s (idx + 1) end_idx

fn :: uuid is valid String => Bool
      uuid is valid u      =
        | (str len u) != 36               = False
        | (str char at u 8) != 45         = False
        | (str char at u 13) != 45        = False
        | (str char at u 18) != 45        = False
        | (str char at u 23) != 45        = False
        | not (uu is hex range u 0 8)     = False
        | not (uu is hex range u 9 13)    = False
        | not (uu is hex range u 14 18)   = False
        | not (uu is hex range u 19 23)   = False
        | not (uu is hex range u 24 36)   = False
        | otherwise =
          var_nib = uu hex val (str char at u 19)
          (var_nib >= 8) and (var_nib <= 11)

fn :: uuid version String => Int
      uuid version u      =
        | not (uuid is valid u) = 0 - 1
        | otherwise             = uu hex val (str char at u 14)

27.5. Garantias algorítmicas, memória RMM1/RMM2 e as 5 Regras de Ouro da uuid

  • Garantia algorítmica e de segurança: uuid v7 at t1 < uuid v7 at t2 garante ordem lexicográfica monótona crescente ao longo do tempo, ideal para índices btree e bases de dados.
  • Complexidade de pilha $O(1)$ com TCO: Todos os ciclos recursivos em src/main/ridgun/lib/uuid.rg estão em posição de cauda, pelo que o ridgunc os compila para saltos nativos sem crescimento de pilha.
  • Memória determinística (RMM1/RMM2): Zero pausas de coletor de lixo; todas as estruturas intermédias são libertadas de forma determinística à saída do escopo.
  • Verificação contínua: Testada exaustivamente com stable check e pela suite automatizada src/test/ridgun/test uuid.rg.

As 5 Regras de Ouro ao programar com a uuid

  1. Declare import uuid no início do ficheiro e aproveite a equivalência entre espaços e underscores para que o seu código se leia como prosa.
  2. Alinhe os seus pipelines de dados com =: encadeie as chamadas à uuid em passos nomeados e deixe o stable format alinhar as colunas.
  3. Aproveite as funções totais da uuid nas suas guardas |: verifique os valores de retorno ou desempacote os ADTs com pattern matching em vez de assumir que a entrada externa é sempre válida.
  4. Evite copiar grandes estruturas se só precisa de consultar um campo: lembre-se de que no RMM2 os argumentos de entrada não incrementam nem decrementam contadores (+0 / -0).
  5. Combine a uuid com test (import test): escreva sempre asserções com assert equals ou assert true para fixar os invariantes do seu domínio.

No Capítulo 28 passaremos a explorar em profundidade a lib log (La lib log — Logging Estruturado NDJSON e Redação Automática de Segredos).