Skip to content

Commit

Permalink
Chaos.IO Update
Browse files Browse the repository at this point in the history
  • Loading branch information
FallenDev committed Jul 19, 2024
1 parent 6de24eb commit 306fbb0
Show file tree
Hide file tree
Showing 9 changed files with 569 additions and 120 deletions.
79 changes: 41 additions & 38 deletions Chaos.IO/Compression/ZLIB.cs
Original file line number Diff line number Diff line change
@@ -1,99 +1,102 @@
using ComponentAce.Compression.Libs.zlib;
using System.IO.Compression;

namespace Chaos.IO.Compression;

/// <summary>
/// Provides methods for compressing and decompressing data using the ZLIB algorithm.
/// </summary>
public static class ZLIB
public static class Zlib
{
/// <summary>
/// Compresses the specified buffer and returns the compressed data as a MemoryStream.
/// </summary>
/// <param name="buffer">The buffer to compress.</param>
/// <returns>A MemoryStream containing the compressed data.</returns>
/// <param name="buffer">
/// The buffer to compress.
/// </param>
/// <returns>
/// A MemoryStream containing the compressed data.
/// </returns>
public static MemoryStream Compress(ReadOnlySpan<byte> buffer)
{
var ret = new MemoryStream();
using var compressed = new MemoryStream();
using var compressor = new ZOutputStream(compressed, zlibConst.Z_DEFAULT_COMPRESSION);
var compressed = new MemoryStream();
using var compressor = new ZLibStream(compressed, CompressionMode.Compress);

compressor.Write(buffer);
compressor.finish();

compressed.Position = 0;
compressed.CopyTo(ret);
ret.Position = 0;

return ret;
return compressed;
}

/// <summary>
/// Compresses the specified buffer in-place.
/// </summary>
/// <param name="buffer">The buffer to compress. The compressed data will replace the original data in the buffer.</param>
/// <param name="buffer">
/// The buffer to compress. The compressed data will replace the original data in the buffer.
/// </param>
public static void Compress(ref Span<byte> buffer)
{
using var compressed = Compress(buffer);
compressed.Position = 0;
var resultArr = new byte[compressed.Length];
var resultBuffer = new Span<byte>(resultArr);

_ = compressed.Read(resultBuffer);
buffer = resultBuffer;
buffer = compressed.ToArray();
}

/// <summary>
/// Compresses the specified buffer in-place.
/// </summary>
/// <param name="buffer">The buffer to compress. The compressed data will replace the original data in the buffer.</param>
/// <param name="buffer">
/// The buffer to compress. The compressed data will replace the original data in the buffer.
/// </param>
public static void Compress(ref byte[] buffer)
{
using var compressed = Compress(buffer);

buffer = compressed.ToArray();
}

/// <summary>
/// Decompresses the specified buffer and returns the decompressed data as a MemoryStream.
/// </summary>
/// <param name="buffer">The buffer to decompress.</param>
/// <returns>A MemoryStream containing the decompressed data.</returns>
/// <param name="buffer">
/// The buffer to decompress.
/// </param>
/// <returns>
/// A MemoryStream containing the decompressed data.
/// </returns>
public static MemoryStream Decompress(ReadOnlySpan<byte> buffer)
{
var ret = new MemoryStream();
using var outData = new MemoryStream();
using var decompressor = new ZOutputStream(outData);

decompressor.Write(buffer);
decompressor.finish();
var decompressed = new MemoryStream();
using var compressed = new MemoryStream();
using var decompressor = new ZLibStream(compressed, CompressionMode.Decompress);

outData.Position = 0;
outData.CopyTo(ret);
ret.Position = 0;
compressed.Write(buffer);
compressed.Seek(0, SeekOrigin.Begin);
decompressor.CopyTo(decompressed);

return ret;
return decompressed;
}

/// <summary>
/// Decompresses the specified buffer in-place.
/// </summary>
/// <param name="buffer">The buffer to decompress. The decompressed data will replace the original data in the buffer.</param>
/// <param name="buffer">
/// The buffer to decompress. The decompressed data will replace the original data in the buffer.
/// </param>
public static void Decompress(ref Span<byte> buffer)
{
using var decompressed = Decompress(buffer);
var resultArr = new byte[decompressed.Length];
var resultBuffer = new Span<byte>(resultArr);
_ = decompressed.Read(resultBuffer);
buffer = resultBuffer;

buffer = decompressed.ToArray();
}

/// <summary>
/// Decompresses the specified buffer in-place.
/// </summary>
/// <param name="buffer">The buffer to decompress. The decompressed data will replace the original data in the buffer.</param>
/// <param name="buffer">
/// The buffer to decompress. The decompressed data will replace the original data in the buffer.
/// </param>
public static void Decompress(ref byte[] buffer)
{
using var decompressed = Decompress(buffer);

buffer = decompressed.ToArray();
}
}
17 changes: 9 additions & 8 deletions Chaos.IO/Definitions/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ namespace Chaos.IO.Definitions;
public enum Endianness
{
/// <summary>
/// Little endian is a term used to describe the byte order of multi-byte data types in computer memory.
/// In little endian, the least significant byte (LSB) is stored at the lowest memory address,
/// and the most significant byte (MSB) is stored at the highest memory address.
/// In other words, the bytes are ordered from the smallest to the largest, based on their significance.
/// Little endian is a term used to describe the byte order of multi-byte data types in computer memory. In little
/// endian, the least significant byte (LSB) is stored at the lowest memory address, and the most significant byte
/// (MSB) is stored at the highest memory address. In other words, the bytes are ordered from the smallest to the
/// largest, based on their significance.
/// </summary>
LittleEndian,

/// <summary>
/// Big endian is a term used to describe the byte order of multi-byte data types in computer memory.
/// In big endian, the most significant byte (MSB) is stored at the lowest memory address,
/// and the least significant byte (LSB) is stored at the highest memory address.
/// In other words, the bytes are ordered from the largest to the smallest, based on their significance.
/// Big endian is a term used to describe the byte order of multi-byte data types in computer memory. In big endian,
/// the most significant byte (MSB) is stored at the lowest memory address, and the least significant byte (LSB) is
/// stored at the highest memory address. In other words, the bytes are ordered from the largest to the smallest, based
/// on their significance.
/// </summary>
BigEndian
}
13 changes: 13 additions & 0 deletions Chaos.IO/Exceptions/RetryableException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Chaos.IO.Exceptions;

