Move zstd wrapping code to BizHawk.Common

thanks delegate*
This commit is contained in:
CasualPokePlayer 2024-04-30 22:44:06 -07:00
parent f0a2ea1ed2
commit c2f549d02c
10 changed files with 207 additions and 140 deletions

View File

@ -2,13 +2,13 @@
using System.IO;
using System.IO.Compression;
using BizHawk.Emulation.Common;
using BizHawk.Common;
namespace BizHawk.Client.Common
{
public class FrameworkZipWriter : IZipWriter
{
private ZipArchive _archive;
private ZipArchive _archive;
private Zstd _zstd;
private readonly CompressionLevel _level;
private readonly int _zstdCompressionLevel;
@ -22,11 +22,11 @@ namespace BizHawk.Client.Common
else if (compressionLevel < 5)
_level = CompressionLevel.Fastest;
else
_level = CompressionLevel.Optimal;
_zstd = new();
// compressionLevel ranges from 0 to 9
// normal compression level range for zstd is 1 to 19
_level = CompressionLevel.Optimal;
_zstd = new();
// compressionLevel ranges from 0 to 9
// normal compression level range for zstd is 1 to 19
_zstdCompressionLevel = compressionLevel * 2 + 1;
}
@ -35,13 +35,13 @@ namespace BizHawk.Client.Common
using var stream = _archive.CreateEntry(name, _level).Open();
if (zstdCompress)
{
using var z = _zstd.CreateZstdCompressionStream(stream, _zstdCompressionLevel);
callback(z);
{
using var z = _zstd.CreateZstdCompressionStream(stream, _zstdCompressionLevel);
callback(z);
}
else
{
callback(stream);
{
callback(stream);
}
}
@ -54,9 +54,9 @@ namespace BizHawk.Client.Common
}
if (_zstd != null)
{
_zstd.Dispose();
_zstd = null;
{
_zstd.Dispose();
_zstd = null;
}
}
}

View File

