Пример #1
0
        public static bool TryFormat(bool value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default)
        {
            if (value)
            {
                if (buffer.Length < 8) // 4 chars for "true" = 8 bytes
                {
                    bytesWritten = 0;
                    return(false);
                }

                if (format.IsDefault || format.Symbol == 'G')
                {
                    s_True.CopyTo(buffer);
                    bytesWritten = s_True.Length;
                    return(true);
                }
                else if (format.Symbol == 'l')
                {
                    s_true.CopyTo(buffer);
                    bytesWritten = s_true.Length;
                    return(true);
                }
                else
                {
                    throw new FormatException();
                }
            }
            else
            {
                if (buffer.Length < 10) // 5 chars for "false" = 10 bytes
                {
                    bytesWritten = 0;
                    return(false);
                }

                if (format.IsDefault || format.Symbol == 'G')
                {
                    s_False.CopyTo(buffer);
                    bytesWritten = s_False.Length;
                    return(true);
                }
                else if (format.Symbol == 'l')
                {
                    s_false.CopyTo(buffer);
                    bytesWritten = s_false.Length;
                    return(true);
                }
                else
                {
                    throw new FormatException();
                }
            }
        }
Пример #2
0
 public static void Append <TFormatter, T>(this TFormatter formatter, T value, StandardFormat format = default) where T : IBufferFormattable where TFormatter : ITextOutput
 {
     while (!formatter.TryAppend(value, format))
     {
         formatter.Enlarge();
     }
 }
Пример #3
0
 public static void Append <TFormatter>(this TFormatter formatter, DateTime value, StandardFormat format = default) where TFormatter : ITextOutput
 {
     while (!formatter.TryAppend(value, format))
     {
         formatter.Enlarge();
     }
 }
Пример #4
0
 public static void StandardFormatParseNegative(string badFormatString)
 {
     Assert.Throws <FormatException>(() => StandardFormat.Parse(badFormatString));
 }
Пример #5
0
 public static bool TryFormat <T>(T value, Span <byte> destination, out int bytesWritten, StandardFormat format = default)
 {
     return(FormatFunctionality <T> .Invoke(value, destination, out bytesWritten, format));
 }
Пример #6
0
 public static void Append <TFormatter>(this TFormatter formatter, double value, SymbolTable symbolTable, StandardFormat format = default) where TFormatter : IOutput
 {
     while (!formatter.TryAppend(value, symbolTable, format))
     {
         formatter.Enlarge();
     }
 }
Пример #7
0
        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;
                    }
                }

                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).
            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 = 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 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)
            {
                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;
            }

            Debug.Assert(idx == requiredOutputLength);
            charsWritten = requiredOutputLength;
            return(true);
        }
Пример #8
0
 public static bool TryFormat(sbyte value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default, SymbolTable symbolTable = null)
 {
     if (symbolTable == null || symbolTable == SymbolTable.InvariantUtf8)
     {
         return(Utf8Formatter.TryFormat(value, buffer, out bytesWritten, format));
     }
     else if (symbolTable == SymbolTable.InvariantUtf16)
     {
         return(Utf16Formatter.TryFormat(value, buffer, out bytesWritten, format));
     }
     else
     {
         return(TryFormatInt64(value, 0xff, buffer, out bytesWritten, format, symbolTable));
     }
 }
Пример #9
0
 public static void Append <TFormatter>(this TFormatter formatter, float value, SymbolTable symbolTable, StandardFormat format = default) where TFormatter : IOutput
 {
     while (!formatter.TryAppend(value, symbolTable, format))
     {
         formatter.GetMoreMemory();
     }
 }
Пример #10
0
        public static bool TryFormat(float value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default, SymbolTable symbolTable = null)
        {
            if (format.IsDefault)
            {
                format = 'G';
            }

            symbolTable = symbolTable ?? SymbolTable.InvariantUtf8;

            switch (format.Symbol)
            {
            case 'G':
                return(CustomFormatter.TryFormatNumber(value, true, buffer, out bytesWritten, format, symbolTable));

            default:
                throw new NotSupportedException();
            }
        }
