// Split long into three parts that can each fit in a uint - {1 digit}{9 digits}{9 digits} private static bool TryFormatInt64LessThanNegativeBillionMaxUInt(long value, Span <byte> destination, out int bytesWritten) { // value can still be negative if value == long.MinValue // Therefore, cast to ulong, since (ulong)value actually equals abs(long.MinValue) ulong overNineDigits = (ulong)value / Utf8Constants.Billion; uint lastNineDigits = (uint)((ulong)value - (overNineDigits * Utf8Constants.Billion)); uint overEighteenDigits = (uint)(overNineDigits / Utf8Constants.Billion); uint middleNineDigits = (uint)(overNineDigits - (overEighteenDigits * Utf8Constants.Billion)); int digitCountOverEighteenDigits = FormattingHelpers.CountDigits(overEighteenDigits); Debug.Assert(digitCountOverEighteenDigits == 1); int digitCount = digitCountOverEighteenDigits + 18; // WriteDigits does not do bounds checks if (digitCount >= destination.Length) { bytesWritten = 0; return(false); } destination[0] = Utf8Constants.Minus; bytesWritten = digitCount + 1; FormattingHelpers.WriteDigits(overEighteenDigits, destination.Slice(1, digitCountOverEighteenDigits)); FormattingHelpers.WriteDigits(middleNineDigits, destination.Slice(digitCountOverEighteenDigits + 1, 9)); FormattingHelpers.WriteDigits(lastNineDigits, destination.Slice(digitCountOverEighteenDigits + 1 + 9, 9)); return(true); }
private static bool TryFormatInt64N(long value, byte precision, Span <byte> buffer, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); int groupSeparators = (int)FormattingHelpers.DivMod(digitCount, Utf8Constants.GroupSize, out long firstGroup); if (firstGroup == 0) { firstGroup = 3; groupSeparators--; } int trailingZeros = (precision == StandardFormat.NoPrecision) ? 2 : precision; int idx = (int)((value >> 63) & 1) + digitCount + groupSeparators; bytesWritten = idx; if (trailingZeros > 0) { bytesWritten += trailingZeros + 1; // +1 for period. } if (buffer.Length < bytesWritten) { bytesWritten = 0; return(false); } ref byte utf8Bytes = ref MemoryMarshal.GetReference(buffer);
private static bool TryFormatDecimalInt64(long value, byte precision, Span <byte> buffer, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); int bytesNeeded = digitCount + (int)((value >> 63) & 1); if (buffer.Length < bytesNeeded) { bytesWritten = 0; return(false); } ref byte utf8Bytes = ref buffer.DangerousGetPinnableReference();
private static bool TryFormatDecimalInt64(long value, byte precision, Span <byte> buffer, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); int charsNeeded = digitCount + (int)((value >> 63) & 1); Span <char> span = buffer.NonPortableCast <byte, char>(); if (span.Length < charsNeeded) { bytesWritten = 0; return(false); } ref char utf16Bytes = ref span.DangerousGetPinnableReference();
private static bool TryFormatDecimalInt64(long value, byte precision, Span <byte> buffer, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); int charsNeeded = digitCount + (int)((value >> 63) & 1); Span <char> span = MemoryMarshal.Cast <byte, char>(buffer); if (span.Length < charsNeeded) { bytesWritten = 0; return(false); } ref char utf16Bytes = ref MemoryMarshal.GetReference(span);
private static bool TryFormatUInt64MultipleDigits(ulong value, Span <byte> destination, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); // WriteDigits does not do bounds checks if (digitCount > destination.Length) { bytesWritten = 0; return(false); } bytesWritten = digitCount; FormattingHelpers.WriteDigits(value, destination.Slice(0, digitCount)); return(true); }
private static bool TryFormatUInt32MultipleDigits(uint value, Span <byte> buffer, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); // WriteDigits does not do bounds checks if (digitCount > buffer.Length) { bytesWritten = 0; return(false); } bytesWritten = digitCount; FormattingHelpers.WriteDigits(value, buffer.Slice(0, digitCount)); return(true); }
private static bool TryFormatInt64D(long value, byte precision, Span <byte> buffer, out int bytesWritten) { int digitCount = FormattingHelpers.CountDigits(value); int bytesNeeded = digitCount + (int)((value >> 63) & 1); if (precision != StandardFormat.NoPrecision && precision > digitCount) { bytesNeeded += (precision - digitCount); } if (buffer.Length < bytesNeeded) { bytesWritten = 0; return(false); } ref byte utf8Bytes = ref MemoryMarshal.GetReference(buffer);
private static bool TryFormatUInt64LessThanBillionMaxUInt(ulong value, Span <byte> destination, out int bytesWritten) { uint overNineDigits = (uint)(value / Utf8Constants.Billion); uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); int digitCountOverNineDigits = FormattingHelpers.CountDigits(overNineDigits); Debug.Assert(digitCountOverNineDigits >= 1 && digitCountOverNineDigits <= 10); int digitCount = digitCountOverNineDigits + 9; // WriteDigits does not do bounds checks if (digitCount > destination.Length) { bytesWritten = 0; return(false); } bytesWritten = digitCount; FormattingHelpers.WriteDigits(overNineDigits, destination.Slice(0, digitCountOverNineDigits)); FormattingHelpers.WriteDigits(lastNineDigits, destination.Slice(digitCountOverNineDigits, 9)); return(true); }
private static bool TryFormatInt64MultipleDigits(long value, Span <byte> destination, out int bytesWritten) { if (value < 0) { value = -value; int digitCount = FormattingHelpers.CountDigits((ulong)value); // WriteDigits does not do bounds checks if (digitCount >= destination.Length) { bytesWritten = 0; return(false); } destination[0] = Utf8Constants.Minus; bytesWritten = digitCount + 1; FormattingHelpers.WriteDigits((ulong)value, destination.Slice(1, digitCount)); return(true); } else { return(TryFormatUInt64MultipleDigits((ulong)value, destination, out bytesWritten)); } }
private static bool TryFormatInt32MultipleDigits(int value, Span <byte> buffer, out int bytesWritten) { if (value < 0) { value = -value; int digitCount = FormattingHelpers.CountDigits((uint)value); // WriteDigits does not do bounds checks if (digitCount >= buffer.Length) { bytesWritten = 0; return(false); } buffer[0] = Utf8Constants.Minus; bytesWritten = digitCount + 1; FormattingHelpers.WriteDigits((uint)value, buffer.Slice(1, digitCount)); return(true); } else { return(TryFormatUInt32MultipleDigits((uint)value, buffer, out bytesWritten)); } }
// Split long into two parts that can each fit in a uint - {1-10 digits}{9 digits} private static bool TryFormatInt64MoreThanNegativeBillionMaxUInt(long value, Span <byte> buffer, out int bytesWritten) { uint overNineDigits = (uint)(value / Utf8Constants.Billion); uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); int digitCountOverNineDigits = FormattingHelpers.CountDigits(overNineDigits); Debug.Assert(digitCountOverNineDigits >= 1 && digitCountOverNineDigits <= 10); int digitCount = digitCountOverNineDigits + 9; // WriteDigits does not do bounds checks if (digitCount >= buffer.Length) { bytesWritten = 0; return(false); } buffer[0] = Utf8Constants.Minus; bytesWritten = digitCount + 1; FormattingHelpers.WriteDigits(overNineDigits, buffer.Slice(1, digitCountOverNineDigits)); FormattingHelpers.WriteDigits(lastNineDigits, buffer.Slice(digitCountOverNineDigits + 1, 9)); return(true); }
// Split ulong into three parts that can each fit in a uint - {1-2 digits}{9 digits}{9 digits} private static bool TryFormatUInt64MoreThanBillionMaxUInt(ulong value, Span <byte> buffer, out int bytesWritten) { ulong overNineDigits = value / Utf8Constants.Billion; uint lastNineDigits = (uint)(value - (overNineDigits * Utf8Constants.Billion)); uint overEighteenDigits = (uint)(overNineDigits / Utf8Constants.Billion); uint middleNineDigits = (uint)(overNineDigits - (overEighteenDigits * Utf8Constants.Billion)); int digitCountOverEighteenDigits = FormattingHelpers.CountDigits(overEighteenDigits); Debug.Assert(digitCountOverEighteenDigits >= 1 && digitCountOverEighteenDigits <= 2); int digitCount = digitCountOverEighteenDigits + 18; // WriteDigits does not do bounds checks if (digitCount > buffer.Length) { bytesWritten = 0; return(false); } bytesWritten = digitCount; FormattingHelpers.WriteDigits(overEighteenDigits, buffer.Slice(0, digitCountOverEighteenDigits)); FormattingHelpers.WriteDigits(middleNineDigits, buffer.Slice(digitCountOverEighteenDigits, 9)); FormattingHelpers.WriteDigits(lastNineDigits, buffer.Slice(digitCountOverEighteenDigits + 9, 9)); return(true); }
/// <summary> /// Formats a TimeSpan as a UTF8 string. /// </summary> /// <param name="value">Value to format</param> /// <param name="destination">Buffer to write the UTF8-formatted value to</param> /// <param name="bytesWritten">Receives the length of the formatted text in bytes</param> /// <param name="format">The standard format to use</param> /// <returns> /// true for success. "bytesWritten" contains the length of the formatted text in bytes. /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. /// </returns> /// <remarks> /// Formats supported: /// c/t/T (default) [-][d.]hh:mm:ss[.fffffff] (constant format) /// G [-]d:hh:mm:ss.fffffff (general long) /// g [-][d:][h]h:mm:ss[.f[f[f[f[f[f[f]]]]]] (general short) /// </remarks> /// <exceptions> /// <cref>System.FormatException</cref> if the format is not valid for this data type. /// </exceptions> public static bool TryFormat(TimeSpan value, Span <byte> destination, out int bytesWritten, StandardFormat format = default) { char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'c'); switch (symbol) { case 'c': case 'G': case 'g': break; case 't': case 'T': symbol = 'c'; break; default: return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten)); } // 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) { 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)Math.Abs(value.Ticks), TimeSpan.TicksPerSecond); fraction = (uint)fraction64; } AfterComputeFraction: int fractionDigits = 0; if (symbol == 'c') { // Only write out the fraction if it's non-zero, and in that // case write out the entire fraction (all digits). if (fraction != 0) { fractionDigits = Utf8Constants.DateTimeNumFractionDigits; } } else if (symbol == 'G') { // Always write out the fraction, even if it's zero. fractionDigits = Utf8Constants.DateTimeNumFractionDigits; } else { // Only write out the fraction if it's non-zero, and in that // case write out only the most significant digits. if (fraction != 0) { fractionDigits = Utf8Constants.DateTimeNumFractionDigits - FormattingHelpers.CountDecimalTrailingZeros(fraction, out fraction); } } Debug.Assert(fraction < 10_000_000); // If we're going to write out a fraction, also need to write the leading decimal. if (fractionDigits != 0) { requiredOutputLength += fractionDigits + 1; } ulong totalMinutesRemaining = 0; ulong 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; ulong 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 divmod since the data has shrunk far enough. Debug.Assert(totalHoursRemaining <= uint.MaxValue); uint days = 0; uint 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 (hours < 10 && symbol == 'g') { // Only writing a one-digit hour, not a two-digit hour hourDigits--; requiredOutputLength--; } int dayDigits = 0; if (days == 0) { if (symbol == 'G') { requiredOutputLength += 2; // for the leading "0:" dayDigits = 1; } } else { dayDigits = FormattingHelpers.CountDigits(days); requiredOutputLength += dayDigits + 1; // for the leading "d:" (or "d.") } if (value.Ticks < 0) { requiredOutputLength++; // for the leading '-' sign } if (destination.Length < requiredOutputLength) { bytesWritten = 0; return(false); } bytesWritten = requiredOutputLength; int idx = 0; // Write leading '-' if necessary if (value.Ticks < 0) { destination[idx++] = Utf8Constants.Minus; } // Write day (and separator) if necessary if (dayDigits > 0) { FormattingHelpers.WriteDigits(days, destination.Slice(idx, dayDigits)); idx += dayDigits; destination[idx++] = (symbol == 'c') ? Utf8Constants.Period : Utf8Constants.Colon; } // Write "[h]h:mm:ss" FormattingHelpers.WriteDigits(hours, destination.Slice(idx, hourDigits)); idx += hourDigits; destination[idx++] = Utf8Constants.Colon; FormattingHelpers.WriteDigits((uint)minutes, destination.Slice(idx, 2)); idx += 2; destination[idx++] = Utf8Constants.Colon; FormattingHelpers.WriteDigits((uint)seconds, destination.Slice(idx, 2)); idx += 2; // Write fraction (and separator) if necessary if (fractionDigits > 0) { destination[idx++] = Utf8Constants.Period; FormattingHelpers.WriteDigits(fraction, destination.Slice(idx, fractionDigits)); idx += fractionDigits; } // And we're done! Debug.Assert(idx == requiredOutputLength); return(true); }
/// <summary> /// Formats a TimeSpan as a UTF8 string. /// </summary> /// <param name="value">Value to format</param> /// <param name="buffer">Buffer to write the UTF8-formatted value to</param> /// <param name="bytesWritten">Receives the length of the formatted text in bytes</param> /// <param name="format">The standard format to use</param> /// <returns> /// true for success. "bytesWritten" contains the length of the formatted text in bytes. /// false if buffer was too short. Iteratively increase the size of the buffer and retry until it succeeds. /// </returns> /// <remarks> /// Formats supported: /// c/t/T (default) [-][d.]hh:mm:ss[.fffffff] (constant format) /// G [-]d:hh:mm:ss.fffffff (general long) /// g [-][d:]h:mm:ss[.f[f[f[f[f[f[f[]]]]]]] (general short) /// </remarks> /// <exceptions> /// <cref>System.FormatException</cref> if the format is not valid for this data type. /// </exceptions> public static bool TryFormat(TimeSpan value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default) { char symbol = format.IsDefault ? 'c' : format.Symbol; switch (symbol) { case 'G': case 'g': case 'c': case 't': case 'T': { bool longForm = (symbol == 'G'); bool constant = (symbol == 't' || symbol == 'T' || symbol == 'c'); long ticks = value.Ticks; int days = (int)FormattingHelpers.DivMod(ticks, TimeSpan.TicksPerDay, out long timeLeft); bool showSign = false; if (ticks < 0) { showSign = true; days = -days; timeLeft = -timeLeft; } int hours = (int)FormattingHelpers.DivMod(timeLeft, TimeSpan.TicksPerHour, out timeLeft); int minutes = (int)FormattingHelpers.DivMod(timeLeft, TimeSpan.TicksPerMinute, out timeLeft); int seconds = (int)FormattingHelpers.DivMod(timeLeft, TimeSpan.TicksPerSecond, out long fraction); int dayDigits = 0; int hourDigits = (constant || longForm || hours > 9) ? 2 : 1; int fractionDigits = 0; bytesWritten = hourDigits + 6; // [h]h:mm:ss if (showSign) { bytesWritten += 1; // [-] } if (longForm || days > 0) { dayDigits = FormattingHelpers.CountDigits(days); bytesWritten += dayDigits + 1; // [d'.'] } if (longForm || fraction > 0) { fractionDigits = (longForm || constant) ? Utf8Constants.DateTimeNumFractionDigits : FormattingHelpers.CountFractionDigits(fraction); bytesWritten += fractionDigits + 1; // ['.'fffffff] or ['.'FFFFFFF] for short-form } if (buffer.Length < bytesWritten) { bytesWritten = 0; return(false); } ref byte utf8Bytes = ref buffer.DangerousGetPinnableReference(); int idx = 0; if (showSign) { Unsafe.Add(ref utf8Bytes, idx++) = Utf8Constants.Minus; } if (dayDigits > 0) { idx += FormattingHelpers.WriteDigits(days, dayDigits, ref utf8Bytes, idx); Unsafe.Add(ref utf8Bytes, idx++) = constant ? Utf8Constants.Period : Utf8Constants.Colon; } idx += FormattingHelpers.WriteDigits(hours, hourDigits, ref utf8Bytes, idx); Unsafe.Add(ref utf8Bytes, idx++) = Utf8Constants.Colon; idx += FormattingHelpers.WriteDigits(minutes, 2, ref utf8Bytes, idx); Unsafe.Add(ref utf8Bytes, idx++) = Utf8Constants.Colon; idx += FormattingHelpers.WriteDigits(seconds, 2, ref utf8Bytes, idx); if (fractionDigits > 0) { Unsafe.Add(ref utf8Bytes, idx++) = Utf8Constants.Period; idx += FormattingHelpers.WriteFractionDigits(fraction, fractionDigits, ref utf8Bytes, idx); } return(true); }