Skip to content

Commit cee11aa

Browse files
authored
Merge pull request #271 from crfloyd/feature/#258
Feature/#258
2 parents 015938f + 369860c commit cee11aa

14 files changed

+234
-25
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ services:
66
before_script:
77
- psql -c 'create database JsonApiDotNetCoreExample;' -U postgres
88
mono: none
9-
dotnet: 2.0.3 # https://www.microsoft.com/net/download/linux
9+
dotnet: 2.1.105 # https://www.microsoft.com/net/download/linux
1010
branches:
1111
only:
1212
- master
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
```ini
2+
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
3+
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
4+
.NET Core SDK=2.1.4
5+
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
6+
DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
7+
```
8+
9+
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
10+
| ---------- | --------: | ---------: | ---------: | -----: | --------: |
11+
| UsingSplit | 421.08 ns | 19.3905 ns | 54.0529 ns | 0.4725 | 744 B |
12+
| Current | 52.23 ns | 0.8052 ns | 0.7532 ns | - | 0 B |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
``` ini
2+
3+
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
4+
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
5+
.NET Core SDK=2.1.4
6+
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
7+
Job-XFMVNE : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
8+
9+
LaunchCount=3 TargetCount=20 WarmupCount=10
10+
11+
```
12+
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
13+
|--------------------------- |-----------:|----------:|----------:|-------:|----------:|
14+
| UsingSplit | 1,197.6 ns | 11.929 ns | 25.933 ns | 0.9251 | 1456 B |
15+
| UsingSpanWithStringBuilder | 1,542.0 ns | 15.249 ns | 33.792 ns | 0.9460 | 1488 B |
16+
| UsingSpanWithNoAlloc | 272.6 ns | 2.265 ns | 5.018 ns | 0.0863 | 136 B |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
``` ini
2+
3+
BenchmarkDotNet=v0.10.10, OS=Mac OS X 10.12
4+
Processor=Intel Core i5-5257U CPU 2.70GHz (Broadwell), ProcessorCount=4
5+
.NET Core SDK=2.1.4
6+
[Host] : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
7+
DefaultJob : .NET Core 2.0.5 (Framework 4.6.0.0), 64bit RyuJIT
8+
9+
10+
```
11+
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
12+
|----------- |----------:|----------:|----------:|-------:|----------:|
13+
| UsingSplit | 157.28 ns | 2.9689 ns | 5.8602 ns | 0.2134 | 336 B |
14+
| Current | 39.96 ns | 0.6489 ns | 0.6070 ns | - | 0 B |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Attributes.Exporters;
3+
4+
namespace Benchmarks.JsonApiContext
5+
{
6+
[MarkdownExporter, MemoryDiagnoser]
7+
public class PathIsRelationship_Benchmarks
8+
{
9+
private const string PATH = "https://example.com/api/v1/namespace/articles/relationships/author/";
10+
11+
[Benchmark]
12+
public void Current()
13+
=> JsonApiDotNetCore.Services.JsonApiContext.PathIsRelationship(PATH);
14+
15+
[Benchmark]
16+
public void UsingSplit() => UsingSplitImpl(PATH);
17+
18+
private bool UsingSplitImpl(string path)
19+
{
20+
var split = path.Split('/');
21+
return split[split.Length - 2] == "relationships";
22+
}
23+
}
24+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Attributes.Exporters;
3+
using BenchmarkDotNet.Attributes.Jobs;
4+
5+
namespace Benchmarks.LinkBuilder
6+
{
7+
[MarkdownExporter, SimpleJob(launchCount : 3, warmupCount : 10, targetCount : 20), MemoryDiagnoser]
8+
public class LinkBuilder_GetNamespaceFromPath_Benchmarks
9+
{
10+
private const string PATH = "/api/some-really-long-namespace-path/resources/current/articles";
11+
private const string ENTITY_NAME = "articles";
12+
13+
[Benchmark]
14+
public void UsingSplit() => GetNamespaceFromPath_BySplitting(PATH, ENTITY_NAME);
15+
16+
[Benchmark]
17+
public void Current() => GetNameSpaceFromPath_Current(PATH, ENTITY_NAME);
18+
19+
public static string GetNamespaceFromPath_BySplitting(string path, string entityName)
20+
{
21+
var nSpace = string.Empty;
22+
var segments = path.Split('/');
23+
24+
for (var i = 1; i < segments.Length; i++)
25+
{
26+
if (segments[i].ToLower() == entityName)
27+
break;
28+
29+
nSpace += $"/{segments[i]}";
30+
}
31+
32+
return nSpace;
33+
}
34+
35+
public static string GetNameSpaceFromPath_Current(string path, string entityName)
36+
=> JsonApiDotNetCore.Builders.LinkBuilder.GetNamespaceFromPath(path, entityName);
37+
}
38+
}

benchmarks/Program.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using BenchmarkDotNet.Running;
2+
using Benchmarks.JsonApiContext;
3+
using Benchmarks.LinkBuilder;
24
using Benchmarks.Query;
5+
using Benchmarks.RequestMiddleware;
36
using Benchmarks.Serialization;
47

58
namespace Benchmarks {
@@ -8,7 +11,10 @@ static void Main(string[] args) {
811
var switcher = new BenchmarkSwitcher(new[] {
912
typeof(JsonApiDeserializer_Benchmarks),
1013
typeof(JsonApiSerializer_Benchmarks),
11-
typeof(QueryParser_Benchmarks)
14+
typeof(QueryParser_Benchmarks),
15+
typeof(LinkBuilder_GetNamespaceFromPath_Benchmarks),
16+
typeof(ContainsMediaTypeParameters_Benchmarks),
17+
typeof(PathIsRelationship_Benchmarks)
1218
});
1319
switcher.Run(args);
1420
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Attributes.Exporters;
3+
using JsonApiDotNetCore.Internal;
4+
5+
namespace Benchmarks.RequestMiddleware
6+
{
7+
[MarkdownExporter, MemoryDiagnoser]
8+
public class ContainsMediaTypeParameters_Benchmarks
9+
{
10+
private const string MEDIA_TYPE = "application/vnd.api+json; version=1";
11+
12+
[Benchmark]
13+
public void UsingSplit() => UsingSplitImpl(MEDIA_TYPE);
14+
15+
[Benchmark]
16+
public void Current()
17+
=> JsonApiDotNetCore.Middleware.RequestMiddleware.ContainsMediaTypeParameters(MEDIA_TYPE);
18+
19+
private bool UsingSplitImpl(string mediaType)
20+
{
21+
var mediaTypeArr = mediaType.Split(';');
22+
return (mediaTypeArr[0] == Constants.ContentType && mediaTypeArr.Length == 2);
23+
}
24+
}
25+
}

src/JsonApiDotNetCore/Builders/LinkBuilder.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using JsonApiDotNetCore.Services;
23
using Microsoft.AspNetCore.Http;
34

@@ -16,24 +17,39 @@ public string GetBasePath(HttpContext context, string entityName)
1617
{
1718
var r = context.Request;
1819
return (_context.Options.RelativeLinks)
19-
? $"{GetNamespaceFromPath(r.Path, entityName)}"
20+
? GetNamespaceFromPath(r.Path, entityName)
2021
: $"{r.Scheme}://{r.Host}{GetNamespaceFromPath(r.Path, entityName)}";
2122
}
2223

23-
private string GetNamespaceFromPath(string path, string entityName)
24+
internal static string GetNamespaceFromPath(string path, string entityName)
2425
{
25-
var nSpace = string.Empty;
26-
var segments = path.Split('/');
27-
28-
for (var i = 1; i < segments.Length; i++)
26+
var entityNameSpan = entityName.AsSpan();
27+
var pathSpan = path.AsSpan();
28+
const char delimiter = '/';
29+
for (var i = 0; i < pathSpan.Length; i++)
2930
{
30-
if (segments[i].ToLower() == entityName)
31-
break;
31+
if(pathSpan[i].Equals(delimiter))
32+
{
33+
var nextPosition = i + 1;
34+
if(pathSpan.Length > i + entityNameSpan.Length)
35+
{
36+
var possiblePathSegment = pathSpan.Slice(nextPosition, entityNameSpan.Length);
37+
if (entityNameSpan.SequenceEqual(possiblePathSegment))
38+
{
39+
// check to see if it's the last position in the string
40+
// or if the next character is a /
41+
var lastCharacterPosition = nextPosition + entityNameSpan.Length;
3242

33-
nSpace += $"/{segments[i]}";
43+
if(lastCharacterPosition == pathSpan.Length || pathSpan.Length >= lastCharacterPosition + 2 && pathSpan[lastCharacterPosition].Equals(delimiter))
44+
{
45+
return pathSpan.Slice(0, i).ToString();
46+
}
47+
}
48+
}
49+
}
3450
}
3551

36-
return nSpace;
52+
return string.Empty;
3753
}
3854

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

src/JsonApiDotNetCore/Internal/Query/RelatedAttrFilterQuery.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Linq;
3+
using JsonApiDotNetCore.Extensions;
34
using JsonApiDotNetCore.Models;
45
using JsonApiDotNetCore.Services;
56

@@ -8,21 +9,19 @@ namespace JsonApiDotNetCore.Internal.Query
89
public class RelatedAttrFilterQuery : BaseFilterQuery
910
{
1011
private readonly IJsonApiContext _jsonApiContext;
11-
12+
1213
public RelatedAttrFilterQuery(
13-
IJsonApiContext jsonApiCopntext,
14+
IJsonApiContext jsonApiContext,
1415
FilterQuery filterQuery)
1516
{
16-
_jsonApiContext = jsonApiCopntext;
17+
_jsonApiContext = jsonApiContext;
1718

1819
var relationshipArray = filterQuery.Attribute.Split('.');
19-
2020
var relationship = GetRelationship(relationshipArray[0]);
2121
if (relationship == null)
2222
throw new JsonApiException(400, $"{relationshipArray[1]} is not a valid relationship on {relationshipArray[0]}.");
2323

2424
var attribute = GetAttribute(relationship, relationshipArray[1]);
25-
2625
if (attribute == null)
2726
throw new JsonApiException(400, $"'{filterQuery.Attribute}' is not a valid attribute.");
2827

0 commit comments

Comments
 (0)