Пример #11
0
 public static bool TryFormat(DateTimeOffset value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default, SymbolTable symbolTable = null)
 {
     if (symbolTable == null || symbolTable == SymbolTable.InvariantUtf8)
     {
         return(Utf8Formatter.TryFormat(value, buffer, out bytesWritten, format));
     }
     else if (symbolTable == SymbolTable.InvariantUtf16)
     {
         return(Utf16Formatter.TryFormat(value, buffer, out bytesWritten, format));
     }
     else
     {
         throw new NotSupportedException();
     }
 }
Пример #12
0
        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':
                return(TryFormatTimeSpan(value, symbol, buffer, out bytesWritten));

            default:
                throw new NotSupportedException();
            }
        }
Пример #13
0
        public static bool TryFormat(DateTime value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default)
        {
            char symbol = format.IsDefault ? 'G' : format.Symbol;

            switch (symbol)
            {
            case 'R':
                return(TryFormatRfc1123(value, buffer, out bytesWritten));

            case 'l':
                return(TryFormatRfc1123Lowercase(value, buffer, out bytesWritten));

            case 'O':
                return(TryFormatO(value, NullOffset, buffer, out bytesWritten));

            case 'G':
                return(TryFormatG(value, NullOffset, buffer, out bytesWritten));

            default:
                throw new NotSupportedException();
            }
        }
Пример #14
0
        public static bool TryFormat(DateTimeOffset value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default)
        {
            TimeSpan offset = NullOffset;
            char     symbol = format.Symbol;

            if (format.IsDefault)
            {
                symbol = 'G';
                offset = value.Offset;
            }

            switch (symbol)
            {
            case 'R':
                return(TryFormatRfc1123(value.UtcDateTime, buffer, out bytesWritten));

            case 'l':
                return(TryFormatRfc1123Lowercase(value.UtcDateTime, buffer, out bytesWritten));

            case 'O':
                return(TryFormatO(value.DateTime, value.Offset, buffer, out bytesWritten));

            case 'G':
                return(TryFormatG(value.DateTime, offset, buffer, out bytesWritten));

            default:
                throw new NotSupportedException();
            }
        }
