Skip to content

Commit

Permalink
PR 82
Browse files Browse the repository at this point in the history
  • Loading branch information
paoloambrosio committed Jun 17, 2024
2 parents 452883d + 1df7fd0 commit c0b06de
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 44 deletions.
58 changes: 45 additions & 13 deletions LibArchive.Net/LibArchiveReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public LibArchiveReader(string[] filenames,uint blockSize=1<<20) : base(true)

private void Throw()
{
throw new ApplicationException($"{Marshal.PtrToStringUTF8(archive_error_string(handle))}");
throw new ApplicationException(Marshal.PtrToStringUTF8(archive_error_string(handle)));
}

protected override bool ReleaseHandle()
Expand All @@ -82,11 +82,13 @@ protected override bool ReleaseHandle()
public IEnumerable<Entry> Entries()
{
int r;
while ((r=archive_read_next_header(handle, out var entry))==0)
while ((r=archive_read_next_header(handle, out var entryHandle))==0)
{
var name = Marshal.PtrToStringUTF8(archive_entry_pathname(entry));
if (name is not null)
yield return new Entry(name, handle);
var entry = Entry.Create(entryHandle, handle);
if (entry is not null)
{
yield return entry;
}
}

if (r != (int)ARCHIVE_RESULT.ARCHIVE_EOF)
Expand All @@ -95,23 +97,50 @@ public IEnumerable<Entry> Entries()

public class Entry
{
protected readonly IntPtr entry;
protected readonly IntPtr archive;

public string Name { get; }
private readonly IntPtr handle;
public FileStream Stream => new FileStream(handle);
public EntryType Type;
public FileStream Stream => new(archive);

internal Entry(string name, IntPtr handle)
public bool IsDirectory => Type == EntryType.Directory;
public bool IsRegularFile => Type == EntryType.RegularFile;

protected Entry(IntPtr entry, IntPtr archive)
{
this.Name = name;
this.handle = handle;
this.entry = entry;
this.archive = archive;
Name = Marshal.PtrToStringUTF8(archive_entry_pathname(entry)) ?? throw new ApplicationException("Unable to retrieve entry's pathname");
Type = (EntryType)archive_entry_filetype(entry);
}

internal static Entry? Create(IntPtr entry, IntPtr archive)
{
try
{
return new Entry(entry, archive);
}
catch (ApplicationException)
{
return null;
}
}
}

public enum EntryType
{
Directory = 0x4000, // AE_IFDIR
RegularFile = 0x8000 // AE_IFREG
}

public class FileStream : Stream
{
private readonly IntPtr _archive;
private readonly IntPtr archive;

internal FileStream(IntPtr archive)
{
this._archive = archive;
this.archive = archive;
}

public override void Flush()
Expand All @@ -120,7 +149,7 @@ public override void Flush()

public override int Read(byte[] buffer, int offset, int count)
{
return archive_read_data(_archive, ref MemoryMarshal.GetReference(buffer.AsSpan()[offset..]), count);
return archive_read_data(archive, ref MemoryMarshal.GetReference(buffer.AsSpan()[offset..]), count);
}

public override long Seek(long offset, SeekOrigin origin)
Expand Down Expand Up @@ -173,6 +202,9 @@ public override long Position
[DllImport("archive")]
private static extern IntPtr archive_entry_pathname(IntPtr entry);

[DllImport("archive")]
private static extern int archive_entry_filetype(IntPtr entry);

[DllImport("archive")]
private static extern int archive_read_free(IntPtr a);

Expand Down
89 changes: 58 additions & 31 deletions Test.LibArchive.Net/ReaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,81 @@
using System.Security.Cryptography;
using System.Text;
using LibArchive.Net;
using NUnit.Framework;
using static LibArchive.Net.LibArchiveReader;

namespace Test.LibArchive.Net;

public class SevenZipTests
{
private readonly SHA256 hash = SHA256.Create();

#region Setup and Tear Down

private readonly HashAlgorithm hasher;
private readonly string emptyHash;

public SevenZipTests()
{
hasher = SHA256.Create();
emptyHash = HashToString(hasher.ComputeHash(Array.Empty<byte>()));
}

[OneTimeTearDownAttribute]
public void OneTimeTearDown() {
hash.Dispose();
}
public void OneTimeTearDown()
{
hasher.Dispose();
}

#endregion

[Test]
public void Test7z()
{
using var lar = new LibArchiveReader("7ztest.7z");
foreach (var e in lar.Entries())

var extracted = lar.Entries().ToDictionary(_ => _.Name, ToExtractedEntry);

Assert.That(extracted, Is.EquivalentTo(new Dictionary<string, ExtractedEntry>
{
using var s = e.Stream;
StringBuilder sb = new(e.Name,e.Name.Length+33);
sb.Append(' ');
foreach (var d in hash.ComputeHash(s))
{
sb.Append(d.ToString("x2"));
}
Console.WriteLine(sb);
}
Assert.Pass();
{ "subdir/", new(EntryType.Directory, emptyHash) },
{ "empty", new(EntryType.RegularFile, emptyHash) },
{ "subdir/empty", new(EntryType.RegularFile, emptyHash) },
{ "1gzero", new(EntryType.RegularFile, "49-BC-20-DF-15-E4-12-A6-44-72-42-1E-13-FE-86-FF-1C-51-65-E1-8B-2A-FC-CF-16-0D-4D-C1-9F-E6-8A-14") },
{ "1krandom", new(EntryType.RegularFile, "DA-26-F3-BE-7A-9A-2D-F1-0A-49-35-87-8B-18-C8-FF-FE-2B-96-13-EA-CD-E2-C8-67-DF-8A-A2-5D-41-0D-0A") },
}));
}

[Test]
public void TestMultiRar()
{
var files = Directory.GetFiles(TestContext.CurrentContext.TestDirectory, "rartest*.rar");
Array.Sort(files);
Assert.That(files, Has.Length.EqualTo(4), "Expected 4 RAR segments");
using var rar = new LibArchiveReader(files);
foreach (var e in rar.Entries())
var files = Enumerable.Range(1, 4).Select(n => $"rartest.part0000{n}.rar").ToArray();
using var lar = new LibArchiveReader(files);

var extracted = lar.Entries().ToDictionary(_ => _.Name, ToExtractedEntry);

Assert.That(extracted, Is.EquivalentTo(new Dictionary<string, ExtractedEntry>
{
using var s = e.Stream;
StringBuilder sb = new(e.Name, e.Name.Length + 33);
sb.Append(' ');
foreach (var d in hash.ComputeHash(s))
{
sb.Append(d.ToString("x2"));
}
Console.WriteLine(sb);
}
{ "subdir", new(EntryType.Directory, emptyHash) },
{ "empty", new(EntryType.RegularFile, emptyHash) },
{ "subdir/empty", new(EntryType.RegularFile, emptyHash) },
{ "1gzero", new(EntryType.RegularFile, "8B-A9-05-B1-20-A7-C8-D7-89-0F-AB-53-3B-75-65-C9-2C-3D-30-B7-E2-98-41-DF-52-C0-CF-F3-9D-3C-F1-A2") },
{ "1krandom", new(EntryType.RegularFile, "DA-26-F3-BE-7A-9A-2D-F1-0A-49-35-87-8B-18-C8-FF-FE-2B-96-13-EA-CD-E2-C8-67-DF-8A-A2-5D-41-0D-0A") },
}));
}

#region Support code

private record ExtractedEntry(EntryType Type, string ContentHash);

private ExtractedEntry ToExtractedEntry(Entry entry) =>
new(entry.Type, ContentHash(entry));

private string ContentHash(Entry entry)
{
using var stream = entry.Stream;
return HashToString(hasher.ComputeHash(stream));
}

private static string HashToString(byte[] hash) =>
BitConverter.ToString(hash);

#endregion
}

0 comments on commit c0b06de

Please sign in to comment.