From 01d0847ad071ee5889f8d6aed75c56a3881f9dc5 Mon Sep 17 00:00:00 2001 From: campersau Date: Fri, 14 May 2021 15:28:18 +0200 Subject: [PATCH] Avoid string allocations in DateField / DecimalField / TimeField when targeting netstandard2.1 --- src/SapNwRfc/Internal/Fields/DateField.cs | 28 ++++++++++++------ src/SapNwRfc/Internal/Fields/DecimalField.cs | 4 +++ src/SapNwRfc/Internal/Fields/TimeField.cs | 30 +++++++++++++------- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/SapNwRfc/Internal/Fields/DateField.cs b/src/SapNwRfc/Internal/Fields/DateField.cs index 4f33b20..9cd0a4a 100644 --- a/src/SapNwRfc/Internal/Fields/DateField.cs +++ b/src/SapNwRfc/Internal/Fields/DateField.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; +using System.Globalization; using SapNwRfc.Internal.Interop; namespace SapNwRfc.Internal.Fields @@ -10,6 +10,7 @@ internal sealed class DateField : Field { private static readonly string ZeroRfcDateString = new string('0', 8); private static readonly string EmptyRfcDateString = new string(' ', 8); + private static readonly string RfcDateFormat = "yyyyMMdd"; public DateField(string name, DateTime? value) : base(name, value) @@ -18,10 +19,17 @@ public DateField(string name, DateTime? value) public override void Apply(RfcInterop interop, IntPtr dataHandle) { +#if NETSTANDARD2_0 + char[] buffer = (Value?.ToString(RfcDateFormat, CultureInfo.InvariantCulture) ?? ZeroRfcDateString).ToCharArray(); +#else + char[] buffer = ZeroRfcDateString.ToCharArray(); + Value?.TryFormat(buffer, out var _, RfcDateFormat, CultureInfo.InvariantCulture); +#endif + RfcResultCode resultCode = interop.SetDate( dataHandle: dataHandle, name: Name, - date: (Value?.ToString("yyyyMMdd") ?? ZeroRfcDateString).ToCharArray(), + date: buffer, errorInfo: out RfcErrorInfo errorInfo); resultCode.ThrowOnError(errorInfo); @@ -39,20 +47,22 @@ public static DateField Extract(RfcInterop interop, IntPtr dataHandle, string na resultCode.ThrowOnError(errorInfo); +#if NETSTANDARD2_0 string dateString = new string(buffer); if (dateString == EmptyRfcDateString || dateString == ZeroRfcDateString) return new DateField(name, null); +#else + Span dateString = buffer.AsSpan(); - Match match = Regex.Match(dateString, "^(?[0-9]{4})(?[0-9]{2})(?[0-9]{2})$"); - if (!match.Success) + if (dateString.SequenceEqual(EmptyRfcDateString) || dateString.SequenceEqual(ZeroRfcDateString)) return new DateField(name, null); +#endif - int year = int.Parse(match.Groups["Year"].Value); - int month = int.Parse(match.Groups["Month"].Value); - int day = int.Parse(match.Groups["Day"].Value); + if (!DateTime.TryParseExact(dateString, RfcDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date)) + return new DateField(name, null); - return new DateField(name, new DateTime(year, month, day)); + return new DateField(name, date); } [ExcludeFromCodeCoverage] diff --git a/src/SapNwRfc/Internal/Fields/DecimalField.cs b/src/SapNwRfc/Internal/Fields/DecimalField.cs index 587e3c7..6ebca79 100644 --- a/src/SapNwRfc/Internal/Fields/DecimalField.cs +++ b/src/SapNwRfc/Internal/Fields/DecimalField.cs @@ -54,7 +54,11 @@ public static DecimalField Extract(RfcInterop interop, IntPtr dataHandle, string resultCode.ThrowOnError(errorInfo); +#if NETSTANDARD2_0 var decimalValue = decimal.Parse(new string(buffer, 0, (int)stringLength), CultureInfo.InvariantCulture); +#else + var decimalValue = decimal.Parse(buffer.AsSpan(), provider: CultureInfo.InvariantCulture); +#endif return new DecimalField(name, decimalValue); } diff --git a/src/SapNwRfc/Internal/Fields/TimeField.cs b/src/SapNwRfc/Internal/Fields/TimeField.cs index 01a1558..dc1ffb3 100644 --- a/src/SapNwRfc/Internal/Fields/TimeField.cs +++ b/src/SapNwRfc/Internal/Fields/TimeField.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; +using System.Globalization; using SapNwRfc.Internal.Interop; namespace SapNwRfc.Internal.Fields @@ -10,6 +10,7 @@ internal sealed class TimeField : Field { private static readonly string ZeroRfcTimeString = new string('0', 6); private static readonly string EmptyRfcTimeString = new string(' ', 6); + private static readonly string RfcTimeFormat = "hhmmss"; public TimeField(string name, TimeSpan? value) : base(name, value) @@ -18,12 +19,17 @@ public TimeField(string name, TimeSpan? value) public override void Apply(RfcInterop interop, IntPtr dataHandle) { - string stringValue = Value?.ToString("hhmmss") ?? ZeroRfcTimeString; +#if NETSTANDARD2_0 + char[] buffer = (Value?.ToString(RfcTimeFormat, CultureInfo.InvariantCulture) ?? ZeroRfcTimeString).ToCharArray(); +#else + char[] buffer = ZeroRfcTimeString.ToCharArray(); + Value?.TryFormat(buffer, out var _, RfcTimeFormat, CultureInfo.InvariantCulture); +#endif RfcResultCode resultCode = interop.SetTime( dataHandle: dataHandle, name: Name, - time: stringValue.ToCharArray(), + time: buffer, out RfcErrorInfo errorInfo); resultCode.ThrowOnError(errorInfo); @@ -41,20 +47,22 @@ public static TimeField Extract(RfcInterop interop, IntPtr dataHandle, string na resultCode.ThrowOnError(errorInfo); - var timeString = new string(buffer); +#if NETSTANDARD2_0 + string timeString = new string(buffer); if (timeString == EmptyRfcTimeString || timeString == ZeroRfcTimeString) return new TimeField(name, null); +#else + Span timeString = buffer.AsSpan(); - Match match = Regex.Match(timeString, "^(?[0-9]{2})(?[0-9]{2})(?[0-9]{2})$"); - if (!match.Success) + if (timeString.SequenceEqual(EmptyRfcTimeString) || timeString.SequenceEqual(ZeroRfcTimeString)) return new TimeField(name, null); +#endif - int hours = int.Parse(match.Groups["Hours"].Value); - int minutes = int.Parse(match.Groups["Minutes"].Value); - int seconds = int.Parse(match.Groups["Seconds"].Value); + if (!TimeSpan.TryParseExact(timeString, RfcTimeFormat, CultureInfo.InvariantCulture, out TimeSpan time)) + return new TimeField(name, null); - return new TimeField(name, new TimeSpan(hours, minutes, seconds)); + return new TimeField(name, time); } [ExcludeFromCodeCoverage]