Пример #15
0
        /// <summary>
        /// Formats a DateTimeOffset 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>
        /// <exceptions>
        /// <remarks>
        /// Formats supported:
        ///     default       05/25/2017 10:30:15 -08:00
        ///     G             05/25/2017 10:30:15
        ///     R             Tue, 03 Jan 2017 08:08:05 GMT       (RFC 1123)
        ///     l             tue, 03 jan 2017 08:08:05 gmt       (Lowercase RFC 1123)
        ///     O             2017-06-12T05:30:45.7680000-07:00   (Round-trippable)
        /// </remarks>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryFormat(DateTimeOffset value, Span <byte> destination, out int bytesWritten, StandardFormat format = default)
        {
            TimeSpan offset = Utf8Constants.NullUtcOffset;
            char     symbol = format.Symbol;

            if (format.IsDefault)
            {
                symbol = 'G';
                offset = value.Offset;
            }

            switch (symbol)
            {
            case 'R':
                return(TryFormatDateTimeR(value.UtcDateTime, destination, out bytesWritten));

            case 'l':
                return(TryFormatDateTimeL(value.UtcDateTime, destination, out bytesWritten));

            case 'O':
                return(TryFormatDateTimeO(value.DateTime, value.Offset, destination, out bytesWritten));

            case 'G':
                return(TryFormatDateTimeG(value.DateTime, offset, destination, out bytesWritten));

            default:
                return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten));
            }
        }
        /// <summary>
        /// Formats a Guid 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:
        ///     D (default)     nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn
        ///     B               {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}
        ///     P               (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn)
        ///     N               nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
        /// </remarks>
        /// <exceptions>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryFormat(Guid value, Span <byte> destination, out int bytesWritten, StandardFormat format = default)
        {
            const int INSERT_DASHES       = unchecked ((int)0x80000000);
            const int NO_DASHES           = 0;
            const int INSERT_CURLY_BRACES = (CloseBrace << 16) | (OpenBrace << 8);
            const int INSERT_ROUND_BRACES = (CloseParen << 16) | (OpenParen << 8);
            const int NO_BRACES           = 0;
            const int LEN_GUID_BASE       = 32;
            const int LEN_ADD_DASHES      = 4;
            const int LEN_ADD_BRACES      = 2;

            // This is a 32-bit value whose contents (where 0 is the low byte) are:
            // 0th byte: minimum required length of the output buffer,
            // 1st byte: the ASCII byte to insert for the opening brace position (or 0 if no braces),
            // 2nd byte: the ASCII byte to insert for the closing brace position (or 0 if no braces),
            // 3rd byte: high bit set if dashes are to be inserted.
            //
            // The reason for keeping a single flag instead of separate vars is that we can avoid register spillage
            // as we build up the output value.
            int flags;

            switch (FormattingHelpers.GetSymbolOrDefault(format, 'D'))
            {
            case 'D':     // nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn
                flags = INSERT_DASHES + NO_BRACES + LEN_GUID_BASE + LEN_ADD_DASHES;
                break;

            case 'B':     // {nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn}
                flags = INSERT_DASHES + INSERT_CURLY_BRACES + LEN_GUID_BASE + LEN_ADD_DASHES + LEN_ADD_BRACES;
                break;

            case 'P':     // (nnnnnnnn-nnnn-nnnn-nnnn-nnnnnnnnnnnn)
                flags = INSERT_DASHES + INSERT_ROUND_BRACES + LEN_GUID_BASE + LEN_ADD_DASHES + LEN_ADD_BRACES;
                break;

            case 'N':     // nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
                flags = NO_BRACES + NO_DASHES + LEN_GUID_BASE;
                break;

            default:
                return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten));
            }

            // At this point, the low byte of flags contains the minimum required length

            if ((byte)flags > destination.Length)
            {
                bytesWritten = 0;
                return(false);
            }

            bytesWritten = (byte)flags;
            flags      >>= 8;

            // At this point, the low byte of flags contains the opening brace char (if any)

            if ((byte)flags != 0)
            {
                destination[0] = (byte)flags;
                destination    = destination.Slice(1);
            }
            flags >>= 8;

            // At this point, the low byte of flags contains the closing brace char (if any)
            // And since we're performing arithmetic shifting the high bit of flags is set (flags is negative) if dashes are required

            DecomposedGuid guidAsBytes = default;

            guidAsBytes.Guid = value;

            // When a GUID is blitted, the first three components are little-endian, and the last component is big-endian.

            // The line below forces the JIT to hoist the bounds check for the following segment.
            // The JIT will optimize away the read, but it cannot optimize away the bounds check
            // because it may have an observable side effect (throwing).
            // We use 8 instead of 7 so that we also capture the dash if we're asked to insert one.

            { _ = destination[8]; }
            HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower);

            if (flags < 0 /* use dash? */)
            {
                destination[8] = Dash;
                destination    = destination.Slice(9);
            }
            else
            {
                destination = destination.Slice(8);
            }

            { _ = destination[4]; }
            HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower);

            if (flags < 0 /* use dash? */)
            {
                destination[4] = Dash;
                destination    = destination.Slice(5);
            }
            else
            {
                destination = destination.Slice(4);
            }

            { _ = destination[4]; }
            HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower);

            if (flags < 0 /* use dash? */)
            {
                destination[4] = Dash;
                destination    = destination.Slice(5);
            }
            else
            {
                destination = destination.Slice(4);
            }

            { _ = destination[4]; }
            HexConverter.ToBytesBuffer(guidAsBytes.Byte08, destination, 0, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte09, destination, 2, HexConverter.Casing.Lower);

            if (flags < 0 /* use dash? */)
            {
                destination[4] = Dash;
                destination    = destination.Slice(5);
            }
            else
            {
                destination = destination.Slice(4);
            }

            { _ = destination[11]; } // can't hoist bounds check on the final brace (if exists)
            HexConverter.ToBytesBuffer(guidAsBytes.Byte10, destination, 0, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte11, destination, 2, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte12, destination, 4, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte13, destination, 6, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte14, destination, 8, HexConverter.Casing.Lower);
            HexConverter.ToBytesBuffer(guidAsBytes.Byte15, destination, 10, HexConverter.Casing.Lower);

            if ((byte)flags != 0)
            {
                destination[12] = (byte)flags;
            }

            return(true);
        }
