Skip to content
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
2 changes: 1 addition & 1 deletion src/Umbraco.Cms.Api.Common/OpenApi/SubTypesSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public IEnumerable<Type> SubTypes(Type type)
{
// Split the path into segments
var segments = _httpContextAccessor.HttpContext.Request.Path.Value![swaggerPath.Length..]
.TrimStart(Constants.CharArrays.ForwardSlash)
.TrimStart('/')
.Split(Constants.CharArrays.ForwardSlash);

// Extract the document name from the path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,14 @@ protected sealed class Node
public static Node Parse(string value)
{
// verify that there are as many start brackets as there are end brackets
if (value.CountOccurrences("[") != value.CountOccurrences("]"))
ReadOnlySpan<char> valueAsSpan = value.AsSpan();
if (valueAsSpan.Count('[') != valueAsSpan.Count(']'))
{
throw new ArgumentException("Value did not contain an equal number of start and end brackets");
}

// verify that the value does not start with a start bracket
if (value.StartsWith("["))
if (value.StartsWith('['))
{
throw new ArgumentException("Value cannot start with a bracket");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private async Task<IActionResult> HandleRequest(string path)
return null;
}

if (Guid.TryParse(path.AsSpan(PreviewContentRequestPathPrefix.Length).TrimEnd("/"), out Guid contentId) is false)
if (Guid.TryParse(path.AsSpan(PreviewContentRequestPathPrefix.Length).TrimEnd('/'), out Guid contentId) is false)
{
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public FilterOption BuildFilterOption(string filter)
return DefaultFilterOption();
}

FilterOperation? filterOperation = ParseFilterOperation(operatorGroup.Value);
FilterOperation? filterOperation = ParseFilterOperation(operatorGroup.ValueSpan);
if (filterOperation.HasValue is false)
{
return DefaultFilterOption();
Expand All @@ -64,7 +64,7 @@ FilterOption DefaultFilterOption()
};
}

private FilterOperation? ParseFilterOperation(string filterOperation)
private static FilterOperation? ParseFilterOperation(ReadOnlySpan<char> filterOperation)
=> filterOperation switch
{
":" => FilterOperation.Is,
Expand Down
47 changes: 30 additions & 17 deletions src/Umbraco.Cms.Api.Delivery/Services/ApiMediaQueryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,21 @@ public Attempt<PagedModel<Guid>, ApiMediaQueryOperationStatus> ExecuteQuery(stri
private IPublishedMediaCache GetRequiredPublishedMediaCache()
=> _publishedMediaCache;

private IPublishedContent? TryGetByPath(string path, IPublishedMediaCache mediaCache)
private IPublishedContent? TryGetByPath(ReadOnlySpan<char> path, IPublishedMediaCache mediaCache)
{
var segments = path.Split(Constants.CharArrays.ForwardSlash, StringSplitOptions.RemoveEmptyEntries);
MemoryExtensions.SpanSplitEnumerator<char> segments = path.Split(Constants.CharArrays.ForwardSlash);
IEnumerable<IPublishedContent> currentChildren = GetRootContent(mediaCache);
IPublishedContent? resolvedMedia = null;

foreach (var segment in segments)
foreach (Range segmentRange in segments)
{
ReadOnlySpan<char> segmentSpan = path[segmentRange];
if (segmentSpan.IsEmpty)
{
continue;
}

string segment = new string(segmentSpan);
resolvedMedia = currentChildren.FirstOrDefault(c => segment.InvariantEquals(c.Name));
if (resolvedMedia is null)
{
Expand All @@ -93,15 +100,16 @@ private IPublishedMediaCache GetRequiredPublishedMediaCache()
return null;
}

var childrenOf = fetch.TrimStart(childrenOfParameter);
if (childrenOf.IsNullOrWhiteSpace())
// The guard above guarantees fetch starts with the prefix, so we can strip it unconditionally.
ReadOnlySpan<char> childrenOf = fetch.AsSpan(childrenOfParameter.Length);
if (childrenOf.IsEmpty)
{
// this mirrors the current behavior of the Content Delivery API :-)
return Array.Empty<IPublishedContent>();
return [];
}

IPublishedMediaCache mediaCache = GetRequiredPublishedMediaCache();
if (childrenOf.Trim(Constants.CharArrays.ForwardSlash).Length == 0)
if (childrenOf.Trim('/').IsEmpty)
{
return GetRootContent(mediaCache);
}
Expand All @@ -110,28 +118,31 @@ private IPublishedMediaCache GetRequiredPublishedMediaCache()
? mediaCache.GetById(parentKey)
: TryGetByPath(childrenOf, mediaCache);

return parent?.Children(_mediaNavigationQueryService, _publishedMediaStatusFilteringService) ?? Array.Empty<IPublishedContent>();
return parent?.Children(_mediaNavigationQueryService, _publishedMediaStatusFilteringService) ?? [];
}

private IEnumerable<IPublishedContent>? ApplyFilters(IEnumerable<IPublishedContent> source, IEnumerable<string> filters)
{
Span<Range> partRanges = stackalloc Range[3];
foreach (var filter in filters)
{
var parts = filter.Split(':');
if (parts.Length != 2)
ReadOnlySpan<char> filterAsSpan = filter.AsSpan();
int partsFound = filterAsSpan.Split(partRanges, ':');
if (partsFound != 2)
{
// invalid filter
_logger.LogInformation("An invalid filter option was encountered. Please ensure that supplied filter options are two-part, separated by ':'.");
return null;
}

switch (parts[0])
string secondPart = new string(filterAsSpan[partRanges[1]]);
switch (filterAsSpan[partRanges[0]])
{
case "mediaType":
source = source.Where(c => c.ContentType.Alias == parts[1]);
source = source.Where(c => c.ContentType.Alias == secondPart);
break;
case "name":
source = source.Where(c => c.Name.InvariantContains(parts[1]));
source = source.Where(c => c.Name.InvariantContains(secondPart));
break;
default:
// unknown filter
Expand All @@ -145,18 +156,20 @@ private IPublishedMediaCache GetRequiredPublishedMediaCache()

private IEnumerable<IPublishedContent>? ApplySorts(IEnumerable<IPublishedContent> source, IEnumerable<string> sorts)
{
Span<Range> partRanges = stackalloc Range[3];
foreach (var sort in sorts)
{
var parts = sort.Split(':');
if (parts.Length != 2)
ReadOnlySpan<char> sortAsSpan = sort.AsSpan();
int partsFound = sortAsSpan.Split(partRanges, ':');
if (partsFound != 2)
{
// invalid sort
_logger.LogInformation("An invalid sort option was encountered. Please ensure that the supplied sort options are two-part, separated by ':'.");
return null;
}

Func<IPublishedContent, object> keySelector;
switch (parts[0])
switch (sortAsSpan[partRanges[0]])
{
case "createDate":
keySelector = content => content.CreateDate;
Expand All @@ -176,7 +189,7 @@ private IPublishedMediaCache GetRequiredPublishedMediaCache()
return null;
}

source = parts[1].StartsWith("asc")
source = sortAsSpan[partRanges[1]].StartsWith("asc")
? source.OrderBy(keySelector)
: source.OrderByDescending(keySelector);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public RequestStartItemProvider(
return _requestedStartContent;
}

var headerValue = RequestedStartItem()?.Trim(Constants.CharArrays.ForwardSlash);
var headerValue = RequestedStartItem()?.Trim('/');
if (headerValue.IsNullOrWhiteSpace())
{
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,5 +324,5 @@ internal OpenIddictApplicationDescriptor DeveloperOpenIddictApplicationDescripto
return descriptor;
}

private static Uri CallbackUrlFor(Uri url, string relativePath) => new Uri($"{url.GetLeftPart(UriPartial.Authority)}/{relativePath.TrimStart(Constants.CharArrays.ForwardSlash)}");
private static Uri CallbackUrlFor(Uri url, string relativePath) => new Uri($"{url.GetLeftPart(UriPartial.Authority)}/{relativePath.AsSpan().TrimStart('/')}");
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ public static ThrottlingCondition FromError(SqlError? error)
{
Match match = _sqlErrorCodeRegEx.Match(error.Message);

if (match.Success && int.TryParse(match.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int reasonCode))
if (match.Success && int.TryParse(match.Groups[1].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out int reasonCode))
{
return FromReasonCode(reasonCode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ public override Sql<ISqlContext> SelectTop(Sql<ISqlContext> sql, int top)
var sb = new StringBuilder();
foreach (ColumnDefinition column in columns)
{
sb.AppendLine(", " + Format(column));
sb.Append(", ").AppendLine(Format(column));
}

return sb.ToString().TrimStart(',');
Expand Down
2 changes: 1 addition & 1 deletion src/Umbraco.Core/DeliveryApi/ApiDocumentUrlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public ApiDocumentUrlService(IDocumentUrlService documentUrlService)
{
var index = route.IndexOf('/');

if (index > -1 && int.TryParse(route.Substring(0, index), out var nodeId))
if (index > -1 && int.TryParse(route.AsSpan(0, index), out var nodeId))
{
documentStartNodeId = nodeId;
route = route.Substring(index);
Expand Down
2 changes: 1 addition & 1 deletion src/Umbraco.Core/DeliveryApi/ApiPublishedContentCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public ApiPublishedContentCache(
// e.g. "1234/second-root-url-segment". in V15+, IDocumentUrlService won't resolve this anymore; it will
// however resolve "1234/" correctly, so to remain backwards compatible, we need to perform this extra step.
var verifyUrlSegment = false;
if (documentKey is null && route.TrimEnd('/').CountOccurrences("/") is 1)
if (documentKey is null && route.AsSpan().TrimEnd('/').Count('/') is 1)
{
documentKey = _apiDocumentUrlService.GetDocumentKeyByRoute(
route[..(route.IndexOf('/') + 1)],
Expand Down
13 changes: 11 additions & 2 deletions src/Umbraco.Core/Extensions/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,18 +268,27 @@ public static string ToQueryString(this IDictionary<string, object?> d)
}

var builder = new StringBuilder();
bool first = true;
foreach (KeyValuePair<string, object?> i in d)
{
if (first)
{
first = false;
}
else
{
builder.Append('&');
}

builder.Append(WebUtility.UrlEncode(i.Key));
builder.Append('=');
if (i.Value != null)
{
builder.Append(WebUtility.UrlEncode(i.Value.ToString()));
}
builder.Append('&');
}

return builder.ToString().TrimEnd(Constants.CharArrays.Ampersand);
return builder.ToString();
}

/// <summary>The get entry ignore case.</summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ internal static class DocumentUrlAliasServiceExtensions
public static string NormalizeAlias(this IDocumentUrlAliasService service, string alias) =>
alias
.Trim()
.TrimStart('/')
.TrimEnd('/')
.Trim('/')
.ToLowerInvariant();
}
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public static string AppendQueryStringToUrl(this string url, params string[] que
for (var i = 0; i < queryStrings.Length; i++)
{
queryStrings[i] = queryStrings[i].TrimStart(Constants.CharArrays.QuestionMarkAmpersand)
.TrimEnd(Constants.CharArrays.Ampersand);
.TrimEnd('&');
}

var nonEmpty = queryStrings.Where(x => !x.IsNullOrWhiteSpace()).ToArray();
Expand Down
2 changes: 1 addition & 1 deletion src/Umbraco.Core/Extensions/UdiGetterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ public static GuidUdi GetUdi(this IWebhook entity)
/// </returns>
private static StringUdi GetUdiFromPath(string entityType, string path)
{
string id = path.TrimStart(Constants.CharArrays.ForwardSlash).Replace("\\", "/");
string id = path.TrimStart('/').Replace('\\', '/');

return new StringUdi(entityType, id).EnsureClosed();
}
Expand Down
18 changes: 9 additions & 9 deletions src/Umbraco.Core/Extensions/UriExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.

using System;
using System.Net;
using System.Web;
using Umbraco.Cms.Core;
Expand All @@ -22,7 +21,7 @@ public static class UriExtensions
/// <remarks>Everything else remains unchanged, except for the fragment which is removed.</remarks>
public static Uri Rewrite(this Uri uri, string path)
{
if (path.StartsWith("/", StringComparison.Ordinal) == false)
if (path.StartsWith('/') == false)
{
throw new ArgumentException("Path must start with a slash.", "path");
}
Expand All @@ -42,12 +41,12 @@ public static Uri Rewrite(this Uri uri, string path)
/// <remarks>Everything else remains unchanged, except for the fragment which is removed.</remarks>
public static Uri Rewrite(this Uri uri, string path, string query)
{
if (path.StartsWith("/", StringComparison.Ordinal) == false)
if (path.StartsWith('/') == false)
{
throw new ArgumentException("Path must start with a slash.", "path");
}

if (query.Length > 0 && query.StartsWith("?", StringComparison.Ordinal) == false)
if (query.Length > 0 && query.StartsWith('?') == false)
{
throw new ArgumentException("Query must start with a question mark.", "query");
}
Expand Down Expand Up @@ -113,15 +112,15 @@ public static Uri EndPathWithSlash(this Uri uri)
var path = uri.GetSafeAbsolutePath();
if (uri.IsAbsoluteUri)
{
if (path != "/" && path.EndsWith("/") == false)
if (path != "/" && path.EndsWith('/') == false)
{
uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path + "/" + uri.Query);
}

return uri;
}

if (path != "/" && path.EndsWith("/") == false)
if (path != "/" && path.EndsWith('/') == false)
{
uri = new Uri(path + "/" + uri.Query, UriKind.Relative);
}
Expand All @@ -142,15 +141,16 @@ public static Uri TrimPathEndSlash(this Uri uri)
{
if (path != "/")
{
uri = new Uri(uri.GetLeftPart(UriPartial.Authority) + path.TrimEnd(Constants.CharArrays.ForwardSlash) +
uri.Query);
uri = new Uri(
$"{uri.GetLeftPart(UriPartial.Authority)}{path.AsSpan().TrimEnd('/')}{uri.Query}"
);
}
}
else
{
if (path != "/")
{
uri = new Uri(path.TrimEnd(Constants.CharArrays.ForwardSlash) + uri.Query, UriKind.Relative);
uri = new Uri($"{path.AsSpan().TrimEnd('/')}{uri.Query}", UriKind.Relative);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Umbraco.Core/GuidUdi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public GuidUdi(string entityType, Guid guid)
public GuidUdi(Uri uriValue)
: base(uriValue)
{
if (Guid.TryParse(uriValue.AbsolutePath.TrimStart(Constants.CharArrays.ForwardSlash), out Guid guid) == false)
if (Guid.TryParse(uriValue.AbsolutePath.AsSpan().TrimStart('/'), out Guid guid) == false)
{
throw new FormatException("URI \"" + uriValue + "\" is not a GUID entity ID.");
}
Expand Down
13 changes: 6 additions & 7 deletions src/Umbraco.Core/IO/IOHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ public string FindFile(string virtualPath)
{
var retval = virtualPath;

if (virtualPath.StartsWith("~"))
if (virtualPath.StartsWith('~'))
{
retval = virtualPath.Replace("~", _hostingEnvironment.ApplicationVirtualPath);
}

if (virtualPath.StartsWith("/") && !PathStartsWith(virtualPath, _hostingEnvironment.ApplicationVirtualPath))
if (virtualPath.StartsWith('/') && !PathStartsWith(virtualPath, _hostingEnvironment.ApplicationVirtualPath))
{
retval = _hostingEnvironment.ApplicationVirtualPath + "/" +
virtualPath.TrimStart(Constants.CharArrays.ForwardSlash);
retval = $"{_hostingEnvironment.ApplicationVirtualPath}/{virtualPath.AsSpan().TrimStart('/')}";
}

return retval;
Expand Down Expand Up @@ -71,9 +70,9 @@ public string MapPath(string path)
if (_hostingEnvironment.IsHosted)
{
var result = !string.IsNullOrEmpty(path) &&
(path.StartsWith("~") || PathStartsWith(path, _hostingEnvironment.ApplicationVirtualPath))
(path.StartsWith('~') || PathStartsWith(path, _hostingEnvironment.ApplicationVirtualPath))
? _hostingEnvironment.MapPathWebRoot(path)
: _hostingEnvironment.MapPathWebRoot("~/" + path.TrimStart(Constants.CharArrays.ForwardSlash));
: _hostingEnvironment.MapPathWebRoot($"~/{path.AsSpan().TrimStart('/')}");

if (result != null)
{
Expand Down Expand Up @@ -149,7 +148,7 @@ public bool VerifyEditPath(string filePath, IEnumerable<string> validDirs)
public bool VerifyFileExtension(string filePath, IEnumerable<string> validFileExtensions)
{
var ext = Path.GetExtension(filePath);
return ext != null && validFileExtensions.Contains(ext.TrimStart(Constants.CharArrays.Period));
return ext != null && validFileExtensions.Contains(ext.TrimStart('.'));
}

/// <inheritdoc />
Expand Down
Loading
Loading