Skip to content

Commit b13cb76

Browse files
authored
Merge pull request #22887 from dotnet/dev/toddgrun/TrySplitNamespaceAndTypeAllocatesTooMuch
Small memory allocation optimization in DefaultRazorTagHelperBinderPh…
2 parents 0343af1 + 43a628e commit b13cb76

File tree

2 files changed

+26
-15
lines changed

2 files changed

+26
-15
lines changed

src/Razor/Microsoft.AspNetCore.Razor.Language/src/DefaultRazorTagHelperBinderPhase.cs

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ public ComponentDirectiveVisitor(string filePath, IReadOnlyList<TagHelperDescrip
247247
{
248248
// If this is a child content tag helper, we want to add it if it's original type is in scope.
249249
// E.g, if the type name is `Test.MyComponent.ChildContent`, we want to add it if `Test.MyComponent` is in scope.
250-
TrySplitNamespaceAndType(typeName, out typeName, out var _);
250+
TrySplitNamespaceAndType(typeName, out var typeNameTextSpan, out var _);
251+
typeName = GetTextSpanContent(typeNameTextSpan, typeName);
251252
}
252253

253254
if (currentNamespace != null && IsTypeInScope(typeName, currentNamespace))
@@ -336,7 +337,8 @@ public override void VisitRazorDirective(RazorDirectiveSyntax node)
336337
{
337338
// If this is a child content tag helper, we want to add it if it's original type is in scope of the given namespace.
338339
// E.g, if the type name is `Test.MyComponent.ChildContent`, we want to add it if `Test.MyComponent` is in this namespace.
339-
TrySplitNamespaceAndType(typeName, out typeName, out var _);
340+
TrySplitNamespaceAndType(typeName, out var typeNameTextSpan, out var _);
341+
typeName = GetTextSpanContent(typeNameTextSpan, typeName);
340342
}
341343
if (typeName != null && IsTypeInNamespace(typeName, @namespace))
342344
{
@@ -350,13 +352,13 @@ public override void VisitRazorDirective(RazorDirectiveSyntax node)
350352

351353
internal static bool IsTypeInNamespace(string typeName, string @namespace)
352354
{
353-
if (!TrySplitNamespaceAndType(typeName, out var typeNamespace, out var _) || typeNamespace == string.Empty)
355+
if (!TrySplitNamespaceAndType(typeName, out var typeNamespace, out var _) || typeNamespace.Length == 0)
354356
{
355357
// Either the typeName is not the full type name or this type is at the top level.
356358
return true;
357359
}
358360

359-
return typeNamespace.Equals(@namespace, StringComparison.Ordinal);
361+
return @namespace.Length == typeNamespace.Length && 0 == string.CompareOrdinal(typeName, typeNamespace.Start, @namespace, 0, @namespace.Length);
360362
}
361363

362364
// Check if the given type is already in scope given the namespace of the current document.
@@ -366,12 +368,13 @@ internal static bool IsTypeInNamespace(string typeName, string @namespace)
366368
// Whereas `MyComponents.SomethingElse.OtherComponent` is not in scope.
367369
internal static bool IsTypeInScope(string typeName, string currentNamespace)
368370
{
369-
if (!TrySplitNamespaceAndType(typeName, out var typeNamespace, out var _) || typeNamespace == string.Empty)
371+
if (!TrySplitNamespaceAndType(typeName, out var typeNamespaceTextSpan, out var _) || typeNamespaceTextSpan.Length == 0)
370372
{
371373
// Either the typeName is not the full type name or this type is at the top level.
372374
return true;
373375
}
374376

377+
var typeNamespace = GetTextSpanContent(typeNamespaceTextSpan, typeName);
375378
var typeNamespaceSegments = typeNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries);
376379
var currentNamespaceSegments = currentNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries);
377380
if (typeNamespaceSegments.Length > currentNamespaceSegments.Length)
@@ -399,21 +402,23 @@ internal static bool IsTagHelperFromMangledClass(TagHelperDescriptor tagHelper)
399402
{
400403
// If this is a child content tag helper, we want to look at it's original type.
401404
// E.g, if the type name is `Test.__generated__MyComponent.ChildContent`, we want to look at `Test.__generated__MyComponent`.
402-
TrySplitNamespaceAndType(typeName, out typeName, out var _);
405+
TrySplitNamespaceAndType(typeName, out var typeNameTextSpan, out var _);
406+
typeName = GetTextSpanContent(typeNameTextSpan, typeName);
403407
}
404-
if (!TrySplitNamespaceAndType(typeName, out var _, out var className))
408+
if (!TrySplitNamespaceAndType(typeName, out var _, out var classNameTextSpan))
405409
{
406410
return false;
407411
}
412+
var className = GetTextSpanContent(classNameTextSpan, typeName);
408413

409414
return ComponentMetadata.IsMangledClass(className);
410415
}
411416

412417
// Internal for testing.
413-
internal static bool TrySplitNamespaceAndType(string fullTypeName, out string @namespace, out string typeName)
418+
internal static bool TrySplitNamespaceAndType(string fullTypeName, out TextSpan @namespace, out TextSpan typeName)
414419
{
415-
@namespace = string.Empty;
416-
typeName = string.Empty;
420+
@namespace = default;
421+
typeName = default;
417422

418423
if (string.IsNullOrEmpty(fullTypeName))
419424
{
@@ -442,20 +447,26 @@ internal static bool TrySplitNamespaceAndType(string fullTypeName, out string @n
442447

443448
if (splitLocation == -1)
444449
{
445-
typeName = fullTypeName;
450+
typeName = new TextSpan(0, fullTypeName.Length);
446451
return true;
447452
}
448453

449-
@namespace = fullTypeName.Substring(0, splitLocation);
454+
@namespace = new TextSpan(0, splitLocation);
450455

451456
var typeNameStartLocation = splitLocation + 1;
452457
if (typeNameStartLocation < fullTypeName.Length)
453458
{
454-
typeName = fullTypeName.Substring(typeNameStartLocation, fullTypeName.Length - typeNameStartLocation);
459+
typeName = new TextSpan(typeNameStartLocation, fullTypeName.Length - typeNameStartLocation);
455460
}
456461

457462
return true;
458463
}
464+
465+
// Internal for testing.
466+
internal static string GetTextSpanContent(TextSpan textSpan, string s)
467+
{
468+
return s.Substring(textSpan.Start, textSpan.Length);
469+
}
459470
}
460471
}
461472
}

src/Razor/Microsoft.AspNetCore.Razor.Language/test/DefaultRazorTagHelperBinderPhaseTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,8 +1359,8 @@ public void TrySplitNamespaceAndType_WorksAsExpected(string fullTypeName, bool e
13591359

13601360
// Assert
13611361
Assert.Equal(expectedResult, result);
1362-
Assert.Equal(expectedNamespace, @namespace);
1363-
Assert.Equal(expectedTypeName, typeName);
1362+
Assert.Equal(expectedNamespace, DefaultRazorTagHelperBinderPhase.ComponentDirectiveVisitor.GetTextSpanContent(@namespace, fullTypeName));
1363+
Assert.Equal(expectedTypeName, DefaultRazorTagHelperBinderPhase.ComponentDirectiveVisitor.GetTextSpanContent(typeName, fullTypeName));
13641364
}
13651365

13661366
private static RazorSourceDocument CreateComponentTestSourceDocument(string content, string filePath = null)

0 commit comments

Comments
 (0)