This project is read-only.
Sometimes all you need to compress a byte array and decompress it later. You do not want to handle all the memory buffer allocations. Two methods have been added to LZ4Codec (not released yet).
  • byte[] Wrap(byte[] buffer) - wraps the byte buffer
  • byte[] Unwrap(byte[] buffer) - unwraps the compressed byte buffer

Note, for now it is using regular compression (not HC). Just replace Encode(...) with EncodeHC(...) if you need HC variant.

#region Envelope

private const int WRAP_OFFSET_0 = 0;
private const int WRAP_OFFSET_4 = sizeof(int);
private const int WRAP_OFFSET_8 = 2 * sizeof(int);
private const int WRAP_LENGTH = WRAP_OFFSET_8;

private static void Poke4(byte[] buffer, int offset, uint value)
{
    buffer[offset + 0] = (byte)value;
    buffer[offset + 1] = (byte)(value >> 8);
    buffer[offset + 2] = (byte)(value >> 16);
    buffer[offset + 3] = (byte)(value >> 24);
}

private static uint Peek4(byte[] buffer, int offset)
{
    // NOTE: It's faster than BitConverter.ToUInt32 (suprised? me too)
    return
        ((uint)buffer[offset]) |
        ((uint)buffer[offset + 1] << 8) |
        ((uint)buffer[offset + 2] << 16) |
        ((uint)buffer[offset + 3] << 24);
}

public static byte[] Wrap(
    byte[] inputBuffer, int inputOffset = 0, int inputLength = int.MaxValue)
{
    inputLength = Math.Min(inputBuffer.Length - inputOffset, inputLength);
    if (inputLength < 0)
        throw new ArgumentException("inputBuffer size of inputLength is invalid");
    if (inputLength == 0)
        return new byte[WRAP_LENGTH];

    var outputLength = inputLength; // MaximumOutputLength(inputLength);
    var outputBuffer = new byte[outputLength];
    outputLength = Encode(
        inputBuffer, inputOffset, inputLength, outputBuffer, 0, outputLength);

    byte[] result;

    if (outputLength >= inputLength || outputLength == 0)
    {
        result = new byte[inputLength + WRAP_LENGTH];
        Poke4(result, WRAP_OFFSET_0, (uint)inputLength);
        Poke4(result, WRAP_OFFSET_4, (uint)inputLength);
        Buffer.BlockCopy(inputBuffer, inputOffset, result, WRAP_OFFSET_8, inputLength);
    }
    else
    {
        result = new byte[outputLength + WRAP_LENGTH];
        Poke4(result, WRAP_OFFSET_0, (uint)inputLength);
        Poke4(result, WRAP_OFFSET_4, (uint)outputLength);
        Buffer.BlockCopy(outputBuffer, 0, result, WRAP_OFFSET_8, outputLength);
    }

    return result;
}

public static byte[] Unwrap(
    byte[] inputBuffer, int inputOffset = 0)
{
    var inputLength = inputBuffer.Length - inputOffset;
    if (inputLength < WRAP_LENGTH)
        throw new ArgumentException("inputBuffer size is invalid");

    var outputLength = (int)Peek4(inputBuffer, inputOffset + WRAP_OFFSET_0);
    inputLength = (int)Peek4(inputBuffer, inputOffset + WRAP_OFFSET_4);
    if (inputLength > inputBuffer.Length - inputOffset - WRAP_LENGTH)
        throw new ArgumentException("inputBuffer size is invalid or has been corrupted");

    byte[] result;

    if (inputLength >= outputLength)
    {
        result = new byte[inputLength];
        Buffer.BlockCopy(
            inputBuffer, inputOffset + WRAP_OFFSET_8, 
            result, 0, inputLength);
    }
    else
    {
        result = new byte[outputLength];
        Decode(
            inputBuffer, inputOffset + WRAP_OFFSET_8, inputLength, 
            result, 0, outputLength, 
            true);
    }

    return result;
}

#endregion

Last edited Apr 19, 2014 at 12:29 AM by Krashan, version 8

Comments

azasisgod Sep 28, 2014 at 2:50 AM 
Can this be used when reading/writing chunks of let's say 2048 bytes at a time, like in streaming?
Anyone care to post an example?

Thanks.

azasisgod Sep 28, 2014 at 2:24 AM 
Could it be possible to implement the above in LZ4mm.dll ?

Naster Sep 18, 2014 at 5:59 PM 
At first, it's not clear what it really does without reading the code.

Theses functions remove the obligation to manually store the uncompressed length in the output buffer.
The output byte array contains :
1 - Uncompressed length 4 bytes
2 - Compressed length 4 bytes
3 - Compressed data

M'I right?
Why do we need to know the uncompressed length for decompression except for validation purpose?

Thx, you do a really good job!