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; }
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); }
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); }