Skip to content

Commit 9936939

Browse files
author
Corey Floyd
committed
#258 Updates LinkBuilder, RelatedAttrFilterQuery, RequestMiddleware, JsonApiContext, and QueryParser to utilize new SpanSplitter library to avoid unnecessary intermediate string creation.
1 parent b85bc14 commit 9936939

File tree

6 files changed

+59
-30
lines changed

6 files changed

+59
-30
lines changed

src/JsonApiDotNetCore/Builders/LinkBuilder.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System;
2+
using System.Text;
3+
using JsonApiDotNetCore.Internal;
14
using JsonApiDotNetCore.Services;
25
using Microsoft.AspNetCore.Http;
36

@@ -20,20 +23,18 @@ public string GetBasePath(HttpContext context, string entityName)
2023
: $"{r.Scheme}://{r.Host}{GetNamespaceFromPath(r.Path, entityName)}";
2124
}
2225

23-
private string GetNamespaceFromPath(string path, string entityName)
26+
private static string GetNamespaceFromPath(string path, string entityName)
2427
{
25-
var nSpace = string.Empty;
26-
var segments = path.Split('/');
27-
28-
for (var i = 1; i < segments.Length; i++)
28+
var sb = new StringBuilder();
29+
var entityNameSpan = entityName.AsSpan();
30+
var subSpans = new SpanSplitter(ref path, '/');
31+
for (var i = 1; i < subSpans.Count; i++)
2932
{
30-
if (segments[i].ToLower() == entityName)
31-
break;
32-
33-
nSpace += $"/{segments[i]}";
33+
var span = subSpans[i];
34+
if (entityNameSpan.SequenceEqual(span)) break;
35+
sb.Append($"/{span.ToString()}");
3436
}
35-
36-
return nSpace;
37+
return sb.ToString();
3738
}
3839

3940
public string GetSelfRelationLink(string parent, string parentId, string child)

src/JsonApiDotNetCore/Extensions/StringExtensions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections.Generic;
13
using System.Text;
24

35
namespace JsonApiDotNetCore.Extensions
@@ -50,5 +52,16 @@ public static string Dasherize(this string str)
5052
}
5153
return str;
5254
}
55+
56+
public static IEnumerable<int> IndexesOf(this string str, char delimeter)
57+
{
58+
var indexes = new List<int>();
59+
for (var i = str.IndexOf(delimeter); i > -1 ; i = str.IndexOf(delimeter, i+1))
60+
{
61+
indexes.Add(i);
62+
}
63+
return indexes;
64+
}
65+
5366
}
5467
}

