Skip to content

Further reduce string allocations #32929

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

Merged
merged 2 commits into from
May 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>ASP.NET Core design time hosting infrastructure for the Razor view engine.</Description>
Expand All @@ -13,6 +13,7 @@
<Compile Include="..\..\Microsoft.AspNetCore.Razor.Language\src\CodeGeneration\CodeWriterExtensions.cs" Link="Shared\CodeWriterExtensions.cs" />
<Compile Include="..\..\Microsoft.AspNetCore.Razor.Language\src\CSharpIdentifier.cs" Link="Shared\CSharpIdentifier.cs" />
<Compile Include="..\..\Microsoft.AspNetCore.Razor.Language\src\Checksum.cs" Link="Shared\Checksum.cs" />
<Compile Include="..\..\Microsoft.AspNetCore.Razor.Language\src\StringSegment.cs" Link="Shared\Checksum.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Intermediate;

Expand Down Expand Up @@ -46,7 +47,7 @@ protected override void OnDocumentStructureCreated(
// It's possible for a Razor document to not have a file path.
// Eg. When we try to generate code for an in memory document like default imports.
var checksum = Checksum.BytesToString(codeDocument.Source.GetChecksum());
@class.ClassName = $"AspNetCore_{checksum}";
@class.ClassName = "AspNetCore_" + checksum;
}
else
{
Expand Down Expand Up @@ -94,12 +95,13 @@ private static string GetClassNameFromPath(string path)
return path;
}

var pathSegment = new StringSegment(path);
if (path.EndsWith(cshtmlExtension, StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(0, path.Length - cshtmlExtension.Length);
pathSegment = pathSegment.Subsegment(0, path.Length - cshtmlExtension.Length);
}

return CSharpIdentifier.SanitizeIdentifier(path);
return CSharpIdentifier.SanitizeIdentifier(pathSegment);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
Expand Down Expand Up @@ -187,12 +188,13 @@ private static string GetClassNameFromPath(string path)
return path;
}

var pathSegment = new StringSegment(path);
if (path.EndsWith(cshtmlExtension, StringComparison.OrdinalIgnoreCase))
{
path = path.Substring(0, path.Length - cshtmlExtension.Length);
pathSegment = pathSegment.Subsegment(0, path.Length - cshtmlExtension.Length);
}

return CSharpIdentifier.SanitizeIdentifier(path);
return CSharpIdentifier.SanitizeIdentifier(pathSegment);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Globalization;
Expand Down Expand Up @@ -33,19 +33,27 @@ private static bool IsIdentifierPartByUnicodeCategory(char character)
category == UnicodeCategory.Format; // Cf
}

