internal static bool TryFormatInt64(long value, byte numberOfBytes, Span<byte> buffer, Format.Parsed format, FormattingData formattingData, out int bytesWritten) { Precondition.Require(numberOfBytes <= sizeof(long)); if (value >= 0) { return TryFormatUInt64(unchecked((ulong)value), numberOfBytes, buffer, format, formattingData, out bytesWritten); } else if (format.IsHexadecimal) { ulong bitMask = GetBitMask(numberOfBytes); return TryFormatUInt64(unchecked((ulong)value) & bitMask, numberOfBytes, buffer, format, formattingData, out bytesWritten); } else { int minusSignBytes = 0; if(!formattingData.TryWriteSymbol(FormattingData.Symbol.MinusSign, buffer, out minusSignBytes)) { bytesWritten = 0; return false; } int digitBytes = 0; if(!TryFormatUInt64(unchecked((ulong)-value), numberOfBytes, buffer.Slice(minusSignBytes), format, formattingData, out digitBytes)) { bytesWritten = 0; return false; } bytesWritten = digitBytes + minusSignBytes; return true; } }
internal static bool TryFormatInt64(long value, byte numberOfBytes, Span <byte> buffer, Format.Parsed format, FormattingData formattingData, out int bytesWritten) { Precondition.Require(numberOfBytes <= sizeof(long)); if (value >= 0) { return(TryFormatUInt64(unchecked ((ulong)value), numberOfBytes, buffer, format, formattingData, out bytesWritten)); } else if (format.IsHexadecimal) { ulong bitMask = GetBitMask(numberOfBytes); return(TryFormatUInt64(unchecked ((ulong)value) & bitMask, numberOfBytes, buffer, format, formattingData, out bytesWritten)); } else { int minusSignBytes = 0; if (!formattingData.TryWriteSymbol(FormattingData.Symbol.MinusSign, buffer, out minusSignBytes)) { bytesWritten = 0; return(false); } int digitBytes = 0; if (!TryFormatUInt64(unchecked ((ulong)-value), numberOfBytes, buffer.Slice(minusSignBytes), format, formattingData, out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten = digitBytes + minusSignBytes; return(true); } }
public static bool TryFormatNumber(double value, bool isSingle, Span <byte> buffer, Format.Parsed format, FormattingData formattingData, out int bytesWritten) { Precondition.Require(format.Symbol == 'G' || format.Symbol == 'E' || format.Symbol == 'F'); bytesWritten = 0; int written; if (Double.IsNaN(value)) { return(formattingData.TryWriteSymbol(FormattingData.Symbol.NaN, buffer, out bytesWritten)); } if (Double.IsInfinity(value)) { if (Double.IsNegativeInfinity(value)) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.MinusSign, buffer, out written)) { bytesWritten = 0; return(false); } bytesWritten += written; } if (!formattingData.TryWriteSymbol(FormattingData.Symbol.InfinitySign, buffer.Slice(bytesWritten), out written)) { bytesWritten = 0; return(false); } bytesWritten += written; return(true); } // TODO: the lines below need to be replaced with properly implemented algorithm // the problem is the algorithm is complex, so I am commiting a stub for now var hack = value.ToString(format.Symbol.ToString()); return(hack.TryFormat(buffer, default(Format.Parsed), formattingData, out bytesWritten)); }
public static bool TryFormatNumber(double value, bool isSingle, Span<byte> buffer, Format.Parsed format, FormattingData formattingData, out int bytesWritten) { Precondition.Require(format.Symbol == 'G' || format.Symbol == 'E' || format.Symbol == 'F'); bytesWritten = 0; int written; if (Double.IsNaN(value)) { return formattingData.TryWriteSymbol(FormattingData.Symbol.NaN, buffer, out bytesWritten); } if (Double.IsInfinity(value)) { if (Double.IsNegativeInfinity(value)) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.MinusSign, buffer, out written)) { bytesWritten = 0; return false; } bytesWritten += written; } if (!formattingData.TryWriteSymbol(FormattingData.Symbol.InfinitySign, buffer.Slice(bytesWritten), out written)) { bytesWritten = 0; return false; } bytesWritten += written; return true; } // TODO: the lines below need to be replaced with properly implemented algorithm // the problem is the algorithm is complex, so I am commiting a stub for now var hack = value.ToString(format.Symbol.ToString()); return hack.TryFormat(buffer, default(Format.Parsed), formattingData, out bytesWritten); }
// TODO: this whole routine is too slow. It does div and mod twice, which are both costly (especially that some JITs cannot optimize it). // It does it twice to avoid reversing the formatted buffer, which can be tricky given it should handle arbitrary cultures. // One optimization I thought we could do is to do div/mod once and store digits in a temp buffer (but that would allocate). Modification to the idea would be to store the digits in a local struct // Another idea possibly worth tying would be to special case cultures that have constant digit size, and go back to the format + reverse buffer approach. private static bool TryFormatDecimal(ulong value, Span<byte> buffer, Format.Parsed format, FormattingData formattingData, out int bytesWritten) { if(format.IsDefault) { format.Symbol = 'G'; } format.Symbol = Char.ToUpperInvariant(format.Symbol); // TODO: this is costly. I think the transformation should happen in Parse Precondition.Require(format.Symbol == 'D' || format.Symbol == 'G' || format.Symbol == 'N'); // Reverse value on decimal basis, count digits and trailing zeros before the decimal separator ulong reversedValueExceptFirst = 0; var digitsCount = 1; var trailingZerosCount = 0; // We reverse the digits in numeric form because reversing encoded digits is hard and/or costly. // If value contains 20 digits, its reversed value will not fit into ulong size. // So reverse it till last digit (reversedValueExceptFirst will have all the digits except the first one). while (value >= 10) { var digit = value % 10UL; value = value / 10UL; if (reversedValueExceptFirst == 0 && digit == 0) { trailingZerosCount++; } else { reversedValueExceptFirst = reversedValueExceptFirst * 10UL + digit; digitsCount++; } } bytesWritten = 0; int digitBytes; // If format is D and precision is greater than digitsCount + trailingZerosCount, append leading zeros if (format.Symbol == 'D' && format.HasPrecision) { var leadingZerosCount = format.Precision - digitsCount - trailingZerosCount; while (leadingZerosCount-- > 0) { if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; } } // Append first digit if (!formattingData.TryWriteDigit(value, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; digitsCount--; if (format.Symbol == 'N') { const int GroupSize = 3; // Count amount of digits before first group separator. It will be reset to groupSize every time digitsLeftInGroup == zero var digitsLeftInGroup = (digitsCount + trailingZerosCount) % GroupSize; if (digitsLeftInGroup == 0) { if (digitsCount + trailingZerosCount > 0) { // There is a new group immediately after the first digit if (!formattingData.TryWriteSymbol(FormattingData.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; } digitsLeftInGroup = GroupSize; } // Append digits while (reversedValueExceptFirst > 0) { if (digitsLeftInGroup == 0) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; digitsLeftInGroup = GroupSize; } var nextDigit = reversedValueExceptFirst % 10UL; reversedValueExceptFirst = reversedValueExceptFirst / 10UL; if (!formattingData.TryWriteDigit(nextDigit, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; digitsLeftInGroup--; } // Append trailing zeros if any while (trailingZerosCount-- > 0) { if (digitsLeftInGroup == 0) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; digitsLeftInGroup = GroupSize; } if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; digitsLeftInGroup--; } } else { while (reversedValueExceptFirst > 0) { var bufferSlice = buffer.Slice(bytesWritten); var nextDigit = reversedValueExceptFirst % 10UL; reversedValueExceptFirst = reversedValueExceptFirst / 10UL; if (!formattingData.TryWriteDigit(nextDigit, bufferSlice, out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; } // Append trailing zeros if any while (trailingZerosCount-- > 0) { if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; } } // If format is N and precision is not defined or is greater than zero, append trailing zeros after decimal point if (format.Symbol == 'N') { int trailingZerosAfterDecimalCount = format.HasPrecision ? format.Precision : 2; if (trailingZerosAfterDecimalCount > 0) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.DecimalSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; while (trailingZerosAfterDecimalCount-- > 0) { if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return false; } bytesWritten += digitBytes; } } } return true; }
// TODO: this whole routine is too slow. It does div and mod twice, which are both costly (especially that some JITs cannot optimize it). // It does it twice to avoid reversing the formatted buffer, which can be tricky given it should handle arbitrary cultures. // One optimization I thought we could do is to do div/mod once and store digits in a temp buffer (but that would allocate). Modification to the idea would be to store the digits in a local struct // Another idea possibly worth tying would be to special case cultures that have constant digit size, and go back to the format + reverse buffer approach. private static bool TryFormatDecimal(ulong value, Span <byte> buffer, Format.Parsed format, FormattingData formattingData, out int bytesWritten) { if (format.IsDefault) { format.Symbol = 'G'; } format.Symbol = Char.ToUpperInvariant(format.Symbol); // TODO: this is costly. I think the transformation should happen in Parse Precondition.Require(format.Symbol == 'D' || format.Symbol == 'G' || format.Symbol == 'N'); // Reverse value on decimal basis, count digits and trailing zeros before the decimal separator ulong reversedValueExceptFirst = 0; var digitsCount = 1; var trailingZerosCount = 0; // We reverse the digits in numeric form because reversing encoded digits is hard and/or costly. // If value contains 20 digits, its reversed value will not fit into ulong size. // So reverse it till last digit (reversedValueExceptFirst will have all the digits except the first one). while (value >= 10) { var digit = value % 10UL; value = value / 10UL; if (reversedValueExceptFirst == 0 && digit == 0) { trailingZerosCount++; } else { reversedValueExceptFirst = reversedValueExceptFirst * 10UL + digit; digitsCount++; } } bytesWritten = 0; int digitBytes; // If format is D and precision is greater than digitsCount + trailingZerosCount, append leading zeros if (format.Symbol == 'D' && format.HasPrecision) { var leadingZerosCount = format.Precision - digitsCount - trailingZerosCount; while (leadingZerosCount-- > 0) { if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; } } // Append first digit if (!formattingData.TryWriteDigit(value, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; digitsCount--; if (format.Symbol == 'N') { const int GroupSize = 3; // Count amount of digits before first group separator. It will be reset to groupSize every time digitsLeftInGroup == zero var digitsLeftInGroup = (digitsCount + trailingZerosCount) % GroupSize; if (digitsLeftInGroup == 0) { if (digitsCount + trailingZerosCount > 0) { // There is a new group immediately after the first digit if (!formattingData.TryWriteSymbol(FormattingData.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; } digitsLeftInGroup = GroupSize; } // Append digits while (reversedValueExceptFirst > 0) { if (digitsLeftInGroup == 0) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; digitsLeftInGroup = GroupSize; } var nextDigit = reversedValueExceptFirst % 10UL; reversedValueExceptFirst = reversedValueExceptFirst / 10UL; if (!formattingData.TryWriteDigit(nextDigit, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; digitsLeftInGroup--; } // Append trailing zeros if any while (trailingZerosCount-- > 0) { if (digitsLeftInGroup == 0) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; digitsLeftInGroup = GroupSize; } if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; digitsLeftInGroup--; } } else { while (reversedValueExceptFirst > 0) { var bufferSlice = buffer.Slice(bytesWritten); var nextDigit = reversedValueExceptFirst % 10UL; reversedValueExceptFirst = reversedValueExceptFirst / 10UL; if (!formattingData.TryWriteDigit(nextDigit, bufferSlice, out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; } // Append trailing zeros if any while (trailingZerosCount-- > 0) { if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; } } // If format is N and precision is not defined or is greater than zero, append trailing zeros after decimal point if (format.Symbol == 'N') { int trailingZerosAfterDecimalCount = format.HasPrecision ? format.Precision : 2; if (trailingZerosAfterDecimalCount > 0) { if (!formattingData.TryWriteSymbol(FormattingData.Symbol.DecimalSeparator, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; while (trailingZerosAfterDecimalCount-- > 0) { if (!formattingData.TryWriteDigitOrSymbol(0, buffer.Slice(bytesWritten), out digitBytes)) { bytesWritten = 0; return(false); } bytesWritten += digitBytes; } } } return(true); }