struct-align
Find and fix wasted space in your C/C++ structs — without leaving the editor.


struct-align is a real-time layout analyzer for C and C++ structs. As you
type, it highlights padding bytes the compiler silently inserted between your
fields and offers a one-click reorder that recovers them — typically 10–40%
of the struct's size, with zero functional change.
Why struct padding matters
C and C++ lay out struct fields in declaration order. Each field must sit
at an offset that is a multiple of its alignment. When the next field needs
more alignment than the running offset provides, the compiler inserts
padding bytes — silent, invisible, and free in CPU terms but costly in
memory and cache.
Consider this struct:
struct Order {
char order_type; // 1 byte
double price; // 8 bytes
uint32_t quantity; // 4 bytes
uint64_t order_id; // 8 bytes
};
It looks like 21 bytes of data. The compiler actually allocates 32 bytes
— eleven bytes are padding the developer never wrote. Reorder by alignment
(largest first) and the same data fits in 24 bytes:
struct Order {
double price;
uint64_t order_id;
uint32_t quantity;
char order_type;
};
That's 25% less memory, with identical semantics. For systems processing
millions of records — fintech order books, ECS components, network packet
buffers, blockchain account state — this compounds into measurable gains in
cache pressure, bandwidth, and dollar cost per request.
struct-align finds these wasted bytes automatically, every time you save.
Features
- Real-time padding diagnostics on every keystroke, powered by an
in-process WebAssembly analyzer. No subprocess, no filesystem access — the
analyzer is sandboxed inside the extension host.
- Quick Fix code action — apply the suggested reorder with a single
click. Preserves comments, attributes, bitfield widths, and inline
initializers.
- Hover popup showing the full byte map per struct (offset, size, field
name, type) plus a per-field summary (offset, alignment, padding before,
parent struct).
- Deep analysis (libclang) — opt-in for projects where the lightweight
analyzer can't resolve a custom type. Resolves templates, inheritance, and
stdlib types exactly, using your existing
compile_commands.json.
- Multi-platform layout: target
x86_64-linux, x86_64-windows, or
x86_32. Stdlib-aware heuristics (libstdcxx, libcxx, msvc-stl) for
std::string, std::vector<T>, and the other usual suspects.
- Honest about limits — flags bitfields, packed structs, virtual
methods, and unknown types instead of silently producing wrong numbers.
- Configurable via 5-level cascade: CLI flags > env vars > project
config > user-global config > defaults. Register custom types (opaque
handles, third-party objects) with explicit size and alignment.
Quick start
- Install the extension from the Marketplace
(or
code --install-extension vesangelov.struct-align).
- Open any
.c, .cpp, .hpp, .cc, or .h file containing a struct
declaration.
- Padding above the
structAlign.paddingThreshold (default 5%) gets a
warning squiggle on the struct name, with the savings shown inline.
- Click the lightbulb 💡 → "Reorder fields to save N bytes" to apply.
- Hover any struct or field for the full byte-map / per-field view.
For optional deep (libclang) analysis on save:
- Install
libclang-dev (Linux), Xcode Command Line Tools (macOS), or
the LLVM Windows installer.
- Build and install
struct-align-cli from
the project repo
with -DBUILD_LIBCLANG_PARSER=ON.
- Set
structAlign.deepAnalysis to on-save.
Settings
| Key |
Default |
Description |
structAlign.platform |
x86_64-linux |
Target ABI for layout calculation. |
structAlign.stdlibImpl |
libstdcxx |
Stdlib heuristic column for std::string, std::vector<T>, etc. |
structAlign.variant |
lightweight |
lightweight (WASM, real-time) or libclang (CLI subprocess, deeper). |
structAlign.paddingThreshold |
5 |
Minimum padding percent to surface as a diagnostic. |
structAlign.enableRealTime |
true |
Analyze on every edit; disable to only analyze on save. |
structAlign.deepAnalysis |
off |
When to invoke Variant B: off, on-save, or on-demand. |
structAlign.cliPath |
"" (auto-detect) |
Path to struct-align-cli. Auto-discovered from PATH and ~/.local/bin. |
structAlign.compileCommandsPath |
"" (walk up) |
Explicit compile_commands.json path. Empty = libclang walks up from the source file. |
structAlign.deepAnalysisTimeoutSec |
5 |
Hard timeout for the libclang subprocess. |
structAlign.devSchemaValidation |
false |
Validate the WASM module's JSON output against the embedded ADR-0003 schema on every analysis. Off by default; enable for dev. |
Commands
| Command |
Description |
struct-align: Analyze current file (lightweight) |
Manual one-shot of the Variant A analysis. |
struct-align: Run deep analysis (libclang) |
Manual one-shot of Variant B (no setting needed). |
struct-align: Show output channel |
Reveal the extension's "struct-align" output channel. |
How it works
The struct-align core is a C++20 layout calculator + optimizer, compiled to
WebAssembly via Emscripten. The extension's TypeScript layer loads the WASM
module lazily on first analysis, then calls it in-process — no spawn, no
file I/O, no temp files.
Deep analysis (Variant B) routes through a native struct-align-cli
subprocess that uses libclang for full type resolution. Results are merged
with Variant A diagnostics under a distinct source label so you can tell
which layer produced which warning.
Architectural decisions are recorded as ADRs in the
project repository.
Modelled constructs (v1.0)
| Construct |
Status |
| Primitive types + pointers + arrays |
✅ full layout + reorder |
Stdlib types (std::string, std::vector) |
✅ via stdlib heuristic table |
| Custom types (in-file cascade or registry) |
✅ |
| Virtual methods (vtable pointer) |
✅ modelled, pinned at offset 0 |
| Single non-virtual inheritance |
✅ modelled via __base pinned |
| Unions |
✅ size + alignment (no reorder) |
#pragma pack / __attribute__((packed)) |
✅ honored for size (no reorder) |
| Bitfields |
✅ total size (no reorder) |
[[no_unique_address]] |
✅ detect + warn |
Known limitations
- Multiple / virtual inheritance — detected and flagged with
INHERITANCE_UNSUPPORTED; layout not modelled (ADR-0012 defers this to
v2.0 because it needs non-linear field placement).
- Polymorphism + inheritance combo — same reason; vtable+base
ABI interaction is its own sub-algorithm.
- Uninstantiated templates — no concrete size, skipped with an
UNSUPPORTED_FEATURE info diagnostic. Instantiate via a using
declaration to make them visible.
- Bitfield / packed / union optimization — layout is reported
correctly but no reorder is suggested. Reordering these changes wire
format / ABI and is deliberately never proposed.
The forward roadmap lives in
ADR-0012.
Feedback & support
When reporting a bug or an incorrect layout, please include:
- The smallest struct that reproduces the issue — one file, one struct,
no external types if possible.
- Output of
struct-align: Show output channel — timestamps + the
diagnostic codes emitted make triage minutes instead of hours.
- Your
structAlign.* settings — paste the relevant block from
settings.json (particularly platform, stdlibImpl, variant,
deepAnalysis).
- Expected vs actual layout — what
sizeof/alignof your compiler
reports and what the extension reports.
Feature ideas are welcome even when incomplete. If you have a real-world
struct that hits a limitation above, an issue with the offending code +
"here's what I wish the tool said" is exactly the input v1.x releases are
prioritised from.
License
MIT © vesangelov.