public static string SanitizeIdentifier(string inputName)
public static string SanitizeIdentifier(StringSegment inputName)
{
if (string.IsNullOrEmpty(inputName))
if (StringSegment.IsNullOrEmpty(inputName))
{
return inputName;
return string.Empty;
}

var length = inputName.Length;
var prependUnderscore = false;
if (!IsIdentifierStart(inputName[0]) && IsIdentifierPart(inputName[0]))
{
inputName = "_" + inputName;
length++;
prependUnderscore = true;
}

var builder = new StringBuilder(length);
if (prependUnderscore)
{
builder.Append('_');
}

var builder = new StringBuilder(inputName.Length);
for (var i = 0; i < inputName.Length; i++)
{
var ch = inputName[i];
Expand All @@ -54,5 +62,19 @@ public static string SanitizeIdentifier(string inputName)

return builder.ToString();
}

public static void AppendSanitized(StringBuilder builder, StringSegment inputName)
{
if (!IsIdentifierStart(inputName[0]) && IsIdentifierPart(inputName[0]))
{
builder.Append('_');
}

for (var i = 0; i < inputName.Length; i++)
{
var ch = inputName[i];
builder.Append(IsIdentifierPart(ch) ? ch : '_');
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Runtime.CompilerServices;
using System.Text;

namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
Expand Down Expand Up @@ -114,6 +115,11 @@ public CodeWriter Write(string value)
return Write(value, 0, value.Length);
}

internal CodeWriter Write(StringSegment value)
{
return WriteCore(value.Buffer, value.Offset, value.Length);
}

public CodeWriter Write(string value, int startIndex, int count)
{
if (value == null)
Expand All @@ -136,6 +142,12 @@ public CodeWriter Write(string value, int startIndex, int count)
throw new ArgumentOutOfRangeException(nameof(startIndex));
}

return WriteCore(value, startIndex, count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal CodeWriter WriteCore(string value, int startIndex, int count)
{
if (count == 0)
{
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,6 @@ private bool TryComputeAttributeNames(
out BoundAttributeDescriptor changeAttribute,
out BoundAttributeDescriptor expressionAttribute)
{
valueAttributeName = null;
changeAttributeName = null;
expressionAttributeName = null;
changeAttributeNode = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ private void WriteComponentAttributeInnards(CodeRenderingContext context, Compon
context.CodeWriter.Write(".");
context.CodeWriter.Write(ComponentsApi.EventCallbackFactory.CreateMethod);

if (node.TryParseEventCallbackTypeArgument(out var argument))
if (node.TryParseEventCallbackTypeArgument(out StringSegment argument))
{
context.CodeWriter.Write("<");
context.CodeWriter.Write(argument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ private bool TryGetHtmlEntity(string content, int position, out string entity, o

// `entity` is guaranteed to be of the format &#****;
var entityValue = entity.Substring(2, entity.Length - 3);
var codePoint = -1;
int codePoint;
if (!int.TryParse(entityValue, out codePoint))
{
// If it is not an integer, check if it is hexadecimal like 0x00CD
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
Expand Down Expand Up @@ -67,7 +67,7 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
routeToken.Content[1] == '/' &&
routeToken.Content[routeToken.Content.Length - 1] == '\"')
{
var template = routeToken.Content.Substring(1, routeToken.Content.Length - 2);
var template = new StringSegment(routeToken.Content, 1, routeToken.Content.Length - 2);
@namespace.Children.Insert(index++, new RouteAttributeExtensionNode(template));
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ private void WriteComponentAttributeInnards(CodeRenderingContext context, Compon
context.CodeWriter.Write(".");
context.CodeWriter.Write(ComponentsApi.EventCallbackFactory.CreateMethod);

if (node.TryParseEventCallbackTypeArgument(out var argument))
if (node.TryParseEventCallbackTypeArgument(out StringSegment argument))
{
context.CodeWriter.Write("<");
context.CodeWriter.Write(argument);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;

namespace Microsoft.AspNetCore.Razor.Language.Components
{
internal class RouteAttributeExtensionNode : ExtensionIntermediateNode
internal sealed class RouteAttributeExtensionNode : ExtensionIntermediateNode
{
public RouteAttributeExtensionNode(string template)
public RouteAttributeExtensionNode(StringSegment template)
{
Template = template;
}

public string Template { get; }
public StringSegment Template { get; }

public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ private IEnumerable<RazorDiagnostic> Validate()
yield return diagnostic;
}

var name = Name;
StringSegment name = Name;
if (isDirectiveAttribute && name.StartsWith("@", StringComparison.Ordinal))
{
name = name.Substring(1);
name = name.Subsegment(1);
}
else if (isDirectiveAttribute)
{
Expand All @@ -201,14 +201,15 @@ private IEnumerable<RazorDiagnostic> Validate()
yield return diagnostic;
}

foreach (var character in name)
for (var i = 0; i < name.Length; i++)
{
var character = name[i];
if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeName(
_parent.GetDisplayName(),
GetDisplayName(),
name,
name.Value,
character);

yield return diagnostic;
Expand Down Expand Up @@ -237,29 +238,30 @@ private IEnumerable<RazorDiagnostic> Validate()
}
else
{
var indexerPrefix = IndexerAttributeNamePrefix;
StringSegment indexerPrefix = IndexerAttributeNamePrefix;
if (isDirectiveAttribute && indexerPrefix.StartsWith("@", StringComparison.Ordinal))
{
indexerPrefix = indexerPrefix.Substring(1);
indexerPrefix = indexerPrefix.Subsegment(1);
}
else if (isDirectiveAttribute)
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundDirectiveAttributePrefix(
_parent.GetDisplayName(),
GetDisplayName(),
indexerPrefix);
indexerPrefix.Value);

yield return diagnostic;
}

foreach (var character in indexerPrefix)
for (var i = 0; i < indexerPrefix.Length; i++)
{
var character = indexerPrefix[i];
if (char.IsWhiteSpace(character) || HtmlConventions.InvalidNonWhitespaceHtmlCharacters.Contains(character))
{
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefix(
_parent.GetDisplayName(),
GetDisplayName(),
indexerPrefix,
indexerPrefix.Value,
character);

yield return diagnostic;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1774,7 +1774,7 @@ public override void VisitMarkupMinimizedTagHelperDirectiveAttribute(MarkupMinim

IntermediateNode attributeNode;
if (parameterMatch &&
TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter, out _))
TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter))
{
var expectsBooleanValue = associatedAttributeParameterDescriptor.IsBooleanProperty;
if (!expectsBooleanValue)
Expand All @@ -1786,7 +1786,7 @@ public override void VisitMarkupMinimizedTagHelperDirectiveAttribute(MarkupMinim
attributeNode = new TagHelperDirectiveAttributeParameterIntermediateNode()
{
AttributeName = actualAttributeName,
AttributeNameWithoutParameter = attributeNameWithoutParameter,
AttributeNameWithoutParameter = attributeNameWithoutParameter.Value,
OriginalAttributeName = attributeName,
BoundAttributeParameter = associatedAttributeParameterDescriptor,
BoundAttribute = associatedAttributeDescriptor,
Expand Down Expand Up @@ -1912,12 +1912,12 @@ public override void VisitMarkupTagHelperDirectiveAttribute(MarkupTagHelperDirec

IntermediateNode attributeNode;
if (parameterMatch &&
TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter, out _))
TagHelperMatchingConventions.TryGetBoundAttributeParameter(actualAttributeName, out var attributeNameWithoutParameter))
{
attributeNode = new TagHelperDirectiveAttributeParameterIntermediateNode()
{
AttributeName = actualAttributeName,
AttributeNameWithoutParameter = attributeNameWithoutParameter,
AttributeNameWithoutParameter = attributeNameWithoutParameter.Value,
OriginalAttributeName = attributeName,
BoundAttributeParameter = associatedAttributeParameterDescriptor,
BoundAttribute = associatedAttributeDescriptor,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
Expand Down Expand Up @@ -493,7 +493,7 @@ public void WriteTagHelperRuntime(CodeRenderingContext context, DefaultTagHelper
if (!context.Options.DesignTime)
{
context.CodeWriter.WriteField(FieldUnusedModifiers, PrivateModifiers, "string", StringValueBufferVariableName);

var backedScopeManageVariableName = "__backed" + ScopeManagerVariableName;
context.CodeWriter
.Write("private ")
Expand Down Expand Up @@ -650,7 +650,7 @@ internal static string GetDeterministicId(CodeRenderingContext context)

private static string GetPropertyAccessor(DefaultTagHelperPropertyIntermediateNode node)
{
var propertyAccessor = $"{node.FieldName}.{node.PropertyName}";
var propertyAccessor = node.FieldName + "." + node.PropertyName;

if (node.IsIndexerNameMatch)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
Expand Down
Loading