/// <summary>
/// An exception that can be retried
/// </summary>
public sealed class RetryableException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="RetryableException" /> class
/// </summary>
public RetryableException(string message, Exception? innerException = null)
: base(message, innerException) { }
}
105 changes: 71 additions & 34 deletions Chaos.IO/FileSystem/DirectorySynchronizer.cs
Original file line number Diff line number Diff line change
@@ -1,91 +1,128 @@
using Chaos.Common.Collections.Synchronized;

namespace Chaos.IO.FileSystem;

/// <summary>
/// Provides methods for performing synchronized operations on directories
/// </summary>
public static class DirectorySynchronizer
{
internal static readonly SynchronizedHashSet<string> LockedDirectories;
internal static readonly HashSet<string> LockedPaths;
private static readonly object Sync = new();

static DirectorySynchronizer() => LockedPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

private static bool InnerTryAddPath(string innerPath)
{
//lock LockedPaths
lock (Sync)
{
if (LockedPaths.Any(lockedPath => PathEx.IsSubPathOf(innerPath, lockedPath)))
return false;

return LockedPaths.Add(innerPath);
}
}

static DirectorySynchronizer() => LockedDirectories = new SynchronizedHashSet<string>(comparer: StringComparer.OrdinalIgnoreCase);
private static void LockPath(string path) => SpinWait.SpinUntil(() => InnerTryAddPath(path));

private static async Task LockPathAsync(string path)
{
while (!InnerTryAddPath(path))
await Task.Yield();
}

/// <summary>
/// Executes the specified function on the specified directory, ensuring that no other actions are being performed on
/// the directory
/// </summary>
/// <param name="directory">The directory to lock during execution</param>
/// <param name="action">The function to execute</param>
public static void SafeExecute(this string directory, Action<string> action)
/// <param name="path">
/// The directory to lock during execution
/// </param>
/// <param name="action">
/// The function to execute
/// </param>
public static void SafeExecute(this string path, Action<string> action)
{
SpinWait.SpinUntil(() => LockedDirectories.Add(directory));
LockPath(path);

try
{
action(directory);
action(path);
} finally
{
LockedDirectories.Remove(directory);
lock (Sync)
LockedPaths.Remove(path);
}
}

/// <summary>
/// Executes the specified function on the specified directory, ensuring that no other actions are being performed on
/// the directory
/// </summary>
/// <param name="directory">The directory to lock during execution</param>
/// <param name="function">The function to execute</param>
public static TResult SafeExecute<TResult>(this string directory, Func<string, TResult> function)
/// <param name="path">
/// The directory to lock during execution
/// </param>
/// <param name="function">
/// The function to execute
/// </param>
public static TResult SafeExecute<TResult>(this string path, Func<string, TResult> function)
{
SpinWait.SpinUntil(() => LockedDirectories.Add(directory));
LockPath(path);

try
{
return function(directory);
return function(path);
} finally
{
LockedDirectories.Remove(directory);
lock (Sync)
LockedPaths.Remove(path);
}
}

/// <summary>
/// Asynchronously executes the specified function on the specified directory, ensuring that no other actions are being
/// performed on the directory
/// Asynchronously executes the specified function on the specified path, ensuring that no other actions are being
/// performed on the path, or any sub-paths
/// </summary>
/// <param name="directory">The directory to lock during execution</param>
/// <param name="function">The function to execute</param>
public static async Task SafeExecuteAsync(this string directory, Func<string, Task> function)
/// <param name="path">
/// The path to lock during execution
/// </param>
/// <param name="function">
/// The function to execute
/// </param>
public static async Task SafeExecuteAsync(this string path, Func<string, Task> function)
{
while (!LockedDirectories.Add(directory))
await Task.Yield();
await LockPathAsync(path);

try
{
await function(directory);
await function(path);
} finally
{
LockedDirectories.Remove(directory);
lock (Sync)
LockedPaths.Remove(path);
}
}

/// <summary>
/// Asynchronously executes the specified function on the specified directory, ensuring that no other actions are being
/// performed on the directory
/// Asynchronously executes the specified function on the specified path, ensuring that no other actions are being
/// performed on the path, or any sub-paths
/// </summary>
/// <param name="directory">The directory to lock during execution</param>
/// <param name="function">The function to execute</param>
public static async Task<T> SafeExecuteAsync<T>(this string directory, Func<string, Task<T>> function)
/// <param name="path">
/// The path to lock during execution
/// </param>
/// <param name="function">
/// The function to execute
/// </param>
public static async Task<T> SafeExecuteAsync<T>(this string path, Func<string, Task<T>> function)
{
while (!LockedDirectories.Add(directory))
await Task.Yield();
await LockPathAsync(path);

try
{
return await function(directory);
return await function(path);
} finally
{
LockedDirectories.Remove(directory);
lock (Sync)
LockedPaths.Remove(path);
}
}
}
Loading

0 comments on commit 306fbb0

Please sign in to comment.