-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
569 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Oops, something went wrong.