diff --git a/src/GraphQLParser.Benchmarks/BinaryTest.graphql b/src/GraphQLParser.Benchmarks/BinaryTest.graphql new file mode 100644 index 00000000..ca03f5ab Binary files /dev/null and b/src/GraphQLParser.Benchmarks/BinaryTest.graphql differ diff --git a/src/GraphQLParser.Benchmarks/GraphQLParser.Benchmarks.csproj b/src/GraphQLParser.Benchmarks/GraphQLParser.Benchmarks.csproj index f9e2b055..5eacd042 100644 --- a/src/GraphQLParser.Benchmarks/GraphQLParser.Benchmarks.csproj +++ b/src/GraphQLParser.Benchmarks/GraphQLParser.Benchmarks.csproj @@ -8,16 +8,26 @@ true + + + + + + + PreserveNewest + + + - + - + diff --git a/src/GraphQLParser.Benchmarks/LexerBenchmark.cs b/src/GraphQLParser.Benchmarks/LexerBenchmark.cs index dbcef69f..d0d6b8de 100644 --- a/src/GraphQLParser.Benchmarks/LexerBenchmark.cs +++ b/src/GraphQLParser.Benchmarks/LexerBenchmark.cs @@ -1,4 +1,6 @@ using BenchmarkDotNet.Attributes; +using GraphQLParser.Exceptions; +using System.IO; namespace GraphQLParser.Benchmarks { @@ -7,6 +9,7 @@ namespace GraphQLParser.Benchmarks #endif public class LexerBenchmark { + private static readonly string Binary = File.ReadAllText("BinaryTest.graphql"); private const string KitchenSink = @" query queryName($foo: ComplexType, $site: Site = MOBILE) { whoever123is: node(id: [123, 456]) { @@ -70,5 +73,18 @@ public void LexKitchenSink() resetPosition = token.End; } } + + [Benchmark] + public void ParseBinaryFile() + { + try + { + var parser = new Parser(new Lexer()); + parser.Parse(new Source(Binary)); + } + catch (GraphQLSyntaxErrorException) + { + } + } } } diff --git a/src/GraphQLParser/AST/GraphQLListValue.cs b/src/GraphQLParser/AST/GraphQLListValue.cs index 41c708d6..04919661 100644 --- a/src/GraphQLParser/AST/GraphQLListValue.cs +++ b/src/GraphQLParser/AST/GraphQLListValue.cs @@ -4,7 +4,7 @@ public class GraphQLListValue : GraphQLValue { - private ASTNodeKind kindField; + private readonly ASTNodeKind kindField; public GraphQLListValue(ASTNodeKind kind) { diff --git a/src/GraphQLParser/Exceptions/GraphQLSyntaxErrorException.cs b/src/GraphQLParser/Exceptions/GraphQLSyntaxErrorException.cs index a6dad2b7..f47ff149 100644 --- a/src/GraphQLParser/Exceptions/GraphQLSyntaxErrorException.cs +++ b/src/GraphQLParser/Exceptions/GraphQLSyntaxErrorException.cs @@ -2,6 +2,7 @@ { using System; using System.Linq; + using System.Text; public class GraphQLSyntaxErrorException : Exception { @@ -49,13 +50,48 @@ private static string LeftPad(int length, string str) private static string ReplaceWithUnicodeRepresentation(string str) { + if (!HasReplacementCharacter(str)) + return str; + + var buffer = new StringBuilder(str.Length); + foreach (var code in str) { - if (code < 0x0020 && code != 0x0009 && code != 0x000A && code != 0x000D) - str = str.Replace(string.Empty + code, "\\u" + ((int)code).ToString("D4")); + if (IsReplacementCharacter(code)) + { + buffer.Append(GetUnicodeRepresentation(code)); + } + else + { + buffer.Append(code); + } } - return str; + return buffer.ToString(); + } + + private static bool HasReplacementCharacter(string str) + { + foreach (var code in str) + { + if (IsReplacementCharacter(code)) + return true; + } + + return false; + } + + private static bool IsReplacementCharacter(char code) => code < 0x0020 && code != 0x0009 && code != 0x000A && code != 0x000D; + + private static string GetUnicodeRepresentation(char code) + { + switch (code) + { + case '\0': + return "\\u0000"; + default: + return "\\u" + ((int)code).ToString("D4"); + } } } } \ No newline at end of file diff --git a/src/GraphQLParser/LexerContext.cs b/src/GraphQLParser/LexerContext.cs index 3dc2f5c5..75e7a17f 100644 --- a/src/GraphQLParser/LexerContext.cs +++ b/src/GraphQLParser/LexerContext.cs @@ -6,7 +6,7 @@ public class LexerContext : IDisposable { private int currentIndex; - private ISource source; + private readonly ISource source; public LexerContext(ISource source, int index) { diff --git a/src/GraphQLParser/ParserContext.cs b/src/GraphQLParser/ParserContext.cs index 3e4e60e2..01e32030 100644 --- a/src/GraphQLParser/ParserContext.cs +++ b/src/GraphQLParser/ParserContext.cs @@ -689,7 +689,6 @@ private GraphQLValue ParseNameValue(bool isConstant) $"Unexpected {this.currentToken}", this.source, this.currentToken.Start); } - private GraphQLValue ParseObject(bool isConstant) { var comment = GetComment();