Skip to content

Add WriteLiteral(ReadOnlyMemory<byte>) for UTF-8 HTML literal support#66429

Draft
DamianEdwards wants to merge 4 commits intomainfrom
damianedwards/razor-utf8-literals
Draft

Add WriteLiteral(ReadOnlyMemory<byte>) for UTF-8 HTML literal support#66429
DamianEdwards wants to merge 4 commits intomainfrom
damianedwards/razor-utf8-literals

Conversation

@DamianEdwards
Copy link
Copy Markdown
Member

@DamianEdwards DamianEdwards commented Apr 23, 2026

Summary

Implements UTF-8 HTML literal support for MVC Razor views per #65605, with a direct byte write path through the entire writer chain so that UTF-8 literal bytes from Razor-compiled views flow directly to the response stream without ever being converted to a string.

The direct UTF-8 write path

At flush time, when ViewBuffer encounters a Utf8HtmlLiteralContent entry:

  1. ViewBuffer.WriteToAsync detects the writer is a PagedBufferedTextWriter
  2. PagedBufferedTextWriter.WriteUtf8Async flushes any buffered chars, then forwards to the inner writer
  3. HttpResponseStreamWriter.WriteUtf8Async flushes any pending char buffer (with encoder finalization), then writes the raw UTF-8 bytes directly to the response Stream

Result: zero string allocations, zero char-to-byte encoding — the compile-time UTF-8 bytes are copied directly to the output.

Changes

  • Utf8HtmlLiteralContent — Internal IHtmlContent wrapper carrying ReadOnlyMemory<byte> through the ViewBuffer
  • RazorPageBase.WriteLiteral(ReadOnlyMemory<byte>) — New public API. When UTF-8 + ViewBufferTextWriter, buffers as Utf8HtmlLiteralContent. Otherwise decodes to string.
  • HttpResponseStreamWriter.WriteUtf8/WriteUtf8Async — New public methods that flush pending chars then write raw UTF-8 bytes directly to the underlying stream. Throws InvalidOperationException if encoding is not UTF-8.
  • PagedBufferedTextWriter.WriteUtf8/WriteUtf8Async — Internal methods that flush buffered chars then forward UTF-8 bytes to the inner HttpResponseStreamWriter (falls back to string for other writers).
  • ViewBuffer.WriteTo/WriteToAsync — Routes Utf8HtmlLiteralContent through the direct UTF-8 path when the writer chain supports it. Avoids both string conversion and per-item FlushAsync penalty.
  • Example generated views — Manually-authored view classes simulating Razor compiler output with u8 literals

Testing

  • HttpResponseStreamWriter.WriteUtf8 tests: direct writes, ordering, non-UTF-8 rejection, disposed state (11 tests)
  • PagedBufferedTextWriter.WriteUtf8 tests: forwarding, ordering, fallback (5 tests)
  • ViewBuffer end-to-end tests: bytes flow directly to stream through full writer chain (3 tests)
  • Utf8HtmlLiteralContent unit tests (6 tests)
  • RazorPageBase.WriteLiteral(ReadOnlyMemory<byte>) unit tests (5 tests)
  • Example generated view integration tests (6 tests)
  • All existing tests pass: 490 WebUtilities + 2,605 ViewFeatures + 373 Mvc.Razor

Fixes #65605

@github-actions github-actions Bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Apr 23, 2026
@DamianEdwards DamianEdwards force-pushed the damianedwards/razor-utf8-literals branch from a0341a2 to 78fce77 Compare April 23, 2026 05:10
else
{
// For non-UTF-8 encodings or non-buffered writers, decode to string
// and use the existing string-based write path.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to defer this to the writer chain so that adjacent UTF8 literals could be coalesced and encoded in as a single block?

Comment thread src/Mvc/Mvc.Razor/src/RazorPageBase.cs Outdated
@DamianEdwards DamianEdwards force-pushed the damianedwards/razor-utf8-literals branch from 78fce77 to 050ac81 Compare April 23, 2026 05:32
Implements UTF-8 HTML literal support for MVC Razor views (issue #65605)
with a direct byte write path through the entire writer chain so that
UTF-8 literal bytes from Razor-compiled views flow directly to the
response stream without ever being converted to a string.

Changes:
- Add Utf8HtmlLiteralContent: internal IHtmlContent wrapper that carries
  ReadOnlyMemory<byte> UTF-8 literal bytes through the ViewBuffer pipeline
- Add RazorPageBase.WriteLiteral(ReadOnlyMemory<byte>): new public API
  overload that buffers UTF-8 content when encoding is UTF-8, or decodes
  to string for non-UTF-8 encodings
- Add HttpResponseStreamWriter.WriteUtf8/WriteUtf8Async: public methods
  that flush pending char data then write raw UTF-8 bytes directly to the
  underlying stream, bypassing char-to-byte encoding entirely
- Add PagedBufferedTextWriter.WriteUtf8/WriteUtf8Async: internal methods
  that flush buffered chars then forward UTF-8 bytes to the inner writer
- Special-case Utf8HtmlLiteralContent in ViewBuffer.WriteTo/WriteToAsync
  to route through the direct UTF-8 write path when the writer chain
  supports it, avoiding both string conversion and per-item flush penalty
- Add comprehensive unit tests and end-to-end tests verifying bytes flow
  directly to the response stream
- Add example manually-authored generated view classes that simulate what
  the Razor compiler will emit when it supports u8 string literals

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Mvc/Mvc.Razor/src/RazorPageBase.cs Outdated
Comment thread src/Mvc/Mvc.Razor/src/RazorPageBase.cs Outdated
/// The writer's encoding is not UTF-8.
/// </exception>
[SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "Required to maintain compatibility")]
public Task WriteUtf8Async(ReadOnlyMemory<byte> utf8Value, CancellationToken cancellationToken = default)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we should use ValueTask

DamianEdwards and others added 3 commits April 23, 2026 15:56
- Update WriteLiteral method to accept ReadOnlyMemory<byte> directly.
- Remove Utf8HtmlLiteralContent class and integrate its functionality into ViewBuffer.
- Modify ViewBufferValue to support UTF-8 encoded values.
- Update tests to reflect changes in UTF-8 handling and remove obsolete tests.
- Enhance benchmarks for UTF-8 writing performance.

Co-authored-by: Copilot <copilot@github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate MVC view buffering subsystem changes to support UTF-8 HTML literal bytes end-to-end

3 participants