public static void RentAndReturnManyOfTheSameSize_NoneAreSame(ArrayPool<byte> pool) { foreach (int length in new[] { 1, 16, 32, 64, 127, 4096, 4097 }) { for (int iter = 0; iter < 2; iter++) { var buffers = new HashSet<byte[]>(); for (int i = 0; i < 100; i++) { buffers.Add(pool.Rent(length)); } Assert.Equal(100, buffers.Count); foreach (byte[] buffer in buffers) { pool.Return(buffer); } } } }
public FileBufferingReadStream( Stream inner, int memoryThreshold, long? bufferLimit, Func<string> tempFileDirectoryAccessor, ArrayPool<byte> bytePool) { if (inner == null) { throw new ArgumentNullException(nameof(inner)); } if (tempFileDirectoryAccessor == null) { throw new ArgumentNullException(nameof(tempFileDirectoryAccessor)); } _bytePool = bytePool; if (memoryThreshold < _maxRentedBufferSize) { _rentedBuffer = bytePool.Rent(memoryThreshold); _buffer = new MemoryStream(_rentedBuffer); _buffer.SetLength(0); } else { _buffer = new MemoryStream(); } _inner = inner; _memoryThreshold = memoryThreshold; _bufferLimit = bufferLimit; _tempFileDirectoryAccessor = tempFileDirectoryAccessor; }
public FixedSizeArrayBufferWriter(int size, ArrayPool <T>?pool = null) { if (size == 0) { size = 1; // to fit the IBufferWriter contract that GetMemory/GetSpan never return empty } _size = size; _buffer = pool?.Rent(size) ?? new T[size]; }
public StreamFormatter(Stream stream, FormattingData formattingData, ArrayPool<byte> pool, int bufferSize = 256) { _pool = pool; _buffer = null; if (bufferSize > 0) { _buffer = _pool.Rent(bufferSize); } _formattingData = formattingData; _stream = stream; }
public StreamFormatter(Stream stream, EncodingData encoding, ArrayPool<byte> pool, int bufferSize = 256) { _pool = pool; _buffer = null; if (bufferSize > 0) { _buffer = _pool.Rent(bufferSize); } _encoding = encoding; _stream = stream; }
public FormReader(string data, ArrayPool<char> charPool) { if (data == null) { throw new ArgumentNullException(nameof(data)); } _buffer = charPool.Rent(_rentedCharPoolLength); _charPool = charPool; _reader = new StringReader(data); }
public FormReader(Stream stream, Encoding encoding, ArrayPool<char> charPool) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } _buffer = charPool.Rent(_rentedCharPoolLength); _charPool = charPool; _reader = new StreamReader(stream, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024 * 2, leaveOpen: true); }
/// <summary> /// Rents the pixel array from the pool. /// </summary> /// <param name="minimumLength">The minimum length of the array to return.</param> /// <returns>The <see cref="T:TColor[]"/></returns> public static TColor[] RentPixels(int minimumLength) { return(ArrayPool.Rent(minimumLength)); }
public static T[] ReturnAndRent <T>(this ArrayPool <T> pool, T[] array, int newMinimumLength) { pool.Return(array); return(pool.Rent(newMinimumLength)); }
public static void CanRentManySizedBuffers(ArrayPool<byte> pool) { for (int i = 1; i < 10000; i++) { byte[] buffer = pool.Rent(i); Assert.Equal(i <= 16 ? 16 : RoundUpToPowerOf2(i), buffer.Length); pool.Return(buffer); } }
/// <summary> /// Buffers a portion of the given stream, returning the buffered stream partition. /// </summary> /// <param name="stream"> /// Stream to buffer from. /// </param> /// <param name="minCount"> /// Minimum number of bytes to buffer. This method will not return until at least this many bytes have been read from <paramref name="stream"/> or the stream completes. /// </param> /// <param name="maxCount"> /// Maximum number of bytes to buffer. /// </param> /// <param name="absolutePosition"> /// Current position of the stream, since <see cref="Stream.Position"/> throws if not seekable. /// </param> /// <param name="arrayPool"> /// Pool to rent buffer space from. /// </param> /// <param name="maxArrayPoolRentalSize"> /// Max size we can request from the array pool. /// </param> /// <param name="async"> /// Whether to perform this operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Cancellation token. /// </param> /// <returns> /// The buffered stream partition with memory backed by an array pool. /// </returns> internal static async Task <PooledMemoryStream> BufferStreamPartitionInternal( Stream stream, long minCount, long maxCount, long absolutePosition, ArrayPool <byte> arrayPool, int?maxArrayPoolRentalSize, bool async, CancellationToken cancellationToken) { long totalRead = 0; var streamPartition = new PooledMemoryStream(arrayPool, absolutePosition, maxArrayPoolRentalSize ?? DefaultMaxArrayPoolRentalSize); // max count to write into a single array int maxCountIndividualBuffer; // min count to write into a single array int minCountIndividualBuffer; // the amount that was written into the current array int readIndividualBuffer; do { // buffer to write to byte[] buffer; // offset to start writing at int offset; BufferPartition latestBuffer = streamPartition.GetLatestBufferWithAvailableSpaceOrDefault(); // whether we got a brand new buffer to write into bool newbuffer; if (latestBuffer != default) { buffer = latestBuffer.Buffer; offset = latestBuffer.DataLength; newbuffer = false; } else { buffer = arrayPool.Rent((int)Math.Min(maxCount - totalRead, streamPartition.MaxArraySize)); offset = 0; newbuffer = true; } // limit max and min count for this buffer by buffer length maxCountIndividualBuffer = (int)Math.Min(maxCount - totalRead, buffer.Length - offset); // definitionally limited by max; we won't ever have a swapped min/max range minCountIndividualBuffer = (int)Math.Min(minCount - totalRead, maxCountIndividualBuffer); readIndividualBuffer = await ReadLoopInternal( stream, buffer, offset : offset, minCountIndividualBuffer, maxCountIndividualBuffer, async, cancellationToken).ConfigureAwait(false); // if nothing was placed in a brand new array if (readIndividualBuffer == 0 && newbuffer) { arrayPool.Return(buffer); } // if brand new array and we did place data in it else if (newbuffer) { streamPartition.BufferSet.Add(new BufferPartition { Buffer = buffer, DataLength = readIndividualBuffer }); } // added to an existing array that was not entirely filled else { latestBuffer.DataLength += readIndividualBuffer; } totalRead += readIndividualBuffer; /* If we filled the buffer this loop, then quitting on min count is pointless. The point of quitting * on min count is when the source stream doesn't have available bytes and we've reached an amount worth * sending instead of blocking on. If we filled the available array, we don't actually know whether more * data is available yet, as we limited our read for reasons outside the stream state. We should therefore * try another read regardless of whether we hit min count. */ } while ( // stream is done if this value is zero; no other check matters readIndividualBuffer != 0 && // stop filling the partition if we've hit the max size of the partition totalRead < maxCount && // stop filling the partition if we've reached min count and we know we've hit at least a pause in the stream (totalRead < minCount || readIndividualBuffer == maxCountIndividualBuffer)); return(streamPartition); }
public HttpRequestStreamReader( Stream stream, Encoding encoding, int bufferSize, ArrayPool<byte> bytePool, ArrayPool<char> charPool) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (!stream.CanRead) { throw new ArgumentException(Resources.HttpRequestStreamReader_StreamNotReadable, nameof(stream)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } if (bytePool == null) { throw new ArgumentNullException(nameof(bytePool)); } if (charPool == null) { throw new ArgumentNullException(nameof(charPool)); } if (bufferSize <= 0) { throw new ArgumentOutOfRangeException(nameof(bufferSize)); } _stream = stream; _encoding = encoding; _byteBufferSize = bufferSize; _bytePool = bytePool; _charPool = charPool; _decoder = encoding.GetDecoder(); _byteBuffer = _bytePool.Rent(bufferSize); try { var requiredLength = encoding.GetMaxCharCount(bufferSize); _charBuffer = _charPool.Rent(requiredLength); } catch { _bytePool.Return(_byteBuffer); _byteBuffer = null; if (_charBuffer != null) { _charPool.Return(_charBuffer); _charBuffer = null; } } }
public HttpResponseStreamWriter( Stream stream, Encoding encoding, int bufferSize, ArrayPool<byte> bytePool, ArrayPool<char> charPool) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (!stream.CanWrite) { throw new ArgumentException(Resources.HttpResponseStreamWriter_StreamNotWritable, nameof(stream)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } if (bytePool == null) { throw new ArgumentNullException(nameof(bytePool)); } if (charPool == null) { throw new ArgumentNullException(nameof(charPool)); } _stream = stream; Encoding = encoding; _charBufferSize = bufferSize; _encoder = encoding.GetEncoder(); _bytePool = bytePool; _charPool = charPool; _charBuffer = charPool.Rent(bufferSize); try { var requiredLength = encoding.GetMaxByteCount(bufferSize); _byteBuffer = bytePool.Rent(requiredLength); } catch { charPool.Return(_charBuffer); _charBuffer = null; if (_byteBuffer != null) { bytePool.Return(_byteBuffer); _byteBuffer = null; } throw; } }
public static BytesOwner ReadPageData(Stream nakedStream, Thrift.CompressionCodec compressionCodec, int compressedLength, int uncompressedLength) { if (!_codecToCompressionMethod.TryGetValue(compressionCodec, out CompressionMethod compressionMethod)) { throw new NotSupportedException($"reader for compression '{compressionCodec}' is not supported."); } int totalBytesRead = 0; int currentBytesRead = int.MinValue; byte[] data = BytesPool.Rent(compressedLength); bool dataRented = true; // Some storage solutions (like Azure blobs) might require more than one 'Read' action to read the requested length. while (totalBytesRead < compressedLength && currentBytesRead != 0) { currentBytesRead = nakedStream.Read(data, totalBytesRead, compressedLength - totalBytesRead); totalBytesRead += currentBytesRead; } if (totalBytesRead != compressedLength) { throw new ParquetException($"expected {compressedLength} bytes in source stream but could read only {totalBytesRead}"); } switch (compressionMethod) { case CompressionMethod.None: //nothing to do, original data is the raw data break; case CompressionMethod.Gzip: using (var source = new MemoryStream(data, 0, compressedLength)) { byte[] unGzData = BytesPool.Rent(uncompressedLength); using (var dest = new MemoryStream(unGzData, 0, uncompressedLength)) { using (var gz = new GZipStream(source, CompressionMode.Decompress)) { gz.CopyTo(dest); } } BytesPool.Return(data); data = unGzData; } break; case CompressionMethod.Snappy: byte[] uncompressed = Snappy.Decode(data.AsSpan(0, compressedLength)); BytesPool.Return(data); data = uncompressed; dataRented = false; break; default: throw new NotSupportedException("method: " + compressionMethod); } return(new BytesOwner(data, 0, data.AsMemory(0, uncompressedLength), d => BytesPool.Return(d), dataRented)); }
public TShared[] Rent(int minimumLength) => _inner.Rent(minimumLength);
protected int ReadNextPacket() { if (Data != null) { StreamingPool.Return(Data); Data = null; } //wait until a magic packet is received Device.MillisTimeout = 100; while (true) { Span <byte> MagicBuf = stackalloc byte[4]; if (Device.Read(MagicBuf) != 4) { if (Token.IsCancellationRequested) { return(-3); } else { continue; } } if (MagicBuf.SequenceEqual(MagicPacket)) { break; } } //read the payload size Span <byte> SizeBuf = stackalloc byte[4]; if (Device.Read(SizeBuf) != 4) { return(-2); } var size = BitConverter.ToUInt32(SizeBuf); if (size > MaxBufSize) { return(-1); } if (size == 0) { return(0); } //read the timestamp Span <byte> TsBuf = stackalloc byte[8]; if (Device.Read(TsBuf) != 8) { return(-4); } Timestamp = BitConverter.ToUInt64(TsBuf); if (firtTs == 0) { firtTs = Timestamp; } Timestamp -= firtTs; //Read the actual data Device.MillisTimeout = 1000; Data = StreamingPool.Rent((int)size); int actualsize = Device.Read(Data, 0, (int)size); if (actualsize != size) { Console.WriteLine("Warning: Reported size doesn't match received size"); } return(actualsize); }
public static void RentingWithInvalidLengthThrows(ArrayPool <byte> pool) { AssertExtensions.Throws <ArgumentOutOfRangeException>("minimumLength", () => pool.Rent(-1)); }
public DisposableArrayPool(ArrayPool <T> pool, int minimumLength) { _pool = pool; Array = pool.Rent(minimumLength); }
BufferSegment CreateSegment() { return(new BufferSegment(_pool.Rent(_segmentSize))); }
public T[] Rent(int minimumLength) { return(_inner.Rent(minimumLength)); }
public ArrayFormatter(int capacity, SymbolTable symbolTable, ArrayPool <byte> pool = null) { _pool = pool ?? ArrayPool <byte> .Shared; _symbolTable = symbolTable; _buffer = new ResizableArray <byte>(_pool.Rent(capacity)); }
public StringFormatter(int capacity, ArrayPool<byte> pool) { _pool = pool; _buffer = _pool.Rent(capacity * 2); }
static TemporaryArray <T> CreateInternal <T>(ArrayPool <T> pool, int length, bool clearOnReturn) => new TemporaryArray <T>(pool, pool?.Rent(length) ?? new T[length], length, clearOnReturn);
public ArrayFormatter(int capacity, EncodingData encoding, ArrayPool<byte> pool = null) { _pool = pool != null ? pool : ArrayPool<byte>.Shared; _encoding = encoding; _buffer = new ResizableArray<byte>(_pool.Rent(capacity)); }
public static void CallingReturnWithoutClearingDoesNotClearTheBuffer(ArrayPool<byte> pool) { byte[] buffer = pool.Rent(4); FillArray(buffer); pool.Return(buffer, clearArray: false); CheckFilledArray(buffer, (byte b1, byte b2) => Assert.Equal(b1, b2)); }
public static void RentingWithInvalidLengthThrows(int length) { ArrayPool <byte> pool = ArrayPool <byte> .Create(); Assert.Throws <ArgumentOutOfRangeException>("minimumLength", () => pool.Rent(length)); }
public override int Read(Span <byte> buffer) { ThrowIfDisposed(); if (_buffer.Position < _buffer.Length || _completelyBuffered) { // Just read from the buffer return(_buffer.Read(buffer)); } var read = _inner.Read(buffer); if (_bufferLimit.HasValue && _bufferLimit - read < _buffer.Length) { throw new IOException("Buffer limit exceeded."); } // We're about to go over the threshold, switch to a file if (_inMemory && _memoryThreshold - read < _buffer.Length) { _inMemory = false; var oldBuffer = _buffer; _buffer = CreateTempFile(); if (_rentedBuffer == null) { // Copy data from the in memory buffer to the file stream using a pooled buffer oldBuffer.Position = 0; var rentedBuffer = _bytePool.Rent(Math.Min((int)oldBuffer.Length, _maxRentedBufferSize)); try { var copyRead = oldBuffer.Read(rentedBuffer); while (copyRead > 0) { _buffer.Write(rentedBuffer.AsSpan(0, copyRead)); copyRead = oldBuffer.Read(rentedBuffer); } } finally { _bytePool.Return(rentedBuffer); } } else { _buffer.Write(_rentedBuffer.AsSpan(0, (int)oldBuffer.Length)); _bytePool.Return(_rentedBuffer); _rentedBuffer = null; } } if (read > 0) { _buffer.Write(buffer.Slice(0, read)); } else { _completelyBuffered = true; } return(read); }
public static void RentingMultipleArraysGivesBackDifferentInstances() { ArrayPool <byte> instance = ArrayPool <byte> .Create(maxArraysPerBucket : 2, maxArrayLength : 16); Assert.NotSame(instance.Rent(100), instance.Rent(100)); }
public static void UsePoolInParallel(ArrayPool<byte> pool) { int[] sizes = new int[] { 16, 32, 64, 128 }; Parallel.For(0, 250000, i => { foreach (int size in sizes) { byte[] array = pool.Rent(size); Assert.NotNull(array); Assert.InRange(array.Length, size, int.MaxValue); pool.Return(array); } }); }
public static void CallingReturnWithClearingDoesClearTheBuffer(ArrayPool<byte> pool) { byte[] buffer = pool.Rent(4); FillArray(buffer); // Note - yes this is bad to hold on to the old instance but we need to validate the contract pool.Return(buffer, clearArray: true); CheckFilledArray(buffer, (byte b1, byte b2) => Assert.Equal(b1, default(byte))); }
public static void Renting0LengthArrayReturnsSingleton(ArrayPool<byte> pool) { byte[] zero0 = pool.Rent(0); byte[] zero1 = pool.Rent(0); byte[] zero2 = pool.Rent(0); byte[] one = pool.Rent(1); Assert.Same(zero0, zero1); Assert.Same(zero1, zero2); Assert.NotSame(zero2, one); pool.Return(zero0); pool.Return(zero1); pool.Return(zero2); pool.Return(one); Assert.Same(zero0, pool.Rent(0)); }
/// <summary> /// Internal part of the DHT processor, whatever does it mean /// </summary> /// <param name="inputProcessor">The decoder instance</param> /// <param name="defineHuffmanTablesData">The temporal buffer that holds the data that has been read from the Jpeg stream</param> /// <param name="remaining">Remaining bits</param> public void ProcessDefineHuffmanTablesMarkerLoop( ref InputProcessor inputProcessor, byte[] defineHuffmanTablesData, ref int remaining) { // Read nCodes and huffman.Valuess (and derive h.Length). // nCodes[i] is the number of codes with code length i. // h.Length is the total number of codes. this.Length = 0; int[] ncodes = new int[MaxCodeLength]; for (int i = 0; i < ncodes.Length; i++) { ncodes[i] = defineHuffmanTablesData[i + 1]; this.Length += ncodes[i]; } if (this.Length == 0) { throw new ImageFormatException("Huffman table has zero length"); } if (this.Length > MaxNCodes) { throw new ImageFormatException("Huffman table has excessive length"); } remaining -= this.Length + 17; if (remaining < 0) { throw new ImageFormatException("DHT has wrong length"); } byte[] values = null; try { values = BytePool256.Rent(MaxNCodes); inputProcessor.ReadFull(values, 0, this.Length); for (int i = 0; i < values.Length; i++) { this.Values[i] = values[i]; } } finally { BytePool256.Return(values, true); } // Derive the look-up table. for (int i = 0; i < this.Lut.Length; i++) { this.Lut[i] = 0; } int x = 0, code = 0; for (int i = 0; i < LutSizeLog2; i++) { code <<= 1; for (int j = 0; j < ncodes[i]; j++) { // The codeLength is 1+i, so shift code by 8-(1+i) to // calculate the high bits for every 8-bit sequence // whose codeLength's high bits matches code. // The high 8 bits of lutValue are the encoded value. // The low 8 bits are 1 plus the codeLength. int base2 = code << (7 - i); int lutValue = (this.Values[x] << 8) | (2 + i); for (int k = 0; k < 1 << (7 - i); k++) { this.Lut[base2 | k] = lutValue; } code++; x++; } } // Derive minCodes, maxCodes, and indices. int c = 0, index = 0; for (int i = 0; i < ncodes.Length; i++) { int nc = ncodes[i]; if (nc == 0) { this.MinCodes[i] = -1; this.MaxCodes[i] = -1; this.Indices[i] = -1; } else { this.MinCodes[i] = c; this.MaxCodes[i] = c + nc - 1; this.Indices[i] = index; c += nc; index += nc; } c <<= 1; } }
public static async Task <ReadOnlyMemory <byte> > ComputeHashAsync( this Stream stream, IHashAlgorithm hashAlgorithm, ArrayPool <byte> arrayPool, int bufferSize, CancellationToken cancellationToken ) { // Validate parameters. if (hashAlgorithm == null) { throw new ArgumentNullException(nameof(hashAlgorithm)); } if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (arrayPool == null) { throw new ArgumentNullException(nameof(arrayPool)); } if (bufferSize <= 0) { throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, $"The { nameof(bufferSize) } parameter must be a positive value."); } // The declarations for the buffer and the copy. byte[]? buffer = null; byte[]? copy = null; // Wrap in a try/finally. try { // Allocate. buffer = arrayPool.Rent(bufferSize); copy = arrayPool.Rent(bufferSize); // The bytes read task. Task <int> bytesRead = stream.ReadAsync(buffer, 0, bufferSize, cancellationToken); // No bytes read, get out. if ((await bytesRead.ConfigureAwait(false)) == 0) { return(hashAlgorithm.Hash); } // While there are items to process. do { // Copy the buffer. // NOTE: Need to do this because it's a shared resource. // If tests show there's a sweet spot between the copy time and hash time < next ReadAsync time, // move buffer size accordingly. Array.Copy(buffer, copy, bufferSize); // Read the next task. bytesRead = stream.ReadAsync(buffer, 0, bufferSize, cancellationToken); // Hash. hashAlgorithm.TransformBlock(copy); // Get the next stream immediately. } while (await bytesRead.ConfigureAwait(false) > 0); // Return. return(hashAlgorithm.Hash); } finally { // Release. arrayPool.Return(buffer); arrayPool.Return(copy); } }
public static void RentingWithInvalidLengthThrows(ArrayPool<byte> pool) { Assert.Throws<ArgumentOutOfRangeException>("minimumLength", () => pool.Rent(-1)); }
/// <summary> /// 申请 /// </summary> /// <param name="minimumLength"></param> /// <returns></returns> public static byte[] Rent(int minimumLength) { return(ArrayPool.Rent(minimumLength)); }
internal const byte ReservedEscapeMessageBytes = 5; // 2 characters total, escape one '\' of 1 byte and real one of up to 4 bytes internal static async IAsyncEnumerable <string> GetMessageParts(string message, string?steamMessagePrefix = null, bool isAccountLimited = false) { if (string.IsNullOrEmpty(message)) { throw new ArgumentNullException(nameof(message)); } int prefixBytes = 0; int prefixLength = 0; if (!string.IsNullOrEmpty(steamMessagePrefix)) { // We must escape our message prefix if needed // ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework steamMessagePrefix = Escape(steamMessagePrefix !); prefixBytes = GetMessagePrefixBytes(steamMessagePrefix); if (prefixBytes > MaxMessagePrefixBytes) { throw new ArgumentOutOfRangeException(nameof(steamMessagePrefix)); } prefixLength = steamMessagePrefix.Length; } int maxMessageBytes = (isAccountLimited ? MaxMessageBytesForLimitedAccounts : MaxMessageBytesForUnlimitedAccounts) - ReservedContinuationMessageBytes; // We must escape our message prior to sending it message = Escape(message); int messagePartBytes = prefixBytes; StringBuilder messagePart = new(steamMessagePrefix); Decoder decoder = Encoding.UTF8.GetDecoder(); ArrayPool <char> charPool = ArrayPool <char> .Shared; using StringReader stringReader = new(message); while (await stringReader.ReadLineAsync().ConfigureAwait(false) is { } line) { // Special case for empty newline if (line.Length == 0) { if (messagePart.Length > prefixLength) { messagePartBytes += NewlineWeight; messagePart.AppendLine(); } // Check if we reached the limit for one message if (messagePartBytes + NewlineWeight + ReservedEscapeMessageBytes > maxMessageBytes) { if (stringReader.Peek() >= 0) { messagePart.Append(ParagraphCharacter); } yield return(messagePart.ToString()); messagePartBytes = prefixBytes; messagePart.Clear(); messagePart.Append(steamMessagePrefix); } // Move on to the next line continue; } byte[] lineBytes = Encoding.UTF8.GetBytes(line); for (int lineBytesRead = 0; lineBytesRead < lineBytes.Length;) { if (messagePart.Length > prefixLength) { if (messagePartBytes + NewlineWeight + lineBytes.Length > maxMessageBytes) { messagePart.Append(ParagraphCharacter); yield return(messagePart.ToString()); messagePartBytes = prefixBytes; messagePart.Clear(); messagePart.Append(steamMessagePrefix); } else { messagePartBytes += NewlineWeight; messagePart.AppendLine(); } } int bytesToTake = Math.Min(maxMessageBytes - messagePartBytes, lineBytes.Length - lineBytesRead); // We can never have more characters than bytes used, so this covers the worst case of 1-byte characters exclusively char[] lineChunk = charPool.Rent(bytesToTake); try { // We have to reset the decoder prior to using it, as we must discard any amount of bytes read from previous incomplete character decoder.Reset(); int charsUsed = decoder.GetChars(lineBytes, lineBytesRead, bytesToTake, lineChunk, 0, false); switch (charsUsed) { case <= 0: throw new InvalidOperationException(nameof(charsUsed)); case >= 2 when(lineChunk[charsUsed - 1] == '\\') && (lineChunk[charsUsed - 2] != '\\'): // If our message is of max length and ends with a single '\' then we can't split it here, because it escapes the next character // Instead, we'll cut this message one char short and include the rest in the next iteration charsUsed--; break; } int bytesUsed = Encoding.UTF8.GetByteCount(lineChunk, 0, charsUsed); if (lineBytesRead > 0) { messagePartBytes += ContinuationCharacterBytes; messagePart.Append(ContinuationCharacter); } lineBytesRead += bytesUsed; messagePartBytes += bytesUsed; messagePart.Append(lineChunk, 0, charsUsed); } finally { charPool.Return(lineChunk); } bool midLineSplitting = false; if (lineBytesRead < lineBytes.Length) { midLineSplitting = true; messagePartBytes += ContinuationCharacterBytes; messagePart.Append(ContinuationCharacter); } // Check if we still have room for one more line if (messagePartBytes + NewlineWeight + ReservedEscapeMessageBytes <= maxMessageBytes) { continue; } if (!midLineSplitting && (stringReader.Peek() >= 0)) { messagePart.Append(ParagraphCharacter); } yield return(messagePart.ToString()); messagePartBytes = prefixBytes; messagePart.Clear(); messagePart.Append(steamMessagePrefix); } } if (messagePart.Length <= prefixLength) { yield break; } yield return(messagePart.ToString()); }
private static ArraySegment <byte> Rent(int size) { byte[] buffer = s_bufferPool.Rent(size); return(new ArraySegment <byte>(buffer, 0, size)); }