Skip to content

Commit

Permalink
Commenting
Browse files Browse the repository at this point in the history
* Mostly commented what I feel like is needed.
* Made MSDelta not read in the whole file but only the first 70 bytes upon checking status as that is required to allow it to do it's job.
* BSDiffDelta using some more modern logic
* More TODO's in places which could be expanded upon
  • Loading branch information
Azyyyyyy committed Jan 13, 2024
1 parent d36d039 commit 9a9b7d2
Show file tree
Hide file tree
Showing 23 changed files with 346 additions and 227 deletions.
49 changes: 18 additions & 31 deletions src/Deltas/TinyUpdate.Delta.BSDiff/BSDiffDelta.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Microsoft.Extensions.Logging;
using System.Buffers.Binary;
using Microsoft.Extensions.Logging;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using TinyUpdate.Core.Abstract;

//TODO: Bring in changes from bsdiff.net into here
// Squirrel.Bsdiff: Adapted from https://github.com/LogosBible/bsdiff.net/blob/master/src/bsdiff/BinaryPatchUtility.cs
// TinyUpdate: Adapted from https://github.com/Squirrel/Squirrel.Windows/blob/develop/src/Squirrel/BinaryPatchUtility.cs
namespace TinyUpdate.Delta.BSDiff;
Expand Down Expand Up @@ -72,7 +74,8 @@ public long TargetStreamSize(Stream deltaStream)
return newSize;
}

public async Task<bool> ApplyDeltaFile(Stream originalStream, Stream deltaStream, Stream targetStream, IProgress<double>? progress = null)
//TODO: Add Source + Target size check
public async Task<bool> ApplyDeltaFile(Stream sourceStream, Stream deltaStream, Stream targetStream, IProgress<double>? progress = null)
{
// check patch stream capabilities
if (!deltaStream.CanRead)
Expand Down Expand Up @@ -107,6 +110,7 @@ with control block a set of triples (x,y,z) meaning "add x bytes
from oldfile to x bytes from the diff block; copy y bytes from the
extra block; seek forwards in oldfile by z bytes".
*/

// read header
long controlLength, diffLength, newSize;
await using (var patchStream = CreatePatchStream())
Expand Down Expand Up @@ -177,7 +181,7 @@ with control block a set of triples (x,y,z) meaning "add x bytes
}

// seek old file to the position that the new data is diffed against
originalStream.Position = oldPosition;
sourceStream.Position = oldPosition;

var bytesToCopy = (int)control[0];
while (bytesToCopy > 0)
Expand All @@ -188,8 +192,8 @@ with control block a set of triples (x,y,z) meaning "add x bytes
await diffStream.ReadExactlyAsync(newData.AsMemory(0, actualBytesToCopy));

// add old data to diff string
var availableOriginalStreamBytes = Math.Min(actualBytesToCopy, (int)(originalStream.Length - originalStream.Position));
await originalStream.ReadExactlyAsync(oldData.AsMemory(0, availableOriginalStreamBytes));
var availableOriginalStreamBytes = Math.Min(actualBytesToCopy, (int)(sourceStream.Length - sourceStream.Position));
await sourceStream.ReadExactlyAsync(oldData.AsMemory(0, availableOriginalStreamBytes));

for (var index = 0; index < availableOriginalStreamBytes; index++)
newData[index] += oldData[index];
Expand Down Expand Up @@ -241,7 +245,8 @@ Stream CreatePatchStream()
}
}

public async Task<bool> CreateDeltaFile(Stream originalStream, Stream targetStream, Stream deltaStream, IProgress<double>? progress = null)
//TODO: Add Source + Target size check
public async Task<bool> CreateDeltaFile(Stream sourceStream, Stream targetStream, Stream deltaStream, IProgress<double>? progress = null)
{
// check arguments
if (!deltaStream.CanSeek)
Expand All @@ -257,8 +262,8 @@ public async Task<bool> CreateDeltaFile(Stream originalStream, Stream targetStre
}

// get data
var oldData = new byte[originalStream.Length];
await originalStream.ReadExactlyAsync(oldData.AsMemory(0, oldData.Length));
var oldData = new byte[sourceStream.Length];
await sourceStream.ReadExactlyAsync(oldData.AsMemory(0, oldData.Length));

var newData = new byte[targetStream.Length];
await targetStream.ReadExactlyAsync(newData.AsMemory(0, newData.Length));
Expand Down Expand Up @@ -670,32 +675,14 @@ private static int[] SuffixSort(byte[] oldData)

private static long ReadInt64(byte[] buf, int offset)
{
long value = buf[offset + 7] & 0x7F;

for (var index = 6; index >= 0; index--)
{
value *= 256;
value += buf[offset + index];
}

if ((buf[offset + 7] & 0x80) != 0)
value = -value;

return value;
var value = BinaryPrimitives.ReadInt64LittleEndian(buf);
var mask = value >> 63;
return (~mask & value) | (((value & unchecked((long) 0x8000_0000_0000_0000)) - value) & mask);
}

private static void WriteInt64(long value, byte[] buf, int offset)
{
var valueToWrite = value < 0 ? -value : value;

for (var byteIndex = 0; byteIndex < 8; byteIndex++)
{
buf[offset + byteIndex] = (byte)(valueToWrite % 256);
valueToWrite -= buf[offset + byteIndex];
valueToWrite /= 256;
}

if (value < 0)
buf[offset + 7] |= 0x80;
var mask = value >> 63;
BinaryPrimitives.WriteInt64LittleEndian(buf, ((value + mask) ^ mask) | (value & unchecked((long) 0x8000_0000_0000_0000)));
}
}
52 changes: 29 additions & 23 deletions src/Deltas/TinyUpdate.Delta.MSDelta/MSDelta.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using System.Text;
using TinyUpdate.Core.Abstract;
using TinyUpdate.Delta.MSDelta.Enum;
using TinyUpdate.Delta.MSDelta.Struct;

