Skip to content

Commit

Permalink
Issue #85
Browse files Browse the repository at this point in the history
  • Loading branch information
MiloszKrajewski authored Aug 15, 2023
1 parent fa8b8e0 commit ccc115c
Show file tree
Hide file tree
Showing 21 changed files with 239 additions and 125 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 1.3.6 (2023/08/15)
* FIXED: Issue #85 (sync operation in DisposeAsync)

## 1.3.5 (2023/01/06)
* NOTE: just a release without beta tag

Expand Down
6 changes: 3 additions & 3 deletions Common.targets
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project>
<PropertyGroup>
<Version>1.3.5</Version>
<AssemblyVersion>1.3.5</AssemblyVersion>
<FileVersion>1.3.5</FileVersion>
<Version>1.3.6</Version>
<AssemblyVersion>1.3.6</AssemblyVersion>
<FileVersion>1.3.6</FileVersion>
</PropertyGroup>
<PropertyGroup>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
Expand Down
4 changes: 3 additions & 1 deletion src/K4os.Compression.LZ4.Streams.Test/AsyncDecoderTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -13,6 +14,7 @@ public class AsyncDecoderTests
[Theory]
[InlineData(".corpus/dickens", "-1 -BD -B4 -BX")]
[InlineData(".corpus/mozilla", "-1 -BD -B7")]
[SuppressMessage("ReSharper", "UseAwaitUsing")]
public async Task UseDecoderWithCopyAsync(string original, string options)
{
original = Tools.FindFile(original);
Expand All @@ -23,7 +25,7 @@ public async Task UseDecoderWithCopyAsync(string original, string options)
ReferenceLZ4.Encode(options, original, compressed);

await Task.CompletedTask;

using (var src = LZ4Stream.Decode(File.OpenRead(compressed)))
using (var dst = File.Create(decompressed))
{
Expand Down
109 changes: 109 additions & 0 deletions src/K4os.Compression.LZ4.Streams.Test/AsyncRoundtripTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#if NET6_0_OR_GREATER

using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using TestHelpers;
using Xunit;

namespace K4os.Compression.LZ4.Streams.Test;

public class AsyncRoundtripTests
{
[Fact]
public async Task WriteThenRead()
{
var data = new byte[1337];
Lorem.Fill(data, 0, data.Length);

using var temp = TempFile.Create();

{
var storage = LZ4Stream.Encode(new AsyncOnlyStream(File.Create(temp.FileName)));
await storage.WriteAsync(data);
await storage.DisposeAsync();
}

{
var storage = LZ4Stream.Decode(new AsyncOnlyStream(File.OpenRead(temp.FileName)));
var read = await storage.ReadAsync(data);
Assert.Equal(data.Length, read);
await storage.DisposeAsync();
}
}
}

public class AsyncOnlyStream: Stream
{
private readonly Stream _stream;

public AsyncOnlyStream(Stream stream) => _stream = stream;

[DoesNotReturn]
private static InvalidOperationException NotAllowed() => new("This operation is not allowed");

public override void Flush() => throw NotAllowed();

public override int Read(byte[] buffer, int offset, int count) => throw NotAllowed();
public override int Read(Span<byte> buffer) => throw NotAllowed();
public override int ReadByte() => throw NotAllowed();

public override void Write(byte[] buffer, int offset, int count) => throw NotAllowed();
public override void Write(ReadOnlySpan<byte> buffer) => throw NotAllowed();
public override void WriteByte(byte value) => throw NotAllowed();

protected override void Dispose(bool disposing) => throw NotAllowed();
public override void Close() => throw NotAllowed();

public override bool CanRead => _stream.CanRead;
public override bool CanSeek => _stream.CanSeek;
public override bool CanWrite => _stream.CanWrite;
public override long Length => _stream.Length;
public override long Position { get => _stream.Position; set => _stream.Position = value; }
public override bool CanTimeout => _stream.CanTimeout;

public override int ReadTimeout
{
get => _stream.ReadTimeout;
set => _stream.ReadTimeout = value;
}

public override int WriteTimeout
{
get => _stream.WriteTimeout;
set => _stream.WriteTimeout = value;
}

public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin);
public override void SetLength(long value) => _stream.SetLength(value);

public override ValueTask DisposeAsync() => _stream.DisposeAsync();

public override Task<int> ReadAsync(
byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
_stream.ReadAsync(buffer, offset, count, cancellationToken);

public override ValueTask<int> ReadAsync(
Memory<byte> buffer, CancellationToken cancellationToken = default) =>
_stream.ReadAsync(buffer, cancellationToken);

public override Task WriteAsync(
byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
_stream.WriteAsync(buffer, offset, count, cancellationToken);

// redirect all async methods to underlying stream
public override ValueTask WriteAsync(
ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) =>
_stream.WriteAsync(buffer, cancellationToken);

public override Task FlushAsync(CancellationToken cancellationToken) =>
_stream.FlushAsync(cancellationToken);

public override Task CopyToAsync(
Stream destination, int bufferSize, CancellationToken cancellationToken) =>
_stream.CopyToAsync(destination, bufferSize, cancellationToken);
}

#endif
107 changes: 53 additions & 54 deletions src/K4os.Compression.LZ4.Streams.Test/RoundtripTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,71 @@
using TestHelpers;
using Xunit;

namespace K4os.Compression.LZ4.Streams.Test
namespace K4os.Compression.LZ4.Streams.Test;

public class RoundtripTests
{
public class RoundtripTests
{
#if DEBUG
[Theory(Skip = "Too long")]
#else
[Theory]
[Theory]
#endif
[InlineData("-1 -BD -B4 -BX", Mem.K8)]
[InlineData("-1 -BD -B5", Mem.K8)]
[InlineData("-1 -BD -B6 -BX", Mem.K8)]
[InlineData("-1 -BD -B7", Mem.K4)]
[InlineData("-9 -BD -B4", Mem.K4)]
[InlineData("-9 -BD -B5 -BX", Mem.K4)]
[InlineData("-9 -BD -B6", Mem.K4)]
[InlineData("-9 -BD -B7 -BX", Mem.K4)]
[InlineData("-1 -B4", Mem.K4)]
[InlineData("-1 -B7", Mem.K4)]
[InlineData("-9 -B7 -BX", Mem.K4)]
[InlineData("-1 -B4 -BD", Mem.M1)]
[InlineData("-9 -B4 -BD", 1337)]
public void WholeCorpus(string options, int chunkSize)
[InlineData("-1 -BD -B4 -BX", Mem.K8)]
[InlineData("-1 -BD -B5", Mem.K8)]
[InlineData("-1 -BD -B6 -BX", Mem.K8)]
[InlineData("-1 -BD -B7", Mem.K4)]
[InlineData("-9 -BD -B4", Mem.K4)]
[InlineData("-9 -BD -B5 -BX", Mem.K4)]
[InlineData("-9 -BD -B6", Mem.K4)]
[InlineData("-9 -BD -B7 -BX", Mem.K4)]
[InlineData("-1 -B4", Mem.K4)]
[InlineData("-1 -B7", Mem.K4)]
[InlineData("-9 -B7 -BX", Mem.K4)]
[InlineData("-1 -B4 -BD", Mem.M1)]
[InlineData("-9 -B4 -BD", 1337)]
public void WholeCorpus(string options, int chunkSize)
{
var settings = Settings.ParseSettings(options);
foreach (var filename in Tools.CorpusNames)
{
var settings = Settings.ParseSettings(options);
foreach (var filename in Tools.CorpusNames)
try
{
TestRoundtrip($".corpus/{filename}", chunkSize, settings);
}
catch (Exception e)
{
try
{
TestRoundtrip($".corpus/{filename}", chunkSize, settings);
}
catch (Exception e)
{
throw new Exception(
$"Failed to process: {filename} @ {options}/{chunkSize}", e);
}
throw new Exception(
$"Failed to process: {filename} @ {options}/{chunkSize}", e);
}
}
}

[Theory]
[InlineData("reymont", "-1 -B4")]
[InlineData("mozilla", "-9 -B5")]
[InlineData("x-ray", "-12 -B7")]
public void SelectiveRoundtrip(string filename, string options)
{
var settings = Settings.ParseSettings(options);
TestRoundtrip($".corpus/{filename}", 1337, settings);
}

[Theory]
[InlineData("reymont", "-1 -B4")]
[InlineData("mozilla", "-9 -B5")]
[InlineData("x-ray", "-12 -B7")]
public void SelectiveRoundtrip(string filename, string options)
private static void TestRoundtrip(string fileName, int chunkSize, LZ4Settings settings)
{
var original = Tools.FindFile(fileName);
var encoded = Path.GetTempFileName();
var decoded = Path.GetTempFileName();

try
{
var settings = Settings.ParseSettings(options);
TestRoundtrip($".corpus/{filename}", 1337, settings);
TestedLZ4.Encode(original, encoded, chunkSize, settings);
TestedLZ4.Decode(encoded, decoded, chunkSize);
Tools.SameFiles(original, decoded);
}

private static void TestRoundtrip(string fileName, int chunkSize, LZ4Settings settings)
finally
{
var original = Tools.FindFile(fileName);
var encoded = Path.GetTempFileName();
var decoded = Path.GetTempFileName();

try
{
TestedLZ4.Encode(original, encoded, chunkSize, settings);
TestedLZ4.Decode(encoded, decoded, chunkSize);
Tools.SameFiles(original, decoded);
}
finally
{
File.Delete(encoded);
File.Delete(decoded);
}
File.Delete(encoded);
File.Delete(decoded);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ public void CallingDisposeMultipleTimesIsFine()
for (var i = 0; i < 1000; i++)
lz4.Dispose();

Assert.True(test.Disposed > 1);
// Dispose can be called more than once,
// but did not crash (that's the important part)
Assert.True(test.Disposed >= 1);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using K4os.Compression.LZ4.Streams.Abstractions;
using K4os.Compression.LZ4.Streams.Internal;
using ReadResult = K4os.Compression.LZ4.Streams.Abstractions.ReadResult;

namespace K4os.Compression.LZ4.Streams.Adapters;
Expand Down Expand Up @@ -52,17 +53,17 @@ public async Task<ReadResult<EmptyState>> ReadAsync(
if (length <= 0)
return ReadResult.Create(state);

var sequence = await ReadFromPipe(_reader, length, token);
var sequence = await ReadFromPipe(_reader, length, token).Weave();
return ReadFromSequence(_reader, sequence, buffer.AsSpan(offset, length));
}

private static async Task<ReadOnlySequence<byte>> ReadFromPipe(
PipeReader reader, int length, CancellationToken token)
{
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
var result = await reader.ReadAtLeastAsync(length, token);
var result = await reader.ReadAtLeastAsync(length, token).Weave();
#else
var result = await reader.ReadAsync(token);
var result = await reader.ReadAsync(token).Weave();
#endif
if (result.IsCanceled) ThrowPendingReadsCancelled();
return result.Buffer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading;
using System.Threading.Tasks;
using K4os.Compression.LZ4.Streams.Abstractions;
using K4os.Compression.LZ4.Streams.Internal;

namespace K4os.Compression.LZ4.Streams.Adapters;

Expand Down Expand Up @@ -33,7 +34,7 @@ public void Write(ref EmptyState state, byte[] buffer, int offset, int length)
public async Task<EmptyState> WriteAsync(
EmptyState state, byte[] buffer, int offset, int length, CancellationToken token)
{
await _writer.WriteAsync(buffer.AsMemory(offset, length), token);
await _writer.WriteAsync(buffer.AsMemory(offset, length), token).Weave();
return state;
}

Expand All @@ -55,7 +56,7 @@ public void Flush(ref EmptyState state)
/// <inheritdoc />
public async Task<EmptyState> FlushAsync(EmptyState state, CancellationToken token)
{
await _writer.FlushAsync(token);
await _writer.FlushAsync(token).Weave();
return state;
}

Expand Down
5 changes: 3 additions & 2 deletions src/K4os.Compression.LZ4.Streams/Adapters/StreamAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading;
using System.Threading.Tasks;
using K4os.Compression.LZ4.Streams.Abstractions;
using K4os.Compression.LZ4.Streams.Internal;

namespace K4os.Compression.LZ4.Streams.Adapters;

Expand Down Expand Up @@ -33,7 +34,7 @@ public int Read(
/// <inheritdoc />
public async Task<ReadResult<EmptyState>> ReadAsync(
EmptyState state, byte[] buffer, int offset, int length, CancellationToken token) =>
ReadResult.Create(state, await _stream.ReadAsync(buffer, offset, length, token));
ReadResult.Create(state, await _stream.ReadAsync(buffer, offset, length, token).Weave());

/// <inheritdoc />
public void Write(ref EmptyState state, byte[] buffer, int offset, int length) =>
Expand All @@ -45,7 +46,7 @@ public async Task<EmptyState> WriteAsync(
byte[] buffer, int offset, int length,
CancellationToken token)
{
await _stream.WriteAsync(buffer, offset, length, token);
await _stream.WriteAsync(buffer, offset, length, token).Weave();
return state;
}

Expand Down
5 changes: 3 additions & 2 deletions src/K4os.Compression.LZ4.Streams/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using K4os.Compression.LZ4.Encoders;
using K4os.Compression.LZ4.Streams.Abstractions;
using K4os.Compression.LZ4.Streams.Frames;
using K4os.Compression.LZ4.Streams.Internal;

namespace K4os.Compression.LZ4.Streams;

Expand Down Expand Up @@ -199,7 +200,7 @@ public static async Task CopyToAsync<TBufferWriter>(
while (true)
{
var span = target.GetMemory(blockSize);
var bytes = await source.ReadManyBytesAsync(span, true);
var bytes = await source.ReadManyBytesAsync(span, true).Weave();
if (bytes == 0) return;

target.Advance(bytes);
Expand Down Expand Up @@ -252,7 +253,7 @@ public static async Task<int> CopyFromAsync(
source = source.Slice(length);
if (bytes.IsEmpty) continue;

await target.WriteManyBytesAsync(bytes);
await target.WriteManyBytesAsync(bytes).Weave();
}

return total;
Expand Down
Loading

0 comments on commit ccc115c

Please sign in to comment.