/// <summary> /// Writes the string into the stream. /// </summary> /// <remarks> /// This method doesn't encode the length of the string. /// </remarks> /// <param name="stream">The stream to write into.</param> /// <param name="value">The string to be encoded.</param> /// <param name="context">The encoding.</param> /// <param name="buffer">The buffer allocated by the caller needed for characters encoding.</param> /// <exception cref="ArgumentException"><paramref name="buffer"/> is too small for encoding.</exception> public static void WriteString(this Stream stream, string value, EncodingContext context, byte[] buffer) { if (value.Length == 0) { return; } //TODO: Should be rewritten for .NET Standard 2.1 if (context.Encoding.GetByteCount(value) <= buffer.Length) { stream.Write(buffer, 0, context.Encoding.GetBytes(value, 0, value.Length, buffer, 0)); } else { var maxChars = buffer.Length / context.Encoding.GetMaxByteCount(1); if (maxChars == 0) { throw new ArgumentException(ExceptionMessages.BufferTooSmall, nameof(buffer)); } var encoder = context.GetEncoder(); for (int charStart = 0, numLeft = value.Length, charsRead; numLeft > 0; charStart += charsRead, numLeft -= charsRead) { charsRead = Math.Min(numLeft, maxChars); stream.Write(buffer, 0, encoder.GetBytes(value, charStart, charsRead, buffer, charsRead == numLeft)); } } }
/// <summary> /// Writes the string into the stream asynchronously. /// </summary> /// <remarks> /// This method doesn't encode the length of the string. /// </remarks> /// <param name="stream">The stream to write into.</param> /// <param name="value">The string to be encoded.</param> /// <param name="context">The encoding context.</param> /// <param name="buffer">The buffer allocated by the caller needed for characters encoding.</param> /// <param name="token">The token that can be used to cancel the operation.</param> /// <returns>The task representing asynchronous state of the operation.</returns> /// <exception cref="ArgumentException"><paramref name="buffer"/> is too small for encoding.</exception> public static async Task WriteStringAsync(this Stream stream, string value, EncodingContext context, byte[] buffer, CancellationToken token = default) { if (value.Length == 0) { return; } //TODO: Should be rewritten for .NET Standard 2.1 if (context.Encoding.GetByteCount(value) <= buffer.Length) { await stream.WriteAsync(buffer, 0, context.Encoding.GetBytes(value, 0, value.Length, buffer, 0), token).ConfigureAwait(false); } else { var maxChars = buffer.Length / context.Encoding.GetMaxByteCount(1); if (maxChars == 0) { throw new ArgumentException(ExceptionMessages.BufferTooSmall, nameof(buffer)); } var encoder = context.GetEncoder(); for (int charStart = 0, numLeft = value.Length, charsRead; numLeft > 0; charStart += charsRead, numLeft -= charsRead) { charsRead = Math.Min(numLeft, maxChars); await stream.WriteAsync(buffer, 0, encoder.GetBytes(value, charStart, charsRead, buffer, charsRead == numLeft), token).ConfigureAwait(false); } } }
/// <summary> /// Encodes the string to bytes and write them to pipe asynchronously. /// </summary> /// <param name="writer">The pipe writer.</param> /// <param name="value">The block of characters to encode.</param> /// <param name="context">The text encoding context.</param> /// <param name="bufferSize">The buffer size (in bytes) used for encoding.</param> /// <param name="lengthFormat">String length encoding format; or <see langword="null"/> to prevent encoding of string length.</param> /// <param name="token">The token that can be used to cancel operation.</param> /// <returns>The result of operation.</returns> /// <exception cref="OperationCanceledException">The operation has been canceled.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="lengthFormat"/> is invalid.</exception> /// <exception cref="EndOfStreamException">Pipe closed unexpectedly.</exception> public static async ValueTask WriteStringAsync(this PipeWriter writer, ReadOnlyMemory <char> value, EncodingContext context, int bufferSize = 0, LengthFormat?lengthFormat = null, CancellationToken token = default) { var result = await writer.WriteLengthAsync(value, context.Encoding, lengthFormat, token).ConfigureAwait(false); result.ThrowIfCancellationRequested(token); if (value.IsEmpty) { return; } var encoder = context.GetEncoder(); for (int charsLeft = value.Length, charsUsed, maxChars, bytesPerChar = context.Encoding.GetMaxByteCount(1); charsLeft > 0; value = value.Slice(charsUsed), charsLeft -= charsUsed) { if (result.IsCompleted) { throw new EndOfStreamException(); } var buffer = writer.GetMemory(bufferSize); maxChars = buffer.Length / bytesPerChar; charsUsed = Math.Min(maxChars, charsLeft); encoder.Convert(value.Span.Slice(0, charsUsed), buffer.Span, charsUsed == charsLeft, out charsUsed, out var bytesUsed, out _); writer.Advance(bytesUsed); result = await writer.FlushAsync(token).ConfigureAwait(false); result.ThrowIfCancellationRequested(token); } }