예제 #1
0
        private static bool TryFormatInt64(long value, ulong mask, Span <byte> buffer, out int bytesWritten, StandardFormat format, SymbolTable symbolTable)
        {
            if (format.IsDefault)
            {
                format = 'G';
            }

            if (value >= 0)
            {
                return(TryFormatUInt64(unchecked ((ulong)value), buffer, out bytesWritten, format, symbolTable));
            }
            else if (format.Symbol == 'x' || format.Symbol == 'X')
            {
                return(TryFormatUInt64(unchecked ((ulong)value) & mask, buffer, out bytesWritten, format, symbolTable));
            }
            else
            {
                if (!symbolTable.TryEncode(SymbolTable.Symbol.MinusSign, buffer, out int minusSignBytes))
                {
                    bytesWritten = 0;
                    return(false);
                }

                if (!TryFormatUInt64(unchecked ((ulong)-value), buffer.Slice(minusSignBytes), out int digitBytes, format, symbolTable))
                {
                    bytesWritten = 0;
                    return(false);
                }
                bytesWritten = digitBytes + minusSignBytes;
                return(true);
            }
        }
예제 #2
0
        private static bool TryFormatNumber(double value, bool isSingle, Span <byte> buffer, out int bytesWritten, StandardFormat format = default, SymbolTable symbolTable = null)
        {
            Precondition.Require(format.Symbol == 'G' || format.Symbol == 'E' || format.Symbol == 'F');

            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            bytesWritten = 0;
            int written;

            if (Double.IsNaN(value))
            {
                return(symbolTable.TryEncode(SymbolTable.Symbol.NaN, buffer, out bytesWritten));
            }

            if (Double.IsInfinity(value))
            {
                if (Double.IsNegativeInfinity(value))
                {
                    if (!symbolTable.TryEncode(SymbolTable.Symbol.MinusSign, buffer, out written))
                    {
                        bytesWritten = 0;
                        return(false);
                    }
                    bytesWritten += written;
                }
                if (!symbolTable.TryEncode(SymbolTable.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());
            var utf16Bytes = hack.AsSpan().AsBytes();

            if (symbolTable == SymbolTable.InvariantUtf8)
            {
                var status = Encodings.Utf16.ToUtf8(utf16Bytes, buffer, out int consumed, out bytesWritten);
                return(status == OperationStatus.Done);
            }
            else if (symbolTable == SymbolTable.InvariantUtf16)
            {
                bytesWritten = utf16Bytes.Length;
                if (utf16Bytes.TryCopyTo(buffer))
                {
                    return(true);
                }

                bytesWritten = 0;
                return(false);
            }
            else
            {
                // TODO: This is currently pretty expensive. Can this be done more efficiently?
                //       Note: removing the hack might solve this problem a very different way.
                var status = Encodings.Utf16.ToUtf8Length(utf16Bytes, out int needed);
                if (status != OperationStatus.Done)
                {
                    bytesWritten = 0;
                    return(false);
                }

                Span <byte> temp = stackalloc byte[needed];

                status = Encodings.Utf16.ToUtf8(utf16Bytes, temp, out int consumed, out written);
                if (status != OperationStatus.Done)
                {
                    bytesWritten = 0;
                    return(false);
                }

                return(symbolTable.TryEncode(temp, buffer, out consumed, out bytesWritten));
            }
        }
예제 #3
0
        // 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, out int bytesWritten, StandardFormat format, SymbolTable symbolTable)
        {
            char symbol = char.ToUpperInvariant(format.Symbol);

            Precondition.Require(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 (symbol == 'D' && format.HasPrecision)
            {
                var leadingZerosCount = format.Precision - digitsCount - trailingZerosCount;
                while (leadingZerosCount-- > 0)
                {
                    if (!symbolTable.TryEncode(SymbolTable.Symbol.D0, buffer.Slice(bytesWritten), out digitBytes))
                    {
                        bytesWritten = 0;
                        return(false);
                    }
                    bytesWritten += digitBytes;
                }
            }

            // Append first digit
            if (!symbolTable.TryEncode((SymbolTable.Symbol)value, buffer.Slice(bytesWritten), out digitBytes))
            {
                bytesWritten = 0;
                return(false);
            }
            bytesWritten += digitBytes;
            digitsCount--;

            if (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 (!symbolTable.TryEncode(SymbolTable.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes))
                        {
                            bytesWritten = 0;
                            return(false);
                        }
                        bytesWritten += digitBytes;
                    }
                    digitsLeftInGroup = GroupSize;
                }

                // Append digits
                while (reversedValueExceptFirst > 0)
                {
                    if (digitsLeftInGroup == 0)
                    {
                        if (!symbolTable.TryEncode(SymbolTable.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes))
                        {
                            bytesWritten = 0;
                            return(false);
                        }
                        bytesWritten     += digitBytes;
                        digitsLeftInGroup = GroupSize;
                    }

                    var nextDigit = reversedValueExceptFirst % 10UL;
                    reversedValueExceptFirst = reversedValueExceptFirst / 10UL;

                    if (!symbolTable.TryEncode((SymbolTable.Symbol)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 (!symbolTable.TryEncode(SymbolTable.Symbol.GroupSeparator, buffer.Slice(bytesWritten), out digitBytes))
                        {
                            bytesWritten = 0;
                            return(false);
                        }
                        bytesWritten     += digitBytes;
                        digitsLeftInGroup = GroupSize;
                    }

                    if (!symbolTable.TryEncode(SymbolTable.Symbol.D0, 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 (!symbolTable.TryEncode((SymbolTable.Symbol)nextDigit, bufferSlice, out digitBytes))
                    {
                        bytesWritten = 0;
                        return(false);
                    }
                    bytesWritten += digitBytes;
                }

                // Append trailing zeros if any
                while (trailingZerosCount-- > 0)
                {
                    if (!symbolTable.TryEncode(SymbolTable.Symbol.D0, 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 (symbol == 'N')
            {
                int trailingZerosAfterDecimalCount = format.HasPrecision ? format.Precision : 2;

                if (trailingZerosAfterDecimalCount > 0)
                {
                    if (!symbolTable.TryEncode(SymbolTable.Symbol.DecimalSeparator, buffer.Slice(bytesWritten), out digitBytes))
                    {
                        bytesWritten = 0;
                        return(false);
                    }
                    bytesWritten += digitBytes;

                    while (trailingZerosAfterDecimalCount-- > 0)
                    {
                        if (!symbolTable.TryEncode(SymbolTable.Symbol.D0, buffer.Slice(bytesWritten), out digitBytes))
                        {
                            bytesWritten = 0;
                            return(false);
                        }
                        bytesWritten += digitBytes;
                    }
                }
            }

            return(true);
        }