aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-10-20 12:18:44 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-10-20 12:18:44 -0400
commitd997950a29ec3ce29cd652298e678d708218fdad (patch)
tree65733374ba223b08c99c98629a51c3789c0aa2e6 /lib/Utils/src
parentf44cdf8f2685c37e5a1d77018a5227942b578863 (diff)
compression rpmalloc static linking, encoder memory callbacks, and more tweaks
Diffstat (limited to 'lib/Utils/src')
-rw-r--r--lib/Utils/src/Async/AsyncQueue.cs45
-rw-r--r--lib/Utils/src/Async/IAsyncEventSource.cs45
-rw-r--r--lib/Utils/src/Async/IAsyncQueue.cs76
-rw-r--r--lib/Utils/src/Extensions/IoExtensions.cs78
-rw-r--r--lib/Utils/src/IO/IsolatedStorageDirectory.cs77
-rw-r--r--lib/Utils/src/IO/VnMemoryStream.cs53
-rw-r--r--lib/Utils/src/IO/VnStreamWriter.cs2
-rw-r--r--lib/Utils/src/Memory/HeapCreation.cs2
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs6
-rw-r--r--lib/Utils/src/Memory/NativeHeap.cs4
-rw-r--r--lib/Utils/src/Memory/ProcessHeap.cs4
-rw-r--r--lib/Utils/src/Memory/Win32PrivateHeap.cs2
-rw-r--r--lib/Utils/src/VnEncoding.cs9
13 files changed, 307 insertions, 96 deletions
diff --git a/lib/Utils/src/Async/AsyncQueue.cs b/lib/Utils/src/Async/AsyncQueue.cs
index ba45513..45f1219 100644
--- a/lib/Utils/src/Async/AsyncQueue.cs
+++ b/lib/Utils/src/Async/AsyncQueue.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -30,11 +30,12 @@ using System.Diagnostics.CodeAnalysis;
namespace VNLib.Utils.Async
{
+
/// <summary>
/// Provides a <see cref="Channel{T}"/> based asynchronous queue
/// </summary>
/// <typeparam name="T">The event object type</typeparam>
- public class AsyncQueue<T>
+ public class AsyncQueue<T> : IAsyncQueue<T>
{
private readonly Channel<T> _channel;
@@ -45,12 +46,13 @@ namespace VNLib.Utils.Async
/// </summary>
/// <param name="capacity">The maxium number of items to allow in the queue</param>
public AsyncQueue(int capacity):this(false, false, capacity)
- {}
+ { }
+
/// <summary>
/// Initalizes a new multi-threaded unbound channel queue
/// </summary>
public AsyncQueue():this(false, false)
- {}
+ { }
/// <summary>
/// Initalizes a new queue that allows specifying concurrency requirements
@@ -105,39 +107,22 @@ namespace VNLib.Utils.Async
_channel = Channel.CreateBounded<T>(options);
}
- /// <summary>
- /// Attemts to enqeue an item if the queue has the capacity
- /// </summary>
- /// <param name="item">The item to eqneue</param>
- /// <returns>True if the queue can accept another item, false otherwise</returns>
+ /// <inheritdoc/>
public bool TryEnque(T item) => _channel.Writer.TryWrite(item);
- /// <summary>
- /// Enqueues an item to the end of the queue and notifies a waiter that an item was enqueued
- /// </summary>
- /// <param name="item">The item to enqueue</param>
- /// <param name="cancellationToken"></param>
+
+ /// <inheritdoc/>
/// <exception cref="ObjectDisposedException"></exception>
public ValueTask EnqueueAsync(T item, CancellationToken cancellationToken = default) => _channel.Writer.WriteAsync(item, cancellationToken);
- /// <summary>
- /// Asynchronously waits for an item to be Enqueued to the end of the queue.
- /// </summary>
- /// <returns>The item at the begining of the queue</returns>
+
+ /// <inheritdoc/>
/// <exception cref="ObjectDisposedException"></exception>
public ValueTask<T> DequeueAsync(CancellationToken cancellationToken = default) => _channel.Reader.ReadAsync(cancellationToken);
- /// <summary>
- /// Removes the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change
- /// event.
- /// </summary>
- /// <param name="result">The item that was at the begining of the queue</param>
- /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns>
+
+ /// <inheritdoc/>
/// <exception cref="ObjectDisposedException"></exception>
public bool TryDequeue([MaybeNullWhen(false)] out T result) => _channel.Reader.TryRead(out result);
- /// <summary>
- /// Peeks the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change
- /// event.
- /// </summary>
- /// <param name="result">The item that was at the begining of the queue</param>
- /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns>
+
+ /// <inheritdoc/>
/// <exception cref="ObjectDisposedException"></exception>
public bool TryPeek([MaybeNullWhen(false)] out T result) => _channel.Reader.TryPeek(out result);
}
diff --git a/lib/Utils/src/Async/IAsyncEventSource.cs b/lib/Utils/src/Async/IAsyncEventSource.cs
new file mode 100644
index 0000000..968f94c
--- /dev/null
+++ b/lib/Utils/src/Async/IAsyncEventSource.cs
@@ -0,0 +1,45 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Utils
+* File: IAsyncEventSource.cs
+*
+* IAsyncEventSource.cs is part of VNLib.Utils which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Utils is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Utils is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Utils. If not, see http://www.gnu.org/licenses/.
+*/
+
+namespace VNLib.Utils.Async
+{
+ /// <summary>
+ /// A type that publishes events to asynchronous event queues
+ /// </summary>
+ /// <typeparam name="T">The event item type to publish</typeparam>
+ public interface IAsyncEventSource<T>
+ {
+ /// <summary>
+ /// Subscribes a new queue to publish events to
+ /// </summary>
+ /// <param name="queue">The queue instance to publish new events to</param>
+ void Subscribe(IAsyncQueue<T> queue);
+
+ /// <summary>
+ /// Unsubscribes a previously subscribed queue from receiving events
+ /// </summary>
+ /// <param name="queue">The queue instance to unregister from events</param>
+ void Unsubscribe(IAsyncQueue<T> queue);
+ }
+}
diff --git a/lib/Utils/src/Async/IAsyncQueue.cs b/lib/Utils/src/Async/IAsyncQueue.cs
new file mode 100644
index 0000000..ab786f1
--- /dev/null
+++ b/lib/Utils/src/Async/IAsyncQueue.cs
@@ -0,0 +1,76 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Utils
+* File: IAsyncQueue.cs
+*
+* IAsyncQueue.cs is part of VNLib.Utils which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Utils is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Utils is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Utils. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System.Threading;
+using System.Threading.Tasks;
+using System.Diagnostics.CodeAnalysis;
+
+namespace VNLib.Utils.Async
+{
+ /// <summary>
+ /// Provides a generic asynchronous queue
+ /// </summary>
+ /// <typeparam name="T">The item message type</typeparam>
+#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
+ public interface IAsyncQueue<T>
+#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
+ {
+ /// <summary>
+ /// Attemts to enqueue an item if the queue has the capacity
+ /// </summary>
+ /// <param name="item">The item to eqneue</param>
+ /// <returns>True if the queue can accept another item, false otherwise</returns>
+ bool TryEnque(T item);
+
+ /// <summary>
+ /// Enqueues an item to the end of the queue and notifies a waiter that an item was enqueued
+ /// </summary>
+ /// <param name="item">The item to enqueue</param>
+ /// <param name="cancellationToken">A token to cancel the operation</param>
+ ValueTask EnqueueAsync(T item, CancellationToken cancellationToken = default);
+
+ /// <summary>
+ /// Asynchronously waits for an item to be Enqueued to the end of the queue.
+ /// </summary>
+ /// <param name="cancellationToken">A token to cancel the operation</param>
+ /// <returns>The item at the begining of the queue</returns>
+ ValueTask<T> DequeueAsync(CancellationToken cancellationToken = default);
+
+ /// <summary>
+ /// Removes the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change
+ /// event.
+ /// </summary>
+ /// <param name="result">The item that was at the begining of the queue</param>
+ /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns>
+ bool TryDequeue([MaybeNullWhen(false)] out T result);
+
+ /// <summary>
+ /// Peeks the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change
+ /// event.
+ /// </summary>
+ /// <param name="result">The item that was at the begining of the queue</param>
+ /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns>
+ bool TryPeek([MaybeNullWhen(false)] out T result);
+ }
+}
diff --git a/lib/Utils/src/Extensions/IoExtensions.cs b/lib/Utils/src/Extensions/IoExtensions.cs
index d727fed..ffa374b 100644
--- a/lib/Utils/src/Extensions/IoExtensions.cs
+++ b/lib/Utils/src/Extensions/IoExtensions.cs
@@ -26,15 +26,19 @@ using System;
using System.IO;
using System.Buffers;
using System.Threading;
+using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.Versioning;
+using System.IO.IsolatedStorage;
using System.Runtime.CompilerServices;
using VNLib.Utils.IO;
using VNLib.Utils.Memory;
-
using static VNLib.Utils.Memory.MemoryUtil;
+//Disable configure await warning
+#pragma warning disable CA2007
+
namespace VNLib.Utils.Extensions
{
/// <summary>
@@ -103,6 +107,7 @@ namespace VNLib.Utils.Extensions
//Wait for copy to complete
await CopyToAsync(source, dest, buffer.Memory, token);
}
+
/// <summary>
/// Provides an async wrapper for copying data from the current stream to another with a
/// buffer from the <paramref name="heap"/>
@@ -186,6 +191,7 @@ namespace VNLib.Utils.Extensions
dest.Write(buffer.Span[..read]);
} while (true);
}
+
/// <summary>
/// Copies data from one stream to another, using self managed buffers. May allocate up to 2MB.
/// </summary>
@@ -440,5 +446,75 @@ namespace VNLib.Utils.Extensions
return Parent.WriteFileAsync(path, data, contentType, cancellation);
}
}
+
+ /// <summary>
+ /// The maximum buffer size to use when copying files
+ /// </summary>
+ public const long MaxCopyBufferSize = 0x10000; //64k
+
+ /// <summary>
+ /// The minimum buffer size to use when copying files
+ /// </summary>
+ public const long MinCopyBufferSize = 0x1000; //4k
+
+ /// <summary>
+ /// Creates a new <see cref="ISimpleFilesystem"/> wrapper for the given <see cref="IsolatedStorageDirectory"/>
+ /// <para>
+ /// Buffers are clamped to <see cref="MaxCopyBufferSize"/> and <see cref="MinCopyBufferSize"/>
+ /// </para>
+ /// </summary>
+ /// <param name="dir"></param>
+ /// <returns>A <see cref="ISimpleFilesystem"/> wrapper around the <see cref="IsolatedStorageDirectory"/></returns>
+ public static ISimpleFilesystem CreateSimpleFs(this IsolatedStorageDirectory dir) => new IsolatedStorageSimpleFs(dir);
+
+ private sealed record class IsolatedStorageSimpleFs(IsolatedStorageDirectory Directory) : ISimpleFilesystem
+ {
+ ///<inheritdoc/>
+ public string GetExternalFilePath(string filePath) => Directory.GetFullFilePath(filePath);
+
+ ///<inheritdoc/>
+ public Task DeleteFileAsync(string filePath, CancellationToken cancellation)
+ {
+ Directory.DeleteFile(filePath);
+ return Task.CompletedTask;
+ }
+
+ ///<inheritdoc/>
+ public async Task WriteFileAsync(string filePath, Stream data, string contentType, CancellationToken cancellation)
+ {
+ //For when I forget and increase the buffer size
+ Debug.Assert(MaxCopyBufferSize < int.MaxValue, "MaxCopyBufferSize is too large to be cast to an int");
+
+ //Try to calculate a buffer size
+ long bufferSize = data.CanSeek ? Math.Clamp(data.Length, MinCopyBufferSize, MaxCopyBufferSize) : MinCopyBufferSize;
+
+ //Open the local file
+ await using IsolatedStorageFileStream output = Directory.OpenFile(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
+
+ //Copy file
+ await CopyToAsync(data, output, (int)bufferSize, Shared, cancellation);
+
+ //All done
+ }
+
+ ///<inheritdoc/>
+ public async Task<long> ReadFileAsync(string filePath, Stream output, CancellationToken cancellation)
+ {
+ //For when I forget and increase the buffer size
+ Debug.Assert(MaxCopyBufferSize < int.MaxValue, "MaxCopyBufferSize is too large to be cast to an int");
+
+ //Open the local file
+ await using IsolatedStorageFileStream local = Directory.OpenFile(filePath, FileMode.Open, FileAccess.Read);
+
+ //Try to calculate a buffer size
+ long bufferSize = Math.Clamp(local.Length, MinCopyBufferSize, MaxCopyBufferSize);
+
+ //Copy file
+ await CopyToAsync(local, output, (int)bufferSize, Shared, cancellation);
+
+ return local.Length;
+ }
+
+ }
}
} \ No newline at end of file
diff --git a/lib/Utils/src/IO/IsolatedStorageDirectory.cs b/lib/Utils/src/IO/IsolatedStorageDirectory.cs
index c06018e..6ed9339 100644
--- a/lib/Utils/src/IO/IsolatedStorageDirectory.cs
+++ b/lib/Utils/src/IO/IsolatedStorageDirectory.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -35,18 +35,23 @@ namespace VNLib.Utils.IO
{
private readonly string DirectoryPath;
private readonly IsolatedStorageFile Storage;
+
/// <summary>
/// Creates a new <see cref="IsolatedStorageDirectory"/> within the specified file using the directory name.
/// </summary>
/// <param name="storage">A configured and open <see cref="IsolatedStorageFile"/></param>
/// <param name="dir">The directory name to open or create within the store</param>
+ /// <exception cref="ArgumentException"></exception>
public IsolatedStorageDirectory(IsolatedStorageFile storage, string dir)
{
- this.Storage = storage;
- this.DirectoryPath = dir;
+ Storage = storage ?? throw new ArgumentNullException(nameof(storage));
+ DirectoryPath = dir ?? throw new ArgumentNullException(nameof(dir));
+
//If the directory doesnt exist, create it
- if (!this.Storage.DirectoryExists(dir))
- this.Storage.CreateDirectory(dir);
+ if (!Storage.DirectoryExists(dir))
+ {
+ Storage.CreateDirectory(dir);
+ }
}
private IsolatedStorageDirectory(IsolatedStorageDirectory parent, string dirName)
@@ -54,9 +59,9 @@ namespace VNLib.Utils.IO
//Store ref to parent dir
Parent = parent;
//Referrence store
- this.Storage = parent.Storage;
+ Storage = parent.Storage;
//Add the name of this dir to the end of the specified dir path
- this.DirectoryPath = Path.Combine(parent.DirectoryPath, dirName);
+ DirectoryPath = Path.Combine(parent.DirectoryPath, dirName);
}
/// <summary>
@@ -67,19 +72,15 @@ namespace VNLib.Utils.IO
/// <exception cref="IsolatedStorageException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="DirectoryNotFoundException"></exception>
- public IsolatedStorageFileStream CreateFile(string fileName)
- {
- return this.Storage.CreateFile(Path.Combine(DirectoryPath, fileName));
- }
+ public IsolatedStorageFileStream CreateFile(string fileName) => Storage.CreateFile(GetFullFilePath(fileName));
+
/// <summary>
/// Removes a file from the current directory
/// </summary>
/// <param name="fileName">The path of the file to remove</param>
/// <exception cref="IsolatedStorageException"></exception>
- public void DeleteFile(string fileName)
- {
- this.Storage.DeleteFile(Path.Combine(this.DirectoryPath, fileName));
- }
+ public void DeleteFile(string fileName) => Storage.DeleteFile(GetFullFilePath(fileName));
+
/// <summary>
/// Opens a file that exists within the current directory
/// </summary>
@@ -87,10 +88,9 @@ namespace VNLib.Utils.IO
/// <param name="mode">File mode</param>
/// <param name="access">File access</param>
/// <returns>The open <see cref="IsolatedStorageFileStream"/> from the current directory</returns>
- public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access)
- {
- return this.Storage.OpenFile(Path.Combine(DirectoryPath, fileName), mode, access);
- }
+ public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access)
+ => Storage.OpenFile(GetFullFilePath(fileName), mode, access);
+
/// <summary>
/// Opens a file that exists within the current directory
/// </summary>
@@ -99,10 +99,8 @@ namespace VNLib.Utils.IO
/// <param name="access">File access</param>
/// <param name="share">The file shareing mode</param>
/// <returns>The open <see cref="IsolatedStorageFileStream"/> from the current directory</returns>
- public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share)
- {
- return this.Storage.OpenFile(Path.Combine(DirectoryPath, fileName), mode, access, share);
- }
+ public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share)
+ => Storage.OpenFile(GetFullFilePath(fileName), mode, access, share);
/// <summary>
/// Determiens if the specified file path refers to an existing file within the directory
@@ -113,25 +111,22 @@ namespace VNLib.Utils.IO
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="IsolatedStorageException"></exception>
/// <exception cref="InvalidOperationException"></exception>
- public bool FileExists(string fileName)
- {
- return this.Storage.FileExists(Path.Combine(this.DirectoryPath, fileName));
- }
+ public bool FileExists(string fileName) => Storage.FileExists(GetFullFilePath(fileName));
/// <summary>
/// Removes the directory and its contents from the store
/// </summary>
- public override void Remove()
- {
- Storage.DeleteDirectory(this.DirectoryPath);
- }
+ public override void Remove() => Storage.DeleteDirectory(DirectoryPath);
///<inheritdoc/>
public override long AvailableFreeSpace => Storage.AvailableFreeSpace;
+
///<inheritdoc/>
public override long Quota => Storage.Quota;
+
///<inheritdoc/>
public override long UsedSize => Storage.UsedSize;
+
///<inheritdoc/>
public override bool IncreaseQuotaTo(long newQuotaSize) => Storage.IncreaseQuotaTo(newQuotaSize);
@@ -139,9 +134,7 @@ namespace VNLib.Utils.IO
/// The parent <see cref="IsolatedStorageDirectory"/> this directory is a child within. null if there are no parent directories
/// above this dir
/// </summary>
-
public IsolatedStorageDirectory? Parent { get; }
-#nullable disable
/// <summary>
/// Creates a child directory within the current directory
@@ -150,9 +143,23 @@ namespace VNLib.Utils.IO
/// <returns>A new <see cref="IsolatedStorageDirectory"/> for which <see cref="IsolatedStorageFileStream"/>s can be opened/created</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
- public IsolatedStorageDirectory CreateChildDirectory(string directoryName)
+ public IsolatedStorageDirectory CreateChildDirectory(string directoryName) => new (this, directoryName);
+
+ /// <summary>
+ /// Gets the IsolatedStorage file path localized to the current directory, including th path
+ /// of any parent directories. NOTE: it's not possible to get the full filesystem path due to
+ /// isolated storage security restrictions.
+ /// </summary>
+ /// <param name="filePath">
+ /// The relative path to the file within the directory to recover the file path from
+ /// </param>
+ /// <returns>The localized relative file path within the current directory</returns>
+ /// <exception cref="ArgumentException"></exception>
+ public string GetFullFilePath(string filePath)
{
- return new IsolatedStorageDirectory(this, directoryName);
+ return Path.IsPathRooted(filePath)
+ ? throw new ArgumentException("The file path may not be fully rooted, it must be a relative", nameof(filePath))
+ : Path.Combine(DirectoryPath, filePath);
}
}
} \ No newline at end of file
diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs
index d269134..97cef03 100644
--- a/lib/Utils/src/IO/VnMemoryStream.cs
+++ b/lib/Utils/src/IO/VnMemoryStream.cs
@@ -56,12 +56,25 @@ namespace VNLib.Utils.IO
/// <param name="readOnly">Should the stream be readonly?</param>
/// <exception cref="ArgumentException"></exception>
/// <returns>A <see cref="VnMemoryStream"/> wrapper to access the handle data</returns>
- public static VnMemoryStream ConsumeHandle(MemoryHandle<byte> handle, nint length, bool readOnly)
+ public static VnMemoryStream ConsumeHandle(MemoryHandle<byte> handle, nint length, bool readOnly) => FromHandle(handle, true, length, readOnly);
+
+ /// <summary>
+ /// Creates a new <see cref="VnMemoryStream"/> from the supplied memory handle
+ /// of the initial length. This function also accepts a value that indicates if this stream
+ /// owns the memory handle, which will cause it to be disposed when the stream is disposed.
+ /// </summary>
+ /// <param name="handle"><see cref="MemoryHandle{T}"/> to consume</param>
+ /// <param name="length">The initial length of the stream</param>
+ /// <param name="readOnly">Should the stream be readonly?</param>
+ /// <param name="ownsHandle">A value that indicates if the current stream owns the memory handle</param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <returns>A <see cref="VnMemoryStream"/> wrapper to access the handle data</returns>
+ public static VnMemoryStream FromHandle(MemoryHandle<byte> handle, bool ownsHandle, nint length, bool readOnly)
{
handle.ThrowIfClosed();
- return new VnMemoryStream(handle, length, readOnly, true);
+ return new VnMemoryStream(handle, length, readOnly, ownsHandle);
}
-
+
/// <summary>
/// Converts a writable <see cref="VnMemoryStream"/> to readonly to allow shallow copies
/// </summary>
@@ -258,6 +271,7 @@ namespace VNLib.Utils.IO
/// </para>
/// </summary>
public override bool CanRead => true;
+
/// <summary>
/// <inheritdoc/>
/// <para>
@@ -265,13 +279,16 @@ namespace VNLib.Utils.IO
/// </para>
/// </summary>
public override bool CanSeek => true;
+
/// <summary>
/// True unless the stream is (or has been converted to) a readonly
/// stream.
/// </summary>
public override bool CanWrite => !_isReadonly;
+
///<inheritdoc/>
public override long Length => _length;
+
///<inheritdoc/>
public override bool CanTimeout => false;
@@ -281,6 +298,7 @@ namespace VNLib.Utils.IO
get => _position;
set => Seek(value, SeekOrigin.Begin);
}
+
/// <summary>
/// Closes the stream and frees the internal allocated memory blocks
/// </summary>
@@ -292,13 +310,17 @@ namespace VNLib.Utils.IO
_buffer.Dispose();
}
}
+
///<inheritdoc/>
public override void Flush() { }
+
// Override to reduce base class overhead
///<inheritdoc/>
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+
///<inheritdoc/>
public override int Read(byte[] buffer, int offset, int count) => Read(buffer.AsSpan(offset, count));
+
///<inheritdoc/>
public override int Read(Span<byte> buffer)
{
@@ -317,6 +339,7 @@ namespace VNLib.Utils.IO
return bytesToRead;
}
+
///<inheritdoc/>
public override unsafe int ReadByte()
{
@@ -346,6 +369,7 @@ namespace VNLib.Utils.IO
int read = Read(buffer.Span);
return ValueTask.FromResult(read);
}
+
///<inheritdoc/>
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
@@ -353,6 +377,7 @@ namespace VNLib.Utils.IO
int read = Read(buffer.AsSpan(offset, count));
return Task.FromResult(read);
}
+
///<inheritdoc/>
public override long Seek(long offset, SeekOrigin origin)
{
@@ -384,8 +409,7 @@ namespace VNLib.Utils.IO
default:
throw new ArgumentException("Stream operation is not supported on current stream");
}
- }
-
+ }
/// <summary>
/// Resizes the internal buffer to the exact size (in bytes) of the
@@ -423,8 +447,10 @@ namespace VNLib.Utils.IO
//Make sure the position is not pointing outside of the buffer after resize
_position = Math.Min(_position, _length);
}
+
///<inheritdoc/>
public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count));
+
///<inheritdoc/>
public override void Write(ReadOnlySpan<byte> buffer)
{
@@ -447,6 +473,7 @@ namespace VNLib.Utils.IO
//Update the position
_position = newPos;
}
+
///<inheritdoc/>
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
@@ -454,6 +481,7 @@ namespace VNLib.Utils.IO
Write(buffer, offset, count);
return Task.CompletedTask;
}
+
///<inheritdoc/>
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
@@ -461,6 +489,7 @@ namespace VNLib.Utils.IO
Write(buffer.Span);
return ValueTask.CompletedTask;
}
+
///<inheritdoc/>
public override void WriteByte(byte value)
{
@@ -504,19 +533,5 @@ namespace VNLib.Utils.IO
/// <exception cref="NotSupportedException"></exception>
public object Clone() => GetReadonlyShallowCopy();
- /*
- * Override the Dispose async method to avoid the base class overhead
- * and task allocation since this will always be a syncrhonous
- * operation (freeing memory)
- */
-
- ///<inheritdoc/>
- public override ValueTask DisposeAsync()
- {
- //Dispose and return completed task
- base.Dispose(true);
- GC.SuppressFinalize(this);
- return ValueTask.CompletedTask;
- }
}
} \ No newline at end of file
diff --git a/lib/Utils/src/IO/VnStreamWriter.cs b/lib/Utils/src/IO/VnStreamWriter.cs
index 03b0a6e..5ec65fc 100644
--- a/lib/Utils/src/IO/VnStreamWriter.cs
+++ b/lib/Utils/src/IO/VnStreamWriter.cs
@@ -33,6 +33,8 @@ using System.Runtime.CompilerServices;
using VNLib.Utils.Memory;
+#pragma warning disable CA2215, CA2007, CS8765 // Dispose methods should call base class dispose
+
namespace VNLib.Utils.IO
{
/// <summary>
diff --git a/lib/Utils/src/Memory/HeapCreation.cs b/lib/Utils/src/Memory/HeapCreation.cs
index 2d30c29..9ef9fdb 100644
--- a/lib/Utils/src/Memory/HeapCreation.cs
+++ b/lib/Utils/src/Memory/HeapCreation.cs
@@ -49,6 +49,6 @@ namespace VNLib.Utils.Memory
/// <summary>
/// Specifies that the requested heap will be a shared heap for the process/library
/// </summary>
- IsSharedHeap = 0x04
+ Shared = 0x04
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 6af2392..8cc9736 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -109,8 +109,8 @@ namespace VNLib.Utils.Memory
_ = ERRNO.TryParse(Environment.GetEnvironmentVariable(SHARED_HEAP_ENABLE_DIAGNOISTICS_ENV), out ERRNO diagEnable);
_ = ERRNO.TryParse(Environment.GetEnvironmentVariable(SHARED_HEAP_GLOBAL_ZERO), out ERRNO globalZero);
- Trace.WriteIf(diagEnable, "Shared heap diagnostics enabled");
- Trace.WriteIf(globalZero, "Shared heap global zero enabled");
+ Trace.WriteLineIf(diagEnable, "Shared heap diagnostics enabled");
+ Trace.WriteLineIf(globalZero, "Shared heap global zero enabled");
Lazy<IUnmangedHeap> heap = new (() => InitHeapInternal(true, diagEnable, globalZero), LazyThreadSafetyMode.PublicationOnly);
@@ -173,7 +173,7 @@ namespace VNLib.Utils.Memory
* The heap impl may reset the synchronziation flag if it does not
* need serialziation
*/
- cFlags |= isShared ? HeapCreation.IsSharedHeap : HeapCreation.None;
+ cFlags |= isShared ? HeapCreation.Shared : HeapCreation.None;
//Set global zero flag if requested
cFlags |= globalZero ? HeapCreation.GlobalZero : HeapCreation.None;
diff --git a/lib/Utils/src/Memory/NativeHeap.cs b/lib/Utils/src/Memory/NativeHeap.cs
index 4b76866..7a3d4dd 100644
--- a/lib/Utils/src/Memory/NativeHeap.cs
+++ b/lib/Utils/src/Memory/NativeHeap.cs
@@ -177,9 +177,9 @@ namespace VNLib.Utils.Memory
{
public IntPtr HeapPointer;
- public HeapCreation CreationFlags;
-
public ERRNO Flags;
+
+ public HeapCreation CreationFlags;
}
readonly record struct HeapMethods
diff --git a/lib/Utils/src/Memory/ProcessHeap.cs b/lib/Utils/src/Memory/ProcessHeap.cs
index 84c9e0e..3d581cd 100644
--- a/lib/Utils/src/Memory/ProcessHeap.cs
+++ b/lib/Utils/src/Memory/ProcessHeap.cs
@@ -45,11 +45,11 @@ namespace VNLib.Utils.Memory
/// <summary>
/// <inheritdoc/>
/// <para>
- /// Is always <see cref="HeapCreation.IsSharedHeap"/> as this heap is the default
+ /// Is always <see cref="HeapCreation.Shared"/> as this heap is the default
/// process heap. Meaining memory will be shared across the process
/// </para>
/// </summary>
- public HeapCreation CreationFlags { get; } = HeapCreation.IsSharedHeap;
+ public HeapCreation CreationFlags { get; } = HeapCreation.Shared;
/// <summary>
/// Initalizes a new global (cross platform) process heap
diff --git a/lib/Utils/src/Memory/Win32PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs
index 0a00a77..41fe33a 100644
--- a/lib/Utils/src/Memory/Win32PrivateHeap.cs
+++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs
@@ -110,7 +110,7 @@ namespace VNLib.Utils.Memory
/// </remarks>
public static Win32PrivateHeap Create(nuint initialSize, HeapCreation cFlags, nuint maxHeapSize = 0, DWORD flags = HEAP_NO_FLAGS)
{
- if (cFlags.HasFlag(HeapCreation.IsSharedHeap))
+ if (cFlags.HasFlag(HeapCreation.Shared))
{
//Clear the synchronization flag because we don't need it for a process heap
cFlags &= ~HeapCreation.UseSynchronization;
diff --git a/lib/Utils/src/VnEncoding.cs b/lib/Utils/src/VnEncoding.cs
index e945135..e24f7df 100644
--- a/lib/Utils/src/VnEncoding.cs
+++ b/lib/Utils/src/VnEncoding.cs
@@ -51,10 +51,15 @@ namespace VNLib.Utils
/// </summary>
/// <param name="data">Data to be encoded</param>
/// <param name="encoding"><see cref="Encoding"/> to encode data with</param>
+ /// <param name="heap">Heap to allocate memory from</param>
/// <returns>A <see cref="Stream"/> contating the encoded data</returns>
- public static VnMemoryStream GetMemoryStream(ReadOnlySpan<char> data, Encoding encoding)
+ public static VnMemoryStream GetMemoryStream(ReadOnlySpan<char> data, Encoding encoding, IUnmangedHeap? heap = null)
{
_ = encoding ?? throw new ArgumentNullException(nameof(encoding));
+
+ //Assign default heap if not specified
+ heap ??= MemoryUtil.Shared;
+
//Create new memory handle to copy data to
MemoryHandle<byte>? handle = null;
try
@@ -62,7 +67,7 @@ namespace VNLib.Utils
//get number of bytes
int byteCount = encoding.GetByteCount(data);
//resize the handle to fit the data
- handle = MemoryUtil.Shared.Alloc<byte>(byteCount);
+ handle = heap.Alloc<byte>(byteCount);
//encode
int size = encoding.GetBytes(data, handle);
//Consume the handle into a new vnmemstream and return it