private static bool TryFormatUInt64Default(ulong value, Span <byte> buffer, out int bytesWritten) { if (value < 10) { if (buffer.Length == 0) { goto FalseExit; } buffer[0] = (byte)('0' + value); bytesWritten = 1; return(true); } int digitCount = FormattingHelpers.CountDigits(value); if (digitCount > buffer.Length) { goto FalseExit; } bytesWritten = digitCount; // WriteDigits does not do bounds checks FormattingHelpers.WriteDigits(value, buffer.Slice(0, digitCount)); return(true); FalseExit: bytesWritten = 0; return(false); }
/// <summary> /// Gets the total number of digits in the <see cref="Number"/>. /// </summary> /// <returns>The total number of digits in the <see cref="Number"/></returns> /// <exception cref="InvalidOperationException">The precision is greater than <see cref="int.MaxValue" />.</exception> public readonly int GetPrecision() { if (_isNull) { return(0); } int precision = FormattingHelpers.CountDigits(_unscaledNumber); int scale = Scale; if (precision <= scale) { return(precision + (scale - precision) + 1); } return(precision); }
private static bool TryFormatUInt64N(ulong value, byte precision, Span <byte> destination, bool insertNegationSign, out int bytesWritten) { // Calculate the actual digit count, number of group separators required, and the // number of trailing zeros requested. From all of this we can get the required // buffer length. int digitCount = FormattingHelpers.CountDigits(value); int commaCount = (digitCount - 1) / 3; int trailingZeroCount = (precision == StandardFormat.NoPrecision) ? 2 /* default for 'N' */ : precision; int requiredBufferLength = digitCount + commaCount; if (trailingZeroCount > 0) { requiredBufferLength += trailingZeroCount + 1; } if (insertNegationSign) { requiredBufferLength++; } if (requiredBufferLength > destination.Length) { bytesWritten = 0; return(false); } bytesWritten = requiredBufferLength; if (insertNegationSign) { destination[0] = Utf8Constants.Minus; destination = destination.Slice(1); } FormattingHelpers.WriteDigitsWithGroupSeparator(value, destination.Slice(0, digitCount + commaCount)); if (trailingZeroCount > 0) { destination[digitCount + commaCount] = Utf8Constants.Period; FormattingHelpers.FillWithAsciiZeros(destination.Slice(digitCount + commaCount + 1, trailingZeroCount)); } return(true); }
private static bool TryFormatInt64Default(long value, Span <byte> buffer, out int bytesWritten) { if ((ulong)value < 10) { if (buffer.Length == 0) { goto FalseExit; } buffer[0] = (byte)('0' + value); bytesWritten = 1; return(true); } if (value < 0) { value = -value; int digitCount = FormattingHelpers.CountDigits((ulong)value); if (digitCount >= buffer.Length) { goto FalseExit; } bytesWritten = digitCount + 1; buffer[0] = Utf8Constants.Minus; buffer = buffer.Slice(1, digitCount); } else { int digitCount = FormattingHelpers.CountDigits((ulong)value); if (digitCount > buffer.Length) { goto FalseExit; } bytesWritten = digitCount; buffer = buffer.Slice(0, digitCount); } // WriteDigits does not do bounds checks FormattingHelpers.WriteDigits((ulong)value, buffer); return(true); FalseExit: bytesWritten = 0; return(false); }
private static bool TryFormatUInt64D(ulong value, byte precision, Span <byte> destination, bool insertNegationSign, out int bytesWritten) { // Calculate the actual digit count and the number of padding zeroes requested. // From all of this we can get the required buffer length. int digitCount = FormattingHelpers.CountDigits(value); int leadingZeroCount = ((precision == StandardFormat.NoPrecision) ? 0 : (int)precision) - digitCount; if (leadingZeroCount < 0) { leadingZeroCount = 0; } int requiredBufferLength = digitCount + leadingZeroCount; if (insertNegationSign) { requiredBufferLength++; } if (requiredBufferLength > destination.Length) { bytesWritten = 0; return(false); } bytesWritten = requiredBufferLength; if (insertNegationSign) { destination[0] = Utf8Constants.Minus; destination = destination.Slice(1); } if (leadingZeroCount > 0) { FormattingHelpers.FillWithAsciiZeros(destination.Slice(0, leadingZeroCount)); } FormattingHelpers.WriteDigits(value, destination.Slice(leadingZeroCount, digitCount)); return(true); }
private static bool TryFormatStandard(TimeSpan value, StandardFormat format, string?decimalSeparator, Span <char> destination, out int charsWritten) { Debug.Assert(format == StandardFormat.C || format == StandardFormat.G || format == StandardFormat.g); // First, calculate how large an output buffer is needed to hold the entire output. int requiredOutputLength = 8; // start with "hh:mm:ss" and adjust as necessary uint fraction; ulong totalSecondsRemaining; { // Turn this into a non-negative TimeSpan if possible. long ticks = value.Ticks; if (ticks < 0) { requiredOutputLength = 9; // requiredOutputLength + 1 for the leading '-' sign ticks = -ticks; if (ticks < 0) { Debug.Assert(ticks == long.MinValue /* -9223372036854775808 */); // We computed these ahead of time; they're straight from the decimal representation of Int64.MinValue. fraction = 4775808; totalSecondsRemaining = 922337203685; goto AfterComputeFraction; } } ulong fraction64; (totalSecondsRemaining, fraction64) = Math.DivRem((ulong)ticks, TimeSpan.TicksPerSecond); fraction = (uint)fraction64; } AfterComputeFraction: // Only write out the fraction if it's non-zero, and in that // case write out the entire fraction (all digits). Debug.Assert(fraction < 10_000_000); int fractionDigits = 0; switch (format) { case StandardFormat.C: // "c": Write out a fraction only if it's non-zero, and write out all 7 digits of it. if (fraction != 0) { fractionDigits = DateTimeFormat.MaxSecondsFractionDigits; requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator } break; case StandardFormat.G: // "G": Write out a fraction regardless of whether it's 0, and write out all 7 digits of it. fractionDigits = DateTimeFormat.MaxSecondsFractionDigits; requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator break; default: // "g": Write out a fraction only if it's non-zero, and write out only the most significant digits. Debug.Assert(format == StandardFormat.g); if (fraction != 0) { fractionDigits = DateTimeFormat.MaxSecondsFractionDigits - FormattingHelpers.CountDecimalTrailingZeros(fraction, out fraction); requiredOutputLength += fractionDigits + 1; // digits plus leading decimal separator } break; } ulong totalMinutesRemaining = 0, seconds = 0; if (totalSecondsRemaining > 0) { // Only compute minutes if the TimeSpan has an absolute value of >= 1 minute. (totalMinutesRemaining, seconds) = Math.DivRem(totalSecondsRemaining, 60 /* seconds per minute */); Debug.Assert(seconds < 60); } ulong totalHoursRemaining = 0, minutes = 0; if (totalMinutesRemaining > 0) { // Only compute hours if the TimeSpan has an absolute value of >= 1 hour. (totalHoursRemaining, minutes) = Math.DivRem(totalMinutesRemaining, 60 /* minutes per hour */); Debug.Assert(minutes < 60); } // At this point, we can switch over to 32-bit DivRem since the data has shrunk far enough. Debug.Assert(totalHoursRemaining <= uint.MaxValue); uint days = 0, hours = 0; if (totalHoursRemaining > 0) { // Only compute days if the TimeSpan has an absolute value of >= 1 day. (days, hours) = Math.DivRem((uint)totalHoursRemaining, 24 /* hours per day */); Debug.Assert(hours < 24); } int hourDigits = 2; if (format == StandardFormat.g && hours < 10) { // "g": Only writing a one-digit hour, rather than expected two-digit hour hourDigits = 1; requiredOutputLength--; } int dayDigits = 0; if (days > 0) { dayDigits = FormattingHelpers.CountDigits(days); Debug.Assert(dayDigits <= 8); requiredOutputLength += dayDigits + 1; // for the leading "d." } else if (format == StandardFormat.G) { // "G": has a leading "0:" if days is 0 requiredOutputLength += 2; dayDigits = 1; } if (destination.Length < requiredOutputLength) { charsWritten = 0; return(false); } // Write leading '-' if necessary int idx = 0; if (value.Ticks < 0) { destination[idx++] = '-'; } // Write day and separator, if necessary if (dayDigits != 0) { WriteDigits(days, destination.Slice(idx, dayDigits)); idx += dayDigits; destination[idx++] = format == StandardFormat.C ? '.' : ':'; } // Write "[h]h:mm:ss Debug.Assert(hourDigits == 1 || hourDigits == 2); if (hourDigits == 2) { WriteTwoDigits(hours, destination.Slice(idx)); idx += 2; } else { destination[idx++] = (char)('0' + hours); } destination[idx++] = ':'; WriteTwoDigits((uint)minutes, destination.Slice(idx)); idx += 2; destination[idx++] = ':'; WriteTwoDigits((uint)seconds, destination.Slice(idx)); idx += 2; // Write fraction and separator, if necessary if (fractionDigits != 0) { Debug.Assert(format == StandardFormat.C || decimalSeparator != null); if (format == StandardFormat.C) { destination[idx++] = '.'; } else if (decimalSeparator !.Length == 1) { destination[idx++] = decimalSeparator[0]; } else { decimalSeparator.AsSpan().CopyTo(destination); idx += decimalSeparator.Length; } WriteDigits(fraction, destination.Slice(idx, fractionDigits)); idx += fractionDigits; }
private static bool TryFormatC(TimeSpan value, Span <char> destination, out int charsWritten) { // First, calculate how large an output buffer is needed to hold the entire output. int requiredOutputLength = 8; // start with "hh:mm:ss" and adjust as necessary uint fraction; ulong totalSecondsRemaining; { // Turn this into a non-negative TimeSpan if possible. long ticks = value.Ticks; if (ticks < 0) { requiredOutputLength = 9; // requiredOutputLength + 1 for the leading '-' sign ticks = -ticks; if (ticks < 0) { Debug.Assert(ticks == long.MinValue /* -9223372036854775808 */); // We computed these ahead of time; they're straight from the decimal representation of Int64.MinValue. fraction = 4775808; totalSecondsRemaining = 922337203685; goto AfterComputeFraction; } } totalSecondsRemaining = Math.DivRem((ulong)ticks, TimeSpan.TicksPerSecond, out ulong fraction64); fraction = (uint)fraction64; } AfterComputeFraction: // Only write out the fraction if it's non-zero, and in that // case write out the entire fraction (all digits). int fractionDigits = 0; if (fraction != 0) { Debug.Assert(fraction < 10_000_000); fractionDigits = DateTimeFormat.MaxSecondsFractionDigits; requiredOutputLength += fractionDigits + 1; // If we're going to write out a fraction, also need to write the leading decimal. } ulong totalMinutesRemaining = 0, seconds = 0; if (totalSecondsRemaining > 0) { // Only compute minutes if the TimeSpan has an absolute value of >= 1 minute. totalMinutesRemaining = Math.DivRem(totalSecondsRemaining, 60 /* seconds per minute */, out seconds); Debug.Assert(seconds < 60); } ulong totalHoursRemaining = 0, minutes = 0; if (totalMinutesRemaining > 0) { // Only compute hours if the TimeSpan has an absolute value of >= 1 hour. totalHoursRemaining = Math.DivRem(totalMinutesRemaining, 60 /* minutes per hour */, out minutes); Debug.Assert(minutes < 60); } // At this point, we can switch over to 32-bit DivRem since the data has shrunk far enough. Debug.Assert(totalHoursRemaining <= uint.MaxValue); uint days = 0, hours = 0; if (totalHoursRemaining > 0) { // Only compute days if the TimeSpan has an absolute value of >= 1 day. days = Math.DivRem((uint)totalHoursRemaining, 24 /* hours per day */, out hours); Debug.Assert(hours < 24); } int dayDigits = 0; if (days > 0) { dayDigits = FormattingHelpers.CountDigits(days); Debug.Assert(dayDigits <= 8); requiredOutputLength += dayDigits + 1; // for the leading "d." } if (destination.Length < requiredOutputLength) { charsWritten = 0; return(false); } // Write leading '-' if necessary int idx = 0; if (value.Ticks < 0) { destination[idx++] = '-'; } // Write day and separator, if necessary if (dayDigits != 0) { WriteDigits(days, destination.Slice(idx, dayDigits)); idx += dayDigits; destination[idx++] = '.'; } // Write "hh:mm:ss" WriteTwoDigits(hours, destination.Slice(idx)); idx += 2; destination[idx++] = ':'; WriteTwoDigits((uint)minutes, destination.Slice(idx)); idx += 2; destination[idx++] = ':'; WriteTwoDigits((uint)seconds, destination.Slice(idx)); idx += 2; // Write fraction and separator, if necessary if (fractionDigits != 0) { destination[idx++] = '.'; WriteDigits(fraction, destination.Slice(idx, fractionDigits)); idx += fractionDigits; } Debug.Assert(idx == requiredOutputLength); charsWritten = requiredOutputLength; return(true); }