Пример #17
0
 public static void AppendHttpHeader <TFormatter, T>(this TFormatter formatter, string name, T value, StandardFormat valueFormat = default) where TFormatter : ITextOutput where T : IBufferFormattable
 {
     formatter.Append(name);
     formatter.Append(value, formatter.SymbolTable, valueFormat);
     formatter.AppendHttpNewLine();
 }
Пример #18
0
        public static unsafe bool TryFormat(decimal value, Span <byte> destination, out int bytesWritten, StandardFormat format = default)
        {
            if (format.IsDefault)
            {
                format = 'G';
            }

            switch (format.Symbol)
            {
            case 'g':
            case 'G':
            {
                if (format.Precision != StandardFormat.NoPrecision)
                {
                    throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported);
                }

                byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
                Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);

                Number.DecimalToNumber(ref value, ref number);
                if (number.Digits[0] == 0)
                {
                    number.IsNegative = false;         // For Decimals, -0 must print as normal 0.
                }
                bool success = TryFormatDecimalG(ref number, destination, out bytesWritten);
#if DEBUG
                // This DEBUG segment exists to close a code coverage hole inside TryFormatDecimalG(). Because we don't call RoundNumber() on this path, we have no way to feed
                // TryFormatDecimalG() a number where trailing zeros before the decimal point have been cropped. So if the chance comes up, we'll crop the zeroes
                // ourselves and make a second call to ensure we get the same outcome.
                if (success)
                {
                    Span <byte> digits    = number.Digits;
                    int         numDigits = number.DigitsCount;
                    if (numDigits != 0 && number.Scale == numDigits && digits[numDigits - 1] == '0')
                    {
                        while (numDigits != 0 && digits[numDigits - 1] == '0')
                        {
                            digits[numDigits - 1] = 0;
                            numDigits--;
                        }

                        number.DigitsCount = numDigits;
                        number.CheckConsistency();

                        byte[] buffer2  = new byte[destination.Length];
                        bool   success2 = TryFormatDecimalG(ref number, buffer2, out int bytesWritten2);
                        Debug.Assert(success2);
                        Debug.Assert(bytesWritten2 == bytesWritten);
                        for (int i = 0; i < bytesWritten; i++)
                        {
                            Debug.Assert(destination[i] == buffer2[i]);
                        }
                    }
                }
#endif // DEBUG
                return(success);
            }

            case 'f':
            case 'F':
            {
                byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
                Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);

                Number.DecimalToNumber(ref value, ref number);
                byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)2 : format.Precision;
                Number.RoundNumber(ref number, number.Scale + precision);
                Debug.Assert((number.Digits[0] != 0) || !number.IsNegative);           // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant.
                return(TryFormatDecimalF(ref number, destination, out bytesWritten, precision));
            }

            case 'e':
            case 'E':
            {
                byte *pDigits = stackalloc byte[Number.DecimalNumberBufferLength];
                Number.NumberBuffer number = new Number.NumberBuffer(Number.NumberBufferKind.Decimal, pDigits, Number.DecimalNumberBufferLength);

                Number.DecimalToNumber(ref value, ref number);
                byte precision = (format.Precision == StandardFormat.NoPrecision) ? (byte)6 : format.Precision;
                Number.RoundNumber(ref number, precision + 1);
                Debug.Assert((number.Digits[0] != 0) || !number.IsNegative);           // For Decimals, -0 must print as normal 0. As it happens, Number.RoundNumber already ensures this invariant.
                return(TryFormatDecimalE(ref number, destination, out bytesWritten, precision, exponentSymbol: (byte)format.Symbol));
            }

            default:
                return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten));
            }
        }