namespace TinyUpdate.Delta.MSDelta;

/*TODO: Possibly imp https://github.com/smilingthax/msdelta-pa30-format so we can at least detect valid files in a cross-platform matter?*/

/// <summary>
/// Provides creating and applying MSDelta... deltas
/// </summary>
public partial class MSDelta : IDeltaApplier, IDeltaCreation
{
public string Extension => ".diff";

public unsafe bool SupportedStream(Stream deltaStream)
{
var deltaBytes = new byte[deltaStream.Length];
var deltaBytes = new byte[70]; //This is enough space to hold a MSDelta header
deltaStream.ReadExactly(deltaBytes, 0, deltaBytes.Length);

fixed (byte* deltaBuf = deltaBytes)
{
var deltaDeltaInput = new DeltaInput(deltaBuf, deltaBytes.Length, true);
var supported = GetDeltaInfoB(deltaDeltaInput, out var info);
var supported = GetDeltaInfoB(deltaDeltaInput, out _);
return supported;
}
}

public unsafe long TargetStreamSize(Stream deltaStream)
{
var deltaBytes = new byte[deltaStream.Length];
var deltaBytes = new byte[70]; //This is enough space to hold a MSDelta header
deltaStream.ReadExactly(deltaBytes, 0, deltaBytes.Length);

fixed (byte* deltaBuf = deltaBytes)
Expand All @@ -39,15 +45,15 @@ public unsafe long TargetStreamSize(Stream deltaStream)
return -1;
}

public unsafe Task<bool> ApplyDeltaFile(Stream sourceFileStream, Stream deltaFileStream,
Stream targetFileStream,
public unsafe Task<bool> ApplyDeltaFile(Stream sourceStream, Stream deltaStream,
Stream targetStream,
IProgress<double>? progress = null)
{
var sourceBytes = new byte[sourceFileStream.Length];
sourceFileStream.ReadExactly(sourceBytes, 0, sourceBytes.Length);
var sourceBytes = new byte[sourceStream.Length];
sourceStream.ReadExactly(sourceBytes, 0, sourceBytes.Length);

var deltaBytes = new byte[deltaFileStream.Length];
deltaFileStream.ReadExactly(deltaBytes, 0, deltaBytes.Length);
var deltaBytes = new byte[deltaStream.Length];
deltaStream.ReadExactly(deltaBytes, 0, deltaBytes.Length);

fixed (byte* sourceBuf = sourceBytes)
fixed (byte* deltaBuf = deltaBytes)
Expand All @@ -59,26 +65,26 @@ public unsafe Task<bool> ApplyDeltaFile(Stream sourceFileStream, Stream deltaFil
var success = ApplyDeltaB(ApplyFlag.None, sourceDeltaInput, deltaDeltaInput, out var output);
if (success)
{
var deltaStream = new UnmanagedMemoryStream((byte*)output.BufferPtr, output.Size);
deltaStream.CopyTo(targetFileStream);
deltaStream.Dispose();
var deltaProcessedStream = new UnmanagedMemoryStream((byte*)output.BufferPtr, output.Size);
deltaProcessedStream.CopyTo(targetStream);
deltaProcessedStream.Dispose();
cleared = DeltaFree(output.BufferPtr);
}

return Task.FromResult(success && cleared);
}
}

public unsafe Task<bool> CreateDeltaFile(Stream sourceFileStream, Stream targetFileStream,
Stream deltaFileStream,
//TODO: Add Source + Target size check
public unsafe Task<bool> CreateDeltaFile(Stream sourceStream, Stream targetStream,
Stream deltaStream,
IProgress<double>? progress = null)
{
//TODO: Add stream length check
var sourceBytes = new byte[sourceFileStream.Length];
sourceFileStream.ReadExactly(sourceBytes, 0, sourceBytes.Length);
var sourceBytes = new byte[sourceStream.Length];
sourceStream.ReadExactly(sourceBytes, 0, sourceBytes.Length);

var targetBytes = new byte[targetFileStream.Length];
targetFileStream.ReadExactly(targetBytes, 0, targetBytes.Length);
var targetBytes = new byte[targetStream.Length];
targetStream.ReadExactly(targetBytes, 0, targetBytes.Length);

fixed (byte* sourceBuf = sourceBytes)
fixed (byte* targetBuf = targetBytes)
Expand All @@ -93,8 +99,8 @@ public unsafe Task<bool> CreateDeltaFile(Stream sourceFileStream, Stream targetF

if (success)
{
using var deltaStream = new UnmanagedMemoryStream((byte*)deltaBuffer.BufferPtr, deltaBuffer.Size);
deltaStream.CopyTo(deltaFileStream);
using var deltaProcessedStream = new UnmanagedMemoryStream((byte*)deltaBuffer.BufferPtr, deltaBuffer.Size);
deltaProcessedStream.CopyTo(deltaStream);
cleared = DeltaFree(deltaBuffer.BufferPtr);
}

Expand All @@ -105,7 +111,7 @@ public unsafe Task<bool> CreateDeltaFile(Stream sourceFileStream, Stream targetF
}
}

//Ms Imp
//MSDelta Hooks
public partial class MSDelta
{
/// <summary>
Expand Down
19 changes: 19 additions & 0 deletions src/Packages/TinyUpdate.TUUP/Consts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,28 @@

public static class Consts
{
/// <summary>
/// The Tuup file extension!
/// </summary>
public const string TuupExtension = ".tuup";

/// <summary>
/// Extension to indicate the file is a brand new file
/// </summary>
public const string NewFileExtension = ".new";

/// <summary>
/// Extension to indicate the file has moved
/// </summary>
public const string MovedFileExtension = ".moved";

/// <summary>
/// Extension to indicate this file contains hash details about another file
/// </summary>
public const string ShasumFileExtension = ".shasum";

/// <summary>
/// Extension to indicate the file has unchanged
/// </summary>
public const string UnchangedFileExtension = ".diff";
}
7 changes: 4 additions & 3 deletions src/Packages/TinyUpdate.TUUP/FileEntryExt.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using TinyUpdate.Core;
using TinyUpdate.Core.Model;

namespace TinyUpdate.TUUP;

Expand All @@ -8,19 +9,19 @@ namespace TinyUpdate.TUUP;
internal static class FileEntryExt
{
/// <summary>
/// Checks that this file is a delta file
/// Checks if the file is a delta file
/// </summary>
/// <param name="fileEntry">Details about the file</param>
public static bool IsDeltaFile(this FileEntry fileEntry) => fileEntry.Filesize != 0 && fileEntry.Extension != Consts.NewFileExtension && fileEntry.Extension != Consts.MovedFileExtension;

/// <summary>
/// Checks that this file is a new file
/// Checks if the file is new
/// </summary>
/// <param name="fileEntry">Details about the file</param>
public static bool IsNewFile(this FileEntry fileEntry) => fileEntry.Filesize != 0 && fileEntry.Extension == Consts.NewFileExtension;

/// <summary>
/// Checks that this file has moved
/// Checks if the file has moved
/// </summary>
/// <param name="fileEntry">Details about the file</param>
public static bool HasFileMoved(this FileEntry fileEntry) => fileEntry.Filesize != 0 && fileEntry.Extension == Consts.MovedFileExtension;
Expand Down
21 changes: 12 additions & 9 deletions src/Packages/TinyUpdate.TUUP/StreamExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,31 @@ public static class StreamExt
/// <summary>
/// Gets the hash and filesize from a file that contains data about a file we need to use for updating
/// </summary>
/// <param name="fileStream">Stream of that file</param>
/// <param name="stream">Stream of that file</param>
/// <param name="hasher">Hasher to use for grabbing checksums</param>
/// <returns>Hash and filesize that is expected</returns>
public static async Task<(string? hash, long filesize)> GetShasumDetails(this Stream fileStream, IHasher hasher)
public static async Task<(string? hash, long filesize)> GetShasumDetails(this Stream stream, IHasher hasher)
{
//Grab the text from the file
using var textStream = new StreamReader(fileStream);
//We expect the information to be contained in the following way:
//<hash> <filesize>

using var textStream = new StreamReader(stream);
var text = await textStream.ReadToEndAsync();

//Return nothing if we don't have anything
if (string.IsNullOrWhiteSpace(text))
{
return (null, -1);
}

//TODO: Make a Span splitter
//Grab what we need, checking that it's what we expect
var textS = text.Split(' ');
var hash = textS[0];
if (textS.Length != 2 ||
var textSplit = text.Split(' ');
var hash = textSplit[0];

if (textSplit.Length != 2 ||
string.IsNullOrWhiteSpace(hash) ||
!hasher.IsValidHash(hash) ||
!long.TryParse(textS[1], out var filesize))
!long.TryParse(textSplit[1], out var filesize))
{
return (null, -1);
}
Expand Down
Loading

0 comments on commit 9a9b7d2

Please sign in to comment.