src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ namespace JsonApiDotNetCore.Internal.Query
88
public class RelatedAttrFilterQuery : BaseFilterQuery
99
{
1010
private readonly IJsonApiContext _jsonApiContext;
11-
11+
1212
public RelatedAttrFilterQuery(
1313
IJsonApiContext jsonApiCopntext,
1414
FilterQuery filterQuery)
1515
{
1616
_jsonApiContext = jsonApiCopntext;
17-
18-
var relationshipArray = filterQuery.Attribute.Split('.');
19-
20-
var relationship = GetRelationship(relationshipArray[0]);
17+
var filterQueryAttribute = filterQuery.Attribute;
18+
var relationshipSubSpans = new SpanSplitter(ref filterQueryAttribute, '.');
19+
var relationship1 = relationshipSubSpans[0].ToString();
20+
var relationship2 = relationshipSubSpans[1].ToString();
21+
var relationship = GetRelationship(relationshipSubSpans[0].ToString());
2122
if (relationship == null)
22-
throw new JsonApiException(400, $"{relationshipArray[1]} is not a valid relationship on {relationshipArray[0]}.");
23+
throw new JsonApiException(400, $"{relationship2} is not a valid relationship on {relationship1}.");
2324

24-
var attribute = GetAttribute(relationship, relationshipArray[1]);
25+
var attribute = GetAttribute(relationship, relationship2);
2526

2627
if (attribute == null)
2728
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");

src/JsonApiDotNetCore/Middleware/RequestMiddleware.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Threading.Tasks;
23
using JsonApiDotNetCore.Internal;
34
using Microsoft.AspNetCore.Http;
@@ -54,8 +55,11 @@ private static bool IsValidAcceptHeader(HttpContext context)
5455

5556
private static bool ContainsMediaTypeParameters(string mediaType)
5657
{
57-
var mediaTypeArr = mediaType.Split(';');
58-
return (mediaTypeArr[0] == Constants.ContentType && mediaTypeArr.Length == 2);
58+
const char delimeter = ';';
59+
var sliceLength = mediaType.IndexOf(delimeter);
60+
if (sliceLength < 0) return false;
61+
var mediaTypeSlice = mediaType.AsSpan().Slice(0, sliceLength);
62+
return mediaTypeSlice.Length == 2 && mediaTypeSlice.SequenceEqual(Constants.ContentType.AsSpan());
5963
}
6064

6165
private static void FlushResponse(HttpContext context, int statusCode)

src/JsonApiDotNetCore/Services/JsonApiContext.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public IJsonApiContext ApplyContext<T>(object controller)
6464
throw new JsonApiException(500, $"A resource has not been properly defined for type '{typeof(T)}'. Ensure it has been registered on the ContextGraph.");
6565

6666
var context = _httpContextAccessor.HttpContext;
67-
var path = context.Request.Path.Value.Split('/');
67+
var requestPath = context.Request.Path.Value;
6868

6969
if (context.Request.Query.Count > 0)
7070
{
@@ -75,10 +75,13 @@ public IJsonApiContext ApplyContext<T>(object controller)
7575
var linkBuilder = new LinkBuilder(this);
7676
BasePath = linkBuilder.GetBasePath(context, _controllerContext.RequestEntity.EntityName);
7777
PageManager = GetPageManager();
78-
IsRelationshipPath = path[path.Length - 2] == "relationships";
78+
79+
var pathSpans = new SpanSplitter(ref requestPath, '/');
80+
IsRelationshipPath = pathSpans[pathSpans.Count - 2].ToString() == "relationships";
81+
7982
return this;
8083
}
81-
84+
8285
private PageManager GetPageManager()
8386
{
8487
if (Options.DefaultPageSize == 0 && (QuerySet == null || QuerySet.PageQuery.PageSize == 0))

src/JsonApiDotNetCore/Services/QueryParser.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using JsonApiDotNetCore.Configuration;
55
using JsonApiDotNetCore.Controllers;
6+
using JsonApiDotNetCore.Extensions;
67
using JsonApiDotNetCore.Internal;
78
using JsonApiDotNetCore.Internal.Query;
89
using JsonApiDotNetCore.Models;
@@ -93,16 +94,16 @@ protected virtual List<FilterQuery> ParseFilterQuery(string key, string value)
9394
// expected input = filter[id]=1
9495
// expected input = filter[id]=eq:1
9596
var queries = new List<FilterQuery>();
97+
var openBracketIndex = key.IndexOf(OPEN_BRACKET);
98+
var closedBracketIndex = key.IndexOf(CLOSE_BRACKET);
99+
var propertyNameSlice = key.AsSpan().Slice(openBracketIndex + 1, closedBracketIndex - openBracketIndex - 1);
100+
var propertyName = propertyNameSlice.ToString();
96101

97-
var propertyName = key.Split(OPEN_BRACKET, CLOSE_BRACKET)[1];
98-
99-
var values = value.Split(COMMA);
100-
foreach (var val in values)
102+
var spanSplitter = new SpanSplitter(ref value, COMMA);
103+
for (var i = 0; i < spanSplitter.Count; i++)
101104
{
102-
(var operation, var filterValue) = ParseFilterOperation(val);
103-
queries.Add(new FilterQuery(propertyName, filterValue, operation));
105+
queries.Add(BuildFilterQuery(spanSplitter[i], propertyName));
104106
}
105-
106107
return queries;
107108
}
108109

@@ -235,5 +236,11 @@ protected virtual AttrAttribute GetAttribute(string propertyName)
235236
throw new JsonApiException(400, $"Attribute '{propertyName}' does not exist on resource '{_controllerContext.RequestEntity.EntityName}'", e);
236237
}
237238
}
239+
240+
private FilterQuery BuildFilterQuery(ReadOnlySpan<char> query, string propertyName)
241+
{
242+
var (operation, filterValue) = ParseFilterOperation(query.ToString());
243+
return new FilterQuery(propertyName, filterValue, operation);
244+
}
238245
}
239246
}

0 commit comments

Comments
 (0)