Пример #19
0
 public static bool TryAppend <TFormatter>(this TFormatter formatter, double value, SymbolTable symbolTable, StandardFormat format = default) where TFormatter : IOutput
 {
     if (!CustomFormatter.TryFormat(value, formatter.Buffer, out int bytesWritten, format, symbolTable))
     {
         return(false);
     }
     formatter.Advance(bytesWritten);
     return(true);
 }
Пример #20
0
 /// <summary>
 /// Formats a Single 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:
 ///     G/g  (default)
 ///     F/f             12.45       Fixed point
 ///     E/e             1.245000e1  Exponential
 /// </remarks>
 /// <exceptions>
 /// <cref>System.FormatException</cref> if the format is not valid for this data type.
 /// </exceptions>
 public static bool TryFormat(float value, Span <byte> destination, out int bytesWritten, StandardFormat format = default)
 {
     return(TryFormatFloatingPoint <float>(value, destination, out bytesWritten, format));
 }
Пример #21
0
        public static void StandardFormatGetHashCodeIsContentBased(StandardFormat f1, StandardFormat f2, bool expectedToBeEqual)
        {
            object boxedf1           = f1;
            object aDifferentBoxedF1 = f1;
            int    h1 = boxedf1.GetHashCode();
            int    h2 = aDifferentBoxedF1.GetHashCode();

            Assert.Equal(h1, h2);
        }
Пример #22
0
        private static bool TryFormatFloatingPoint <T>(T value, Span <byte> destination, out int bytesWritten, StandardFormat format) where T : IFormattable, ISpanFormattable
        {
            Span <char> formatText = stackalloc char[0];

            if (!format.IsDefault)
            {
                formatText = stackalloc char[StandardFormat.FormatStringLength];
                int formatTextLength = format.Format(formatText);
                formatText = formatText.Slice(0, formatTextLength);
            }

            // We first try to format into a stack-allocated buffer, and if it succeeds, we can avoid
            // all allocation.  If that fails, we fall back to allocating strings.  If it proves impactful,
            // that allocation (as well as roundtripping from byte to char and back to byte) could be avoided by
            // calling into a refactored Number.FormatSingle/Double directly.

            const int           StackBufferLength = 128; // large enough to handle the majority cases
            Span <char>         stackBuffer       = stackalloc char[StackBufferLength];
            ReadOnlySpan <char> utf16Text         = stackalloc char[0];

            // Try to format into the stack buffer.  If we're successful, we can avoid all allocations.
            if (value.TryFormat(stackBuffer, out int formattedLength, formatText, CultureInfo.InvariantCulture))
            {
                utf16Text = stackBuffer.Slice(0, formattedLength);
            }
Пример #23
0
 public static bool Invoke(T value, Span <byte> destination, out int bytesWritten, StandardFormat format)
 {
     return(Instance._formatter(value, destination, out bytesWritten, format));
 }
Пример #24
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 = MemoryMarshal.AsBytes(hack.AsSpan());

            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));
            }
        }
Пример #25
0
 public static bool TryFormat(ulong value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default) => throw null;