@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Client.Common
{

View File

@ -4,7 +4,7 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using BizHawk.Emulation.Common;
using BizHawk.Common;
namespace BizHawk.Client.Common
{
@ -91,13 +91,13 @@ namespace BizHawk.Client.Common
ret._zip = new ZipArchive(new FileStream(filename, FileMode.Open, FileAccess.Read), ZipArchiveMode.Read);
ret.PopulateEntries();
if (isMovieLoad)
{
if (!ret.GetLump(BinaryStateLump.ZipVersion, false, ret.ReadZipVersion, false))
{
// movies before 1.0.2 did not include the BizState 1.0 file, don't strictly error in this case
ret._ver = new Version(1, 0, 0);
Console.WriteLine("Read a zipstate of version {0}", ret._ver);
}
{
if (!ret.GetLump(BinaryStateLump.ZipVersion, false, ret.ReadZipVersion, false))
{
// movies before 1.0.2 did not include the BizState 1.0 file, don't strictly error in this case
ret._ver = new Version(1, 0, 0);
Console.WriteLine("Read a zipstate of version {0}", ret._ver);
}
}
else if (!ret.GetLump(BinaryStateLump.ZipVersion, false, ret.ReadZipVersion, false))
{
@ -123,16 +123,16 @@ namespace BizHawk.Client.Common
{
if (_entriesByName.TryGetValue(lump.ReadName, out var e))
{
using var zs = e.Open();
using var zs = e.Open();
if (isZstdCompressed && _ver.Build > 1)
{
using var z = _zstd.CreateZstdDecompressionStream(zs);
callback(z, e.Length);
}
else
{
callback(zs, e.Length);
{
callback(zs, e.Length);
}
return true;

View File

@ -0,0 +1,131 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace BizHawk.Common
{
public static unsafe class LibZstd
{
static LibZstd()
{
var resolver = new DynamicLibraryImportResolver(
OSTailoredCode.IsUnixHost ? "libzstd.so.1" : "libzstd.dll", hasLimitedLifetime: false);
ZSTD_isError = (delegate* unmanaged[Cdecl]<nuint, uint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_isError));
ZSTD_getErrorName = (delegate* unmanaged[Cdecl]<nuint, IntPtr>)resolver.GetProcAddrOrThrow(nameof(ZSTD_getErrorName));
ZSTD_minCLevel = (delegate* unmanaged[Cdecl]<int>)resolver.GetProcAddrOrThrow(nameof(ZSTD_minCLevel));
ZSTD_maxCLevel = (delegate* unmanaged[Cdecl]<int>)resolver.GetProcAddrOrThrow(nameof(ZSTD_maxCLevel));
ZSTD_createCStream = (delegate* unmanaged[Cdecl]<IntPtr>)resolver.GetProcAddrOrThrow(nameof(ZSTD_createCStream));
ZSTD_freeCStream = (delegate* unmanaged[Cdecl]<IntPtr, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_freeCStream));
ZSTD_initCStream = (delegate* unmanaged[Cdecl]<IntPtr, int, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_initCStream));
ZSTD_compressStream = (delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, StreamBuffer*, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_compressStream));
ZSTD_flushStream = (delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_flushStream));
ZSTD_endStream = (delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_endStream));
ZSTD_createDStream = (delegate* unmanaged[Cdecl]<IntPtr>)resolver.GetProcAddrOrThrow(nameof(ZSTD_createDStream));
ZSTD_freeDStream = (delegate* unmanaged[Cdecl]<IntPtr, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_freeDStream));
ZSTD_initDStream = (delegate* unmanaged[Cdecl]<IntPtr, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_initDStream));
ZSTD_decompressStream = (delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, StreamBuffer*, nuint>)resolver.GetProcAddrOrThrow(nameof(ZSTD_decompressStream));
}
private static readonly delegate* unmanaged[Cdecl]<nuint, uint> ZSTD_isError;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint IsError(nuint code) => ZSTD_isError(code);
private static readonly delegate* unmanaged[Cdecl]<nuint, IntPtr> ZSTD_getErrorName;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr GetErrorName(nuint code) => ZSTD_getErrorName(code);
private static readonly delegate* unmanaged[Cdecl]<int> ZSTD_minCLevel;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int MinCLevel() => ZSTD_minCLevel();
private static readonly delegate* unmanaged[Cdecl]<int> ZSTD_maxCLevel;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int MaxCLevel() => ZSTD_maxCLevel();
[StructLayout(LayoutKind.Sequential)]
public struct StreamBuffer
{
public IntPtr Ptr;
public nuint Size;
public nuint Pos;
}
private static readonly delegate* unmanaged[Cdecl]<IntPtr> ZSTD_createCStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr CreateCStream() => ZSTD_createCStream();
private static readonly delegate* unmanaged[Cdecl]<IntPtr, nuint> ZSTD_freeCStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint FreeCStream(IntPtr zcs) => ZSTD_freeCStream(zcs);
private static readonly delegate* unmanaged[Cdecl]<IntPtr, int, nuint> ZSTD_initCStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint InitCStream(IntPtr zcs, int compressionLevel) => ZSTD_initCStream(zcs, compressionLevel);
private static readonly delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, StreamBuffer*, nuint> ZSTD_compressStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint CompressStream(IntPtr zcs, ref StreamBuffer output, ref StreamBuffer input)
{
fixed (StreamBuffer* outputPtr = &output, inputPtr = &input)
{
return ZSTD_compressStream(zcs, outputPtr, inputPtr);
}
}
private static readonly delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, nuint> ZSTD_flushStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint FlushStream(IntPtr zcs, ref StreamBuffer output)
{
fixed (StreamBuffer* outputPtr = &output)
{
return ZSTD_flushStream(zcs, outputPtr);
}
}
private static readonly delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, nuint> ZSTD_endStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint EndStream(IntPtr zcs, ref StreamBuffer output)
{
fixed (StreamBuffer* outputPtr = &output)
{
return ZSTD_endStream(zcs, outputPtr);
}
}
private static readonly delegate* unmanaged[Cdecl]<IntPtr> ZSTD_createDStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IntPtr CreateDStream() => ZSTD_createDStream();
private static readonly delegate* unmanaged[Cdecl]<IntPtr, nuint> ZSTD_freeDStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint FreeDStream(IntPtr zds) => ZSTD_freeDStream(zds);
private static readonly delegate* unmanaged[Cdecl]<IntPtr, nuint> ZSTD_initDStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint InitDStream(IntPtr zds) => ZSTD_initDStream(zds);
private static readonly delegate* unmanaged[Cdecl]<IntPtr, StreamBuffer*, StreamBuffer*, nuint> ZSTD_decompressStream;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static nuint DecompressStream(IntPtr zds, ref StreamBuffer output, ref StreamBuffer input)
{
fixed (StreamBuffer* outputPtr = &output, inputPtr = &input)
{
return ZSTD_decompressStream(zds, outputPtr, inputPtr);
}
}
}
}

