diff --git a/README.md b/README.md index c31882025b..fc2a0e6ac4 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ We are grateful to the following sponsors, who provide the team with a no-cost l Araxis Logo -

+

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. diff --git a/src/JsonApiDotNetCore/Queries/Parsing/QueryExpressionParser.cs b/src/JsonApiDotNetCore/Queries/Parsing/QueryExpressionParser.cs index d324ffc11b..46fa2c8273 100644 --- a/src/JsonApiDotNetCore/Queries/Parsing/QueryExpressionParser.cs +++ b/src/JsonApiDotNetCore/Queries/Parsing/QueryExpressionParser.cs @@ -53,7 +53,20 @@ protected virtual void Tokenize(string source) Source = source; var tokenizer = new QueryTokenizer(source); - TokenStack = new Stack(tokenizer.EnumerateTokens().Reverse()); + Token[] tokens = tokenizer.EnumerateTokens().ToArray(); + TokenStack = TokensToStack(tokens); + } + + private static Stack TokensToStack(Token[] tokens) + { + var stack = new Stack(tokens.Length); + + for (int index = tokens.Length - 1; index >= 0; index--) + { + stack.Push(tokens[index]); + } + + return stack; } /// @@ -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); } diff --git a/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs b/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs index 0e1e7660a2..bf7b714e96 100644 --- a/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs +++ b/src/JsonApiDotNetCore/Queries/Parsing/QueryTokenizer.cs @@ -1,4 +1,3 @@ -using System.Collections.ObjectModel; using System.Text; using JetBrains.Annotations; @@ -7,18 +6,20 @@ namespace JsonApiDotNetCore.Queries.Parsing; [PublicAPI] public sealed class QueryTokenizer { - public static readonly IReadOnlyDictionary SingleCharacterToTokenKinds = new ReadOnlyDictionary( - new Dictionary - { - ['('] = TokenKind.OpenParen, - [')'] = TokenKind.CloseParen, - ['['] = TokenKind.OpenBracket, - [']'] = TokenKind.CloseBracket, - ['.'] = TokenKind.Period, - [','] = TokenKind.Comma, - [':'] = TokenKind.Colon, - ['-'] = TokenKind.Minus - }); + public static readonly IReadOnlyDictionary SingleCharacterToTokenKinds = new Dictionary + { + ['('] = TokenKind.OpenParen, + [')'] = TokenKind.CloseParen, + ['['] = TokenKind.OpenBracket, + [']'] = TokenKind.CloseBracket, + ['.'] = TokenKind.Period, + [','] = TokenKind.Comma, + [':'] = TokenKind.Colon, + ['-'] = TokenKind.Minus + }.AsReadOnly(); + + internal static readonly IReadOnlyDictionary TokenKindToSingleCharacterLookup = + SingleCharacterToTokenKinds.ToDictionary(pair => pair.Value, pair => pair.Key).AsReadOnly(); private readonly string _source; private readonly StringBuilder _textBuffer = new(); diff --git a/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs b/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs index 61f3349df3..4a0650b035 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/FingerprintGenerator.cs @@ -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); - } /// public string Generate(IEnumerable elements) @@ -30,22 +23,6 @@ public string Generate(IEnumerable 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); } } diff --git a/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs b/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs index 67bb61213b..66dcaedf29 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/JsonApiWriter.cs @@ -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) diff --git a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs index 7417adc6ff..c18eecd8b7 100644 --- a/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs +++ b/src/JsonApiDotNetCore/Serialization/Response/ResourceObjectTreeNode.cs @@ -184,11 +184,11 @@ public IList GetResponseIncluded() HashSet primaryResourceObjectSet = GetDirectChildren().Select(node => node.ResourceObject).ToHashSet(ResourceObjectComparer.Instance); List 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); } }