From 75cdf208cc905a206c5961819e8badae3db560bb Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 28 May 2022 12:51:28 -0400 Subject: [PATCH] Refactored ImapUtils' Read[N]StringAsync() and ReadNumberAsync() methods to reduce async/await overhead Another partial fix for issue #1335 --- MailKit/Net/Imap/ImapUtils.cs | 98 ++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 8 deletions(-) diff --git a/MailKit/Net/Imap/ImapUtils.cs b/MailKit/Net/Imap/ImapUtils.cs index 688342b283..971c7f0cad 100644 --- a/MailKit/Net/Imap/ImapUtils.cs +++ b/MailKit/Net/Imap/ImapUtils.cs @@ -731,13 +731,13 @@ public static Task ParseMetadataAsync (ImapEngine engine, ImapCommand ic, int in return ParseMetadataAsync (engine, metadata, doAsync, ic.CancellationToken); } - internal static async ValueTask ReadStringTokenAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) + static string ReadStringToken (ImapEngine engine, string format, CancellationToken cancellationToken) { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + var token = engine.ReadToken (cancellationToken); switch (token.Type) { case ImapTokenType.Literal: - return await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); + return engine.ReadLiteral (cancellationToken); case ImapTokenType.QString: case ImapTokenType.Atom: return (string) token.Value; @@ -746,14 +746,39 @@ internal static async ValueTask ReadStringTokenAsync (ImapEngine engine, } } - static async ValueTask ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, bool doAsync, CancellationToken cancellationToken) + static async ValueTask ReadStringTokenAsync (ImapEngine engine, string format, CancellationToken cancellationToken) { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + switch (token.Type) { + case ImapTokenType.Literal: + return await engine.ReadLiteralAsync (cancellationToken).ConfigureAwait (false); + case ImapTokenType.QString: + case ImapTokenType.Atom: + return (string) token.Value; + default: + throw ImapEngine.UnexpectedToken (format, token); + } + } + + internal static ValueTask ReadStringTokenAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) + { + if (doAsync) + return ReadStringTokenAsync (engine, format, cancellationToken); + + var value = ReadStringToken (engine, format, cancellationToken); + + return new ValueTask (value); + } + + static string ReadNStringToken (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken) + { + var token = engine.ReadToken (cancellationToken); string value; switch (token.Type) { case ImapTokenType.Literal: - value = await engine.ReadLiteralAsync (doAsync, cancellationToken).ConfigureAwait (false); + value = engine.ReadLiteral (cancellationToken); break; case ImapTokenType.QString: case ImapTokenType.Atom: @@ -771,10 +796,43 @@ static async ValueTask ReadNStringTokenAsync (ImapEngine engine, string return value; } - static async ValueTask ReadNumberAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) + static async ValueTask ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, CancellationToken cancellationToken) { - var token = await engine.ReadTokenAsync (doAsync, cancellationToken).ConfigureAwait (false); + var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); + string value; + + switch (token.Type) { + case ImapTokenType.Literal: + value = await engine.ReadLiteralAsync (cancellationToken).ConfigureAwait (false); + break; + case ImapTokenType.QString: + case ImapTokenType.Atom: + value = (string) token.Value; + break; + case ImapTokenType.Nil: + return null; + default: + throw ImapEngine.UnexpectedToken (format, token); + } + + if (rfc2047) + return Rfc2047.DecodeText (TextEncodings.UTF8.GetBytes (value)); + + return value; + } + + static ValueTask ReadNStringTokenAsync (ImapEngine engine, string format, bool rfc2047, bool doAsync, CancellationToken cancellationToken) + { + if (doAsync) + return ReadNStringTokenAsync (engine, format, rfc2047, cancellationToken); + + var value = ReadNStringToken (engine, format, rfc2047, cancellationToken); + + return new ValueTask (value); + } + static uint ParseNumberToken (ImapToken token, string format) + { // Note: this is a work-around for broken IMAP servers that return negative integer values for things // like octet counts and line counts. if (token.Type == ImapTokenType.Atom) { @@ -793,6 +851,30 @@ static async ValueTask ReadNumberAsync (ImapEngine engine, string format, return ImapEngine.ParseNumber (token, false, format, token); } + static uint ReadNumber (ImapEngine engine, string format, CancellationToken cancellationToken) + { + var token = engine.ReadToken (cancellationToken); + + return ParseNumberToken (token, format); + } + + static async ValueTask ReadNumberAsync (ImapEngine engine, string format, CancellationToken cancellationToken) + { + var token = await engine.ReadTokenAsync (cancellationToken).ConfigureAwait (false); + + return ParseNumberToken (token, format); + } + + static ValueTask ReadNumberAsync (ImapEngine engine, string format, bool doAsync, CancellationToken cancellationToken) + { + if (doAsync) + return ReadNumberAsync (engine, format, cancellationToken); + + var value = ReadNumber (engine, format, cancellationToken); + + return new ValueTask (value); + } + static bool NeedsQuoting (string value) { for (int i = 0; i < value.Length; i++) {