add all
This commit is contained in:
@@ -0,0 +1,260 @@
|
||||
using Best.HTTP.Shared.Extensions;
|
||||
using Best.HTTP.Shared.PlatformSupport.Memory;
|
||||
using Best.HTTP.Shared.PlatformSupport.Threading;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Best.HTTP.Shared.Databases
|
||||
{
|
||||
public sealed class FolderAndFileOptions
|
||||
{
|
||||
public string FolderName = "Best.HTTP.Shared.Databases";
|
||||
public string DatabaseFolderName = "Databases";
|
||||
public string MetadataExtension = "metadata";
|
||||
public string DatabaseExtension = "db";
|
||||
public string DatabaseFreeListExtension = "freelist";
|
||||
public string HashExtension = "hash";
|
||||
}
|
||||
|
||||
public abstract class Database<ContentType, MetadataType, IndexingServiceType, MetadataServiceType> : IDisposable, IHeartbeat
|
||||
where MetadataType : Metadata, new()
|
||||
where IndexingServiceType : IndexingService<ContentType, MetadataType>
|
||||
where MetadataServiceType : MetadataService<MetadataType, ContentType>
|
||||
{
|
||||
public static FolderAndFileOptions FolderAndFileOptions = new FolderAndFileOptions();
|
||||
|
||||
public string SaveDir { get; private set; }
|
||||
public string Name { get { return this.Options.Name; } }
|
||||
|
||||
public string MetadataFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.MetadataExtension); } }
|
||||
public string DatabaseFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.DatabaseExtension); } }
|
||||
public string DatabaseFreeListFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.DatabaseFreeListExtension); } }
|
||||
public string HashFileName { get { return Path.ChangeExtension(Path.Combine(this.SaveDir, this.Name), FolderAndFileOptions.HashExtension); } }
|
||||
|
||||
public MetadataServiceType MetadataService { get; private set; }
|
||||
|
||||
protected DatabaseOptions Options { get; private set; }
|
||||
protected IndexingServiceType IndexingService { get; private set; }
|
||||
protected DiskManager<ContentType> DiskManager { get; private set; }
|
||||
|
||||
protected int isDirty = 0;
|
||||
|
||||
protected ReaderWriterLockSlim rwlock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
|
||||
|
||||
public Database(string directory,
|
||||
DatabaseOptions options,
|
||||
IndexingServiceType indexingService,
|
||||
IDiskContentParser<ContentType> diskContentParser,
|
||||
MetadataServiceType metadataService)
|
||||
{
|
||||
this.SaveDir = directory;
|
||||
this.Options = options;
|
||||
this.IndexingService = indexingService;
|
||||
this.MetadataService = metadataService;
|
||||
|
||||
var dir = Path.GetDirectoryName(this.DatabaseFileName);
|
||||
if (!HTTPManager.IOService.DirectoryExists(dir))
|
||||
HTTPManager.IOService.DirectoryCreate(dir);
|
||||
|
||||
this.DiskManager = new DiskManager<ContentType>(
|
||||
HTTPManager.IOService.CreateFileStream(this.DatabaseFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.OpenReadWrite),
|
||||
HTTPManager.IOService.CreateFileStream(this.DatabaseFreeListFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.OpenReadWrite),
|
||||
diskContentParser,
|
||||
options.DiskManager);
|
||||
|
||||
using (var fileStream = HTTPManager.IOService.CreateFileStream(this.MetadataFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.OpenReadWrite))
|
||||
using (var stream = new BufferedStream(fileStream))
|
||||
this.MetadataService.LoadFrom(stream);
|
||||
}
|
||||
|
||||
public int Clear()
|
||||
{
|
||||
using (new WriteLock(this.rwlock))
|
||||
{
|
||||
int count = this.MetadataService.Metadatas.Count;
|
||||
|
||||
this.IndexingService.Clear();
|
||||
this.DiskManager.Clear();
|
||||
this.MetadataService.Clear();
|
||||
FlagDirty(1);
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public int Delete(IEnumerable<MetadataType> metadatas)
|
||||
{
|
||||
if (metadatas == null)
|
||||
return 0;
|
||||
|
||||
using (new WriteLock(this.rwlock))
|
||||
{
|
||||
int deletedCount = 0;
|
||||
foreach (var metadata in metadatas)
|
||||
if (DeleteMetadata(metadata))
|
||||
deletedCount++;
|
||||
|
||||
FlagDirty(deletedCount);
|
||||
|
||||
return deletedCount;
|
||||
}
|
||||
}
|
||||
|
||||
public int Delete(IEnumerable<int> metadataIndexes)
|
||||
{
|
||||
if (metadataIndexes == null)
|
||||
return 0;
|
||||
|
||||
using (new WriteLock(this.rwlock))
|
||||
{
|
||||
int deletedCount = 0;
|
||||
foreach (int idx in metadataIndexes)
|
||||
{
|
||||
var metadata = this.MetadataService.Metadatas[idx];
|
||||
|
||||
if (DeleteMetadata(metadata))
|
||||
deletedCount++;
|
||||
}
|
||||
|
||||
FlagDirty(deletedCount);
|
||||
|
||||
return deletedCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool DeleteMetadata(MetadataType metadata)
|
||||
{
|
||||
if (metadata.Length > 0)
|
||||
this.DiskManager.Delete(metadata);
|
||||
this.MetadataService.Remove(metadata);
|
||||
FlagDirty(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the first content from the metadata indexes.
|
||||
/// </summary>
|
||||
public ContentType FromFirstMetadataIndex(IEnumerable<int> metadataIndexes)
|
||||
{
|
||||
if (metadataIndexes == null)
|
||||
return default;
|
||||
|
||||
var index = metadataIndexes.DefaultIfEmpty(-1).First();
|
||||
if (index < 0)
|
||||
return default;
|
||||
|
||||
return FromMetadataIndex(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the content from the metadata index.
|
||||
/// </summary>
|
||||
public ContentType FromMetadataIndex(int metadataIndex)
|
||||
{
|
||||
if (metadataIndex < 0 || metadataIndex >= this.MetadataService.Metadatas.Count)
|
||||
return default;
|
||||
|
||||
//using (new ReadLock(this.rwlock))
|
||||
{
|
||||
var metadata = this.MetadataService.Metadatas[metadataIndex];
|
||||
return this.DiskManager.Load(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
public ContentType FromMetadata(MetadataType metadata) => this.DiskManager.Load(metadata);
|
||||
|
||||
/// <summary>
|
||||
/// Loads all content from the metadatas.
|
||||
/// </summary>
|
||||
public IEnumerable<ContentType> FromMetadatas(IEnumerable<MetadataType> metadatas) => FromMetadataIndexes(from m in metadatas select m.Index);
|
||||
|
||||
/// <summary>
|
||||
/// Loads all content from the metadata indexes.
|
||||
/// </summary>
|
||||
public IEnumerable<ContentType> FromMetadataIndexes(IEnumerable<int> metadataIndexes)
|
||||
{
|
||||
if (metadataIndexes == null)
|
||||
yield break;
|
||||
|
||||
//using (new ReadLock(this.rwlock))
|
||||
{
|
||||
foreach (int metadataIndex in metadataIndexes)
|
||||
{
|
||||
var metadata = this.MetadataService.Metadatas[metadataIndex];
|
||||
var content = this.DiskManager.Load(metadata);
|
||||
//result.Add(content);
|
||||
|
||||
yield return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void FlagDirty(int dirty)
|
||||
{
|
||||
if (dirty != 0 && Interlocked.CompareExchange(ref this.isDirty, dirty, 0) == 0)
|
||||
HTTPManager.Heartbeats.Subscribe(this);
|
||||
}
|
||||
|
||||
public bool Save()
|
||||
{
|
||||
if (!this.rwlock.TryEnterWriteLock(TimeSpan.FromMilliseconds(0)))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
int itWasDirty = Interlocked.CompareExchange(ref this.isDirty, 0, 1);
|
||||
if (itWasDirty == 0)
|
||||
return true;
|
||||
|
||||
using (var fileStream = HTTPManager.IOService.CreateFileStream(this.MetadataFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.Create))
|
||||
using (var stream = new BufferedStream(fileStream))
|
||||
this.MetadataService.SaveTo(stream);
|
||||
|
||||
if (this.Options.UseHashFile)
|
||||
{
|
||||
using (var hashStream = HTTPManager.IOService.CreateFileStream(this.HashFileName, Best.HTTP.Shared.PlatformSupport.FileSystem.FileStreamModes.Create))
|
||||
{
|
||||
var hash = this.DiskManager.CalculateHash();
|
||||
hashStream.Write(hash.Data, 0, hash.Count);
|
||||
BufferPool.Release(hash);
|
||||
}
|
||||
}
|
||||
|
||||
this.DiskManager.Save();
|
||||
|
||||
Interlocked.Exchange(ref this.isDirty, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.rwlock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
void IHeartbeat.OnHeartbeatUpdate(DateTime now, TimeSpan dif)
|
||||
{
|
||||
if (this.Save())
|
||||
HTTPManager.Heartbeats.Unsubscribe(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Save();
|
||||
HTTPManager.Heartbeats.Unsubscribe(this);
|
||||
|
||||
this.DiskManager.Dispose();
|
||||
this.DiskManager = null;
|
||||
|
||||
this.rwlock.Dispose();
|
||||
this.rwlock = null;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user