public void Write(ReadOnlySpan <byte> bytes)
        {
            // ToDo - use bytes.Length + 11 here as the worst-case?
            // ASCII length, colon separator, bytes
            int required = bytes.Length + BEncodingHelpers.DigitCount(bytes.Length) + 1;

            if (_memory.Length - BytesPending < required)
            {
                Grow(required);
            }

            var span = _memory.Slice(BytesPending).Span;

            // ToDo - is this fast enough?
            bool success = Utf8Formatter.TryFormat(bytes.Length, span, out int written);

            Debug.Assert(success);
            Debug.Assert(written == BEncodingHelpers.DigitCount(bytes.Length));

            span[written] = BEncodingConstants.ColonSeparator;

            bytes.CopyTo(span.Slice(written + 1));

            BytesPending += required;
        }
        public void WriteASCIIUnchecked(ReadOnlySpan <char> ascii)
        {
            Debug.Assert(BEncodingHelpers.UTF8.GetByteCount(ascii) == ascii.Length, "Non ASCII chars found");

            // ToDo - use bytes.Length + 11 here as the worst-case?
            // ASCII length, colon separator, bytes
            int required = ascii.Length + BEncodingHelpers.DigitCount(ascii.Length) + 1;

            if (_memory.Length - BytesPending < required)
            {
                Grow(required);
            }

            var span = _memory.Slice(BytesPending).Span;

            // ToDo - is this fast enough?
            bool success = Utf8Formatter.TryFormat(ascii.Length, span, out int written);

            Debug.Assert(success);
            Debug.Assert(written == BEncodingHelpers.DigitCount(ascii.Length));

            span[written] = BEncodingConstants.ColonSeparator;

            int bytesWritten = BEncodingHelpers.ASCII.GetBytes(ascii, span.Slice(written + 1));

            Debug.Assert(bytesWritten == ascii.Length);

            BytesPending += required;
        }
        private void WriteString_UnknownLength(ReadOnlySpan <char> chars)
        {
            int byteCount = BEncodingHelpers.UTF8.GetByteCount(chars);

            // ToDo - use knownByteCount + 11 here as the worst-case?
            // ASCII length, colon separator, bytes
            int required = byteCount + BEncodingHelpers.DigitCount(byteCount) + 1;

            if (_memory.Length - BytesPending < required)
            {
                Grow(required);
            }

            var span = _memory.Slice(BytesPending).Span;

            // ToDo - is this fast enough?
            bool success = Utf8Formatter.TryFormat(byteCount, span, out int written);

            Debug.Assert(success);
            Debug.Assert(written == BEncodingHelpers.DigitCount(byteCount));

            span[written] = BEncodingConstants.ColonSeparator;

            int bytesWritten = BEncodingHelpers.UTF8.GetBytes(chars, span.Slice(written + 1));

            Debug.Assert(bytesWritten == byteCount);

            BytesPending += required;
        }
        private void WriteString_AssumedLengthDigitCount(ReadOnlySpan <char> chars, int lengthDigitCount)
        {
            // ASCII length, 1 potential length extension, colon separator, bytes (could expand up to 3x)
            int required = lengthDigitCount + 2 + chars.Length * BEncodingConstants.MaxExpansionFactorWhileTranscoding;

            if (_memory.Length - BytesPending < required)
            {
                Grow(required);
            }

            var span = _memory.Slice(BytesPending).Span;

            // Write the UTF8 bytes first - we don't know the exact length yet
            var utf8Span     = span.Slice(lengthDigitCount + 1);
            int bytesWritten = BEncodingHelpers.UTF8.GetBytes(chars, utf8Span);

            bool success = Utf8Formatter.TryFormat(bytesWritten, span, out int written);

            Debug.Assert(success);
            Debug.Assert(written == lengthDigitCount || written == lengthDigitCount + 1);

            if (BEncodingHelpers.DigitCount(bytesWritten) != lengthDigitCount)
            {
                // Unlikely case, writing the ColonSeparator would overwrite the first utf8 byte
                // Shift all the written bytes one place to the right
                // This is slow as it boils down to a P/Invoke because the buffers are overlapping
                utf8Span.Slice(0, bytesWritten).CopyTo(utf8Span.Slice(1, bytesWritten));

                // ToDo benchmark - stackalloc a temp buffer and do two copies vs P/Invoke
            }

            span[written] = BEncodingConstants.ColonSeparator;

            BytesPending += bytesWritten + written + 1;
        }
        private void WriteString_KnownLengthDigitCount(ReadOnlySpan <char> chars, int lengthDigitCount)
        {
            // ASCII length, colon separator, bytes (could expand up to 3x)
            int required = lengthDigitCount + 1 + chars.Length * BEncodingConstants.MaxExpansionFactorWhileTranscoding;

            if (_memory.Length - BytesPending < required)
            {
                Grow(required);
            }

            var span = _memory.Slice(BytesPending).Span;

            // Write the UTF8 bytes first - we don't know the exact length yet
            int bytesWritten = BEncodingHelpers.UTF8.GetBytes(chars, span.Slice(lengthDigitCount + 1));

            Debug.Assert(lengthDigitCount == BEncodingHelpers.DigitCount(bytesWritten));

            bool success = Utf8Formatter.TryFormat(bytesWritten, span, out int written);

            Debug.Assert(success);
            Debug.Assert(written == lengthDigitCount);

            span[written] = BEncodingConstants.ColonSeparator;

            BytesPending += bytesWritten + written + 1;
        }
