123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Threading;
- using Best.HTTP.Shared.Extensions;
- using Best.HTTP.Shared.PlatformSupport.Memory;
- using Best.HTTP.Shared.PlatformSupport.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();
- this.DiskManager.Dispose();
- this.rwlock.Dispose();
- GC.SuppressFinalize(this);
- }
- }
- }
|