Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IUtf8SpanFormattable & other updates #27

Merged
merged 9 commits into from
May 30, 2023

Conversation

DamianEdwards
Copy link
Owner

  • Adds support for IUtf8SpanFormattable in net8.0
  • Use HtmlEncoder from DI if one isn't explicitly provided to RazorSliceHttpResult
  • Refactor TextWriter output support
  • Allow passing CancellationToken to RenderAsync methods

Fixes #15

@DamianEdwards DamianEdwards force-pushed the IUtf8SpanFormattable branch from fc4b70e to eda1165 Compare May 30, 2023 15:28
@DamianEdwards DamianEdwards merged commit f2c0108 into main May 30, 2023
@DamianEdwards DamianEdwards deleted the IUtf8SpanFormattable branch May 30, 2023 15:32
Comment on lines +511 to +513
else if (value is IUtf8SpanFormattable)
{
WriteUtf8SpanFormattable((IUtf8SpanFormattable)(object)value, default, null);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

        else if (value is IUtf8SpanFormattable formattable)
        {
            WriteUtf8SpanFormattable(formattable, default, null);

Saves an unnecessary castclass call in IL, although JIT might be smart enough to remove it?

Copy link
Owner Author

Choose a reason for hiding this comment

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

I was told this is the pattern to use as JIT supports it and will avoid the boxing, etc.
Cc @stephentoub

Choose a reason for hiding this comment

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

I was told this is the pattern to use as JIT supports it and will avoid the boxing, etc.

That's correct. See the difference between M1 and M2 here:
SharpLab

Copy link
Contributor

Choose a reason for hiding this comment

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

Huh, that's interesting. Any reason the JIT can't produce the same code in both cases?

Choose a reason for hiding this comment

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

{
var buffer = ArrayPool<char>.Shared.Rent(value.Length);
value.CopyTo(buffer);
htmlEncoder.Encode(textWriter, buffer, 0, value.Length);
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like an opportunity for a new API? Renting and copying is only needed because there is an API lacking for ROS<char>

Copy link
Owner Author

Choose a reason for hiding this comment

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

API like TextEncoder.Encode(TextWriter, ReadOnlySpan<char>)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah

Copy link
Owner Author

Choose a reason for hiding this comment

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

@stephentoub thoughts?

Choose a reason for hiding this comment

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

We could possibly add a TextWriter, ReadOnlySpan<char> overload, but note that the string/char[] overloads themselves just turn around and rent an array from the pool (they also need temporary space into which to encode before then writing that encoded output to the writer).

HtmlEncoder does have an Encode that takes a ReadOnlySpan<char>, it just writes to a destination span rather than to a TextWriter.

@GrabYourPitchforks, did we consider adding a convenience span-based Encode with TextWriter? I see one mentioned in dotnet/runtime#27919 but that was closed.

{
renderTask.GetAwaiter().GetResult();
return httpContext.Response.BodyWriter.FlushAsync().AsTask();
return httpContext.Response.BodyWriter.FlushAsync(httpContext.RequestAborted).AsTask();
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can allocate since it'll create a Task<FlushResult> even though we're throwing away the result. See https://github.com/dotnet/aspnetcore/blob/main/src/Shared/ValueTaskExtensions/ValueTaskExtensions.cs for what we do in ASP.NET Core when ignoring the result to avoid the allocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use IUtf8SpanFormattable in .NET 8 once available
3 participants