Esempio n. 6
0
        private bool ConsumeString(byte first)
        {
            if (!BEncodingHelpers.IsDigit(first))
            {
                return(false);
            }

            ReadOnlySpan <byte> localSpan = _buffer.Slice(Consumed);

            int index = localSpan.IndexOf(BEncodingConstants.ColonSeparator);

            if (index == -1)
            {
                return(false);
            }

            if (first == '0')
            {
                if (index != 1)
                {
                    return(false);
                }

                Consumed += 2;
                ValueSpan = default;
            }
            else
            {
                if (!Utf8Parser.TryParse(localSpan.Slice(0, index), out int length, out int bytesConsumed) || bytesConsumed != index)
                {
                    return(false);
                }

                int consumed = Consumed + index + 1 + length;

                if ((uint)consumed > (uint)_buffer.Length)
                {
                    return(false);
                }

                Consumed = consumed;

                ValueSpan = localSpan.Slice(index + 1, length);
            }

            TokenType = BEncodingTokenType.String;
            return(true);
        }
        // ToDo benchmark - split writes into chunks?

        public async Task WriteAsync(Memory <byte> bytes)
        {
            if (_stream is null)
            {
                Write(bytes.Span);
                return;
            }

            int required = BEncodingHelpers.DigitCount(bytes.Length);

            if (_memory.Length - BytesPending < required)
            {
                Grow(required);
            }

            bool success = Utf8Formatter.TryFormat(bytes.Length, _memory.Span.Slice(BytesPending), out int written);

            Debug.Assert(success);
            Debug.Assert(written == required);
            BytesPending += written;

            _arrayBufferWriter.Advance(BytesPending);
            Debug.Assert(BytesPending == _arrayBufferWriter.WrittenCount);
            await _stream.WriteAsync(_arrayBufferWriter.WrittenMemory).ConfigureAwait(false);

            _arrayBufferWriter.Clear();
            _memory = _arrayBufferWriter.GetMemory(DefaultGrowthSize);

            BytesCommitted += BytesPending + bytes.Length;
            BytesPending    = 0;

            while (bytes.Length > 0)
            {
                int chunkSize = Math.Min(bytes.Length, 4096);
                await _stream.WriteAsync(bytes.Slice(0, chunkSize)).ConfigureAwait(false);

                bytes = bytes.Slice(chunkSize);
            }

            await _stream.FlushAsync().ConfigureAwait(false);
        }
Esempio n. 8
0
        public bool TryGet(out BigInteger value)
        {
            // Fast-path "smaller" integers
            if (ValueSpan.Length < 19)
            {
                if (TryGet(out long longValue))
                {
                    value = new BigInteger(longValue);
                    return(true);
                }

                goto ReturnFalse;
            }

            // It's a big number
            // We can still get some perf by building the BigInteger by sizeof(ulong)

            // Possible perf improvement:
            // manually building up the bits representation and constructing a BigInteger only once for huge values

            // We can start with a 'long' section to pick up the sign
            if (!Utf8Parser.TryParse(ValueSpan.Slice(0, 18), out long startingNumber, out int bytesConsumed) || bytesConsumed != 18)
            {
                goto ReturnFalse;
            }

            if (startingNumber > -10_000_000_000_000_000L &&
                startingNumber < 100_000_000_000_000_000L)
            {
                goto ReturnFalse;
            }

            value = new BigInteger(startingNumber < 0 ? -startingNumber : startingNumber);

            ReadOnlySpan <byte> localSpan = ValueSpan.Slice(18);

            do
            {
                int len = Math.Min(localSpan.Length, 19);
                if (!Utf8Parser.TryParse(localSpan.Slice(0, len), out ulong segment, out bytesConsumed) || bytesConsumed != len)
                {
                    goto ReturnFalse;
                }

                localSpan = localSpan.Slice(len);

                ulong shift = len == 19
                    ? 10_000_000_000_000_000_000u
                    : BEncodingHelpers.Pow10(len);

                value = value * shift + segment;
            }while (localSpan.Length > 0);

            if (startingNumber < 0)
            {
                value = -value;
            }

            return(true);

ReturnFalse:
            value = BigInteger.Zero;
            return(false);
        }