feat!: stack-based format buffer for wdk crate #611
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new fixed-size, stack-friendly formatting buffer (WdkFormatBuffer) to support heap-free formatting in driver contexts, and integrates it into the kernel print!/println! path by replacing the previous DbgPrintBufWriter implementation.
Changes:
- Added
crates/wdk/src/fmt.rsimplementingWdkFormatBuffer<const T: usize = 512>withfmt::Write, plusas_str()/as_cstr()helpers and tests. - Updated
crates/wdk/src/print.rs(WDM/KMDF path) to format intoWdkFormatBufferand callDbgPrintdirectly; removed the old buffering/flushing module. - Adjusted the KMDF sample driver to add an explicit type annotation for the
WdfDriverCreateoutput handle pointer.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| examples/sample-kmdf-driver/src/lib.rs | Tweaks the WdfDriverCreate out-handle variable typing. |
| crates/wdk/src/print.rs | Switches kernel printing to WdkFormatBuffer + direct DbgPrint. |
| crates/wdk/src/lib.rs | Adds the new fmt module and re-exports WdkFormatBuffer. |
| crates/wdk/src/fmt.rs | New formatting buffer type, CStr/str views, and unit tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #611 +/- ##
==========================================
+ Coverage 77.47% 78.44% +0.97%
==========================================
Files 24 25 +1
Lines 4848 5201 +353
Branches 4848 5201 +353
==========================================
+ Hits 3756 4080 +324
- Misses 975 1001 +26
- Partials 117 120 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
wdk crate
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8050685 to
74a4e88
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds and integrates `wdk::fmt::FormatBuffer`. ## Summary - Introduced `crates/wdk/src/fmt.rs` with a public `wdk::fmt` module containing two types: - `FormatBuffer<N>` — a fixed-size, stack-allocated formatting buffer implementing `fmt::Write` - `FlushableFormatBuffer<F, N>` — a wrapper that auto-flushes via closure on overflow and on drop - Swapped `print.rs` WDM/KMDF path to use `FlushableFormatBuffer` instead of `DbgPrintBufWriter`; removed that module and its tests. - Turns the `alloc` feature from the `wdk` crate into a no-op — it had no remaining uses after the stack-buffer migration. Avoids removal of feature to prevent a breaking change - Bumps MSRV from 1.85 to 1.91 due to use of [`str::char_floor_boundary`](https://doc.rust-lang.org/core/primitive.str.html#method.floor_char_boundary) ## Details - `FormatBuffer<N>` stores a zero-initialized `[u8; N]` and tracks `used` bytes. The last byte is reserved for a NUL terminator, so usable capacity is `N - 1`. `N` must be at least 2; smaller values will not compile (`const { assert!(N >= 2) }`). - All buffer mutations go through a single `append_bytes` helper that copies data and sets `buffer[used] = 0`, centralizing the NUL terminator invariant. - `as_str()` returns an infallible UTF-8 view — only valid UTF-8 enters via `write_str` (both `FormatBuffer::write_str` and `FlushableFormatBuffer::write_str` copy from `&str` sources). - `as_c_str()` returns a `&CStr` view up to the first NUL. Infallible in practice since the NUL invariant is always maintained. - `fmt::Write` clamps overflow writes at a UTF-8 char boundary via `floor_char_boundary` and signals truncation via `fmt::Error`. - `FlushableFormatBuffer<F, N>` wraps `FormatBuffer` and auto-flushes via a closure when the buffer fills. Remaining content is flushed on drop. This allows arbitrarily long formatted output to be processed in fixed-size chunks. - `print.rs` WDM/KMDF path now formats into `FlushableFormatBuffer<_, 512>` and calls `DbgPrint` via `%s` using the resulting `CStr`. - Removed the `alloc` feature gate from the WDM/KMDF print path — `print!`/`println!` no longer require heap allocation and work at any IRQL where `DbgPrint` is valid. - Custom `Debug` impl on `FormatBuffer` shows only the used content, not stale bytes after `clear()`. ## Testing 29 tests covering: - basic write, `as_str()`, `as_c_str()` usage - overflow, multi-write, and exact-fit scenarios - empty writes and min-sized buffers (`N = 2`) - clear-then-shorter-write regression (NUL terminator correctness) - multi-byte UTF-8 char boundary handling in both buffer types - explicit `flush()` mid-stream and flush-on-drop - interior NUL truncation via `as_c_str()` - `compile_fail` doctest for `N < 2` ```pwsh cargo test --package wdk --lib --all-features -- fmt --nocapture ``` ## Notes / Follow-ups - `_print` ignores `fmt::write` errors — partial output is acceptable for debug printing. Errors from individual `Display` impls are silently dropped. - The old `DbgPrintBufWriter` stripped interior NUL bytes and continued printing. The new WDM/KMDF path truncates each chunk at the first NUL (via `as_c_str`). UMDF still strips NULs and prints the remainder. --------- Signed-off-by: Leon Durrenberger <leon.durrenberger@gmail.com> Co-authored-by: leon-xd <leondu@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Adds and integrates
wdk::fmt::FormatBuffer.Summary
crates/wdk/src/fmt.rswith a publicwdk::fmtmodule containing two types:FormatBuffer<N>— a fixed-size, stack-allocated formatting buffer implementingfmt::WriteFlushableFormatBuffer<F, N>— a wrapper that auto-flushes via closure on overflow and on dropprint.rsWDM/KMDF path to useFlushableFormatBufferinstead ofDbgPrintBufWriter; removed that module and its tests.allocfeature from thewdkcrate into a no-op — it had no remaining uses after the stack-buffer migration. Avoids removal of feature to prevent a breaking changestr::char_floor_boundaryDetails
FormatBuffer<N>stores a zero-initialized[u8; N]and tracksusedbytes. The last byte is reserved for a NUL terminator, so usable capacity isN - 1.Nmust be at least 2; smaller values will not compile (const { assert!(N >= 2) }).append_byteshelper that copies data and setsbuffer[used] = 0, centralizing the NUL terminator invariant.as_str()returns an infallible UTF-8 view — only valid UTF-8 enters viawrite_str(bothFormatBuffer::write_strandFlushableFormatBuffer::write_strcopy from&strsources).as_c_str()returns a&CStrview up to the first NUL. Infallible in practice since the NUL invariant is always maintained.fmt::Writeclamps overflow writes at a UTF-8 char boundary viafloor_char_boundaryand signals truncation viafmt::Error.FlushableFormatBuffer<F, N>wrapsFormatBufferand auto-flushes via a closure when the buffer fills. Remaining content is flushed on drop. This allows arbitrarily long formatted output to be processed in fixed-size chunks.print.rsWDM/KMDF path now formats intoFlushableFormatBuffer<_, 512>and callsDbgPrintvia%susing the resultingCStr.allocfeature gate from the WDM/KMDF print path —print!/println!no longer require heap allocation and work at any IRQL whereDbgPrintis valid.Debugimpl onFormatBuffershows only the used content, not stale bytes afterclear().Testing
29 tests covering:
as_str(),as_c_str()usageN = 2)flush()mid-stream and flush-on-dropas_c_str()compile_faildoctest forN < 2Notes / Follow-ups
_printignoresfmt::writeerrors — partial output is acceptable for debug printing. Errors from individualDisplayimpls are silently dropped.DbgPrintBufWriterstripped interior NUL bytes and continued printing. The new WDM/KMDF path truncates each chunk at the first NUL (viaas_c_str). UMDF still strips NULs and prints the remainder.