Codebelt

Cuemon.IO

Stream factory, compression and conversion decorator extensions, and a structured options hierarchy for IO operations — an addition to System.IO.

.NET 10.0 / .NET 9.0 / .NET Standard 2.0 / .NET Standard 2.1 MIT v10.5.3 362,944 downloads

Overview

Cuemon.IO extends System.IO with two complementary capabilities: a StreamFactory static class for creating in-memory Stream instances from delegate callbacks, and decorator extension classes (StreamDecoratorExtensions, TextReaderDecoratorExtensions) that compress, convert, and copy streams through the IDecorator<T> pattern from Cuemon.Kernel.

The package also provides a structured options hierarchy — both synchronous and async variants — that controls buffer sizes, character encoding, BOM preamble handling, disposability (LeaveOpen), and compression level for each IO operation. These options types are the intended extension points when callers need to deviate from the defaults.

Key APIs

StreamFactory is a static factory that creates positioned-at-zero MemoryStream instances from an Action<StreamWriter> or Action<IBufferWriter<byte>> delegate. Overloads accept up to five strongly typed arguments forwarded from the call site to the delegate, so callers can close over the data they need without capturing state unnecessarily. The IBufferWriter<byte> overloads are only available on .NET Standard 2.1 and .NET 9+.

StreamDecoratorExtensions.ToByteArray and ToByteArrayAsync convert the enclosed Stream to a byte[], controlled by StreamCopyOptions and AsyncStreamCopyOptions respectively. Both default the buffer to 81920 bytes, and the LeaveOpen property on the options object determines whether the source stream is disposed after the copy.

StreamDecoratorExtensions.ToEncodedString and ToEncodedStringAsync decode the enclosed Stream to a string using StreamReaderOptions or AsyncStreamReaderOptions. Both auto-detect the encoding via BOM when the configured encoding equals the library default, and InvalidEnumArgumentException is thrown if the Preamble value is out of the valid PreambleSequence range.

StreamDecoratorExtensions.CompressGZip, CompressDeflate, and CompressBrotli (and their async counterparts) each compress the enclosed stream into a new MemoryStream using the corresponding System.IO.Compression stream type. Brotli overloads are conditionally compiled for .NET Standard 2.1 and .NET 9+. The symmetric decompress methods (DecompressGZip, DecompressDeflate, DecompressBrotli) reverse the operation from the same decorator entry point.

StreamWriterOptions configures the StreamWriter-based StreamFactory overloads. It exposes AutoFlush, BufferSize (default 1024), FormatProvider (default CultureInfo.InvariantCulture), and NewLine (default Environment.NewLine) in addition to the Encoding and Preamble members inherited from StreamEncodingOptions.

StreamCompressionOptions and AsyncStreamCompressionOptions each extend the copy-options chain with a Level property (CompressionLevel.Optimal by default) that is forwarded to the underlying compression stream constructor. Their async counterparts also carry a CancellationToken through AsyncOptions.

TextReaderDecoratorExtensions.CopyToAsync asynchronously reads the enclosed TextReader and writes every chunk to a caller-supplied TextWriter using a configurable character buffer (default 81920). It validates that the buffer size is greater than zero and throws ArgumentNullException for null arguments before any reading begins.

Basic usage

using System.IO;
using Codebelt.Extensions.Xunit;
using Cuemon;
using Cuemon.IO;
using Xunit;

namespace MyProject.Tests;

public class StreamFactoryUsageTest : Test
{
    public StreamFactoryUsageTest(ITestOutputHelper output) : base(output)
    {
    }

    [Fact]
    public void Create_ShouldBuildStreamFromDelegateAndDecodeAsString()
    {
        var entries = new[] { "id,name,score", "1,alice,95", "2,bob,88" };

        var stream = StreamFactory.Create((w, lines) =>
        {
            foreach (var line in lines)
                w.WriteLine(line);
        }, entries);

        var csv = Decorator.Enclose(stream).ToEncodedString();

        Assert.Contains("alice,95", csv);
        Assert.Contains("bob,88", csv);
        TestOutput.WriteLine(csv);
    }
}

Use this pattern when code must produce a Stream from in-process data — such as a formatted report, serialized payload, or any text written via StreamWriter — and the result needs to be passed downstream without intermediate file I/O. It matters because StreamFactory encapsulates the MemoryStream setup, encoding configuration, BOM handling, and position reset in one call, while ToEncodedString on the resulting decorator handles encoding detection and preamble stripping without requiring callers to manage readers manually.

Installation

dotnet add package Cuemon.IO

Usage guidance

Adopt Cuemon.IO when code constructs streams from in-memory data via delegates, needs uniform compression and decompression across GZip, Deflate, and Brotli using a shared decorator API, or requires consistent encoding and preamble control across synchronous and asynchronous read paths. If the only requirement is converting a string or byte[] to a Stream, the extension methods in Cuemon.Extensions.IO do that directly without needing the factory or options hierarchy. For compression scenarios where the algorithm is fixed and performance is the primary concern, wrapping the BCL compression types directly may be simpler than routing through the decorator pattern.

Family packages