Skip to content

Commit

Permalink
Avoid string allocations in DateField / DecimalField / TimeField when…
Browse files Browse the repository at this point in the history
… targeting netstandard2.1
  • Loading branch information
campersau committed May 14, 2021
1 parent 92e64e9 commit 01d0847
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 20 deletions.
28 changes: 19 additions & 9 deletions 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
Expand All @@ -10,6 +10,7 @@ internal sealed class DateField : Field<DateTime?>
{
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)
Expand All @@ -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);
Expand All @@ -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<char> dateString = buffer.AsSpan();

Match match = Regex.Match(dateString, "^(?<Year>[0-9]{4})(?<Month>[0-9]{2})(?<Day>[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]
Expand Down
4 changes: 4 additions & 0 deletions src/SapNwRfc/Internal/Fields/DecimalField.cs
Expand Up @@ -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);
}
Expand Down
30 changes: 19 additions & 11 deletions 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
Expand All @@ -10,6 +10,7 @@ internal sealed class TimeField : Field<TimeSpan?>
{
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)
Expand All @@ -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);
Expand All @@ -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<char> timeString = buffer.AsSpan();

Match match = Regex.Match(timeString, "^(?<Hours>[0-9]{2})(?<Minutes>[0-9]{2})(?<Seconds>[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]
Expand Down

0 comments on commit 01d0847

Please sign in to comment.