Skip to content

Commit

Permalink
Added dependency on System.Memory and enabled Span<T> support in net4…
Browse files Browse the repository at this point in the history
…51 and netstandard2.0 (#49)

* .build/dependencies.props: Bumped J2N to 2.1.0-alpha-0043

* SWEEP: Added dependency on System.Memory on netstandard2.0 and net451. Modified RuleBasedNumberFormat components to accommodate these targets.

* ICU4N.Text.StringHelper: Refactored business logic and added tests
  • Loading branch information
NightOwl888 authored Nov 16, 2023
1 parent 608b652 commit 21748f1
Show file tree
Hide file tree
Showing 33 changed files with 1,377 additions and 275 deletions.
4 changes: 3 additions & 1 deletion .build/TestReferences.Common.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<Project>
<ItemGroup Label="Test Project Common References">
<ProjectReference Include="..\..\src\ICU4N.TestFramework\ICU4N.TestFramework.csproj" />
<ProjectReference Include="..\..\src\ICU4N.TestFramework\ICU4N.TestFramework.csproj">
<SetTargetFramework>$(SetTargetFramework)</SetTargetFramework>
</ProjectReference>

<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageReferenceVersion)" />
<PackageReference Include="NUnit" Version="$(NUnitPackageReferenceVersion)" />
Expand Down
3 changes: 2 additions & 1 deletion .build/dependencies.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup Label="NuGet Package Reference Versions">
<IKVMMavenSdkPackageReferenceVersion>1.2.0</IKVMMavenSdkPackageReferenceVersion>
<J2NPackageReferenceVersion>2.0.0</J2NPackageReferenceVersion>
<J2NPackageReferenceVersion>2.1.0-alpha-0043</J2NPackageReferenceVersion>
<MicrosoftExtensionsCachingMemoryPackageReferenceVersion>2.0.0</MicrosoftExtensionsCachingMemoryPackageReferenceVersion>
<MicrosoftExtensionsCachingMemoryPackageReferenceVersion Condition=" $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">6.0.0</MicrosoftExtensionsCachingMemoryPackageReferenceVersion>
<MicrosoftExtensionsCachingMemoryPackageReferenceVersion Condition=" '$(TargetFramework)' == 'net451' ">1.1.2</MicrosoftExtensionsCachingMemoryPackageReferenceVersion>
Expand All @@ -13,6 +13,7 @@
<NETStandardLibrary20PackageReferenceVersion>2.0.3</NETStandardLibrary20PackageReferenceVersion>
<NUnitPackageReferenceVersion>3.12.0</NUnitPackageReferenceVersion>
<NUnit3TestAdapterPackageReferenceVersion>3.13.0</NUnit3TestAdapterPackageReferenceVersion>
<SystemMemoryPackageReferenceVersion>4.5.5</SystemMemoryPackageReferenceVersion>
</PropertyGroup>
<PropertyGroup Label="Maven Package Reference Versions">
<ICU4JMavenPackageReferenceVersion>60.1</ICU4JMavenPackageReferenceVersion>
Expand Down
20 changes: 12 additions & 8 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@
<!--Features in .NET 6+ only-->
<PropertyGroup Condition=" $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">

<!-- For now, we only support ArrayPool on .NET 6. -->
<DefineConstants>$(DefineConstants);FEATURE_ARRAYPOOL</DefineConstants>
<!-- For now, we only support Span on .NET 6. -->
<DefineConstants>$(DefineConstants);FEATURE_SPAN</DefineConstants>
<!-- For now, we only support Rune on .NET 6. -->
<DefineConstants>$(DefineConstants);FEATURE_RUNE</DefineConstants>
<!-- For now, we only support Span on .NET 6. -->
<DefineConstants>$(DefineConstants);FEATURE_SPANFORMATTABLE</DefineConstants>

</PropertyGroup>
Expand All @@ -33,6 +26,14 @@

</PropertyGroup>

<!-- Features in .NET Core 3.x, .NET 5.x, .NET 6.x, and .NET 7.x -->
<PropertyGroup Condition=" $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">

<DefineConstants>$(DefineConstants);FEATURE_RUNE</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_STRING_CONCAT_READONLYSPAN</DefineConstants>

</PropertyGroup>

<!--Features in .NET Standard 2.x or .NET Core-->
<PropertyGroup Condition=" $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('netcoreapp')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">

Expand All @@ -51,19 +52,22 @@
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' Or ($(TargetFramework.StartsWith('netcoreapp2.')) And '$(TargetFramework)' != 'netcoreapp2.0') Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">