View File

@ -2,10 +2,9 @@
using System.IO;
using System.Runtime.InteropServices;
using BizHawk.BizInvoke;
using BizHawk.Common;
// ReSharper disable MemberCanBePrivate.Local
namespace BizHawk.Emulation.Common
namespace BizHawk.Common
{
public sealed class Zstd : IDisposable
{
@ -14,11 +13,11 @@ namespace BizHawk.Emulation.Common
public readonly IntPtr Zcs;
public readonly byte[] InputBuffer;
public readonly GCHandle InputHandle;
public GCHandle InputHandle;
public LibZstd.StreamBuffer Input;
public readonly byte[] OutputBuffer;
public readonly GCHandle OutputHandle;
public GCHandle OutputHandle;
public LibZstd.StreamBuffer Output;
// TODO: tweak these sizes
@ -32,7 +31,7 @@ namespace BizHawk.Emulation.Common
public ZstdCompressionStreamContext()
{
Zcs = _lib.ZSTD_createCStream();
Zcs = LibZstd.CreateCStream();
InputBuffer = new byte[INPUT_BUFFER_SIZE];
InputHandle = GCHandle.Alloc(InputBuffer, GCHandleType.Pinned);
@ -57,13 +56,13 @@ namespace BizHawk.Emulation.Common
InUse = false;
}
private bool _disposed = false;
private bool _disposed;
public void Dispose()
{
if (!_disposed)
{
_lib.ZSTD_freeCStream(Zcs);
LibZstd.FreeCStream(Zcs);
InputHandle.Free();
OutputHandle.Free();
_disposed = true;
@ -77,7 +76,7 @@ namespace BizHawk.Emulation.Common
throw new InvalidOperationException("Cannot init context still in use!");
}
_lib.ZSTD_initCStream(Zcs, compressionLevel);
LibZstd.InitCStream(Zcs, compressionLevel);
Input.Size = Input.Pos = Output.Pos = 0;
InUse = true;
}
@ -94,7 +93,7 @@ namespace BizHawk.Emulation.Common
_ctx = ctx;
}
private bool _disposed = false;
private bool _disposed;
protected override void Dispose(bool disposing)
{
@ -103,7 +102,7 @@ namespace BizHawk.Emulation.Common
Flush();
while (true)
{
var n = _lib.ZSTD_endStream(_ctx.Zcs, ref _ctx.Output);
var n = LibZstd.EndStream(_ctx.Zcs, ref _ctx.Output);
CheckError(n);
InternalFlush();
if (n == 0)
@ -146,7 +145,7 @@ namespace BizHawk.Emulation.Common
{
while (_ctx.Input.Pos < _ctx.Input.Size)
{
CheckError(_lib.ZSTD_compressStream(_ctx.Zcs, ref _ctx.Output, ref _ctx.Input));
CheckError(LibZstd.CompressStream(_ctx.Zcs, ref _ctx.Output, ref _ctx.Input));
while (true)
{
if (_ctx.Output.Pos == ZstdCompressionStreamContext.OUTPUT_BUFFER_SIZE)
@ -154,7 +153,7 @@ namespace BizHawk.Emulation.Common
InternalFlush();
}
var n = _lib.ZSTD_flushStream(_ctx.Zcs, ref _ctx.Output);
var n = LibZstd.FlushStream(_ctx.Zcs, ref _ctx.Output);
CheckError(n);
if (n == 0)
{
@ -188,7 +187,7 @@ namespace BizHawk.Emulation.Common
var n = Math.Min(count, (int)(ZstdCompressionStreamContext.INPUT_BUFFER_SIZE - _ctx.Input.Size));
Marshal.Copy(buffer, offset, _ctx.Input.Ptr + (int)_ctx.Input.Size, n);
offset += n;
_ctx.Input.Size += (ulong)n;
_ctx.Input.Size += (uint)n;
count -= n;
}
}
@ -199,11 +198,11 @@ namespace BizHawk.Emulation.Common
public readonly IntPtr Zds;
public readonly byte[] InputBuffer;
public readonly GCHandle InputHandle;
public GCHandle InputHandle;
public LibZstd.StreamBuffer Input;
public readonly byte[] OutputBuffer;
public readonly GCHandle OutputHandle;
public GCHandle OutputHandle;
public LibZstd.StreamBuffer Output;
// TODO: tweak these sizes
@ -217,7 +216,7 @@ namespace BizHawk.Emulation.Common
public ZstdDecompressionStreamContext()
{
Zds = _lib.ZSTD_createDStream();
Zds = LibZstd.CreateDStream();
InputBuffer = new byte[INPUT_BUFFER_SIZE];
InputHandle = GCHandle.Alloc(InputBuffer, GCHandleType.Pinned);
@ -242,13 +241,13 @@ namespace BizHawk.Emulation.Common
InUse = false;
}
private bool _disposed = false;
private bool _disposed;
public void Dispose()
{
if (!_disposed)
{
_lib.ZSTD_freeDStream(Zds);
LibZstd.FreeDStream(Zds);
InputHandle.Free();
OutputHandle.Free();
_disposed = true;
@ -262,7 +261,7 @@ namespace BizHawk.Emulation.Common
throw new InvalidOperationException("Cannot init context still in use!");
}
_lib.ZSTD_initDStream(Zds);
LibZstd.InitDStream(Zds);
Input.Size = Input.Pos = Output.Pos = 0;
InUse = true;
}
@ -279,7 +278,7 @@ namespace BizHawk.Emulation.Common
_ctx = ctx;
}
private bool _disposed = false;
private bool _disposed;
protected override void Dispose(bool disposing)
{
@ -313,7 +312,7 @@ namespace BizHawk.Emulation.Common
public override void Flush()
=> throw new NotImplementedException();
private ulong _outputConsumed = 0;
private ulong _outputConsumed;
public override int Read(byte[] buffer, int offset, int count)
{
@ -322,12 +321,12 @@ namespace BizHawk.Emulation.Common
{
var inputConsumed = _baseStream.Read(_ctx.InputBuffer,
(int)_ctx.Input.Size, (int)(ZstdDecompressionStreamContext.INPUT_BUFFER_SIZE - _ctx.Input.Size));
_ctx.Input.Size += (ulong)inputConsumed;
_ctx.Input.Size += (uint)inputConsumed;
// avoid interop in case compression cannot be done
if (_ctx.Output.Pos < ZstdDecompressionStreamContext.OUTPUT_BUFFER_SIZE
&& _ctx.Input.Pos < _ctx.Input.Size)
{
CheckError(_lib.ZSTD_decompressStream(_ctx.Zds, ref _ctx.Output, ref _ctx.Input));
CheckError(LibZstd.DecompressStream(_ctx.Zds, ref _ctx.Output, ref _ctx.Input));
}
var outputToConsume = Math.Min(n, (int)(_ctx.Output.Pos - _outputConsumed));
Marshal.Copy(_ctx.Output.Ptr + (int)_outputConsumed, buffer, offset, outputToConsume);
@ -338,7 +337,8 @@ namespace BizHawk.Emulation.Common
if (_outputConsumed == ZstdDecompressionStreamContext.OUTPUT_BUFFER_SIZE)
{
// all the buffer is consumed, kick these back to the beginning
_ctx.Output.Pos = _outputConsumed = 0;
_outputConsumed = 0;
_ctx.Output.Pos = 0;
}
if (_ctx.Input.Pos == ZstdDecompressionStreamContext.INPUT_BUFFER_SIZE)
@ -368,26 +368,19 @@ namespace BizHawk.Emulation.Common
=> throw new NotImplementedException();
}
private static readonly LibZstd _lib;
public static int MinCompressionLevel { get; }
public static int MaxCompressionLevel { get; }
static Zstd()
{
var resolver = new DynamicLibraryImportResolver(
OSTailoredCode.IsUnixHost ? "libzstd.so.1" : "libzstd.dll", hasLimitedLifetime: false);
_lib = BizInvoker.GetInvoker<LibZstd>(resolver, CallingConventionAdapters.Native);
MinCompressionLevel = _lib.ZSTD_minCLevel();
MaxCompressionLevel = _lib.ZSTD_maxCLevel();
MinCompressionLevel = LibZstd.MinCLevel();
MaxCompressionLevel = LibZstd.MaxCLevel();
}
private ZstdCompressionStreamContext? _compressionStreamContext;
private ZstdDecompressionStreamContext? _decompressionStreamContext;
private bool _disposed = false;
private bool _disposed;
public void Dispose()
{
@ -399,11 +392,11 @@ namespace BizHawk.Emulation.Common
}
}
private static void CheckError(ulong code)
private static void CheckError(nuint code)
{
if (_lib.ZSTD_isError(code) != 0)
if (LibZstd.IsError(code) != 0)
{
throw new Exception($"ZSTD ERROR: {Marshal.PtrToStringAnsi(_lib.ZSTD_getErrorName(code))}");
throw new Exception($"ZSTD ERROR: {Marshal.PtrToStringAnsi(LibZstd.GetErrorName(code))}");
}
}

View File

@ -1,62 +0,0 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.BizInvoke;
namespace BizHawk.Emulation.Common
{
public abstract class LibZstd
{
private const CallingConvention cc = CallingConvention.Cdecl;
[BizImport(cc)]
public abstract uint ZSTD_isError(ulong code);
[BizImport(cc)]
public abstract IntPtr ZSTD_getErrorName(ulong code);
[BizImport(cc)]
public abstract int ZSTD_minCLevel();
[BizImport(cc)]
public abstract int ZSTD_maxCLevel();
[StructLayout(LayoutKind.Sequential)]
public struct StreamBuffer
{
public IntPtr Ptr;
public ulong Size;
public ulong Pos;
}
[BizImport(cc)]
public abstract IntPtr ZSTD_createCStream();
[BizImport(cc)]
public abstract ulong ZSTD_freeCStream(IntPtr zcs);
[BizImport(cc)]
public abstract ulong ZSTD_initCStream(IntPtr zcs, int compressionLevel);
[BizImport(cc)]
public abstract ulong ZSTD_compressStream(IntPtr zcs, ref StreamBuffer output, ref StreamBuffer input);
[BizImport(cc)]
public abstract ulong ZSTD_flushStream(IntPtr zcs, ref StreamBuffer output);
[BizImport(cc)]
public abstract ulong ZSTD_endStream(IntPtr zcs, ref StreamBuffer output);
[BizImport(cc)]
public abstract IntPtr ZSTD_createDStream();
[BizImport(cc)]
public abstract ulong ZSTD_freeDStream(IntPtr zds);
[BizImport(cc)]
public abstract ulong ZSTD_initDStream(IntPtr zds);
[BizImport(cc)]
public abstract ulong ZSTD_decompressStream(IntPtr zds, ref StreamBuffer output, ref StreamBuffer input);
}
}

View File

@ -1,11 +1,13 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Properties;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Properties;
namespace BizHawk.Emulation.Cores.Computers.AmstradCPC
{
/// <summary>

View File

@ -1,10 +1,12 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Properties;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Properties;
using BizHawk.Emulation.Cores.Components;
namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum

View File

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Properties;

View File

@ -1,10 +1,11 @@
using BizHawk.Common;
using BizHawk.BizInvoke;
using BizHawk.Emulation.Common;
using System;
using System;
using System.IO;
using System.Runtime.InteropServices;
using BizHawk.Common;
using BizHawk.BizInvoke;
using BizHawk.Emulation.Common;
using static BizHawk.Emulation.Cores.Waterbox.WaterboxHostNative;
namespace BizHawk.Emulation.Cores.Waterbox