Skip to content

Commit

Permalink
Merge pull request #25 from cpsusie/issue_24
Browse files Browse the repository at this point in the history
Close issue #24
  • Loading branch information
cpsusie authored Oct 1, 2021
2 parents dd33a67 + 8a40a79 commit eacc61e
Show file tree
Hide file tree
Showing 21 changed files with 712 additions and 145 deletions.
44 changes: 31 additions & 13 deletions BigMath/Int128.cs
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ public readonly bool TryConvert(Type conversionType, IFormatProvider provider, b
/// <returns>
/// A value that is equivalent to the number specified in the value parameter.
/// </returns>
public static Int128 Parse(string value) => Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
public static Int128 Parse(ReadOnlySpan<char> value) => Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);

/// <summary>
/// Converts the string representation of a number in a specified style format to its Int128 equivalent.
Expand All @@ -571,7 +571,7 @@ public readonly bool TryConvert(Type conversionType, IFormatProvider provider, b
/// <returns>
/// A value that is equivalent to the number specified in the value parameter.
/// </returns>
public static Int128 Parse(string value, NumberStyles style) => Parse(value, style, NumberFormatInfo.CurrentInfo);
public static Int128 Parse(ReadOnlySpan<char> value, NumberStyles style) => Parse(value, style, NumberFormatInfo.CurrentInfo);

/// <summary>
/// Converts the string representation of a number in a culture-specific format to its Int128 equivalent.
Expand All @@ -581,7 +581,7 @@ public readonly bool TryConvert(Type conversionType, IFormatProvider provider, b
/// <returns>
/// A value that is equivalent to the number specified in the value parameter.
/// </returns>
public static Int128 Parse(string value, IFormatProvider provider) => Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
public static Int128 Parse(ReadOnlySpan<char> value, IFormatProvider provider) => Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));

