using System; using System.Runtime.InteropServices; namespace ScreenConnect; public abstract class ZStandardCoder : CriticalFinalizerDisposableObject, IMultiCallCoder, ICoder, IDisposable { private IntPtr nativeStream; private ulong inputSize; private ulong outputSize; public virtual bool AreInputOutputCursorsAlwaysSynced => false; public ZStandardCoder() { Initialize(); } private void Initialize() { nativeStream = CreateStream(); try { ThrowIfError(InitializeStream(nativeStream)); (TParam, int)[] parameterValues = GetParameterValues(); for (int i = 0; i < parameterValues.Length; i++) { (TParam, int) tuple = parameterValues[i]; ThrowIfError(SetParameterValue(nativeStream, tuple.Item1, tuple.Item2)); } } catch { FreeStream(nativeStream); } inputSize = (ulong)(long)GetInputSize(); outputSize = (ulong)(long)GetOutputSize(); } public unsafe CoderProcessResult Process(byte* inputData, int inputCount, byte* outputData, int outputCount, CoderFlushType flushType, out int inputTaken, out int outputProduced) { Native.libzstd.ZSTD_Buffer input = new Native.libzstd.ZSTD_Buffer { pData = (IntPtr)inputData, size = (IntPtr)(long)Math.Min((ulong)inputCount, inputSize), pos = (IntPtr)0 }; Native.libzstd.ZSTD_Buffer output = new Native.libzstd.ZSTD_Buffer { pData = (IntPtr)outputData, size = (IntPtr)(long)Math.Min((ulong)outputCount, outputSize), pos = (IntPtr)0 }; IntPtr intPtr = Process(nativeStream, ref input, ref output, flushType); inputTaken = (int)input.pos; outputProduced = (int)output.pos; if (!NativeLibrarySingleton.Instance.ZSTD_isError(intPtr)) { return CoderProcessResult.Success; } return (Native.libzstd.ZSTD_error)(int)intPtr switch { Native.libzstd.ZSTD_error.dstBuffer_wrong => CoderProcessResult.BufferError, Native.libzstd.ZSTD_error.srcBuffer_wrong => CoderProcessResult.BufferError, Native.libzstd.ZSTD_error.dstSize_tooSmall => CoderProcessResult.BufferError, Native.libzstd.ZSTD_error.srcSize_wrong => CoderProcessResult.BufferError, Native.libzstd.ZSTD_error.dstBuffer_null => CoderProcessResult.BufferError, Native.libzstd.ZSTD_error.memory_allocation => CoderProcessResult.MemoryError, _ => CoderProcessResult.DataError, }; } protected override void Dispose(bool disposing) { if (nativeStream != IntPtr.Zero) { try { ThrowIfError(FreeStream(nativeStream)); } catch { } nativeStream = IntPtr.Zero; } } private void ThrowIfError(IntPtr result) { if (NativeLibrarySingleton.Instance.ZSTD_isError(result)) { throw new Exception(Marshal.PtrToStringAnsi(NativeLibrarySingleton.Instance.ZSTD_getErrorName(result))); } } public IntPtr GetCurrentSize() { return GetCurrentSize(nativeStream); } protected abstract IntPtr GetInputSize(); protected abstract IntPtr GetOutputSize(); protected abstract IntPtr Process(IntPtr stream, ref Native.libzstd.ZSTD_Buffer input, ref Native.libzstd.ZSTD_Buffer output, CoderFlushType flushType); public abstract IntPtr GetCurrentSize(IntPtr stream); protected abstract (TParam, int)[] GetParameterValues(); protected abstract IntPtr CreateStream(); protected abstract IntPtr InitializeStream(IntPtr stream); protected abstract IntPtr FreeStream(IntPtr stream); protected abstract IntPtr SetParameterValue(IntPtr stream, TParam parameter, int value); }