From 83992dc334c5ec59cfa1a961d8d43e75b36ec897 Mon Sep 17 00:00:00 2001 From: mbdavid Date: Mon, 25 Dec 2017 22:16:53 -0200 Subject: [PATCH] Remove multi disk implementation and keep only stream --- LiteDB.Demo/LiteDB_Paging.cs | 4 +- LiteDB.Tests/Concurrency/Locker_Tests.cs | 6 +- LiteDB.Tests/Concurrency/Thread_Tests.cs | 2 +- .../{Engine => Document}/Json_Tests.cs | 0 .../{Engine => Document}/ObjectId_Tests.cs | 0 LiteDB.Tests/Engine/Bulk_Insert_Tests.cs | 2 +- LiteDB.Tests/Engine/DropCollection_Tests.cs | 3 +- LiteDB.Tests/Engine/Encrypted_Tests.cs | 8 +- LiteDB.Tests/Engine/Engine_Tests.cs | 9 +- LiteDB.Tests/Engine/IndexOrder_Tests.cs | 3 +- LiteDB.Tests/Engine/MemoryStream_Tests.cs | 4 +- LiteDB.Tests/Engine/Performance_Tests.cs | 2 +- LiteDB.Tests/Engine/Query_Tests.cs | 5 +- LiteDB.Tests/Engine/ReadOnly_Tests.cs | 2 +- LiteDB.Tests/Engine/Shrink_Tests.cs | 4 +- LiteDB.Tests/Utils/LoremIpsum.cs | 42 +++++ LiteDB.Tests/Utils/TempFile.cs | 70 +------ LiteDB/Engine/Disks/FileDiskService.cs | 174 ------------------ LiteDB/Engine/Disks/IDiskService.cs | 39 ---- LiteDB/Engine/Disks/TempDiskService.cs | 160 ---------------- LiteDB/Engine/Engine/FindSort.cs | 3 +- LiteDB/Engine/Engine/Info.cs | 2 +- LiteDB/Engine/Engine/Shrink.cs | 9 +- LiteDB/Engine/LiteEngine.cs | 140 +++----------- LiteDB/Engine/Pages/HeaderPage.cs | 14 -- LiteDB/Engine/Services/CacheService.cs | 4 +- .../DiskService.cs} | 106 ++++++----- LiteDB/Engine/Services/LockService.cs | 4 +- LiteDB/Engine/Services/PageService.cs | 4 +- LiteDB/Engine/Services/TransactionService.cs | 22 +-- LiteDB/Utils/ConnectionString.cs | 63 ++++--- LiteDB/Utils/FileOptions.cs | 24 --- README.md | 13 +- 33 files changed, 220 insertions(+), 727 deletions(-) rename LiteDB.Tests/{Engine => Document}/Json_Tests.cs (100%) rename LiteDB.Tests/{Engine => Document}/ObjectId_Tests.cs (100%) create mode 100644 LiteDB.Tests/Utils/LoremIpsum.cs delete mode 100644 LiteDB/Engine/Disks/FileDiskService.cs delete mode 100644 LiteDB/Engine/Disks/IDiskService.cs delete mode 100644 LiteDB/Engine/Disks/TempDiskService.cs rename LiteDB/Engine/{Disks/StreamDiskService.cs => Services/DiskService.cs} (52%) delete mode 100644 LiteDB/Utils/FileOptions.cs diff --git a/LiteDB.Demo/LiteDB_Paging.cs b/LiteDB.Demo/LiteDB_Paging.cs index f01caf63e..f7289d423 100644 --- a/LiteDB.Demo/LiteDB_Paging.cs +++ b/LiteDB.Demo/LiteDB_Paging.cs @@ -20,9 +20,7 @@ public void Init() { File.Delete(filename); - var disk = new FileDiskService(filename); - - _engine = new LiteEngine(disk, cacheSize: 50000); + _engine = new LiteEngine(filename); } public void Populate(IEnumerable docs) diff --git a/LiteDB.Tests/Concurrency/Locker_Tests.cs b/LiteDB.Tests/Concurrency/Locker_Tests.cs index cb64f1763..0f1566f13 100644 --- a/LiteDB.Tests/Concurrency/Locker_Tests.cs +++ b/LiteDB.Tests/Concurrency/Locker_Tests.cs @@ -9,10 +9,10 @@ public class Locker_Tests [TestMethod] public void Loop_With_Update() { - using (var tmp = new TempFile()) + using (var file = new TempFile()) { // initialize database with 4 - using (var db = new LiteEngine(tmp.Filename)) + using (var db = new LiteEngine(file.Filename)) { db.Insert("col", new BsonDocument { { "Number", 1 } }, BsonType.Int32); db.Insert("col", new BsonDocument { { "Number", 2 } }, BsonType.Int32); @@ -21,7 +21,7 @@ public void Loop_With_Update() db.Insert("col", new BsonDocument { { "Number", 5 } }, BsonType.Int32); } - using (var db = new LiteEngine(tmp.Filename)) + using (var db = new LiteEngine(file.Filename)) { foreach (var doc in db.Find("col", Query.All(), 0, 1000)) { diff --git a/LiteDB.Tests/Concurrency/Thread_Tests.cs b/LiteDB.Tests/Concurrency/Thread_Tests.cs index c36530b7c..92bdb67d1 100644 --- a/LiteDB.Tests/Concurrency/Thread_Tests.cs +++ b/LiteDB.Tests/Concurrency/Thread_Tests.cs @@ -74,7 +74,7 @@ public void Thread_Insert_Update() { { "_id", i }, { "updated", true }, - { "name", TempFile.LoremIpsum(5, 10, 1, 5, 1) } + { "name", LoremIpsum.Generate(5, 10, 1, 5, 1) } }; if (db.Update("col", doc)) i++; diff --git a/LiteDB.Tests/Engine/Json_Tests.cs b/LiteDB.Tests/Document/Json_Tests.cs similarity index 100% rename from LiteDB.Tests/Engine/Json_Tests.cs rename to LiteDB.Tests/Document/Json_Tests.cs diff --git a/LiteDB.Tests/Engine/ObjectId_Tests.cs b/LiteDB.Tests/Document/ObjectId_Tests.cs similarity index 100% rename from LiteDB.Tests/Engine/ObjectId_Tests.cs rename to LiteDB.Tests/Document/ObjectId_Tests.cs diff --git a/LiteDB.Tests/Engine/Bulk_Insert_Tests.cs b/LiteDB.Tests/Engine/Bulk_Insert_Tests.cs index a54f477fe..52f9a64df 100644 --- a/LiteDB.Tests/Engine/Bulk_Insert_Tests.cs +++ b/LiteDB.Tests/Engine/Bulk_Insert_Tests.cs @@ -38,7 +38,7 @@ private IEnumerable GetDocs(int initial, int count, int type = 1) { "_id", i }, { "name", Guid.NewGuid().ToString() }, { "first", "John" }, - { "lorem", TempFile.LoremIpsum(3, 5, 2, 3, 3) } + { "lorem", LoremIpsum.Generate(3, 5, 2, 3, 3) } }; } } diff --git a/LiteDB.Tests/Engine/DropCollection_Tests.cs b/LiteDB.Tests/Engine/DropCollection_Tests.cs index 44d32b878..f2c94cf5e 100644 --- a/LiteDB.Tests/Engine/DropCollection_Tests.cs +++ b/LiteDB.Tests/Engine/DropCollection_Tests.cs @@ -10,8 +10,7 @@ public class DropCollection_Tests [TestMethod] public void DropCollection() { - using (var file = new TempFile()) - using (var db = new LiteEngine(file.Filename)) + using (var db = new LiteEngine()) { Assert.IsFalse(db.GetCollectionNames().Any(x => x == "col")); diff --git a/LiteDB.Tests/Engine/Encrypted_Tests.cs b/LiteDB.Tests/Engine/Encrypted_Tests.cs index 0191b3a6f..0e9ef4777 100644 --- a/LiteDB.Tests/Engine/Encrypted_Tests.cs +++ b/LiteDB.Tests/Engine/Encrypted_Tests.cs @@ -23,7 +23,7 @@ public void Encrypted_Database() Assert.IsTrue(plain.ReadAsText().Contains("Mauricio David")); // create a database with password - using (var db = new LiteEngine(encrypt.Filename, "abc123")) + using (var db = new LiteEngine(new ConnectionString { Filename = encrypt.Filename, Password = "abc123" })) { db.Insert("col", new BsonDocument { { "name", "Mauricio David" } }); } @@ -34,7 +34,7 @@ public void Encrypted_Database() // try access using wrong password try { - using (var db = new LiteEngine(encrypt.Filename, "abc1234")) + using (var db = new LiteEngine(new ConnectionString { Filename = encrypt.Filename, Password = "abc1234" })) { Assert.Fail(); // can't work } @@ -45,14 +45,14 @@ public void Encrypted_Database() } // open encrypted db and read document - using (var db = new LiteEngine(encrypt.Filename, "abc123")) + using (var db = new LiteEngine(new ConnectionString { Filename = encrypt.Filename, Password = "abc123" })) { var doc = db.Find("col", Query.All()).First(); Assert.AreEqual("Mauricio David", doc["name"].AsString); // let's remove password to work CheckIntegrety - db.Shrink(null, null); + db.Shrink(null); } } } diff --git a/LiteDB.Tests/Engine/Engine_Tests.cs b/LiteDB.Tests/Engine/Engine_Tests.cs index 8c9484432..ce839e1d5 100644 --- a/LiteDB.Tests/Engine/Engine_Tests.cs +++ b/LiteDB.Tests/Engine/Engine_Tests.cs @@ -37,8 +37,7 @@ public void Engine_Insert_Documents() [TestMethod] public void Engine_Upsert_Documents() { - using (var file = new TempFile()) - using (var db = new LiteEngine(file.Filename)) + using (var db = new LiteEngine()) { var doc1 = new BsonDocument { { "_id", 1 }, { "name", "John" } }; @@ -62,8 +61,7 @@ public void Engine_Upsert_Documents() [TestMethod] public void Engine_Delete_Documents() { - using (var file = new TempFile()) - using (var db = new LiteEngine(file.Filename)) + using (var db = new LiteEngine()) { var doc1 = new BsonDocument { { "_id", 1 }, { "name", "John" } }; var doc2 = new BsonDocument { { "_id", 2 }, { "name", "Doe" } }; @@ -79,8 +77,7 @@ public void Engine_Delete_Documents() public void Engine_QueryUpdate_Documents() { - using (var file = new TempFile()) - using (var db = new LiteEngine(file.Filename)) + using (var db = new LiteEngine()) { db.EnsureIndex("col", "name"); diff --git a/LiteDB.Tests/Engine/IndexOrder_Tests.cs b/LiteDB.Tests/Engine/IndexOrder_Tests.cs index 6b2ed224f..42b9478cc 100644 --- a/LiteDB.Tests/Engine/IndexOrder_Tests.cs +++ b/LiteDB.Tests/Engine/IndexOrder_Tests.cs @@ -10,8 +10,7 @@ public class IndexOrder_Tests [TestMethod] public void Index_Order() { - using (var tmp = new TempFile()) - using (var db = new LiteEngine(tmp.Filename)) + using (var db = new LiteEngine()) { db.Insert("col", new BsonDocument { { "text", "D" } }); db.Insert("col", new BsonDocument { { "text", "A" } }); diff --git a/LiteDB.Tests/Engine/MemoryStream_Tests.cs b/LiteDB.Tests/Engine/MemoryStream_Tests.cs index 5555afce1..c6d1c87a1 100644 --- a/LiteDB.Tests/Engine/MemoryStream_Tests.cs +++ b/LiteDB.Tests/Engine/MemoryStream_Tests.cs @@ -17,13 +17,13 @@ public void Engine_Using_MemoryStream() { var mem = new MemoryStream(); - using (var db = new LiteEngine(mem)) + using (var db = new LiteEngine(new ConnectionString(), mem)) { db.Insert("col", new BsonDocument { { "_id", 1 } , { "name", "John" } }); db.Insert("col", new BsonDocument { { "_id", 2 }, { "name", "Doe" } }); } - using (var db = new LiteEngine(mem)) + using (var db = new LiteEngine(new ConnectionString(), mem)) { var john = db.Find("col", Query.EQ("_id", 1)).FirstOrDefault(); var doe = db.Find("col", Query.EQ("_id", 2)).FirstOrDefault(); diff --git a/LiteDB.Tests/Engine/Performance_Tests.cs b/LiteDB.Tests/Engine/Performance_Tests.cs index 3a98b33a6..0301f60b2 100644 --- a/LiteDB.Tests/Engine/Performance_Tests.cs +++ b/LiteDB.Tests/Engine/Performance_Tests.cs @@ -64,7 +64,7 @@ private IEnumerable GetDocs(int count) { "_id", i }, { "name", Guid.NewGuid().ToString() }, { "type", rnd.Next(1, 100) }, - { "lorem", TempFile.LoremIpsum(3, 5, 2, 3, 3) } + { "lorem", LoremIpsum.Generate(3, 5, 2, 3, 3) } }; } } diff --git a/LiteDB.Tests/Engine/Query_Tests.cs b/LiteDB.Tests/Engine/Query_Tests.cs index a2bd84c9c..587ee6024 100644 --- a/LiteDB.Tests/Engine/Query_Tests.cs +++ b/LiteDB.Tests/Engine/Query_Tests.cs @@ -26,7 +26,7 @@ public void Query_Using_Fullscan_Search() public void ExecuteQuery(bool createIndex) { - using (var db = new LiteEngine(new MemoryStream())) + using (var db = new LiteEngine()) { db.Insert("col", new BsonDocument[] { @@ -83,8 +83,7 @@ public void ExecuteQuery(bool createIndex) [TestMethod] public void Query_Using_First_Linq() { - using (var file = new TempFile()) - using (var db = new LiteEngine(file.Filename)) + using (var db = new LiteEngine()) { db.Insert("col", new BsonDocument[] { diff --git a/LiteDB.Tests/Engine/ReadOnly_Tests.cs b/LiteDB.Tests/Engine/ReadOnly_Tests.cs index b621adbbf..5081e248a 100644 --- a/LiteDB.Tests/Engine/ReadOnly_Tests.cs +++ b/LiteDB.Tests/Engine/ReadOnly_Tests.cs @@ -21,7 +21,7 @@ public void Datafile_Read_Only_Access() db.Insert("col", new BsonDocument { { "_id", 1 } }); } - using (var r = new LiteEngine(new FileDiskService(file.Filename, new FileOptions { ReadOnly = true }))) + using (var r = new LiteEngine(new ConnectionString { Filename = file.Filename, ReadOnly = true })) { var doc = r.Find("col", Query.EQ("_id", 1)).FirstOrDefault(); diff --git a/LiteDB.Tests/Engine/Shrink_Tests.cs b/LiteDB.Tests/Engine/Shrink_Tests.cs index c23ebd030..12b1e9517 100644 --- a/LiteDB.Tests/Engine/Shrink_Tests.cs +++ b/LiteDB.Tests/Engine/Shrink_Tests.cs @@ -84,7 +84,7 @@ public void Shrink_Large_Files() } // re-open, again, but now with password - using (var db = new LiteEngine(file.Filename, "abc123")) + using (var db = new LiteEngine(new ConnectionString { Filename = file.Filename, Password = "abc123" })) { DoTest(db); @@ -106,7 +106,7 @@ private IEnumerable GetDocs(int initial, int count, int type = 1) { "_id", i }, { "name", Guid.NewGuid().ToString() }, { "first", "John" }, - { "lorem", TempFile.LoremIpsum(3, 5, 2, 3, 3) } + { "lorem", LoremIpsum.Generate(3, 5, 2, 3, 3) } }; } } diff --git a/LiteDB.Tests/Utils/LoremIpsum.cs b/LiteDB.Tests/Utils/LoremIpsum.cs new file mode 100644 index 000000000..bda621ccf --- /dev/null +++ b/LiteDB.Tests/Utils/LoremIpsum.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace LiteDB.Tests +{ + public class LoremIpsum + { + public static string Generate(int minWords, int maxWords, + int minSentences, int maxSentences, + int numParagraphs) + { + var words = new[] { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", + "adipiscing", "elit", "sed", "diam", "nonummy", "nibh", "euismod", + "tincidunt", "ut", "laoreet", "dolore", "magna", "aliquam", "erat" }; + + var rand = new Random(DateTime.Now.Millisecond); + var numSentences = rand.Next(maxSentences - minSentences) + minSentences + 1; + var numWords = rand.Next(maxWords - minWords) + minWords + 1; + + var result = new StringBuilder(); + + for (int p = 0; p < numParagraphs; p++) + { + for (int s = 0; s < numSentences; s++) + { + for (int w = 0; w < numWords; w++) + { + if (w > 0) { result.Append(" "); } + result.Append(words[rand.Next(words.Length)]); + } + result.Append(". "); + } + result.AppendLine(); + } + + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/LiteDB.Tests/Utils/TempFile.cs b/LiteDB.Tests/Utils/TempFile.cs index e20ba79c6..4adc3d97f 100644 --- a/LiteDB.Tests/Utils/TempFile.cs +++ b/LiteDB.Tests/Utils/TempFile.cs @@ -12,35 +12,12 @@ public class TempFile : IDisposable public string Filename { get; private set; } - public TempFile(string ext = "db", bool checkIntegrity = true) + public TempFile(bool checkIntegrity = true) { - this.Filename = Path.Combine(Path.GetTempPath(), string.Format("test-{0}.{1}", Guid.NewGuid(), ext)); + this.Filename = Path.Combine(Path.GetTempPath(), string.Format("test-{0}.{1}", Guid.NewGuid(), ".db")); _checkIntegrity = checkIntegrity; } - public void CreateDatafile() - { - using (var s = new FileStream(Filename, System.IO.FileMode.CreateNew)) - { - LiteEngine.CreateDatabase(s); - } - } - - public IDiskService Disk() - { - return new FileDiskService(Filename); - } - - public IDiskService Disk(FileOptions options) - { - return new FileDiskService(Filename, options); - } - - public string Conn(string connectionString) - { - return "filename=\"" + this.Filename + "\";" + connectionString; - } - #region Dispose private bool _disposed; @@ -98,21 +75,21 @@ private void CheckIntegrity() { var cols = db.GetCollectionNames().ToArray(); - foreach(var col in cols) + foreach (var col in cols) { var indexes = db.GetIndexes(col).ToArray(); - foreach(var idx in indexes) + foreach (var idx in indexes) { var q = db.Find(col, Query.All(idx.Field)); - foreach(var doc in q) + foreach (var doc in q) { // document are ok! } // lets drop this index (if not _id) - if(idx.Field != "_id") + if (idx.Field != "_id") { db.DropIndex(col, idx.Field); } @@ -126,40 +103,5 @@ private void CheckIntegrity() db.Shrink(); } } - - #region LoremIpsum Generator - - public static string LoremIpsum(int minWords, int maxWords, - int minSentences, int maxSentences, - int numParagraphs) - { - var words = new[] { "lorem", "ipsum", "dolor", "sit", "amet", "consectetuer", - "adipiscing", "elit", "sed", "diam", "nonummy", "nibh", "euismod", - "tincidunt", "ut", "laoreet", "dolore", "magna", "aliquam", "erat" }; - - var rand = new Random(DateTime.Now.Millisecond); - var numSentences = rand.Next(maxSentences - minSentences) + minSentences + 1; - var numWords = rand.Next(maxWords - minWords) + minWords + 1; - - var result = new StringBuilder(); - - for (int p = 0; p < numParagraphs; p++) - { - for (int s = 0; s < numSentences; s++) - { - for (int w = 0; w < numWords; w++) - { - if (w > 0) { result.Append(" "); } - result.Append(words[rand.Next(words.Length)]); - } - result.Append(". "); - } - result.AppendLine(); - } - - return result.ToString(); - } - - #endregion } } \ No newline at end of file diff --git a/LiteDB/Engine/Disks/FileDiskService.cs b/LiteDB/Engine/Disks/FileDiskService.cs deleted file mode 100644 index f9e7cf95f..000000000 --- a/LiteDB/Engine/Disks/FileDiskService.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; - -namespace LiteDB -{ - /// - /// Implement NTFS File disk - /// - public class FileDiskService : IDiskService - { - /// - /// Position, on page, about page type - /// - private const int PAGE_TYPE_POSITION = 4; - - private FileStream _stream; - private string _filename; - - private Logger _log; // will be initialize in "Initialize()" - private FileOptions _options; - - #region Initialize/Dispose disk - - public FileDiskService(string filename) - : this(filename, new FileOptions()) - { - } - - public FileDiskService(string filename, FileOptions options) - { - // simple validations - if (filename.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(filename)); - if (options.InitialSize > options.LimitSize) throw new ArgumentException("limit size less than initial size"); - - // setting class variables - _filename = filename; - _options = options; - } - - public void Initialize(Logger log, string password) - { - // get log instance to disk - _log = log; - - _log.Write(Logger.DISK, "open datafile '{0}'", Path.GetFileName(_filename)); - - // open/create file using read only/exclusive options - _stream = this.CreateFileStream(_filename, - _options.ReadOnly ? System.IO.FileMode.Open : System.IO.FileMode.OpenOrCreate, - _options.ReadOnly ? FileAccess.Read : FileAccess.ReadWrite, - FileShare.None); - - // if file is new, initialize - if (_stream.Length == 0) - { - _log.Write(Logger.DISK, "initialize new datafile"); - - // create datafile - LiteEngine.CreateDatabase(_stream, password, _options.InitialSize); - } - } - - public virtual void Dispose() - { - if (_stream != null) - { - _log.Write(Logger.DISK, "close datafile '{0}'", Path.GetFileName(_filename)); - _stream.Dispose(); - _stream = null; - } - } - - #endregion - - #region Read/Write - - /// - /// Read page bytes from disk - /// - public virtual byte[] ReadPage(uint pageID) - { - var buffer = new byte[BasePage.PAGE_SIZE]; - var position = BasePage.GetSizeOfPages(pageID); - - lock (_stream) - { - // position cursor - if (_stream.Position != position) - { - _stream.Seek(position, SeekOrigin.Begin); - } - - // read bytes from data file - _stream.Read(buffer, 0, BasePage.PAGE_SIZE); - } - - _log.Write(Logger.DISK, "read page #{0:0000} :: {1}", pageID, (PageType)buffer[PAGE_TYPE_POSITION]); - - return buffer; - } - - /// - /// Persist single page bytes to disk - /// - public virtual void WritePage(uint pageID, byte[] buffer) - { - var position = BasePage.GetSizeOfPages(pageID); - - _log.Write(Logger.DISK, "write page #{0:0000} :: {1}", pageID, (PageType)buffer[PAGE_TYPE_POSITION]); - - // position cursor - if (_stream.Position != position) - { - _stream.Seek(position, SeekOrigin.Begin); - } - - _stream.Write(buffer, 0, BasePage.PAGE_SIZE); - } - - /// - /// Set datafile length - /// - public void SetLength(long fileSize) - { - // checks if new fileSize will exceed limit size - if (fileSize > _options.LimitSize) throw LiteException.FileSizeExceeded(_options.LimitSize); - - // fileSize parameter tell me final size of data file - helpful to extend first datafile - _stream.SetLength(fileSize); - } - - /// - /// Returns file length - /// - public long FileLength { get { return _stream.Length; } } - - /// - /// Flush data from memory to disk - /// - public void Flush() - { - _log.Write(Logger.DISK, "flush data from memory to disk"); - - _stream.Flush(); - } - - #endregion - - #region Create Stream - - /// - /// Create a new filestream. Can be synced over async task (netstandard) - /// - private FileStream CreateFileStream(string path, System.IO.FileMode mode, FileAccess access, FileShare share) - { -#if HAVE_SYNC_OVER_ASYNC - if (_options.Async) - { - return System.Threading.Tasks.Task.Run(() => new FileStream(path, mode, access, share, BasePage.PAGE_SIZE)) - .ConfigureAwait(false) - .GetAwaiter() - .GetResult(); - } -#endif - return new FileStream(path, mode, access, share, - BasePage.PAGE_SIZE, - System.IO.FileOptions.RandomAccess); - } - - #endregion - } -} \ No newline at end of file diff --git a/LiteDB/Engine/Disks/IDiskService.cs b/LiteDB/Engine/Disks/IDiskService.cs deleted file mode 100644 index adcd6172d..000000000 --- a/LiteDB/Engine/Disks/IDiskService.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace LiteDB -{ - public interface IDiskService : IDisposable - { - /// - /// Open data file (creating if doest exists) and return header content bytes - /// - void Initialize(Logger log, string password); - - /// - /// Read a page from disk datafile - /// - byte[] ReadPage(uint pageID); - - /// - /// Write a page in disk datafile - /// - void WritePage(uint pageID, byte[] buffer); - - /// - /// Set datafile length before start writing in disk - /// - void SetLength(long fileSize); - - /// - /// Gets file length in bytes - /// - long FileLength { get; } - - /// - /// Ensures all pages from the OS cache are persisted on medium - /// - void Flush(); - } -} \ No newline at end of file diff --git a/LiteDB/Engine/Disks/TempDiskService.cs b/LiteDB/Engine/Disks/TempDiskService.cs deleted file mode 100644 index ec508b308..000000000 --- a/LiteDB/Engine/Disks/TempDiskService.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; - -namespace LiteDB -{ - /// - /// Implement temporary disk access. Open datafile only when be used and delete when dispose. - /// - public class TempDiskService : IDiskService - { - /// - /// Position, on page, about page type - /// - private const int PAGE_TYPE_POSITION = 4; - - private FileStream _stream; - private string _filename; - - #region Initialize/Dispose disk - - public TempDiskService() - { - } - - public void Initialize(Logger log, string password) - { - // datafile will be created only when used - } - - private void InternalInitialize() - { - // create a temp filename in temp directory - _filename = Path.Combine(Path.GetTempPath(), "litedb-sort-" + Guid.NewGuid().ToString("n").Substring(0, 6) + ".db"); - - // create disk - _stream = this.CreateFileStream(_filename, System.IO.FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); - } - - public virtual void Dispose() - { - if (_stream != null) - { - _stream.Dispose(); - _stream = null; - - // after release stream, delete datafile - FileHelper.TryDelete(_filename); - } - } - - #endregion - - #region Read/Write - - /// - /// Read page bytes from disk - /// - public virtual byte[] ReadPage(uint pageID) - { - // if stream are not initialized but need header, create new header - if(_stream == null && pageID == 0) - { - var header = new HeaderPage - { - LastPageID = 1 - }; - - return header.WritePage(); - } - else if (_stream == null) - { - this.InternalInitialize(); - } - - var buffer = new byte[BasePage.PAGE_SIZE]; - var position = BasePage.GetSizeOfPages(pageID); - - // position cursor - if (_stream.Position != position) - { - _stream.Seek(position, SeekOrigin.Begin); - } - - // read bytes from data file - _stream.Read(buffer, 0, BasePage.PAGE_SIZE); - - return buffer; - } - - /// - /// Persist single page bytes to disk - /// - public virtual void WritePage(uint pageID, byte[] buffer) - { - if (_stream == null) this.InternalInitialize(); - - var position = BasePage.GetSizeOfPages(pageID); - - // position cursor - if (_stream.Position != position) - { - _stream.Seek(position, SeekOrigin.Begin); - } - - _stream.Write(buffer, 0, BasePage.PAGE_SIZE); - } - - /// - /// Set datafile length - /// - public void SetLength(long fileSize) - { - if (_stream == null) this.InternalInitialize(); - - // fileSize parameter tell me final size of data file - helpful to extend first datafile - _stream.SetLength(fileSize); - } - - /// - /// Returns file length - /// - public long FileLength { get { return _stream?.Length ?? 0; } } - - /// - /// Flush data from memory to disk - /// - public void Flush() - { - if (_stream != null) - { - _stream.Flush(); - } - } - - #endregion - - #region Create Stream - - /// - /// Create a new filestream. Can be synced over async task (netstandard) - /// - private FileStream CreateFileStream(string path, System.IO.FileMode mode, FileAccess access, FileShare share) - { -#if HAVE_SYNC_OVER_ASYNC - return System.Threading.Tasks.Task.Run(() => new FileStream(path, mode, access, share, BasePage.PAGE_SIZE)) - .ConfigureAwait(false) - .GetAwaiter() - .GetResult(); -#else - return new FileStream(path, mode, access, share, - BasePage.PAGE_SIZE, - System.IO.FileOptions.RandomAccess); -#endif - } - - #endregion - } -} \ No newline at end of file diff --git a/LiteDB/Engine/Engine/FindSort.cs b/LiteDB/Engine/Engine/FindSort.cs index 60aca918a..7c528424b 100644 --- a/LiteDB/Engine/Engine/FindSort.cs +++ b/LiteDB/Engine/Engine/FindSort.cs @@ -31,10 +31,9 @@ public List FindSort(string collection, Query query, string orderB var last = order == Query.Ascending ? BsonValue.MaxValue : BsonValue.MinValue; var total = limit == int.MaxValue ? int.MaxValue : skip + limit; var indexCounter = 0; - var disk = new TempDiskService(); // create memory database - using (var engine = new LiteEngine(disk)) + using (var engine = new LiteEngine(new ConnectionString { Filename = ":temp:" })) { // get collection page var col = this.GetCollectionPage(collection, false); diff --git a/LiteDB/Engine/Engine/Info.cs b/LiteDB/Engine/Engine/Info.cs index acefea550..767fea0e6 100644 --- a/LiteDB/Engine/Engine/Info.cs +++ b/LiteDB/Engine/Engine/Info.cs @@ -43,7 +43,7 @@ public BsonDocument Info() return new BsonDocument { { "userVersion", (int)header.UserVersion }, - { "encrypted", header.Password.Any(x => x > 0) }, +// { "encrypted", header.Password.Any(x => x > 0) }, { "changeID", (int)header.ChangeID }, { "lastPageID", (int)header.LastPageID }, { "fileSize", BasePage.GetSizeOfPages(header.LastPageID + 1) }, diff --git a/LiteDB/Engine/Engine/Shrink.cs b/LiteDB/Engine/Engine/Shrink.cs index cc909cddd..4274adb9c 100644 --- a/LiteDB/Engine/Engine/Shrink.cs +++ b/LiteDB/Engine/Engine/Shrink.cs @@ -7,17 +7,18 @@ namespace LiteDB public partial class LiteEngine { /// - /// Reduce disk size re-arranging unused spaces. Can change password. If temporary disk was not provided, use MemoryStream temp disk + /// Reduce disk size re-arranging unused spaces. Can change password. /// - public long Shrink(string password = null, IDiskService tempDisk = null) + public long Shrink(string password = null) { var originalSize = _disk.FileLength; // if temp disk are not passed, use memory stream disk - using (var temp = tempDisk ?? new StreamDiskService(new MemoryStream())) using (_locker.Write()) - using (var engine = new LiteEngine(temp, password)) + using (var engine = new LiteEngine(new ConnectionString { Filename = ":temp:", Password = password })) { + var temp = engine._disk; + // read all collection foreach (var collectionName in this.GetCollectionNames()) { diff --git a/LiteDB/Engine/LiteEngine.cs b/LiteDB/Engine/LiteEngine.cs index a8304846c..ea5b4640f 100644 --- a/LiteDB/Engine/LiteEngine.cs +++ b/LiteDB/Engine/LiteEngine.cs @@ -15,7 +15,7 @@ public partial class LiteEngine : IDisposable private LockService _locker; - private IDiskService _disk; + private DiskService _disk; private CacheService _cache; @@ -31,102 +31,61 @@ public partial class LiteEngine : IDisposable private AesEncryption _crypto; - private int _cacheSize; - - private TimeSpan _timeout; - private BsonReader _bsonReader; private BsonWriter _bsonWriter = new BsonWriter(); + private ConnectionString _options; + /// /// Get log instance for debug operations /// public Logger Log { get { return _log; } } - /// - /// Get memory cache size limit. Works only with journal enabled (number in pages). If journal is disabled, pages in cache can exceed this limit. Default is 5000 pages - /// - public int CacheSize { get { return _cacheSize; } } - - /// - /// Get how many pages are on cache - /// - public int CacheUsed { get { return _cache.CleanUsed; } } - - /// - /// Gets time waiting write lock operation before throw LiteException timeout - /// - public TimeSpan Timeout { get { return _timeout; } } - - /// - /// Instance of locker control - /// - public LockService Locker { get { return _locker; } } - #endregion #region Ctor /// - /// Initialize LiteEngine using default FileDiskService + /// Initialize LiteEngine using memory database /// - public LiteEngine(string filename) - : this(new FileDiskService(filename)) + public LiteEngine() + : this(new ConnectionString()) { } /// - /// Initialize LiteEngine with password encryption + /// Initialize LiteEngine using default file implementation /// - public LiteEngine(string filename, string password) - : this(new FileDiskService(filename), password) + public LiteEngine(string connectionString) + : this(new ConnectionString(connectionString)) { } /// - /// Initialize LiteEngine using StreamDiskService + /// Initialize LiteEngine using full connection string options, stream (if null use connection string GetStream()) and logger instance (if null create new) /// - public LiteEngine(Stream stream, string password = null) - : this(new StreamDiskService(stream), password) + public LiteEngine(ConnectionString connectionString, Stream datafile = null, Logger log = null) { - } + _options = connectionString; + _log = log ?? new Logger(_options.Log); - /// - /// Initialize LiteEngine using custom disk service implementation and full engine options - /// - public LiteEngine(IDiskService disk, string password = null, TimeSpan? timeout = null, int cacheSize = 5000, Logger log = null, bool utcDate = false) - { - if (disk == null) throw new ArgumentNullException(nameof(disk)); + // get stream implementation from connection string + var stream = datafile ?? _options.GetStream(); - _timeout = timeout ?? TimeSpan.FromMinutes(1); - _cacheSize = cacheSize; - _disk = disk; - _log = log ?? new Logger(); - _bsonReader = new BsonReader(utcDate); + // initialize disk and create new database if needed (dispose only if stream was not passed) + _disk = new DiskService(stream, _options.InitialSize, datafile == null, _log); try { - // initialize datafile (create) and set log instance - _disk.Initialize(_log, password); - var buffer = _disk.ReadPage(0); // create header instance from array bytes var header = BasePage.ReadPage(buffer) as HeaderPage; - // hash password with sha1 or keep as empty byte[20] - var sha1 = password == null ? new byte[20] : AesEncryption.HashSHA1(password); - - // compare header password with user password even if not passed password (datafile can have password) - if (sha1.BinaryCompareTo(header.Password) != 0) - { - throw LiteException.DatabaseWrongPassword(); - } - // initialize AES encryptor - if (password != null) + if (_options.Password != null) { - _crypto = new AesEncryption(password, header.Salt); + _crypto = new AesEncryption(_options.Password, header.Salt); } // initialize all services @@ -145,12 +104,14 @@ public LiteEngine(IDiskService disk, string password = null, TimeSpan? timeout = /// private void InitializeServices() { + _bsonReader = new BsonReader(_options.UtcDate); + _cache = new CacheService(_disk, _log); - _locker = new LockService(_disk, _cache, _timeout, _log); + _locker = new LockService(_disk, _cache, _options.Timeout, _log); _pager = new PageService(_disk, _crypto, _cache, _log); _indexer = new IndexService(_pager, _log); _data = new DataService(_pager, _log); - _trans = new TransactionService(_disk, _crypto, _pager, _locker, _cache, _cacheSize, _log); + _trans = new TransactionService(_disk, _crypto, _pager, _locker, _cache, _log); _collections = new CollectionService(_pager, _indexer, _data, _trans, _log); } @@ -214,60 +175,5 @@ public void Dispose() // dispose crypto if (_crypto != null) _crypto.Dispose(); } - - /// - /// Initialize new datafile with header page + lock reserved area zone - /// - public static void CreateDatabase(Stream stream, string password = null, long initialSize = 0) - { - // calculate how many empty pages will be added on disk - var emptyPages = initialSize == 0 ? 0 : (initialSize - (2 * BasePage.PAGE_SIZE)) / BasePage.PAGE_SIZE; - - // if too small size (less than 2 pages), assume no initial size - if (emptyPages < 0) emptyPages = 0; - - // create a new header page in bytes (keep second page empty) - var header = new HeaderPage - { - LastPageID = initialSize == 0 ? 1 : (uint)emptyPages + 1, - FreeEmptyPageID = initialSize == 0 ? uint.MaxValue : 2 - }; - - if (password != null) - { - header.Password = AesEncryption.HashSHA1(password); - header.Salt = AesEncryption.Salt(); - } - - // point to begin file - stream.Seek(0, SeekOrigin.Begin); - - // get header page in bytes - var buffer = header.WritePage(); - - stream.Write(buffer, 0, BasePage.PAGE_SIZE); - - // write second page as an empty AREA (it's not a page) just to use as lock control - stream.Write(new byte[BasePage.PAGE_SIZE], 0, BasePage.PAGE_SIZE); - - // if initial size is defined, lets create empty pages in a linked list - if (emptyPages > 0) - { - stream.SetLength(initialSize); - - var pageID = 1u; - - while(++pageID < (emptyPages + 2)) - { - var empty = new EmptyPage(pageID) - { - PrevPageID = pageID == 2 ? 0 : pageID - 1, - NextPageID = pageID == emptyPages + 1 ? uint.MaxValue : pageID + 1 - }; - - stream.Write(empty.WritePage(), 0, BasePage.PAGE_SIZE); - } - } - } } } \ No newline at end of file diff --git a/LiteDB/Engine/Pages/HeaderPage.cs b/LiteDB/Engine/Pages/HeaderPage.cs index 1e18f9a26..e56bf1470 100644 --- a/LiteDB/Engine/Pages/HeaderPage.cs +++ b/LiteDB/Engine/Pages/HeaderPage.cs @@ -41,11 +41,6 @@ internal class HeaderPage : BasePage /// public ushort UserVersion { get; set; } - /// - /// Password hash in SHA1 [20 bytes] - /// - public byte[] Password { get; set; } - /// /// When using encryption, store salt for password /// @@ -65,7 +60,6 @@ public HeaderPage() this.ItemCount = 1; // fixed for header this.FreeBytes = 0; // no free bytes on header this.UserVersion = 0; - this.Password = new byte[20]; this.Salt = new byte[16]; this.CollectionPages = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -84,7 +78,6 @@ protected override void ReadContent(ByteReader reader) this.FreeEmptyPageID = reader.ReadUInt32(); this.LastPageID = reader.ReadUInt32(); this.UserVersion = reader.ReadUInt16(); - this.Password = reader.ReadBytes(this.Password.Length); this.Salt = reader.ReadBytes(this.Salt.Length); // read page collections references (position on end of page) @@ -93,10 +86,6 @@ protected override void ReadContent(ByteReader reader) { this.CollectionPages.Add(reader.ReadString(), reader.ReadUInt32()); } - - // use last page byte position for recovery mode only because i forgot to reserve area before collection names! - // TODO: fix this in next change data structure - reader.Position = BasePage.PAGE_SIZE - 1; } protected override void WriteContent(ByteWriter writer) @@ -107,7 +96,6 @@ protected override void WriteContent(ByteWriter writer) writer.Write(this.FreeEmptyPageID); writer.Write(this.LastPageID); writer.Write(this.UserVersion); - writer.Write(this.Password); writer.Write(this.Salt); writer.Write((byte)this.CollectionPages.Count); @@ -116,8 +104,6 @@ protected override void WriteContent(ByteWriter writer) writer.Write(key); writer.Write(this.CollectionPages[key]); } - - writer.Position = BasePage.PAGE_SIZE - 1; } #endregion diff --git a/LiteDB/Engine/Services/CacheService.cs b/LiteDB/Engine/Services/CacheService.cs index 9ff8112aa..0d1330bf6 100644 --- a/LiteDB/Engine/Services/CacheService.cs +++ b/LiteDB/Engine/Services/CacheService.cs @@ -16,10 +16,10 @@ internal class CacheService /// private Dictionary _dirty = new Dictionary(); - private IDiskService _disk; + private DiskService _disk; private Logger _log; - public CacheService(IDiskService disk, Logger log) + public CacheService(DiskService disk, Logger log) { _disk = disk; _log = log; diff --git a/LiteDB/Engine/Disks/StreamDiskService.cs b/LiteDB/Engine/Services/DiskService.cs similarity index 52% rename from LiteDB/Engine/Disks/StreamDiskService.cs rename to LiteDB/Engine/Services/DiskService.cs index 91483e14f..be33da31f 100644 --- a/LiteDB/Engine/Disks/StreamDiskService.cs +++ b/LiteDB/Engine/Services/DiskService.cs @@ -1,14 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; -using System.Threading; namespace LiteDB { - /// - /// Implement generic Stream disk service. Used for any read/write/seek stream - /// - public class StreamDiskService : IDiskService + internal class DiskService : IDisposable { /// /// Position, on page, about page type @@ -16,65 +13,41 @@ public class StreamDiskService : IDiskService private const int PAGE_TYPE_POSITION = 4; private Stream _stream; - private Logger _log; // will be initialize in "Initialize()" - private bool _disposeStream; + private Logger _log; + private bool _dispose; - #region Initialize disk - - public StreamDiskService(Stream stream, bool disposeStream = false) + public DiskService(Stream stream, long initialSize, bool dispose, Logger log) { _stream = stream; - _disposeStream = disposeStream; - } - - public void Initialize(Logger log, string password) - { - // get log instance to disk _log = log; + _dispose = dispose; - // if stream are empty, create header page and save to stream if (_stream.Length == 0) { - _log.Write(Logger.DISK, "initialize new datafile"); - - // create datafile - LiteEngine.CreateDatabase(_stream, password); + this.CreateDatabase(initialSize); } } - public virtual void Dispose() - { - if (_disposeStream) - { - _stream.Dispose(); - } - else - { - // do nothing - keeps stream opened - } - } - - #endregion - - #region Read/Write - /// /// Read page bytes from disk /// - public virtual byte[] ReadPage(uint pageID) + public byte[] ReadPage(uint pageID) { var buffer = new byte[BasePage.PAGE_SIZE]; var position = BasePage.GetSizeOfPages(pageID); - // position cursor - if (_stream.Position != position) + lock (_stream) { - _stream.Seek(position, SeekOrigin.Begin); + // position cursor + if (_stream.Position != position) + { + _stream.Seek(position, SeekOrigin.Begin); + } + + // read bytes from data file + _stream.Read(buffer, 0, BasePage.PAGE_SIZE); } - // read bytes from data file - _stream.Read(buffer, 0, BasePage.PAGE_SIZE); - _log.Write(Logger.DISK, "read page #{0:0000} :: {1}", pageID, (PageType)buffer[PAGE_TYPE_POSITION]); return buffer; @@ -83,7 +56,7 @@ public virtual byte[] ReadPage(uint pageID) /// /// Persist single page bytes to disk /// - public virtual void WritePage(uint pageID, byte[] buffer) + public void WritePage(uint pageID, byte[] buffer) { var position = BasePage.GetSizeOfPages(pageID); @@ -98,6 +71,8 @@ public virtual void WritePage(uint pageID, byte[] buffer) _stream.Write(buffer, 0, BasePage.PAGE_SIZE); } + public long FileLength => _stream.Length; + /// /// Set datafile length /// @@ -108,17 +83,48 @@ public void SetLength(long fileSize) } /// - /// Returns file length + /// Flush data from memory to disk /// - public long FileLength { get { return _stream.Length; } } + public void Flush() + { + _log.Write(Logger.DISK, "flush data from memory to disk"); + + _stream.Flush(); + } /// - /// No flush implemented + /// Create new database based in _stream /// - public void Flush() + private void CreateDatabase(long initialSize) { + // create a new header page in bytes (keep second page empty) + var header = new HeaderPage + { + LastPageID = 1, + Salt = AesEncryption.Salt() + }; + + // point to begin file + _stream.Seek(0, SeekOrigin.Begin); + + // get header page in bytes + var buffer = header.WritePage(); + + _stream.Write(buffer, 0, BasePage.PAGE_SIZE); + + // if has initial size (at least 10 pages), alocate disk space now + if (initialSize > (BasePage.PAGE_SIZE * 10)) + { + _stream.SetLength(initialSize); + } } - #endregion + public void Dispose() + { + if (_stream != null && _dispose) + { + _stream.Dispose(); + } + } } } \ No newline at end of file diff --git a/LiteDB/Engine/Services/LockService.cs b/LiteDB/Engine/Services/LockService.cs index d5aadf813..ab79fac02 100644 --- a/LiteDB/Engine/Services/LockService.cs +++ b/LiteDB/Engine/Services/LockService.cs @@ -15,12 +15,12 @@ public class LockService #region Properties + Ctor private TimeSpan _timeout; - private IDiskService _disk; + private DiskService _disk; private CacheService _cache; private Logger _log; private ReaderWriterLockSlim _thread = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - internal LockService(IDiskService disk, CacheService cache, TimeSpan timeout, Logger log) + internal LockService(DiskService disk, CacheService cache, TimeSpan timeout, Logger log) { _disk = disk; _cache = cache; diff --git a/LiteDB/Engine/Services/PageService.cs b/LiteDB/Engine/Services/PageService.cs index e51d942e9..5abe243ff 100644 --- a/LiteDB/Engine/Services/PageService.cs +++ b/LiteDB/Engine/Services/PageService.cs @@ -7,11 +7,11 @@ namespace LiteDB internal class PageService { private CacheService _cache; - private IDiskService _disk; + private DiskService _disk; private AesEncryption _crypto; private Logger _log; - public PageService(IDiskService disk, AesEncryption crypto, CacheService cache, Logger log) + public PageService(DiskService disk, AesEncryption crypto, CacheService cache, Logger log) { _disk = disk; _crypto = crypto; diff --git a/LiteDB/Engine/Services/TransactionService.cs b/LiteDB/Engine/Services/TransactionService.cs index 9b15e2332..67e33b3a4 100644 --- a/LiteDB/Engine/Services/TransactionService.cs +++ b/LiteDB/Engine/Services/TransactionService.cs @@ -9,22 +9,20 @@ namespace LiteDB /// internal class TransactionService { - private IDiskService _disk; + private DiskService _disk; private AesEncryption _crypto; private LockService _locker; private PageService _pager; private CacheService _cache; private Logger _log; - private int _cacheSize; - internal TransactionService(IDiskService disk, AesEncryption crypto, PageService pager, LockService locker, CacheService cache, int cacheSize, Logger log) + internal TransactionService(DiskService disk, AesEncryption crypto, PageService pager, LockService locker, CacheService cache, Logger log) { _disk = disk; _crypto = crypto; _cache = cache; _locker = locker; _pager = pager; - _cacheSize = cacheSize; _log = log; } @@ -36,14 +34,14 @@ internal TransactionService(IDiskService disk, AesEncryption crypto, PageService /// public bool CheckPoint() { - if (_cache.CleanUsed > _cacheSize) - { - _log.Write(Logger.CACHE, "cache size reached {0} pages, will clear now", _cache.CleanUsed); - - _cache.ClearPages(); - - return true; - } + // if (_cache.CleanUsed > _cacheSize) + // { + // _log.Write(Logger.CACHE, "cache size reached {0} pages, will clear now", _cache.CleanUsed); + // + // _cache.ClearPages(); + // + // return true; + // } return false; } diff --git a/LiteDB/Utils/ConnectionString.cs b/LiteDB/Utils/ConnectionString.cs index c88eb088b..c44b54f11 100644 --- a/LiteDB/Utils/ConnectionString.cs +++ b/LiteDB/Utils/ConnectionString.cs @@ -15,61 +15,54 @@ namespace LiteDB public class ConnectionString { /// - /// "filename": Full path or relative path from DLL directory + /// "filename": Full path or relative path from DLL directory (default: ':memory:') /// - public string Filename { get; set; } + public string Filename { get; set; } = ":memory:"; /// /// "password": Encrypt (using AES) your datafile with a password (default: null - no encryption) /// - public string Password { get; set; } - - /// - /// "cache size": Max number of pages in cache. After this size, flush data to disk to avoid too memory usage (default: 5000) - /// - public int CacheSize { get; set; } + public string Password { get; set; } = null; /// /// "timeout": Timeout for waiting unlock operations (default: 1 minute) /// - public TimeSpan Timeout { get; set; } + public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1); /// /// "read only": Define if datafile will be read only, with no insert/update/delete data (default: false) /// - public bool ReadOnly { get; set; } + public bool ReadOnly { get; set; } = false; /// /// "initial size": If database is new, initialize with allocated space - support KB, MB, GB (default: null) /// - public long InitialSize { get; set; } + public long InitialSize { get; set; } = 0; /// /// "limit size": Max limit of datafile - support KB, MB, GB (default: null) /// - public long LimitSize { get; set; } + public long LimitSize { get; set; } = long.MaxValue; /// /// "log": Debug messages from database - use `LiteDatabase.Log` (default: Logger.NONE) /// - public byte Log { get; set; } + public byte Log { get; set; } = Logger.NONE; /// /// "utc": Returns date in UTC timezone from BSON deserialization (default: false == LocalTime) /// - public bool UtcDate { get; set; } + public bool UtcDate { get; set; } = false; /// - /// "upgrade": Test if database is in old version and update if needed (default: false) + /// "async": Use "sync over async" to UWP apps access any directory /// - public bool Upgrade { get; set; } + public bool Async { get; set; } = false; -#if HAVE_SYNC_OVER_ASYNC /// - /// "async": Use "sync over async" to UWP apps access any directory + /// "flush": If true, force flush to disk (default: false) /// - public bool Async { get; set; } -#endif + public bool Flush { get; set; } = false; /// /// Initialize empty connection string @@ -98,19 +91,39 @@ public ConnectionString(string connectionString) } // setting values to properties - this.Filename = values.GetValue("filename", ""); + this.Filename = values.GetValue("filename", null); this.Password = values.GetValue("password", null); - this.CacheSize = values.GetValue(@"cache size", 5000); this.Timeout = values.GetValue("timeout", TimeSpan.FromMinutes(1)); this.ReadOnly = values.GetValue("read only", false); this.InitialSize = values.GetFileSize(@"initial size", 0); this.LimitSize = values.GetFileSize(@"limit size", long.MaxValue); this.Log = values.GetValue("log", Logger.NONE); this.UtcDate = values.GetValue("utc", false); - this.Upgrade = values.GetValue("upgrade", false); -#if HAVE_SYNC_OVER_ASYNC this.Async = values.GetValue("async", false); -#endif + } + + /// + /// Get stream implementation based on filename + /// + internal Stream GetStream() + { + if (this.Filename == ":memory:") + { + return new MemoryStream(); + } + else if (this.Filename == ":temp:") + { + return new MemoryStream(); + } + else + { + return new FileStream(this.Filename, + this.ReadOnly ? FileMode.Open : FileMode.OpenOrCreate, + this.ReadOnly ? FileAccess.Read : FileAccess.ReadWrite, + this.ReadOnly ? FileShare.Read : FileShare.None, + BasePage.PAGE_SIZE, + FileOptions.RandomAccess); + } } } } \ No newline at end of file diff --git a/LiteDB/Utils/FileOptions.cs b/LiteDB/Utils/FileOptions.cs deleted file mode 100644 index d24efca25..000000000 --- a/LiteDB/Utils/FileOptions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace LiteDB -{ - /// - /// Datafile open options (for FileDiskService) - /// - public class FileOptions - { - public long InitialSize { get; set; } - public long LimitSize { get; set; } - public bool ReadOnly { get; set; } -#if HAVE_SYNC_OVER_ASYNC - public bool Async { get; set; } -#endif - - public FileOptions() - { - this.InitialSize = 0; - this.LimitSize = long.MaxValue; - this.ReadOnly = false; - } - } -} diff --git a/README.md b/README.md index 55fe089a5..154024e03 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ # NEXT -- Hide Database, Mapper, FileStorage, Shell, +Tests from solution -- Remove FileOptions, use only ConnectionString -- Keep LiteEngine with minimum data access methods +- Implement TempStream : Stream (using MemoryStream + FileStream after size X) -- Async +- Test password do not storing password sha1 +- Review logs (with #DEBUG call) +- Do tests with Async operations +- Rename LiteEngine to DbEngine with minimum data access methods +- Fix CreateDatabase initial size (back to simple way) + remove password in header +- Fix database info +# TESTS REMOVED + # LiteDB - A .NET NoSQL Document Store in a single data file