From c49378412ebe1d151b5076f90c0fe698e50d2a0e Mon Sep 17 00:00:00 2001 From: Soar Qin Date: Wed, 14 Sep 2022 17:34:15 +0800 Subject: [PATCH] Add CompressSave, with Chinese instructions in READMEs --- CheatEnabler/README.md | 31 +- CompressSave/CompressSave.cs | 234 ++ CompressSave/CompressSave.csproj | 33 + CompressSave/CompressionGameSaveHeader.cs | 7 + CompressSave/LZ4Wrap/BlackHoleStream.cs | 50 + CompressSave/LZ4Wrap/BufferWriter.cs | 326 ++ CompressSave/LZ4Wrap/BufferedStream.cs | 117 + CompressSave/LZ4Wrap/DoubleBuffer.cs | 131 + CompressSave/LZ4Wrap/LZ4CompressionStream.cs | 223 ++ .../LZ4Wrap/LZ4DecompressionStream.cs | 145 + CompressSave/LZ4Wrap/LZ4Wrap.cs | 109 + CompressSave/LZ4Wrap/PeekableReader.cs | 17 + CompressSave/LZ4WrapC/CMakeLists.txt | 16 + CompressSave/LZ4WrapC/LZ4Wrap.c | 150 + CompressSave/LZ4WrapC/LZ4Wrap.h | 39 + CompressSave/LZ4WrapC/dllmain.c | 19 + CompressSave/LZ4WrapC/lz4/lz4.c | 2720 +++++++++++++++++ CompressSave/LZ4WrapC/lz4/lz4.h | 842 +++++ CompressSave/LZ4WrapC/lz4/lz4frame.c | 2077 +++++++++++++ CompressSave/LZ4WrapC/lz4/lz4frame.h | 692 +++++ CompressSave/LZ4WrapC/lz4/lz4frame_static.h | 47 + CompressSave/LZ4WrapC/lz4/lz4hc.c | 1631 ++++++++++ CompressSave/LZ4WrapC/lz4/lz4hc.h | 413 +++ CompressSave/LZ4WrapC/lz4/xxhash.c | 1030 +++++++ CompressSave/LZ4WrapC/lz4/xxhash.h | 328 ++ CompressSave/PatchUILoadGame.cs | 97 + CompressSave/PatchUISaveGame.cs | 112 + CompressSave/README.md | 104 + CompressSave/SaveUtil.cs | 99 + CompressSave/package/LZ4.dll | Bin 0 -> 143872 bytes CompressSave/package/icon.png | Bin 0 -> 53761 bytes CompressSave/package/manifest.json | 7 + DSP_Mods.sln | 6 + Dustbin/README.md | 26 +- HideTips/README.md | 18 +- LogisticMiner/README.md | 34 +- README.md | 88 +- 37 files changed, 11924 insertions(+), 94 deletions(-) create mode 100644 CompressSave/CompressSave.cs create mode 100644 CompressSave/CompressSave.csproj create mode 100644 CompressSave/CompressionGameSaveHeader.cs create mode 100644 CompressSave/LZ4Wrap/BlackHoleStream.cs create mode 100644 CompressSave/LZ4Wrap/BufferWriter.cs create mode 100644 CompressSave/LZ4Wrap/BufferedStream.cs create mode 100644 CompressSave/LZ4Wrap/DoubleBuffer.cs create mode 100644 CompressSave/LZ4Wrap/LZ4CompressionStream.cs create mode 100644 CompressSave/LZ4Wrap/LZ4DecompressionStream.cs create mode 100644 CompressSave/LZ4Wrap/LZ4Wrap.cs create mode 100644 CompressSave/LZ4Wrap/PeekableReader.cs create mode 100644 CompressSave/LZ4WrapC/CMakeLists.txt create mode 100644 CompressSave/LZ4WrapC/LZ4Wrap.c create mode 100644 CompressSave/LZ4WrapC/LZ4Wrap.h create mode 100644 CompressSave/LZ4WrapC/dllmain.c create mode 100644 CompressSave/LZ4WrapC/lz4/lz4.c create mode 100644 CompressSave/LZ4WrapC/lz4/lz4.h create mode 100644 CompressSave/LZ4WrapC/lz4/lz4frame.c create mode 100644 CompressSave/LZ4WrapC/lz4/lz4frame.h create mode 100644 CompressSave/LZ4WrapC/lz4/lz4frame_static.h create mode 100644 CompressSave/LZ4WrapC/lz4/lz4hc.c create mode 100644 CompressSave/LZ4WrapC/lz4/lz4hc.h create mode 100644 CompressSave/LZ4WrapC/lz4/xxhash.c create mode 100644 CompressSave/LZ4WrapC/lz4/xxhash.h create mode 100644 CompressSave/PatchUILoadGame.cs create mode 100644 CompressSave/PatchUISaveGame.cs create mode 100644 CompressSave/README.md create mode 100644 CompressSave/SaveUtil.cs create mode 100644 CompressSave/package/LZ4.dll create mode 100644 CompressSave/package/icon.png create mode 100644 CompressSave/package/manifest.json diff --git a/CheatEnabler/README.md b/CheatEnabler/README.md index d4d3602..d95e4c1 100644 --- a/CheatEnabler/README.md +++ b/CheatEnabler/README.md @@ -1,6 +1,8 @@ -## CheatEnabler +# CheatEnabler -### Add various cheat functions while disabling abnormal determinants +#### Add various cheat functions while disabling abnormal determinants + +## Usage * Disable abnormal determinants (Disable all sanity checks, and can get achievements on using Console and Developer Mode shortcuts). @@ -25,3 +27,28 @@ * PageUp: Locks game camera using remembered Pose. * Always infinite resource. * Each function can be enabled individually in config file. + +## 使用说明 + +* 屏蔽游戏异常检测 (使用任何作弊功能都不影响正常获得成就)。 +* Shift+F4 切换开发模式开关 + * 按小键盘1:获得所有物品并扩展背包 + * 按小键盘2:加快行走速度及采集速度,加快能量恢复速度 + * 按小键盘3:将地基铺设整个星球并掩埋所有矿物 + * 按小键盘4:建设机器人 +1 + * 按小键盘5:建设机器人满级 + * 按小键盘6:解锁当前科技 + * 按小键盘7:解锁驱动技术I + * 按小键盘8:解锁驱动技术II 最大化能量 + * 按小键盘9:机甲曲速解锁 + * 按小键盘0:物流站通过传送带出物品无消耗 + * 左ctrl + T:解锁所有非升级科技 + * 左ctrl + A:重置所有本地成就 + * 左ctrl + Q:增加各项元数据10000点 + * 左ctrl + W:进入沙盒模式 + * 小键盘乘号 *:给手上的物品喷涂加速剂 + * 小键盘除号 /:清除手上的物品加速剂 + * PageDown:记录摄像机当前的Pose + * PageUp:用记录的Pose锁定摄像机 +* 总是无限资源。 +* 各功能可以在配置文件中单独开关。 diff --git a/CompressSave/CompressSave.cs b/CompressSave/CompressSave.cs new file mode 100644 index 0000000..1e8c95b --- /dev/null +++ b/CompressSave/CompressSave.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection.Emit; +using BepInEx; +using HarmonyLib; +using CompressSave.LZ4Wrap; + +namespace CompressSave; + +[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] +public class CompressSave : BaseUnityPlugin +{ + public void Awake() + { + SaveUtil.logger = Logger; + if (LZ4API.Avaliable) + { + if (GameConfig.gameVersion != SaveUtil.VerifiedVersion) + { + SaveUtil.logger.LogWarning($"Save version mismatch. Expect:{SaveUtil.VerifiedVersion}, Current:{GameConfig.gameVersion}. MOD may not work as expected."); + } + Harmony.CreateAndPatchAll(typeof(PatchSave)); + if (PatchSave.EnableCompress) + Harmony.CreateAndPatchAll(typeof(PatchUISaveGame)); + Harmony.CreateAndPatchAll(typeof(PatchUILoadGame)); + } + else + SaveUtil.logger.LogWarning("LZ4.dll is not avaliable."); + } + + public void OnDestroy() + { + PatchUISaveGame.OnDestroy(); + PatchUILoadGame.OnDestroy(); + Harmony.UnpatchAll(); + } +} + +class PatchSave +{ + const long MB = 1024 * 1024; + static LZ4CompressionStream.CompressBuffer compressBuffer = LZ4CompressionStream.CreateBuffer((int)MB); //Bigger buffer for GS2 compatible + public static bool UseCompressSave = false; + public static bool IsCompressedSave; + static Stream lzstream = null; + public static bool EnableCompress; + public static bool EnableDecompress; + + private static void WriteHeader(FileStream fileStream) + { + for (int i = 0; i < 4; i++) + fileStream.WriteByte(0xCC); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(GameSave), "AutoSave")] + [HarmonyPatch(typeof(GameSave), "SaveAsLastExit")] + static void BeforeAutoSave() + { + UseCompressSave = EnableCompress; + } + + [HarmonyTranspiler] + [HarmonyPatch(typeof(GameSave), "SaveCurrentGame")] + static IEnumerable SaveCurrentGame_Transpiler(IEnumerable instructions, ILGenerator generator) + { + /* BinaryWriter binaryWriter = new BinaryWriter(fileStream); => Create lzstream and replace binaryWriter. + * set PerformanceMonitor.BeginStream to lzstream. + * fileStream.Seek(6L, SeekOrigin.Begin); binaryWriter.Write(position); => Disable seek&write function. + * binaryWriter.Dispose(); => Dispose lzstream before fileStream close. + */ + try + { + var matcher = new CodeMatcher(instructions, generator) + .MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryWriter), new Type[] { typeof(FileStream) }))) + .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryWriter")) + .MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream"))) + .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream")) + .MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IO.Stream), "Seek"))) + .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite0")) + .MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryWriter), "Write", new Type[] { typeof(long) }))) + .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthWrite1")) + .MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IDisposable), "Dispose"))) + .Advance(1) + .Insert(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeLzstream"))); + EnableCompress = true; + return matcher.InstructionEnumeration(); + } + catch (Exception ex) + { + SaveUtil.logger.LogError("SaveCurrentGame_Transpiler failed. Mod version not compatible with game version."); + SaveUtil.logger.LogError(ex); + } + return instructions; + } + + public static void MonitorStream(Stream fileStream) + { + PerformanceMonitor.BeginStream(UseCompressSave ? lzstream : fileStream); + } + + public static BinaryWriter CreateBinaryWriter(FileStream fileStream) + { + if (UseCompressSave) + { + SaveUtil.logger.LogDebug("Begin compress save"); + WriteHeader(fileStream); + lzstream = new LZ4CompressionStream(fileStream, compressBuffer, true); //need to dispose after use + return ((LZ4CompressionStream)lzstream).BufferWriter; + } + SaveUtil.logger.LogDebug("Begin normal save"); + return new BinaryWriter(fileStream); + } + + public static long FileLengthWrite0(FileStream fileStream, long offset, SeekOrigin origin) + { + if (!UseCompressSave) + return fileStream.Seek(offset, origin); + return 0L; + } + + public static void FileLengthWrite1(BinaryWriter binaryWriter, long value) + { + if (!UseCompressSave) + binaryWriter.Write(value); + } + + public static void DisposeLzstream() + { + if (!UseCompressSave) return; + var writeflag = lzstream.CanWrite; + lzstream?.Dispose(); //Dispose need to be done before fstream closed. + lzstream = null; + if (writeflag) //Reset UseCompressSave after writing to file + UseCompressSave = false; + } + + + [HarmonyTranspiler] + [HarmonyPatch(typeof(GameSave), "LoadCurrentGame")] + [HarmonyPatch(typeof(GameSave), "LoadGameDesc")] + [HarmonyPatch(typeof(GameSave), "ReadHeader")] + [HarmonyPatch(typeof(GameSave), "ReadHeaderAndDescAndProperty")] + static IEnumerable LoadCurrentGame_Transpiler(IEnumerable instructions, ILGenerator iLGenerator) + { + /* using (BinaryReader binaryReader = new BinaryReader(fileStream)) => Create lzstream and replace binaryReader. + * set PerformanceMonitor.BeginStream to lzstream. + * if (fileStream.Length != binaryReader.ReadInt64()) => Replace binaryReader.ReadInt64() to pass file length check. + * fileStream.Seek((long)num2, SeekOrigin.Current); => Use lzstream.Read to seek forward + * binaryReader.Dispose(); => Dispose lzstream before fileStream close. + */ + try + { + var matcher = new CodeMatcher(instructions, iLGenerator) + .MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(BinaryReader), new Type[] { typeof(FileStream) }))) + .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "CreateBinaryReader")) + .MatchForward(false, new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(PerformanceMonitor), "BeginStream"))); + + if (matcher.IsValid) + matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "MonitorStream")); + + matcher.Start().MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(BinaryReader), "ReadInt64"))) + .Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "FileLengthRead")) + .MatchForward(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IDisposable), "Dispose"))) + .Advance(1) + .Insert(new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "DisposeLzstream"))) + .MatchBack(false, new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(System.IO.Stream), "Seek"))); + if (matcher.IsValid) + matcher.Set(OpCodes.Call, AccessTools.Method(typeof(PatchSave), "ReadSeek")); + matcher.Start() + .MatchForward(false, new CodeMatch(OpCodes.Newobj, AccessTools.Constructor(typeof(GameSaveHeader)))); + if (matcher.IsValid) + matcher.Set(OpCodes.Newobj, AccessTools.Constructor(typeof(CompressionGameSaveHeader))); //ReadHeader + + EnableDecompress = true; + return matcher.InstructionEnumeration(); + } + catch (Exception ex) + { + SaveUtil.logger.LogError("LoadCurrentGame_Transpiler failed. Mod version not compatible with game version."); + SaveUtil.logger.LogError(ex); + } + return instructions; + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(GameSave), "ReadHeader")] + [HarmonyPatch(typeof(GameSave), "ReadHeaderAndDescAndProperty")] + static void ReadHeader_Postfix(ref GameSaveHeader header) + { + if (header != null) + ((CompressionGameSaveHeader)header).IsCompressed = IsCompressedSave; + } + + public static BinaryReader CreateBinaryReader(FileStream fileStream) + { + if ((IsCompressedSave = SaveUtil.IsCompressedSave(fileStream))) + { + UseCompressSave = true; + lzstream = new LZ4DecompressionStream(fileStream); + return new PeekableReader((LZ4DecompressionStream)lzstream); + } + else + { + UseCompressSave = false; + fileStream.Seek(0, SeekOrigin.Begin); + return new BinaryReader(fileStream); + } + } + + public static long FileLengthRead(BinaryReader binaryReader) + { + if (UseCompressSave) + { + binaryReader.ReadInt64(); + return lzstream.Length; + } + else + return binaryReader.ReadInt64(); + } + + public static long ReadSeek(FileStream fileStream, long offset, SeekOrigin origin) + { + if (UseCompressSave) + { + while (offset > 0) + offset -= lzstream.Read(compressBuffer.outBuffer, 0, (int)offset); + return lzstream.Position; + } + else + return fileStream.Seek(offset, origin); + } +} \ No newline at end of file diff --git a/CompressSave/CompressSave.csproj b/CompressSave/CompressSave.csproj new file mode 100644 index 0000000..70088a4 --- /dev/null +++ b/CompressSave/CompressSave.csproj @@ -0,0 +1,33 @@ + + + + net472 + CompressSave + org.soardev.compresssave + DSP MOD - CompressSave + 1.1.3 + true + latest + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CompressSave/CompressionGameSaveHeader.cs b/CompressSave/CompressionGameSaveHeader.cs new file mode 100644 index 0000000..a5092b9 --- /dev/null +++ b/CompressSave/CompressionGameSaveHeader.cs @@ -0,0 +1,7 @@ +namespace CompressSave +{ + internal class CompressionGameSaveHeader: GameSaveHeader + { + public bool IsCompressed = false; + } +} diff --git a/CompressSave/LZ4Wrap/BlackHoleStream.cs b/CompressSave/LZ4Wrap/BlackHoleStream.cs new file mode 100644 index 0000000..8715bc7 --- /dev/null +++ b/CompressSave/LZ4Wrap/BlackHoleStream.cs @@ -0,0 +1,50 @@ +using System; +using System.IO; + +namespace CompressSave.LZ4Wrap; + +class BlackHoleStream : Stream +{ + private long length; + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => length; + + public override long Position { get; set; } + + public BlackHoleStream() + { + + } + + public override void Flush() + { + ; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return count; + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + length = value; + } + public byte[] testBuffer = new byte[1024 * 1024]; + + public override void Write(byte[] buffer, int offset, int count) + { + Array.Copy(buffer, offset, testBuffer, 0, Math.Min(count, testBuffer.Length)); + } +} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/BufferWriter.cs b/CompressSave/LZ4Wrap/BufferWriter.cs new file mode 100644 index 0000000..5acac36 --- /dev/null +++ b/CompressSave/LZ4Wrap/BufferWriter.cs @@ -0,0 +1,326 @@ +using System; +using System.IO; +using System.Text; +using System.Runtime.CompilerServices; + +namespace CompressSave.LZ4Wrap; + +public unsafe class BufferWriter : BinaryWriter +{ + ByteSpan currentBuffer => doubleBuffer.writeBuffer; + + DoubleBuffer doubleBuffer; + + private Encoding _encoding; + + private Encoder encoder; + + byte[] Buffer => currentBuffer.Buffer; + + long SuplusCapacity => endPos - curPos; + + long swapedBytes = 0; + + public long WriteSum => swapedBytes + curPos - startPos; + + public override Stream BaseStream => _baseStream; + + public override void Write(char[] chars, int index, int count) + { + if (chars == null) + { + throw new ArgumentNullException("chars"); + } + byte[] bytes = _encoding.GetBytes(chars, index, count); + Write(bytes); + } + + byte* curPos; + byte* endPos; + byte* startPos; + private Stream _baseStream; + + public BufferWriter(DoubleBuffer doubleBuffer, LZ4CompressionStream outStream) + : this(doubleBuffer, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), outStream) + { + + } + + BufferWriter(DoubleBuffer buffer , UTF8Encoding encoding, LZ4CompressionStream outStream) : base(Stream.Null, encoding) + { + _baseStream = outStream; + swapedBytes = 0; + doubleBuffer = buffer; + RefreshStatus(); + _encoding = encoding; + encoder = _encoding.GetEncoder(); + } + + void SwapBuffer() + { + currentBuffer.Position = 0; + + currentBuffer.Length = (int)(curPos - startPos); + swapedBytes += currentBuffer.Length; + doubleBuffer.SwapBuffer(); + RefreshStatus(); + } + + void RefreshStatus() + { + startPos = (byte*)Unsafe.AsPointer(ref Buffer[0]); + curPos = startPos; + endPos = (byte*)Unsafe.AsPointer(ref Buffer[Buffer.Length - 1]) + 1; + } + + void CheckCapacityAndSwap(int requiredCapacity) + { + if (SuplusCapacity < requiredCapacity) + { + SwapBuffer(); + } + } + + public override void Write(byte value) + { + CheckCapacityAndSwap(1); + *(curPos++) = value; + } + + public override void Write(bool value) => Write((byte)(value ? 1 : 0)); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + SwapBuffer(); + } + base.Dispose(disposing); + } + + public override void Close() + { + Dispose(disposing: true); + } + + public override void Flush() + { + SwapBuffer(); + } + + public override long Seek(int offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void Write(sbyte value) => Write((byte)value); + + public override void Write(byte[] _buffer) => Write(_buffer, 0, _buffer.Length); + + + public override void Write(byte[] _buffer, int index, int count) + { + if (_buffer == null) + { + throw new ArgumentNullException("buffer"); + } + fixed (byte* start = _buffer) + { + byte* srcPos = start + index; + while (SuplusCapacity <= count) + { + int dstSuplus = (int)SuplusCapacity; + //Array.Copy(_buffer, index + writed, Buffer, Position, SuplusCapacity); + Unsafe.CopyBlock(curPos, srcPos, (uint)dstSuplus); + count -= dstSuplus; + srcPos += dstSuplus; + curPos = endPos; + SwapBuffer(); + } + Unsafe.CopyBlock(curPos, srcPos, (uint)count); + curPos += count; + } + } + + public unsafe override void Write(char ch) + { + if (char.IsSurrogate(ch)) + { + throw new ArgumentException("Arg_SurrogatesNotAllowedAsSingleChar"); + } + + CheckCapacityAndSwap(4); + + curPos += encoder.GetBytes(&ch, 1, curPos, (int)SuplusCapacity, flush: true); + } + + //slow + public override void Write(char[] chars) + { + if (chars == null) + { + throw new ArgumentNullException("chars"); + } + byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length); + Write(bytes); + } + + public unsafe override void Write(double value) + { + CheckCapacityAndSwap(8); + ulong num = (ulong)(*(long*)(&value)); + *(curPos++) = (byte)num; + *(curPos++) = (byte)(num >> 8); + *(curPos++) = (byte)(num >> 16); + *(curPos++) = (byte)(num >> 24); + *(curPos++) = (byte)(num >> 32); + *(curPos++) = (byte)(num >> 40); + *(curPos++) = (byte)(num >> 48); + *(curPos++) = (byte)(num >> 56); + } + + //slow + public override void Write(decimal d) + { + CheckCapacityAndSwap(16); + int[] bits = decimal.GetBits(d); + + Write(bits[0]); + Write(bits[1]); + Write(bits[2]); + Write(bits[3]); + } + + + public override void Write(short value) + { + CheckCapacityAndSwap(2); + *(curPos++) = (byte)value; + *(curPos++) = (byte)(value >> 8); + } + + public override void Write(ushort value) + { + CheckCapacityAndSwap(2); + *(curPos++) = (byte)value; + *(curPos++) = (byte)(value >> 8); + } + + + public override void Write(int value) + { + if (SuplusCapacity < 4) + { + SwapBuffer(); + } + *(curPos++) = (byte)value; + *(curPos++) = (byte)(value >> 8); + *(curPos++) = (byte)(value >> 16); + *(curPos++) = (byte)(value >> 24); + } + + public override void Write(uint value) + { + CheckCapacityAndSwap(4); + *(curPos++) = (byte)value; + *(curPos++) = (byte)(value >> 8); + *(curPos++) = (byte)(value >> 16); + *(curPos++) = (byte)(value >> 24); + } + + + public override void Write(long value) + { + CheckCapacityAndSwap(8); + *(curPos++) = (byte)value; + *(curPos++) = (byte)(value >> 8); + *(curPos++) = (byte)(value >> 16); + *(curPos++) = (byte)(value >> 24); + *(curPos++) = (byte)(value >> 32); + *(curPos++) = (byte)(value >> 40); + *(curPos++) = (byte)(value >> 48); + *(curPos++) = (byte)(value >> 56); + } + + public override void Write(ulong value) + { + CheckCapacityAndSwap(8); + *(curPos++) = (byte)value; + *(curPos++) = (byte)(value >> 8); + *(curPos++) = (byte)(value >> 16); + *(curPos++) = (byte)(value >> 24); + *(curPos++) = (byte)(value >> 32); + *(curPos++) = (byte)(value >> 40); + *(curPos++) = (byte)(value >> 48); + *(curPos++) = (byte)(value >> 56); + } + + public unsafe override void Write(float value) + { + if (SuplusCapacity < 4) + { + SwapBuffer(); + } + uint num = *(uint*)(&value); + *(curPos++) = (byte)num; + *(curPos++) = (byte)(num >> 8); + *(curPos++) = (byte)(num >> 16); + *(curPos++) = (byte)(num >> 24); + } + + + //slow + public unsafe override void Write(string value) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + int byteCount = _encoding.GetByteCount(value); + Write7BitEncodedInt(byteCount); + { + var dstSuplus = (int)SuplusCapacity; + if (byteCount <= dstSuplus) + { + fixed (char* start = value) + { + int Wcount = _encoding.GetBytes(start, value.Length, curPos, dstSuplus); + curPos += Wcount; + //Console.WriteLine($"Using quick write!"); + return; + } + } + } + + int charIndex = 0; + bool completed; + fixed (char* chars = value) + { + do + { + encoder.Convert(chars + charIndex, value.Length - charIndex, + curPos, (int)SuplusCapacity, true, + out int charsConsumed, out int bytesWritten, out completed); + charIndex += charsConsumed; + curPos += bytesWritten; + //Console.WriteLine($"charsConsumed{charsConsumed} charIndex{charIndex} bytesWritten{bytesWritten} position{position} suplusCapacity{suplusCapacity}"); + + if (SuplusCapacity <= 0) + SwapBuffer(); + } while (!completed); + } + encoder.Reset(); //flush + } + + + + protected new void Write7BitEncodedInt(int value) + { + uint num; + for (num = (uint)value; num >= 128; num >>= 7) + { + Write((byte)(num | 0x80)); + } + Write((byte)num); + } +} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/BufferedStream.cs b/CompressSave/LZ4Wrap/BufferedStream.cs new file mode 100644 index 0000000..a39ac30 --- /dev/null +++ b/CompressSave/LZ4Wrap/BufferedStream.cs @@ -0,0 +1,117 @@ +namespace CompressSave.LZ4Wrap; + +//public class BufferedFileStream : FileStream +//{ +// public override bool CanTimeout => base.CanTimeout; + +// public override int ReadTimeout { get => base.ReadTimeout; set => base.ReadTimeout = value; } +// public override int WriteTimeout { get => base.WriteTimeout; set => base.WriteTimeout = value; } + +// public override long Position { get => base.Position; set => base.Position = value; } + +// public override SafeFileHandle SafeFileHandle => base.SafeFileHandle; + +// public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject) +// { +// return base.BeginRead(array, offset, numBytes, userCallback, stateObject); +// } + +// public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject) +// { +// return base.BeginWrite(array, offset, numBytes, userCallback, stateObject); +// } + +// public override void Close() +// { +// var bs = new BufferedStream(this); + + +// base.Close(); +// } + +// public override bool Equals(object obj) +// { +// return base.Equals(obj); +// } + +// public override void Flush() +// { +// base.Flush(); +// } + +// public override void Flush(bool flushToDisk) +// { +// base.Flush(flushToDisk); +// } + +// public override Task FlushAsync(CancellationToken cancellationToken) +// { +// return base.FlushAsync(cancellationToken); +// } + +// public override int GetHashCode() +// { +// return base.GetHashCode(); +// } + +// public override object InitializeLifetimeService() +// { +// return base.InitializeLifetimeService(); +// } + +// public override void Lock(long position, long length) +// { +// base.Lock(position, length); +// } + +// public override int Read(byte[] array, int offset, int count) +// { +// return base.Read(array, offset, count); +// } + +// public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) +// { +// return base.ReadAsync(buffer, offset, count, cancellationToken); +// } + +// public override long Seek(long offset, SeekOrigin origin) +// { +// return base.Seek(offset, origin); +// } + +// public override void SetLength(long value) +// { +// base.SetLength(value); +// } + +// public override string ToString() +// { +// return base.ToString(); +// } + +// public override void Unlock(long position, long length) +// { +// base.Unlock(position, length); +// } + +// public override void Write(byte[] array, int offset, int count) +// { +// base.Write(array, offset, count); +// } + +// public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) +// { +// return base.WriteAsync(buffer, offset, count, cancellationToken); +// } + +// public override void WriteByte(byte value) +// { +// base.WriteByte(value); +// } + +// protected override void Dispose(bool disposing) +// { +// base.Dispose(disposing); +// } + +//} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/DoubleBuffer.cs b/CompressSave/LZ4Wrap/DoubleBuffer.cs new file mode 100644 index 0000000..d64f010 --- /dev/null +++ b/CompressSave/LZ4Wrap/DoubleBuffer.cs @@ -0,0 +1,131 @@ +using System; +using System.Threading; + +namespace CompressSave.LZ4Wrap; + +public class ByteSpan +{ + public byte[] Buffer { get; private set; } + //public int Start; + public int Length; + public int Capacity; + public int IdleCapacity => Capacity - Length; + public int Position; + + public ByteSpan(byte[] buffer) + { + Buffer = buffer; + Capacity = Buffer.Length; + } + public void Clear() + { + Length = 0; + Position = 0; + } + public int Write(byte[] src, int offset, int count) + { + int writeLen = Math.Min(Capacity - Length, count); + Array.Copy(src, offset, Buffer, Length, writeLen); + Length += writeLen; + return writeLen; + } + + public int Read(byte[] dst, int offset, int count) + { + count = Math.Min(Length - Position, count); + Array.Copy(Buffer, Position, dst, offset, count); + Position += count; + return count; + } + + public static implicit operator byte[](ByteSpan bs) => bs.Buffer; +} + +public struct ReadOnlySpan +{ + public readonly int Length; + public readonly byte[] Buffer; + public int Position; + + public ReadOnlySpan(byte[] buffer, int length) + { + Buffer = buffer; + Length = length; + Position = 0; + } + + public int Read(byte[] dst, int offset, int count) + { + count = Math.Min(Length - Position, count); + Array.Copy(Buffer, Position, dst, offset, count); + Position += count; + return count; + } + + public static implicit operator byte[](ReadOnlySpan s) => s.Buffer; +} + +public class DoubleBuffer +{ + public const int MB = 1024 * 1024; + + public ByteSpan writeBuffer; + public ByteSpan readBuffer; + private ByteSpan midBuffer; + private Action onReadBufferReady; + + Semaphore readEnd = new Semaphore(1, 1); + Semaphore writeEnd = new Semaphore(0, 1); + + public DoubleBuffer(byte[] readBuffer, byte[] writeBuffer, Action onReadBufferReady) + { + this.onReadBufferReady = onReadBufferReady; + this.midBuffer = new ByteSpan(readBuffer); + this.writeBuffer = new ByteSpan(writeBuffer); + } + + public ByteSpan ReadBegin() + { + writeEnd.WaitOne(); + return readBuffer; + } + + public void ReadEnd() + { + readBuffer.Clear(); + midBuffer = readBuffer; + readBuffer = null; + readEnd.Release(); + } + /// + /// swap current write buffer to read and wait a new write buffer + /// + /// write buffer + public ByteSpan SwapBuffer(bool triggerEvent = true) + { + var write = SwapBegin(); + SwapEnd(); + onReadBufferReady?.Invoke(); + return write; + } + + public void WaitReadEnd() + { + readEnd.WaitOne(); + readEnd.Release(); + } + + public ByteSpan SwapBegin() + { + readEnd.WaitOne(); + readBuffer = writeBuffer; + writeBuffer = midBuffer; + midBuffer = null; + return writeBuffer; + } + + public void SwapEnd() + { + writeEnd.Release(); + } +} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/LZ4CompressionStream.cs b/CompressSave/LZ4Wrap/LZ4CompressionStream.cs new file mode 100644 index 0000000..bf95019 --- /dev/null +++ b/CompressSave/LZ4Wrap/LZ4CompressionStream.cs @@ -0,0 +1,223 @@ +using System; +using System.IO; +using System.Threading; + +namespace CompressSave.LZ4Wrap; + +public class LZ4CompressionStream : Stream, IDisposable +{ + public const int MB = 1024 * 1024; + public override bool CanRead => false; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => totalWrite; + + // only use for game statistics + public override long Position { get => BufferWriter.WriteSum; set => new NotImplementedException(); } + + readonly Stream outStream; + + long totalWrite = 0; + bool useMultiThread; + DoubleBuffer doubleBuffer; + + private byte[] outBuffer; + + IntPtr cctx; + long lastError = 0; + bool stopWorker = true; + Thread compressThread; + + public bool HasError() + { + return lastError != 0; + } + + public void HandleError(long errorCode) + { + if (errorCode < 0) + { + LZ4API.FreeCompressContext(cctx); + cctx = IntPtr.Zero; + lastError = errorCode; + throw new Exception(errorCode.ToString()); + } + } + + public struct CompressBuffer + { + public byte[] readBuffer; + public byte[] writeBuffer; + public byte[] outBuffer; + } + + public static CompressBuffer CreateBuffer(int ExBufferSize = 4 * MB) + { + try + { + return new CompressBuffer + { + outBuffer = new byte[LZ4API.CalCompressOutBufferSize(ExBufferSize) + 1], + readBuffer = new byte[ExBufferSize], + writeBuffer = new byte[ExBufferSize], + }; + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + return new CompressBuffer(); + } + + public BufferWriter BufferWriter => bfferWriter; + BufferWriter bfferWriter; + + public LZ4CompressionStream(Stream outStream, CompressBuffer compressBuffer,bool useMultiThread) + { + this.outStream = outStream; + InitBuffer(compressBuffer.readBuffer, compressBuffer.writeBuffer, compressBuffer.outBuffer); + long writeSize = LZ4API.CompressBegin(out cctx, outBuffer, outBuffer.Length); + HandleError(writeSize); + outStream.Write(outBuffer, 0, (int)writeSize); + this.useMultiThread = useMultiThread; + if(useMultiThread) + { + stopWorker = false; + compressThread = new Thread(() => CompressAsync()); + compressThread.Start(); + } + + } + + void InitBuffer(byte[] readBuffer, byte[] writeBuffer, byte[] outBuffer) + { + doubleBuffer = new DoubleBuffer(readBuffer ?? new byte[4 * MB], writeBuffer ?? new byte[4 * MB], Compress); + this.outBuffer = outBuffer ?? new byte[LZ4API.CalCompressOutBufferSize(writeBuffer.Length)]; + bfferWriter = new BufferWriter(doubleBuffer,this); + } + + public override void Flush() + { + doubleBuffer.SwapBuffer(); + if(useMultiThread) + { + doubleBuffer.WaitReadEnd(); + } + lock (outBuffer) + { + outStream.Flush(); + } + } + + void Compress() + { + if (!useMultiThread) + { + Compress_Internal(); + } + } + + void Compress_Internal() + { + var consumeBuffer = doubleBuffer.ReadBegin(); + if (consumeBuffer.Length > 0) + { + lock (outBuffer) + { + long writeSize = 0; + try + { + writeSize = LZ4API.CompressUpdateEx(cctx, outBuffer, 0, consumeBuffer.Buffer, 0, consumeBuffer.Length); + HandleError(writeSize); + } + finally + { + doubleBuffer.ReadEnd(); + } + outStream.Write(outBuffer, 0, (int)writeSize); + totalWrite += writeSize; + } + } + else + { + doubleBuffer.ReadEnd(); + } + } + + void CompressAsync() + { + while(!stopWorker) + { + Compress_Internal(); + } + } + + 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(); + } + + + public override void Write(byte[] buffer, int offset, int count) + { + BufferWriter.Write(buffer, offset, count); + //var writeBuffer = doubleBuffer.writeBuffer; + //int writeSize = writeBuffer.Write(buffer, offset, count); + //while (count - writeSize > 0) + //{ + // SwapBuffer(ref writeBuffer); + // offset += writeSize; + // count -= writeSize; + // writeSize = writeBuffer.Write(buffer, offset, count); + //} + //inputSum += count; + } + + protected void FreeContext() + { + LZ4API.FreeCompressContext(cctx); + cctx = IntPtr.Zero; + } + + bool closed = false; + public override void Close() + { + if(!closed) + { + BufferWriter.Close(); + closed = true; + //Console.WriteLine($"FLUSH"); + + Flush(); + + // try stop the worker + stopWorker = true; + doubleBuffer.SwapBuffer(); + + long size = LZ4API.CompressEnd(cctx, outBuffer, outBuffer.Length); + //Debug.Log($"End"); + outStream.Write(outBuffer, 0, (int)size); + base.Close(); + } + } + + protected override void Dispose(bool disposing) + { + FreeContext(); + base.Dispose(disposing); + } + +} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/LZ4DecompressionStream.cs b/CompressSave/LZ4Wrap/LZ4DecompressionStream.cs new file mode 100644 index 0000000..3f21985 --- /dev/null +++ b/CompressSave/LZ4Wrap/LZ4DecompressionStream.cs @@ -0,0 +1,145 @@ +using System; +using System.IO; + +namespace CompressSave.LZ4Wrap; + +class LZ4DecompressionStream : Stream +{ + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => inStream.Length; + + public override long Position + { get => readPos; + set + { + if (value < readPos) + ResetStream(); + else + value -= readPos; + byte[] tmpBuffer = new byte[1024]; + while (value > 0) + { + value -= Read(tmpBuffer, 0, (int)(value < 1024 ? value : 1024)); + } + } + } + + public Stream inStream; + + IntPtr dctx = IntPtr.Zero; + + readonly ByteSpan srcBuffer; + readonly ByteSpan dcmpBuffer; + private bool decompressFinish = false; + readonly long startPos = 0; + long readPos = 0; //sum of readlen + + public LZ4DecompressionStream(Stream inStream,int extraBufferSize = 512*1024) + { + this.inStream = inStream; + startPos = inStream.Position; + srcBuffer = new ByteSpan(new byte[extraBufferSize]); + int len = Fill(); + long expect = LZ4API.DecompressBegin(ref dctx, srcBuffer.Buffer, ref len, out var blockSize); + srcBuffer.Position += len; + if (expect < 0) throw new Exception(expect.ToString()); + dcmpBuffer = new ByteSpan(new byte[blockSize]); + } + + public void ResetStream() + { + inStream.Seek(startPos, SeekOrigin.Begin); + decompressFinish = false; + srcBuffer.Clear(); + dcmpBuffer.Clear(); + LZ4API.ResetDecompresssCTX(dctx); + readPos = 0; + } + + public int Fill() + { + int suplus = srcBuffer.Length - srcBuffer.Position; + if (srcBuffer.Length> 0 && srcBuffer.Position >= suplus) + { + Array.Copy(srcBuffer, srcBuffer.Position, srcBuffer, 0, suplus); + srcBuffer.Length -= srcBuffer.Position; + srcBuffer.Position = 0; + } + if (srcBuffer.IdleCapacity > 0) + { + var readlen = inStream.Read(srcBuffer, srcBuffer.Length, srcBuffer.IdleCapacity); + srcBuffer.Length += readlen; + } + return srcBuffer.Length - srcBuffer.Position; + } + + public override void Flush() + { + + } + + protected override void Dispose(bool disposing) + { + LZ4API.DecompressEnd(dctx); + dctx = IntPtr.Zero; + base.Dispose(disposing); + } + + public override int Read(byte[] buffer, int offset, int count) + { + int readlen = 0; + while (count > (readlen += dcmpBuffer.Read(buffer, offset + readlen, count - readlen)) && !decompressFinish) + { + var buffSize = Fill(); + if (buffSize <= 0) return readlen; + + var rt = LZ4API.DecompressUpdateEx(dctx, dcmpBuffer, 0, dcmpBuffer.Capacity, srcBuffer, srcBuffer.Position,buffSize, null); + if (rt.expect < 0) throw new Exception(rt.expect.ToString()); + if (rt.expect == 0) decompressFinish = true; + + srcBuffer.Position += (int)rt.readLen; + dcmpBuffer.Position = 0; + dcmpBuffer.Length = (int)rt.writeLen; + } + readPos += readlen; + return readlen; + } + + public int PeekByte() + { + if (dcmpBuffer.Length <= dcmpBuffer.Position) + { + var buffSize = Fill(); + if (buffSize <= 0) return -1; + + var rt = LZ4API.DecompressUpdateEx(dctx, dcmpBuffer, 0, dcmpBuffer.Capacity, srcBuffer, srcBuffer.Position, buffSize, null); + if (rt.expect < 0) throw new Exception(rt.expect.ToString()); + if (rt.expect == 0) decompressFinish = true; + + srcBuffer.Position += (int)rt.readLen; + dcmpBuffer.Position = 0; + dcmpBuffer.Length = (int)rt.writeLen; + } + return dcmpBuffer.Buffer[dcmpBuffer.Position]; + } + + 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(); + } +} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/LZ4Wrap.cs b/CompressSave/LZ4Wrap/LZ4Wrap.cs new file mode 100644 index 0000000..73dba15 --- /dev/null +++ b/CompressSave/LZ4Wrap/LZ4Wrap.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.IO; +using MonoMod.Utils; + +namespace CompressSave.LZ4Wrap; + +public struct DecompressStatus +{ + public long writeLen; + public long readLen; + public long expect; +} + +public static class LZ4API +{ + public static readonly bool Avaliable; + static LZ4API() + { + Avaliable = true; + string assemblyPath = System.Reflection.Assembly.GetAssembly(typeof(LZ4API)).Location; + string root = string.Empty; + try + { + if (!string.IsNullOrEmpty(assemblyPath)) + { + root = Path.GetDirectoryName(assemblyPath); + } + var map = new Dictionary> + { + { "LZ4.dll" ,new List{ + "LZ4.dll", + "X64/LZ4.dll", + "BepInEx/scripts/x64/LZ4.dll", + Path.Combine(root,"X64/LZ4.dll"), + Path.Combine(root,"LZ4.dll") + } }, + }; + typeof(LZ4API).ResolveDynDllImports(map); + } + catch (Exception e) + { + Avaliable = false; + Console.WriteLine($"Error: {e}"); + return; + } + } + + public delegate long _CalCompressOutBufferSize(long inBufferSize); + + [DynDllImport(libraryName: "LZ4.dll")] + public static _CalCompressOutBufferSize CalCompressOutBufferSize; + + [DynDllImport(libraryName: "LZ4.dll")] + public static _CompressBegin CompressBegin; + public delegate long _CompressBegin(out IntPtr ctx, byte[] outBuff, long outCapacity, byte[] dictBuffer = null, long dictSize = 0); + + [DynDllImport(libraryName: "LZ4.dll")] + public static _CompressUpdate CompressUpdate; + public unsafe delegate long _CompressUpdate(IntPtr ctx, byte* dstBuffer, long dstCapacity, byte* srcBuffer, long srcSize); + + public unsafe static long CompressUpdateEx(IntPtr ctx, byte[] dstBuffer, long dstOffset, byte[] srcBuffer, long srcOffset, long srcLen) + { + fixed (byte* pdst = dstBuffer, psrc = srcBuffer) + { + return CompressUpdate(ctx, pdst + dstOffset, dstBuffer.Length - dstOffset, psrc + srcOffset, srcLen - srcOffset); + } + } + + [DynDllImport(libraryName: "LZ4.dll")] + public static _FreeCompressContext FreeCompressContext; + public delegate void _FreeCompressContext(IntPtr ctx); + + [DynDllImport(libraryName: "LZ4.dll")] + public static _CompressEnd CompressEnd; + public delegate long _CompressEnd(IntPtr ctx, byte[] dstBuffer, long dstCapacity); + + [DynDllImport(libraryName: "LZ4.dll")] + public static _DecompressEnd DecompressEnd; + public delegate long _DecompressEnd(IntPtr dctx); + + [DynDllImport(libraryName: "LZ4.dll")] + unsafe static _DecompressUpdate DecompressUpdate = null; + public unsafe delegate long _DecompressUpdate(IntPtr dctx, byte* dstBuffer, ref long dstCapacity, byte* srcBuffer, ref long srcSize, byte* dict, long dictSize); + public unsafe static DecompressStatus DecompressUpdateEx(IntPtr dctx, byte[] dstBuffer, int dstOffset, int dstCount, byte[] srcBuffer, long srcOffset, long count, byte[] dict) + { + long dstLen = Math.Min(dstCount, dstBuffer.Length - dstOffset); + long errCode = 0; + fixed (byte* pdst = dstBuffer, psrc = srcBuffer, pdict = dict) + { + errCode = DecompressUpdate(dctx, pdst + dstOffset, ref dstLen, psrc + srcOffset, ref count, pdict, dict == null ? 0 : dict.Length); + } + return new DecompressStatus + { + expect = errCode, + readLen = count, + writeLen = dstLen, + }; + } + + [DynDllImport(libraryName: "LZ4.dll")] + public static _DecompressBegin DecompressBegin; + public delegate long _DecompressBegin(ref IntPtr pdctx, byte[] inBuffer, ref int inBufferSize, out int blockSize); + + public delegate void _ResetDecompresssCTX(IntPtr dctx); + + [DynDllImport(libraryName: "LZ4.dll")] + public static _ResetDecompresssCTX ResetDecompresssCTX; +} \ No newline at end of file diff --git a/CompressSave/LZ4Wrap/PeekableReader.cs b/CompressSave/LZ4Wrap/PeekableReader.cs new file mode 100644 index 0000000..b74bf66 --- /dev/null +++ b/CompressSave/LZ4Wrap/PeekableReader.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace CompressSave.LZ4Wrap; + +class PeekableReader : BinaryReader +{ + LZ4DecompressionStream lzstream; + public PeekableReader(LZ4DecompressionStream input) : base (input) + { + lzstream = input; + } + + public override int PeekChar() + { + return lzstream.PeekByte(); + } +} \ No newline at end of file diff --git a/CompressSave/LZ4WrapC/CMakeLists.txt b/CompressSave/LZ4WrapC/CMakeLists.txt new file mode 100644 index 0000000..e65d39e --- /dev/null +++ b/CompressSave/LZ4WrapC/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.2) + +project(LZ4Wrap) + +add_library(LZ4 SHARED + lz4/lz4.c lz4/lz4.h + lz4/lz4frame.c lz4/lz4frame.h lz4/lz4frame_static.h + lz4/lz4hc.c lz4/lz4hc.h + lz4/xxhash.c lz4/xxhash.h + dllmain.c LZ4Wrap.c LZ4Wrap.h) + +target_compile_definitions(LZ4 PRIVATE LZ4WRAP_EXPORTS) +target_include_directories(LZ4 PRIVATE lz4) +if(WIN32) + set_target_properties(LZ4 PROPERTIES PREFIX "") +endif() diff --git a/CompressSave/LZ4WrapC/LZ4Wrap.c b/CompressSave/LZ4WrapC/LZ4Wrap.c new file mode 100644 index 0000000..92861de --- /dev/null +++ b/CompressSave/LZ4WrapC/LZ4Wrap.c @@ -0,0 +1,150 @@ +// MathLibrary.cpp : Defines the exported functions for the DLL. +#include "LZ4Wrap.h" + +#include +#include +#include +#include +#define Check(assert,errorcode) if(!(assert)) {printf(LZ4F_getErrorName(errorcode)); return errorcode;} + + + +static CContext* CreateCompressContext() +{ + return (CContext*)malloc(sizeof(CContext)); +} + +void FreeCompressContext(CContext* ctx) +{ + if (ctx != NULL) + { + LZ4F_freeCompressionContext(ctx->cctx); + LZ4F_freeCDict(ctx->dict); + free(ctx); + } +} + +const LZ4F_preferences_t kPrefs = { + { LZ4F_max4MB, LZ4F_blockLinked, LZ4F_contentChecksumEnabled, LZ4F_frame, + 0 /* unknown content size */, 0/* no dictID */ , LZ4F_blockChecksumEnabled }, + 0, /* compression level; 0 == default */ + 0, /* autoflush */ + 0, /* favor decompression speed */ + { 0, 0, 0 }, /* reserved, must be set to 0 */ +}; + +size_t CalCompressOutBufferSize(size_t inBufferSize) +{ + return LZ4F_compressBound(inBufferSize, &kPrefs) + LZ4F_HEADER_SIZE_MAX; +} + +CContext* CreateCompressContextFromBuffer(void* dictBuffer, size_t dictSize) { + CContext* ctx = CreateCompressContext(); + if (dictBuffer) + ctx->dict = LZ4F_createCDict(dictBuffer, dictSize); + else + ctx->dict = NULL; + if (ctx == NULL) return NULL; + LZ4F_compressionContext_t innerctx; + + LZ4F_errorCode_t ctxCreation = LZ4F_createCompressionContext(&innerctx, LZ4F_VERSION); + if (LZ4F_isError(ctxCreation)) + { + LZ4F_freeCompressionContext(innerctx); + FreeCompressContext(ctx); + return NULL; + } + ctx->cctx = innerctx; + + return ctx; +} + +size_t CompressBegin(CContext** pctx, void* outBuff , size_t outCapacity, void* dictBuffer, size_t dictSize) +{ + CContext* ctx = CreateCompressContextFromBuffer(dictBuffer, dictSize); + if (ctx == NULL) return -1; + + if (outCapacity < LZ4F_HEADER_SIZE_MAX || outCapacity < LZ4F_compressBound(0, &kPrefs)) return LZ4F_ERROR_dstMaxSize_tooSmall; + + + /* write frame header */ + size_t const headerSize = ctx->dict == NULL + ? LZ4F_compressBegin(ctx->cctx, outBuff, outCapacity, &kPrefs) + : LZ4F_compressBegin_usingCDict(ctx->cctx, outBuff, outCapacity, ctx->dict, &kPrefs); + + if (LZ4F_isError(headerSize)) + { + return headerSize; + } + *pctx = ctx; + return headerSize; +} + + +size_t CompressUpdate(CContext* ctx,void* dstBuffer, size_t dstCapacity,const void* srcBuffer, size_t srcSize) +{ + size_t result = ctx->dict == NULL + ? LZ4F_compressUpdate(ctx->cctx, dstBuffer, dstCapacity, srcBuffer, srcSize, NULL) + : LZ4F_compressFrame_usingCDict(ctx->cctx, dstBuffer, dstCapacity, srcBuffer, srcSize, ctx->dict, NULL); + if (LZ4F_isError(result)) + { + const char *str = LZ4F_getErrorName(result); + fprintf(stderr, "%s\n", str); + } + return result; +} + +size_t CompressEnd(CContext* ctx, void* dstBuffer, size_t dstCapacity) +{ + size_t writeSize = LZ4F_compressEnd(ctx->cctx, dstBuffer, dstCapacity, NULL); + return writeSize; +} + +static size_t get_block_size(const LZ4F_frameInfo_t* info) { + switch (info->blockSizeID) { + case LZ4F_default: + case LZ4F_max64KB: return 1 << 16; + case LZ4F_max256KB: return 1 << 18; + case LZ4F_max1MB: return 1 << 20; + case LZ4F_max4MB: return 1 << 22; + default: + return -1; + } +} +//return: input bytes expects for next call +size_t DecompressBegin(LZ4F_dctx **pdctx,void *inBuffer,size_t *inBufferSize, size_t *blockSize) +{ + LZ4F_dctx* dctx; + LZ4F_dctx** _pdctx = &dctx; + size_t const dctxStatus = LZ4F_createDecompressionContext(_pdctx, LZ4F_VERSION); + Check(!LZ4F_isError(dctxStatus), dctxStatus); + + Check(*inBufferSize >= LZ4F_HEADER_SIZE_MAX, LZ4F_ERROR_dstMaxSize_tooSmall); + + LZ4F_frameInfo_t info; + size_t const fires = LZ4F_getFrameInfo(*_pdctx, &info, inBuffer, inBufferSize); + Check(!LZ4F_isError(fires), fires); + + *blockSize = get_block_size(&info); + *pdctx = *_pdctx; + return fires; +} + +void ResetDecompresssCTX(LZ4F_dctx* dctx) +{ + LZ4F_resetDecompressionContext(dctx); +} + +size_t DecompressUpdate(LZ4F_dctx* dctx, void* outBuffer, size_t * outBufferSize, void* inBuffer, size_t * inBufferSize,void* dict,size_t dictSize) +{ + size_t ret = dict == NULL + ? LZ4F_decompress(dctx, outBuffer, outBufferSize, inBuffer, inBufferSize, NULL) + : LZ4F_decompress_usingDict(dctx, outBuffer, outBufferSize, inBuffer, inBufferSize, dict, dictSize,NULL); + Check(!LZ4F_isError(ret), ret); + return ret; +} + +size_t DecompressEnd(LZ4F_dctx* ctx) +{ + return LZ4F_freeDecompressionContext(ctx); +} \ No newline at end of file diff --git a/CompressSave/LZ4WrapC/LZ4Wrap.h b/CompressSave/LZ4WrapC/LZ4Wrap.h new file mode 100644 index 0000000..0e87927 --- /dev/null +++ b/CompressSave/LZ4WrapC/LZ4Wrap.h @@ -0,0 +1,39 @@ +#pragma once +#define LZ4F_STATIC_LINKING_ONLY +#if defined(__cplusplus) +#define API_EXTERN_C extern "C" +#else +#define API_EXTERN_C +#endif +#ifdef LZ4WRAP_EXPORTS +#define LZ4API API_EXTERN_C __declspec(dllexport) +#else +#define LZ4API API_EXTERN_C __declspec(dllimport) +#endif + +#include +#include + +typedef struct +{ + LZ4F_cctx* cctx; + LZ4F_CDict* dict; +} CContext; + +LZ4API void FreeCompressContext(CContext* ctx); + +LZ4API size_t CalCompressOutBufferSize(size_t inBufferSize); + +LZ4API size_t CompressBegin(CContext** ctx, void* outBuff, size_t outCapacity, void* dictBuffer, size_t dictSize); + +LZ4API size_t CompressUpdate(CContext* ctx, void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize); + +LZ4API size_t CompressEnd(CContext* ctx, void* dstBuffer, size_t dstCapacity); + +LZ4API size_t DecompressBegin(LZ4F_dctx** pdctx, void* inBuffer, size_t* inBufferSize, size_t* blockSize); + +LZ4API void ResetDecompresssCTX(LZ4F_dctx* dctx); + +LZ4API size_t DecompressUpdate(LZ4F_dctx* dctx, void* outBuffer, size_t* outBufferSize, void* inBuffer, size_t* inBufferSize, void* dict, size_t dictSize); + +LZ4API size_t DecompressEnd(LZ4F_dctx* dctx); diff --git a/CompressSave/LZ4WrapC/dllmain.c b/CompressSave/LZ4WrapC/dllmain.c new file mode 100644 index 0000000..52d4ad2 --- /dev/null +++ b/CompressSave/LZ4WrapC/dllmain.c @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/CompressSave/LZ4WrapC/lz4/lz4.c b/CompressSave/LZ4WrapC/lz4/lz4.c new file mode 100644 index 0000000..8999137 --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4.c @@ -0,0 +1,2720 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * LZ4_HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef LZ4_HEAPMODE +# define LZ4_HEAPMODE 0 +#endif + +/* + * LZ4_ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define LZ4_ACCELERATION_DEFAULT 1 +/* + * LZ4_ACCELERATION_MAX : + * Any "acceleration" value higher than this threshold + * get treated as LZ4_ACCELERATION_MAX instead (fix #876) + */ +#define LZ4_ACCELERATION_MAX 65537 + + +/*-************************************ +* CPU Feature Detection +**************************************/ +/* LZ4_FORCE_MEMORY_ACCESS + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method is portable but violate C standard. + * It can generate buggy code on targets which assembly generation depends on alignment. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ +# if defined(__GNUC__) && \ + ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define LZ4_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) +# define LZ4_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ +# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ +# define LZ4_FORCE_SW_BITCOUNT +#endif + + + +/*-************************************ +* Dependency +**************************************/ +/* + * LZ4_SRC_INCLUDED: + * Amalgamation flag, whether lz4.c is included + */ +#ifndef LZ4_SRC_INCLUDED +# define LZ4_SRC_INCLUDED 1 +#endif + +#ifndef LZ4_STATIC_LINKING_ONLY +#define LZ4_STATIC_LINKING_ONLY +#endif + +#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS +#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ +#endif + +#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ +#include "lz4.h" +/* see also "memory routines" below */ + + +/*-************************************ +* Compiler Options +**************************************/ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ +# include /* only present in VS2005+ */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ +#endif /* _MSC_VER */ + +#ifndef LZ4_FORCE_INLINE +# ifdef _MSC_VER /* Visual Studio */ +# define LZ4_FORCE_INLINE static __forceinline +# else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define LZ4_FORCE_INLINE static inline +# endif +# else +# define LZ4_FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +# endif /* _MSC_VER */ +#endif /* LZ4_FORCE_INLINE */ + +/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE + * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, + * together with a simple 8-byte copy loop as a fall-back path. + * However, this optimization hurts the decompression speed by >30%, + * because the execution does not go to the optimized loop + * for typical compressible data, and all of the preamble checks + * before going to the fall-back path become useless overhead. + * This optimization happens only with the -O3 flag, and -O2 generates + * a simple 8-byte copy loop. + * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 + * functions are annotated with __attribute__((optimize("O2"))), + * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute + * of LZ4_wildCopy8 does not affect the compression speed. + */ +#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) +# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) +# undef LZ4_FORCE_INLINE +# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) +#else +# define LZ4_FORCE_O2 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#ifndef likely +#define likely(expr) expect((expr) != 0, 1) +#endif +#ifndef unlikely +#define unlikely(expr) expect((expr) != 0, 0) +#endif + +/* Should the alignment test prove unreliable, for some reason, + * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ +#ifndef LZ4_ALIGN_TEST /* can be externally provided */ +# define LZ4_ALIGN_TEST 1 +#endif + + +/*-************************************ +* Memory routines +**************************************/ + +/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION : + * Disable relatively high-level LZ4/HC functions that use dynamic memory + * allocation functions (malloc(), calloc(), free()). + * + * Note that this is a compile-time switch. And since it disables + * public/stable LZ4 v1 API functions, we don't recommend using this + * symbol to generate a library for distribution. + * + * The following public functions are removed when this symbol is defined. + * - lz4 : LZ4_createStream, LZ4_freeStream, + * LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated) + * - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC, + * LZ4_createHC (deprecated), LZ4_freeHC (deprecated) + * - lz4frame, lz4file : All LZ4F_* functions + */ +#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +# define ALLOC(s) lz4_error_memory_allocation_is_disabled +# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled +# define FREEMEM(p) lz4_error_memory_allocation_is_disabled +#elif defined(LZ4_USER_MEMORY_FUNCTIONS) +/* memory management functions can be customized by user project. + * Below functions must exist somewhere in the Project + * and be available at link time */ +void* LZ4_malloc(size_t s); +void* LZ4_calloc(size_t n, size_t s); +void LZ4_free(void* p); +# define ALLOC(s) LZ4_malloc(s) +# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) +# define FREEMEM(p) LZ4_free(p) +#else +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,s) +# define FREEMEM(p) free(p) +#endif + +#if ! LZ4_FREESTANDING +# include /* memset, memcpy */ +#endif +#if !defined(LZ4_memset) +# define LZ4_memset(p,v,s) memset((p),(v),(s)) +#endif +#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s)) + + +/*-************************************ +* Common Constants +**************************************/ +#define MINMATCH 4 + +#define WILDCOPYLENGTH 8 +#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ +#define FASTLOOP_SAFE_DISTANCE 64 +static const int LZ4_minLength = (MFLIMIT+1); + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 +#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ +# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" +#endif + +#define ML_BITS 4 +#define ML_MASK ((1U<=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) +# include + static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ ": "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + +static int LZ4_isAligned(const void* ptr, size_t alignment) +{ + return ((size_t)ptr & (alignment -1)) == 0; +} + + +/*-************************************ +* Types +**************************************/ +#include +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; + typedef uintptr_t uptrval; +#else +# if UINT_MAX != 4294967295UL +# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" +# endif + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; + typedef size_t uptrval; /* generally true, except OpenVMS-64 */ +#endif + +#if defined(__x86_64__) + typedef U64 reg_t; /* 64-bits in x32 mode */ +#else + typedef size_t reg_t; /* 32-bits in x32 mode */ +#endif + +typedef enum { + notLimited = 0, + limitedOutput = 1, + fillOutput = 2 +} limitedOutput_directive; + + +/*-************************************ +* Reading and writing into memory +**************************************/ + +/** + * LZ4 relies on memcpy with a constant size being inlined. In freestanding + * environments, the compiler can't assume the implementation of memcpy() is + * standard compliant, so it can't apply its specialized memcpy() inlining + * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze + * memcpy() as if it were standard compliant, so it can inline it in freestanding + * environments. This is needed when decompressing the Linux Kernel, for example. + */ +#if !defined(LZ4_memcpy) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) +# else +# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +# endif +#endif + +#if !defined(LZ4_memmove) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memmove __builtin_memmove +# else +# define LZ4_memmove memmove +# endif +#endif + +static unsigned LZ4_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + + +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ + +static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } +static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } +static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } + +static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } +static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; + +static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } +static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } +static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } + +static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } +static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } + +#else /* safe and portable access using memcpy() */ + +static U16 LZ4_read16(const void* memPtr) +{ + U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static U32 LZ4_read32(const void* memPtr) +{ + U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static reg_t LZ4_read_ARCH(const void* memPtr) +{ + reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static void LZ4_write16(void* memPtr, U16 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +static void LZ4_write32(void* memPtr, U32 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + + +static U16 LZ4_readLE16(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read16(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U16)((U16)p[0] + (p[1]<<8)); + } +} + +static void LZ4_writeLE16(void* memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) { + LZ4_write16(memPtr, value); + } else { + BYTE* p = (BYTE*)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ +LZ4_FORCE_INLINE +void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ +LZ4_FORCE_INLINE void +LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH + * - there is at least 8 bytes available to write after dstEnd */ +LZ4_FORCE_INLINE void +LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) +{ + BYTE v[8]; + + assert(dstEnd >= dstPtr + MINMATCH); + + switch(offset) { + case 1: + MEM_INIT(v, *srcPtr, 8); + break; + case 2: + LZ4_memcpy(v, srcPtr, 2); + LZ4_memcpy(&v[2], srcPtr, 2); +#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ +# pragma warning(push) +# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ +#endif + LZ4_memcpy(&v[4], v, 4); +#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ +# pragma warning(pop) +#endif + break; + case 4: + LZ4_memcpy(v, srcPtr, 4); + LZ4_memcpy(&v[4], srcPtr, 4); + break; + default: + LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); + return; + } + + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + while (dstPtr < dstEnd) { + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + } +} +#endif + + +/*-************************************ +* Common functions +**************************************/ +static unsigned LZ4_NbCommonBytes (reg_t val) +{ + assert(val != 0); + if (LZ4_isLittleEndian()) { + if (sizeof(val) == 8) { +# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) +/*-************************************************************************************************* +* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. +* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics +* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. +****************************************************************************************************/ +# if defined(__clang__) && (__clang_major__ < 10) + /* Avoid undefined clang-cl intrinsics issue. + * See https://github.com/lz4/lz4/pull/1017 for details. */ + return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; +# else + /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ + return (unsigned)_tzcnt_u64(val) >> 3; +# endif +# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64(&r, (U64)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctzll((U64)val) >> 3; +# else + const U64 m = 0x0101010101010101ULL; + val ^= val - 1; + return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); +# endif + } else /* 32 bits */ { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, (U32)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctz((U32)val) >> 3; +# else + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; +# endif + } + } else /* Big Endian CPU */ { + if (sizeof(val)==8) { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clzll((U64)val) >> 3; +# else +#if 1 + /* this method is probably faster, + * but adds a 128 bytes lookup table */ + static const unsigned char ctz7_tab[128] = { + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + }; + U64 const mask = 0x0101010101010101ULL; + U64 const t = (((val >> 8) - mask) | val) & mask; + return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; +#else + /* this method doesn't consume memory space like the previous one, + * but it contains several branches, + * that may end up slowing execution */ + static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. + Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. + Note that this code path is never triggered in 32-bits mode. */ + unsigned r; + if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; +#endif +# endif + } else /* 32 bits */ { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clz((U32)val) >> 3; +# else + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; +# endif + } + } +} + + +#define STEPSIZE sizeof(reg_t) +LZ4_FORCE_INLINE +unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) +{ + const BYTE* const pStart = pIn; + + if (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { + pIn+=STEPSIZE; pMatch+=STEPSIZE; + } else { + return LZ4_NbCommonBytes(diff); + } } + + while (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } + pIn += LZ4_NbCommonBytes(diff); + return (unsigned)(pIn - pStart); + } + + if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } + if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } + if ((pIn compression run slower on incompressible data */ + + +/*-************************************ +* Local Structures and types +**************************************/ +typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; + +/** + * This enum distinguishes several different modes of accessing previous + * content in the stream. + * + * - noDict : There is no preceding content. + * - withPrefix64k : Table entries up to ctx->dictSize before the current blob + * blob being compressed are valid and refer to the preceding + * content (of length ctx->dictSize), which is available + * contiguously preceding in memory the content currently + * being compressed. + * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere + * else in memory, starting at ctx->dictionary with length + * ctx->dictSize. + * - usingDictCtx : Everything concerning the preceding content is + * in a separate context, pointed to by ctx->dictCtx. + * ctx->dictionary, ctx->dictSize, and table entries + * in the current context that refer to positions + * preceding the beginning of the current compression are + * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx + * ->dictSize describe the location and size of the preceding + * content, and matches are found by looking in the ctx + * ->dictCtx->hashTable. + */ +typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; + + +/*-************************************ +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); } + + +/*-**************************************** +* Internal Definitions, used only in Tests +*******************************************/ +#if defined (__cplusplus) +extern "C" { +#endif + +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); + +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize); +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize); +#if defined (__cplusplus) +} +#endif + +/*-****************************** +* Compression functions +********************************/ +LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); + else + return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); +} + +LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + if (LZ4_isLittleEndian()) { + const U64 prime5bytes = 889523592379ULL; + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + } else { + const U64 prime8bytes = 11400714785074694791ULL; + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); + } +} + +LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) +{ + if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); + return LZ4_hash4(LZ4_read32(p), tableType); +} + +LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: /* fallthrough */ + case byPtr: { /* illegal! */ assert(0); return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } + case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, + void* tableBase, tableType_t const tableType, + const BYTE* srcBase) +{ + switch (tableType) + { + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); +} + +/* LZ4_getIndexOnHash() : + * Index of match position registered in hash table. + * hash position must be calculated by using base+index, or dictBase+index. + * Assumption 1 : only valid if tableType == byU32 or byU16. + * Assumption 2 : h is presumed valid (within limits of hash table) + */ +LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) +{ + LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); + if (tableType == byU32) { + const U32* const hashTable = (const U32*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-2))); + return hashTable[h]; + } + if (tableType == byU16) { + const U16* const hashTable = (const U16*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-1))); + return hashTable[h]; + } + assert(0); return 0; /* forbidden case */ +} + +static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType) +{ + assert(tableType == byPtr); (void)tableType; + { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } +} + +LZ4_FORCE_INLINE const BYTE* +LZ4_getPosition(const BYTE* p, + const void* tableBase, tableType_t tableType) +{ + U32 const h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType); +} + +LZ4_FORCE_INLINE void +LZ4_prepareTable(LZ4_stream_t_internal* const cctx, + const int inputSize, + const tableType_t tableType) { + /* If the table hasn't been used, it's guaranteed to be zeroed out, and is + * therefore safe to use no matter what mode we're in. Otherwise, we figure + * out if it's safe to leave as is or whether it needs to be reset. + */ + if ((tableType_t)cctx->tableType != clearedTable) { + assert(inputSize >= 0); + if ((tableType_t)cctx->tableType != tableType + || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) + || ((tableType == byU32) && cctx->currentOffset > 1 GB) + || tableType == byPtr + || inputSize >= 4 KB) + { + DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); + MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); + cctx->currentOffset = 0; + cctx->tableType = (U32)clearedTable; + } else { + DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); + } + } + + /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, + * is faster than compressing without a gap. + * However, compressing with currentOffset == 0 is faster still, + * so we preserve that case. + */ + if (cctx->currentOffset != 0 && tableType == byU32) { + DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); + cctx->currentOffset += 64 KB; + } + + /* Finally, clear history */ + cctx->dictCtx = NULL; + cctx->dictionary = NULL; + cctx->dictSize = 0; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time. + * Presumed already validated at this stage: + * - source != NULL + * - inputSize > 0 + */ +LZ4_FORCE_INLINE int LZ4_compress_generic_validated( + LZ4_stream_t_internal* const cctx, + const char* const source, + char* const dest, + const int inputSize, + int* inputConsumed, /* only written when outputDirective == fillOutput */ + const int maxOutputSize, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + int result; + const BYTE* ip = (const BYTE*) source; + + U32 const startIndex = cctx->currentOffset; + const BYTE* base = (const BYTE*) source - startIndex; + const BYTE* lowLimit; + + const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; + const BYTE* const dictionary = + dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; + const U32 dictSize = + dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; + const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ + + int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); + U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ + const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; + const BYTE* anchor = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; + const BYTE* const matchlimit = iend - LASTLITERALS; + + /* the dictCtx currentOffset is indexed on the start of the dictionary, + * while a dictionary in the current context precedes the currentOffset */ + const BYTE* dictBase = (dictionary == NULL) ? NULL : + (dictDirective == usingDictCtx) ? + dictionary + dictSize - dictCtx->currentOffset : + dictionary + dictSize - startIndex; + + BYTE* op = (BYTE*) dest; + BYTE* const olimit = op + maxOutputSize; + + U32 offset = 0; + U32 forwardH; + + DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); + assert(ip != NULL); + /* If init conditions are not met, we don't have to mark stream + * as having dirty context, since no action was taken yet */ + if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ + if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ + if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ + assert(acceleration >= 1); + + lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); + + /* Update context state */ + if (dictDirective == usingDictCtx) { + /* Subsequent linked blocks can't use the dictionary. */ + /* Instead, they use the block we just compressed. */ + cctx->dictCtx = NULL; + cctx->dictSize = (U32)inputSize; + } else { + cctx->dictSize += (U32)inputSize; + } + cctx->currentOffset += (U32)inputSize; + cctx->tableType = (U32)tableType; + + if (inputSizehashTable, tableType, base); + ip++; forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for ( ; ; ) { + const BYTE* match; + BYTE* token; + const BYTE* filledIp; + + /* Find a match */ + if (tableType == byPtr) { + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); + + } while ( (match+LZ4_DISTANCE_MAX < ip) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + + } else { /* byU32, byU16 */ + + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + U32 const current = (U32)(forwardIp - base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex <= current); + assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + assert(tableType == byU32); + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + matchIndex += dictDelta; /* make dictCtx index comparable with current context */ + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else if (dictDirective == usingExtDict) { + if (matchIndex < startIndex) { + DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); + assert(startIndex - matchIndex >= MINMATCH); + assert(dictBase); + match = dictBase + matchIndex; + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else { /* single continuous memory segment */ + match = base + matchIndex; + } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + + DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); + if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ + assert(matchIndex < current); + if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) + && (matchIndex+LZ4_DISTANCE_MAX < current)) { + continue; + } /* too far */ + assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ + + if (LZ4_read32(match) == LZ4_read32(ip)) { + if (maybe_extMem) offset = current - matchIndex; + break; /* match found */ + } + + } while(1); + } + + /* Catch up */ + filledIp = ip; + while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + + /* Encode Literals */ + { unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ + (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + if ((outputDirective == fillOutput) && + (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { + op--; + goto _last_literals; + } + if (litLength >= RUN_MASK) { + int len = (int)(litLength - RUN_MASK); + *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< olimit)) { + /* the match was too close to the end, rewind and go to last literals */ + op = token; + goto _last_literals; + } + + /* Encode Offset */ + if (maybe_extMem) { /* static test */ + DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); + assert(offset <= LZ4_DISTANCE_MAX && offset > 0); + LZ4_writeLE16(op, (U16)offset); op+=2; + } else { + DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); + assert(ip-match <= LZ4_DISTANCE_MAX); + LZ4_writeLE16(op, (U16)(ip - match)); op+=2; + } + + /* Encode MatchLength */ + { unsigned matchCode; + + if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) + && (lowLimit==dictionary) /* match within extDict */ ) { + const BYTE* limit = ip + (dictEnd-match); + assert(dictEnd > match); + if (limit > matchlimit) limit = matchlimit; + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); + ip += (size_t)matchCode + MINMATCH; + if (ip==limit) { + unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); + matchCode += more; + ip += more; + } + DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); + } else { + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + ip += (size_t)matchCode + MINMATCH; + DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); + } + + if ((outputDirective) && /* Check output buffer overflow */ + (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { + if (outputDirective == fillOutput) { + /* Match description too long : reduce it */ + U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; + ip -= matchCode - newMatchCode; + assert(newMatchCode < matchCode); + matchCode = newMatchCode; + if (unlikely(ip <= filledIp)) { + /* We have already filled up to filledIp so if ip ends up less than filledIp + * we have positions in the hash table beyond the current position. This is + * a problem if we reuse the hash table. So we have to remove these positions + * from the hash table. + */ + const BYTE* ptr; + DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); + for (ptr = ip; ptr <= filledIp; ++ptr) { + U32 const h = LZ4_hashPosition(ptr, tableType); + LZ4_clearHash(h, cctx->hashTable, tableType); + } + } + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LZ4_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*255) { + op+=4; + LZ4_write32(op, 0xFFFFFFFF); + matchCode -= 4*255; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else + *token += (BYTE)(matchCode); + } + /* Ensure we have enough space for the last literals. */ + assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); + + anchor = ip; + + /* Test end of chunk */ + if (ip >= mflimitPlusOne) break; + + /* Fill table */ + LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); + + /* Test next position */ + if (tableType == byPtr) { + + match = LZ4_getPosition(ip, cctx->hashTable, tableType); + LZ4_putPosition(ip, cctx->hashTable, tableType, base); + if ( (match+LZ4_DISTANCE_MAX >= ip) + && (LZ4_read32(match) == LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + } else { /* byU32, byU16 */ + + U32 const h = LZ4_hashPosition(ip, tableType); + U32 const current = (U32)(ip-base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex < current); + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + matchIndex += dictDelta; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else if (dictDirective==usingExtDict) { + if (matchIndex < startIndex) { + assert(dictBase); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else { /* single memory segment */ + match = base + matchIndex; + } + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + assert(matchIndex < current); + if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) + && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) + && (LZ4_read32(match) == LZ4_read32(ip)) ) { + token=op++; + *token=0; + if (maybe_extMem) offset = current - matchIndex; + DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", + (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); + goto _next_match; + } + } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRun = (size_t)(iend - anchor); + if ( (outputDirective) && /* Check output buffer overflow */ + (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { + if (outputDirective == fillOutput) { + /* adapt lastRun to fill 'dst' */ + assert(olimit >= op); + lastRun = (size_t)(olimit-op) - 1/*token*/; + lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRun< 0); + DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); + return result; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time; + * takes care of src == (NULL, 0) + * and forward the rest to LZ4_compress_generic_validated */ +LZ4_FORCE_INLINE int LZ4_compress_generic( + LZ4_stream_t_internal* const cctx, + const char* const src, + char* const dst, + const int srcSize, + int *inputConsumed, /* only written when outputDirective == fillOutput */ + const int dstCapacity, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", + srcSize, dstCapacity); + + if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ + if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ + if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ + DEBUGLOG(5, "Generating an empty block"); + assert(outputDirective == notLimited || dstCapacity >= 1); + assert(dst != NULL); + dst[0] = 0; + if (outputDirective == fillOutput) { + assert (inputConsumed != NULL); + *inputConsumed = 0; + } + return 1; + } + assert(src != NULL); + + return LZ4_compress_generic_validated(cctx, src, dst, srcSize, + inputConsumed, /* only written into if outputDirective == fillOutput */ + dstCapacity, outputDirective, + tableType, dictDirective, dictIssue, acceleration); +} + + +int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; + assert(ctx != NULL); + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + if (maxOutputSize >= LZ4_compressBound(inputSize)) { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + +/** + * LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of + * "correctly initialized"). + */ +int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) +{ + LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + if (dstCapacity >= LZ4_compressBound(srcSize)) { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + + +int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + int result; +#if (LZ4_HEAPMODE) + LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctxPtr == NULL) return 0; +#else + LZ4_stream_t ctx; + LZ4_stream_t* const ctxPtr = &ctx; +#endif + result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); + +#if (LZ4_HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + + +int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) +{ + return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); +} + + +/* Note!: This function leaves the stream in an unclean/broken state! + * It is not safe to subsequently use the same state with a _fastReset() or + * _continue() call without resetting it. */ +static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ + void* const s = LZ4_initStream(state, sizeof (*state)); + assert(s != NULL); (void)s; + + if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } else { + if (*srcSizePtr < LZ4_64Klimit) { + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); + } else { + tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); + } } +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (LZ4_HEAPMODE) + LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctx == NULL) return 0; +#else + LZ4_stream_t ctxBody; + LZ4_stream_t* ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + +#if (LZ4_HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + + + +/*-****************************** +* Streaming functions +********************************/ + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_stream_t* LZ4_createStream(void) +{ + LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); + LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal)); + DEBUGLOG(4, "LZ4_createStream %p", lz4s); + if (lz4s == NULL) return NULL; + LZ4_initStream(lz4s, sizeof(*lz4s)); + return lz4s; +} +#endif + +static size_t LZ4_stream_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_stream_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_stream_t); +#else + return 1; /* effectively disabled */ +#endif +} + +LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) +{ + DEBUGLOG(5, "LZ4_initStream"); + if (buffer == NULL) { return NULL; } + if (size < sizeof(LZ4_stream_t)) { return NULL; } + if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; + MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); + return (LZ4_stream_t*)buffer; +} + +/* resetStream is now deprecated, + * prefer initStream() which is more general */ +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); +} + +void LZ4_resetStream_fast(LZ4_stream_t* ctx) { + LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +int LZ4_freeStream (LZ4_stream_t* LZ4_stream) +{ + if (!LZ4_stream) return 0; /* support free on NULL */ + DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); + FREEMEM(LZ4_stream); + return (0); +} +#endif + + +#define HASH_UNIT sizeof(reg_t) +int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; + const tableType_t tableType = byU32; + const BYTE* p = (const BYTE*)dictionary; + const BYTE* const dictEnd = p + dictSize; + const BYTE* base; + + DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); + + /* It's necessary to reset the context, + * and not just continue it with prepareTable() + * to avoid any risk of generating overflowing matchIndex + * when compressing using this dictionary */ + LZ4_resetStream(LZ4_dict); + + /* We always increment the offset by 64 KB, since, if the dict is longer, + * we truncate it to the last 64k, and if it's shorter, we still want to + * advance by a whole window length so we can provide the guarantee that + * there are only valid offsets in the window, which allows an optimization + * in LZ4_compress_fast_continue() where it uses noDictIssue even when the + * dictionary isn't a full 64k. */ + dict->currentOffset += 64 KB; + + if (dictSize < (int)HASH_UNIT) { + return 0; + } + + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + base = dictEnd - dict->currentOffset; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->tableType = (U32)tableType; + + while (p <= dictEnd-HASH_UNIT) { + LZ4_putPosition(p, dict->hashTable, tableType, base); + p+=3; + } + + return (int)dict->dictSize; +} + +void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) +{ + const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : + &(dictionaryStream->internal_donotuse); + + DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", + workingStream, dictionaryStream, + dictCtx != NULL ? dictCtx->dictSize : 0); + + if (dictCtx != NULL) { + /* If the current offset is zero, we will never look in the + * external dictionary context, since there is no value a table + * entry can take that indicate a miss. In that case, we need + * to bump the offset to something non-zero. + */ + if (workingStream->internal_donotuse.currentOffset == 0) { + workingStream->internal_donotuse.currentOffset = 64 KB; + } + + /* Don't actually attach an empty dictionary. + */ + if (dictCtx->dictSize == 0) { + dictCtx = NULL; + } + } + workingStream->internal_donotuse.dictCtx = dictCtx; +} + + +static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) +{ + assert(nextSize >= 0); + if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ + /* rescale hash table */ + U32 const delta = LZ4_dict->currentOffset - 64 KB; + const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + DEBUGLOG(4, "LZ4_renormDictT"); + for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; + else LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + + +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, + const char* source, char* dest, + int inputSize, int maxOutputSize, + int acceleration) +{ + const tableType_t tableType = byU32; + LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; + const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; + + DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); + + LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + /* invalidate tiny dictionaries */ + if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ + && (dictEnd != source) /* prefix mode */ + && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ + && (streamPtr->dictCtx == NULL) /* usingDictCtx */ + ) { + DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); + /* remove dictionary existence from history, to employ faster prefix mode */ + streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)source; + dictEnd = source; + } + + /* Check overlapping input/dictionary space */ + { const char* const sourceEnd = source + inputSize; + if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == source) { + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); + else + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); + } + + /* external dictionary mode */ + { int result; + if (streamPtr->dictCtx) { + /* We depend here on the fact that dictCtx'es (produced by + * LZ4_loadDict) guarantee that their tables contain no references + * to offsets between dictCtx->currentOffset - 64 KB and + * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe + * to use noDictIssue even when the dict isn't a full 64 KB. + */ + if (inputSize > 4 KB) { + /* For compressing large blobs, it is faster to pay the setup + * cost to copy the dictionary's tables into the active context, + * so that the compression loop is only looking into one table. + */ + LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); + } + } else { /* small data <= 4 KB */ + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } + } + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + return result; + } +} + + +/* Hidden debug function, to force-test external dictionary mode */ +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) +{ + LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; + int result; + + LZ4_renormDictT(streamPtr, srcSize); + + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + } + + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)srcSize; + + return result; +} + + +/*! LZ4_saveDict() : + * If previously compressed data block is not guaranteed to remain available at its memory location, + * save it into a safer place (char* safeBuffer). + * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, + * one can therefore call LZ4_compress_fast_continue() right after. + * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + */ +int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) +{ + LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + + DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); + + if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } + + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) { + const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + assert(dict->dictionary); + LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize); + } + + dict->dictionary = (const BYTE*)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + + + +/*-******************************* + * Decompression functions + ********************************/ + +typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; + +#undef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + + +/* variant for decompress_unsafe() + * does not know end of input + * presumes input is well formed + * note : will consume at least one byte */ +size_t read_long_length_no_check(const BYTE** pp) +{ + size_t b, l = 0; + do { b = **pp; (*pp)++; l += b; } while (b==255); + DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1) + return l; +} + +/* core decoder variant for LZ4_decompress_fast*() + * for legacy support only : these entry points are deprecated. + * - Presumes input is correctly formed (no defense vs malformed inputs) + * - Does not know input size (presume input buffer is "large enough") + * - Decompress a full block (only) + * @return : nb of bytes read from input. + * Note : this variant is not optimized for speed, just for maintenance. + * the goal is to remove support of decompress_fast*() variants by v2.0 +**/ +LZ4_FORCE_INLINE int +LZ4_decompress_unsafe_generic( + const BYTE* const istart, + BYTE* const ostart, + int decompressedSize, + + size_t prefixSize, + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note: =0 if dictStart==NULL */ + ) +{ + const BYTE* ip = istart; + BYTE* op = (BYTE*)ostart; + BYTE* const oend = ostart + decompressedSize; + const BYTE* const prefixStart = ostart - prefixSize; + + DEBUGLOG(5, "LZ4_decompress_unsafe_generic"); + if (dictStart == NULL) assert(dictSize == 0); + + while (1) { + /* start new sequence */ + unsigned token = *ip++; + + /* literals */ + { size_t ll = token >> ML_BITS; + if (ll==15) { + /* long literal length */ + ll += read_long_length_no_check(&ip); + } + if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */ + LZ4_memmove(op, ip, ll); /* support in-place decompression */ + op += ll; + ip += ll; + if ((size_t)(oend-op) < MFLIMIT) { + if (op==oend) break; /* end of block */ + DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op); + /* incorrect end of block : + * last match must start at least MFLIMIT==12 bytes before end of output block */ + return -1; + } } + + /* match */ + { size_t ml = token & 15; + size_t const offset = LZ4_readLE16(ip); + ip+=2; + + if (ml==15) { + /* long literal length */ + ml += read_long_length_no_check(&ip); + } + ml += MINMATCH; + + if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */ + + { const BYTE* match = op - offset; + + /* out of range */ + if (offset > (size_t)(op - prefixStart) + dictSize) { + DEBUGLOG(6, "offset out of range"); + return -1; + } + + /* check special case : extDict */ + if (offset > (size_t)(op - prefixStart)) { + /* extDict scenario */ + const BYTE* const dictEnd = dictStart + dictSize; + const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart)); + size_t const extml = (size_t)(dictEnd - extMatch); + if (extml > ml) { + /* match entirely within extDict */ + LZ4_memmove(op, extMatch, ml); + op += ml; + ml = 0; + } else { + /* match split between extDict & prefix */ + LZ4_memmove(op, extMatch, extml); + op += extml; + ml -= extml; + } + match = prefixStart; + } + + /* match copy - slow variant, supporting overlap copy */ + { size_t u; + for (u=0; u= ipmax before start of loop. Returns initial_error if so. + * @error (output) - error code. Must be set to 0 before call. +**/ +typedef size_t Rvl_t; +static const Rvl_t rvl_error = (Rvl_t)(-1); +LZ4_FORCE_INLINE Rvl_t +read_variable_length(const BYTE** ip, const BYTE* ilimit, + int initial_check) +{ + Rvl_t s, length = 0; + assert(ip != NULL); + assert(*ip != NULL); + assert(ilimit != NULL); + if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ + return rvl_error; + } + do { + s = **ip; + (*ip)++; + length += s; + if (unlikely((*ip) > ilimit)) { /* read limit reached */ + return rvl_error; + } + /* accumulator overflow detection (32-bit mode only) */ + if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + return rvl_error; + } + } while (s==255); + + return length; +} + +/*! LZ4_decompress_generic() : + * This generic decompression function covers all use cases. + * It shall be instantiated several times, using different sets of directives. + * Note that it is important for performance that this function really get inlined, + * in order to remove useless branches during compilation optimization. + */ +LZ4_FORCE_INLINE int +LZ4_decompress_generic( + const char* const src, + char* const dst, + int srcSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ + + earlyEnd_directive partialDecoding, /* full, partial */ + dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + if ((src == NULL) || (outputSize < 0)) { return -1; } + + { const BYTE* ip = (const BYTE*) src; + const BYTE* const iend = ip + srcSize; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + outputSize; + BYTE* cpy; + + const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; + + const int checkOffset = (dictSize < (int)(64 KB)); + + + /* Set up the "end" pointers for the shortcut. */ + const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/; + const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/; + + const BYTE* match; + size_t offset; + unsigned token; + size_t length; + + + DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); + + /* Special cases */ + assert(lowPrefix <= op); + if (unlikely(outputSize==0)) { + /* Empty output buffer */ + if (partialDecoding) return 0; + return ((srcSize==1) && (*ip==0)) ? 0 : -1; + } + if (unlikely(srcSize==0)) { return -1; } + + /* LZ4_FAST_DEC_LOOP: + * designed for modern OoO performance cpus, + * where copying reliably 32-bytes is preferable to an unpredictable branch. + * note : fast loop may show a regression for some client arm chips. */ +#if LZ4_FAST_DEC_LOOP + if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(6, "skip fast decode loop"); + goto safe_decode; + } + + /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */ + while (1) { + /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ + assert(oend - op >= FASTLOOP_SAFE_DISTANCE); + assert(ip < iend); + token = *ip++; + length = token >> ML_BITS; /* literal length */ + + /* decode literal length */ + if (length == RUN_MASK) { + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + + /* copy literals */ + cpy = op+length; + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } + LZ4_wildCopy32(op, ip, cpy); + ip += length; op = cpy; + } else { + cpy = op+length; + DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); + /* We don't need to check oend, since we check it once for each loop below */ + if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } + /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ + LZ4_memcpy(op, ip, 16); + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + assert(match <= op); /* overflow check */ + + /* get matchlength */ + length = token & ML_MASK; + + if (length == ML_MASK) { + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; + length += MINMATCH; + if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + } else { + length += MINMATCH; + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + + /* Fastpath check: skip LZ4_wildCopy32 when true */ + if ((dict == withPrefix64k) || (match >= lowPrefix)) { + if (offset >= 8) { + assert(match >= lowPrefix); + assert(match <= op); + assert(op + 18 <= oend); + + LZ4_memcpy(op, match, 8); + LZ4_memcpy(op+8, match+8, 8); + LZ4_memcpy(op+16, match+16, 2); + op += length; + continue; + } } } + + if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) { + DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); + length = MIN(length, (size_t)(oend-op)); + } else { + goto _output_error; /* end-of-block condition violated */ + } } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) { *op++ = *copyFrom++; } + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + + /* copy match within block */ + cpy = op + length; + + assert((op <= oend) && (oend-op >= 32)); + if (unlikely(offset<16)) { + LZ4_memcpy_using_offset(op, match, cpy, offset); + } else { + LZ4_wildCopy32(op, match, cpy); + } + + op = cpy; /* wildcopy correction */ + } + safe_decode: +#endif + + /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ + while (1) { + assert(ip < iend); + token = *ip++; + length = token >> ML_BITS; /* literal length */ + + /* A two-stage shortcut for the most common case: + * 1) If the literal length is 0..14, and there is enough space, + * enter the shortcut and copy 16 bytes on behalf of the literals + * (in the fast mode, only 8 bytes can be safely copied this way). + * 2) Further if the match length is 4..18, copy 18 bytes in a similar + * manner; but we ensure that there's enough space in the output for + * those 18 bytes earlier, upon entering the shortcut (in other words, + * there is a combined check for both stages). + */ + if ( (length != RUN_MASK) + /* strictly "less than" on input, to re-enter the loop with at least one byte */ + && likely((ip < shortiend) & (op <= shortoend)) ) { + /* Copy the literals */ + LZ4_memcpy(op, ip, 16); + op += length; ip += length; + + /* The second stage: prepare for match copying, decode full info. + * If it doesn't work out, the info won't be wasted. */ + length = token & ML_MASK; /* match length */ + offset = LZ4_readLE16(ip); ip += 2; + match = op - offset; + assert(match <= op); /* check overflow */ + + /* Do not deal with overlapping matches. */ + if ( (length != ML_MASK) + && (offset >= 8) + && (dict==withPrefix64k || match >= lowPrefix) ) { + /* Copy the match. */ + LZ4_memcpy(op + 0, match + 0, 8); + LZ4_memcpy(op + 8, match + 8, 8); + LZ4_memcpy(op +16, match +16, 2); + op += length + MINMATCH; + /* Both stages worked, load the next token. */ + continue; + } + + /* The second stage didn't work out, but the info is ready. + * Propel it right to the point of match copying. */ + goto _copy_match; + } + + /* decode literal length */ + if (length == RUN_MASK) { + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + } + + /* copy literals */ + cpy = op+length; +#if LZ4_FAST_DEC_LOOP + safe_literal_copy: +#endif + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { + /* We've either hit the input parsing restriction or the output parsing restriction. + * In the normal scenario, decoding a full block, it must be the last sequence, + * otherwise it's an error (invalid input or dimensions). + * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. + */ + if (partialDecoding) { + /* Since we are partial decoding we may be in this block because of the output parsing + * restriction, which is not valid since the output buffer is allowed to be undersized. + */ + DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") + DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); + DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); + DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); + /* Finishing in the middle of a literals segment, + * due to lack of input. + */ + if (ip+length > iend) { + length = (size_t)(iend-ip); + cpy = op + length; + } + /* Finishing in the middle of a literals segment, + * due to lack of output space. + */ + if (cpy > oend) { + cpy = oend; + assert(op<=oend); + length = (size_t)(oend-op); + } + } else { + /* We must be on the last sequence (or invalid) because of the parsing limitations + * so check that we exactly consume the input and don't overrun the output buffer. + */ + if ((ip+length != iend) || (cpy > oend)) { + DEBUGLOG(6, "should have been last run of literals") + DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); + goto _output_error; + } + } + LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */ + ip += length; + op += length; + /* Necessarily EOF when !partialDecoding. + * When partialDecoding, it is EOF if we've either + * filled the output buffer or + * can't proceed with reading an offset for following match. + */ + if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { + break; + } + } else { + LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */ + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + + /* get matchlength */ + length = token & ML_MASK; + + _copy_match: + if (length == ML_MASK) { + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + +#if LZ4_FAST_DEC_LOOP + safe_match_copy: +#endif + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) length = MIN(length, (size_t)(oend-op)); + else goto _output_error; /* doesn't respect parsing restriction */ + } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + assert(match >= lowPrefix); + + /* copy match within block */ + cpy = op + length; + + /* partialDecoding : may end anywhere within the block */ + assert(op<=oend); + if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + size_t const mlen = MIN(length, (size_t)(oend-op)); + const BYTE* const matchEnd = match + mlen; + BYTE* const copyEnd = op + mlen; + if (matchEnd > op) { /* overlap copy */ + while (op < copyEnd) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, mlen); + } + op = copyEnd; + if (op == oend) { break; } + continue; + } + + if (unlikely(offset<8)) { + LZ4_write32(op, 0); /* silence msan warning when offset==0 */ + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += inc32table[offset]; + LZ4_memcpy(op+4, match, 4); + match -= dec64table[offset]; + } else { + LZ4_memcpy(op, match, 8); + match += 8; + } + op += 8; + + if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); + if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ + if (op < oCopyLimit) { + LZ4_wildCopy8(op, match, oCopyLimit); + match += oCopyLimit - op; + op = oCopyLimit; + } + while (op < cpy) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, 8); + if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } + } + op = cpy; /* wildcopy correction */ + } + + /* end of decoding */ + DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); + return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ + + /* Overflow error detected */ + _output_error: + return (int) (-(((const char*)ip)-src))-1; + } +} + + +/*===== Instantiate the API decoding functions. =====*/ + +LZ4_FORCE_O2 +int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, + decode_full_block, noDict, + (BYTE*)dest, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, + partial_decode, + noDict, (BYTE*)dst, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_fast(const char* source, char* dest, int originalSize) +{ + DEBUGLOG(5, "LZ4_decompress_fast"); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, NULL, 0); +} + +/*===== Instantiate a few more decoding cases, used more than once. =====*/ + +LZ4_FORCE_O2 /* Exported, an obsolete API function. */ +int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +/* Another obsolete API function, paired with the previous one. */ +int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, + size_t prefixSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, (const BYTE*)dictStart, dictSize); +} + +/* The "double dictionary" mode, for use with e.g. ring buffers: the first part + * of the dictionary is passed as prefix, and the second via dictStart + dictSize. + * These routines are used only once, in LZ4_decompress_*_continue(). + */ +LZ4_FORCE_INLINE +int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize, const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, usingExtDict, + (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); +} + +/*===== streaming decompression functions =====*/ + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_streamDecode_t* LZ4_createStreamDecode(void) +{ + LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal)); + return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); +} + +int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) +{ + if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ + FREEMEM(LZ4_stream); + return 0; +} +#endif + +/*! LZ4_setStreamDecode() : + * Use this function to instruct where to find the dictionary. + * This function is not necessary if previous data is still available where it was decoded. + * Loading a size of 0 is allowed (same effect as no dictionary). + * @return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + lz4sd->prefixSize = (size_t)dictSize; + if (dictSize) { + assert(dictionary != NULL); + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + } else { + lz4sd->prefixEnd = (const BYTE*) dictionary; + } + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/*! LZ4_decoderRingBufferSize() : + * when setting a ring buffer for streaming decompression (optional scenario), + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * Note : in a ring buffer scenario, + * blocks are presumed decompressed next to each other. + * When not enough space remains for next block (remainingSize < maxBlockSize), + * decoding resumes from beginning of ring buffer. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +int LZ4_decoderRingBufferSize(int maxBlockSize) +{ + if (maxBlockSize < 0) return 0; + if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; + if (maxBlockSize < 16) maxBlockSize = 16; + return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +LZ4_FORCE_O2 +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + + if (lz4sd->prefixSize == 0) { + /* The first call, no dictionary yet. */ + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + /* They're rolling the current segment. */ + if (lz4sd->prefixSize >= 64 KB - 1) + result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + else if (lz4sd->extDictSize == 0) + result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize); + else + result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)result; + lz4sd->prefixEnd += result; + } else { + /* The buffer wraps around, or they're switching to another buffer. */ + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } + + return result; +} + +LZ4_FORCE_O2 int +LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* source, char* dest, int originalSize) +{ + LZ4_streamDecode_t_internal* const lz4sd = + (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse); + int result; + + DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize); + assert(originalSize >= 0); + + if (lz4sd->prefixSize == 0) { + DEBUGLOG(5, "first invocation : no prefix nor extDict"); + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_fast(source, dest, originalSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + DEBUGLOG(5, "continue using existing prefix"); + result = LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + lz4sd->prefixSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)originalSize; + lz4sd->prefixEnd += originalSize; + } else { + DEBUGLOG(5, "prefix becomes extDict"); + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_fast_extDict(source, dest, originalSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } + + return result; +} + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) +{ + if (dictSize==0 || dictStart+dictSize == dest) + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + (size_t)dictSize, NULL, 0); + assert(dictSize >= 0); + return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); +} + + +/*=************************************************* +* Obsolete Functions +***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_default(source, dest, inputSize, maxOutputSize); +} +int LZ4_compress(const char* src, char* dest, int srcSize) +{ + return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); +} +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); +} +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); +} +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) +{ + return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); +} +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) +{ + return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); +} + +/* +These decompression functions are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress (const char* source, char* dest, int outputSize) +{ + return LZ4_decompress_fast(source, dest, outputSize); +} +int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) +{ + return LZ4_decompress_safe(source, dest, isize, maxOutputSize); +} + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); } + +int LZ4_resetStreamState(void* state, char* inputBuffer) +{ + (void)inputBuffer; + LZ4_resetStream((LZ4_stream_t*)state); + return 0; +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +void* LZ4_create (char* inputBuffer) +{ + (void)inputBuffer; + return LZ4_createStream(); +} +#endif + +char* LZ4_slideInputBuffer (void* state) +{ + /* avoid const char * -> char * conversion warning */ + return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; +} + +#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/CompressSave/LZ4WrapC/lz4/lz4.h b/CompressSave/LZ4WrapC/lz4/lz4.h new file mode 100644 index 0000000..491c608 --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4.h @@ -0,0 +1,842 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4_H_2983827168210 +#define LZ4_H_2983827168210 + +/* --- Dependency --- */ +#include /* size_t */ + + +/** + Introduction + + LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, + scalable with multi-cores CPU. It features an extremely fast decoder, with speed in + multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. + + The LZ4 compression library provides in-memory compression and decompression functions. + It gives full buffer control to user. + Compression can be done in: + - a single step (described as Simple Functions) + - a single step, reusing a context (described in Advanced Functions) + - unbounded multiple steps (described as Streaming compression) + + lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). + Decompressing such a compressed block requires additional metadata. + Exact metadata depends on exact decompression function. + For the typical case of LZ4_decompress_safe(), + metadata includes block's compressed size, and maximum bound of decompressed size. + Each application is free to encode and pass such metadata in whichever way it wants. + + lz4.h only handle blocks, it can not generate Frames. + + Blocks are different from Frames (doc/lz4_Frame_format.md). + Frames bundle both blocks and metadata in a specified manner. + Embedding metadata is required for compressed data to be self-contained and portable. + Frame format is delivered through a companion API, declared in lz4frame.h. + The `lz4` CLI can only manage frames. +*/ + +/*^*************************************************************** +* Export parameters +*****************************************************************/ +/* +* LZ4_DLL_EXPORT : +* Enable exporting of functions when building a Windows DLL +* LZ4LIB_VISIBILITY : +* Control library symbols visibility. +*/ +#ifndef LZ4LIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4LIB_VISIBILITY +# endif +#endif +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define LZ4LIB_API LZ4LIB_VISIBILITY +#endif + +/*! LZ4_FREESTANDING : + * When this macro is set to 1, it enables "freestanding mode" that is + * suitable for typical freestanding environment which doesn't support + * standard C library. + * + * - LZ4_FREESTANDING is a compile-time switch. + * - It requires the following macros to be defined: + * LZ4_memcpy, LZ4_memmove, LZ4_memset. + * - It only enables LZ4/HC functions which don't use heap. + * All LZ4F_* functions are not supported. + * - See tests/freestanding.c to check its basic setup. + */ +#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1) +# define LZ4_HEAPMODE 0 +# define LZ4HC_HEAPMODE 0 +# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1 +# if !defined(LZ4_memcpy) +# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'." +# endif +# if !defined(LZ4_memset) +# error "LZ4_FREESTANDING requires macro 'LZ4_memset'." +# endif +# if !defined(LZ4_memmove) +# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'." +# endif +#elif ! defined(LZ4_FREESTANDING) +# define LZ4_FREESTANDING 0 +#endif + + +/*------ Version ------*/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ + +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) + +#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE +#define LZ4_QUOTE(str) #str +#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) +#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */ + +LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */ +LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */ + + +/*-************************************ +* Tuning parameter +**************************************/ +#define LZ4_MEMORY_USAGE_MIN 10 +#define LZ4_MEMORY_USAGE_DEFAULT 14 +#define LZ4_MEMORY_USAGE_MAX 20 + +/*! + * LZ4_MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) + * Increasing memory usage improves compression ratio, at the cost of speed. + * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. + * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + */ +#ifndef LZ4_MEMORY_USAGE +# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT +#endif + +#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) +# error "LZ4_MEMORY_USAGE is too small !" +#endif + +#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) +# error "LZ4_MEMORY_USAGE is too large !" +#endif + +/*-************************************ +* Simple Functions +**************************************/ +/*! LZ4_compress_default() : + * Compresses 'srcSize' bytes from buffer 'src' + * into already allocated 'dst' buffer of size 'dstCapacity'. + * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). + * It also runs faster, so it's a recommended setting. + * If the function cannot compress 'src' into a more limited 'dst' budget, + * compression stops *immediately*, and the function result is zero. + * In which case, 'dst' content is undefined (invalid). + * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. + * dstCapacity : size of buffer 'dst' (which must be already allocated) + * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) + * or 0 if compression fails + * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). + */ +LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); + +/*! LZ4_decompress_safe() : + * compressedSize : is the exact complete size of the compressed block. + * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. + * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) + * If destination buffer is not large enough, decoding will stop and output an error code (negative value). + * If the source stream is detected malformed, the function will stop decoding and return a negative result. + * Note 1 : This function is protected against malicious data packets : + * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, + * even if the compressed block is maliciously modified to order the decoder to do these actions. + * In such case, the decoder stops immediately, and considers the compressed block malformed. + * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. + * The implementation is free to send / store / derive this information in whichever way is most beneficial. + * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. + */ +LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); + + +/*-************************************ +* Advanced Functions +**************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + +/*! LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is incorrect (too large or negative) +*/ +LZ4LIB_API int LZ4_compressBound(int inputSize); + +/*! LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows selection of "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). + Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). +*/ +LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_fast_extState() : + * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. + * Use LZ4_sizeofState() to know how much memory must be allocated, + * and allocate it on 8-bytes boundaries (using `malloc()` typically). + * Then, provide this buffer as `void* state` to compression function. + */ +LZ4LIB_API int LZ4_sizeofState(void); +LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_destSize() : + * Reverse the logic : compresses as much data as possible from 'src' buffer + * into already allocated buffer 'dst', of size >= 'targetDestSize'. + * This function either compresses the entire 'src' content into 'dst' if it's large enough, + * or fill 'dst' buffer completely with as much data as possible from 'src'. + * note: acceleration parameter is fixed to "default". + * + * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. + * New value is necessarily <= input value. + * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) + * or 0 if compression fails. + * + * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): + * the produced compressed content could, in specific circumstances, + * require to be decompressed into a destination buffer larger + * by at least 1 byte than the content to decompress. + * If an application uses `LZ4_compress_destSize()`, + * it's highly recommended to update liblz4 to v1.9.2 or better. + * If this can't be done or ensured, + * the receiving decompression function should provide + * a dstCapacity which is > decompressedSize, by at least 1 byte. + * See https://github.com/lz4/lz4/issues/859 for details + */ +LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); + + +/*! LZ4_decompress_safe_partial() : + * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', + * into destination buffer 'dst' of size 'dstCapacity'. + * Up to 'targetOutputSize' bytes will be decoded. + * The function stops decoding on reaching this objective. + * This can be useful to boost performance + * whenever only the beginning of a block is required. + * + * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) + * If source stream is detected malformed, function returns a negative result. + * + * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. + * + * Note 2 : targetOutputSize must be <= dstCapacity + * + * Note 3 : this function effectively stops decoding on reaching targetOutputSize, + * so dstCapacity is kind of redundant. + * This is because in older versions of this function, + * decoding operation would still write complete sequences. + * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, + * it could write more bytes, though only up to dstCapacity. + * Some "margin" used to be required for this operation to work properly. + * Thankfully, this is no longer necessary. + * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. + * + * Note 4 : If srcSize is the exact size of the block, + * then targetOutputSize can be any value, + * including larger than the block's decompressed size. + * The function will, at most, generate block's decompressed size. + * + * Note 5 : If srcSize is _larger_ than block's compressed size, + * then targetOutputSize **MUST** be <= block's decompressed size. + * Otherwise, *silent corruption will occur*. + */ +LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); + + +/*-********************************************* +* Streaming Compression Functions +***********************************************/ +typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ + +/** + Note about RC_INVOKED + + - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). + https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros + + - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars) + and reports warning "RC4011: identifier truncated". + + - To eliminate the warning, we surround long preprocessor symbol with + "#if !defined(RC_INVOKED) ... #endif" block that means + "skip this block when rc.exe is trying to read it". +*/ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); +LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif + +/*! LZ4_resetStream_fast() : v1.9.0+ + * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks + * (e.g., LZ4_compress_fast_continue()). + * + * An LZ4_stream_t must be initialized once before usage. + * This is automatically done when created by LZ4_createStream(). + * However, should the LZ4_stream_t be simply declared on stack (for example), + * it's necessary to initialize it first, using LZ4_initStream(). + * + * After init, start any new stream with LZ4_resetStream_fast(). + * A same LZ4_stream_t can be re-used multiple times consecutively + * and compress multiple streams, + * provided that it starts each new stream with LZ4_resetStream_fast(). + * + * LZ4_resetStream_fast() is much faster than LZ4_initStream(), + * but is not compatible with memory regions containing garbage data. + * + * Note: it's only useful to call LZ4_resetStream_fast() + * in the context of streaming compression. + * The *extState* functions perform their own resets. + * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. + */ +LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); + +/*! LZ4_loadDict() : + * Use this function to reference a static dictionary into LZ4_stream_t. + * The dictionary must remain available during compression. + * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. + * The same dictionary will have to be loaded on decompression side for successful decoding. + * Dictionary are useful for better compression of small data (KB range). + * While LZ4 accept any input as dictionary, + * results are generally better when using Zstandard's Dictionary Builder. + * Loading a size of 0 is allowed, and is the same as reset. + * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) + */ +LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_compress_fast_continue() : + * Compress 'src' content using data from previously compressed blocks, for better compression ratio. + * 'dst' buffer must be already allocated. + * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * + * @return : size of compressed block + * or 0 if there is an error (typically, cannot fit into 'dst'). + * + * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. + * Each block has precise boundaries. + * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. + * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. + * + * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! + * + * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. + * Make sure that buffers are separated, by at least one byte. + * This construction ensures that each block only depends on previous block. + * + * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. + * + * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. + */ +LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_saveDict() : + * If last 64KB data cannot be guaranteed to remain available at its current memory location, + * save it into a safer place (char* safeBuffer). + * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), + * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. + * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. + */ +LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); + + +/*-********************************************** +* Streaming Decompression Functions +* Bufferless synchronous API +************************************************/ +typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ + +/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : + * creation / destruction of streaming decompression tracking context. + * A tracking context can be re-used multiple times. + */ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); +LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif + +/*! LZ4_setStreamDecode() : + * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. + * Use this function to start decompression of a new stream of blocks. + * A dictionary can optionally be set. Use NULL or size 0 for a reset order. + * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. + * @return : 1 if OK, 0 if error + */ +LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); + +/*! LZ4_decoderRingBufferSize() : v1.8.2+ + * Note : in a ring buffer scenario (optional), + * blocks are presumed decompressed next to each other + * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), + * at which stage it resumes from beginning of ring buffer. + * When setting such a ring buffer for streaming decompression, + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); +#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ + +/*! LZ4_decompress_*_continue() : + * These decoding functions allow decompression of consecutive blocks in "streaming" mode. + * A block is an unsplittable entity, it must be presented entirely to a decompression function. + * Decompression functions only accepts one block at a time. + * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. + * If less than 64KB of data has been decoded, all the data must be present. + * + * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : + * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). + * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. + * In which case, encoding and decoding buffers do not need to be synchronized. + * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. + * - Synchronized mode : + * Decompression buffer size is _exactly_ the same as compression buffer size, + * and follows exactly same update rule (block boundaries at same positions), + * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), + * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). + * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. + * In which case, encoding and decoding buffers do not need to be synchronized, + * and encoding ring buffer can have any size, including small ones ( < 64 KB). + * + * Whenever these conditions are not possible, + * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, + * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. +*/ +LZ4LIB_API int +LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* src, char* dst, + int srcSize, int dstCapacity); + + +/*! LZ4_decompress_*_usingDict() : + * These decoding functions work the same as + * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() + * They are stand-alone, and don't need an LZ4_streamDecode_t structure. + * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. + * Performance tip : Decompression speed can be substantially increased + * when dst == dictStart + dictSize. + */ +LZ4LIB_API int +LZ4_decompress_safe_usingDict(const char* src, char* dst, + int srcSize, int dstCapacity, + const char* dictStart, int dictSize); + +LZ4LIB_API int +LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, + int compressedSize, + int targetOutputSize, int maxOutputSize, + const char* dictStart, int dictSize); + +#endif /* LZ4_H_2983827168210 */ + + +/*^************************************* + * !!!!!! STATIC LINKING ONLY !!!!!! + ***************************************/ + +/*-**************************************************************************** + * Experimental section + * + * Symbols declared in this section must be considered unstable. Their + * signatures or semantics may change, or they may be removed altogether in the + * future. They are therefore only safe to depend on when the caller is + * statically linked against the library. + * + * To protect against unsafe usage, not only are the declarations guarded, + * the definitions are hidden by default + * when building LZ4 as a shared/dynamic library. + * + * In order to access these declarations, + * define LZ4_STATIC_LINKING_ONLY in your application + * before including LZ4's headers. + * + * In order to make their implementations accessible dynamically, you must + * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. + ******************************************************************************/ + +#ifdef LZ4_STATIC_LINKING_ONLY + +#ifndef LZ4_STATIC_3504398509 +#define LZ4_STATIC_3504398509 + +#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS +#define LZ4LIB_STATIC_API LZ4LIB_API +#else +#define LZ4LIB_STATIC_API +#endif + + +/*! LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. + * It is only safe to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). + * From a high level, the difference is that + * this function initializes the provided state with a call to something like LZ4_resetStream_fast() + * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). + */ +LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_attach_dictionary() : + * This is an experimental API that allows + * efficient use of a static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a + * working LZ4_stream_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDict() should + * be expected to work. + * + * Alternatively, the provided dictionaryStream may be NULL, + * in which case any existing dictionary stream is unset. + * + * If a dictionary is provided, it replaces any pre-existing stream history. + * The dictionary contents are the only history that can be referenced and + * logically immediately precede the data compressed in the first subsequent + * compression call. + * + * The dictionary will only remain attached to the working stream through the + * first compression call, at the end of which it is cleared. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the completion of the first compression call on the stream. + */ +LZ4LIB_STATIC_API void +LZ4_attach_dictionary(LZ4_stream_t* workingStream, + const LZ4_stream_t* dictionaryStream); + + +/*! In-place compression and decompression + * + * It's possible to have input and output sharing the same buffer, + * for highly constrained memory environments. + * In both cases, it requires input to lay at the end of the buffer, + * and decompression to start at beginning of the buffer. + * Buffer size must feature some margin, hence be larger than final size. + * + * |<------------------------buffer--------------------------------->| + * |<-----------compressed data--------->| + * |<-----------decompressed size------------------>| + * |<----margin---->| + * + * This technique is more useful for decompression, + * since decompressed size is typically larger, + * and margin is short. + * + * In-place decompression will work inside any buffer + * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). + * This presumes that decompressedSize > compressedSize. + * Otherwise, it means compression actually expanded data, + * and it would be more efficient to store such data with a flag indicating it's not compressed. + * This can happen when data is not compressible (already compressed, or encrypted). + * + * For in-place compression, margin is larger, as it must be able to cope with both + * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, + * and data expansion, which can happen when input is not compressible. + * As a consequence, buffer size requirements are much higher, + * and memory savings offered by in-place compression are more limited. + * + * There are ways to limit this cost for compression : + * - Reduce history size, by modifying LZ4_DISTANCE_MAX. + * Note that it is a compile-time constant, so all compressions will apply this limit. + * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, + * so it's a reasonable trick when inputs are known to be small. + * - Require the compressor to deliver a "maximum compressed size". + * This is the `dstCapacity` parameter in `LZ4_compress*()`. + * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, + * in which case, the return code will be 0 (zero). + * The caller must be ready for these cases to happen, + * and typically design a backup scheme to send data uncompressed. + * The combination of both techniques can significantly reduce + * the amount of margin required for in-place compression. + * + * In-place compression can work in any buffer + * which size is >= (maxCompressedSize) + * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. + * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, + * so it's possible to reduce memory requirements by playing with them. + */ + +#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) +#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ + +#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ +# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ +#endif + +#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ +#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ + +#endif /* LZ4_STATIC_3504398509 */ +#endif /* LZ4_STATIC_LINKING_ONLY */ + + + +#ifndef LZ4_H_98237428734687 +#define LZ4_H_98237428734687 + +/*-************************************************************ + * Private Definitions + ************************************************************** + * Do not use these definitions directly. + * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. + * Accessing members will expose user code to API and/or ABI break in future versions of the library. + **************************************************************/ +#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) +#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) +#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ + +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef int8_t LZ4_i8; + typedef uint8_t LZ4_byte; + typedef uint16_t LZ4_u16; + typedef uint32_t LZ4_u32; +#else + typedef signed char LZ4_i8; + typedef unsigned char LZ4_byte; + typedef unsigned short LZ4_u16; + typedef unsigned int LZ4_u32; +#endif + +/*! LZ4_stream_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_stream_t object. +**/ + +typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; +struct LZ4_stream_t_internal { + LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; + const LZ4_byte* dictionary; + const LZ4_stream_t_internal* dictCtx; + LZ4_u32 currentOffset; + LZ4_u32 tableType; + LZ4_u32 dictSize; + /* Implicit padding to ensure structure is aligned */ +}; + +#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ +union LZ4_stream_u { + char minStateSize[LZ4_STREAM_MINSIZE]; + LZ4_stream_t_internal internal_donotuse; +}; /* previously typedef'd to LZ4_stream_t */ + + +/*! LZ4_initStream() : v1.9.0+ + * An LZ4_stream_t structure must be initialized at least once. + * This is automatically done when invoking LZ4_createStream(), + * but it's not when the structure is simply declared on stack (for example). + * + * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. + * It can also initialize any arbitrary buffer of sufficient size, + * and will @return a pointer of proper type upon initialization. + * + * Note : initialization fails if size and alignment conditions are not respected. + * In which case, the function will @return NULL. + * Note2: An LZ4_stream_t structure guarantees correct alignment and size. + * Note3: Before v1.9.0, use LZ4_resetStream() instead +**/ +LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); + + +/*! LZ4_streamDecode_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_streamDecode_t object. +**/ +typedef struct { + const LZ4_byte* externalDict; + const LZ4_byte* prefixEnd; + size_t extDictSize; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +#define LZ4_STREAMDECODE_MINSIZE 32 +union LZ4_streamDecode_u { + char minStateSize[LZ4_STREAMDECODE_MINSIZE]; + LZ4_streamDecode_t_internal internal_donotuse; +} ; /* previously typedef'd to LZ4_streamDecode_t */ + + + +/*-************************************ +* Obsolete Functions +**************************************/ + +/*! Deprecation warnings + * + * Deprecated functions make the compiler generate a warning when invoked. + * This is meant to invite users to update their source code. + * Should deprecation warnings be a problem, it is generally possible to disable them, + * typically with -Wno-deprecated-declarations for gcc + * or _CRT_SECURE_NO_WARNINGS in Visual. + * + * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS + * before including the header file. + */ +#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS +# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ +#else +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define LZ4_DEPRECATED(message) [[deprecated(message)]] +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# else +# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") +# define LZ4_DEPRECATED(message) /* disabled */ +# endif +#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ + +/*! Obsolete compression functions (since v1.7.3) */ +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/*! Obsolete decompression functions (since v1.8.0) */ +LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); +LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); + +/* Obsolete streaming functions (since v1.7.0) + * degraded functionality; do not use! + * + * In order to perform streaming compression, these functions depended on data + * that is no longer tracked in the state. They have been preserved as well as + * possible: using them will still produce a correct output. However, they don't + * actually retain any history between compression calls. The compression ratio + * achieved will therefore be no better than compressing each chunk + * independently. + */ +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); + +/*! Obsolete streaming decoding functions (since v1.7.0) */ +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); + +/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : + * These functions used to be faster than LZ4_decompress_safe(), + * but this is no longer the case. They are now slower. + * This is because LZ4_decompress_fast() doesn't know the input size, + * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. + * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. + * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. + * + * The last remaining LZ4_decompress_fast() specificity is that + * it can decompress a block without knowing its compressed size. + * Such functionality can be achieved in a more secure manner + * by employing LZ4_decompress_safe_partial(). + * + * Parameters: + * originalSize : is the uncompressed size to regenerate. + * `dst` must be already allocated, its size must be >= 'originalSize' bytes. + * @return : number of bytes read from source buffer (== compressed size). + * The function expects to finish at block's end exactly. + * If the source stream is detected malformed, the function stops decoding and returns a negative result. + * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. + * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. + * Also, since match offsets are not validated, match reads from 'src' may underflow too. + * These issues never happen if input (compressed) data is correct. + * But they may happen if input data is invalid (error or intentional tampering). + * As a consequence, use these functions in trusted environments with trusted data **only**. + */ +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") +LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") +LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") +LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); + +/*! LZ4_resetStream() : + * An LZ4_stream_t structure must be initialized at least once. + * This is done with LZ4_initStream(), or LZ4_resetStream(). + * Consider switching to LZ4_initStream(), + * invoking LZ4_resetStream() will trigger deprecation warnings in the future. + */ +LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); + + +#endif /* LZ4_H_98237428734687 */ + + +#if defined (__cplusplus) +} +#endif diff --git a/CompressSave/LZ4WrapC/lz4/lz4frame.c b/CompressSave/LZ4WrapC/lz4/lz4frame.c new file mode 100644 index 0000000..4d991e1 --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4frame.c @@ -0,0 +1,2077 @@ +/* + * LZ4 auto-framing library + * Copyright (C) 2011-2016, Yann Collet. + * + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://www.lz4.org + * - LZ4 source repository : https://github.com/lz4/lz4 + */ + +/* LZ4F is a stand-alone API to create LZ4-compressed Frames + * in full conformance with specification v1.6.1 . + * This library rely upon memory management capabilities (malloc, free) + * provided either by , + * or redirected towards another library of user's choice + * (see Memory Routines below). + */ + + +/*-************************************ +* Compiler Options +**************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * LZ4F_HEAPMODE : + * Select how LZ4F_compressFrame will allocate the Compression Context, + * either on stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef LZ4F_HEAPMODE +# define LZ4F_HEAPMODE 0 +#endif + + +/*-************************************ +* Library declarations +**************************************/ +#define LZ4F_STATIC_LINKING_ONLY +#include "lz4frame.h" +#define LZ4_STATIC_LINKING_ONLY +#include "lz4.h" +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/*-************************************ +* Memory routines +**************************************/ +/* + * User may redirect invocations of + * malloc(), calloc() and free() + * towards another library or solution of their choice + * by modifying below section. +**/ + +#include /* memset, memcpy, memmove */ +#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ +# define MEM_INIT(p,v,s) memset((p),(v),(s)) +#endif + +#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,(s)) +# define FREEMEM(p) free(p) +#endif + +static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem) +{ + /* custom calloc defined : use it */ + if (cmem.customCalloc != NULL) { + return cmem.customCalloc(cmem.opaqueState, s); + } + /* nothing defined : use default 's calloc() */ + if (cmem.customAlloc == NULL) { + return ALLOC_AND_ZERO(s); + } + /* only custom alloc defined : use it, and combine it with memset() */ + { void* const p = cmem.customAlloc(cmem.opaqueState, s); + if (p != NULL) MEM_INIT(p, 0, s); + return p; +} } + +static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem) +{ + /* custom malloc defined : use it */ + if (cmem.customAlloc != NULL) { + return cmem.customAlloc(cmem.opaqueState, s); + } + /* nothing defined : use default 's malloc() */ + return ALLOC(s); +} + +static void LZ4F_free(void* p, LZ4F_CustomMem cmem) +{ + /* custom malloc defined : use it */ + if (cmem.customFree != NULL) { + cmem.customFree(cmem.opaqueState, p); + return; + } + /* nothing defined : use default 's free() */ + FREEMEM(p); +} + + +/*-************************************ +* Debug +**************************************/ +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define LZ4F_STATIC_ASSERT(c) { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG) +# include +static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ ": "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + + +/*-************************************ +* Basic Types +**************************************/ +#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; +#endif + + +/* unoptimized version; solves endianness & alignment issues */ +static U32 LZ4F_readLE32 (const void* src) +{ + const BYTE* const srcPtr = (const BYTE*)src; + U32 value32 = srcPtr[0]; + value32 += ((U32)srcPtr[1])<< 8; + value32 += ((U32)srcPtr[2])<<16; + value32 += ((U32)srcPtr[3])<<24; + return value32; +} + +static void LZ4F_writeLE32 (void* dst, U32 value32) +{ + BYTE* const dstPtr = (BYTE*)dst; + dstPtr[0] = (BYTE)value32; + dstPtr[1] = (BYTE)(value32 >> 8); + dstPtr[2] = (BYTE)(value32 >> 16); + dstPtr[3] = (BYTE)(value32 >> 24); +} + +static U64 LZ4F_readLE64 (const void* src) +{ + const BYTE* const srcPtr = (const BYTE*)src; + U64 value64 = srcPtr[0]; + value64 += ((U64)srcPtr[1]<<8); + value64 += ((U64)srcPtr[2]<<16); + value64 += ((U64)srcPtr[3]<<24); + value64 += ((U64)srcPtr[4]<<32); + value64 += ((U64)srcPtr[5]<<40); + value64 += ((U64)srcPtr[6]<<48); + value64 += ((U64)srcPtr[7]<<56); + return value64; +} + +static void LZ4F_writeLE64 (void* dst, U64 value64) +{ + BYTE* const dstPtr = (BYTE*)dst; + dstPtr[0] = (BYTE)value64; + dstPtr[1] = (BYTE)(value64 >> 8); + dstPtr[2] = (BYTE)(value64 >> 16); + dstPtr[3] = (BYTE)(value64 >> 24); + dstPtr[4] = (BYTE)(value64 >> 32); + dstPtr[5] = (BYTE)(value64 >> 40); + dstPtr[6] = (BYTE)(value64 >> 48); + dstPtr[7] = (BYTE)(value64 >> 56); +} + + +/*-************************************ +* Constants +**************************************/ +#ifndef LZ4_SRC_INCLUDED /* avoid double definition */ +# define KB *(1<<10) +# define MB *(1<<20) +# define GB *(1<<30) +#endif + +#define _1BIT 0x01 +#define _2BITS 0x03 +#define _3BITS 0x07 +#define _4BITS 0x0F +#define _8BITS 0xFF + +#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U +#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB + +static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN; /* 7 */ +static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX; /* 19 */ +static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE; /* block header : size, and compress flag */ +static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE; /* block footer : checksum (optional) */ + + +/*-************************************ +* Structures and local types +**************************************/ + +typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_blockCompression_t; + +typedef struct LZ4F_cctx_s +{ + LZ4F_CustomMem cmem; + LZ4F_preferences_t prefs; + U32 version; + U32 cStage; /* 0 : compression uninitialized ; 1 : initialized, can compress */ + const LZ4F_CDict* cdict; + size_t maxBlockSize; + size_t maxBufferSize; + BYTE* tmpBuff; /* internal buffer, for streaming */ + BYTE* tmpIn; /* starting position of data compress within internal buffer (>= tmpBuff) */ + size_t tmpInSize; /* amount of data to compress after tmpIn */ + U64 totalInSize; + XXH32_state_t xxh; + void* lz4CtxPtr; + U16 lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */ + U16 lz4CtxState; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */ + LZ4F_blockCompression_t blockCompression; +} LZ4F_cctx_t; + + +/*-************************************ +* Error management +**************************************/ +#define LZ4F_GENERATE_STRING(STRING) #STRING, +static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) }; + + +unsigned LZ4F_isError(LZ4F_errorCode_t code) +{ + return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode)); +} + +const char* LZ4F_getErrorName(LZ4F_errorCode_t code) +{ + static const char* codeError = "Unspecified error code"; + if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)]; + return codeError; +} + +LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult) +{ + if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError; + return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult); +} + +static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code) +{ + /* A compilation error here means sizeof(ptrdiff_t) is not large enough */ + LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t)); + return (LZ4F_errorCode_t)-(ptrdiff_t)code; +} + +#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e) + +#define RETURN_ERROR_IF(c,e) if (c) RETURN_ERROR(e) + +#define FORWARD_IF_ERROR(r) if (LZ4F_isError(r)) return (r) + +unsigned LZ4F_getVersion(void) { return LZ4F_VERSION; } + +int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; } + +size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID) +{ + static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB }; + + if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT; + if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB) + RETURN_ERROR(maxBlockSize_invalid); + { int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB; + return blockSizes[blockSizeIdx]; +} } + +/*-************************************ +* Private functions +**************************************/ +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +static BYTE LZ4F_headerChecksum (const void* header, size_t length) +{ + U32 const xxh = XXH32(header, length, 0); + return (BYTE)(xxh >> 8); +} + + +/*-************************************ +* Simple-pass compression functions +**************************************/ +static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID, + const size_t srcSize) +{ + LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB; + size_t maxBlockSize = 64 KB; + while (requestedBSID > proposedBSID) { + if (srcSize <= maxBlockSize) + return proposedBSID; + proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1); + maxBlockSize <<= 2; + } + return requestedBSID; +} + +/*! LZ4F_compressBound_internal() : + * Provides dstCapacity given a srcSize to guarantee operation success in worst case situations. + * prefsPtr is optional : if NULL is provided, preferences will be set to cover worst case scenario. + * @return is always the same for a srcSize and prefsPtr, so it can be relied upon to size reusable buffers. + * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations. + */ +static size_t LZ4F_compressBound_internal(size_t srcSize, + const LZ4F_preferences_t* preferencesPtr, + size_t alreadyBuffered) +{ + LZ4F_preferences_t prefsNull = LZ4F_INIT_PREFERENCES; + prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled; /* worst case */ + prefsNull.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled; /* worst case */ + { const LZ4F_preferences_t* const prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr; + U32 const flush = prefsPtr->autoFlush | (srcSize==0); + LZ4F_blockSizeID_t const blockID = prefsPtr->frameInfo.blockSizeID; + size_t const blockSize = LZ4F_getBlockSize(blockID); + size_t const maxBuffered = blockSize - 1; + size_t const bufferedSize = MIN(alreadyBuffered, maxBuffered); + size_t const maxSrcSize = srcSize + bufferedSize; + unsigned const nbFullBlocks = (unsigned)(maxSrcSize / blockSize); + size_t const partialBlockSize = maxSrcSize & (blockSize-1); + size_t const lastBlockSize = flush ? partialBlockSize : 0; + unsigned const nbBlocks = nbFullBlocks + (lastBlockSize>0); + + size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag; + size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize); + + return ((BHSize + blockCRCSize) * nbBlocks) + + (blockSize * nbFullBlocks) + lastBlockSize + frameEnd; + } +} + +size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr) +{ + LZ4F_preferences_t prefs; + size_t const headerSize = maxFHSize; /* max header size, including optional fields */ + + if (preferencesPtr!=NULL) prefs = *preferencesPtr; + else MEM_INIT(&prefs, 0, sizeof(prefs)); + prefs.autoFlush = 1; + + return headerSize + LZ4F_compressBound_internal(srcSize, &prefs, 0);; +} + + +/*! LZ4F_compressFrame_usingCDict() : + * Compress srcBuffer using a dictionary, in a single step. + * cdict can be NULL, in which case, no dictionary is used. + * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * The LZ4F_preferences_t structure is optional : you may provide NULL as argument, + * however, it's the only way to provide a dictID, so it's not recommended. + * @return : number of bytes written into dstBuffer, + * or an error code if it fails (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr) +{ + LZ4F_preferences_t prefs; + LZ4F_compressOptions_t options; + BYTE* const dstStart = (BYTE*) dstBuffer; + BYTE* dstPtr = dstStart; + BYTE* const dstEnd = dstStart + dstCapacity; + + if (preferencesPtr!=NULL) + prefs = *preferencesPtr; + else + MEM_INIT(&prefs, 0, sizeof(prefs)); + if (prefs.frameInfo.contentSize != 0) + prefs.frameInfo.contentSize = (U64)srcSize; /* auto-correct content size if selected (!=0) */ + + prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize); + prefs.autoFlush = 1; + if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID)) + prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* only one block => no need for inter-block link */ + + MEM_INIT(&options, 0, sizeof(options)); + options.stableSrc = 1; + + RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall); + + { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs); /* write header */ + FORWARD_IF_ERROR(headerSize); + dstPtr += headerSize; /* header size */ } + + assert(dstEnd >= dstPtr); + { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options); + FORWARD_IF_ERROR(cSize); + dstPtr += cSize; } + + assert(dstEnd >= dstPtr); + { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options); /* flush last block, and generate suffix */ + FORWARD_IF_ERROR(tailSize); + dstPtr += tailSize; } + + assert(dstEnd >= dstStart); + return (size_t)(dstPtr - dstStart); +} + + +/*! LZ4F_compressFrame() : + * Compress an entire srcBuffer into a valid LZ4 frame, in a single step. + * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default. + * @return : number of bytes written into dstBuffer. + * or an error code if it fails (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_preferences_t* preferencesPtr) +{ + size_t result; +#if (LZ4F_HEAPMODE) + LZ4F_cctx_t* cctxPtr; + result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION); + FORWARD_IF_ERROR(result); +#else + LZ4F_cctx_t cctx; + LZ4_stream_t lz4ctx; + LZ4F_cctx_t* const cctxPtr = &cctx; + + MEM_INIT(&cctx, 0, sizeof(cctx)); + cctx.version = LZ4F_VERSION; + cctx.maxBufferSize = 5 MB; /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */ + if ( preferencesPtr == NULL + || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) { + LZ4_initStream(&lz4ctx, sizeof(lz4ctx)); + cctxPtr->lz4CtxPtr = &lz4ctx; + cctxPtr->lz4CtxAlloc = 1; + cctxPtr->lz4CtxState = 1; + } +#endif + DEBUGLOG(4, "LZ4F_compressFrame"); + + result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity, + srcBuffer, srcSize, + NULL, preferencesPtr); + +#if (LZ4F_HEAPMODE) + LZ4F_freeCompressionContext(cctxPtr); +#else + if ( preferencesPtr != NULL + && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) { + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); + } +#endif + return result; +} + + +/*-*************************************************** +* Dictionary compression +*****************************************************/ + +struct LZ4F_CDict_s { + LZ4F_CustomMem cmem; + void* dictContent; + LZ4_stream_t* fastCtx; + LZ4_streamHC_t* HCCtx; +}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */ + +LZ4F_CDict* +LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize) +{ + const char* dictStart = (const char*)dictBuffer; + LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem); + DEBUGLOG(4, "LZ4F_createCDict_advanced"); + if (!cdict) return NULL; + cdict->cmem = cmem; + if (dictSize > 64 KB) { + dictStart += dictSize - 64 KB; + dictSize = 64 KB; + } + cdict->dictContent = LZ4F_malloc(dictSize, cmem); + cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem); + if (cdict->fastCtx) + LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t)); + cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem); + if (cdict->HCCtx) + LZ4_initStream(cdict->HCCtx, sizeof(LZ4_streamHC_t)); + if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) { + LZ4F_freeCDict(cdict); + return NULL; + } + memcpy(cdict->dictContent, dictStart, dictSize); + LZ4_loadDict (cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize); + LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT); + LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize); + return cdict; +} + +/*! LZ4F_createCDict() : + * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. + * LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. + * LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict + * @return : digested dictionary for compression, or NULL if failed */ +LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize) +{ + DEBUGLOG(4, "LZ4F_createCDict"); + return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize); +} + +void LZ4F_freeCDict(LZ4F_CDict* cdict) +{ + if (cdict==NULL) return; /* support free on NULL */ + LZ4F_free(cdict->dictContent, cdict->cmem); + LZ4F_free(cdict->fastCtx, cdict->cmem); + LZ4F_free(cdict->HCCtx, cdict->cmem); + LZ4F_free(cdict, cdict->cmem); +} + + +/*-********************************* +* Advanced compression functions +***********************************/ + +LZ4F_cctx* +LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version) +{ + LZ4F_cctx* const cctxPtr = + (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem); + if (cctxPtr==NULL) return NULL; + + cctxPtr->cmem = customMem; + cctxPtr->version = version; + cctxPtr->cStage = 0; /* Uninitialized. Next stage : init cctx */ + + return cctxPtr; +} + +/*! LZ4F_createCompressionContext() : + * The first thing to do is to create a compressionContext object, which will be used in all compression operations. + * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure. + * The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries. + * The function will provide a pointer to an allocated LZ4F_compressionContext_t object. + * If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation. + * Object can release its memory using LZ4F_freeCompressionContext(); +**/ +LZ4F_errorCode_t +LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version) +{ + assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */ + /* in case it nonetheless happen in production */ + RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null); + + *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version); + RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed); + return LZ4F_OK_NoError; +} + + +LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr) +{ + if (cctxPtr != NULL) { /* support free on NULL */ + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */ + LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem); + LZ4F_free(cctxPtr, cctxPtr->cmem); + } + return LZ4F_OK_NoError; +} + + +/** + * This function prepares the internal LZ4(HC) stream for a new compression, + * resetting the context and attaching the dictionary, if there is one. + * + * It needs to be called at the beginning of each independent compression + * stream (i.e., at the beginning of a frame in blockLinked mode, or at the + * beginning of each block in blockIndependent mode). + */ +static void LZ4F_initStream(void* ctx, + const LZ4F_CDict* cdict, + int level, + LZ4F_blockMode_t blockMode) { + if (level < LZ4HC_CLEVEL_MIN) { + if (cdict != NULL || blockMode == LZ4F_blockLinked) { + /* In these cases, we will call LZ4_compress_fast_continue(), + * which needs an already reset context. Otherwise, we'll call a + * one-shot API. The non-continued APIs internally perform their own + * resets at the beginning of their calls, where they know what + * tableType they need the context to be in. So in that case this + * would be misguided / wasted work. */ + LZ4_resetStream_fast((LZ4_stream_t*)ctx); + } + LZ4_attach_dictionary((LZ4_stream_t *)ctx, cdict ? cdict->fastCtx : NULL); + } else { + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level); + LZ4_attach_HC_dictionary((LZ4_streamHC_t *)ctx, cdict ? cdict->HCCtx : NULL); + } +} + +static int ctxTypeID_to_size(int ctxTypeID) { + switch(ctxTypeID) { + case 1: + return LZ4_sizeofState(); + case 2: + return LZ4_sizeofStateHC(); + default: + return 0; + } +} + +/*! LZ4F_compressBegin_usingCDict() : + * init streaming compression AND writes frame header into @dstBuffer. + * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * @return : number of bytes written into @dstBuffer for the header + * or an error code (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr) +{ + LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES; + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + + RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall); + if (preferencesPtr == NULL) preferencesPtr = &prefNull; + cctxPtr->prefs = *preferencesPtr; + + /* cctx Management */ + { U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2; + int requiredSize = ctxTypeID_to_size(ctxTypeID); + int allocatedSize = ctxTypeID_to_size(cctxPtr->lz4CtxAlloc); + if (allocatedSize < requiredSize) { + /* not enough space allocated */ + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); + if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) { + /* must take ownership of memory allocation, + * in order to respect custom allocator contract */ + cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctxPtr->cmem); + if (cctxPtr->lz4CtxPtr) + LZ4_initStream(cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t)); + } else { + cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem); + if (cctxPtr->lz4CtxPtr) + LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); + } + RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed); + cctxPtr->lz4CtxAlloc = ctxTypeID; + cctxPtr->lz4CtxState = ctxTypeID; + } else if (cctxPtr->lz4CtxState != ctxTypeID) { + /* otherwise, a sufficient buffer is already allocated, + * but we need to reset it to the correct context type */ + if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) { + LZ4_initStream((LZ4_stream_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t)); + } else { + LZ4_initStreamHC((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); + LZ4_setCompressionLevel((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel); + } + cctxPtr->lz4CtxState = ctxTypeID; + } } + + /* Buffer Management */ + if (cctxPtr->prefs.frameInfo.blockSizeID == 0) + cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT; + cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID); + + { size_t const requiredBuffSize = preferencesPtr->autoFlush ? + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 64 KB : 0) : /* only needs past data up to window size */ + cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB : 0); + + if (cctxPtr->maxBufferSize < requiredBuffSize) { + cctxPtr->maxBufferSize = 0; + LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem); + cctxPtr->tmpBuff = (BYTE*)LZ4F_malloc(requiredBuffSize, cctxPtr->cmem); + RETURN_ERROR_IF(cctxPtr->tmpBuff == NULL, allocation_failed); + cctxPtr->maxBufferSize = requiredBuffSize; + } } + cctxPtr->tmpIn = cctxPtr->tmpBuff; + cctxPtr->tmpInSize = 0; + (void)XXH32_reset(&(cctxPtr->xxh), 0); + + /* context init */ + cctxPtr->cdict = cdict; + if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) { + /* frame init only for blockLinked : blockIndependent will be init at each block */ + LZ4F_initStream(cctxPtr->lz4CtxPtr, cdict, cctxPtr->prefs.compressionLevel, LZ4F_blockLinked); + } + if (preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) { + LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, (int)preferencesPtr->favorDecSpeed); + } + + /* Magic Number */ + LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER); + dstPtr += 4; + { BYTE* const headerStart = dstPtr; + + /* FLG Byte */ + *dstPtr++ = (BYTE)(((1 & _2BITS) << 6) /* Version('01') */ + + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5) + + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4) + + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3) + + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2) + + (cctxPtr->prefs.frameInfo.dictID > 0) ); + /* BD Byte */ + *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4); + /* Optional Frame content size field */ + if (cctxPtr->prefs.frameInfo.contentSize) { + LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize); + dstPtr += 8; + cctxPtr->totalInSize = 0; + } + /* Optional dictionary ID field */ + if (cctxPtr->prefs.frameInfo.dictID) { + LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID); + dstPtr += 4; + } + /* Header CRC Byte */ + *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart)); + dstPtr++; + } + + cctxPtr->cStage = 1; /* header written, now request input data block */ + return (size_t)(dstPtr - dstStart); +} + + +/*! LZ4F_compressBegin() : + * init streaming compression AND writes frame header into @dstBuffer. + * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * @preferencesPtr can be NULL, in which case default parameters are selected. + * @return : number of bytes written into dstBuffer for the header + * or an error code (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressBegin(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_preferences_t* preferencesPtr) +{ + return LZ4F_compressBegin_usingCDict(cctxPtr, dstBuffer, dstCapacity, + NULL, preferencesPtr); +} + + +/* LZ4F_compressBound() : + * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario. + * LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario. + * This function cannot fail. + */ +size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr) +{ + if (preferencesPtr && preferencesPtr->autoFlush) { + return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0); + } + return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1); +} + + +typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict); + + +/*! LZ4F_makeBlock(): + * compress a single block, add header and optional checksum. + * assumption : dst buffer capacity is >= BHSize + srcSize + crcSize + */ +static size_t LZ4F_makeBlock(void* dst, + const void* src, size_t srcSize, + compressFunc_t compress, void* lz4ctx, int level, + const LZ4F_CDict* cdict, + LZ4F_blockChecksum_t crcFlag) +{ + BYTE* const cSizePtr = (BYTE*)dst; + U32 cSize; + assert(compress != NULL); + cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize), + (int)(srcSize), (int)(srcSize-1), + level, cdict); + + if (cSize == 0 || cSize >= srcSize) { + cSize = (U32)srcSize; + LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG); + memcpy(cSizePtr+BHSize, src, srcSize); + } else { + LZ4F_writeLE32(cSizePtr, cSize); + } + if (crcFlag) { + U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0); /* checksum of compressed data */ + LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32); + } + return BHSize + cSize + ((U32)crcFlag)*BFSize; +} + + +static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + int const acceleration = (level < 0) ? -level + 1 : 1; + DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize); + LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent); + if (cdict) { + return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); + } else { + return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration); + } +} + +static int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + int const acceleration = (level < 0) ? -level + 1 : 1; + (void)cdict; /* init once at beginning of frame */ + DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize); + return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); +} + +static int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent); + if (cdict) { + return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity); + } + return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level); +} + +static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + (void)level; (void)cdict; /* init once at beginning of frame */ + return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity); +} + +static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict; + return 0; +} + +static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_blockCompression_t compressMode) +{ + if (compressMode == LZ4B_UNCOMPRESSED) return LZ4F_doNotCompressBlock; + if (level < LZ4HC_CLEVEL_MIN) { + if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock; + return LZ4F_compressBlock_continue; + } + if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC; + return LZ4F_compressBlockHC_continue; +} + +/* Save history (up to 64KB) into @tmpBuff */ +static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr) +{ + if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) + return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB); + return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB); +} + +typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus; + +static const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } }; + + + /*! LZ4F_compressUpdateImpl() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If the block compression does not match the compression of the previous block, the old data is flushed + * and operations continue with the new compression mode. + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on. + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr, + LZ4F_blockCompression_t blockCompression) + { + size_t const blockSize = cctxPtr->maxBlockSize; + const BYTE* srcPtr = (const BYTE*)srcBuffer; + const BYTE* const srcEnd = srcPtr + srcSize; + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + LZ4F_lastBlockStatus lastBlockCompressed = notDone; + compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression); + size_t bytesWritten; + DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize); + + RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); /* state must be initialized and waiting for next block */ + if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize)) + RETURN_ERROR(dstMaxSize_tooSmall); + + if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize) + RETURN_ERROR(dstMaxSize_tooSmall); + + /* flush currently written block, to continue with new block compression */ + if (cctxPtr->blockCompression != blockCompression) { + bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + dstPtr += bytesWritten; + cctxPtr->blockCompression = blockCompression; + } + + if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull; + + /* complete tmp buffer */ + if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */ + size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize; + assert(blockSize > cctxPtr->tmpInSize); + if (sizeToCopy > srcSize) { + /* add src to tmpIn buffer */ + memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize); + srcPtr = srcEnd; + cctxPtr->tmpInSize += srcSize; + /* still needs some CRC */ + } else { + /* complete tmpIn block and then compress it */ + lastBlockCompressed = fromTmpBuffer; + memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy); + srcPtr += sizeToCopy; + + dstPtr += LZ4F_makeBlock(dstPtr, + cctxPtr->tmpIn, blockSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize; + cctxPtr->tmpInSize = 0; + } } + + while ((size_t)(srcEnd - srcPtr) >= blockSize) { + /* compress full blocks */ + lastBlockCompressed = fromSrcBuffer; + dstPtr += LZ4F_makeBlock(dstPtr, + srcPtr, blockSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + srcPtr += blockSize; + } + + if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) { + /* autoFlush : remaining input (< blockSize) is compressed */ + lastBlockCompressed = fromSrcBuffer; + dstPtr += LZ4F_makeBlock(dstPtr, + srcPtr, (size_t)(srcEnd - srcPtr), + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + srcPtr = srcEnd; + } + + /* preserve dictionary within @tmpBuff whenever necessary */ + if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) { + /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */ + assert(blockCompression == LZ4B_COMPRESSED); + if (compressOptionsPtr->stableSrc) { + cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */ + } else { + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + assert(0 <= realDictSize && realDictSize <= 64 KB); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + } + } + + /* keep tmpIn within limits */ + if (!(cctxPtr->prefs.autoFlush) /* no autoflush : there may be some data left within internal buffer */ + && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) ) /* not enough room to store next block */ + { + /* only preserve 64KB within internal buffer. Ensures there is enough room for next block. + * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */ + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)); + } + + /* some input data left, necessarily < blockSize */ + if (srcPtr < srcEnd) { + /* fill tmp buffer */ + size_t const sizeToCopy = (size_t)(srcEnd - srcPtr); + memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy); + cctxPtr->tmpInSize = sizeToCopy; + } + + if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) + (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize); + + cctxPtr->totalInSize += srcSize; + return (size_t)(dstPtr - dstStart); +} + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_COMPRESSED); +} + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * This is only supported when LZ4F_blockIndependent is used + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) { + RETURN_ERROR_IF(cctxPtr->prefs.frameInfo.blockMode != LZ4F_blockIndependent, blockMode_invalid); + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_UNCOMPRESSED); +} + + +/*! LZ4F_flush() : + * When compressed data must be sent immediately, without waiting for a block to be filled, + * invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx. + * The result of the function is the number of bytes written into dstBuffer. + * It can be zero, this means there was no data left within LZ4F_cctx. + * The function outputs an error code if it fails (can be tested using LZ4F_isError()) + * LZ4F_compressOptions_t* is optional. NULL is a valid argument. + */ +size_t LZ4F_flush(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + compressFunc_t compress; + + if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */ + RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); + RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall); + (void)compressOptionsPtr; /* not useful (yet) */ + + /* select compression function */ + compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompression); + + /* compress tmp buffer */ + dstPtr += LZ4F_makeBlock(dstPtr, + cctxPtr->tmpIn, cctxPtr->tmpInSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + assert(((void)"flush overflows dstBuffer!", (size_t)(dstPtr - dstStart) <= dstCapacity)); + + if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) + cctxPtr->tmpIn += cctxPtr->tmpInSize; + cctxPtr->tmpInSize = 0; + + /* keep tmpIn within limits */ + if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) { /* necessarily LZ4F_blockLinked */ + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + } + + return (size_t)(dstPtr - dstStart); +} + + +/*! LZ4F_compressEnd() : + * When you want to properly finish the compressed frame, just call LZ4F_compressEnd(). + * It will flush whatever data remained within compressionContext (like LZ4_flush()) + * but also properly finalize the frame, with an endMark and an (optional) checksum. + * LZ4F_compressOptions_t structure is optional : you can provide NULL as argument. + * @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size)) + * or an error code if it fails (can be tested using LZ4F_isError()) + * The context can then be used again to compress a new frame, starting with LZ4F_compressBegin(). + */ +size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + + size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity); + FORWARD_IF_ERROR(flushSize); + dstPtr += flushSize; + + assert(flushSize <= dstCapacity); + dstCapacity -= flushSize; + + RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall); + LZ4F_writeLE32(dstPtr, 0); + dstPtr += 4; /* endMark */ + + if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) { + U32 const xxh = XXH32_digest(&(cctxPtr->xxh)); + RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall); + DEBUGLOG(5,"Writing 32-bit content checksum"); + LZ4F_writeLE32(dstPtr, xxh); + dstPtr+=4; /* content Checksum */ + } + + cctxPtr->cStage = 0; /* state is now re-usable (with identical preferences) */ + + if (cctxPtr->prefs.frameInfo.contentSize) { + if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize) + RETURN_ERROR(frameSize_wrong); + } + + return (size_t)(dstPtr - dstStart); +} + + +/*-*************************************************** +* Frame Decompression +*****************************************************/ + +typedef enum { + dstage_getFrameHeader=0, dstage_storeFrameHeader, + dstage_init, + dstage_getBlockHeader, dstage_storeBlockHeader, + dstage_copyDirect, dstage_getBlockChecksum, + dstage_getCBlock, dstage_storeCBlock, + dstage_flushOut, + dstage_getSuffix, dstage_storeSuffix, + dstage_getSFrameSize, dstage_storeSFrameSize, + dstage_skipSkippable +} dStage_t; + +struct LZ4F_dctx_s { + LZ4F_CustomMem cmem; + LZ4F_frameInfo_t frameInfo; + U32 version; + dStage_t dStage; + U64 frameRemainingSize; + size_t maxBlockSize; + size_t maxBufferSize; + BYTE* tmpIn; + size_t tmpInSize; + size_t tmpInTarget; + BYTE* tmpOutBuffer; + const BYTE* dict; + size_t dictSize; + BYTE* tmpOut; + size_t tmpOutSize; + size_t tmpOutStart; + XXH32_state_t xxh; + XXH32_state_t blockChecksum; + int skipChecksum; + BYTE header[LZ4F_HEADER_SIZE_MAX]; +}; /* typedef'd to LZ4F_dctx in lz4frame.h */ + + +LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version) +{ + LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem); + if (dctx == NULL) return NULL; + + dctx->cmem = customMem; + dctx->version = version; + return dctx; +} + +/*! LZ4F_createDecompressionContext() : + * Create a decompressionContext object, which will track all decompression operations. + * Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object. + * Object can later be released using LZ4F_freeDecompressionContext(). + * @return : if != 0, there was an error during context creation. + */ +LZ4F_errorCode_t +LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber) +{ + assert(LZ4F_decompressionContextPtr != NULL); /* violation of narrow contract */ + RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null); /* in case it nonetheless happen in production */ + + *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber); + if (*LZ4F_decompressionContextPtr == NULL) { /* failed allocation */ + RETURN_ERROR(allocation_failed); + } + return LZ4F_OK_NoError; +} + +LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx) +{ + LZ4F_errorCode_t result = LZ4F_OK_NoError; + if (dctx != NULL) { /* can accept NULL input, like free() */ + result = (LZ4F_errorCode_t)dctx->dStage; + LZ4F_free(dctx->tmpIn, dctx->cmem); + LZ4F_free(dctx->tmpOutBuffer, dctx->cmem); + LZ4F_free(dctx, dctx->cmem); + } + return result; +} + + +/*==--- Streaming Decompression operations ---==*/ + +void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx) +{ + dctx->dStage = dstage_getFrameHeader; + dctx->dict = NULL; + dctx->dictSize = 0; + dctx->skipChecksum = 0; +} + + +/*! LZ4F_decodeHeader() : + * input : `src` points at the **beginning of the frame** + * output : set internal values of dctx, such as + * dctx->frameInfo and dctx->dStage. + * Also allocates internal buffers. + * @return : nb Bytes read from src (necessarily <= srcSize) + * or an error code (testable with LZ4F_isError()) + */ +static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize) +{ + unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID; + size_t frameHeaderSize; + const BYTE* srcPtr = (const BYTE*)src; + + DEBUGLOG(5, "LZ4F_decodeHeader"); + /* need to decode header to get frameInfo */ + RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete); /* minimal frame header size */ + MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo)); + + /* special case : skippable frames */ + if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) { + dctx->frameInfo.frameType = LZ4F_skippableFrame; + if (src == (void*)(dctx->header)) { + dctx->tmpInSize = srcSize; + dctx->tmpInTarget = 8; + dctx->dStage = dstage_storeSFrameSize; + return srcSize; + } else { + dctx->dStage = dstage_getSFrameSize; + return 4; + } } + + /* control magic number */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) { + DEBUGLOG(4, "frame header error : unknown magic number"); + RETURN_ERROR(frameType_unknown); + } +#endif + dctx->frameInfo.frameType = LZ4F_frame; + + /* Flags */ + { U32 const FLG = srcPtr[4]; + U32 const version = (FLG>>6) & _2BITS; + blockChecksumFlag = (FLG>>4) & _1BIT; + blockMode = (FLG>>5) & _1BIT; + contentSizeFlag = (FLG>>3) & _1BIT; + contentChecksumFlag = (FLG>>2) & _1BIT; + dictIDFlag = FLG & _1BIT; + /* validate */ + if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */ + if (version != 1) RETURN_ERROR(headerVersion_wrong); /* Version Number, only supported value */ + } + + /* Frame Header Size */ + frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0); + + if (srcSize < frameHeaderSize) { + /* not enough input to fully decode frame header */ + if (srcPtr != dctx->header) + memcpy(dctx->header, srcPtr, srcSize); + dctx->tmpInSize = srcSize; + dctx->tmpInTarget = frameHeaderSize; + dctx->dStage = dstage_storeFrameHeader; + return srcSize; + } + + { U32 const BD = srcPtr[5]; + blockSizeID = (BD>>4) & _3BITS; + /* validate */ + if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */ + if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid); /* 4-7 only supported values for the time being */ + if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bits */ + } + + /* check header */ + assert(frameHeaderSize > 5); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + { BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5); + RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid); + } +#endif + + /* save */ + dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode; + dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag; + dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag; + dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID; + dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID); + if (contentSizeFlag) + dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6); + if (dictIDFlag) + dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5); + + dctx->dStage = dstage_init; + + return frameHeaderSize; +} + + +/*! LZ4F_headerSize() : + * @return : size of frame header + * or an error code, which can be tested using LZ4F_isError() + */ +size_t LZ4F_headerSize(const void* src, size_t srcSize) +{ + RETURN_ERROR_IF(src == NULL, srcPtr_wrong); + + /* minimal srcSize to determine header size */ + if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH) + RETURN_ERROR(frameHeader_incomplete); + + /* special case : skippable frames */ + if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) + return 8; + + /* control magic number */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER) + RETURN_ERROR(frameType_unknown); +#endif + + /* Frame Header Size */ + { BYTE const FLG = ((const BYTE*)src)[4]; + U32 const contentSizeFlag = (FLG>>3) & _1BIT; + U32 const dictIDFlag = FLG & _1BIT; + return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0); + } +} + +/*! LZ4F_getFrameInfo() : + * This function extracts frame parameters (max blockSize, frame checksum, etc.). + * Usage is optional. Objective is to provide relevant information for allocation purposes. + * This function works in 2 situations : + * - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process. + * Amount of input data provided must be large enough to successfully decode the frame header. + * A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum. + * - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx. + * The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value). + * Decompression must resume from (srcBuffer + *srcSizePtr). + * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call, + * or an error code which can be tested using LZ4F_isError() + * note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped. + * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure. + */ +LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, + LZ4F_frameInfo_t* frameInfoPtr, + const void* srcBuffer, size_t* srcSizePtr) +{ + LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader); + if (dctx->dStage > dstage_storeFrameHeader) { + /* frameInfo already decoded */ + size_t o=0, i=0; + *srcSizePtr = 0; + *frameInfoPtr = dctx->frameInfo; + /* returns : recommended nb of bytes for LZ4F_decompress() */ + return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL); + } else { + if (dctx->dStage == dstage_storeFrameHeader) { + /* frame decoding already started, in the middle of header => automatic fail */ + *srcSizePtr = 0; + RETURN_ERROR(frameDecoding_alreadyStarted); + } else { + size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr); + if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; } + if (*srcSizePtr < hSize) { + *srcSizePtr=0; + RETURN_ERROR(frameHeader_incomplete); + } + + { size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize); + if (LZ4F_isError(decodeResult)) { + *srcSizePtr = 0; + } else { + *srcSizePtr = decodeResult; + decodeResult = BHSize; /* block header size */ + } + *frameInfoPtr = dctx->frameInfo; + return decodeResult; + } } } +} + + +/* LZ4F_updateDict() : + * only used for LZ4F_blockLinked mode + * Condition : @dstPtr != NULL + */ +static void LZ4F_updateDict(LZ4F_dctx* dctx, + const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart, + unsigned withinTmp) +{ + assert(dstPtr != NULL); + if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr; /* will lead to prefix mode */ + assert(dctx->dict != NULL); + + if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */ + dctx->dictSize += dstSize; + return; + } + + assert(dstPtr >= dstBufferStart); + if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) { /* history in dstBuffer becomes large enough to become dictionary */ + dctx->dict = (const BYTE*)dstBufferStart; + dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize; + return; + } + + assert(dstSize < 64 KB); /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */ + + /* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */ + assert(dctx->tmpOutBuffer != NULL); + + if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) { /* continue history within tmpOutBuffer */ + /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */ + assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart); + dctx->dictSize += dstSize; + return; + } + + if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */ + size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer); + size_t copySize = 64 KB - dctx->tmpOutSize; + const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart; + if (dctx->tmpOutSize > 64 KB) copySize = 0; + if (copySize > preserveSize) copySize = preserveSize; + + memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize); + + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize; + return; + } + + if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */ + if (dctx->dictSize + dstSize > dctx->maxBufferSize) { /* tmp buffer not large enough */ + size_t const preserveSize = 64 KB - dstSize; + memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize); + dctx->dictSize = preserveSize; + } + memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize); + dctx->dictSize += dstSize; + return; + } + + /* join dict & dest into tmp */ + { size_t preserveSize = 64 KB - dstSize; + if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize; + memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize); + memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize); + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = preserveSize + dstSize; + } +} + + +/*! LZ4F_decompress() : + * Call this function repetitively to regenerate compressed data in srcBuffer. + * The function will attempt to decode up to *srcSizePtr bytes from srcBuffer + * into dstBuffer of capacity *dstSizePtr. + * + * The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value). + * + * The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value). + * If number of bytes read is < number of bytes provided, then decompression operation is not complete. + * Remaining data will have to be presented again in a subsequent invocation. + * + * The function result is an hint of the better srcSize to use for next call to LZ4F_decompress. + * Schematically, it's the size of the current (or remaining) compressed block + header of next block. + * Respecting the hint provides a small boost to performance, since it allows less buffer shuffling. + * Note that this is just a hint, and it's always possible to any srcSize value. + * When a frame is fully decoded, @return will be 0. + * If decompression failed, @return is an error code which can be tested using LZ4F_isError(). + */ +size_t LZ4F_decompress(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const LZ4F_decompressOptions_t* decompressOptionsPtr) +{ + LZ4F_decompressOptions_t optionsNull; + const BYTE* const srcStart = (const BYTE*)srcBuffer; + const BYTE* const srcEnd = srcStart + *srcSizePtr; + const BYTE* srcPtr = srcStart; + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL; + BYTE* dstPtr = dstStart; + const BYTE* selectedIn = NULL; + unsigned doAnotherStage = 1; + size_t nextSrcSizeHint = 1; + + + DEBUGLOG(5, "LZ4F_decompress : %p,%u => %p,%u", + srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr); + if (dstBuffer == NULL) assert(*dstSizePtr == 0); + MEM_INIT(&optionsNull, 0, sizeof(optionsNull)); + if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull; + *srcSizePtr = 0; + *dstSizePtr = 0; + assert(dctx != NULL); + dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */ + + /* behaves as a state machine */ + + while (doAnotherStage) { + + switch(dctx->dStage) + { + + case dstage_getFrameHeader: + DEBUGLOG(6, "dstage_getFrameHeader"); + if ((size_t)(srcEnd-srcPtr) >= maxFHSize) { /* enough to decode - shortcut */ + size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr)); /* will update dStage appropriately */ + FORWARD_IF_ERROR(hSize); + srcPtr += hSize; + break; + } + dctx->tmpInSize = 0; + if (srcEnd-srcPtr == 0) return minFHSize; /* 0-size input */ + dctx->tmpInTarget = minFHSize; /* minimum size to decode header */ + dctx->dStage = dstage_storeFrameHeader; + /* fall-through */ + + case dstage_storeFrameHeader: + DEBUGLOG(6, "dstage_storeFrameHeader"); + { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr)); + memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy); + dctx->tmpInSize += sizeToCopy; + srcPtr += sizeToCopy; + } + if (dctx->tmpInSize < dctx->tmpInTarget) { + nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */ + doAnotherStage = 0; /* not enough src data, ask for some more */ + break; + } + FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */ + break; + + case dstage_init: + DEBUGLOG(6, "dstage_init"); + if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0); + /* internal buffers allocation */ + { size_t const bufferNeeded = dctx->maxBlockSize + + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0); + if (bufferNeeded > dctx->maxBufferSize) { /* tmp buffers too small */ + dctx->maxBufferSize = 0; /* ensure allocation will be re-attempted on next entry*/ + LZ4F_free(dctx->tmpIn, dctx->cmem); + dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem); + RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed); + LZ4F_free(dctx->tmpOutBuffer, dctx->cmem); + dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem); + RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed); + dctx->maxBufferSize = bufferNeeded; + } } + dctx->tmpInSize = 0; + dctx->tmpInTarget = 0; + dctx->tmpOut = dctx->tmpOutBuffer; + dctx->tmpOutStart = 0; + dctx->tmpOutSize = 0; + + dctx->dStage = dstage_getBlockHeader; + /* fall-through */ + + case dstage_getBlockHeader: + if ((size_t)(srcEnd - srcPtr) >= BHSize) { + selectedIn = srcPtr; + srcPtr += BHSize; + } else { + /* not enough input to read cBlockSize field */ + dctx->tmpInSize = 0; + dctx->dStage = dstage_storeBlockHeader; + } + + if (dctx->dStage == dstage_storeBlockHeader) /* can be skipped */ + case dstage_storeBlockHeader: + { size_t const remainingInput = (size_t)(srcEnd - srcPtr); + size_t const wantedData = BHSize - dctx->tmpInSize; + size_t const sizeToCopy = MIN(wantedData, remainingInput); + memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy); + srcPtr += sizeToCopy; + dctx->tmpInSize += sizeToCopy; + + if (dctx->tmpInSize < BHSize) { /* not enough input for cBlockSize */ + nextSrcSizeHint = BHSize - dctx->tmpInSize; + doAnotherStage = 0; + break; + } + selectedIn = dctx->tmpIn; + } /* if (dctx->dStage == dstage_storeBlockHeader) */ + + /* decode block header */ + { U32 const blockHeader = LZ4F_readLE32(selectedIn); + size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU; + size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize; + if (blockHeader==0) { /* frameEnd signal, no more block */ + DEBUGLOG(5, "end of frame"); + dctx->dStage = dstage_getSuffix; + break; + } + if (nextCBlockSize > dctx->maxBlockSize) { + RETURN_ERROR(maxBlockSize_invalid); + } + if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) { + /* next block is uncompressed */ + dctx->tmpInTarget = nextCBlockSize; + DEBUGLOG(5, "next block is uncompressed (size %u)", (U32)nextCBlockSize); + if (dctx->frameInfo.blockChecksumFlag) { + (void)XXH32_reset(&dctx->blockChecksum, 0); + } + dctx->dStage = dstage_copyDirect; + break; + } + /* next block is a compressed block */ + dctx->tmpInTarget = nextCBlockSize + crcSize; + dctx->dStage = dstage_getCBlock; + if (dstPtr==dstEnd || srcPtr==srcEnd) { + nextSrcSizeHint = BHSize + nextCBlockSize + crcSize; + doAnotherStage = 0; + } + break; + } + + case dstage_copyDirect: /* uncompressed block */ + DEBUGLOG(6, "dstage_copyDirect"); + { size_t sizeToCopy; + if (dstPtr == NULL) { + sizeToCopy = 0; + } else { + size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr)); + sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize); + memcpy(dstPtr, srcPtr, sizeToCopy); + if (!dctx->skipChecksum) { + if (dctx->frameInfo.blockChecksumFlag) { + (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy); + } + if (dctx->frameInfo.contentChecksumFlag) + (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy); + } + if (dctx->frameInfo.contentSize) + dctx->frameRemainingSize -= sizeToCopy; + + /* history management (linked blocks only)*/ + if (dctx->frameInfo.blockMode == LZ4F_blockLinked) { + LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0); + } + srcPtr += sizeToCopy; + dstPtr += sizeToCopy; + } + if (sizeToCopy == dctx->tmpInTarget) { /* all done */ + if (dctx->frameInfo.blockChecksumFlag) { + dctx->tmpInSize = 0; + dctx->dStage = dstage_getBlockChecksum; + } else + dctx->dStage = dstage_getBlockHeader; /* new block */ + break; + } + dctx->tmpInTarget -= sizeToCopy; /* need to copy more */ + } + nextSrcSizeHint = dctx->tmpInTarget + + +(dctx->frameInfo.blockChecksumFlag ? BFSize : 0) + + BHSize /* next header size */; + doAnotherStage = 0; + break; + + /* check block checksum for recently transferred uncompressed block */ + case dstage_getBlockChecksum: + DEBUGLOG(6, "dstage_getBlockChecksum"); + { const void* crcSrc; + if ((srcEnd-srcPtr >= 4) && (dctx->tmpInSize==0)) { + crcSrc = srcPtr; + srcPtr += 4; + } else { + size_t const stillToCopy = 4 - dctx->tmpInSize; + size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr)); + memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy); + dctx->tmpInSize += sizeToCopy; + srcPtr += sizeToCopy; + if (dctx->tmpInSize < 4) { /* all input consumed */ + doAnotherStage = 0; + break; + } + crcSrc = dctx->header; + } + if (!dctx->skipChecksum) { + U32 const readCRC = LZ4F_readLE32(crcSrc); + U32 const calcCRC = XXH32_digest(&dctx->blockChecksum); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + DEBUGLOG(6, "compare block checksum"); + if (readCRC != calcCRC) { + DEBUGLOG(4, "incorrect block checksum: %08X != %08X", + readCRC, calcCRC); + RETURN_ERROR(blockChecksum_invalid); + } +#else + (void)readCRC; + (void)calcCRC; +#endif + } } + dctx->dStage = dstage_getBlockHeader; /* new block */ + break; + + case dstage_getCBlock: + DEBUGLOG(6, "dstage_getCBlock"); + if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) { + dctx->tmpInSize = 0; + dctx->dStage = dstage_storeCBlock; + break; + } + /* input large enough to read full block directly */ + selectedIn = srcPtr; + srcPtr += dctx->tmpInTarget; + + if (0) /* always jump over next block */ + case dstage_storeCBlock: + { size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize; + size_t const inputLeft = (size_t)(srcEnd-srcPtr); + size_t const sizeToCopy = MIN(wantedData, inputLeft); + memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy); + dctx->tmpInSize += sizeToCopy; + srcPtr += sizeToCopy; + if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */ + nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + + (dctx->frameInfo.blockChecksumFlag ? BFSize : 0) + + BHSize /* next header size */; + doAnotherStage = 0; + break; + } + selectedIn = dctx->tmpIn; + } + + /* At this stage, input is large enough to decode a block */ + + /* First, decode and control block checksum if it exists */ + if (dctx->frameInfo.blockChecksumFlag) { + assert(dctx->tmpInTarget >= 4); + dctx->tmpInTarget -= 4; + assert(selectedIn != NULL); /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */ + { U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget); + U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid); +#else + (void)readBlockCrc; + (void)calcBlockCrc; +#endif + } } + + /* decode directly into destination buffer if there is enough room */ + if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize) + /* unless the dictionary is stored in tmpOut: + * in which case it's faster to decode within tmpOut + * to benefit from prefix speedup */ + && !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) ) + { + const char* dict = (const char*)dctx->dict; + size_t dictSize = dctx->dictSize; + int decodedSize; + assert(dstPtr != NULL); + if (dict && dictSize > 1 GB) { + /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */ + dict += dictSize - 64 KB; + dictSize = 64 KB; + } + decodedSize = LZ4_decompress_safe_usingDict( + (const char*)selectedIn, (char*)dstPtr, + (int)dctx->tmpInTarget, (int)dctx->maxBlockSize, + dict, (int)dictSize); + RETURN_ERROR_IF(decodedSize < 0, decompressionFailed); + if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum)) + XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize); + if (dctx->frameInfo.contentSize) + dctx->frameRemainingSize -= (size_t)decodedSize; + + /* dictionary management */ + if (dctx->frameInfo.blockMode==LZ4F_blockLinked) { + LZ4F_updateDict(dctx, dstPtr, (size_t)decodedSize, dstStart, 0); + } + + dstPtr += decodedSize; + dctx->dStage = dstage_getBlockHeader; /* end of block, let's get another one */ + break; + } + + /* not enough place into dst : decode into tmpOut */ + + /* manage dictionary */ + if (dctx->frameInfo.blockMode == LZ4F_blockLinked) { + if (dctx->dict == dctx->tmpOutBuffer) { + /* truncate dictionary to 64 KB if too big */ + if (dctx->dictSize > 128 KB) { + memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB); + dctx->dictSize = 64 KB; + } + dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize; + } else { /* dict not within tmpOut */ + size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB); + dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace; + } } + + /* Decode block into tmpOut */ + { const char* dict = (const char*)dctx->dict; + size_t dictSize = dctx->dictSize; + int decodedSize; + if (dict && dictSize > 1 GB) { + /* the dictSize param is an int, avoid truncation / sign issues */ + dict += dictSize - 64 KB; + dictSize = 64 KB; + } + decodedSize = LZ4_decompress_safe_usingDict( + (const char*)selectedIn, (char*)dctx->tmpOut, + (int)dctx->tmpInTarget, (int)dctx->maxBlockSize, + dict, (int)dictSize); + RETURN_ERROR_IF(decodedSize < 0, decompressionFailed); + if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum) + XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize); + if (dctx->frameInfo.contentSize) + dctx->frameRemainingSize -= (size_t)decodedSize; + dctx->tmpOutSize = (size_t)decodedSize; + dctx->tmpOutStart = 0; + dctx->dStage = dstage_flushOut; + } + /* fall-through */ + + case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */ + DEBUGLOG(6, "dstage_flushOut"); + if (dstPtr != NULL) { + size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr)); + memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy); + + /* dictionary management */ + if (dctx->frameInfo.blockMode == LZ4F_blockLinked) + LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/); + + dctx->tmpOutStart += sizeToCopy; + dstPtr += sizeToCopy; + } + if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */ + dctx->dStage = dstage_getBlockHeader; /* get next block */ + break; + } + /* could not flush everything : stop there, just request a block header */ + doAnotherStage = 0; + nextSrcSizeHint = BHSize; + break; + + case dstage_getSuffix: + RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong); /* incorrect frame size decoded */ + if (!dctx->frameInfo.contentChecksumFlag) { /* no checksum, frame is completed */ + nextSrcSizeHint = 0; + LZ4F_resetDecompressionContext(dctx); + doAnotherStage = 0; + break; + } + if ((srcEnd - srcPtr) < 4) { /* not enough size for entire CRC */ + dctx->tmpInSize = 0; + dctx->dStage = dstage_storeSuffix; + } else { + selectedIn = srcPtr; + srcPtr += 4; + } + + if (dctx->dStage == dstage_storeSuffix) /* can be skipped */ + case dstage_storeSuffix: + { size_t const remainingInput = (size_t)(srcEnd - srcPtr); + size_t const wantedData = 4 - dctx->tmpInSize; + size_t const sizeToCopy = MIN(wantedData, remainingInput); + memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy); + srcPtr += sizeToCopy; + dctx->tmpInSize += sizeToCopy; + if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */ + nextSrcSizeHint = 4 - dctx->tmpInSize; + doAnotherStage=0; + break; + } + selectedIn = dctx->tmpIn; + } /* if (dctx->dStage == dstage_storeSuffix) */ + + /* case dstage_checkSuffix: */ /* no direct entry, avoid initialization risks */ + if (!dctx->skipChecksum) { + U32 const readCRC = LZ4F_readLE32(selectedIn); + U32 const resultCRC = XXH32_digest(&(dctx->xxh)); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid); +#else + (void)readCRC; + (void)resultCRC; +#endif + } + nextSrcSizeHint = 0; + LZ4F_resetDecompressionContext(dctx); + doAnotherStage = 0; + break; + + case dstage_getSFrameSize: + if ((srcEnd - srcPtr) >= 4) { + selectedIn = srcPtr; + srcPtr += 4; + } else { + /* not enough input to read cBlockSize field */ + dctx->tmpInSize = 4; + dctx->tmpInTarget = 8; + dctx->dStage = dstage_storeSFrameSize; + } + + if (dctx->dStage == dstage_storeSFrameSize) + case dstage_storeSFrameSize: + { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, + (size_t)(srcEnd - srcPtr) ); + memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy); + srcPtr += sizeToCopy; + dctx->tmpInSize += sizeToCopy; + if (dctx->tmpInSize < dctx->tmpInTarget) { + /* not enough input to get full sBlockSize; wait for more */ + nextSrcSizeHint = dctx->tmpInTarget - dctx->tmpInSize; + doAnotherStage = 0; + break; + } + selectedIn = dctx->header + 4; + } /* if (dctx->dStage == dstage_storeSFrameSize) */ + + /* case dstage_decodeSFrameSize: */ /* no direct entry */ + { size_t const SFrameSize = LZ4F_readLE32(selectedIn); + dctx->frameInfo.contentSize = SFrameSize; + dctx->tmpInTarget = SFrameSize; + dctx->dStage = dstage_skipSkippable; + break; + } + + case dstage_skipSkippable: + { size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr)); + srcPtr += skipSize; + dctx->tmpInTarget -= skipSize; + doAnotherStage = 0; + nextSrcSizeHint = dctx->tmpInTarget; + if (nextSrcSizeHint) break; /* still more to skip */ + /* frame fully skipped : prepare context for a new frame */ + LZ4F_resetDecompressionContext(dctx); + break; + } + } /* switch (dctx->dStage) */ + } /* while (doAnotherStage) */ + + /* preserve history within tmpOut whenever necessary */ + LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2); + if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked) /* next block will use up to 64KB from previous ones */ + && (dctx->dict != dctx->tmpOutBuffer) /* dictionary is not already within tmp */ + && (dctx->dict != NULL) /* dictionary exists */ + && (!decompressOptionsPtr->stableDst) /* cannot rely on dst data to remain there for next call */ + && ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) ) /* valid stages : [init ... getSuffix[ */ + { + if (dctx->dStage == dstage_flushOut) { + size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer); + size_t copySize = 64 KB - dctx->tmpOutSize; + const BYTE* oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart; + if (dctx->tmpOutSize > 64 KB) copySize = 0; + if (copySize > preserveSize) copySize = preserveSize; + assert(dctx->tmpOutBuffer != NULL); + + memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize); + + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = preserveSize + dctx->tmpOutStart; + } else { + const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize; + size_t const newDictSize = MIN(dctx->dictSize, 64 KB); + + memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize); + + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = newDictSize; + dctx->tmpOut = dctx->tmpOutBuffer + newDictSize; + } + } + + *srcSizePtr = (size_t)(srcPtr - srcStart); + *dstSizePtr = (size_t)(dstPtr - dstStart); + return nextSrcSizeHint; +} + +/*! LZ4F_decompress_usingDict() : + * Same as LZ4F_decompress(), using a predefined dictionary. + * Dictionary is used "in place", without any preprocessing. + * It must remain accessible throughout the entire frame decoding. + */ +size_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const void* dict, size_t dictSize, + const LZ4F_decompressOptions_t* decompressOptionsPtr) +{ + if (dctx->dStage <= dstage_init) { + dctx->dict = (const BYTE*)dict; + dctx->dictSize = dictSize; + } + return LZ4F_decompress(dctx, dstBuffer, dstSizePtr, + srcBuffer, srcSizePtr, + decompressOptionsPtr); +} diff --git a/CompressSave/LZ4WrapC/lz4/lz4frame.h b/CompressSave/LZ4WrapC/lz4/lz4frame.h new file mode 100644 index 0000000..8d9380b --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4frame.h @@ -0,0 +1,692 @@ +/* + LZ4F - LZ4-Frame library + Header File + Copyright (C) 2011-2020, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +/* LZ4F is a stand-alone API able to create and decode LZ4 frames + * conformant with specification v1.6.1 in doc/lz4_Frame_format.md . + * Generated frames are compatible with `lz4` CLI. + * + * LZ4F also offers streaming capabilities. + * + * lz4.h is not required when using lz4frame.h, + * except to extract common constants such as LZ4_VERSION_NUMBER. + * */ + +#ifndef LZ4F_H_09782039843 +#define LZ4F_H_09782039843 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* --- Dependency --- */ +#include /* size_t */ + + +/** + * Introduction + * + * lz4frame.h implements LZ4 frame specification: see doc/lz4_Frame_format.md . + * LZ4 Frames are compatible with `lz4` CLI, + * and designed to be interoperable with any system. +**/ + +/*-*************************************************************** + * Compiler specifics + *****************************************************************/ +/* LZ4_DLL_EXPORT : + * Enable exporting of functions when building a Windows DLL + * LZ4FLIB_VISIBILITY : + * Control library symbols visibility. + */ +#ifndef LZ4FLIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4FLIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4FLIB_VISIBILITY +# endif +#endif +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4FLIB_API __declspec(dllexport) LZ4FLIB_VISIBILITY +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4FLIB_API __declspec(dllimport) LZ4FLIB_VISIBILITY +#else +# define LZ4FLIB_API LZ4FLIB_VISIBILITY +#endif + +#ifdef LZ4F_DISABLE_DEPRECATE_WARNINGS +# define LZ4F_DEPRECATE(x) x +#else +# if defined(_MSC_VER) +# define LZ4F_DEPRECATE(x) x /* __declspec(deprecated) x - only works with C++ */ +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +# define LZ4F_DEPRECATE(x) x __attribute__((deprecated)) +# else +# define LZ4F_DEPRECATE(x) x /* no deprecation warning for this compiler */ +# endif +#endif + + +/*-************************************ + * Error management + **************************************/ +typedef size_t LZ4F_errorCode_t; + +LZ4FLIB_API unsigned LZ4F_isError(LZ4F_errorCode_t code); /**< tells when a function result is an error code */ +LZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code); /**< return error code string; for debugging */ + + +/*-************************************ + * Frame compression types + ************************************* */ +/* #define LZ4F_ENABLE_OBSOLETE_ENUMS // uncomment to enable obsolete enums */ +#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS +# define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x +#else +# define LZ4F_OBSOLETE_ENUM(x) +#endif + +/* The larger the block size, the (slightly) better the compression ratio, + * though there are diminishing returns. + * Larger blocks also increase memory usage on both compression and decompression sides. + */ +typedef enum { + LZ4F_default=0, + LZ4F_max64KB=4, + LZ4F_max256KB=5, + LZ4F_max1MB=6, + LZ4F_max4MB=7 + LZ4F_OBSOLETE_ENUM(max64KB) + LZ4F_OBSOLETE_ENUM(max256KB) + LZ4F_OBSOLETE_ENUM(max1MB) + LZ4F_OBSOLETE_ENUM(max4MB) +} LZ4F_blockSizeID_t; + +/* Linked blocks sharply reduce inefficiencies when using small blocks, + * they compress better. + * However, some LZ4 decoders are only compatible with independent blocks */ +typedef enum { + LZ4F_blockLinked=0, + LZ4F_blockIndependent + LZ4F_OBSOLETE_ENUM(blockLinked) + LZ4F_OBSOLETE_ENUM(blockIndependent) +} LZ4F_blockMode_t; + +typedef enum { + LZ4F_noContentChecksum=0, + LZ4F_contentChecksumEnabled + LZ4F_OBSOLETE_ENUM(noContentChecksum) + LZ4F_OBSOLETE_ENUM(contentChecksumEnabled) +} LZ4F_contentChecksum_t; + +typedef enum { + LZ4F_noBlockChecksum=0, + LZ4F_blockChecksumEnabled +} LZ4F_blockChecksum_t; + +typedef enum { + LZ4F_frame=0, + LZ4F_skippableFrame + LZ4F_OBSOLETE_ENUM(skippableFrame) +} LZ4F_frameType_t; + +#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS +typedef LZ4F_blockSizeID_t blockSizeID_t; +typedef LZ4F_blockMode_t blockMode_t; +typedef LZ4F_frameType_t frameType_t; +typedef LZ4F_contentChecksum_t contentChecksum_t; +#endif + +/*! LZ4F_frameInfo_t : + * makes it possible to set or read frame parameters. + * Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO, + * setting all parameters to default. + * It's then possible to update selectively some parameters */ +typedef struct { + LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */ + LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */ + LZ4F_contentChecksum_t contentChecksumFlag; /* 1: frame terminated with 32-bit checksum of decompressed data; 0: disabled (default) */ + LZ4F_frameType_t frameType; /* read-only field : LZ4F_frame or LZ4F_skippableFrame */ + unsigned long long contentSize; /* Size of uncompressed content ; 0 == unknown */ + unsigned dictID; /* Dictionary ID, sent by compressor to help decoder select correct dictionary; 0 == no dictID provided */ + LZ4F_blockChecksum_t blockChecksumFlag; /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */ +} LZ4F_frameInfo_t; + +#define LZ4F_INIT_FRAMEINFO { LZ4F_default, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U, LZ4F_noBlockChecksum } /* v1.8.3+ */ + +/*! LZ4F_preferences_t : + * makes it possible to supply advanced compression instructions to streaming interface. + * Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES, + * setting all parameters to default. + * All reserved fields must be set to zero. */ +typedef struct { + LZ4F_frameInfo_t frameInfo; + int compressionLevel; /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */ + unsigned autoFlush; /* 1: always flush; reduces usage of internal buffers */ + unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* v1.8.2+ */ + unsigned reserved[3]; /* must be zero for forward compatibility */ +} LZ4F_preferences_t; + +#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } } /* v1.8.3+ */ + + +/*-********************************* +* Simple compression function +***********************************/ + +LZ4FLIB_API int LZ4F_compressionLevel_max(void); /* v1.8.0+ */ + +/*! LZ4F_compressFrameBound() : + * Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences. + * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences. + * Note : this result is only usable with LZ4F_compressFrame(). + * It may also be relevant to LZ4F_compressUpdate() _only if_ no flush() operation is ever performed. + */ +LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr); + +/*! LZ4F_compressFrame() : + * Compress an entire srcBuffer into a valid LZ4 frame. + * dstCapacity MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default. + * @return : number of bytes written into dstBuffer. + * or an error code if it fails (can be tested using LZ4F_isError()) + */ +LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_preferences_t* preferencesPtr); + + +/*-*********************************** +* Advanced compression functions +*************************************/ +typedef struct LZ4F_cctx_s LZ4F_cctx; /* incomplete type */ +typedef LZ4F_cctx* LZ4F_compressionContext_t; /* for compatibility with older APIs, prefer using LZ4F_cctx */ + +typedef struct { + unsigned stableSrc; /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */ + unsigned reserved[3]; +} LZ4F_compressOptions_t; + +/*--- Resource Management ---*/ + +#define LZ4F_VERSION 100 /* This number can be used to check for an incompatible API breaking change */ +LZ4FLIB_API unsigned LZ4F_getVersion(void); + +/*! LZ4F_createCompressionContext() : + * The first thing to do is to create a compressionContext object, + * which will keep track of operation state during streaming compression. + * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version, + * and a pointer to LZ4F_cctx*, to write the resulting pointer into. + * @version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL. + * The function provides a pointer to a fully allocated LZ4F_cctx object. + * @cctxPtr MUST be != NULL. + * If @return != zero, context creation failed. + * A created compression context can be employed multiple times for consecutive streaming operations. + * Once all streaming compression jobs are completed, + * the state object can be released using LZ4F_freeCompressionContext(). + * Note1 : LZ4F_freeCompressionContext() is always successful. Its return value can be ignored. + * Note2 : LZ4F_freeCompressionContext() works fine with NULL input pointers (do nothing). +**/ +LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version); +LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx); + + +/*---- Compression ----*/ + +#define LZ4F_HEADER_SIZE_MIN 7 /* LZ4 Frame header size can vary, depending on selected parameters */ +#define LZ4F_HEADER_SIZE_MAX 19 + +/* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */ +#define LZ4F_BLOCK_HEADER_SIZE 4 + +/* Size in bytes of a block checksum footer in little-endian format. */ +#define LZ4F_BLOCK_CHECKSUM_SIZE 4 + +/* Size in bytes of the content checksum. */ +#define LZ4F_CONTENT_CHECKSUM_SIZE 4 + +/*! LZ4F_compressBegin() : + * will write the frame header into dstBuffer. + * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * `prefsPtr` is optional : NULL can be provided to set all preferences to default. + * @return : number of bytes written into dstBuffer for the header + * or an error code (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_preferences_t* prefsPtr); + +/*! LZ4F_compressBound() : + * Provides minimum dstCapacity required to guarantee success of + * LZ4F_compressUpdate(), given a srcSize and preferences, for a worst case scenario. + * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() instead. + * Note that the result is only valid for a single invocation of LZ4F_compressUpdate(). + * When invoking LZ4F_compressUpdate() multiple times, + * if the output buffer is gradually filled up instead of emptied and re-used from its start, + * one must check if there is enough remaining capacity before each invocation, using LZ4F_compressBound(). + * @return is always the same for a srcSize and prefsPtr. + * prefsPtr is optional : when NULL is provided, preferences will be set to cover worst case scenario. + * tech details : + * @return if automatic flushing is not enabled, includes the possibility that internal buffer might already be filled by up to (blockSize-1) bytes. + * It also includes frame footer (ending + checksum), since it might be generated by LZ4F_compressEnd(). + * @return doesn't include frame header, as it was already generated by LZ4F_compressBegin(). + */ +LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr); + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations. + * This value is provided by LZ4F_compressBound(). + * If this condition is not respected, LZ4F_compress() will fail (result is an errorCode). + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + * or an error code if it fails (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); + +/*! LZ4F_flush() : + * When data must be generated and sent immediately, without waiting for a block to be completely filled, + * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. + * `dstCapacity` must be large enough to ensure the operation will be successful. + * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. + * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) + * or an error code if it fails (which can be tested using LZ4F_isError()) + * Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). + */ +LZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* cOptPtr); + +/*! LZ4F_compressEnd() : + * To properly finish an LZ4 frame, invoke LZ4F_compressEnd(). + * It will flush whatever data remained within `cctx` (like LZ4_flush()) + * and properly finalize the frame, with an endMark and a checksum. + * `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default. + * @return : nb of bytes written into dstBuffer, necessarily >= 4 (endMark), + * or an error code if it fails (which can be tested using LZ4F_isError()) + * Note : LZ4F_compressEnd() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). + * A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task. + */ +LZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* cOptPtr); + + +/*-********************************* +* Decompression functions +***********************************/ +typedef struct LZ4F_dctx_s LZ4F_dctx; /* incomplete type */ +typedef LZ4F_dctx* LZ4F_decompressionContext_t; /* compatibility with previous API versions */ + +typedef struct { + unsigned stableDst; /* pledges that last 64KB decompressed data will remain available unmodified between invocations. + * This optimization skips storage operations in tmp buffers. */ + unsigned skipChecksums; /* disable checksum calculation and verification, even when one is present in frame, to save CPU time. + * Setting this option to 1 once disables all checksums for the rest of the frame. */ + unsigned reserved1; /* must be set to zero for forward compatibility */ + unsigned reserved0; /* idem */ +} LZ4F_decompressOptions_t; + + +/* Resource management */ + +/*! LZ4F_createDecompressionContext() : + * Create an LZ4F_dctx object, to track all decompression operations. + * @version provided MUST be LZ4F_VERSION. + * @dctxPtr MUST be valid. + * The function fills @dctxPtr with the value of a pointer to an allocated and initialized LZ4F_dctx object. + * The @return is an errorCode, which can be tested using LZ4F_isError(). + * dctx memory can be released using LZ4F_freeDecompressionContext(); + * Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released. + * That is, it should be == 0 if decompression has been completed fully and correctly. + */ +LZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version); +LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx); + + +/*-*********************************** +* Streaming decompression functions +*************************************/ + +#define LZ4F_MAGICNUMBER 0x184D2204U +#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U +#define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5 + +/*! LZ4F_headerSize() : v1.9.0+ + * Provide the header size of a frame starting at `src`. + * `srcSize` must be >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH, + * which is enough to decode the header length. + * @return : size of frame header + * or an error code, which can be tested using LZ4F_isError() + * note : Frame header size is variable, but is guaranteed to be + * >= LZ4F_HEADER_SIZE_MIN bytes, and <= LZ4F_HEADER_SIZE_MAX bytes. + */ +LZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize); + +/*! LZ4F_getFrameInfo() : + * This function extracts frame parameters (max blockSize, dictID, etc.). + * Its usage is optional: user can also invoke LZ4F_decompress() directly. + * + * Extracted information will fill an existing LZ4F_frameInfo_t structure. + * This can be useful for allocation and dictionary identification purposes. + * + * LZ4F_getFrameInfo() can work in the following situations : + * + * 1) At the beginning of a new frame, before any invocation of LZ4F_decompress(). + * It will decode header from `srcBuffer`, + * consuming the header and starting the decoding process. + * + * Input size must be large enough to contain the full frame header. + * Frame header size can be known beforehand by LZ4F_headerSize(). + * Frame header size is variable, but is guaranteed to be >= LZ4F_HEADER_SIZE_MIN bytes, + * and not more than <= LZ4F_HEADER_SIZE_MAX bytes. + * Hence, blindly providing LZ4F_HEADER_SIZE_MAX bytes or more will always work. + * It's allowed to provide more input data than the header size, + * LZ4F_getFrameInfo() will only consume the header. + * + * If input size is not large enough, + * aka if it's smaller than header size, + * function will fail and return an error code. + * + * 2) After decoding has been started, + * it's possible to invoke LZ4F_getFrameInfo() anytime + * to extract already decoded frame parameters stored within dctx. + * + * Note that, if decoding has barely started, + * and not yet read enough information to decode the header, + * LZ4F_getFrameInfo() will fail. + * + * The number of bytes consumed from srcBuffer will be updated in *srcSizePtr (necessarily <= original value). + * LZ4F_getFrameInfo() only consumes bytes when decoding has not yet started, + * and when decoding the header has been successful. + * Decompression must then resume from (srcBuffer + *srcSizePtr). + * + * @return : a hint about how many srcSize bytes LZ4F_decompress() expects for next call, + * or an error code which can be tested using LZ4F_isError(). + * note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely. + * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure. + */ +LZ4FLIB_API size_t +LZ4F_getFrameInfo(LZ4F_dctx* dctx, + LZ4F_frameInfo_t* frameInfoPtr, + const void* srcBuffer, size_t* srcSizePtr); + +/*! LZ4F_decompress() : + * Call this function repetitively to regenerate data compressed in `srcBuffer`. + * + * The function requires a valid dctx state. + * It will read up to *srcSizePtr bytes from srcBuffer, + * and decompress data into dstBuffer, of capacity *dstSizePtr. + * + * The nb of bytes consumed from srcBuffer will be written into *srcSizePtr (necessarily <= original value). + * The nb of bytes decompressed into dstBuffer will be written into *dstSizePtr (necessarily <= original value). + * + * The function does not necessarily read all input bytes, so always check value in *srcSizePtr. + * Unconsumed source data must be presented again in subsequent invocations. + * + * `dstBuffer` can freely change between each consecutive function invocation. + * `dstBuffer` content will be overwritten. + * + * @return : an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call. + * Schematically, it's the size of the current (or remaining) compressed block + header of next block. + * Respecting the hint provides some small speed benefit, because it skips intermediate buffers. + * This is just a hint though, it's always possible to provide any srcSize. + * + * When a frame is fully decoded, @return will be 0 (no more data expected). + * When provided with more bytes than necessary to decode a frame, + * LZ4F_decompress() will stop reading exactly at end of current frame, and @return 0. + * + * If decompression failed, @return is an error code, which can be tested using LZ4F_isError(). + * After a decompression error, the `dctx` context is not resumable. + * Use LZ4F_resetDecompressionContext() to return to clean state. + * + * After a frame is fully decoded, dctx can be used again to decompress another frame. + */ +LZ4FLIB_API size_t +LZ4F_decompress(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const LZ4F_decompressOptions_t* dOptPtr); + + +/*! LZ4F_resetDecompressionContext() : added in v1.8.0 + * In case of an error, the context is left in "undefined" state. + * In which case, it's necessary to reset it, before re-using it. + * This method can also be used to abruptly stop any unfinished decompression, + * and start a new one using same context resources. */ +LZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); /* always successful */ + + + +#if defined (__cplusplus) +} +#endif + +#endif /* LZ4F_H_09782039843 */ + +#if defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) +#define LZ4F_H_STATIC_09782039843 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* These declarations are not stable and may change in the future. + * They are therefore only safe to depend on + * when the caller is statically linked against the library. + * To access their declarations, define LZ4F_STATIC_LINKING_ONLY. + * + * By default, these symbols aren't published into shared/dynamic libraries. + * You can override this behavior and force them to be published + * by defining LZ4F_PUBLISH_STATIC_FUNCTIONS. + * Use at your own risk. + */ +#ifdef LZ4F_PUBLISH_STATIC_FUNCTIONS +# define LZ4FLIB_STATIC_API LZ4FLIB_API +#else +# define LZ4FLIB_STATIC_API +#endif + + +/* --- Error List --- */ +#define LZ4F_LIST_ERRORS(ITEM) \ + ITEM(OK_NoError) \ + ITEM(ERROR_GENERIC) \ + ITEM(ERROR_maxBlockSize_invalid) \ + ITEM(ERROR_blockMode_invalid) \ + ITEM(ERROR_contentChecksumFlag_invalid) \ + ITEM(ERROR_compressionLevel_invalid) \ + ITEM(ERROR_headerVersion_wrong) \ + ITEM(ERROR_blockChecksum_invalid) \ + ITEM(ERROR_reservedFlag_set) \ + ITEM(ERROR_allocation_failed) \ + ITEM(ERROR_srcSize_tooLarge) \ + ITEM(ERROR_dstMaxSize_tooSmall) \ + ITEM(ERROR_frameHeader_incomplete) \ + ITEM(ERROR_frameType_unknown) \ + ITEM(ERROR_frameSize_wrong) \ + ITEM(ERROR_srcPtr_wrong) \ + ITEM(ERROR_decompressionFailed) \ + ITEM(ERROR_headerChecksum_invalid) \ + ITEM(ERROR_contentChecksum_invalid) \ + ITEM(ERROR_frameDecoding_alreadyStarted) \ + ITEM(ERROR_compressionState_uninitialized) \ + ITEM(ERROR_parameter_null) \ + ITEM(ERROR_maxCode) + +#define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM, + +/* enum list is exposed, to handle specific errors */ +typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) + _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes; + +LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult); + + +/*! LZ4F_getBlockSize() : + * Return, in scalar format (size_t), + * the maximum block size associated with blockSizeID. +**/ +LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID); + +/*! LZ4F_uncompressedUpdate() : + * LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. + * Important rule: dstCapacity MUST be large enough to store the entire source buffer as + * no compression is done for this operation + * If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously a compressed block was written, buffered data is flushed + * before appending uncompressed data is continued. + * This is only supported when LZ4F_blockIndependent is used + * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + * or an error code if it fails (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_STATIC_API size_t +LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); + +/********************************** + * Bulk processing dictionary API + *********************************/ + +/* A Dictionary is useful for the compression of small messages (KB range). + * It dramatically improves compression efficiency. + * + * LZ4 can ingest any input as dictionary, though only the last 64 KB are useful. + * Best results are generally achieved by using Zstandard's Dictionary Builder + * to generate a high-quality dictionary from a set of samples. + * + * Loading a dictionary has a cost, since it involves construction of tables. + * The Bulk processing dictionary API makes it possible to share this cost + * over an arbitrary number of compression jobs, even concurrently, + * markedly improving compression latency for these cases. + * + * The same dictionary will have to be used on the decompression side + * for decoding to be successful. + * To help identify the correct dictionary at decoding stage, + * the frame header allows optional embedding of a dictID field. + */ +typedef struct LZ4F_CDict_s LZ4F_CDict; + +/*! LZ4_createCDict() : + * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it just once. + * LZ4_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. + * LZ4_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * `dictBuffer` can be released after LZ4_CDict creation, since its content is copied within CDict */ +LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize); +LZ4FLIB_STATIC_API void LZ4F_freeCDict(LZ4F_CDict* CDict); + + +/*! LZ4_compressFrame_usingCDict() : + * Compress an entire srcBuffer into a valid LZ4 frame using a digested Dictionary. + * cctx must point to a context created by LZ4F_createCompressionContext(). + * If cdict==NULL, compress without a dictionary. + * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * If this condition is not respected, function will fail (@return an errorCode). + * The LZ4F_preferences_t structure is optional : you may provide NULL as argument, + * but it's not recommended, as it's the only way to provide dictID in the frame header. + * @return : number of bytes written into dstBuffer. + * or an error code if it fails (can be tested using LZ4F_isError()) */ +LZ4FLIB_STATIC_API size_t +LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr); + + +/*! LZ4F_compressBegin_usingCDict() : + * Inits streaming dictionary compression, and writes the frame header into dstBuffer. + * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * `prefsPtr` is optional : you may provide NULL as argument, + * however, it's the only way to provide dictID in the frame header. + * @return : number of bytes written into dstBuffer for the header, + * or an error code (which can be tested using LZ4F_isError()) */ +LZ4FLIB_STATIC_API size_t +LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* prefsPtr); + + +/*! LZ4F_decompress_usingDict() : + * Same as LZ4F_decompress(), using a predefined dictionary. + * Dictionary is used "in place", without any preprocessing. +** It must remain accessible throughout the entire frame decoding. */ +LZ4FLIB_STATIC_API size_t +LZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const void* dict, size_t dictSize, + const LZ4F_decompressOptions_t* decompressOptionsPtr); + + +/*! Custom memory allocation : + * These prototypes make it possible to pass custom allocation/free functions. + * LZ4F_customMem is provided at state creation time, using LZ4F_create*_advanced() listed below. + * All allocation/free operations will be completed using these custom variants instead of regular ones. + */ +typedef void* (*LZ4F_AllocFunction) (void* opaqueState, size_t size); +typedef void* (*LZ4F_CallocFunction) (void* opaqueState, size_t size); +typedef void (*LZ4F_FreeFunction) (void* opaqueState, void* address); +typedef struct { + LZ4F_AllocFunction customAlloc; + LZ4F_CallocFunction customCalloc; /* optional; when not defined, uses customAlloc + memset */ + LZ4F_FreeFunction customFree; + void* opaqueState; +} LZ4F_CustomMem; +static +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif +LZ4F_CustomMem const LZ4F_defaultCMem = { NULL, NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */ + +LZ4FLIB_STATIC_API LZ4F_cctx* LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version); +LZ4FLIB_STATIC_API LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version); +LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict_advanced(LZ4F_CustomMem customMem, const void* dictBuffer, size_t dictSize); + + +#if defined (__cplusplus) +} +#endif + +#endif /* defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) */ diff --git a/CompressSave/LZ4WrapC/lz4/lz4frame_static.h b/CompressSave/LZ4WrapC/lz4/lz4frame_static.h new file mode 100644 index 0000000..2b44a63 --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4frame_static.h @@ -0,0 +1,47 @@ +/* + LZ4 auto-framing library + Header File for static linking only + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +#ifndef LZ4FRAME_STATIC_H_0398209384 +#define LZ4FRAME_STATIC_H_0398209384 + +/* The declarations that formerly were made here have been merged into + * lz4frame.h, protected by the LZ4F_STATIC_LINKING_ONLY macro. Going forward, + * it is recommended to simply include that header directly. + */ + +#define LZ4F_STATIC_LINKING_ONLY +#include "lz4frame.h" + +#endif /* LZ4FRAME_STATIC_H_0398209384 */ diff --git a/CompressSave/LZ4WrapC/lz4/lz4hc.c b/CompressSave/LZ4WrapC/lz4/lz4hc.c new file mode 100644 index 0000000..e83246b --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4hc.c @@ -0,0 +1,1631 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ + + +/* ************************************* +* Tuning Parameter +***************************************/ + +/*! HEAPMODE : + * Select how default compression function will allocate workplace memory, + * in stack (0:fastest), or in heap (1:requires malloc()). + * Since workplace is rather large, heap mode is recommended. +**/ +#ifndef LZ4HC_HEAPMODE +# define LZ4HC_HEAPMODE 1 +#endif + + +/*=== Dependency ===*/ +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" + + +/*=== Common definitions ===*/ +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif +#if defined (__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +#define LZ4_COMMONDEFS_ONLY +#ifndef LZ4_SRC_INCLUDED +#include "lz4.c" /* LZ4_count, constants, mem */ +#endif + + +/*=== Enums ===*/ +typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; + + +/*=== Constants ===*/ +#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) +#define LZ4_OPT_NUM (1<<12) + + +/*=== Macros ===*/ +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG)) +#define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */ +#define DELTANEXTU16(table, pos) table[(U16)(pos)] /* faster */ +/* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */ +#define UPDATABLE(ip, op, anchor) &ip, &op, &anchor + +#define LZ4HC_HASHSIZE 4 +static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); } + + +/************************************** +* HC Compression +**************************************/ +static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) +{ + MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable)); + MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); +} + +static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) +{ + size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart); + size_t newStartingOffset = bufferSize + hc4->dictLimit; + assert(newStartingOffset >= bufferSize); /* check overflow */ + if (newStartingOffset > 1 GB) { + LZ4HC_clearTables(hc4); + newStartingOffset = 0; + } + newStartingOffset += 64 KB; + hc4->nextToUpdate = (U32)newStartingOffset; + hc4->prefixStart = start; + hc4->end = start; + hc4->dictStart = start; + hc4->dictLimit = (U32)newStartingOffset; + hc4->lowLimit = (U32)newStartingOffset; +} + + +/* Update chains up to ip (excluded) */ +LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) +{ + U16* const chainTable = hc4->chainTable; + U32* const hashTable = hc4->hashTable; + const BYTE* const prefixPtr = hc4->prefixStart; + U32 const prefixIdx = hc4->dictLimit; + U32 const target = (U32)(ip - prefixPtr) + prefixIdx; + U32 idx = hc4->nextToUpdate; + assert(ip >= prefixPtr); + assert(target >= prefixIdx); + + while (idx < target) { + U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx); + size_t delta = idx - hashTable[h]; + if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX; + DELTANEXTU16(chainTable, idx) = (U16)delta; + hashTable[h] = idx; + idx++; + } + + hc4->nextToUpdate = target; +} + +/** LZ4HC_countBack() : + * @return : negative value, nb of common bytes before ip/match */ +LZ4_FORCE_INLINE +int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, + const BYTE* const iMin, const BYTE* const mMin) +{ + int back = 0; + int const min = (int)MAX(iMin - ip, mMin - match); + assert(min <= 0); + assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31)); + assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31)); + while ( (back > min) + && (ip[back-1] == match[back-1]) ) + back--; + return back; +} + +#if defined(_MSC_VER) +# define LZ4HC_rotl32(x,r) _rotl(x,r) +#else +# define LZ4HC_rotl32(x,r) ((x << r) | (x >> (32 - r))) +#endif + + +static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern) +{ + size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3; + if (bitsToRotate == 0) return pattern; + return LZ4HC_rotl32(pattern, (int)bitsToRotate); +} + +/* LZ4HC_countPattern() : + * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */ +static unsigned +LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) +{ + const BYTE* const iStart = ip; + reg_t const pattern = (sizeof(pattern)==8) ? + (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32; + + while (likely(ip < iEnd-(sizeof(pattern)-1))) { + reg_t const diff = LZ4_read_ARCH(ip) ^ pattern; + if (!diff) { ip+=sizeof(pattern); continue; } + ip += LZ4_NbCommonBytes(diff); + return (unsigned)(ip - iStart); + } + + if (LZ4_isLittleEndian()) { + reg_t patternByte = pattern; + while ((ip>= 8; + } + } else { /* big endian */ + U32 bitOffset = (sizeof(pattern)*8) - 8; + while (ip < iEnd) { + BYTE const byte = (BYTE)(pattern >> bitOffset); + if (*ip != byte) break; + ip ++; bitOffset -= 8; + } } + + return (unsigned)(ip - iStart); +} + +/* LZ4HC_reverseCountPattern() : + * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) + * read using natural platform endianness */ +static unsigned +LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) +{ + const BYTE* const iStart = ip; + + while (likely(ip >= iLow+4)) { + if (LZ4_read32(ip-4) != pattern) break; + ip -= 4; + } + { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */ + while (likely(ip>iLow)) { + if (ip[-1] != *bytePtr) break; + ip--; bytePtr--; + } } + return (unsigned)(iStart - ip); +} + +/* LZ4HC_protectDictEnd() : + * Checks if the match is in the last 3 bytes of the dictionary, so reading the + * 4 byte MINMATCH would overflow. + * @returns true if the match index is okay. + */ +static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex) +{ + return ((U32)((dictLimit - 1) - matchIndex) >= 3); +} + +typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e; +typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; + +LZ4_FORCE_INLINE int +LZ4HC_InsertAndGetWiderMatch ( + LZ4HC_CCtx_internal* const hc4, + const BYTE* const ip, + const BYTE* const iLowLimit, const BYTE* const iHighLimit, + int longest, + const BYTE** matchpos, + const BYTE** startpos, + const int maxNbAttempts, + const int patternAnalysis, const int chainSwap, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + U16* const chainTable = hc4->chainTable; + U32* const HashTable = hc4->hashTable; + const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx; + const BYTE* const prefixPtr = hc4->prefixStart; + const U32 prefixIdx = hc4->dictLimit; + const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; + const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex); + const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX; + const BYTE* const dictStart = hc4->dictStart; + const U32 dictIdx = hc4->lowLimit; + const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx; + int const lookBackLength = (int)(ip-iLowLimit); + int nbAttempts = maxNbAttempts; + U32 matchChainPos = 0; + U32 const pattern = LZ4_read32(ip); + U32 matchIndex; + repeat_state_e repeat = rep_untested; + size_t srcPatternLength = 0; + + DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch"); + /* First Match */ + LZ4HC_Insert(hc4, ip); + matchIndex = HashTable[LZ4HC_hashPtr(ip)]; + DEBUGLOG(7, "First match at index %u / %u (lowestMatchIndex)", + matchIndex, lowestMatchIndex); + + while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0)) { + int matchLength=0; + nbAttempts--; + assert(matchIndex < ipIndex); + if (favorDecSpeed && (ipIndex - matchIndex < 8)) { + /* do nothing */ + } else if (matchIndex >= prefixIdx) { /* within current Prefix */ + const BYTE* const matchPtr = prefixPtr + matchIndex - prefixIdx; + assert(matchPtr < ip); + assert(longest >= 1); + if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) { + if (LZ4_read32(matchPtr) == pattern) { + int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0; + matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit); + matchLength -= back; + if (matchLength > longest) { + longest = matchLength; + *matchpos = matchPtr + back; + *startpos = ip + back; + } } } + } else { /* lowestMatchIndex <= matchIndex < dictLimit */ + const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx); + assert(matchIndex >= dictIdx); + if ( likely(matchIndex <= prefixIdx - 4) + && (LZ4_read32(matchPtr) == pattern) ) { + int back = 0; + const BYTE* vLimit = ip + (prefixIdx - matchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) + matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit); + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0; + matchLength -= back; + if (matchLength > longest) { + longest = matchLength; + *matchpos = prefixPtr - prefixIdx + matchIndex + back; /* virtual pos, relative to ip, to retrieve offset */ + *startpos = ip + back; + } } } + + if (chainSwap && matchLength==longest) { /* better match => select a better chain */ + assert(lookBackLength==0); /* search forward only */ + if (matchIndex + (U32)longest <= ipIndex) { + int const kTrigger = 4; + U32 distanceToNextMatch = 1; + int const end = longest - MINMATCH + 1; + int step = 1; + int accel = 1 << kTrigger; + int pos; + for (pos = 0; pos < end; pos += step) { + U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos); + step = (accel++ >> kTrigger); + if (candidateDist > distanceToNextMatch) { + distanceToNextMatch = candidateDist; + matchChainPos = (U32)pos; + accel = 1 << kTrigger; + } } + if (distanceToNextMatch > 1) { + if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ + matchIndex -= distanceToNextMatch; + continue; + } } } + + { U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex); + if (patternAnalysis && distNextMatch==1 && matchChainPos==0) { + U32 const matchCandidateIdx = matchIndex-1; + /* may be a repeated pattern */ + if (repeat == rep_untested) { + if ( ((pattern & 0xFFFF) == (pattern >> 16)) + & ((pattern & 0xFF) == (pattern >> 24)) ) { + repeat = rep_confirmed; + srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern); + } else { + repeat = rep_not; + } } + if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex) + && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) { + const int extDict = matchCandidateIdx < prefixIdx; + const BYTE* const matchPtr = (extDict ? dictStart - dictIdx : prefixPtr - prefixIdx) + matchCandidateIdx; + if (LZ4_read32(matchPtr) == pattern) { /* good candidate */ + const BYTE* const iLimit = extDict ? dictEnd : iHighLimit; + size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern); + if (extDict && matchPtr + forwardPatternLength == iLimit) { + U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern); + forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern); + } + { const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr; + size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern); + size_t currentSegmentLength; + if (!extDict + && matchPtr - backLength == prefixPtr + && dictIdx < prefixIdx) { + U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern); + backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern); + } + /* Limit backLength not go further than lowestMatchIndex */ + backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex); + assert(matchCandidateIdx - backLength >= lowestMatchIndex); + currentSegmentLength = backLength + forwardPatternLength; + /* Adjust to end of pattern if the source pattern fits, otherwise the beginning of the pattern */ + if ( (currentSegmentLength >= srcPatternLength) /* current pattern segment large enough to contain full srcPatternLength */ + && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */ + U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength; /* best position, full pattern, might be followed by more match */ + if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) + matchIndex = newMatchIndex; + else { + /* Can only happen if started in the prefix */ + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; + } + } else { + U32 const newMatchIndex = matchCandidateIdx - (U32)backLength; /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */ + if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) { + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; + } else { + matchIndex = newMatchIndex; + if (lookBackLength==0) { /* no back possible */ + size_t const maxML = MIN(currentSegmentLength, srcPatternLength); + if ((size_t)longest < maxML) { + assert(prefixPtr - prefixIdx + matchIndex != ip); + if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break; + assert(maxML < 2 GB); + longest = (int)maxML; + *matchpos = prefixPtr - prefixIdx + matchIndex; /* virtual pos, relative to ip, to retrieve offset */ + *startpos = ip; + } + { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex); + if (distToNextPattern > matchIndex) break; /* avoid overflow */ + matchIndex -= distToNextPattern; + } } } } } + continue; + } } + } } /* PA optimization */ + + /* follow current chain */ + matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos); + + } /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */ + + if ( dict == usingDictCtxHc + && nbAttempts > 0 + && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) { + size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; + U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; + assert(dictEndOffset <= 1 GB); + matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset; + while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { + const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex; + + if (LZ4_read32(matchPtr) == pattern) { + int mlt; + int back = 0; + const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; + mlt -= back; + if (mlt > longest) { + longest = mlt; + *matchpos = prefixPtr - prefixIdx + matchIndex + back; + *startpos = ip + back; + } } + + { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex); + dictMatchIndex -= nextOffset; + matchIndex -= nextOffset; + } } } + + return longest; +} + +LZ4_FORCE_INLINE int +LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ + const BYTE* const ip, const BYTE* const iLimit, + const BYTE** matchpos, + const int maxNbAttempts, + const int patternAnalysis, + const dictCtx_directive dict) +{ + const BYTE* uselessPtr = ip; + /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), + * but this won't be the case here, as we define iLowLimit==ip, + * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ + return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, matchpos, &uselessPtr, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); +} + +/* LZ4HC_encodeSequence() : + * @return : 0 if ok, + * 1 if buffer issue detected */ +LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + int matchLength, + const BYTE* const match, + limitedOutput_directive limit, + BYTE* oend) +{ +#define ip (*_ip) +#define op (*_op) +#define anchor (*_anchor) + + size_t length; + BYTE* const token = op++; + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) + static const BYTE* start = NULL; + static U32 totalCost = 0; + U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start); + U32 const ll = (U32)(ip - anchor); + U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; + U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; + U32 const cost = 1 + llAdd + ll + 2 + mlAdd; + if (start==NULL) start = anchor; /* only works for single segment */ + /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */ + DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5u, cost:%4u + %5u", + pos, + (U32)(ip - anchor), matchLength, (U32)(ip-match), + cost, totalCost); + totalCost += cost; +#endif + + /* Encode Literal length */ + length = (size_t)(ip - anchor); + LZ4_STATIC_ASSERT(notLimited == 0); + /* Check output limit */ + if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) { + DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)", + (int)length, (int)(oend - op)); + return 1; + } + if (length >= RUN_MASK) { + size_t len = length - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for(; len >= 255 ; len -= 255) *op++ = 255; + *op++ = (BYTE)len; + } else { + *token = (BYTE)(length << ML_BITS); + } + + /* Copy Literals */ + LZ4_wildCopy8(op, anchor, op + length); + op += length; + + /* Encode Offset */ + assert( (ip - match) <= LZ4_DISTANCE_MAX ); /* note : consider providing offset as a value, rather than as a pointer difference */ + LZ4_writeLE16(op, (U16)(ip - match)); op += 2; + + /* Encode MatchLength */ + assert(matchLength >= MINMATCH); + length = (size_t)matchLength - MINMATCH; + if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) { + DEBUGLOG(6, "Not enough room to write match length"); + return 1; /* Check output limit */ + } + if (length >= ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; } + if (length >= 255) { length -= 255; *op++ = 255; } + *op++ = (BYTE)length; + } else { + *token += (BYTE)(length); + } + + /* Prepare next loop */ + ip += matchLength; + anchor = ip; + + return 0; +} +#undef ip +#undef op +#undef anchor + +LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( + LZ4HC_CCtx_internal* const ctx, + const char* const source, + char* const dest, + int* srcSizePtr, + int const maxOutputSize, + int maxNbAttempts, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + const int inputSize = *srcSizePtr; + const int patternAnalysis = (maxNbAttempts > 128); /* levels 9+ */ + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + + BYTE* optr = (BYTE*) dest; + BYTE* op = (BYTE*) dest; + BYTE* oend = op + maxOutputSize; + + int ml0, ml, ml2, ml3; + const BYTE* start0; + const BYTE* ref0; + const BYTE* ref = NULL; + const BYTE* start2 = NULL; + const BYTE* ref2 = NULL; + const BYTE* start3 = NULL; + const BYTE* ref3 = NULL; + + /* init */ + *srcSizePtr = 0; + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ + + /* Main Loop */ + while (ip <= mflimit) { + ml = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, &ref, maxNbAttempts, patternAnalysis, dict); + if (ml encode ML1 */ + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + continue; + } + + if (start0 < ip) { /* first match was skipped at least once */ + if (start2 < ip + ml0) { /* squeezing ML1 between ML0(original ML1) and ML2 */ + ip = start0; ref = ref0; ml = ml0; /* restore initial ML1 */ + } } + + /* Here, start0==ip */ + if ((start2 - ip) < 3) { /* First Match too small : removed */ + ml = ml2; + ip = start2; + ref =ref2; + goto _Search2; + } + +_Search3: + /* At this stage, we have : + * ml2 > ml1, and + * ip1+3 <= ip2 (usually < ip1+ml1) */ + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + int new_ml = ml; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */ + + if (start2 + ml2 <= mflimit) { + ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, + start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, + maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); + } else { + ml3 = ml2; + } + + if (ml3 == ml2) { /* No better match => encode ML1 and ML2 */ + /* ip & ref are known; Now for ml */ + if (start2 < ip+ml) ml = (int)(start2 - ip); + /* Now, encode 2 sequences */ + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + ip = start2; + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml2, ref2, limit, oend)) { + ml = ml2; + ref = ref2; + goto _dest_overflow; + } + continue; + } + + if (start3 < ip+ml+3) { /* Not enough space for match 2 : remove it */ + if (start3 >= (ip+ml)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */ + if (start2 < ip+ml) { + int correction = (int)(ip+ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + ip = start3; + ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _Search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _Search3; + } + + /* + * OK, now we have 3 ascending matches; + * let's write the first one ML1. + * ip & ref are known; Now decide ml. + */ + if (start2 < ip+ml) { + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; + if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } else { + ml = (int)(start2 - ip); + } + } + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + + /* ML2 becomes ML1 */ + ip = start2; ref = ref2; ml = ml2; + + /* ML3 becomes ML2 */ + start2 = start3; ref2 = ref3; ml2 = ml3; + + /* let's find a new ML3 */ + goto _Search3; + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) return 0; + /* adapt lastRunSize to fill 'dest' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + return (int) (((char*)op)-dest); + +_dest_overflow: + if (limit == fillOutput) { + /* Assumption : ip, anchor, ml and ref must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence overflowing"); + op = optr; /* restore correct out pointer */ + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); assert(ml >= 0); + if ((size_t)ml > maxMlSize) ml = (int)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ml >= MFLIMIT) { + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, notLimited, oend); + } } + goto _last_literals; + } + /* compression failed */ + return 0; +} + + +static int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx, + const char* const source, char* dst, + int* srcSizePtr, int dstCapacity, + int const nbSearches, size_t sufficient_len, + const limitedOutput_directive limit, int const fullUpdate, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed); + + +LZ4_FORCE_INLINE int LZ4HC_compress_generic_internal ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + typedef enum { lz4hc, lz4opt } lz4hc_strat_e; + typedef struct { + lz4hc_strat_e strat; + int nbSearches; + U32 targetLength; + } cParams_t; + static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = { + { lz4hc, 2, 16 }, /* 0, unused */ + { lz4hc, 2, 16 }, /* 1, unused */ + { lz4hc, 2, 16 }, /* 2, unused */ + { lz4hc, 4, 16 }, /* 3 */ + { lz4hc, 8, 16 }, /* 4 */ + { lz4hc, 16, 16 }, /* 5 */ + { lz4hc, 32, 16 }, /* 6 */ + { lz4hc, 64, 16 }, /* 7 */ + { lz4hc, 128, 16 }, /* 8 */ + { lz4hc, 256, 16 }, /* 9 */ + { lz4opt, 96, 64 }, /*10==LZ4HC_CLEVEL_OPT_MIN*/ + { lz4opt, 512,128 }, /*11 */ + { lz4opt,16384,LZ4_OPT_NUM }, /* 12==LZ4HC_CLEVEL_MAX */ + }; + + DEBUGLOG(4, "LZ4HC_compress_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)", + ctx, src, *srcSizePtr, limit); + + if (limit == fillOutput && dstCapacity < 1) return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size (too large or negative) */ + + ctx->end += *srcSizePtr; + if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT; /* note : convention is different from lz4frame, maybe something to review */ + cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel); + { cParams_t const cParam = clTable[cLevel]; + HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio; + int result; + + if (cParam.strat == lz4hc) { + result = LZ4HC_compress_hashChain(ctx, + src, dst, srcSizePtr, dstCapacity, + cParam.nbSearches, limit, dict); + } else { + assert(cParam.strat == lz4opt); + result = LZ4HC_compress_optimal(ctx, + src, dst, srcSizePtr, dstCapacity, + cParam.nbSearches, cParam.targetLength, limit, + cLevel == LZ4HC_CLEVEL_MAX, /* ultra mode */ + dict, favor); + } + if (result <= 0) ctx->dirty = 1; + return result; + } +} + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock); + +static int +LZ4HC_compress_generic_noDictCtx ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + assert(ctx->dictCtx == NULL); + return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx); +} + +static int +LZ4HC_compress_generic_dictCtx ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit); + assert(ctx->dictCtx != NULL); + if (position >= 64 KB) { + ctx->dictCtx = NULL; + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else if (position == 0 && *srcSizePtr > 4 KB) { + LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal)); + LZ4HC_setExternalDict(ctx, (const BYTE *)src); + ctx->compressionLevel = (short)cLevel; + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else { + return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc); + } +} + +static int +LZ4HC_compress_generic ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + if (ctx->dictCtx == NULL) { + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else { + return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } +} + + +int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } + +static size_t LZ4_streamHC_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_streamHC_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_streamHC_t); +#else + return 1; /* effectively disabled */ +#endif +} + +/* state is presumed correctly initialized, + * in which case its size and alignment have already been validate */ +int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; + if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0; + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); + LZ4HC_init_internal (ctx, (const BYTE*)src); + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput); + else + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited); +} + +int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); + if (ctx==NULL) return 0; /* init failure */ + return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel); +} + +int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + int cSize; +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t)); + if (statePtr==NULL) return 0; +#else + LZ4_streamHC_t state; + LZ4_streamHC_t* const statePtr = &state; +#endif + cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + FREEMEM(statePtr); +#endif + return cSize; +} + +/* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */ +int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel) +{ + LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); + if (ctx==NULL) return 0; /* init failure */ + LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source); + LZ4_setCompressionLevel(ctx, cLevel); + return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput); +} + + + +/************************************** +* Streaming Functions +**************************************/ +/* allocation */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_streamHC_t* LZ4_createStreamHC(void) +{ + LZ4_streamHC_t* const state = + (LZ4_streamHC_t*)ALLOC_AND_ZERO(sizeof(LZ4_streamHC_t)); + if (state == NULL) return NULL; + LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT); + return state; +} + +int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) +{ + DEBUGLOG(4, "LZ4_freeStreamHC(%p)", LZ4_streamHCPtr); + if (!LZ4_streamHCPtr) return 0; /* support free on NULL */ + FREEMEM(LZ4_streamHCPtr); + return 0; +} +#endif + + +LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) +{ + LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; + DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size); + /* check conditions */ + if (buffer == NULL) return NULL; + if (size < sizeof(LZ4_streamHC_t)) return NULL; + if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL; + /* init */ + { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse); + MEM_INIT(hcstate, 0, sizeof(*hcstate)); } + LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT); + return LZ4_streamHCPtr; +} + +/* just a stub */ +void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); +} + +void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + LZ4HC_CCtx_internal* const s = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(4, "LZ4_resetStreamHC_fast(%p, %d)", LZ4_streamHCPtr, compressionLevel); + if (s->dirty) { + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + } else { + assert(s->end >= s->prefixStart); + s->dictLimit += (U32)(s->end - s->prefixStart); + s->prefixStart = NULL; + s->end = NULL; + s->dictCtx = NULL; + } + LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); +} + +void LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + DEBUGLOG(5, "LZ4_setCompressionLevel(%p, %d)", LZ4_streamHCPtr, compressionLevel); + if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT; + if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX; + LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel; +} + +void LZ4_favorDecompressionSpeed(LZ4_streamHC_t* LZ4_streamHCPtr, int favor) +{ + LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = (favor!=0); +} + +/* LZ4_loadDictHC() : + * LZ4_streamHCPtr is presumed properly initialized */ +int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* dictionary, int dictSize) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(4, "LZ4_loadDictHC(ctx:%p, dict:%p, dictSize:%d)", LZ4_streamHCPtr, dictionary, dictSize); + assert(LZ4_streamHCPtr != NULL); + if (dictSize > 64 KB) { + dictionary += (size_t)dictSize - 64 KB; + dictSize = 64 KB; + } + /* need a full initialization, there are bad side-effects when using resetFast() */ + { int const cLevel = ctxPtr->compressionLevel; + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + LZ4_setCompressionLevel(LZ4_streamHCPtr, cLevel); + } + LZ4HC_init_internal (ctxPtr, (const BYTE*)dictionary); + ctxPtr->end = (const BYTE*)dictionary + dictSize; + if (dictSize >= LZ4HC_HASHSIZE) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); + return dictSize; +} + +void LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream) { + working_stream->internal_donotuse.dictCtx = dictionary_stream != NULL ? &(dictionary_stream->internal_donotuse) : NULL; +} + +/* compression */ + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) +{ + DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock); + if (ctxPtr->end >= ctxPtr->prefixStart + 4) + LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ + + /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart); + ctxPtr->prefixStart = newBlock; + ctxPtr->end = newBlock; + ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ + + /* cannot reference an extDict and a dictCtx at the same time */ + ctxPtr->dictCtx = NULL; +} + +static int +LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int dstCapacity, + limitedOutput_directive limit) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(5, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)", + LZ4_streamHCPtr, src, *srcSizePtr, limit); + assert(ctxPtr != NULL); + /* auto-init if forgotten */ + if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src); + + /* Check overflow */ + if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) { + size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart); + if (dictSize > 64 KB) dictSize = 64 KB; + LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize); + } + + /* Check if blocks follow each other */ + if ((const BYTE*)src != ctxPtr->end) + LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src); + + /* Check overlapping input/dictionary space */ + { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr; + const BYTE* const dictBegin = ctxPtr->dictStart; + const BYTE* const dictEnd = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit); + if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) { + if (sourceEnd > dictEnd) sourceEnd = dictEnd; + ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart); + ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart); + /* invalidate dictionary is it's too small */ + if (ctxPtr->dictLimit - ctxPtr->lowLimit < LZ4HC_HASHSIZE) { + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + } } } + + return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); +} + +int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity) +{ + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput); + else + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, notLimited); +} + +int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize) +{ + return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput); +} + + + +/* LZ4_saveDictHC : + * save history content + * into a user-provided buffer + * which is then used to continue compression + */ +int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) +{ + LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; + int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart); + DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); + assert(prefixSize >= 0); + if (dictSize > 64 KB) dictSize = 64 KB; + if (dictSize < 4) dictSize = 0; + if (dictSize > prefixSize) dictSize = prefixSize; + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) + LZ4_memmove(safeBuffer, streamPtr->end - dictSize, (size_t)dictSize); + { U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit; + streamPtr->end = (safeBuffer == NULL) ? NULL : (const BYTE*)safeBuffer + dictSize; + streamPtr->prefixStart = (const BYTE*)safeBuffer; + streamPtr->dictLimit = endIndex - (U32)dictSize; + streamPtr->lowLimit = endIndex - (U32)dictSize; + streamPtr->dictStart = streamPtr->prefixStart; + if (streamPtr->nextToUpdate < streamPtr->dictLimit) + streamPtr->nextToUpdate = streamPtr->dictLimit; + } + return dictSize; +} + + +/*************************************************** +* Deprecated Functions +***************************************************/ + +/* These functions currently generate deprecation warnings */ + +/* Wrappers for deprecated compression functions */ +int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); } +int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); } + + +/* Deprecated streaming functions */ +int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); } + +/* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t) + * @return : 0 on success, !=0 if error */ +int LZ4_resetStreamStateHC(void* state, char* inputBuffer) +{ + LZ4_streamHC_t* const hc4 = LZ4_initStreamHC(state, sizeof(*hc4)); + if (hc4 == NULL) return 1; /* init failed */ + LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + return 0; +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +void* LZ4_createHC (const char* inputBuffer) +{ + LZ4_streamHC_t* const hc4 = LZ4_createStreamHC(); + if (hc4 == NULL) return NULL; /* not enough memory */ + LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + return hc4; +} + +int LZ4_freeHC (void* LZ4HC_Data) +{ + if (!LZ4HC_Data) return 0; /* support free on NULL */ + FREEMEM(LZ4HC_Data); + return 0; +} +#endif + +int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, notLimited); +} + +int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput); +} + +char* LZ4_slideInputBufferHC(void* LZ4HC_Data) +{ + LZ4HC_CCtx_internal* const s = &((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse; + const BYTE* const bufferStart = s->prefixStart - s->dictLimit + s->lowLimit; + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)LZ4HC_Data, s->compressionLevel); + /* ugly conversion trick, required to evade (const char*) -> (char*) cast-qual warning :( */ + return (char*)(uptrval)bufferStart; +} + + +/* ================================================ + * LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX]) + * ===============================================*/ +typedef struct { + int price; + int off; + int mlen; + int litlen; +} LZ4HC_optimal_t; + +/* price in bytes */ +LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen) +{ + int price = litlen; + assert(litlen >= 0); + if (litlen >= (int)RUN_MASK) + price += 1 + ((litlen-(int)RUN_MASK) / 255); + return price; +} + + +/* requires mlen >= MINMATCH */ +LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen) +{ + int price = 1 + 2 ; /* token + 16-bit offset */ + assert(litlen >= 0); + assert(mlen >= MINMATCH); + + price += LZ4HC_literalsPrice(litlen); + + if (mlen >= (int)(ML_MASK+MINMATCH)) + price += 1 + ((mlen-(int)(ML_MASK+MINMATCH)) / 255); + + return price; +} + + +typedef struct { + int off; + int len; +} LZ4HC_match_t; + +LZ4_FORCE_INLINE LZ4HC_match_t +LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx, + const BYTE* ip, const BYTE* const iHighLimit, + int minLen, int nbSearches, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + LZ4HC_match_t match = { 0 , 0 }; + const BYTE* matchPtr = NULL; + /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), + * but this won't be the case here, as we define iLowLimit==ip, + * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ + int matchLength = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, &matchPtr, &ip, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); + if (matchLength <= minLen) return match; + if (favorDecSpeed) { + if ((matchLength>18) & (matchLength<=36)) matchLength=18; /* favor shortcut */ + } + match.len = matchLength; + match.off = (int)(ip-matchPtr); + return match; +} + + +static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, + const char* const source, + char* dst, + int* srcSizePtr, + int dstCapacity, + int const nbSearches, + size_t sufficient_len, + const limitedOutput_directive limit, + int const fullUpdate, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + int retval = 0; +#define TRAILING_LITERALS 3 +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS)); +#else + LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS]; /* ~64 KB, which is a bit large for stack... */ +#endif + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + BYTE* op = (BYTE*) dst; + BYTE* opSaved = (BYTE*) dst; + BYTE* oend = op + dstCapacity; + int ovml = MINMATCH; /* overflow - last sequence */ + const BYTE* ovref = NULL; + + /* init */ +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + if (opt == NULL) goto _return_label; +#endif + DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity); + *srcSizePtr = 0; + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1; + + /* Main Loop */ + while (ip <= mflimit) { + int const llen = (int)(ip - anchor); + int best_mlen, best_off; + int cur, last_match_pos = 0; + + LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); + if (firstMatch.len==0) { ip++; continue; } + + if ((size_t)firstMatch.len > sufficient_len) { + /* good enough solution : immediate encoding */ + int const firstML = firstMatch.len; + const BYTE* const matchPos = ip - firstMatch.off; + opSaved = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, matchPos, limit, oend) ) { /* updates ip, op and anchor */ + ovml = firstML; + ovref = matchPos; + goto _dest_overflow; + } + continue; + } + + /* set prices for first positions (literals) */ + { int rPos; + for (rPos = 0 ; rPos < MINMATCH ; rPos++) { + int const cost = LZ4HC_literalsPrice(llen + rPos); + opt[rPos].mlen = 1; + opt[rPos].off = 0; + opt[rPos].litlen = llen + rPos; + opt[rPos].price = cost; + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", + rPos, cost, opt[rPos].litlen); + } } + /* set prices using initial match */ + { int mlen = MINMATCH; + int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ + int const offset = firstMatch.off; + assert(matchML < LZ4_OPT_NUM); + for ( ; mlen <= matchML ; mlen++) { + int const cost = LZ4HC_sequencePrice(llen, mlen); + opt[mlen].mlen = mlen; + opt[mlen].off = offset; + opt[mlen].litlen = llen; + opt[mlen].price = cost; + DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i) -- initial setup", + mlen, cost, mlen); + } } + last_match_pos = firstMatch.len; + { int addLit; + for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { + opt[last_match_pos+addLit].mlen = 1; /* literal */ + opt[last_match_pos+addLit].off = 0; + opt[last_match_pos+addLit].litlen = addLit; + opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", + last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); + } } + + /* check further positions */ + for (cur = 1; cur < last_match_pos; cur++) { + const BYTE* const curPtr = ip + cur; + LZ4HC_match_t newMatch; + + if (curPtr > mflimit) break; + DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u", + cur, opt[cur].price, opt[cur+1].price, cur+1); + if (fullUpdate) { + /* not useful to search here if next position has same (or lower) cost */ + if ( (opt[cur+1].price <= opt[cur].price) + /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */ + && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) ) + continue; + } else { + /* not useful to search here if next position has same (or lower) cost */ + if (opt[cur+1].price <= opt[cur].price) continue; + } + + DEBUGLOG(7, "search at rPos:%u", cur); + if (fullUpdate) + newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); + else + /* only test matches of minimum length; slightly faster, but misses a few bytes */ + newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches, dict, favorDecSpeed); + if (!newMatch.len) continue; + + if ( ((size_t)newMatch.len > sufficient_len) + || (newMatch.len + cur >= LZ4_OPT_NUM) ) { + /* immediate encoding */ + best_mlen = newMatch.len; + best_off = newMatch.off; + last_match_pos = cur + 1; + goto encode; + } + + /* before match : set price with literals at beginning */ + { int const baseLitlen = opt[cur].litlen; + int litlen; + for (litlen = 1; litlen < MINMATCH; litlen++) { + int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen); + int const pos = cur + litlen; + if (price < opt[pos].price) { + opt[pos].mlen = 1; /* literal */ + opt[pos].off = 0; + opt[pos].litlen = baseLitlen+litlen; + opt[pos].price = price; + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", + pos, price, opt[pos].litlen); + } } } + + /* set prices using match at position = cur */ + { int const matchML = newMatch.len; + int ml = MINMATCH; + + assert(cur + newMatch.len < LZ4_OPT_NUM); + for ( ; ml <= matchML ; ml++) { + int const pos = cur + ml; + int const offset = newMatch.off; + int price; + int ll; + DEBUGLOG(7, "testing price rPos %i (last_match_pos=%i)", + pos, last_match_pos); + if (opt[cur].mlen == 1) { + ll = opt[cur].litlen; + price = ((cur > ll) ? opt[cur - ll].price : 0) + + LZ4HC_sequencePrice(ll, ml); + } else { + ll = 0; + price = opt[cur].price + LZ4HC_sequencePrice(0, ml); + } + + assert((U32)favorDecSpeed <= 1); + if (pos > last_match_pos+TRAILING_LITERALS + || price <= opt[pos].price - (int)favorDecSpeed) { + DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)", + pos, price, ml); + assert(pos < LZ4_OPT_NUM); + if ( (ml == matchML) /* last pos of last match */ + && (last_match_pos < pos) ) + last_match_pos = pos; + opt[pos].mlen = ml; + opt[pos].off = offset; + opt[pos].litlen = ll; + opt[pos].price = price; + } } } + /* complete following positions with literals */ + { int addLit; + for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { + opt[last_match_pos+addLit].mlen = 1; /* literal */ + opt[last_match_pos+addLit].off = 0; + opt[last_match_pos+addLit].litlen = addLit; + opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); + } } + } /* for (cur = 1; cur <= last_match_pos; cur++) */ + + assert(last_match_pos < LZ4_OPT_NUM + TRAILING_LITERALS); + best_mlen = opt[last_match_pos].mlen; + best_off = opt[last_match_pos].off; + cur = last_match_pos - best_mlen; + +encode: /* cur, last_match_pos, best_mlen, best_off must be set */ + assert(cur < LZ4_OPT_NUM); + assert(last_match_pos >= 1); /* == 1 when only one candidate */ + DEBUGLOG(6, "reverse traversal, looking for shortest path (last_match_pos=%i)", last_match_pos); + { int candidate_pos = cur; + int selected_matchLength = best_mlen; + int selected_offset = best_off; + while (1) { /* from end to beginning */ + int const next_matchLength = opt[candidate_pos].mlen; /* can be 1, means literal */ + int const next_offset = opt[candidate_pos].off; + DEBUGLOG(7, "pos %i: sequence length %i", candidate_pos, selected_matchLength); + opt[candidate_pos].mlen = selected_matchLength; + opt[candidate_pos].off = selected_offset; + selected_matchLength = next_matchLength; + selected_offset = next_offset; + if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */ + assert(next_matchLength > 0); /* can be 1, means literal */ + candidate_pos -= next_matchLength; + } } + + /* encode all recorded sequences in order */ + { int rPos = 0; /* relative position (to ip) */ + while (rPos < last_match_pos) { + int const ml = opt[rPos].mlen; + int const offset = opt[rPos].off; + if (ml == 1) { ip++; rPos++; continue; } /* literal; note: can end up with several literals, in which case, skip them */ + rPos += ml; + assert(ml >= MINMATCH); + assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX)); + opSaved = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ip - offset, limit, oend) ) { /* updates ip, op and anchor */ + ovml = ml; + ovref = ip - offset; + goto _dest_overflow; + } } } + } /* while (ip <= mflimit) */ + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) { /* Check output limit */ + retval = 0; + goto _return_label; + } + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + retval = (int) ((char*)op-dst); + goto _return_label; + +_dest_overflow: +if (limit == fillOutput) { + /* Assumption : ip, anchor, ovml and ovref must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence overflowing (only %i bytes remaining)", (int)(oend-1-opSaved)); + op = opSaved; /* restore correct out pointer */ + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); assert(ovml >= 0); + if ((size_t)ovml > maxMlSize) ovml = (int)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ovml >= MFLIMIT) { + DEBUGLOG(6, "Space to end : %i + ml (%i)", (int)((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1), ovml); + DEBUGLOG(6, "Before : ip = %p, anchor = %p", ip, anchor); + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ovml, ovref, notLimited, oend); + DEBUGLOG(6, "After : ip = %p, anchor = %p", ip, anchor); + } } + goto _last_literals; +} +_return_label: +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + FREEMEM(opt); +#endif + return retval; +} diff --git a/CompressSave/LZ4WrapC/lz4/lz4hc.h b/CompressSave/LZ4WrapC/lz4/lz4hc.h new file mode 100644 index 0000000..e937acf --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/lz4hc.h @@ -0,0 +1,413 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Header File + Copyright (C) 2011-2020, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#ifndef LZ4_HC_H_19834876238432 +#define LZ4_HC_H_19834876238432 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* --- Dependency --- */ +/* note : lz4hc requires lz4.h/lz4.c for compilation */ +#include "lz4.h" /* stddef, LZ4LIB_API, LZ4_DEPRECATED */ + + +/* --- Useful constants --- */ +#define LZ4HC_CLEVEL_MIN 3 +#define LZ4HC_CLEVEL_DEFAULT 9 +#define LZ4HC_CLEVEL_OPT_MIN 10 +#define LZ4HC_CLEVEL_MAX 12 + + +/*-************************************ + * Block Compression + **************************************/ +/*! LZ4_compress_HC() : + * Compress data from `src` into `dst`, using the powerful but slower "HC" algorithm. + * `dst` must be already allocated. + * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h") + * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h") + * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work. + * Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX. + * @return : the number of bytes written into 'dst' + * or 0 if compression fails. + */ +LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel); + + +/* Note : + * Decompression functions are provided within "lz4.h" (BSD license) + */ + + +/*! LZ4_compress_HC_extStateHC() : + * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`. + * `state` size is provided by LZ4_sizeofStateHC(). + * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly). + */ +LZ4LIB_API int LZ4_sizeofStateHC(void); +LZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel); + + +/*! LZ4_compress_HC_destSize() : v1.9.0+ + * Will compress as much data as possible from `src` + * to fit into `targetDstSize` budget. + * Result is provided in 2 parts : + * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) + * or 0 if compression fails. + * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src` + */ +LZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize, + int compressionLevel); + + +/*-************************************ + * Streaming Compression + * Bufferless synchronous API + **************************************/ + typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */ + +/*! LZ4_createStreamHC() and LZ4_freeStreamHC() : + * These functions create and release memory for LZ4 HC streaming state. + * Newly created states are automatically initialized. + * A same state can be used multiple times consecutively, + * starting with LZ4_resetStreamHC_fast() to start a new stream of blocks. + */ +LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void); +LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr); + +/* + These functions compress data in successive blocks of any size, + using previous blocks as dictionary, to improve compression ratio. + One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks. + There is an exception for ring buffers, which can be smaller than 64 KB. + Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue(). + + Before starting compression, state must be allocated and properly initialized. + LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT. + + Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream) + or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental). + LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once, + which is automatically the case when state is created using LZ4_createStreamHC(). + + After reset, a first "fictional block" can be designated as initial dictionary, + using LZ4_loadDictHC() (Optional). + + Invoke LZ4_compress_HC_continue() to compress each successive block. + The number of blocks is unlimited. + Previous input blocks, including initial dictionary when present, + must remain accessible and unmodified during compression. + + It's allowed to update compression level anytime between blocks, + using LZ4_setCompressionLevel() (experimental). + + 'dst' buffer should be sized to handle worst case scenarios + (see LZ4_compressBound(), it ensures compression success). + In case of failure, the API does not guarantee recovery, + so the state _must_ be reset. + To ensure compression success + whenever `dst` buffer size cannot be made >= LZ4_compressBound(), + consider using LZ4_compress_HC_continue_destSize(). + + Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks, + it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC(). + Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB) + + After completing a streaming compression, + it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state, + just by resetting it, using LZ4_resetStreamHC_fast(). +*/ + +LZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel); /* v1.9.0+ */ +LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize); + +LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, + const char* src, char* dst, + int srcSize, int maxDstSize); + +/*! LZ4_compress_HC_continue_destSize() : v1.9.0+ + * Similar to LZ4_compress_HC_continue(), + * but will read as much data as possible from `src` + * to fit into `targetDstSize` budget. + * Result is provided into 2 parts : + * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) + * or 0 if compression fails. + * `srcSizePtr` : on success, *srcSizePtr will be updated to indicate how much bytes were read from `src`. + * Note that this function may not consume the entire input. + */ +LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize); + +LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize); + + + +/*^********************************************** + * !!!!!! STATIC LINKING ONLY !!!!!! + ***********************************************/ + +/*-****************************************************************** + * PRIVATE DEFINITIONS : + * Do not use these definitions directly. + * They are merely exposed to allow static allocation of `LZ4_streamHC_t`. + * Declare an `LZ4_streamHC_t` directly, rather than any type below. + * Even then, only do so in the context of static linking, as definitions may change between versions. + ********************************************************************/ + +#define LZ4HC_DICTIONARY_LOGSIZE 16 +#define LZ4HC_MAXD (1<= LZ4HC_CLEVEL_OPT_MIN. + */ +LZ4LIB_STATIC_API void LZ4_favorDecompressionSpeed( + LZ4_streamHC_t* LZ4_streamHCPtr, int favor); + +/*! LZ4_resetStreamHC_fast() : v1.9.0+ + * When an LZ4_streamHC_t is known to be in a internally coherent state, + * it can often be prepared for a new compression with almost no work, only + * sometimes falling back to the full, expensive reset that is always required + * when the stream is in an indeterminate state (i.e., the reset performed by + * LZ4_resetStreamHC()). + * + * LZ4_streamHCs are guaranteed to be in a valid state when: + * - returned from LZ4_createStreamHC() + * - reset by LZ4_resetStreamHC() + * - memset(stream, 0, sizeof(LZ4_streamHC_t)) + * - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast() + * - the stream was in a valid state and was then used in any compression call + * that returned success + * - the stream was in an indeterminate state and was used in a compression + * call that fully reset the state (LZ4_compress_HC_extStateHC()) and that + * returned success + * + * Note: + * A stream that was last used in a compression call that returned an error + * may be passed to this function. However, it will be fully reset, which will + * clear any existing history and settings from the context. + */ +LZ4LIB_STATIC_API void LZ4_resetStreamHC_fast( + LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); + +/*! LZ4_compress_HC_extStateHC_fastReset() : + * A variant of LZ4_compress_HC_extStateHC(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStreamHC_fast() for a definition of + * "correctly initialized"). From a high level, the difference is that this + * function initializes the provided state with a call to + * LZ4_resetStreamHC_fast() while LZ4_compress_HC_extStateHC() starts with a + * call to LZ4_resetStreamHC(). + */ +LZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset ( + void* state, + const char* src, char* dst, + int srcSize, int dstCapacity, + int compressionLevel); + +/*! LZ4_attach_HC_dictionary() : + * This is an experimental API that allows for the efficient use of a + * static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a + * working LZ4_streamHC_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDictHC() should + * be expected to work. + * + * Alternatively, the provided dictionary stream pointer may be NULL, in which + * case any existing dictionary stream is unset. + * + * A dictionary should only be attached to a stream without any history (i.e., + * a stream that has just been reset). + * + * The dictionary will remain attached to the working stream only for the + * current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the + * dictionary context association from the working stream. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the lifetime of the stream session. + */ +LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary( + LZ4_streamHC_t *working_stream, + const LZ4_streamHC_t *dictionary_stream); + +#if defined (__cplusplus) +} +#endif + +#endif /* LZ4_HC_SLO_098092834 */ +#endif /* LZ4_HC_STATIC_LINKING_ONLY */ diff --git a/CompressSave/LZ4WrapC/lz4/xxhash.c b/CompressSave/LZ4WrapC/lz4/xxhash.c new file mode 100644 index 0000000..ff28749 --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/xxhash.c @@ -0,0 +1,1030 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include /* assert */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +static int XXH_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +/* mix all bits */ +static U32 XXH32_avalanche(U32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +static U32 +XXH32_finalize(U32 h32, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) + +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1 \ + h32 += (*p++) * PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(p) * PRIME32_3; \ + p+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + switch(len&15) /* or switch(bEnd - p) */ + { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + assert(0); + return h32; /* reaching this point is deemed impossible */ +} + + +FORCE_INLINE U32 +XXH32_endian_align(const void* input, size_t len, U32 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 15; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32)len; + + return XXH32_finalize(h32, p, len&15, endian, align); +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE XXH_errorcode +XXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + +FORCE_INLINE U32 +XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned); +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static U64 XXH64_avalanche(U64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +static U64 +XXH64_finalize(U64 h64, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1_64 \ + h64 ^= (*p++) * PRIME64_5; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \ + p+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \ + p+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + switch(len&31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + + /* impossible to reach */ + assert(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +FORCE_INLINE U64 +XXH64_endian_align(const void* input, size_t len, U64 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + return XXH64_finalize(h64, p, len, endian, align); +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE XXH_errorcode +XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (U64) state->total_len; + + return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned); +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/CompressSave/LZ4WrapC/lz4/xxhash.h b/CompressSave/LZ4WrapC/lz4/xxhash.h new file mode 100644 index 0000000..d6bad94 --- /dev/null +++ b/CompressSave/LZ4WrapC/lz4/xxhash.h @@ -0,0 +1,328 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This is useful to include xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining can offer dramatic performance improvement on small keys. + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate module. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 5 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* + * Streaming functions generate the xxHash of an input provided in multiple segments. + * Note that, for small input, they are slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hashes later on, by calling again XXH*_digest(). + * + * When done, free XXH state space if it was allocated dynamically. + */ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. + * The canonical representation uses human-readable write convention, aka big-endian (large digits first). + * These functions allow transformation of hash result into and from its canonical format. + * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. + */ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + +struct XXH32_state_s { + uint32_t total_len_32; + uint32_t large_len; + uint32_t v1; + uint32_t v2; + uint32_t v3; + uint32_t v4; + uint32_t mem32[4]; + uint32_t memsize; + uint32_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +struct XXH64_state_s { + uint64_t total_len; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t v4; + uint64_t mem64[4]; + uint32_t memsize; + uint32_t reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ + +# else + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; + unsigned memsize; + unsigned reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +# ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; + unsigned memsize; + unsigned reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +# endif + +# endif + + +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/CompressSave/PatchUILoadGame.cs b/CompressSave/PatchUILoadGame.cs new file mode 100644 index 0000000..0402235 --- /dev/null +++ b/CompressSave/PatchUILoadGame.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using UnityEngine; +using UnityEngine.UI; + +namespace CompressSave; + +class PatchUILoadGame +{ + static UIButton decompressButton; + + [HarmonyTranspiler] + [HarmonyPatch(typeof(UISaveGameWindow), "OnSelectedChange")] + [HarmonyPatch(typeof(UILoadGameWindow), "OnSelectedChange")] + static IEnumerable OnSelectedChange_Transpiler(IEnumerable instructions, ILGenerator generator) + { + var ReadHeader = typeof(GameSave).GetMethod("ReadHeaderAndDescAndProperty", BindingFlags.Static | BindingFlags.Public); + if (ReadHeader == null) return instructions; + var codes = new List(instructions); + for (int i = 0; i < codes.Count; i++) + { + var code = codes[i]; + if (code.opcode == OpCodes.Ldstr && code.OperandIs("#,##0")) + { + var iffalse = generator.DefineLabel(); + var callLabel = generator.DefineLabel(); + code.WithLabels(iffalse) + .operand = "(N)#,##0"; + codes[i + 1].WithLabels(callLabel); + var IL = new List { + new CodeInstruction(OpCodes.Ldloc_0), + new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(CompressionGameSaveHeader),"IsCompressed")), + new CodeInstruction(OpCodes.Brfalse_S,iffalse), + new CodeInstruction(OpCodes.Ldstr,"(LZ4)#,##0"), + new CodeInstruction(OpCodes.Br_S,callLabel), + }; + codes.InsertRange(i, IL); + break; + } + } + + return codes.AsEnumerable(); + } + + [HarmonyPatch(typeof(UILoadGameWindow), "OnSelectedChange"), HarmonyPostfix] + static void OnSelectedChange(UILoadGameWindow __instance, UIButton ___loadButton, Text ___prop3Text) + { + bool compressedSave = (___prop3Text != null &&___prop3Text.text.Contains("LZ4")) || (___loadButton.button.interactable == false && SaveUtil.IsCompressedSave(__instance.selected?.saveName)); + if (decompressButton) + decompressButton.button.interactable = compressedSave; + } + + [HarmonyPatch(typeof(UILoadGameWindow), "_OnOpen"), HarmonyPostfix] + static void _OnOpen(UILoadGameWindow __instance, UIButton ___loadButton, GameObject ___loadSandboxGroup, List ___entries) + { + if (!decompressButton) + { + decompressButton = ___loadButton; + + decompressButton = (__instance.transform.Find("button-decompress")?.gameObject ?? GameObject.Instantiate(___loadButton.gameObject, ___loadButton.transform.parent)).GetComponent(); + + ___loadSandboxGroup.transform.Translate(new Vector3(-2.5f, 0, 0)); + decompressButton.gameObject.name = "button-decompress"; + decompressButton.transform.Translate(new Vector3(-2.0f, 0, 0)); + decompressButton.button.image.color = new Color32(0, 0xf4, 0x92, 0x77); + var localizer = decompressButton.transform.Find("button-text")?.GetComponent(); + var text = decompressButton.transform.Find("button-text")?.GetComponent(); + + if (localizer) + { + localizer.stringKey = "Decompress".Translate(); + localizer.translation = "Decompress".Translate(); + } + if (text) + text.text = "Decompress".Translate(); + + decompressButton.onClick += _ =>{ + if(SaveUtil.DecompressSave(__instance.selected.saveName, out var newfileName)) + { + __instance.RefreshList(); + __instance.selected = ___entries.First(e => e.saveName == newfileName); + } + }; + decompressButton.button.interactable = false; + } + } + + public static void OnDestroy() + { + if (decompressButton) + GameObject.Destroy(decompressButton.gameObject); + decompressButton = null; + } +} \ No newline at end of file diff --git a/CompressSave/PatchUISaveGame.cs b/CompressSave/PatchUISaveGame.cs new file mode 100644 index 0000000..d6aa0e3 --- /dev/null +++ b/CompressSave/PatchUISaveGame.cs @@ -0,0 +1,112 @@ +using HarmonyLib; +using UnityEngine; +using UnityEngine.UI; + +namespace CompressSave; + +static class PatchUISaveGame +{ + [HarmonyPatch(typeof(UISaveGameWindow), "_OnDestroy"), HarmonyPostfix] + static void _OnDestroy() + { + //Console.WriteLine("OnCreate"); + context = new UIContext(); + } + //[HarmonyPatch(typeof(UISaveGameWindow), "_OnRegEvent"), HarmonyPostfix] + //static void _OnRegEvent() + //{ + // Console.WriteLine("OnRegEvent"); + //} + //[HarmonyPatch(typeof(UISaveGameWindow), "_OnUnregEvent"), HarmonyPostfix] + //static void _OnUnregEvent() + //{ + // Console.WriteLine("OnUnregEvent"); + //} + + static void Test() + { + GameSave.ReadHeaderAndDescAndProperty("aa", true, out var header, out var desc, out var property); + if (header != null) + if (header is CompressionGameSaveHeader) + "b".Translate(); + else + "a".Translate(); + "d.A".Translate(); + return; + } + + [HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyReversePatch] + static void OSaveGameAs(this UISaveGameWindow ui, int data) { } + + [HarmonyPatch(typeof(UISaveGameWindow), "CheckAndSetSaveButtonEnable"), HarmonyPostfix] + static void CheckAndSetSaveButtonEnable(UISaveGameWindow __instance, UIButton ___saveButton, Text ___saveButtonText) + { + _OnOpen(__instance, ___saveButton, ___saveButtonText); + if (context.saveButtonText && context.saveButton) + SetButtonState(context.saveButtonText.text, context.saveButton.button.interactable); + } + + static void SetButtonState(string text, bool interactable) + { + context.buttonCompress.button.interactable = interactable; + context.buttonCompressText.text = text; + } + + class UIContext + { + public UIButton buttonCompress; + public UIButton saveButton; + public Text buttonCompressText; + public Text saveButtonText; + public UISaveGameWindow ui; + } + + [HarmonyPatch(typeof(UISaveGameWindow), "OnSaveClick"), HarmonyPrefix] + static void OnSaveClick() + { + PatchSave.UseCompressSave = true; + } + + static UIContext context = new UIContext(); + + [HarmonyPatch(typeof(UISaveGameWindow), "_OnOpen"), HarmonyPostfix] + static void _OnOpen(UISaveGameWindow __instance, UIButton ___saveButton, Text ___saveButtonText) + { + if (!context.buttonCompress) + { + context.saveButton = ___saveButton; + context.saveButtonText = ___saveButtonText; + + context.ui = __instance; + context.buttonCompress = (__instance.transform.Find("button-compress")?.gameObject??GameObject.Instantiate(___saveButton.gameObject, ___saveButton.transform.parent)).GetComponent(); + + context.buttonCompress.gameObject.name = "button-compress"; + context.buttonCompress.transform.Translate(new Vector3(-2.0f, 0, 0)); + context.buttonCompress.button.image.color = new Color32(0xfc,0x6f,00,0x77); + context.buttonCompressText = context.buttonCompress.transform.Find("button-text")?.GetComponent(); + + context.buttonCompress.onClick += __instance.OnSaveClick; + context.saveButton.onClick -= __instance.OnSaveClick; + context.saveButton.onClick += WrapClick; + } + } + + static void WrapClick(int data) + { + PatchSave.UseCompressSave = false; + context.ui.OSaveGameAs(data); + } + + + public static void OnDestroy() + { + if (context.buttonCompress) + GameObject.Destroy(context.buttonCompress.gameObject); + if (context.ui) + { + context.saveButton.onClick -= WrapClick; + context.saveButton.onClick += context.ui.OnSaveClick; + } + _OnDestroy(); + } +} \ No newline at end of file diff --git a/CompressSave/README.md b/CompressSave/README.md new file mode 100644 index 0000000..6548aa5 --- /dev/null +++ b/CompressSave/README.md @@ -0,0 +1,104 @@ +# CompressSave + +## Updates + +### 1.1.13 + +* Match game version 0.9.26.13026. +* Move "Sandbox Mode" button on Save Panel to avoid overlap. +* Avoid warning message on "Continue" button of main menu. + +### 1.1.12 (by [@starfi5h](https://github.com/starfi5h/DSP_CompressSave)) + +* Match game version 0.9.25.12007. + +### 1.1.11 (by [@bluedoom](https://github.com/bluedoom/DSP_Mod) till this version) + +* fix 1.1.10 package issue. + +### 1.1.10 + +* Fix 1.1.8 Archive corruption with DIY System, corrupted archives can be fixed by using \[Fix118\] mod + + Fix118: https://github.com/bluedoom/DSP_Mod/blob/master/Fix118 + +### 1.1.9 + +* CompressSave is temporarily disabled due to some error with the DIY system. + +### 1.1.8 + +* Match game version 0.9.24.11029 + +### 1.1.7 + +* Fix incorrect data on statistic panel. +* Improve performance. + +### 1.1.6 + +* fix memory leak + +### 1.1.5 (Game Version 0.8.22) + +* Match game version 0.8.22. +* Thanks [@starfi5h] for + - PatchSave now use transpiler for better robustness. + - Change version check to soft warning. + - Add PeekableReader so other mods can use BinaryReader.PeekChar(). + - Change LZ4DecompressionStream.Position behavior. Position setter i - available now. + +### 1.1.4 (Game Version 0.8.19) + +* Match game version 0.8.19. + +### 1.1.3 (2021/05/29) (Game Version 0.7.18) + +* Match game version 0.7.18. +* Fix memory leak. + +### 1.1.2 (2021/03/24) (Game Version 0.6.17) + +* Handle lz4 library missing Error + +### 1.1.1 (2021/03/17) (Game Version 0.6.17) + +* Fix Load Error + +### 1.1.0 (2021/03/17) (Game Version 0.6.17) + +* Add UI button + +## Introduction + +* Reduce archive size by 30% / save time by 75% (On HDD + i7-4790K@4.4G + DDR3 2400MHz) + + | Before | After | + | - | - | + | 50MB 0.8s | 30MB 0.2s | + | 220M 3.6s | 147M 0.7s | + | 1010M 19.1s | 690M 3.6s | + +## Usage + +* All autosaves are compressed +* Manual saves are compressed while using the new `Save` button. +* You can decompress saves on load panel. +* Remember to backup your save(use original save button) before updating game to avoid loading failure. + +## 介绍 + +* 减少存档容量30% / 存档用时75% (测试环境:机械硬盘 + i7-4790K@4.4G + DDR3 2400MHz) + + | 原存档 | 压缩后 | + | - | - | + | 50MB 0.8s | 30MB 0.2s | + | 220M 3.6s | 147M 0.7s | + | 1010M 19.1s | 690M 3.6s | + +## 使用说明 + +* 所有自动存档都会被压缩。 +* 手动存档使用新加的保存按钮即可压缩保存。 +* 可以在读取存档面板解压存档。 +* 如果游戏有版本更新记得先备份存档(使用原保存按钮)以免更新后无法读取存档。 diff --git a/CompressSave/SaveUtil.cs b/CompressSave/SaveUtil.cs new file mode 100644 index 0000000..5fb2207 --- /dev/null +++ b/CompressSave/SaveUtil.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; +using BepInEx.Logging; +using CompressSave.LZ4Wrap; + +namespace CompressSave; + +class SaveUtil +{ + public static ManualLogSource logger; + + + public static readonly Version VerifiedVersion = new Version + { + Major = 0, + Minor = 9, + Release = 26, + }; + + public static string UnzipToFile(LZ4DecompressionStream lzStream, string fullPath) + { + lzStream.ResetStream(); + string dir = Path.GetDirectoryName(fullPath); + string filename = "[Recovery]-" + Path.GetFileNameWithoutExtension(fullPath); + fullPath = Path.Combine(dir, filename + GameSave.saveExt); + int i = 0; + while(File.Exists(fullPath)) + { + fullPath = Path.Combine(dir, $"{filename}[{i++}]{GameSave.saveExt}"); + } + var buffer = new byte[1024 * 1024]; + using (var fs = new FileStream(fullPath, FileMode.Create)) + using (var br = new BinaryWriter(fs)) + { + for (int read = lzStream.Read(buffer, 0, buffer.Length); read > 0; read = lzStream.Read(buffer, 0, buffer.Length)) + { + fs.Write(buffer, 0, read); + } + fs.Seek(6L, SeekOrigin.Begin); + br.Write(fs.Length); + + } + return Path.GetFileNameWithoutExtension(fullPath); + } + + public static bool DecompressSave(string saveName, out string newSaveName) + { + newSaveName = string.Empty; + string path = GameConfig.gameSaveFolder + saveName + GameSave.saveExt; + try + { + using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + if (!IsCompressedSave(fileStream)) return false; + using (var lzstream = new LZ4DecompressionStream(fileStream)) + { + newSaveName = UnzipToFile(lzstream, path); + } + } + return true; + } + catch (Exception e) + { + logger.LogError(e); + return false; + } + } + public static bool IsCompressedSave(FileStream fs) + { + for (int i = 0; i < 4; i++) + { + if (0xCC != fs.ReadByte()) + return false; + } + return true; + } + + internal static bool IsCompressedSave(string saveName) + { + if (string.IsNullOrEmpty(saveName)) return false; + try + { + using (FileStream fileStream = new FileStream(GetFullSavePath(saveName), FileMode.Open)) + return IsCompressedSave(fileStream); + } + catch (Exception e) + { + logger.LogWarning(e); + return false; + } + } + + public static string GetFullSavePath(string saveName) => GameConfig.gameSaveFolder + saveName + GameSave.saveExt; + + public static bool VerifyVersion(int majorVersion, int minorVersion, int releaseVersion) + { + return new Version(majorVersion, minorVersion, releaseVersion) == VerifiedVersion; + } +} \ No newline at end of file diff --git a/CompressSave/package/LZ4.dll b/CompressSave/package/LZ4.dll new file mode 100644 index 0000000000000000000000000000000000000000..58a3f614127647834c167f17a8b95fec5d3f2e71 GIT binary patch literal 143872 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 Zv{PC*=c4?A_LBCpb^>4i{y%}h{{`mT(M|vW literal 0 HcmV?d00001 diff --git a/CompressSave/package/icon.png b/CompressSave/package/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0fee584ca00625d21d10da89ed5d761f346ad8b7 GIT binary patch literal 53761 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 KMNUMnLSTX)Go3d8 literal 0 HcmV?d00001 diff --git a/CompressSave/package/manifest.json b/CompressSave/package/manifest.json new file mode 100644 index 0000000..480da6d --- /dev/null +++ b/CompressSave/package/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "CompressSave", + "version_number": "1.1.11", + "website_url": "https://github.com/bluedoom/DSP_Mod", + "description": "压缩存档,优化写入速度|Compress Save File and Optimize the write Speed.", + "dependencies": ["xiaoye97-BepInEx-5.4.17"] +} \ No newline at end of file diff --git a/DSP_Mods.sln b/DSP_Mods.sln index 14bd575..74a81d6 100644 --- a/DSP_Mods.sln +++ b/DSP_Mods.sln @@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HideTips", "HideTips\HideTi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dustbin", "Dustbin\Dustbin.csproj", "{931F0230-5941-4E49-AB19-66921AF14096}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompressSave", "CompressSave\CompressSave.csproj", "{A283A918-207F-4DD7-A098-F99EBE610558}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +32,9 @@ Global {931F0230-5941-4E49-AB19-66921AF14096}.Debug|Any CPU.Build.0 = Debug|Any CPU {931F0230-5941-4E49-AB19-66921AF14096}.Release|Any CPU.ActiveCfg = Release|Any CPU {931F0230-5941-4E49-AB19-66921AF14096}.Release|Any CPU.Build.0 = Release|Any CPU + {A283A918-207F-4DD7-A098-F99EBE610558}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A283A918-207F-4DD7-A098-F99EBE610558}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A283A918-207F-4DD7-A098-F99EBE610558}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A283A918-207F-4DD7-A098-F99EBE610558}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Dustbin/README.md b/Dustbin/README.md index 47780c8..fb593cc 100644 --- a/Dustbin/README.md +++ b/Dustbin/README.md @@ -1,8 +1,15 @@ -## Dustbin +# Dustbin -### Storages can destroy incoming items while capacity limited to zero +#### Storages can destroy incoming items while capacity limited to zero -* Conditions to be dustbin: Storages with capacity limited to zero at top of stacks, and empty in 1st cell. +## Updates + +* 1.0.1 + * Remove a debug log + +## Usage + +* Conditions to be dustbin: Storages with capacity limited to zero at top of stacks(or only one level), and empty in 1st cell. * Items sent into dustbins are removed immediately. * Can get sands from destroyed items (with factors configurable): * Get 10/100 sands from each silicon/fractal silicon ore @@ -14,7 +21,14 @@ This is caused by a logic bug in original code where faulty set `lastFullItem` field of `StorageComponent` for empty storages. -#### Updates +## 使用说明 -* 1.0.1 - * Remove a debug log +* 垃圾桶条件:空间限制为0第一格为空并且放在堆叠顶部(或者只有一层)的储存箱。 +* 送进垃圾桶的物品会立即被移除。 +* 可以从移除的物品中获得沙子(可配置,下为默认值): + * 从硅石和分形硅石中获得10/100个沙子。 + * 从普通物品中获得1个沙子,但液体不会给沙子。 +* 已知Bug + * 在空间限制为0的箱子上面再叠一个箱子后再移除,会导致垃圾箱功能失效,放一个物品进去再拿出来即可恢复正常。 + + 这是原游戏的逻辑Bug错误设置了`StorageComponent`的`lastFullItem`字段导致。 diff --git a/HideTips/README.md b/HideTips/README.md index f8f8d49..74f2ce1 100644 --- a/HideTips/README.md +++ b/HideTips/README.md @@ -1,12 +1,20 @@ -## HideTips +# HideTips -### Hide/Disable various tutorial tips/messages +#### Hide/Disable various tutorial tips/messages + +## Updates + +* 1.0.1 + * Hide 3 more random tutorial tips popup from bottom-right panel + +## Usage * Tips/messages that can be hidden: random-reminder, tutorial, achievement/milestone card. * Skip prologue for new game. * Each type of tips/messages is configurable individually. -#### Updates +## 使用说明 -* 1.0.1 - * Hide 3 more random tutorial tips popup from bottom-right panel \ No newline at end of file +* 可以屏蔽的消息:随机提醒,教程,成就/里程碑卡片。 +* 跳过新游戏引导动画。 +* 各种屏蔽功能都可以在配置文件里开关。 diff --git a/LogisticMiner/README.md b/LogisticMiner/README.md index 04be5ab..3d9f4f3 100644 --- a/LogisticMiner/README.md +++ b/LogisticMiner/README.md @@ -1,6 +1,8 @@ -## LogisticMiner +# LogisticMiner -### Logistic Storages can mine all ores/water on current planet +#### Logistic Storages can mine all ores/water on current planet + +## Usage * Inspired by [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner)) @@ -16,9 +18,9 @@ * Can burn fuels in certain slot when energy below half of max. * Sprayed fuels generates extra energy as normal. * All used parameters are configurable: - * ILS has the same speed as normal Mining Machine for normal ores by default. + * Logistic Miner has the same speed as normal Mining Machine for normal ores by default. - But you can set mining scale in configuration, which makes ILS working like Advance Mining Machines: power + But you can set mining scale in configuration, which makes Logistic Miner working like Advance Mining Machines: power consumption increases by the square of the scale, and gradually decrease mining speed over half of the maximum count. @@ -30,3 +32,27 @@ * Energy costs: 1MW/vein-group & 10MW/water-slot & 1.8MW/oil-seep(configurable), `Veins Utilization` upgrades does not increase power consumption(unlike PlanetMiner). * Fuels burning slot. Default: 4th for ILS, 3rd for PLS. Set to 0 to disable it. + +## 使用说明 + +* 创意来自 [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner)) + + 对性能重度优化,并解决了PlanetMiner的精度等问题。 +* (对PlanetMiner的优化) 仅当矿堆发生变化(填埋/恢复/采完)时重新计算矿堆数据,解决每行星每计算帧要重建字典的性能问题。 +* (对PlanetMiner的优化) 用浮点数保证更精确的帧计算。 +* (对PlanetMiner的优化) 升级`矿物利用`不会提升能耗。 +* (对PlanetMiner的优化) 分开矿物,油井和水的采集能耗。 +* (对PlanetMiner的优化) 采集能耗以矿物组,油井为单位,相对更加合理。 +* 剩余电量少于一半时可以燃烧指定格子的燃料补充。 + * (对PlanetMiner的优化) 喷涂了增产剂的燃料按照正常的计算方式提供更多的能量(除了原本就不增加能量输出的反物质燃料棒)。 +* 所有参数都可以在设置文件内配置: + * 物流塔矿机和普通矿机采矿速度一样(等同于同时采集所有对应矿物)。 + + 你可以设置采矿倍率改变物流塔矿机采矿速度,和高级采矿机相同地,能耗和倍率的平方成正比,并且在存储矿物量多于一半时逐渐降低采矿倍率。 + + 此倍率对各种矿物,油井和水的采集都生效。 + + 倍率可以设置为0(默认),此时倍率会随科技解锁而变化,默认是100%,解锁高级采矿机后变为300%。 + * 水的采集速度默认为100/s。 + * 能耗:每矿物组 1MW,单格水 10MW,每油井 1.8MW。 + * 燃料格位置。默认:星际物流塔第4格,行星内物流塔第3格。 diff --git a/README.md b/README.md index 92498a8..363ed8b 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,21 @@ -# DSP Mods by Soar Qin +## DSP Mods by Soar Qin -## [CheatEnabler](CheatEnabler) +# [CheatEnabler](CheatEnabler) -### Add various cheat functions while disabling abnormal determinants +Add various cheat functions while disabling abnormal determinants -* Disable abnormal determinants (Disable all sanity checks, and can get achievements on using Console and Developer Mode - shortcuts). -* Shift+F4 to switch Developer Mode on/off. - * Numpad 1: Gets all items and extends bag. - * Numpad 2: Boosts walk speed, gathering speed and mecha energy restoration. - * Numpad 3: Fills planet with foundations and bury all veins. - * Numpad 4: +1 construction drone. - * Numpad 5: Upgrades drone engine tech to full. - * Numpad 6: Unlocks researching tech. - * Numpad 7: Unlocks Drive Engine 1. - * Numpad 8: Unlocks Drive Engine 2 and maximize energy. - * Numpad 9: Unlocks ability to warp. - * Numpad 0: No costs for Logistic Storages' output. - * LCtrl + T: Unlocks all techs (not upgrades). - * LCtrl + A: Resets all local achievements. - * LCtrl + Q: Adds 10000 to every metadata. - * LCtrl + W: Enters Sandbox Mode. - * Numpad *: Proliferates items on hand. - * Numpad /: Removes proliferations from items on hand. - * PageDown: Remembers Pose of game camera. - * PageUp: Locks game camera using remembered Pose. -* Always infinite resource. -* Each function can be enabled individually in config file. +# [CompressSave](CompressSave) -## [LogisticMiner](LogisticMiner) +Compress game saves to reduce space use and boost save time -### Logistic Storages can mine all ores/water on current planet +# [LogisticMiner](LogisticMiner) -* Inspired - by [PlanetMiner](https://dsp.thunderstore.io/package/blacksnipebiu/PlanetMiner)([github](https://github.com/blacksnipebiu/PlanetMiner)) - . +Logistic Storages can mine all ores/water on current planet - But it is heavily optimized to resolve performance, accuracy and other issues in PlanetMiner. -* Only recalculate count of veins when vein chunks are changed (added/removed by foundations/Sandbox-Mode, or - exhausted), so this removes Dictionary allocation on each planet for every frame. -* More accurate frame counting by use float number. -* Does not increase power consumptions on `Veins Utilization` upgrades. -* Separate power consumptions for veins, oil seeps and water. -* Power consumptions are counted by groups of veins and count of oil seeps, which is more sensible. -* Can burn fuels in certain slot when energy below half of max. - * Sprayed fuels generates extra energy as normal. -* All used parameters are configurable: - * ILS has the same speed as normal Mining Machine for normal ores by default. +# [HideTips](HideTips) - But you can set mining scale in configuration, which makes ILS working like Advance Mining Machines: power - consumption increases by the square of the scale, and gradually decrease mining speed over half of the maximum - count. +Hide/Disable various tutorial tips/messages - This applies to all of veins, oils and water. +# [Dustbin](Dustbin) - Mining scale can be set to 0(by default), which means it is automatically set by tech unlocks, set to 300 when you - reaches Advanced Mining Machine, otherwise 100. - * 100/s for water by default. - * Energy costs: 1MW/vein-group & 10MW/water-slot & 1.8MW/oil-seep(configurable), `Veins Utilization` upgrades - does not increase power consumption(unlike PlanetMiner). - * Fuels burning slot. Default: 4th for ILS, 3rd for PLS. Set to 0 to disable it. - -## [HideTips](HideTips) - -### Hide/Disable various tutorial tips/messages - -* Tips/messages that can be hidden: random-reminder, tutorial, achievement/milestone card. -* Skip prologue for new game. -* Each type of tips/messages is configurable individually. -* For sanity check warning messages, please check [CheatEnabler](CheatEnabler) - -## [Dustbin](Dustbin) - -### Storages can destroy incoming items while capacity limited to zero - -* Conditions to be dustbin: Storages with capacity limited to zero at top of stacks, and empty in 1st cell. -* Items sent into dustbins are removed immediately. -* Can get sands from destroyed items (with factors configurable): - * Get 10/100 sands from each silicon/fractal silicon ore - * Get 1 sand from any other normal item but fluid -* Known bugs - * Stack 1 more storage up on a zero limited one and remove it will cause dustbin stop working. Just put somethings - in and take them out to make the dustbin working again. - - This is caused by a logic bug in original code where faulty set `lastFullItem` field of `StorageComponent` for - empty storages. \ No newline at end of file +Storages can destroy incoming items while capacity limited to zero \ No newline at end of file