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