<DefineConstants>$(DefineConstants);FEATURE_BIGINTEGER_TOBYTEARRAY_BIGENDIAN</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_STRING_IMPLCIT_TO_READONLYSPAN</DefineConstants>

</PropertyGroup>

<!-- Features in .NET Framework 4.5+, .NET Standard 2.x, .NET Core 2.x, .NET Core 3.x, .NET 5.x, .NET 6.x, and .NET 7.x -->
<!-- These features are not in .NET Framework 4.0 or .NET Framework 4.5.2 (the target framework we use for testing .NET Framework 4.0) -->
<PropertyGroup Condition=" ('$(TargetFramework)' != 'net40' And '$(TargetFramework)' != 'net452' And $(TargetFramework.StartsWith('net4'))) Or $(TargetFramework.StartsWith('netstandard2.')) Or $(TargetFramework.StartsWith('netcoreapp2.')) Or $(TargetFramework.StartsWith('netcoreapp3.')) Or $(TargetFramework.StartsWith('net5.')) Or $(TargetFramework.StartsWith('net6.')) Or $(TargetFramework.StartsWith('net7.')) ">

<DefineConstants>$(DefineConstants);FEATURE_ARRAYPOOL</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_CULTUREINFO_DEFAULTTHREADCURRENTCULTURE</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_EXCEPTION_HRESULT</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_IREADONLYCOLLECTIONS</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_METHODIMPLOPTIONS_AGRESSIVEINLINING</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_MICROSOFT_EXTENSIONS_CACHING</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_IREADONLYCOLLECTIONS</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_REGEX_MATCHTIMEOUT</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_SPAN</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_TASK_ASYNC_AWAIT</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_TASK_RUN</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_TYPEDWEAKREFERENCE</DefineConstants>
Expand Down
2 changes: 2 additions & 0 deletions src/ICU4N/ICU4N.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@

