Import zlib port, with apologies to Nanook

This commit is contained in:
Matt Nadareski
2024-03-24 22:51:55 -04:00
parent 38f3ea1c98
commit 57c0f9b747
10 changed files with 7866 additions and 0 deletions

View File

@@ -12,3 +12,9 @@ Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTo
| MSZIP | Yes* | No |
**Note:** If something is marked with a `*` it means that it need testing.
## External Libraries
| Library Name | Use |
| --- | ---|
| [ZLibPort](https://github.com/Nanook/zlib-C-To-CSharp-Port) | Adds zlib code for internal and external use; minor edits have been made |

View File

@@ -0,0 +1,218 @@
using System;
using System.Runtime.InteropServices;
namespace SabreTools.Compression.zlib
{
public static unsafe class CRuntime
{
private static readonly string numbers = "0123456789";
public static void* malloc(ulong size)
{
return malloc((long)size);
}
public static void* malloc(long size)
{
var ptr = Marshal.AllocHGlobal((int)size);
MemoryStats.Allocated();
return ptr.ToPointer();
}
public static void free(void* a)
{
if (a == null)
return;
var ptr = new IntPtr(a);
Marshal.FreeHGlobal(ptr);
MemoryStats.Freed();
}
public static void memcpy(void* a, void* b, long size)
{
var ap = (byte*)a;
var bp = (byte*)b;
for (long i = 0; i < size; ++i)
*ap++ = *bp++;
}
public static void memcpy(void* a, void* b, ulong size)
{
memcpy(a, b, (long)size);
}
public static void memmove(void* a, void* b, long size)
{
void* temp = null;
try
{
temp = malloc(size);
memcpy(temp, b, size);
memcpy(a, temp, size);
}
finally
{
if (temp != null)
free(temp);
}
}
public static void memmove(void* a, void* b, ulong size)
{
memmove(a, b, (long)size);
}
public static int memcmp(void* a, void* b, long size)
{
var result = 0;
var ap = (byte*)a;
var bp = (byte*)b;
for (long i = 0; i < size; ++i)
{
if (*ap != *bp)
result += 1;
ap++;
bp++;
}
return result;
}
public static int memcmp(void* a, void* b, ulong size)
{
return memcmp(a, b, (long)size);
}
public static int memcmp(byte* a, byte[] b, ulong size)
{
fixed (void* bptr = b)
{
return memcmp(a, bptr, (long)size);
}
}
public static void memset(void* ptr, int value, long size)
{
var bptr = (byte*)ptr;
var bval = (byte)value;
for (long i = 0; i < size; ++i)
*bptr++ = bval;
}
public static void memset(void* ptr, int value, ulong size)
{
memset(ptr, value, (long)size);
}
public static uint _lrotl(uint x, int y)
{
return (x << y) | (x >> (32 - y));
}
public static void* realloc(void* a, long newSize)
{
if (a == null)
return malloc(newSize);
var ptr = new IntPtr(a);
var result = Marshal.ReAllocHGlobal(ptr, new IntPtr(newSize));
return result.ToPointer();
}
public static void* realloc(void* a, ulong newSize)
{
return realloc(a, (long)newSize);
}
public static int abs(int v)
{
return Math.Abs(v);
}
public static double pow(double a, double b)
{
return Math.Pow(a, b);
}
public static void SetArray<T>(T[] data, T value)
{
for (var i = 0; i < data.Length; ++i)
data[i] = value;
}
public static double ldexp(double number, int exponent)
{
return number * Math.Pow(2, exponent);
}
public static int strcmp(sbyte* src, string token)
{
var result = 0;
for (var i = 0; i < token.Length; ++i)
{
if (src[i] != token[i])
{
++result;
}
}
return result;
}
public static int strncmp(sbyte* src, string token, ulong size)
{
var result = 0;
for (var i = 0; i < Math.Min(token.Length, (int)size); ++i)
{
if (src[i] != token[i])
{
++result;
}
}
return result;
}
public static long strtol(sbyte* start, sbyte** end, int radix)
{
// First step - determine length
var length = 0;
sbyte* ptr = start;
while (numbers.IndexOf((char)*ptr) != -1)
{
++ptr;
++length;
}
long result = 0;
// Now build up the number
ptr = start;
while (length > 0)
{
long num = numbers.IndexOf((char)*ptr);
long pow = (long)Math.Pow(10, length - 1);
result += num * pow;
++ptr;
--length;
}
if (end != null)
{
*end = ptr;
}
return result;
}
}
}

View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Threading;
namespace SabreTools.Compression.zlib
{
public unsafe static class MemoryStats
{
private static int _allocations;
public static int Allocations
{
get
{
return _allocations;
}
}
internal static void Allocated()
{
Interlocked.Increment(ref _allocations);
}
internal static void Freed()
{
Interlocked.Decrement(ref _allocations);
}
}
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Runtime.InteropServices;
namespace SabreTools.Compression.zlib
{
public unsafe class UnsafeArray1D<T> where T : struct
{
private readonly T[] _data;
private readonly GCHandle _pinHandle;
public bool IsFreed { get; private set; }
internal GCHandle PinHandle => _pinHandle;
public T this[int index]
{
get => _data[index];
set
{
_data[index] = value;
}
}
public T this[uint index]
{
get => _data[index];
set
{
_data[index] = value;
}
}
public T[] Data => _data;
public UnsafeArray1D(int size)
{
if (size < 0)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
_data = new T[size];
_pinHandle = GCHandle.Alloc(_data, GCHandleType.Pinned);
IsFreed = false;
}
public UnsafeArray1D(T[] data, int sizeOf)
{
if (sizeOf <= 0)
{
throw new ArgumentOutOfRangeException(nameof(sizeOf));
}
_data = data ?? throw new ArgumentNullException(nameof(data));
_pinHandle = GCHandle.Alloc(_data, GCHandleType.Pinned);
IsFreed = false;
}
public void Free()
{
if (!IsFreed)
{
_pinHandle.Free();
IsFreed = true;
}
}
~UnsafeArray1D()
{
if (!IsFreed)
_pinHandle.Free();
}
public void* ToPointer()
{
return _pinHandle.AddrOfPinnedObject().ToPointer();
}
public static implicit operator void*(UnsafeArray1D<T> array)
{
return array.ToPointer();
}
public static void* operator +(UnsafeArray1D<T> array, int delta)
{
return array.ToPointer();
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Runtime.InteropServices;
namespace SabreTools.Compression.zlib
{
public unsafe class UnsafeArray2D<T> where T : struct
{
private readonly UnsafeArray1D<T>[] _data;
private long[] _pinAddresses;
private readonly GCHandle _pinAddressesHandle;
public UnsafeArray1D<T> this[int index]
{
get => _data[index];
set
{
_data[index] = value;
}
}
public UnsafeArray2D(int size1, int size2)
{
_data = new UnsafeArray1D<T>[size1];
_pinAddresses = new long[size1];
for (var i = 0; i < size1; ++i)
{
_data[i] = new UnsafeArray1D<T>(size2);
_pinAddresses[i] = _data[i].PinHandle.AddrOfPinnedObject().ToInt64();
}
_pinAddressesHandle = GCHandle.Alloc(_pinAddresses, GCHandleType.Pinned);
}
~UnsafeArray2D()
{
_pinAddressesHandle.Free();
}
public void* ToPointer() => _pinAddressesHandle.AddrOfPinnedObject().ToPointer();
public static implicit operator void*(UnsafeArray2D<T> array)
{
return array.ToPointer();
}
}
}

9
zlib/LICENSE Normal file
View File

@@ -0,0 +1,9 @@
MIT License
Copyright (c) 2022 Nanook
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

23
zlib/README.md Normal file
View File

@@ -0,0 +1,23 @@
Project to port ZLib from C to C# (CSharp).
Src zlib 1.2.12 2022-Mar-28 - https://github.com/madler/zlib
See the Stages folder
1_zlib.c - Created by running 1_zlib.c_Concat.ps1 Builds with Clang (used by hebron to convert)
- Only deflate, inflate, crc32 and adler32 code at the moment. GZip might be added if required.
- The only edits to these files are to remove any #includes that have been combined
- The file list includes a 000_ to insert any #defines etc and 100_ for a main for debugging etc
- Notice crc32.c and trees.c had to be split to allow the single file to build
2_zlib.cs_Converted - The converted output that Hebron produced - https://github.com/HebronFramework/Hebron
- This is a little app that uses Clang to read the C code as DOM and write with Roslyn
- It does a fairly decent job and removes a lot of complication
3_zlib.cs_Working - The fixed up and amended C# that actually runs and matches the C code output
- It's had minimal change so is not the prettiest C# code
- It's Unsafe in places
Deflate and Inflate streams have been added.

178
zlib/ZlibDeflateStream.cs Normal file
View File

@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace SabreTools.Compression.zlib
{
public class ZlibDeflateStream : Stream
{
private readonly bool _leaveOpen;
private ZLib.z_stream_s? _s;
private long _p;
private byte[]? _b;
public ZlibDeflateStream(int level, Stream baseStream) : this(level, false, 0, baseStream, false)
{
}
public ZlibDeflateStream(int level, Stream baseStream, bool leaveOpen) : this(level, false, 0, baseStream, leaveOpen)
{
}
public ZlibDeflateStream(int level, bool headerless, Stream baseStream, bool leaveOpen) : this(level, headerless, 0, baseStream, leaveOpen)
{
}
public ZlibDeflateStream(int level, int bufferSize, Stream baseStream, bool leaveOpen) : this(level, false, bufferSize, baseStream, leaveOpen)
{
}
public ZlibDeflateStream(int level, bool headerless, int bufferSize, Stream baseStream, bool leaveOpen)
{
this.Level = level;
this.Headerless = headerless;
this.BaseStream = baseStream;
_leaveOpen = leaveOpen;
_s = null;
_b = new byte[bufferSize == 0 ? 0x10000 : bufferSize];
}
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => _p;
public override long Position { get => _p; set => throw new NotImplementedException(); }
public int Level { get; }
public bool Headerless { get; }
public Stream BaseStream { get; }
public string Version { get => ZLib.zlibVersion(); }
public override void Flush()
{
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
unsafe public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException();
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
if ((offset + count) > buffer.Length) throw new ArgumentException();
int err = 0;
int hdr = 0;
if (_s == null)
{
_s = new ZLib.z_stream_s();
ZLib.deflateInit_(_s, this.Level, this.Version, 0); //0 = sizeof(z_stream_s) not used
if (this.Headerless)
hdr = 2;
_s.total_in = 0u;
_s.total_out = 0u;
_s.avail_in = 0u;
_s.avail_out = 0u;
}
_s.avail_in = (uint)count;
fixed (byte* i = buffer, o = _b)
{
_s.next_in = i;
_s.next_out = o + _s.total_out;
while (err >= 0 && _s.avail_in != 0) //process the buffer
{
if (_s.avail_out == 0) //get more data
{
if (_s.total_out != 0)
{
if (hdr != 0)
{
BaseStream.Write(_b!, hdr, (int)_s.total_out - hdr);
_s.total_out -= (uint)hdr;
hdr = 0;
}
else
BaseStream.Write(_b!, 0, (int)_s.total_out);
}
_p += _s.total_out;
_s.avail_out = (uint)_b!.Length;
_s.next_out = o;
_s.total_out = 0;
}
if (_s.avail_in != 0 || _s.avail_out != 0)
err = ZLib.deflate(_s, 2);
}
}
}
/// <summary>
/// Allow blocks to be written to the base stream. Call when write is finished with.
/// Used for creating block seekable files. The caller must manage blocks, indexes and lengths
/// </summary>
unsafe public long BlockFlush()
{
//finish previous stream
if (_s != null)
{
int err = 0;
fixed (byte* o = _b)
{
_s.next_in = null;
_s.avail_in = 0;
_s.next_out = o + _s.total_out; //point to correct location
int hdr = _p == 0 && Headerless ? 2 : 0;
while (err == 0 && (_s.total_out != 0 || _s.state!.pending != 0))
{
this.BaseStream.Write(_b!, hdr, (int)_s.total_out - hdr);
_s.avail_out = (uint)_b!.Length;
_p += _s.total_out - hdr;
hdr = 0;
_s.next_out = o;
_s.total_out = 0;
if (_s.state!.pending != 0)
err = ZLib.deflate(_s, 2);
}
err = ZLib.deflate(_s, 4);
}
ZLib.deflateEnd(_s);
_s = null;
}
long ret = _p;
_p = 0;
return ret;
}
unsafe protected override void Dispose(bool disposing)
{
this.BlockFlush();
_b = null;
if (!_leaveOpen)
this.BaseStream.Dispose();
}
}
}

170
zlib/ZlibInflateStream.cs Normal file
View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace SabreTools.Compression.zlib
{
public class ZlibInflateStream : Stream
{
private readonly bool _leaveOpen;
private ZLib.z_stream_s? _s;
private long _p;
private byte[]? _b;
private bool _complete;
public ZlibInflateStream(Stream baseStream) : this(0, false, 0, baseStream, false)
{
}
public ZlibInflateStream(Stream baseStream, bool leaveOpen) : this(0, false, 0, baseStream, leaveOpen)
{
}
public ZlibInflateStream(bool headerless, Stream baseStream, bool leaveOpen) : this(0, headerless, 0, baseStream, leaveOpen)
{
}
public ZlibInflateStream(bool headerless, int bufferSize, Stream baseStream, bool leaveOpen) : this(0, headerless, bufferSize, baseStream, leaveOpen)
{
}
public ZlibInflateStream(int bufferSize, Stream baseStream, bool leaveOpen) : this(0, false, bufferSize, baseStream, leaveOpen)
{
}
public ZlibInflateStream(long maxRead, bool headerless, int bufferSize, Stream baseStream, bool leaveOpen)
{
this.MaxRead = maxRead == 0 ? int.MaxValue : maxRead;
this.Headerless = headerless;
this.BaseStream = baseStream;
_leaveOpen = leaveOpen;
_s = null;
_b = new byte[bufferSize == 0 ? 0x10000 : bufferSize];
_complete = false;
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override long Length => _p;
public override long Position { get => _p; set => throw new NotImplementedException(); }
public long MaxRead { get; private set; }
public bool Headerless { get; }
public Stream BaseStream { get; }
public string Version { get => ZLib.zlibVersion(); }
public override void Flush()
{
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
unsafe public override int Read(byte[] buffer, int offset, int count)
{
if (buffer == null) throw new ArgumentNullException();
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
if ((offset + count) > buffer.Length) throw new ArgumentException();
if (_complete)
return 0;
int err = 0;
int hdr = 0;
if (_s == null)
{
_s = new ZLib.z_stream_s();
ZLib.inflateInit_(_s, this.Version, 0); //0 = sizeof(z_stream_s) not used
if (this.Headerless)
{
_b![0] = 0x78;
_b[1] = 0x9c; //da
hdr = 2;
}
_s.total_in = 0u;
_s.total_out = 0u;
_s.avail_in = 0u;
_s.avail_out = 0u;
}
int read;
_s.avail_out = (uint)count;
fixed (byte* i = _b, o = buffer)
{
_s.next_in = i + _s.total_in;
_s.next_out = o;
while (err == 0 && (_s.avail_out != 0 && !_complete)) //process the buffer
{
if (_s.avail_in == 0) //get more data
{
_s.total_in = 0;
read = (int)Math.Min(this.MaxRead - _p, (long)_b!.Length);
if (hdr != 0) //test once to save on the extra calculations
{
_s.avail_in = (uint)(hdr + (read = BaseStream.Read(_b, hdr, Math.Min(read, _b.Length - hdr))));
hdr = 0;
}
else
_s.avail_in = (uint)(read = BaseStream.Read(_b, 0, read));
_complete = read == 0;
_p += (long)read;
_s.next_in = i;
}
if (_s.avail_in != 0 || (!_complete && _s.total_out != 0))
err = ZLib.inflate(_s, 2);
}
}
uint ret = _s.total_out;
_s.total_out = 0u;
return (int)ret;
}
/// <summary>
/// Allow blocks to be read from the base stream without overreading. Call when write is finished with.
/// Used for reading block seekable files. The caller must manage blocks, indexes and lengths. Seek the BaseStream
/// </summary>
public long BlockFlush(int maxRead)
{
this.MaxRead = maxRead;
if (_s != null)
{
ZLib.deflateEnd(_s);
_s = null;
}
_complete = false;
long ret = _p;
_p = 0;
return ret;
}
protected override void Dispose(bool disposing)
{
BlockFlush(0);
_complete = true;
_b = null;
if (!_leaveOpen)
this.BaseStream.Dispose();
}
}
}

7101
zlib/zlib.cs Normal file

File diff suppressed because one or more lines are too long