use Regex Source Generator for .NET 7+ (#1401)

* use Regex Source Generator for .NET 7+

fixes #1131

* use shorter pattern for GeneratedRegex
This commit is contained in:
mus65
2024-05-20 17:27:53 +02:00
committed by GitHub
parent aa70718a86
commit 23323aa217
6 changed files with 90 additions and 31 deletions
-7
View File
@@ -33,13 +33,6 @@ dotnet_diagnostic.S2589.severity = none
dotnet_diagnostic.S2372.severity = none
#### SYSLIB diagnostics ####
# SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time
#
# TODO: Remove this when https://github.com/sshnet/SSH.NET/issues/1131 is implemented.
dotnet_diagnostic.SYSLIB1045.severity = none
#### StyleCop Analyzers rules ####
# SA1123: Do not place regions within elements
+20 -6
View File
@@ -29,8 +29,25 @@ namespace Renci.SshNet.Connection
/// </item>
/// </list>
/// </remarks>
internal sealed class HttpConnector : ProxyConnector
internal sealed partial class HttpConnector : ProxyConnector
{
private const string HttpResponsePattern = @"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$";
private const string HttpHeaderPattern = @"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?";
#if NET7_0_OR_GREATER
private static readonly Regex HttpResponseRegex = GetHttpResponseRegex();
private static readonly Regex HttpHeaderRegex = GetHttpHeaderRegex();
[GeneratedRegex(HttpResponsePattern)]
private static partial Regex GetHttpResponseRegex();
[GeneratedRegex(HttpHeaderPattern)]
private static partial Regex GetHttpHeaderRegex();
#else
private static readonly Regex HttpResponseRegex = new Regex(HttpResponsePattern, RegexOptions.Compiled);
private static readonly Regex HttpHeaderRegex = new Regex(HttpHeaderPattern, RegexOptions.Compiled);
#endif
public HttpConnector(ISocketFactory socketFactory)
: base(socketFactory)
{
@@ -38,9 +55,6 @@ namespace Renci.SshNet.Connection
protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
{
var httpResponseRe = new Regex(@"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$");
var httpHeaderRe = new Regex(@"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?");
SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(string.Format(CultureInfo.InvariantCulture,
"CONNECT {0}:{1} HTTP/1.0\r\n",
connectionInfo.Host,
@@ -71,7 +85,7 @@ namespace Renci.SshNet.Connection
if (statusCode is null)
{
var statusMatch = httpResponseRe.Match(response);
var statusMatch = HttpResponseRegex.Match(response);
if (statusMatch.Success)
{
var httpStatusCode = statusMatch.Result("${statusCode}");
@@ -86,7 +100,7 @@ namespace Renci.SshNet.Connection
}
// continue on parsing message headers coming from the server
var headerMatch = httpHeaderRe.Match(response);
var headerMatch = HttpHeaderRegex.Match(response);
if (headerMatch.Success)
{
var fieldName = headerMatch.Result("${fieldName}");
@@ -19,11 +19,19 @@ namespace Renci.SshNet.Connection
/// <remarks>
/// https://tools.ietf.org/html/rfc4253#section-4.2.
/// </remarks>
internal sealed class ProtocolVersionExchange : IProtocolVersionExchange
internal sealed partial class ProtocolVersionExchange : IProtocolVersionExchange
{
private const byte Null = 0x00;
private const string ServerVersionPattern = "^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.+?)([ ](?<comments>.+))?$";
private static readonly Regex ServerVersionRe = new Regex("^SSH-(?<protoversion>[^-]+)-(?<softwareversion>.+?)([ ](?<comments>.+))?$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
#if NET7_0_OR_GREATER
private static readonly Regex ServerVersionRegex = GetServerVersionRegex();
[GeneratedRegex(ServerVersionPattern, RegexOptions.ExplicitCapture)]
private static partial Regex GetServerVersionRegex();
#else
private static readonly Regex ServerVersionRegex = new Regex(ServerVersionPattern, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
#endif
/// <summary>
/// Performs the SSH protocol version exchange.
@@ -57,7 +65,7 @@ namespace Renci.SshNet.Connection
throw CreateServerResponseDoesNotContainIdentification(bytesReceived);
}
var identificationMatch = ServerVersionRe.Match(line);
var identificationMatch = ServerVersionRegex.Match(line);
if (identificationMatch.Success)
{
return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"),
@@ -104,7 +112,7 @@ namespace Renci.SshNet.Connection
throw CreateServerResponseDoesNotContainIdentification(bytesReceived);
}
var identificationMatch = ServerVersionRe.Match(line);
var identificationMatch = ServerVersionRegex.Match(line);
if (identificationMatch.Success)
{
return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"),
+20 -5
View File
@@ -9,10 +9,11 @@ using Renci.SshNet.Common;
namespace Renci.SshNet.NetConf
{
internal sealed class NetConfSession : SubsystemSession, INetConfSession
internal sealed partial class NetConfSession : SubsystemSession, INetConfSession
{
private const string Prompt = "]]>]]>";
private const string LengthPattern = @"\n#(?<length>\d+)\n";
private const string ReplyPattern = @"\n##\n";
private readonly StringBuilder _data = new StringBuilder();
private bool _usingFramingProtocol;
private EventWaitHandle _serverCapabilitiesConfirmed = new AutoResetEvent(initialState: false);
@@ -20,6 +21,20 @@ namespace Renci.SshNet.NetConf
private StringBuilder _rpcReply = new StringBuilder();
private int _messageId;
#if NET7_0_OR_GREATER
private static readonly Regex LengthRegex = GetLengthRegex();
private static readonly Regex ReplyRegex = GetReplyRegex();
[GeneratedRegex(LengthPattern)]
private static partial Regex GetLengthRegex();
[GeneratedRegex(ReplyPattern)]
private static partial Regex GetReplyRegex();
#else
private static readonly Regex LengthRegex = new Regex(LengthPattern, RegexOptions.Compiled);
private static readonly Regex ReplyRegex = new Regex(ReplyPattern, RegexOptions.Compiled);
#endif
/// <summary>
/// Gets NetConf server capabilities.
/// </summary>
@@ -145,7 +160,7 @@ namespace Renci.SshNet.NetConf
for (; ; )
{
var match = Regex.Match(chunk.Substring(position), @"\n#(?<length>\d+)\n");
var match = LengthRegex.Match(chunk.Substring(position));
if (!match.Success)
{
break;
@@ -157,9 +172,9 @@ namespace Renci.SshNet.NetConf
}
#if NET7_0_OR_GREATER
if (Regex.IsMatch(chunk.AsSpan(position), @"\n##\n"))
if (ReplyRegex.IsMatch(chunk.AsSpan(position)))
#else
if (Regex.IsMatch(chunk.Substring(position), @"\n##\n"))
if (ReplyRegex.IsMatch(chunk.Substring(position)))
#endif // NET7_0_OR_GREATER
{
_ = _rpcReplyReceived.Set();
+11 -2
View File
@@ -62,10 +62,19 @@ namespace Renci.SshNet
/// </list>
/// </para>
/// </remarks>
public class PrivateKeyFile : IPrivateKeySource, IDisposable
public partial class PrivateKeyFile : IPrivateKeySource, IDisposable
{
private static readonly Regex PrivateKeyRegex = new Regex(@"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)(\r?\n)?-+ *END \k<keyName> PRIVATE KEY *-+",
private const string PrivateKeyPattern = @"^-+ *BEGIN (?<keyName>\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?<cipherName>[A-Z0-9-]+),(?<salt>[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?<data>([a-zA-Z0-9/+=]{1,80}\r?\n)+)(\r?\n)?-+ *END \k<keyName> PRIVATE KEY *-+";
#if NET7_0_OR_GREATER
private static readonly Regex PrivateKeyRegex = GetPrivateKeyRegex();
[GeneratedRegex(PrivateKeyPattern, RegexOptions.Multiline | RegexOptions.ExplicitCapture)]
private static partial Regex GetPrivateKeyRegex();
#else
private static readonly Regex PrivateKeyRegex = new Regex(PrivateKeyPattern,
RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.ExplicitCapture);
#endif
private readonly List<HostAlgorithm> _hostAlgorithms = new List<HostAlgorithm>();
private Key _key;
+27 -7
View File
@@ -32,11 +32,31 @@ namespace Renci.SshNet
public partial class ScpClient : BaseClient
{
private const string Message = "filename";
private static readonly Regex FileInfoRe = new Regex(@"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)", RegexOptions.Compiled);
private const string FileInfoPattern = @"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)";
private const string DirectoryInfoPattern = @"D(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)";
private const string TimestampPattern = @"T(?<mtime>\d+) 0 (?<atime>\d+) 0";
#if NET7_0_OR_GREATER
private static readonly Regex FileInfoRegex = GetFileInfoRegex();
private static readonly Regex DirectoryInfoRegex = GetDirectoryInfoRegex();
private static readonly Regex TimestampRegex = GetTimestampRegex();
[GeneratedRegex(FileInfoPattern)]
private static partial Regex GetFileInfoRegex();
[GeneratedRegex(DirectoryInfoPattern)]
private static partial Regex GetDirectoryInfoRegex();
[GeneratedRegex(TimestampPattern)]
private static partial Regex GetTimestampRegex();
#else
private static readonly Regex FileInfoRegex = new Regex(FileInfoPattern, RegexOptions.Compiled);
private static readonly Regex DirectoryInfoRegex = new Regex(DirectoryInfoPattern, RegexOptions.Compiled);
private static readonly Regex TimestampRegex = new Regex(TimestampPattern, RegexOptions.Compiled);
#endif
private static readonly byte[] SuccessConfirmationCode = { 0 };
private static readonly byte[] ErrorConfirmationCode = { 1 };
private static readonly Regex DirectoryInfoRe = new Regex(@"D(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)", RegexOptions.Compiled);
private static readonly Regex TimestampRe = new Regex(@"T(?<mtime>\d+) 0 (?<atime>\d+) 0", RegexOptions.Compiled);
private IRemotePathTransformation _remotePathTransformation;
private TimeSpan _operationTimeout;
@@ -458,7 +478,7 @@ namespace Renci.SshNet
SendSuccessConfirmation(channel); // Send reply
var message = ReadString(input);
var match = FileInfoRe.Match(message);
var match = FileInfoRegex.Match(message);
if (match.Success)
{
@@ -757,7 +777,7 @@ namespace Renci.SshNet
continue;
}
var match = DirectoryInfoRe.Match(message);
var match = DirectoryInfoRegex.Match(message);
if (match.Success)
{
SendSuccessConfirmation(channel); // Send reply
@@ -784,7 +804,7 @@ namespace Renci.SshNet
continue;
}
match = FileInfoRe.Match(message);
match = FileInfoRegex.Match(message);
if (match.Success)
{
// Read file
@@ -814,7 +834,7 @@ namespace Renci.SshNet
continue;
}
match = TimestampRe.Match(message);
match = TimestampRegex.Match(message);
if (match.Success)
{
// Read timestamp