Пример #26
0
        private static bool TryFormatInt64(long value, ulong mask, Span <byte> destination, out int bytesWritten, StandardFormat format)
        {
            if (format.IsDefault)
            {
                return(TryFormatInt64Default(value, destination, out bytesWritten));
            }

            switch (format.Symbol)
            {
            case 'G':
            case 'g':
            case 'R':
            case 'r':     // treat 'r' (which historically was only for floating-point) as being equivalent to 'g', rather than throwing
                if (format.HasPrecision)
                {
                    throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported);     // With a precision, 'G' can produce exponential format, even for integers.
                }
                return(TryFormatInt64D(value, format.Precision, destination, out bytesWritten));

            case 'd':
            case 'D':
                return(TryFormatInt64D(value, format.Precision, destination, out bytesWritten));

            case 'n':
            case 'N':
                return(TryFormatInt64N(value, format.Precision, destination, out bytesWritten));

            case 'x':
                return(TryFormatUInt64X((ulong)value & mask, format.Precision, true, destination, out bytesWritten));

            case 'X':
                return(TryFormatUInt64X((ulong)value & mask, format.Precision, false, destination, out bytesWritten));

            default:
                return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten));
            }
        }
Пример #27
0
 public static bool TryAppend <TFormatter, T>(this TFormatter formatter, T value, StandardFormat format = default) where T : IBufferFormattable where TFormatter : ITextOutput
 {
     if (!value.TryFormat(formatter.GetSpan(), out int bytesWritten, format, formatter.SymbolTable))
     {
         return(false);
     }
     formatter.Advance(bytesWritten);
     return(true);
 }
Пример #28
0
        /// <summary>
        /// Formats a DateTime 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:
        ///     G  (default)  05/25/2017 10:30:15
        ///     R             Tue, 03 Jan 2017 08:08:05 GMT       (RFC 1123)
        ///     l             tue, 03 jan 2017 08:08:05 gmt       (Lowercase RFC 1123)
        ///     O             2017-06-12T05:30:45.7680000-07:00   (Round-trippable)
        /// </remarks>
        /// <exceptions>
        /// <cref>System.FormatException</cref> if the format is not valid for this data type.
        /// </exceptions>
        public static bool TryFormat(DateTime value, Span <byte> destination, out int bytesWritten, StandardFormat format = default)
        {
            char symbol = FormattingHelpers.GetSymbolOrDefault(format, 'G');

            switch (symbol)
            {
            case 'R':
                return(TryFormatDateTimeR(value, destination, out bytesWritten));

            case 'l':
                return(TryFormatDateTimeL(value, destination, out bytesWritten));

            case 'O':
                return(TryFormatDateTimeO(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten));

            case 'G':
                return(TryFormatDateTimeG(value, Utf8Constants.NullUtcOffset, destination, out bytesWritten));

            default:
                return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten));
            }
        }
Пример #29
0
 public static bool TryAppend <TFormatter>(this TFormatter formatter, DateTime value, StandardFormat format = default) where TFormatter : ITextOutput
 {
     if (!CustomFormatter.TryFormat(value, formatter.GetSpan(), out int bytesWritten, format, formatter.SymbolTable))
     {
         return(false);
     }
     formatter.Advance(bytesWritten);
     return(true);
 }
Пример #30
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="value"></param>
        /// <param name="buffer"></param>
        /// <param name="bytesWritten"></param>
        /// <param name="format">only 'G' format is supported</param>
        /// <returns></returns>
        public static bool TryFormat(decimal value, Span <byte> buffer, out int bytesWritten, StandardFormat format = default)
        {
            if (format.IsDefault)
            {
                format = 'G';
            }
            else if (format.Symbol != 'G')
            {
                throw new FormatException();
            }

            var text = value.ToString("G");

            if (MemoryMarshal.AsBytes(text.AsSpan()).TryCopyTo(buffer))
            {
                bytesWritten = text.Length * 2;
                return(true);
            }
            bytesWritten = 0;
            return(false);
        }