/// <summary> /// Combines one or more ReadOnlySequences into a single ReadOnlySequence /// </summary> /// <typeparam name="T">The element type in the sequence</typeparam> /// <param name="source">An enumeration of ReadOnlySequences</param> /// <returns>The concatenation of the given ReadOnlySequences</returns> public static ReadOnlySequence<T> Combine<T>(this IEnumerable<ReadOnlySequence<T>> source) { if (source == null) throw new ArgumentNullException(nameof(source)); ReadOnlySequence<T>? FirstSequence = null; CombinedSegment<T>? StartSegment = null, CurrentSegment = null; // Merge all the sequences into one foreach (var Sequence in source) { if (Sequence.IsEmpty) continue; if (CurrentSegment == null) { // Cache the first sequence. If there are no others, we can just return it as-is if (FirstSequence == null) { FirstSequence = Sequence; continue; } // Found a second sequence // Create the first segment of the linked list StartSegment = CurrentSegment = new CombinedSegment<T>(FirstSequence.Value.First); // Append any subsequent segments foreach (var ChildSegment in FirstSequence.Value.Slice(StartSegment.Memory.Length)) CurrentSegment = new CombinedSegment<T>(CurrentSegment, ChildSegment); } // Append the segments of this sequence to the linked list as well foreach (var ChildSegment in Sequence) CurrentSegment = new CombinedSegment<T>(CurrentSegment, ChildSegment); } if (CurrentSegment == null) { // Only found one non-empty sequence if (FirstSequence != null) return FirstSequence.Value; // Found no non-empty sequences return ReadOnlySequence<T>.Empty; } return new ReadOnlySequence<T>(StartSegment!, 0, CurrentSegment, CurrentSegment.Memory.Length); }
/// <summary> /// Encodes a char sequence as a byte sequence /// </summary> /// <param name="sequence">The char sequence to decode</param> /// <param name="encoding">The encoding to use</param> /// <returns>The encoded contents of the sequence</returns> public static ReadOnlySequence<byte> Encode(this ReadOnlySequence<char> sequence, Encoding encoding) { if (encoding == null) throw new ArgumentNullException(nameof(encoding)); if (sequence.IsSingleSegment) return new ReadOnlySequence<byte>(encoding.GetBytes(sequence.First.Span)); var Encoder = encoding.GetEncoder(); CombinedSegment<byte>? StartSegment = null, CurrentSegment = null; foreach (var MySegment in sequence) { var InBuffer = MySegment.Span; var OutBuffer = new byte[Encoder.GetByteCount(InBuffer, false)]; var WrittenChars = Encoder.GetBytes(InBuffer, OutBuffer, false); var OutSegment = new ReadOnlyMemory<byte>(OutBuffer, 0, WrittenChars); if (StartSegment == null) StartSegment = CurrentSegment = new CombinedSegment<byte>(OutSegment); else CurrentSegment = new CombinedSegment<byte>(CurrentSegment!, OutSegment); } // Flush the encoder var RemainingBytes = Encoder.GetByteCount(Array.Empty<char>(), true); if (RemainingBytes > 0) { var OutBuffer = new byte[RemainingBytes]; var WrittenChars = Encoder.GetBytes(Array.Empty<char>(), OutBuffer, true); var OutSegment = new ReadOnlyMemory<byte>(OutBuffer, 0, WrittenChars); if (StartSegment == null) StartSegment = CurrentSegment = new CombinedSegment<byte>(OutSegment); else CurrentSegment = new CombinedSegment<byte>(CurrentSegment!, OutSegment); } if (CurrentSegment == null) return ReadOnlySequence<byte>.Empty; return new ReadOnlySequence<byte>(StartSegment!, 0, CurrentSegment, CurrentSegment.Memory.Length); }
/// <summary> /// Combines one or more ReadOnlyMemory blocks into a single ReadOnlySequence /// </summary> /// <typeparam name="T">The element type in the sequence</typeparam> /// <param name="source">An enumeration of ReadOnlyMemory blocks</param> /// <returns>A ReadOnlySequence from the given blocks</returns> public static ReadOnlySequence<T> Combine<T>(this IEnumerable<ReadOnlyMemory<T>> source) { if (source == null) throw new ArgumentNullException(nameof(source)); ReadOnlyMemory<T>? FirstSegment = null; CombinedSegment<T>? StartSegment = null, CurrentSegment = null; foreach (var Segment in source) { if (Segment.IsEmpty) continue; if (CurrentSegment == null) { // Cache the first segment. If there are no others, we can just wrap it as-is if (FirstSegment == null) { FirstSegment = Segment; continue; } // Found a second segment // Create the first segment of the linked list StartSegment = CurrentSegment = new CombinedSegment<T>(FirstSegment.Value); } CurrentSegment = new CombinedSegment<T>(CurrentSegment, Segment); } if (CurrentSegment == null) { // Only found one non-empty sequence if (FirstSegment != null) return new ReadOnlySequence<T>(FirstSegment.Value); // Found no non-empty sequences return ReadOnlySequence<T>.Empty; } return new ReadOnlySequence<T>(StartSegment!, 0, CurrentSegment, CurrentSegment.Memory.Length); }