Skip to content

Commit

Permalink
Implement ZraFullDecompressionStream and
Browse files Browse the repository at this point in the history
- Use rented arrays in ZraCompressionStream rather than creating a new byte array
- Updated ZRA to new version with bug fixes
- Cleaned some code
  • Loading branch information
Xpl0itR committed Sep 11, 2020
1 parent dd8dd0a commit 664f96a
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 24 deletions.
3 changes: 2 additions & 1 deletion ZRA.NET/LibZra.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,9 @@ public static class LibZra
* <param name="fullDecompressor">A pointer to the ZraFullDecompressor object</param>
* <param name="outputBuffer"> The buffer to write the decompressed output into</param>
* <param name="outputCapacity"> The size of the output buffer, it should be at least <see cref="ZraGetFrameSizeWithHeader"/> bytes</param>
* <param name="outputSize"> The size of the uncompressed data that has been written into the buffer, this will be 0 at the end of compression</param>
* <returns>A <see cref="ZraStatus"/> structure which contains the result code of the completed operation.</returns>
*/
[DllImport("libzra")] public static extern ZraStatus ZraDecompressWithFullDecompressor(IntPtr fullDecompressor, byte[] outputBuffer, ulong outputCapacity);
[DllImport("libzra")] public static extern ZraStatus ZraDecompressWithFullDecompressor(IntPtr fullDecompressor, byte[] outputBuffer, ulong outputCapacity, out ulong outputSize);
}
}
10 changes: 7 additions & 3 deletions ZRA.NET/Streaming/ZraCompressionStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright © 2020 ZRA Contributors (https://github.com/zraorg)

using System;
using System.Buffers;
using System.IO;

namespace ZRA.NET.Streaming
Expand Down Expand Up @@ -59,10 +60,12 @@ public override void Write(byte[] buffer, int offset, int count)
{
if (offset > 0) Array.Copy(buffer, offset, buffer, 0, count);

byte[] outputBuffer = new byte[LibZra.ZraGetOutputBufferSizeWithCompressor(_compressor, (ulong)buffer.LongLength)];
byte[] outputBuffer = ArrayPool<byte>.Shared.Rent((int)LibZra.ZraGetOutputBufferSizeWithCompressor(_compressor, (ulong)buffer.LongLength));
LibZra.ZraCompressWithCompressor(_compressor, buffer, (ulong)count, outputBuffer, out ulong outputSize).ThrowIfError();

_outStream.Write(outputBuffer, 0, (int)outputSize);

ArrayPool<byte>.Shared.Return(outputBuffer);
}

/**
Expand All @@ -73,12 +76,13 @@ public override void Write(byte[] buffer, int offset, int count)
*/
protected override void Dispose(bool disposing)
{
byte[] headerBuffer = new byte[_headerLength];
byte[] headerBuffer = ArrayPool<byte>.Shared.Rent((int)_headerLength);
LibZra.ZraGetHeaderWithCompressor(_compressor, headerBuffer).ThrowIfError();

_outStream.Position = _startingPos;
_outStream.Write(headerBuffer);
_outStream.Write(headerBuffer, 0, (int)_headerLength);

ArrayPool<byte>.Shared.Return(headerBuffer);
LibZra.ZraDeleteCompressor(_compressor);

if (!_leaveOpen)
Expand Down
17 changes: 6 additions & 11 deletions ZRA.NET/Streaming/ZraDecompressionStream.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: BSD-3-Clause
// SPDX-License-Identifier: BSD-3-Clause
// Copyright © 2020 ZRA Contributors (https://github.com/zraorg)

using System;
Expand All @@ -15,6 +15,7 @@ public class ZraDecompressionStream : Stream
public override long Position { get; set; }

private readonly IntPtr _decompressor;
private readonly IntPtr _header;
private readonly Stream _baseStream;
private readonly bool _leaveOpen;

Expand Down Expand Up @@ -46,9 +47,12 @@ public ZraDecompressionStream(Stream baseStream, ulong maxCacheSize = 1024 * 102
}

LibZra.ZraCreateDecompressor(out _decompressor, _readFunction, maxCacheSize).ThrowIfError();
Length = (long)LibZra.ZraGetUncompressedSizeWithHeader(LibZra.ZraGetHeaderWithDecompressor(_decompressor));
_header = LibZra.ZraGetHeaderWithDecompressor(_decompressor);
Length = (long)LibZra.ZraGetUncompressedSizeWithHeader(_header);
}

public byte[] GetMetaSection() => Zra.GetZraMetaSection(_header);

/**
* <summary>
* Reads a sequence of bytes from the current stream and decompresses it, then advances the position within the stream by the number of bytes read.
Expand All @@ -75,15 +79,6 @@ public override int Read(byte[] buffer, int offset, int count)
return count;
}

/**
*
*/
public byte[] GetZraMetaSection()
{
IntPtr headerPtr = LibZra.ZraGetHeaderWithDecompressor(_decompressor);
return Zra.GetZraMetaSection(headerPtr);
}

public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
Expand Down
118 changes: 118 additions & 0 deletions ZRA.NET/Streaming/ZraFullDecompressionStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright © 2020 ZRA Contributors (https://github.com/zraorg)

using System;
using System.IO;
using System.Threading.Tasks;

namespace ZRA.NET.Streaming
{
public class ZraFullDecompressionStream : Stream
{
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length { get; }

public override long Position
{
get => _position;
set => throw new NotSupportedException();
}

private long _position;

private readonly IntPtr _fullDecompressor;
private readonly IntPtr _header;
private readonly Stream _baseStream;
private readonly bool _leaveOpen;

// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly LibZra.ReadFunction _readFunction;

/**
* <summary>
* Creates a <see cref="ZraFullDecompressionStream"/> used to stream-decompress data.
* This stream is non-seekable.
* </summary>
* <param name="baseStream">The underlying stream where the compressed data will be read from.</param>
* <param name="leaveOpen">Whether to leave the underlying stream open or not when the <see cref="ZraFullDecompressionStream"/> is disposed.</param>
* <returns><see cref="ZraFullDecompressionStream"/></returns>
*/
public ZraFullDecompressionStream(Stream baseStream, bool leaveOpen = false)
{
_baseStream = baseStream;
_leaveOpen = leaveOpen;

unsafe
{
_readFunction = (ulong offset, ulong size, byte* buffer) =>
{
_baseStream.Seek((long)offset, SeekOrigin.Begin);
_baseStream.Read(new Span<byte>(buffer, (int)size));
};
}

LibZra.ZraCreateFullDecompressor(out _fullDecompressor, _readFunction).ThrowIfError();
_header = LibZra.ZraGetHeaderWithFullDecompressor(_fullDecompressor);
Length = (long)LibZra.ZraGetUncompressedSizeWithHeader(_header);
}

public byte[] GetMetaSection() => Zra.GetZraMetaSection(_header);

/**
* <summary>
* Reads a sequence of bytes from the current stream and decompresses it, then advances the position within the stream by the number of bytes read.
* </summary>
* <param name="buffer">An array of bytes. When this method returns, the buffer contains the bytes decompressed from the current source.</param>
* <param name="offset">This stream is non-seekable, therefore a <see cref="ArgumentOutOfRangeException"/> is thrown if offset is non-zero.</param>
* <param name="count">The maximum number of bytes to be read and decompressed from the current stream.</param>
* <returns>The total number of bytes read and decompressed into the buffer.
* This can be less than the number of bytes requested if that many bytes are not currently available,
* or zero (0) if the end of the stream has been reached.</returns>
*/
public override int Read(byte[] buffer, int offset, int count)
{
if (offset != 0) throw new ArgumentOutOfRangeException(nameof(offset), "This stream is non-seekable, therefore passing a value to the offset parameter is not allowed.");

LibZra.ZraDecompressWithFullDecompressor(_fullDecompressor, buffer, (ulong)count, out ulong outputSize).ThrowIfError();

_position += (long)outputSize;
return (int)outputSize;
}

public new void CopyTo(Stream destination)
{
ulong bufferSize = LibZra.ZraGetFrameSizeWithHeader(_header);
CopyTo(destination, (int)bufferSize);
}

public new Task CopyToAsync(Stream destination)
{
ulong bufferSize = LibZra.ZraGetFrameSizeWithHeader(_header);
return CopyToAsync(destination, (int)bufferSize);
}

protected override void Dispose(bool disposing)
{
LibZra.ZraDeleteFullDecompressor(_fullDecompressor);

if (!_leaveOpen)
_baseStream?.Dispose();

base.Dispose(disposing);
}

/**<summary>Not Supported.</summary>*/
public override void Flush() => throw new NotSupportedException();

/**<summary>Not Supported.</summary>*/
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

/**<summary>Not Supported.</summary>*/
public override void SetLength(long value) => throw new NotSupportedException();

/**<summary>Not Supported.</summary>*/
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
}
}
2 changes: 1 addition & 1 deletion ZRA.NET/ZRA.NET.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>8.0</LangVersion>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<Version>0.0.2-Beta</Version>
<Version>0.0.3-Beta</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

Expand Down
22 changes: 14 additions & 8 deletions ZRA.NET/Zra.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ public static byte[] Compress(byte[] inBuffer, byte compressionLevel = 9, uint f
*/
public static byte[] Decompress(byte[] inBuffer)
{
LibZra.ZraCreateHeader2(out IntPtr header, inBuffer, (ulong)inBuffer.LongLength).ThrowIfError();
LibZra.ZraCreateHeader2(out IntPtr headerPtr, inBuffer, (ulong)inBuffer.LongLength).ThrowIfError();

byte[] outBuffer = new byte[LibZra.ZraGetUncompressedSizeWithHeader(header)];
byte[] outBuffer = new byte[LibZra.ZraGetUncompressedSizeWithHeader(headerPtr)];
LibZra.ZraDecompressBuffer(inBuffer, (ulong)inBuffer.LongLength, outBuffer).ThrowIfError();

LibZra.ZraDeleteHeader(header);
LibZra.ZraDeleteHeader(headerPtr);

return outBuffer;
}
Expand All @@ -59,22 +59,28 @@ public static byte[] Decompress(byte[] inBuffer)
*/
public static byte[] DecompressSection(byte[] inBuffer, ulong offset, ulong length)
{
LibZra.ZraCreateHeader2(out IntPtr header, inBuffer, (ulong)inBuffer.LongLength).ThrowIfError();
LibZra.ZraCreateHeader2(out IntPtr headerPtr, inBuffer, (ulong)inBuffer.LongLength).ThrowIfError();

byte[] outBuffer = new byte[LibZra.ZraGetUncompressedSizeWithHeader(header)];
byte[] outBuffer = new byte[LibZra.ZraGetUncompressedSizeWithHeader(headerPtr)];
LibZra.ZraDecompressRA(inBuffer, (ulong)inBuffer.LongLength, outBuffer, offset, length).ThrowIfError();

LibZra.ZraDeleteHeader(header);
LibZra.ZraDeleteHeader(headerPtr);

return outBuffer;
}

/**
*
* <param name="headerPtr"> A pointer to the ZRA header to extract the meta section from</param>
* <returns>A byte array containing the ZRA meta section.</returns>
*/
public static byte[] GetZraMetaSection(IntPtr headerPtr)
{
byte[] metaBuffer = new byte[LibZra.ZraGetMetadataSize(headerPtr)];
ulong metaSize = LibZra.ZraGetMetadataSize(headerPtr);

if (metaSize == 0)
return null;

byte[] metaBuffer = new byte[metaSize];
LibZra.ZraGetMetadata(headerPtr, metaBuffer);

return metaBuffer;
Expand Down
Binary file modified ZRA.NET/runtimes/linux-x64/native/libzra.so
Binary file not shown.
Binary file modified ZRA.NET/runtimes/osx-x64/native/libzra.dylib
Binary file not shown.
Binary file modified ZRA.NET/runtimes/win-x64/native/libzra.dll
Binary file not shown.

0 comments on commit 664f96a

Please sign in to comment.