gloam 0.4.2

Loader generator for Vulkan, OpenGL, OpenGL ES, EGL, GLX, and WGL
# Contributing to gloam

## Architecture overview

The pipeline is strictly linear: **parse -> IR -> resolve -> generate**.

```
CLI args
  -> fetch/bundled XML specs
  -> parse into RawSpec (IR)
  -> resolve into FeatureSet (indexed, sorted, grouped)
  -> generate C code via minijinja templates
```

### Module map

| Module | Purpose |
|---|---|
| `main.rs` | Entry point; orchestrates CLI -> resolve -> generate |
| `cli.rs` | clap-derived CLI definitions, `ApiRequest` parsing, extension filter parsing |
| `ir.rs` | Raw intermediate representation types directly from XML (pre-resolution) |
| `bundled.rs` | Compile-time-embedded XML specs and auxiliary headers (`include_str!`) |
| `fetch.rs` | Load specs from bundled copies or remote Khronos URLs (`--fetch`) |
| `build_info.rs` | Git version metadata embedded at compile time (generated by `build.rs`) |
| `preamble.rs` | Copyright/license/provenance comment block for generated files |
| **`parse/`** | **XML -> `RawSpec` IR** |
| `parse/mod.rs` | Orchestrator; raw C text extraction, enum value computation |
| `parse/types.rs` | `<types>` -> `RawType[]` with topological sort (Kahn's algorithm) |
| `parse/enums.rs` | `<enums>` -> flat enums + Vulkan typed enum groups |
| `parse/commands.rs` | `<commands>` -> `RawCommand[]` with alias-chain fixup and Vulkan scope inference |
| `parse/features.rs` | `<feature>` + `<extension>` -> `RawFeature[]` + `RawExtension[]` |
| **`resolve/`** | **`RawSpec` + CLI args -> `FeatureSet`** |
| `resolve/mod.rs` | Three-phase orchestrator (selection -> materialization -> grouping) |
| `resolve/types.rs` | Public output types: `FeatureSet`, `Feature`, `Extension`, `Command`, etc. |
| `resolve/selection.rs` | Which features/extensions are "in" (filter, promoted, predecessors) |
| `resolve/requirements.rs` | Collects required types/enums/commands from selected features+extensions |
| `resolve/commands.rs` | Indexed `Command` entries, PFN ordering optimization, alias pair extraction |
| `resolve/pfn.rs` | PFN range tables (feature -> command index ranges) |
| `resolve/enums.rs` | Flat enums and Vulkan enum groups for the resolved set |
| `resolve/typedefs.rs` | Type list with dependency ordering and include-guard inference |
| `resolve/protect.rs` | Platform protection lattice; group-by-protection coalescing |
| `resolve/spec_info.rs` | Spec-level constants: display names, PFN prefixes, name prefixes |
| **`generator/`** | **`FeatureSet` -> output files** |
| `generator/c/mod.rs` | C generator: template rendering, function-name blob layout, aux header copying |
| `generator/c/templates/` | Minijinja templates (`header.h.j2`, `source.c.j2`, `hash_search.j2`, `library.j2`, `loader.j2`, etc.) |

### Key data types

- **`RawSpec`** (`ir.rs`): Everything parsed from one XML spec family (types,
  enums, commands, features, extensions). This is the pre-resolution IR.
- **`FeatureSet`** (`resolve/types.rs`): Fully indexed, sorted,
  protection-grouped output ready for template rendering. Contains features,
  extensions, commands, types, flat enums, enum groups, PFN range tables,
  extension index subsets, and protection-grouped lists.
- **`Command`** (`resolve/types.rs`): Indexed command with short name, PFN type
  name, parameter string, scope (for Vulkan), and protection.
- **`Protection`** (`resolve/protect.rs`): `Unconditional` or `Guarded(macros)`
  — used to emit `#ifdef` blocks in generated code.

### Resolution phases

1. **Selection**: Determine which API features and extensions are included
   based on CLI flags (`--api`, `--extensions`, `--promoted`, `--predecessors`,
   `--baseline`).
2. **Materialization**: Build indexed arrays of commands, types, enums.
   Optimize command ordering to minimize PFN range fragmentation. Build PFN
   range tables.
3. **Grouping**: Coalesce items by platform protection for `#ifdef`-correct
   header emission.

## Extension detection strategy

At load time, the generated code detects driver-supported extensions
without any string comparisons:

1. Calls `glGetIntegerv(GL_NUM_EXTENSIONS, &n)` to get the count.
2. Calls `glGetStringi(GL_EXTENSIONS, i)` for each `i`, hashes each name
   with XXH3-64 (the same algorithm used at generator time), and stores
   the hashes in a heap-allocated `uint64_t[]`.
3. Shellsorts the array in-place (Ciura gap sequence — no extra memory,
   ~160 bytes of code).
4. Binary-searches the sorted driver hashes against the pre-baked known
   extension hash table embedded in the generated source.

This gives O(n log n) total work to detect all extensions, with O(log n)
per lookup.

## Alias resolution

When `--alias` is passed, the generated loader emits a runtime resolver:
after loading all function pointers, if the canonical slot for an alias
pair is null but the alias slot was loaded by the driver (or vice versa),
the loaded pointer is propagated to both slots. This handles the case
where a driver only exposes one spelling of a promoted function.

`--alias` is a *runtime* concern — it does not affect which extensions
are selected. For selection-time alias expansion see `--promoted` and
`--predecessors`.

## Generated code internals

### Function name blob

All function names are packed into a single `kFnNameData[]` string
table with pre-computed offsets in `kFnNameOffsets[]`. This is indexed
in lockstep with `pfnArray[]` so that loading code can look up the name
for any function pointer slot by index.

### PFN range tables

Features and extensions map to contiguous ranges of command indices.
The tables are contiguous-run compressed: each entry is a
`(start_index, count)` pair. This allows bulk-loading all function
pointers for a feature or extension with a single loop over the range.

### Platform guards

Extensions that require platform-specific headers are wrapped in
`#ifdef` blocks (e.g. `VK_USE_PLATFORM_WIN32_KHR`). The resolver
groups consecutive items with identical protection into
`ProtectedGroup<T>` to minimize the number of `#ifdef`/`#endif` pairs
in the generated output.

## Khronos XML spec gotchas

The XML specs have numerous inconsistencies that the parser handles
explicitly. These are documented in code comments as "Spec gotcha #N":

1. Command alias entries lack full prototypes — walk alias chains to copy
   signatures
2. Type and enum dependency ordering requires topological sort
3. Vulkan enum groups can be empty after filtering — prune them
4. Bitwidth=64 must propagate through enum alias chains
5. GL has auto-excluded types that should be silently skipped
6. Some XML contains C++ `//` comments — rewrite to `/* */` for C99
7. macOS needs a special `ptrdiff_t` guard for GL pointer-sized types
8. `GLX_SGIX_video_source` and `GLX_SGIX_dmbuffer` are broken — silently drop
9. `WGL_ARB_extensions_string` is mandatory but might be missing — warn
11. Vulkan `<enums type="enum"|"bitmask">` should not be re-processed as flat
    constants
12. Vulkan command scope is inferred from first parameter type
    (Global/Instance/Device)
13. Duplicate enum names with conflicting values must be detected and rejected

## Testing

Integration tests live in `tests/`. They invoke the binary via
`assert_cmd`, generate into temp directories, and optionally compile the
output with `cc` when available.

| Test file | Coverage |
|---|---|
| `generate_c.rs` | GL/GLES C loader generation + compilation |
| `generate_vulkan.rs` | Vulkan-specific generation |
| `generate_wgl_glx.rs` | WGL, GLX, and cross-API edge cases |
| `predecessor_promoted.rs` | `--promoted` and `--predecessors` flag behavior |

Run the full suite:

```sh
cargo test
```

## Style notes

- **Determinism**: `IndexMap` is used throughout for insertion-order
  preservation. Never introduce `HashMap` iteration or other sources of
  non-determinism.
- **Self-contained binary**: XML specs and auxiliary headers are embedded via
  `include_str!` from `bundled/`.
- **Progress output**: goes to stderr, respects `--quiet`. Only errors use
  `eprintln!` unconditionally.
- **Preamble**: generated files include the exact command line, gloam version,
  extension selection summary, and license notices.
- **Minimal changes**: prefer small, focused changes. Don't over-abstract or
  add speculative infrastructure.