/*
* Copyright (c) 2022 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Data.Caching.ObjectCache
* File: BlobItem.cs
*
* BlobItem.cs is part of VNLib.Data.Caching.ObjectCache which is part of the larger
* VNLib collection of libraries and utilities.
*
* VNLib.Data.Caching.ObjectCache is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* VNLib.Data.Caching.ObjectCache 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using VNLib.Utils;
using VNLib.Utils.IO;
using VNLib.Utils.Memory;
using VNLib.Utils.Logging;
using VNLib.Utils.Extensions;
namespace VNLib.Data.Caching
{
///
/// A general purpose binary storage item
///
public class BlobItem //: VnDisposeable
{
/*
private static readonly JoinableTaskContext JTX = new();
private static readonly Semaphore CentralSwapLock = new(Environment.ProcessorCount, Environment.ProcessorCount);
private readonly VnMemoryStream _loadedData;
private bool _loaded;
///
/// The time the blob was last modified
///
public DateTimeOffset LastAccessed { get; private set; }
///
/// Gets the current size of the file (in bytes) as an atomic operation
///
public int FileSize => (int)_loadedData.Length;
///
/// The operation synchronization lock
///
public AsyncReaderWriterLock OpLock { get; }
///
/// Initializes a new
///
/// The heap to allocate buffers from
internal BlobItem(IUnmangedHeap heap)
{
_loadedData = new(heap);
OpLock = new AsyncReaderWriterLock(JTX);
_loaded = true;
LastAccessed = DateTimeOffset.UtcNow;
}
///
protected override void Free()
{
_loadedData.Dispose();
OpLock.Dispose();
}
///
/// Reads data from the internal buffer and copies it to the specified buffer.
/// Use the property to obtain the size of the internal buffer
///
/// The buffer to copy data to
/// When completed, the number of bytes copied to the buffer
public int Read(Span buffer)
{
//Make sure the blob has been swapped back into memory
if (!_loaded)
{
throw new InvalidOperationException("The blob was not loaded from the disk");
}
//Read all data from the buffer and write it to the output buffer
_loadedData.AsSpan().CopyTo(buffer);
//Update last-accessed
LastAccessed = DateTimeOffset.UtcNow;
return (int)_loadedData.Length;
}
///
/// Overwrites the internal buffer with the contents of the supplied buffer
///
/// The buffer containing data to store within the blob
/// A that completes when write access has been granted and copied
///
public void Write(ReadOnlySpan buffer)
{
//Make sure the blob has been swapped back into memory
if (!_loaded)
{
throw new InvalidOperationException("The blob was not loaded from the disk");
}
//Reset the buffer
_loadedData.SetLength(buffer.Length);
_loadedData.Seek(0, SeekOrigin.Begin);
_loadedData.Write(buffer);
LastAccessed = DateTimeOffset.UtcNow;
}
///
/// Writes the contents of the memory buffer to its designated file on the disk
///
/// The heap to allocate buffers from
/// The that stores the file
/// The name of the file to write data do
/// A log to write errors to
/// A task that completes when the swap to disk is complete
internal async Task SwapToDiskAsync(IUnmangedHeap heap, DirectoryInfo swapDir, string filename, ILogProvider log)
{
try
{
//Wait for write lock
await using (AsyncReaderWriterLock.Releaser releaser = await OpLock.WriteLockAsync())
{
//Enter swap lock
await CentralSwapLock;
try
{
//Open swap file data stream
await using FileStream swapFile = swapDir.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, bufferSize: 8128);
//reset swap file
swapFile.SetLength(0);
//Seek loaded-data back to 0 before writing
_loadedData.Seek(0, SeekOrigin.Begin);
//Write loaded data to disk
await _loadedData.CopyToAsync(swapFile, 8128, heap);
}
finally
{
CentralSwapLock.Release();
}
//Release memory held by stream
_loadedData.SetLength(0);
//Clear loaded flag
_loaded = false;
LastAccessed = DateTimeOffset.UtcNow;
}
log.Debug("Blob {name} swapped to disk", filename);
}
catch(Exception ex)
{
log.Error(ex, "Blob swap to disk error");
}
}
///
/// Reads the contents of the blob into a memory buffer from its designated file on disk
///
/// The heap to allocate buffers from
/// The that stores the file
/// The name of the file to write the blob data to
/// A log to write errors to
/// A task that completes when the swap from disk is complete
internal async Task SwapFromDiskAsync(IUnmangedHeap heap, DirectoryInfo swapDir, string filename, ILogProvider log)
{
try
{
//Wait for write lock
await using (AsyncReaderWriterLock.Releaser releaser = await OpLock.WriteLockAsync())
{
//Enter swap lock
await CentralSwapLock;
try
{
//Open swap file data stream
await using FileStream swapFile = swapDir.OpenFile(filename, FileMode.OpenOrCreate, FileAccess.Read, bufferSize:8128);
//Copy from disk to memory
await swapFile.CopyToAsync(_loadedData, 8128, heap);
}
finally
{
CentralSwapLock.Release();
}
//Set loaded flag
_loaded = true;
LastAccessed = DateTimeOffset.UtcNow;
}
log.Debug("Blob {name} swapped from disk", filename);
}
catch(Exception ex)
{
log.Error(ex, "Blob swap from disk error");
}
}
*/
}
}