Search code examples
c#memorystreamdeflatestream

How to get the length of the compressed data from DeflateStream?


The following is a simple compression method I wrote using DeflateStream:

public static int Compress(
    byte[] inputData, 
    int inputStartIndex, 
    int inputLength, 
    byte[] outputData, 
    int outputStartIndex, 
    int outputLength)
{
    if (inputData == null)
        throw new ArgumentNullException("inputData must be non-null");

    MemoryStream memStream = new MemoryStream(outputData, outputStartIndex, outputLength);

    using (DeflateStream dstream = new DeflateStream(memStream, CompressionLevel.Optimal))
    {
        dstream.Write(inputData, inputStartIndex, inputLength);
        return (int)(memStream.Position - outputStartIndex);
    }
}

What is special in this method is that I didn't use the parameter-less constructor of MemoryStream. This is because it is a high-throughput server. Array outputData is rented from ArrayPool, to be used to hold the compressed bytes, so that after I make use of it I can return it to ArrayPool.

The compression happened properly, and the compressed data is properly placed into outputData, but memStream.Position was zero, so I can't find out how many bytes have been written into the MemoryStream.

Only part of outputData is occupied by the compressed data. How do I find out the length of the compressed data?


Solution

  • MemoryStream.Position is 0 because data was not actually written there yet at the point you read Position. Instead, tell DeflateStream to leave underlying stream (MemoryStream) open, then dispose DeflateStream. At this point you can be sure it's done writing whatever it needs. Now you can read MemoryStream.Position to check how many bytes were written:

    public static int Compress(
        byte[] inputData, 
        int inputStartIndex, 
        int inputLength, 
        byte[] outputData, 
        int outputStartIndex, 
        int outputLength)
    {
        if (inputData == null)
            throw new ArgumentNullException("inputData must be non-null");
    
        using (var memStream = new MemoryStream(outputData, outputStartIndex, outputLength)) {
    
            // leave open
            using (DeflateStream dstream = new DeflateStream(memStream, CompressionLevel.Optimal, leaveOpen: true)) {
                dstream.Write(inputData, inputStartIndex, inputLength);
            }
    
            return (int) memStream.Position; // now it's not 0
        }
    }
    

    You also don't need to substract outputStartIndex, because Position is already relative to that index you passed to constructor.