/// <summary>
/// Converts the string representation of a number in a specified style and culture-specific format to its Int128
Expand All @@ -591,7 +591,7 @@ public readonly bool TryConvert(Type conversionType, IFormatProvider provider, b
/// <param name="style">A bitwise combination of the enumeration values that specify the permitted format of value.</param>
/// <param name="provider">An object that provides culture-specific formatting information about value.</param>
/// <returns>A value that is equivalent to the number specified in the value parameter.</returns>
public static Int128 Parse(string value, NumberStyles style, IFormatProvider provider)
public static Int128 Parse(ReadOnlySpan<char> value, NumberStyles style, IFormatProvider provider)
{
Int128 result;
if (!TryParse(value, style, provider, out result))
Expand All @@ -614,7 +614,7 @@ public static Int128 Parse(string value, NumberStyles style, IFormatProvider pro
/// <returns>
/// true if the value parameter was converted successfully; otherwise, false.
/// </returns>
public static bool TryParse(string value, out Int128 result) => TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
public static bool TryParse(ReadOnlySpan<char> value, out Int128 result) => TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);

/// <summary>
/// Tries to convert the string representation of a number in a specified style and culture-specific format to its
Expand All @@ -634,23 +634,26 @@ public static Int128 Parse(string value, NumberStyles style, IFormatProvider pro
/// or Int128.Zero if the conversion failed. This parameter is passed uninitialized.
/// </param>
/// <returns>true if the value parameter was converted successfully; otherwise, false.</returns>
public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out Int128 result)
public static bool TryParse(ReadOnlySpan<char> value, NumberStyles style, IFormatProvider provider, out Int128 result)
{
result = Zero;
if (string.IsNullOrEmpty(value))
if (value.IsEmpty || value.IsWhiteSpace())
{
return false;
}

if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
ReadOnlySpan<char> x = stackalloc char[1] { 'x' };
ReadOnlySpan<char> ohX = stackalloc char[2] { '0', 'x' };

if (value.StartsWith(x, StringComparison.OrdinalIgnoreCase))
{
style |= NumberStyles.AllowHexSpecifier;
value = value.Substring(1);
value = value.Slice(1);
}
else if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
else if (value.StartsWith(ohX, StringComparison.OrdinalIgnoreCase))
{
style |= NumberStyles.AllowHexSpecifier;
value = value.Substring(2);
value = value.Slice(2);
}

if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
Expand All @@ -661,7 +664,7 @@ public static bool TryParse(string value, NumberStyles style, IFormatProvider pr
return TryParseNum(value, out result);
}

private static bool TryParseHex(string value, out Int128 result)
private static bool TryParseHex(ReadOnlySpan<char> value, out Int128 result)
{
if (value.Length > 32)
{
Expand Down Expand Up @@ -711,16 +714,26 @@ private static bool TryParseHex(string value, out Int128 result)
return true;
}

private static bool TryParseNum(string value, out Int128 result)
private static bool TryParseNum(ReadOnlySpan<char> value, out Int128 result)
{
ReadOnlySpan<char> dash = stackalloc char[1] { '-' };
result = Zero;
bool isNegative = value.StartsWith(dash);
if (isNegative)
{
value = value.Slice(1);
}
foreach (char ch in value)
{
byte b;
if ((ch >= '0') && (ch <= '9'))
{
b = (byte) (ch - '0');
}
else if (ch == ',')
{
continue;
}
else
{
return false;
Expand All @@ -729,6 +742,11 @@ private static bool TryParseNum(string value, out Int128 result)
result = 10*result;
result += b;
}

if (isNegative)
{
result = 0 - result;
}
return true;
}

Expand Down
23 changes: 16 additions & 7 deletions High Precision Time Stamps.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,27 @@
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
<Authors>Christopher P. Susie</Authors>
<Company>CJM Screws, LLC</Company>
<Version>1.0.0.4</Version>
<Version>1.0.0.6</Version>
<Description>It is well known that DateTime.Now is often used inappropriately. For example, it may be used together with TimeSpan to produce a task's timeout point or subtracted from another DateTime to calculate a duration. This can cause subtle bugs because DateTime is not monotonic: the system clock can change, making the result of the subtraction inaccurate -- potentially causing a premature timeout or an infinite loop. Yet, DateTime is an incredibly convenient and widely used value type in .NET code and is especially useful when printed in ISO-8601 format (with the "O" format specifier).

With the "O" specifier, you can resolution down to tenths of a microsecond, which is nice. Until you learn that the resolution of the system clock is usually more coarse than several *milliseconds*, making the additional decimal places misleading garbage values. For calculating durations (time between events), it is better to use a high-resolution and monotonic clock like that provided by System.Diagnostics.Stopwatch: on most computers it is far more **accurate** than DateTime.Now even though, seemingly paradoxically, on a few systems, its *resolution* is lower than that of DateTime. Also, unsurprisingly, Stopwatch does not provide values that correlate to times of day: while it is appropriate for calculating durations, it is inappropriate for timestamping against a readable date and time.

This library provides timestamps (both as DateTime and as analogous value types it defines) that use the Stopwatch (and your system's high peformance event counter) as its clock, but returns values as DateTimes or an analog thereto so that these values can be used for a mixed purpose of timestamping and providing a meaningful way to calculate time elapsed between events or to calculate how long to perform a programmatic task.</Description>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReleaseNotes>
#### Version 1.0.0.6:
To resolve ([Issue 24][10]), add Parse and TryParse methods to parse stringified PortableMonotonicTimestamps and PortableDurations. These methods only work with values stringified via the default ToString() methods.

Added unit tests for the methods.

Unit test application now enables the nullable reference type feature.

#### Version 1.0.0.4:
This release fixes two bugs.

First (see [Issue 21][8]), it fixes a bug where using monotonic timestamps close to the extreme values (within a month or so of 1-1-0001AD and 12-31-9999AD) was causing overflow in .NET 5.0. The fix increased the buffer around the min/max values so that overflow does not occur in .NET 5.0. You may have to alter your uses if you were (for some reason) storing portable monotonic stamps close to the extrema of permissible values.

Second (see [Issue 22][9]), it fixes a bug where subtracting a portable duration from a portable monotonic stamp was executing addition, not subtraction.
Second (see [Issue 22][9]), it fixes a bug where subtracting a portable duration from a portable monotonic stamp was executing addition, not subtraction.

Finally, please note that unit test applications, example code and test application are now all built and run against .NET 5.0 rather than .NET Core 3.1.
#### Version 1.0.0.2:
Expand All @@ -42,13 +49,14 @@ This library provides timestamps (both as DateTime and as analogous value types
[6]: https://github.com/cpsusie/High-Precision-Time-Stamps/commit/01670d88755a4775100f7dd9d09eef61e0775555
[7]: https://github.com/cpsusie/High-Precision-Time-Stamps/blob/01670d88755a4775100f7dd9d09eef61e0775555/PortableMonotonicStamp.cs#L540
[8]: https://github.com/cpsusie/High-Precision-Time-Stamps/issues/21
[9]: https://github.com/cpsusie/High-Precision-Time-Stamps/issues/22
[9]: https://github.com/cpsusie/High-Precision-Time-Stamps/issues/22
[10]: https://github.com/cpsusie/High-Precision-Time-Stamps/issues/24
</PackageReleaseNotes>
<RepositoryUrl>https://github.com/cpsusie/High-Precision-Time-Stamps.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>Timestamps DateTime Duration Monotonic-Clock TimeSpan High-Resolution ISO-8601</PackageTags>
<AssemblyVersion>1.0.0.4</AssemblyVersion>
<FileVersion>1.0.0.4</FileVersion>
<AssemblyVersion>1.0.0.6</AssemblyVersion>
<FileVersion>1.0.0.6</FileVersion>
<Copyright>Copyright (c) 2020-2021 CJM Screws, LLC</Copyright>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
Expand All @@ -60,15 +68,15 @@ This library provides timestamps (both as DateTime and as analogous value types
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>./HighPrecisionTimeStamps.xml</DocumentationFile>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<LangVersion>8.0</LangVersion>
<WarningsAsErrors />
<LangVersion>9</LangVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<LangVersion>8.0</LangVersion>
<WarningsAsErrors />
<DocumentationFile>./HighPrecisionTimeStamps.xml</DocumentationFile>
<LangVersion>9</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down Expand Up @@ -98,6 +106,7 @@ This library provides timestamps (both as DateTime and as analogous value types

<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
<PackageReference Include="System.Collections.Immutable" Version="5.0.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
Expand Down
Loading

0 comments on commit eacc61e

Please sign in to comment.