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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ We are grateful to the following sponsors, who provide the team with a no-cost l
<a href="https://www.araxis.com">
<img align="middle" src="docs/home/assets/img/araxis-logo.png" alt="Araxis Logo" style="width:150px">
</a>
<p/>
</p>

Do you like this project? Consider to [sponsor](https://github.com/sponsors/json-api-dotnet), or just reward us by
giving our repository a star.
21 changes: 19 additions & 2 deletions src/JsonApiDotNetCore/Queries/Parsing/QueryExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,20 @@ protected virtual void Tokenize(string source)
Source = source;

var tokenizer = new QueryTokenizer(source);
TokenStack = new Stack<Token>(tokenizer.EnumerateTokens().Reverse());
Token[] tokens = tokenizer.EnumerateTokens().ToArray();
TokenStack = TokensToStack(tokens);
}

private static Stack<Token> TokensToStack(Token[] tokens)
{
var stack = new Stack<Token>(tokens.Length);

for (int index = tokens.Length - 1; index >= 0; index--)
{
stack.Push(tokens[index]);
}

return stack;
}

/// <summary>
Expand Down Expand Up @@ -143,7 +156,11 @@ protected virtual void EatSingleCharacterToken(TokenKind kind)
{
if (!TokenStack.TryPop(out Token? token) || token.Kind != kind)
{
char ch = QueryTokenizer.SingleCharacterToTokenKinds.Single(pair => pair.Value == kind).Key;
if (!QueryTokenizer.TokenKindToSingleCharacterLookup.TryGetValue(kind, out char ch))
{
throw new InvalidOperationException($"Token kind '{kind}' is not a single-character token.");
}

int position = token?.Position ?? GetNextTokenPositionOrEnd();
throw new QueryParseException($"{ch} expected.", position);
}
Expand Down
27 changes: 14 additions & 13 deletions src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Collections.ObjectModel;
using System.Text;
using JetBrains.Annotations;

Expand All @@ -7,18 +6,20 @@ namespace JsonApiDotNetCore.Queries.Parsing;
[PublicAPI]
public sealed class QueryTokenizer
{
public static readonly IReadOnlyDictionary<char, TokenKind> SingleCharacterToTokenKinds = new ReadOnlyDictionary<char, TokenKind>(
new Dictionary<char, TokenKind>
{
['('] = TokenKind.OpenParen,
[')'] = TokenKind.CloseParen,
['['] = TokenKind.OpenBracket,
[']'] = TokenKind.CloseBracket,
['.'] = TokenKind.Period,
[','] = TokenKind.Comma,
[':'] = TokenKind.Colon,
['-'] = TokenKind.Minus
});
public static readonly IReadOnlyDictionary<char, TokenKind> SingleCharacterToTokenKinds = new Dictionary<char, TokenKind>
{
['('] = TokenKind.OpenParen,
[')'] = TokenKind.CloseParen,
['['] = TokenKind.OpenBracket,
[']'] = TokenKind.CloseBracket,
['.'] = TokenKind.Period,
[','] = TokenKind.Comma,
[':'] = TokenKind.Colon,
['-'] = TokenKind.Minus
}.AsReadOnly();

internal static readonly IReadOnlyDictionary<TokenKind, char> TokenKindToSingleCharacterLookup =
SingleCharacterToTokenKinds.ToDictionary(pair => pair.Value, pair => pair.Key).AsReadOnly();

private readonly string _source;
private readonly StringBuilder _textBuffer = new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ namespace JsonApiDotNetCore.Serialization.Response;
internal sealed class FingerprintGenerator : IFingerprintGenerator
{
private static readonly byte[] Separator = "|"u8.ToArray();
private static readonly uint[] LookupTable = Enumerable.Range(0, 256).Select(ToLookupEntry).ToArray();

private static uint ToLookupEntry(int index)
{
string hex = index.ToString("X2");
return hex[0] + ((uint)hex[1] << 16);
}

/// <inheritdoc />
public string Generate(IEnumerable<string> elements)
Expand All @@ -30,22 +23,6 @@ public string Generate(IEnumerable<string> elements)
}

byte[] hash = hasher.GetHashAndReset();
return ByteArrayToHex(hash);
}

private static string ByteArrayToHex(byte[] bytes)
{
// https://stackoverflow.com/questions/311165/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-and-vice-versa

char[] buffer = new char[bytes.Length * 2];

for (int index = 0; index < bytes.Length; index++)
{
uint value = LookupTable[bytes[index]];
buffer[2 * index] = (char)value;
buffer[2 * index + 1] = (char)(value >> 16);
}

return new string(buffer);
return Convert.ToHexString(hash);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private static bool CanWriteBody(HttpStatusCode statusCode)

private static bool IsSuccessStatusCode(HttpStatusCode statusCode)
{
return new HttpResponseMessage(statusCode).IsSuccessStatusCode;
return (int)statusCode is >= 200 and <= 299;
}

private string RenderModel(object? model)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,11 @@ public IList<ResourceObject> GetResponseIncluded()
HashSet<ResourceObject> primaryResourceObjectSet = GetDirectChildren().Select(node => node.ResourceObject).ToHashSet(ResourceObjectComparer.Instance);
List<ResourceObject> includes = [];

foreach (ResourceObject include in visited.Select(node => node.ResourceObject))
foreach (ResourceObjectTreeNode include in visited)
{
if (!primaryResourceObjectSet.Contains(include))
if (!primaryResourceObjectSet.Contains(include.ResourceObject))
{
includes.Add(include);
includes.Add(include.ResourceObject);
}
}

Expand Down
Loading