<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageReferenceVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageReferenceVersion)" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageReferenceVersion)" />
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageReferenceVersion)" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
Expand Down
17 changes: 10 additions & 7 deletions src/ICU4N/Impl/Number/DecimalQuantity_AbstractBCD.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using ICU4N.Support.Text;
using ICU4N.Text;
using J2N;
using J2N.Globalization;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -526,20 +527,22 @@ private void ConvertToAccurateDouble()
Debug.Assert(dstr.IndexOf('.') == 1);
int expPos = dstr.IndexOf('E');
#if FEATURE_SPAN
SetToLongImpl(long.Parse(string.Concat(dstr.AsSpan(0, 1), dstr.AsSpan(2, expPos - 2)), CultureInfo.InvariantCulture)); // ICU4N: Corrected 2nd arg.
SetToLongImpl(long.Parse(StringHelper.Concat(dstr.AsSpan(0, 1), dstr.AsSpan(2, expPos - 2)), CultureInfo.InvariantCulture)); // ICU4N: Corrected 2nd arg.
scale += J2N.Numerics.Int32.Parse(dstr.AsSpan(expPos + 1), NumberStyle.Integer, CultureInfo.InvariantCulture) - (expPos - 1) + 1;
#else
SetToLongImpl(long.Parse(dstr[0] + dstr.Substring(2, expPos - 2), CultureInfo.InvariantCulture)); // ICU4N: Corrected 2nd arg.
scale += J2N.Numerics.Int32.Parse(dstr, startIndex: expPos + 1, length: dstr.Length - (expPos + 1), radix: 10) - (expPos - 1) + 1;
#endif
scale += int.Parse(dstr.Substring(expPos + 1), CultureInfo.InvariantCulture) - (expPos - 1) + 1;

}
else if (dstr[0] == '0')
{
// Case 2: Fraction-only number.
Debug.Assert(dstr.IndexOf('.') == 1);
#if FEATURE_SPAN
SetToLongImpl(long.Parse(dstr.AsSpan(2), NumberStyles.None, CultureInfo.InvariantCulture));
SetToLongImpl(J2N.Numerics.Int64.Parse(dstr.AsSpan(2), NumberStyle.None, CultureInfo.InvariantCulture));
#else
SetToLongImpl(long.Parse(dstr.Substring(2), NumberStyles.None, CultureInfo.InvariantCulture));
SetToLongImpl(J2N.Numerics.Int64.Parse(dstr, startIndex: 2, length: dstr.Length - 2, radix: 10));
#endif
scale += 2 - dstr.Length;
}
Expand All @@ -551,10 +554,10 @@ private void ConvertToAccurateDouble()
Debug.Assert(dstr.IndexOf('.') == dstr.Length - 2);
Debug.Assert(dstr.Length - 2 <= 18);
#if FEATURE_SPAN
SetToLongImpl(long.Parse(dstr.AsSpan(0, dstr.Length - 2), NumberStyles.None, CultureInfo.InvariantCulture)); // ICU4N: Checked 2nd arg
SetToLongImpl(J2N.Numerics.Int64.Parse(dstr.AsSpan(0, dstr.Length - 2), NumberStyle.None, CultureInfo.InvariantCulture)); // ICU4N: Checked 2nd arg
// no need to adjust scale
#else
SetToLongImpl(long.Parse(dstr.Substring(0, dstr.Length - 2), NumberStyles.None, CultureInfo.InvariantCulture)); // ICU4N: Checked 2nd arg
SetToLongImpl(J2N.Numerics.Int64.Parse(dstr, startIndex: 0, length: dstr.Length - 2, radix:10)); // ICU4N: Checked 2nd arg
// no need to adjust scale
#endif
}
Expand All @@ -563,7 +566,7 @@ private void ConvertToAccurateDouble()
// Case 4: Number with both a fraction and an integer.
int decimalPos = dstr.IndexOf('.');
#if FEATURE_SPAN
SetToLongImpl(long.Parse(string.Concat(dstr.AsSpan(0, decimalPos), dstr.AsSpan(decimalPos + 1)), CultureInfo.InvariantCulture)); // ICU4N: Checked 2nd arg
SetToLongImpl(long.Parse(StringHelper.Concat(dstr.AsSpan(0, decimalPos), dstr.AsSpan(decimalPos + 1)), CultureInfo.InvariantCulture)); // ICU4N: Checked 2nd arg
#else
SetToLongImpl(long.Parse(dstr.Substring(0, decimalPos) + dstr.Substring(decimalPos + 1), CultureInfo.InvariantCulture)); // ICU4N: Checked 2nd arg
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public override void DoSubstitution(double number, ref ValueStringBuilder toInse
{
// if we're not in "byDigits" mode, just use the inherited
// doSubstitution() routine
base.DoSubstitution(number, ref toInsertInto, position, info, recursionCount);
base.DoSubstitution(number, ref toInsertInto, position, info!, recursionCount);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using ICU4N.Support.Text;
using ICU4N.Text;
using System;
using System.Diagnostics;
using System.Globalization;
#nullable enable

namespace ICU4N.Globalization
Expand Down Expand Up @@ -80,8 +82,8 @@ internal ModulusSubstitution(int pos,

if (divisor == 0)
{ // this will cause recursion
throw new InvalidOperationException(string.Concat("Substitution with bad divisor (" + divisor + ") ", description.Slice(0, pos), // ICU4N: Checked 2nd parameter
" | ", description.Slice(pos)));
throw new InvalidOperationException(StringHelper.Concat(("Substitution with bad divisor (" + divisor.ToString(CultureInfo.InvariantCulture) + ") ").AsSpan(), description.Slice(0, pos), // ICU4N: Checked 2nd parameter
" | ".AsSpan(), description.Slice(pos)));
}

// the >>> token doesn't alter how this substitution calculates the
Expand Down Expand Up @@ -168,15 +170,15 @@ public override void DoSubstitution(long number, ref ValueStringBuilder toInsert
// to format its substitution value)
if (ruleToUse is null)
{
base.DoSubstitution(number, ref toInsertInto, position, info, recursionCount);
base.DoSubstitution(number, ref toInsertInto, position, info!, recursionCount);

}
else
{
// a >>> substitution goes straight to a particular rule to
// format the substitution value
long numberToFormat = TransformNumber(number);
ruleToUse.DoFormat(numberToFormat, ref toInsertInto, position + pos, info, recursionCount);
ruleToUse.DoFormat(numberToFormat, ref toInsertInto, position + pos, info!, recursionCount);
}
}

Expand All @@ -199,15 +201,15 @@ public override void DoSubstitution(double number, ref ValueStringBuilder toInse
// to format its substitution value)
if (ruleToUse is null)
{
base.DoSubstitution(number, ref toInsertInto, position, info, recursionCount);
base.DoSubstitution(number, ref toInsertInto, position, info!, recursionCount);

}
else
{
// a >>> substitution goes straight to a particular rule to
// format the substitution value
double numberToFormat = TransformNumber(number);
ruleToUse.DoFormat(numberToFormat, ref toInsertInto, position + pos, info, recursionCount);
ruleToUse.DoFormat(numberToFormat, ref toInsertInto, position + pos, info!, recursionCount);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ internal MultiplierSubstitution(int pos,

if (divisor == 0)
{ // this will cause recursion
throw new InvalidOperationException(string.Concat("Substitution with divisor 0 ", description.Slice(0, pos), // ICU4N: Checked 2nd parameter
" | ", description.Slice(pos)));
throw new InvalidOperationException(StringHelper.Concat("Substitution with divisor 0 ".AsSpan(), description.Slice(0, pos), // ICU4N: Checked 2nd parameter
" | ".AsSpan(), description.Slice(pos)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,7 @@ public void DoFormat(long number, ref ValueStringBuilder toInsertInto, int pos,
}
double pluralVal = (double)number / Power(radix, exponent);
#if FEATURE_SPAN
unsafe
{
var sb = new ValueStringBuilder(stackalloc char[IcuNumber.PluralCharStackBufferSize]);
try
{
IcuNumber.FormatPlural(ref sb, pluralVal, null, pluralMessagePattern, pluralType, info);
toInsertInto.Insert(pos, new ReadOnlySpan<char>(sb.GetCharsPointer(), sb.Length));
}
finally
{
sb.Dispose();
}
}
toInsertInto.InsertFormatPlural(pos, pluralVal, null, pluralMessagePattern, pluralType, info!);
#else
toInsertInto.Insert(pos, IcuNumber.FormatPlural(pluralVal, null, pluralMessagePattern, pluralType, info));
#endif
Expand All @@ -76,8 +64,8 @@ public void DoFormat(long number, ref ValueStringBuilder toInsertInto, int pos,
}
lengthOffset = ruleText.Length - (toInsertInto.Length - initialLength);
}
sub2?.DoSubstitution(number, ref toInsertInto, pos - (sub2.Pos > pluralRuleStart ? lengthOffset : 0), info, recursionCount);
sub1?.DoSubstitution(number, ref toInsertInto, pos - (sub1.Pos > pluralRuleStart ? lengthOffset : 0), info, recursionCount);
sub2?.DoSubstitution(number, ref toInsertInto, pos - (sub2.Pos > pluralRuleStart ? lengthOffset : 0), info!, recursionCount);
sub1?.DoSubstitution(number, ref toInsertInto, pos - (sub1.Pos > pluralRuleStart ? lengthOffset : 0), info!, recursionCount);
}

/// <summary>
Expand Down Expand Up @@ -129,19 +117,7 @@ public void DoFormat(double number, ref ValueStringBuilder toInsertInto, int pos
pluralVal = pluralVal / Power(radix, exponent);
}
#if FEATURE_SPAN
unsafe
{
var sb = new ValueStringBuilder(stackalloc char[IcuNumber.PluralCharStackBufferSize]);
try
{
IcuNumber.FormatPlural(ref sb, (long)pluralVal, null, pluralMessagePattern, pluralType, info);
toInsertInto.Insert(pos, new ReadOnlySpan<char>(sb.GetCharsPointer(), sb.Length));
}
finally
{
sb.Dispose();
}
}
toInsertInto.InsertFormatPlural(pos, (long)pluralVal, null, pluralMessagePattern, pluralType, info!);
#else
toInsertInto.Insert(pos, IcuNumber.FormatPlural((long)pluralVal, null, pluralMessagePattern, pluralType, info));
#endif
Expand All @@ -155,8 +131,8 @@ public void DoFormat(double number, ref ValueStringBuilder toInsertInto, int pos
}
lengthOffset = ruleText.Length - (toInsertInto.Length - initialLength);
}
sub2?.DoSubstitution(number, ref toInsertInto, pos - (sub2.Pos > pluralRuleStart ? lengthOffset : 0), info, recursionCount);
sub1?.DoSubstitution(number, ref toInsertInto, pos - (sub1.Pos > pluralRuleStart ? lengthOffset : 0), info, recursionCount);
sub2?.DoSubstitution(number, ref toInsertInto, pos - (sub2.Pos > pluralRuleStart ? lengthOffset : 0), info!, recursionCount);
sub1?.DoSubstitution(number, ref toInsertInto, pos - (sub1.Pos > pluralRuleStart ? lengthOffset : 0), info!, recursionCount);
}

/// <summary>
Expand Down
Loading

0 comments on commit 21748f1

Please sign in to comment.