/* * Copyright (c) 2022 Vaughn Nugent * * Library: VNLib * Package: VNLib.Data.Caching.ObjectCache * File: BlobCache.cs * * BlobCache.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.Linq; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using VNLib.Utils.IO; using VNLib.Utils.Logging; using VNLib.Utils.Memory; using VNLib.Utils.Memory.Caching; namespace VNLib.Data.Caching { /// /// A general purpose binary data storage /// public class BlobCache : LRUCache> { readonly IUnmangedHeap Heap; readonly DirectoryInfo SwapDir; readonly ILogProvider Log; /// public override bool IsReadOnly { get; } /// protected override int MaxCapacity { get; } /// /// Initializes a new store /// /// The to swap blob data to when cache /// The maximum number of items to keep in memory /// A to write log data to /// A to allocate buffers and store data in memory public BlobCache(DirectoryInfo swapDir, int maxCapacity, ILogProvider log, IUnmangedHeap heap) :base(StringComparer.Ordinal) { IsReadOnly = false; MaxCapacity = maxCapacity; SwapDir = swapDir; //Update the lookup table size LookupTable.EnsureCapacity(maxCapacity); //Set default heap if not specified Heap = heap; Log = log; } /// protected override bool CacheMiss(string key, [NotNullWhen(true)] out MemoryHandle? value) { value = null; return false; } /// protected override void Evicted(KeyValuePair> evicted) { //Dispose the blob evicted.Value.Dispose(); } /// /// If the is found in the store, changes the key /// that referrences the blob. /// /// The key that currently referrences the blob in the store /// The new key that will referrence the blob /// The if its found in the store /// True if the record was found and the key was changes public bool TryChangeKey(string currentKey, string newKey, [NotNullWhen(true)] out MemoryHandle? blob) { if (LookupTable.Remove(currentKey, out LinkedListNode>>? node)) { //Remove the node from the ll List.Remove(node); //Update the node kvp blob = node.Value.Value; node.Value = new KeyValuePair>(newKey, blob); //Add to end of list List.AddLast(node); //Re-add to lookup table with new key LookupTable.Add(newKey, node); return true; } blob = null; return false; } /// /// Removes the from the store without disposing the blobl /// /// The key that referrences the in the store /// A value indicating if the blob was removed public override bool Remove(string key) { //Remove the item from the lookup table and if it exists, remove the node from the list if (LookupTable.Remove(key, out LinkedListNode>>? node)) { //Remove the new from the list List.Remove(node); //dispose the buffer node.Value.Value.Dispose(); return true; } return false; } /// /// Removes and disposes all blobl elements in cache (or in the backing store) /// public override void Clear() { foreach (MemoryHandle blob in List.Select(kp => kp.Value)) { blob.Dispose(); } base.Clear(); } } }