From 74df69fcdcdc7affc1f0385f0fd9625dd5d7574a Mon Sep 17 00:00:00 2001 From: Corey Kaylor Date: Sat, 4 Feb 2023 10:54:04 -0500 Subject: [PATCH] net7.0 & C# style improvements (#158) net7.0 LibraryImport, C# 11, and nint changes Re-enable test with path fix Style changes for C# 11 and analysis Updating dependencies --- src/LightningDB.Benchmarks/BenchmarkBase.cs | 119 ++- src/LightningDB.Benchmarks/KeyBatch.cs | 108 ++- .../LightningDB.Benchmarks.csproj | 17 +- src/LightningDB.Benchmarks/Main.cs | 15 +- src/LightningDB.Benchmarks/ReadBenchmarks.cs | 39 +- src/LightningDB.Benchmarks/WriteBenchmarks.cs | 29 +- src/LightningDB.Tests/ConditionalFacts.cs | 2 +- src/LightningDB.Tests/CursorTests.cs | 599 ++++++------ src/LightningDB.Tests/DatabaseIOTests.cs | 221 +++-- src/LightningDB.Tests/DatabaseTests.cs | 273 +++--- src/LightningDB.Tests/EnvironmentTests.cs | 302 +++--- .../LightningDB.Tests.csproj | 2 + src/LightningDB.Tests/MultiProcessTests.cs | 87 +- src/LightningDB.Tests/SharedFileSystem.cs | 43 +- src/LightningDB.Tests/TestHelperExtensions.cs | 108 ++- src/LightningDB.Tests/TransactionTests.cs | 358 ++++--- src/LightningDB/CursorDeleteOption.cs | 31 +- src/LightningDB/CursorOperation.cs | 191 ++-- src/LightningDB/CursorPutOptions.cs | 95 +- src/LightningDB/DatabaseConfiguration.cs | 104 +- src/LightningDB/DatabaseOpenFlags.cs | 91 +- src/LightningDB/EnvironmentConfiguration.cs | 93 +- src/LightningDB/EnvironmentCopyFlags.cs | 19 +- src/LightningDB/EnvironmentInfo.cs | 33 +- src/LightningDB/EnvironmentOpenFlags.cs | 157 ++- src/LightningDB/LightningCursor.cs | 891 +++++++++--------- src/LightningDB/LightningDB.csproj | 5 +- src/LightningDB/LightningDatabase.cs | 223 +++-- src/LightningDB/LightningEnvironment.cs | 510 +++++----- src/LightningDB/LightningException.cs | 41 +- src/LightningDB/LightningExtensions.cs | 272 +++--- src/LightningDB/LightningTransaction.cs | 658 +++++++------ src/LightningDB/LightningTransactionState.cs | 43 +- src/LightningDB/LightningVersionInfo.cs | 71 +- src/LightningDB/MDBResultCode.cs | 247 +++-- src/LightningDB/MDBValue.cs | 83 +- src/LightningDB/Native/CompareFunction.cs | 9 +- src/LightningDB/Native/Lmdb.cs | 358 ++++--- src/LightningDB/Native/MDBEnvInfo.cs | 59 +- src/LightningDB/Native/MDBStat.cs | 59 +- src/LightningDB/PutOptions.cs | 65 +- src/LightningDB/Stats.cs | 57 +- src/LightningDB/TransactionBeginFlags.cs | 69 +- src/LightningDB/UnixAccessMode.cs | 113 ++- src/SecondProcess/Program.cs | 31 +- src/SecondProcess/SecondProcess.csproj | 1 + 46 files changed, 3532 insertions(+), 3469 deletions(-) diff --git a/src/LightningDB.Benchmarks/BenchmarkBase.cs b/src/LightningDB.Benchmarks/BenchmarkBase.cs index 5870c90..aa4ea90 100644 --- a/src/LightningDB.Benchmarks/BenchmarkBase.cs +++ b/src/LightningDB.Benchmarks/BenchmarkBase.cs @@ -1,92 +1,85 @@ using System; -using System.Dynamic; using System.IO; - -using BenchmarkDotNet; using BenchmarkDotNet.Attributes; -using LightningDB; -using Microsoft.CodeAnalysis.CSharp.Syntax; +namespace LightningDB.Benchmarks; -namespace LightningDB.Benchmarks +public abstract class BenchmarksBase { - public abstract class BenchmarksBase - { - public LightningEnvironment Env { get; set; } - public LightningDatabase DB { get; set; } + public LightningEnvironment Env { get; set; } + public LightningDatabase DB { get; set; } - [GlobalSetup] - public void GlobalSetup() - { - Console.WriteLine("Global Setup Begin"); + [GlobalSetup] + public void GlobalSetup() + { + Console.WriteLine("Global Setup Begin"); - const string Path = "TestDirectory"; + const string Path = "TestDirectory"; - if (Directory.Exists(Path)) - Directory.Delete(Path, true); + if (Directory.Exists(Path)) + Directory.Delete(Path, true); - Env = new LightningEnvironment(Path) { - MaxDatabases = 1 - }; + Env = new LightningEnvironment(Path) { + MaxDatabases = 1 + }; - Env.Open(); + Env.Open(); + + using (var tx = Env.BeginTransaction()) { + DB = tx.OpenDatabase(); + tx.Commit(); + } + + RunSetup(); - using (var tx = Env.BeginTransaction()) { - DB = tx.OpenDatabase(); - tx.Commit(); - } + Console.WriteLine("Global Setup End"); + } - RunSetup(); + public abstract void RunSetup(); - Console.WriteLine("Global Setup End"); - } + [GlobalCleanup] + public void GlobalCleanup() + { + Console.WriteLine("Global Cleanup Begin"); - public abstract void RunSetup(); - - [GlobalCleanup] - public void GlobalCleanup() - { - Console.WriteLine("Global Cleanup Begin"); - - try { - DB.Dispose(); - Env.Dispose(); - } - catch(Exception ex) { - Console.WriteLine(ex.ToString()); - } - Console.WriteLine("Global Cleanup End"); + try { + DB.Dispose(); + Env.Dispose(); + } + catch(Exception ex) { + Console.WriteLine(ex.ToString()); } + Console.WriteLine("Global Cleanup End"); } +} - public abstract class RWBenchmarksBase : BenchmarksBase - { - //***** Argument Matrix Start *****// - [Params(1, 100, 1000)] - public int OpsPerTransaction { get; set; } +public abstract class RWBenchmarksBase : BenchmarksBase +{ + //***** Argument Matrix Start *****// + [Params(1, 100, 1000)] + public int OpsPerTransaction { get; set; } - [Params(8, 64, 256)] - public int ValueSize { get; set; } + [Params(8, 64, 256)] + public int ValueSize { get; set; } - [Params(KeyOrdering.Sequential)] - public KeyOrdering KeyOrder { get; set; } + [Params(KeyOrdering.Sequential)] + public KeyOrdering KeyOrder { get; set; } - //***** Argument Matrix End *****// + //***** Argument Matrix End *****// - //***** Test Values Begin *****// + //***** Test Values Begin *****// - protected byte[] ValueBuffer { get; private set; } - protected KeyBatch KeyBuffers { get; private set; } + protected byte[] ValueBuffer { get; private set; } + protected KeyBatch KeyBuffers { get; private set; } - //***** Test Values End *****// + //***** Test Values End *****// - public override void RunSetup() - { - ValueBuffer = new byte[ValueSize]; - KeyBuffers = KeyBatch.Generate(OpsPerTransaction, KeyOrder); - } + public override void RunSetup() + { + ValueBuffer = new byte[ValueSize]; + KeyBuffers = KeyBatch.Generate(OpsPerTransaction, KeyOrder); } -} +} \ No newline at end of file diff --git a/src/LightningDB.Benchmarks/KeyBatch.cs b/src/LightningDB.Benchmarks/KeyBatch.cs index 4979589..527bcce 100644 --- a/src/LightningDB.Benchmarks/KeyBatch.cs +++ b/src/LightningDB.Benchmarks/KeyBatch.cs @@ -2,80 +2,78 @@ using System.Collections.Generic; using System.Runtime.InteropServices; -namespace LightningDB.Benchmarks +namespace LightningDB.Benchmarks; + +public enum KeyOrdering { + Sequential, + Random +} - public enum KeyOrdering +/// +/// A collection of 4 byte key arrays +/// +public class KeyBatch +{ + private KeyBatch(byte[][] buffers) { - Sequential, - Random + Buffers = buffers; } - /// - /// A collection of 4 byte key arrays - /// - public class KeyBatch - { - private KeyBatch(byte[][] buffers) - { - Buffers = buffers; - } + public byte[][] Buffers { get; } - public byte[][] Buffers { get; } + public int Count => Buffers.Length; + public ref byte[] this[int index] => ref Buffers[index]; - public int Count => Buffers.Length; - public ref byte[] this[int index] => ref Buffers[index]; + public static KeyBatch Generate(int keyCount, KeyOrdering keyOrdering) + { + var buffers = new byte[keyCount][]; - public static KeyBatch Generate(int keyCount, KeyOrdering keyOrdering) - { - var buffers = new byte[keyCount][]; - - switch (keyOrdering) { - case KeyOrdering.Sequential: - PopulateSequential(buffers); - break; - - case KeyOrdering.Random: - PopulateRandom(buffers); - break; + switch (keyOrdering) { + case KeyOrdering.Sequential: + PopulateSequential(buffers); + break; - default: - throw new ArgumentException("That isn't a valid KeyOrdering", nameof(keyOrdering)); - } + case KeyOrdering.Random: + PopulateRandom(buffers); + break; - return new KeyBatch(buffers); + default: + throw new ArgumentException("That isn't a valid KeyOrdering", nameof(keyOrdering)); } - private static void PopulateSequential(byte[][] buffers) - { - for (int i = 0; i < buffers.Length; i++) { - buffers[i] = CopyToArray(i); - } + return new KeyBatch(buffers); + } + + private static void PopulateSequential(byte[][] buffers) + { + for (var i = 0; i < buffers.Length; i++) { + buffers[i] = CopyToArray(i); } + } - private static void PopulateRandom(byte[][] buffers) - { - var random = new Random(0); - var seen = new HashSet(buffers.Length); + private static void PopulateRandom(byte[][] buffers) + { + var random = new Random(0); + var seen = new HashSet(buffers.Length); - int i = 0; - while (i < buffers.Length) { - var keyValue = random.Next(0, buffers.Length); + var i = 0; + while (i < buffers.Length) { + var keyValue = random.Next(0, buffers.Length); - if (!seen.Add(keyValue)) - continue;//skip duplicates + if (!seen.Add(keyValue)) + continue;//skip duplicates - buffers[i++] = CopyToArray(keyValue); - } + buffers[i++] = CopyToArray(keyValue); } + } - private static byte[] CopyToArray(int keyValue) - { - var key = new byte[4]; - MemoryMarshal.Write(key, ref keyValue); - return key; - } + private static byte[] CopyToArray(int keyValue) + { + var key = new byte[4]; + MemoryMarshal.Write(key, ref keyValue); + return key; } -} +} \ No newline at end of file diff --git a/src/LightningDB.Benchmarks/LightningDB.Benchmarks.csproj b/src/LightningDB.Benchmarks/LightningDB.Benchmarks.csproj index a7aac07..423b168 100644 --- a/src/LightningDB.Benchmarks/LightningDB.Benchmarks.csproj +++ b/src/LightningDB.Benchmarks/LightningDB.Benchmarks.csproj @@ -3,25 +3,18 @@ Exe net6.0;net7.0 + 11 + .\ - + - - - - - - - - - - - + + diff --git a/src/LightningDB.Benchmarks/Main.cs b/src/LightningDB.Benchmarks/Main.cs index 2c676e3..f2d89f3 100644 --- a/src/LightningDB.Benchmarks/Main.cs +++ b/src/LightningDB.Benchmarks/Main.cs @@ -1,13 +1,12 @@ -using System; using BenchmarkDotNet.Running; -namespace LightningDB.Benchmarks { - public static class Entry +namespace LightningDB.Benchmarks; + +public static class Entry +{ + public static void Main(string[] args) { - public static void Main(string[] args) - { - //BenchmarkRunner.Run(); - BenchmarkRunner.Run(); - } + //BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } } \ No newline at end of file diff --git a/src/LightningDB.Benchmarks/ReadBenchmarks.cs b/src/LightningDB.Benchmarks/ReadBenchmarks.cs index ded31e7..9296491 100644 --- a/src/LightningDB.Benchmarks/ReadBenchmarks.cs +++ b/src/LightningDB.Benchmarks/ReadBenchmarks.cs @@ -1,31 +1,30 @@  using BenchmarkDotNet.Attributes; -namespace LightningDB.Benchmarks +namespace LightningDB.Benchmarks; + +[MemoryDiagnoser] +public class ReadBenchmarks : RWBenchmarksBase { - [MemoryDiagnoser] - public class ReadBenchmarks : RWBenchmarksBase + public override void RunSetup() { - public override void RunSetup() - { - base.RunSetup(); + base.RunSetup(); - //setup data to read - using var tx = Env.BeginTransaction(); - for (int i = 0; i < KeyBuffers.Count; i++) - tx.Put(DB, KeyBuffers[i], ValueBuffer); + //setup data to read + using var tx = Env.BeginTransaction(); + for (var i = 0; i < KeyBuffers.Count; i++) + tx.Put(DB, KeyBuffers[i], ValueBuffer); - tx.Commit(); - } + tx.Commit(); + } - [Benchmark] - public void Read() - { - using var transaction = Env.BeginTransaction(beginFlags: TransactionBeginFlags.ReadOnly); + [Benchmark] + public void Read() + { + using var transaction = Env.BeginTransaction(beginFlags: TransactionBeginFlags.ReadOnly); - for (int i = 0; i < OpsPerTransaction; i++) { - var _ = transaction.Get(DB, KeyBuffers[i]); - } + for (var i = 0; i < OpsPerTransaction; i++) { + var _ = transaction.Get(DB, KeyBuffers[i]); } } -} +} \ No newline at end of file diff --git a/src/LightningDB.Benchmarks/WriteBenchmarks.cs b/src/LightningDB.Benchmarks/WriteBenchmarks.cs index 3c4bb09..32551cf 100644 --- a/src/LightningDB.Benchmarks/WriteBenchmarks.cs +++ b/src/LightningDB.Benchmarks/WriteBenchmarks.cs @@ -1,24 +1,19 @@ -using System.Collections; +using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Attributes; +namespace LightningDB.Benchmarks; -using Microsoft.Diagnostics.Tracing.Parsers.MicrosoftWindowsTCPIP; - -namespace LightningDB.Benchmarks +[MemoryDiagnoser] +public class WriteBenchmarks : RWBenchmarksBase { - [MemoryDiagnoser] - public class WriteBenchmarks : RWBenchmarksBase + [Benchmark] + public void Write() { - [Benchmark] - public void Write() - { - using var transaction = Env.BeginTransaction(); - - for (int i = 0; i < OpsPerTransaction; i++) { - transaction.Put(DB, KeyBuffers[i], ValueBuffer); - } + using var transaction = Env.BeginTransaction(); - transaction.Commit(); + for (var i = 0; i < OpsPerTransaction; i++) { + transaction.Put(DB, KeyBuffers[i], ValueBuffer); } + + transaction.Commit(); } -} +} \ No newline at end of file diff --git a/src/LightningDB.Tests/ConditionalFacts.cs b/src/LightningDB.Tests/ConditionalFacts.cs index 69b3f63..72b819c 100644 --- a/src/LightningDB.Tests/ConditionalFacts.cs +++ b/src/LightningDB.Tests/ConditionalFacts.cs @@ -9,7 +9,7 @@ public WindowsOnlyFactAttribute() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Skip = $"Skipped for for non-Windows OS"; + Skip = "Skipped for for non-Windows OS"; } } } diff --git a/src/LightningDB.Tests/CursorTests.cs b/src/LightningDB.Tests/CursorTests.cs index 1018185..1ced1af 100644 --- a/src/LightningDB.Tests/CursorTests.cs +++ b/src/LightningDB.Tests/CursorTests.cs @@ -3,364 +3,363 @@ using Xunit; using static System.Text.Encoding; -namespace LightningDB.Tests +namespace LightningDB.Tests; + +[Collection("SharedFileSystem")] +public class CursorTests : IDisposable { - [Collection("SharedFileSystem")] - public class CursorTests : IDisposable + private readonly LightningEnvironment _env; + + public CursorTests(SharedFileSystem fileSystem) { - private readonly LightningEnvironment _env; + var path = fileSystem.CreateNewDirectoryForTest(); + _env = new LightningEnvironment(path); + _env.Open(); + } - public CursorTests(SharedFileSystem fileSystem) - { - var path = fileSystem.CreateNewDirectoryForTest(); - _env = new LightningEnvironment(path); - _env.Open(); - } + public void Dispose() + { + _env.Dispose(); + } + + private static byte[][] PopulateCursorValues(LightningCursor cursor, int count = 5, string keyPrefix = "key") + { + var keys = Enumerable.Range(1, count) + .Select(i => UTF8.GetBytes(keyPrefix + i)) + .ToArray(); - public void Dispose() + foreach (var k in keys) { - _env.Dispose(); + var result = cursor.Put(k, k, CursorPutOptions.None); + Assert.Equal(MDBResultCode.Success, result); } - private static byte[][] PopulateCursorValues(LightningCursor cursor, int count = 5, string keyPrefix = "key") - { - var keys = Enumerable.Range(1, count) - .Select(i => UTF8.GetBytes(keyPrefix + i)) - .ToArray(); + return keys; + } - foreach (var k in keys) - { - var result = cursor.Put(k, k, CursorPutOptions.None); - Assert.Equal(MDBResultCode.Success, result); - } + private static byte[][] PopulateMultipleCursorValues(LightningCursor cursor, string key = "TestKey") + { + var values = Enumerable.Range(1, 5).Select(BitConverter.GetBytes).ToArray(); + var result = cursor.Put(UTF8.GetBytes(key), values); + Assert.Equal(MDBResultCode.Success, result); + var notDuplicate = values[0]; + result = cursor.Put(notDuplicate, notDuplicate, CursorPutOptions.NoDuplicateData); + Assert.Equal(MDBResultCode.Success, result); + return values; + } - return keys; - } + [Fact] + public void CursorShouldBeCreated() + { + _env.RunCursorScenario((_, _, c) => Assert.NotNull(c)); + } - private static byte[][] PopulateMultipleCursorValues(LightningCursor cursor, string key = "TestKey") + [Fact] + public void CursorShouldPutValues() + { + _env.RunCursorScenario((tx, _, c) => { - var values = Enumerable.Range(1, 5).Select(BitConverter.GetBytes).ToArray(); - var result = cursor.Put(UTF8.GetBytes(key), values); - Assert.Equal(MDBResultCode.Success, result); - var notDuplicate = values[0]; - result = cursor.Put(notDuplicate, notDuplicate, CursorPutOptions.NoDuplicateData); + PopulateCursorValues(c); + c.Dispose(); + //TODO evaluate how not to require this Dispose likely due to #155 + var result = tx.Commit(); Assert.Equal(MDBResultCode.Success, result); - return values; - } - - [Fact] - public void CursorShouldBeCreated() - { - _env.RunCursorScenario((tx, db, c) => Assert.NotNull(c)); - } - - [Fact] - public void CursorShouldPutValues() - { - _env.RunCursorScenario((tx, db, c) => - { - PopulateCursorValues(c); - c.Dispose(); - //TODO evaluate how not to require this Dispose on Linux (test only fails there) - var result = tx.Commit(); - Assert.Equal(MDBResultCode.Success, result); - }); - } + }); + } - [Fact] - public void CursorShouldSetSpanKey() + [Fact] + public void CursorShouldSetSpanKey() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var keys = PopulateCursorValues(c); - var firstKey = keys.First(); - var result = c.Set(firstKey.AsSpan()); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(firstKey, current.key.CopyToNewArray()); - }); - } + var keys = PopulateCursorValues(c); + var firstKey = keys.First(); + var result = c.Set(firstKey.AsSpan()); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(firstKey, current.key.CopyToNewArray()); + }); + } - [Fact] - public void CursorShouldMoveToLast() + [Fact] + public void CursorShouldMoveToLast() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var keys = PopulateCursorValues(c); - var lastKey = keys.Last(); - var result = c.Last(); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(lastKey, current.key.CopyToNewArray()); - }); - } + var keys = PopulateCursorValues(c); + var lastKey = keys.Last(); + var result = c.Last(); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(lastKey, current.key.CopyToNewArray()); + }); + } - [Fact] - public void CursorShouldMoveToFirst() + [Fact] + public void CursorShouldMoveToFirst() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var keys = PopulateCursorValues(c); - var firstKey = keys.First(); - var result = c.First(); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(firstKey, current.key.CopyToNewArray()); - }); - } + var keys = PopulateCursorValues(c); + var firstKey = keys.First(); + var result = c.First(); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(firstKey, current.key.CopyToNewArray()); + }); + } - [Fact] - public void ShouldIterateThroughCursor() + [Fact] + public void ShouldIterateThroughCursor() + { + _env.RunCursorScenario((tx, db, c) => { - _env.RunCursorScenario((tx, db, c) => + var keys = PopulateCursorValues(c); + using var c2 = tx.CreateCursor(db); + var items = c2.AsEnumerable().Select((x, i) => (x, i)).ToList(); + foreach (var (x, i) in items) { - var keys = PopulateCursorValues(c); - using var c2 = tx.CreateCursor(db); - var items = c2.AsEnumerable().Select((x, i) => (x, i)).ToList(); - foreach (var (x, i) in items) - { - Assert.Equal(keys[i], x.Item1.CopyToNewArray()); - } + Assert.Equal(keys[i], x.Item1.CopyToNewArray()); + } - Assert.Equal(keys.Length, items.Count); - }); - } + Assert.Equal(keys.Length, items.Count); + }); + } - [Fact] - public void CursorShouldDeleteElements() + [Fact] + public void CursorShouldDeleteElements() + { + _env.RunCursorScenario((tx, db, c) => { - _env.RunCursorScenario((tx, db, c) => + var keys = PopulateCursorValues(c).Take(2).ToArray(); + for (var i = 0; i < 2; ++i) { - var keys = PopulateCursorValues(c).Take(2).ToArray(); - for (var i = 0; i < 2; ++i) - { - c.Next(); - c.Delete(); - } + c.Next(); + c.Delete(); + } - using var c2 = tx.CreateCursor(db); - Assert.DoesNotContain(c2.AsEnumerable(), x => - keys.Any(k => x.Item1.CopyToNewArray() == k)); - }); - } + using var c2 = tx.CreateCursor(db); + Assert.DoesNotContain(c2.AsEnumerable(), x => + keys.Any(k => x.Item1.CopyToNewArray() == k)); + }); + } - [Fact] - public void ShouldPutMultiple() - { - _env.RunCursorScenario((tx, db, c) => { PopulateMultipleCursorValues(c); }, - DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + [Fact] + public void ShouldPutMultiple() + { + _env.RunCursorScenario((_, _, c) => { PopulateMultipleCursorValues(c); }, + DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldGetMultiple() + [Fact] + public void ShouldGetMultiple() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey"); - var keys = PopulateMultipleCursorValues(c); - c.Set(key); - c.NextDuplicate(); - var (resultCode, _, value) = c.GetMultiple(); - Assert.Equal(MDBResultCode.Success, resultCode); - Assert.Equal(keys, value.CopyToNewArray().Split(sizeof(int)).ToArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray(); + var keys = PopulateMultipleCursorValues(c); + c.Set(key); + c.NextDuplicate(); + var (resultCode, _, value) = c.GetMultiple(); + Assert.Equal(MDBResultCode.Success, resultCode); + Assert.Equal(keys, value.CopyToNewArray().Split(sizeof(int)).ToArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldGetNextMultiple() + [Fact] + public void ShouldGetNextMultiple() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey"); - var keys = PopulateMultipleCursorValues(c); - c.Set(key); - var (resultCode, _, value) = c.NextMultiple(); - Assert.Equal(MDBResultCode.Success, resultCode); - Assert.Equal(keys, value.CopyToNewArray().Split(sizeof(int)).ToArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray(); + var keys = PopulateMultipleCursorValues(c); + c.Set(key); + var (resultCode, _, value) = c.NextMultiple(); + Assert.Equal(MDBResultCode.Success, resultCode); + Assert.Equal(keys, value.CopyToNewArray().Split(sizeof(int)).ToArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldAdvanceKeyToClosestWhenKeyNotFound() + [Fact] + public void ShouldAdvanceKeyToClosestWhenKeyNotFound() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var expected = PopulateCursorValues(c).First(); - var result = c.Set(UTF8.GetBytes("key")); - Assert.Equal(MDBResultCode.NotFound, result); - var (_, key, _) = c.GetCurrent(); - Assert.Equal(expected, key.CopyToNewArray()); - }); - } + var expected = PopulateCursorValues(c).First(); + var result = c.Set("key"u8.ToArray()); + Assert.Equal(MDBResultCode.NotFound, result); + var (_, key, _) = c.GetCurrent(); + Assert.Equal(expected, key.CopyToNewArray()); + }); + } - [Fact] - public void ShouldSetKeyAndGet() + [Fact] + public void ShouldSetKeyAndGet() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var expected = PopulateCursorValues(c).ElementAt(2); - var result = c.SetKey(expected); - Assert.Equal(MDBResultCode.Success, result.resultCode); - Assert.Equal(expected, result.key.CopyToNewArray()); - }); - } + var expected = PopulateCursorValues(c).ElementAt(2); + var result = c.SetKey(expected); + Assert.Equal(MDBResultCode.Success, result.resultCode); + Assert.Equal(expected, result.key.CopyToNewArray()); + }); + } - [Fact] - public void ShouldSetKeyAndGetWithSpan() + [Fact] + public void ShouldSetKeyAndGetWithSpan() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var expected = PopulateCursorValues(c).ElementAt(2); - var result = c.SetKey(expected.AsSpan()); - Assert.Equal(MDBResultCode.Success, result.resultCode); - Assert.Equal(expected, result.key.CopyToNewArray()); - }); - } + var expected = PopulateCursorValues(c).ElementAt(2); + var result = c.SetKey(expected.AsSpan()); + Assert.Equal(MDBResultCode.Success, result.resultCode); + Assert.Equal(expected, result.key.CopyToNewArray()); + }); + } - [Fact] - public void ShouldGetBoth() + [Fact] + public void ShouldGetBoth() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var expected = PopulateCursorValues(c).ElementAt(2); - var result = c.GetBoth(expected, expected); - Assert.Equal(MDBResultCode.Success, result); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var expected = PopulateCursorValues(c).ElementAt(2); + var result = c.GetBoth(expected, expected); + Assert.Equal(MDBResultCode.Success, result); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldGetBothWithSpan() + [Fact] + public void ShouldGetBothWithSpan() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var expected = PopulateCursorValues(c).ElementAt(2); - var expectedSpan = expected.AsSpan(); - var result = c.GetBoth(expectedSpan, expectedSpan); - Assert.Equal(MDBResultCode.Success, result); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var expected = PopulateCursorValues(c).ElementAt(2); + var expectedSpan = expected.AsSpan(); + var result = c.GetBoth(expectedSpan, expectedSpan); + Assert.Equal(MDBResultCode.Success, result); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldMoveToPrevious() + [Fact] + public void ShouldMoveToPrevious() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var expected = PopulateCursorValues(c).ElementAt(2); - var expectedSpan = expected.AsSpan(); - c.GetBoth(expectedSpan, expectedSpan); - var result = c.Previous(); - Assert.Equal(MDBResultCode.Success, result); - }); - } + var expected = PopulateCursorValues(c).ElementAt(2); + var expectedSpan = expected.AsSpan(); + c.GetBoth(expectedSpan, expectedSpan); + var result = c.Previous(); + Assert.Equal(MDBResultCode.Success, result); + }); + } - [Fact] - public void ShouldSetRangeWithSpan() + [Fact] + public void ShouldSetRangeWithSpan() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var values = PopulateCursorValues(c); - var firstAfter = values[0].AsSpan(); - var result = c.SetRange(firstAfter); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(values[0], current.value.CopyToNewArray()); - }); - } + var values = PopulateCursorValues(c); + var firstAfter = values[0].AsSpan(); + var result = c.SetRange(firstAfter); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(values[0], current.value.CopyToNewArray()); + }); + } - [Fact] - public void ShouldGetBothRange() + [Fact] + public void ShouldGetBothRange() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey"); - var values = PopulateMultipleCursorValues(c); - var result = c.GetBothRange(key, values[1]); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(values[1], current.value.CopyToNewArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray(); + var values = PopulateMultipleCursorValues(c); + var result = c.GetBothRange(key, values[1]); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(values[1], current.value.CopyToNewArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldGetBothRangeWithSpan() + [Fact] + public void ShouldGetBothRangeWithSpan() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey").AsSpan(); - var values = PopulateMultipleCursorValues(c); - var result = c.GetBothRange(key, values[1].AsSpan()); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(values[1], current.value.CopyToNewArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray().AsSpan(); + var values = PopulateMultipleCursorValues(c); + var result = c.GetBothRange(key, values[1].AsSpan()); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(values[1], current.value.CopyToNewArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldMoveToFirstDuplicate() + [Fact] + public void ShouldMoveToFirstDuplicate() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey"); - var values = PopulateMultipleCursorValues(c); - var result = c.GetBothRange(key, values[1]); - Assert.Equal(MDBResultCode.Success, result); - result = c.FirstDuplicate(); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(values[0], current.value.CopyToNewArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray(); + var values = PopulateMultipleCursorValues(c); + var result = c.GetBothRange(key, values[1]); + Assert.Equal(MDBResultCode.Success, result); + result = c.FirstDuplicate(); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(values[0], current.value.CopyToNewArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldMoveToLastDuplicate() + [Fact] + public void ShouldMoveToLastDuplicate() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey"); - var values = PopulateMultipleCursorValues(c); - c.Set(key); - var result = c.LastDuplicate(); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(values[4], current.value.CopyToNewArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray(); + var values = PopulateMultipleCursorValues(c); + c.Set(key); + var result = c.LastDuplicate(); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(values[4], current.value.CopyToNewArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldMoveToNextNoDuplicate() + [Fact] + public void ShouldMoveToNextNoDuplicate() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var values = PopulateMultipleCursorValues(c); - var result = c.NextNoDuplicate(); - Assert.Equal(MDBResultCode.Success, result); - var current = c.GetCurrent(); - Assert.Equal(values[0], current.value.CopyToNewArray()); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var values = PopulateMultipleCursorValues(c); + var result = c.NextNoDuplicate(); + Assert.Equal(MDBResultCode.Success, result); + var current = c.GetCurrent(); + Assert.Equal(values[0], current.value.CopyToNewArray()); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); + } - [Fact] - public void ShouldRenewSameTransaction() + [Fact] + public void ShouldRenewSameTransaction() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var result = c.Renew(); - Assert.Equal(MDBResultCode.Success, result); - }, transactionFlags: TransactionBeginFlags.ReadOnly); - } + var result = c.Renew(); + Assert.Equal(MDBResultCode.Success, result); + }, transactionFlags: TransactionBeginFlags.ReadOnly); + } - [Fact] - public void ShouldDeleteDuplicates() + [Fact] + public void ShouldDeleteDuplicates() + { + _env.RunCursorScenario((_, _, c) => { - _env.RunCursorScenario((tx, db, c) => - { - var key = UTF8.GetBytes("TestKey"); - PopulateMultipleCursorValues(c); - c.Set(key); - c.DeleteDuplicateData(); - var result = c.Set(key); - Assert.Equal(MDBResultCode.NotFound, result); - }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); - } + var key = "TestKey"u8.ToArray(); + PopulateMultipleCursorValues(c); + c.Set(key); + c.DeleteDuplicateData(); + var result = c.Set(key); + Assert.Equal(MDBResultCode.NotFound, result); + }, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create); } } \ No newline at end of file diff --git a/src/LightningDB.Tests/DatabaseIOTests.cs b/src/LightningDB.Tests/DatabaseIOTests.cs index 7c1563b..399a0e1 100644 --- a/src/LightningDB.Tests/DatabaseIOTests.cs +++ b/src/LightningDB.Tests/DatabaseIOTests.cs @@ -1,151 +1,144 @@ using System; using Xunit; -using static System.Text.Encoding; -namespace LightningDB.Tests +namespace LightningDB.Tests; + +[Collection("SharedFileSystem")] +public class DatabaseIOTests : IDisposable { - [Collection("SharedFileSystem")] - public class DatabaseIOTests : IDisposable - { - private LightningEnvironment _env; - private LightningTransaction _txn; - private LightningDatabase _db; + private readonly LightningEnvironment _env; + private readonly LightningTransaction _txn; + private readonly LightningDatabase _db; - public DatabaseIOTests(SharedFileSystem fileSystem) - { - var path = fileSystem.CreateNewDirectoryForTest(); + public DatabaseIOTests(SharedFileSystem fileSystem) + { + var path = fileSystem.CreateNewDirectoryForTest(); - _env = new LightningEnvironment(path); - _env.MaxDatabases = 2; - _env.Open(); + _env = new LightningEnvironment(path); + _env.MaxDatabases = 2; + _env.Open(); - _txn = _env.BeginTransaction(); - _db = _txn.OpenDatabase(configuration: new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); - } + _txn = _env.BeginTransaction(); + _db = _txn.OpenDatabase(configuration: new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); + } - public void Dispose() - { - _env.Dispose(); - } + public void Dispose() + { + _env.Dispose(); + } - [Fact] - public void DatabasePutShouldNotThrowExceptions() - { - var key = "key"; - var value = "value"; + [Fact] + public void DatabasePutShouldNotThrowExceptions() + { + const string key = "key"; + const string value = "value"; - _txn.Put(_db, key, value); - } + _txn.Put(_db, key, value); + } - [Fact] - public void DatabaseGetShouldNotThrowExceptions() - { - _txn.Get(_db, UTF8.GetBytes("key")); - } + [Fact] + public void DatabaseGetShouldNotThrowExceptions() + { + _txn.Get(_db, "key"u8.ToArray()); + } - [Fact] - public void DatabaseInsertedValueShouldBeRetrievedThen() - { - var key = "key"; - var value = "value"; - _txn.Put(_db, key, value); + [Fact] + public void DatabaseInsertedValueShouldBeRetrievedThen() + { + const string key = "key"; + const string value = "value"; + _txn.Put(_db, key, value); - var persistedValue = _txn.Get(_db, key); + var persistedValue = _txn.Get(_db, key); - Assert.Equal(persistedValue, value); - } + Assert.Equal(value, persistedValue); + } - [Fact] - public void DatabaseDeleteShouldRemoveItem() - { - var key = "key"; - var value = "value"; - _txn.Put(_db, key, value); + [Fact] + public void DatabaseDeleteShouldRemoveItem() + { + const string key = "key"; + const string value = "value"; + _txn.Put(_db, key, value); - _txn.Delete(_db, key); + _txn.Delete(_db, key); - Assert.False(_txn.ContainsKey(_db, key)); - } + Assert.False(_txn.ContainsKey(_db, key)); + } - [Fact] - public void DatabaseDeleteShouldRemoveAllDuplicateDataItems() - { - var fs = new SharedFileSystem(); - using (var env = new LightningEnvironment(fs.CreateNewDirectoryForTest(), configuration: new EnvironmentConfiguration {MapSize = 1024 * 1024, MaxDatabases = 1})) - { - env.Open(); - using (var tx = env.BeginTransaction()) - using (var db = tx.OpenDatabase(configuration: new DatabaseConfiguration() { Flags = DatabaseOpenFlags.DuplicatesSort})) - { - var key = "key"; - var value1 = "value1"; - var value2 = "value2"; - - tx.Put(db, key, value1); - tx.Put(db, key, value2); - - tx.Delete(db, key); - Assert.False(tx.ContainsKey(db, key)); - } - } - } + [Fact] + public void DatabaseDeleteShouldRemoveAllDuplicateDataItems() + { + var fs = new SharedFileSystem(); + using var env = new LightningEnvironment(fs.CreateNewDirectoryForTest(), configuration: new EnvironmentConfiguration {MapSize = 1024 * 1024, MaxDatabases = 1}); + env.Open(); + using var tx = env.BeginTransaction(); + using var db = tx.OpenDatabase(configuration: new DatabaseConfiguration { Flags = DatabaseOpenFlags.DuplicatesSort}); + const string key = "key"; + const string value1 = "value1"; + const string value2 = "value2"; + + tx.Put(db, key, value1); + tx.Put(db, key, value2); + + tx.Delete(db, key); + Assert.False(tx.ContainsKey(db, key)); + } - [Fact] - public void ContainsKeyShouldReturnTrueIfKeyExists() - { - var key = "key"; - var value = "value"; + [Fact] + public void ContainsKeyShouldReturnTrueIfKeyExists() + { + const string key = "key"; + const string value = "value"; - _txn.Put(_db, key, value); + _txn.Put(_db, key, value); - var exists = _txn.ContainsKey(_db, key); + var exists = _txn.ContainsKey(_db, key); - Assert.True(exists); - } + Assert.True(exists); + } - [Fact] - public void ContainsKeyShouldReturnFalseIfKeyNotExists() - { - var key = "key"; + [Fact] + public void ContainsKeyShouldReturnFalseIfKeyNotExists() + { + const string key = "key"; - var exists = _txn.ContainsKey(_db, key); + var exists = _txn.ContainsKey(_db, key); - Assert.False(exists); - } + Assert.False(exists); + } - [Fact] - public void TryGetShouldReturnValueIfKeyExists() - { - var key = "key"; - var value = "value"; + [Fact] + public void TryGetShouldReturnValueIfKeyExists() + { + const string key = "key"; + const string value = "value"; - _txn.Put(_db, key, value); + _txn.Put(_db, key, value); - string persistedValue; - var exists = _txn.TryGet(_db, key, out persistedValue); + var exists = _txn.TryGet(_db, key, out var persistedValue); - Assert.True(exists); - Assert.Equal(value, persistedValue); - } + Assert.True(exists); + Assert.Equal(value, persistedValue); + } - [Fact] - public void CanCommitTransactionToNamedDatabase() + [Fact] + public void CanCommitTransactionToNamedDatabase() + { + using (var db = _txn.OpenDatabase("test", new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) { - using (var db = _txn.OpenDatabase("test", new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) - { - _txn.Put(db, "key1", "value"); + _txn.Put(db, "key1", "value"); - _txn.Commit(); - } + _txn.Commit(); + } - using (var txn2 = _env.BeginTransaction()) + using (var txn2 = _env.BeginTransaction()) + { + using (var db = txn2.OpenDatabase("test")) { - using (var db = txn2.OpenDatabase("test")) - { - var value = txn2.Get(db, "key1"); - Assert.Equal("value", value); - } + var value = txn2.Get(db, "key1"); + Assert.Equal("value", value); } } } -} +} \ No newline at end of file diff --git a/src/LightningDB.Tests/DatabaseTests.cs b/src/LightningDB.Tests/DatabaseTests.cs index 1ca015a..740e947 100644 --- a/src/LightningDB.Tests/DatabaseTests.cs +++ b/src/LightningDB.Tests/DatabaseTests.cs @@ -2,171 +2,170 @@ using Xunit; using static System.Text.Encoding; -namespace LightningDB.Tests +namespace LightningDB.Tests; + +[Collection("SharedFileSystem")] +public class DatabaseTests : IDisposable { - [Collection("SharedFileSystem")] - public class DatabaseTests : IDisposable - { - private LightningEnvironment _env; - private LightningTransaction _txn; + private readonly LightningEnvironment _env; + private LightningTransaction _txn; - public DatabaseTests(SharedFileSystem fileSystem) - { - var path = fileSystem.CreateNewDirectoryForTest(); - _env = new LightningEnvironment(path); - } + public DatabaseTests(SharedFileSystem fileSystem) + { + var path = fileSystem.CreateNewDirectoryForTest(); + _env = new LightningEnvironment(path); + } - public void Dispose() + public void Dispose() + { + _env.Dispose(); + } + + [Fact] + public void DatabaseShouldBeCreated() + { + const string dbName = "test"; + _env.MaxDatabases = 2; + _env.Open(); + using (var txn = _env.BeginTransaction()) + using (txn.OpenDatabase(dbName, new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) { - _env.Dispose(); + txn.Commit(); } - - [Fact] - public void DatabaseShouldBeCreated() + using (var txn = _env.BeginTransaction()) + using (var db = txn.OpenDatabase(dbName, new DatabaseConfiguration { Flags = DatabaseOpenFlags.None })) { - var dbName = "test"; - _env.MaxDatabases = 2; - _env.Open(); - using (var txn = _env.BeginTransaction()) - using (txn.OpenDatabase(dbName, new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) - { - txn.Commit(); - } - using (var txn = _env.BeginTransaction()) - using (var db = txn.OpenDatabase(dbName, new DatabaseConfiguration { Flags = DatabaseOpenFlags.None })) - { - Assert.False(db.IsReleased); - txn.Commit(); - } + Assert.False(db.IsReleased); + txn.Commit(); } + } - [Fact] - public void DatabaseShouldBeClosed() - { - _env.Open(); - _txn = _env.BeginTransaction(); - var db = _txn.OpenDatabase(); + [Fact] + public void DatabaseShouldBeClosed() + { + _env.Open(); + _txn = _env.BeginTransaction(); + var db = _txn.OpenDatabase(); + + db.Dispose(); - db.Dispose(); + Assert.False(db.IsOpened); + } + + [Fact] + public void DatabaseFromCommittedTransactionShouldBeAccessible() + { + _env.Open(); - Assert.False(db.IsOpened); + LightningDatabase db; + using (var committed = _env.BeginTransaction()) + { + db = committed.OpenDatabase(); + committed.Commit(); } - [Fact] - public void DatabaseFromCommitedTransactionShouldBeAccessable() + using (db) + using (var txn = _env.BeginTransaction()) { - _env.Open(); + txn.Put(db, "key", 1.ToString()); + txn.Commit(); + } + } - LightningDatabase db; - using (var committed = _env.BeginTransaction()) - { - db = committed.OpenDatabase(); - committed.Commit(); - } + [Fact] + public void NamedDatabaseNameExistsInMaster() + { + _env.MaxDatabases = 2; + _env.Open(); - using (db) - using (var txn = _env.BeginTransaction()) - { - txn.Put(db, "key", 1.ToString()); - txn.Commit(); - } + using (var tx = _env.BeginTransaction()) + { + tx.OpenDatabase("customdb", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); + tx.Commit(); } - - [Fact] - public void NamedDatabaseNameExistsInMaster() + using (var tx = _env.BeginTransaction()) { - _env.MaxDatabases = 2; - _env.Open(); - - using (var tx = _env.BeginTransaction()) + var db = tx.OpenDatabase(); + using (var cursor = tx.CreateCursor(db)) { - var db = tx.OpenDatabase("customdb", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); - tx.Commit(); - } - using (var tx = _env.BeginTransaction()) - { - var db = tx.OpenDatabase(); - using (var cursor = tx.CreateCursor(db)) - { - cursor.Next(); - Assert.Equal("customdb", UTF8.GetString(cursor.GetCurrent().key.CopyToNewArray())); - } + cursor.Next(); + Assert.Equal("customdb", UTF8.GetString(cursor.GetCurrent().key.CopyToNewArray())); } } + } - [Fact] - public void ReadonlyTransactionOpenedDatabasesDontGetReused() - { - //This is here to assert that previous issues with the way manager - //classes (since removed) worked don't happen anymore. - _env.MaxDatabases = 2; - _env.Open(); + [Fact] + public void ReadonlyTransactionOpenedDatabasesDontGetReused() + { + //This is here to assert that previous issues with the way manager + //classes (since removed) worked don't happen anymore. + _env.MaxDatabases = 2; + _env.Open(); - using (var tx = _env.BeginTransaction()) - using (var db = tx.OpenDatabase("custom", new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) - { - tx.Put(db, "hello", "world"); - tx.Commit(); - } - using (var tx = _env.BeginTransaction(TransactionBeginFlags.ReadOnly)) - { - var db = tx.OpenDatabase("custom"); - var result = tx.Get(db, "hello"); - Assert.Equal("world", result); - } - using (var tx = _env.BeginTransaction(TransactionBeginFlags.ReadOnly)) - { - var db = tx.OpenDatabase("custom"); - var result = tx.Get(db, "hello"); - Assert.Equal("world", result); - } + using (var tx = _env.BeginTransaction()) + using (var db = tx.OpenDatabase("custom", new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create })) + { + tx.Put(db, "hello", "world"); + tx.Commit(); } - - [Fact] - public void DatabaseShouldBeDropped() + using (var tx = _env.BeginTransaction(TransactionBeginFlags.ReadOnly)) { - _env.MaxDatabases = 2; - _env.Open(); - _txn = _env.BeginTransaction(); - var db = _txn.OpenDatabase("notmaster", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); - _txn.Commit(); - _txn.Dispose(); - db.Dispose(); + var db = tx.OpenDatabase("custom"); + var result = tx.Get(db, "hello"); + Assert.Equal("world", result); + } + using (var tx = _env.BeginTransaction(TransactionBeginFlags.ReadOnly)) + { + var db = tx.OpenDatabase("custom"); + var result = tx.Get(db, "hello"); + Assert.Equal("world", result); + } + } - _txn = _env.BeginTransaction(); - db = _txn.OpenDatabase("notmaster"); + [Fact] + public void DatabaseShouldBeDropped() + { + _env.MaxDatabases = 2; + _env.Open(); + _txn = _env.BeginTransaction(); + var db = _txn.OpenDatabase("notmaster", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); + _txn.Commit(); + _txn.Dispose(); + db.Dispose(); - db.Drop(_txn); - _txn.Commit(); - _txn.Dispose(); + _txn = _env.BeginTransaction(); + db = _txn.OpenDatabase("notmaster"); - _txn = _env.BeginTransaction(); + db.Drop(_txn); + _txn.Commit(); + _txn.Dispose(); - var ex = Assert.Throws(() => _txn.OpenDatabase("notmaster")); + _txn = _env.BeginTransaction(); - Assert.Equal(-30798, ex.StatusCode); - } + var ex = Assert.Throws(() => _txn.OpenDatabase("notmaster")); - [Fact] - public void TruncatingTheDatabase() - { - _env.Open(); - _txn = _env.BeginTransaction(); - var db = _txn.OpenDatabase(); - - _txn.Put(db, "hello", "world"); - _txn.Commit(); - _txn.Dispose(); - _txn = _env.BeginTransaction(); - db = _txn.OpenDatabase(); - db.Truncate(_txn); - _txn.Commit(); - _txn.Dispose(); - _txn = _env.BeginTransaction(); - db = _txn.OpenDatabase(); - var result = _txn.Get(db, UTF8.GetBytes("hello")); - - Assert.Equal(MDBResultCode.NotFound, result.resultCode); - } + Assert.Equal(-30798, ex.StatusCode); + } + + [Fact] + public void TruncatingTheDatabase() + { + _env.Open(); + _txn = _env.BeginTransaction(); + var db = _txn.OpenDatabase(); + + _txn.Put(db, "hello", "world"); + _txn.Commit(); + _txn.Dispose(); + _txn = _env.BeginTransaction(); + db = _txn.OpenDatabase(); + db.Truncate(_txn); + _txn.Commit(); + _txn.Dispose(); + _txn = _env.BeginTransaction(); + db = _txn.OpenDatabase(); + var result = _txn.Get(db, "hello"u8.ToArray()); + + Assert.Equal(MDBResultCode.NotFound, result.resultCode); } -} +} \ No newline at end of file diff --git a/src/LightningDB.Tests/EnvironmentTests.cs b/src/LightningDB.Tests/EnvironmentTests.cs index 9dae5b4..3c1fbbb 100644 --- a/src/LightningDB.Tests/EnvironmentTests.cs +++ b/src/LightningDB.Tests/EnvironmentTests.cs @@ -1,189 +1,185 @@ using System; using System.IO; -using System.Runtime.InteropServices; using Xunit; -namespace LightningDB.Tests -{ - [Collection("SharedFileSystem")] - public class EnvironmentTests : IDisposable - { - private string _path, _pathCopy; - private LightningEnvironment _env; +namespace LightningDB.Tests; - public EnvironmentTests(SharedFileSystem fileSystem) - { - _path = fileSystem.CreateNewDirectoryForTest(); - _pathCopy = fileSystem.CreateNewDirectoryForTest(); - } +[Collection("SharedFileSystem")] +public class EnvironmentTests : IDisposable +{ + private readonly string _path, _pathCopy; + private LightningEnvironment _env; - public void Dispose() - { - if (_env != null) - _env.Dispose(); + public EnvironmentTests(SharedFileSystem fileSystem) + { + _path = fileSystem.CreateNewDirectoryForTest(); + _pathCopy = fileSystem.CreateNewDirectoryForTest(); + } - _env = null; - } + public void Dispose() + { + _env?.Dispose(); + _env = null; + } - [Fact] - public void EnvironmentShouldBeCreatedIfWithoutFlags() - { - _env = new LightningEnvironment(_path); - _env.Open(); - } + [Fact] + public void EnvironmentShouldBeCreatedIfWithoutFlags() + { + _env = new LightningEnvironment(_path); + _env.Open(); + } - [Fact] - public void EnvironmentCreatedFromConfig() - { - var mapExpected = 1024*1024*20; - var maxDatabaseExpected = 2; - var maxReadersExpected = 3; - var config = new EnvironmentConfiguration {MapSize = mapExpected, MaxDatabases = maxDatabaseExpected, MaxReaders = maxReadersExpected}; - _env = new LightningEnvironment(_path, config); - Assert.Equal(_env.MapSize, mapExpected); - Assert.Equal(_env.MaxDatabases, maxDatabaseExpected); - Assert.Equal(_env.MaxReaders, maxReadersExpected); - } + [Fact] + public void EnvironmentCreatedFromConfig() + { + const int mapExpected = 1024*1024*20; + const int maxDatabaseExpected = 2; + const int maxReadersExpected = 3; + var config = new EnvironmentConfiguration {MapSize = mapExpected, MaxDatabases = maxDatabaseExpected, MaxReaders = maxReadersExpected}; + _env = new LightningEnvironment(_path, config); + Assert.Equal(mapExpected, _env.MapSize); + Assert.Equal(maxDatabaseExpected, _env.MaxDatabases); + Assert.Equal(maxReadersExpected, _env.MaxReaders); + } - [Fact] - public void StartingTransactionBeforeEnvironmentOpen() - { - _env = new LightningEnvironment(_path); - Assert.Throws(() => _env.BeginTransaction()); - } + [Fact] + public void StartingTransactionBeforeEnvironmentOpen() + { + _env = new LightningEnvironment(_path); + Assert.Throws(() => _env.BeginTransaction()); + } - [Fact] - public void CanGetEnvironmentInfo() + [Fact] + public void CanGetEnvironmentInfo() + { + const long mapSize = 1024 * 1024 * 200; + _env = new LightningEnvironment(_path, new EnvironmentConfiguration { - long mapSize = 1024 * 1024 * 200; - _env = new LightningEnvironment(_path, new EnvironmentConfiguration - { - MapSize = mapSize, - }); - _env.Open(); - var info = _env.Info; - Assert.Equal(_env.MapSize, info.MapSize); - } + MapSize = mapSize + }); + _env.Open(); + var info = _env.Info; + Assert.Equal(_env.MapSize, info.MapSize); + } - [Not32BitFact] - public void CanGetLargeEnvironmentInfo() + [Not32BitFact] + public void CanGetLargeEnvironmentInfo() + { + const long mapSize = 1024 * 1024 * 1024 * 3L; + _env = new LightningEnvironment(_path, new EnvironmentConfiguration { - long mapSize = 1024 * 1024 * 1024 * 3L; - _env = new LightningEnvironment(_path, new EnvironmentConfiguration - { - MapSize = mapSize, - }); - _env.Open(); - var info = _env.Info; - Assert.Equal(_env.MapSize, info.MapSize); - } + MapSize = mapSize + }); + _env.Open(); + var info = _env.Info; + Assert.Equal(_env.MapSize, info.MapSize); + } - [Fact] - public void MaxDatabasesWorksThroughConfigIssue62() + [Fact] + public void MaxDatabasesWorksThroughConfigIssue62() + { + var config = new EnvironmentConfiguration { MaxDatabases = 2 }; + _env = new LightningEnvironment(_path, config); + _env.Open(); + using (var tx = _env.BeginTransaction()) { - var config = new EnvironmentConfiguration { MaxDatabases = 2 }; - _env = new LightningEnvironment(_path, config); - _env.Open(); - using (var tx = _env.BeginTransaction()) - { - tx.OpenDatabase("db1", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); - tx.OpenDatabase("db2", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); - tx.Commit(); - } - Assert.Equal(2, _env.MaxDatabases); + tx.OpenDatabase("db1", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); + tx.OpenDatabase("db2", new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}); + tx.Commit(); } + Assert.Equal(2, _env.MaxDatabases); + } - [Fact] - public void CanLoadAndDisposeMultipleEnvironments() - { - _env = new LightningEnvironment(_path); - _env.Dispose(); - _env = new LightningEnvironment(_path); - } + [Fact] + public void CanLoadAndDisposeMultipleEnvironments() + { + _env = new LightningEnvironment(_path); + _env.Dispose(); + _env = new LightningEnvironment(_path); + } - [Fact] - public void EnvironmentShouldBeCreatedIfReadOnly() - { - _env = new LightningEnvironment(_path); - _env.Open(); //readonly requires environment to have been created at least once before - _env.Dispose(); - _env = new LightningEnvironment(_path); - _env.Open(EnvironmentOpenFlags.ReadOnly); - } + [Fact] + public void EnvironmentShouldBeCreatedIfReadOnly() + { + _env = new LightningEnvironment(_path); + _env.Open(); //readonly requires environment to have been created at least once before + _env.Dispose(); + _env = new LightningEnvironment(_path); + _env.Open(EnvironmentOpenFlags.ReadOnly); + } - [Fact] - public void EnvironmentShouldBeOpened() - { - _env = new LightningEnvironment(_path); - _env.Open(); + [Fact] + public void EnvironmentShouldBeOpened() + { + _env = new LightningEnvironment(_path); + _env.Open(); - Assert.True(_env.IsOpened); - } + Assert.True(_env.IsOpened); + } - [Fact] - public void EnvironmentShouldBeClosed() - { - _env = new LightningEnvironment(_path); - _env.Open(); + [Fact] + public void EnvironmentShouldBeClosed() + { + _env = new LightningEnvironment(_path); + _env.Open(); - _env.Dispose(); + _env.Dispose(); - Assert.False(_env.IsOpened); - } + Assert.False(_env.IsOpened); + } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void EnvironmentShouldBeCopied(bool compact) - { - _env = new LightningEnvironment(_path); - _env.Open(); + [Theory] + [InlineData(true)] + [InlineData(false)] + public void EnvironmentShouldBeCopied(bool compact) + { + _env = new LightningEnvironment(_path); + _env.Open(); - _env.CopyTo(_pathCopy, compact); + _env.CopyTo(_pathCopy, compact); - if (Directory.GetFiles(_pathCopy).Length == 0) - Assert.True(false, "Copied files doesn't exist"); - } + if (Directory.GetFiles(_pathCopy).Length == 0) + Assert.True(false, "Copied files doesn't exist"); + } - [Fact] - public void CanOpenEnvironmentMoreThan50Mb() + [Fact] + public void CanOpenEnvironmentMoreThan50Mb() + { + _env = new LightningEnvironment(_path) { - _env = new LightningEnvironment(_path) - { - MapSize = 55 * 1024 * 1024 - }; + MapSize = 55 * 1024 * 1024 + }; - _env.Open(); - } + _env.Open(); + } #if NETCOREAPP3_1_OR_GREATER - [WindowsOnlyFact] - public void CreateEnvironmentWithAutoResize() + [WindowsOnlyFact] + public void CreateEnvironmentWithAutoResize() + { + using (var env = new LightningEnvironment(_path, new EnvironmentConfiguration + { + MapSize = 1048576, + AutoResizeWindows = true + })) { - using (var env = new LightningEnvironment(_path, new EnvironmentConfiguration - { - MapSize = 1048576, - AutoResizeWindows = true, - })) - { - env.Open(); - } - - using (var env = new LightningEnvironment(_path, new EnvironmentConfiguration - { - MapSize = 1048576, - AutoResizeWindows = true, - })) - { - env.Open(); - } - - using (var dbFile = File.OpenRead(Path.Combine(_path, "data.mdb"))) - { - Assert.Equal(8192, dbFile.Length); - } + env.Open(); + } + + using (var env = new LightningEnvironment(_path, new EnvironmentConfiguration + { + MapSize = 1048576, + AutoResizeWindows = true + })) + { + env.Open(); + } + + using (var dbFile = File.OpenRead(Path.Combine(_path, "data.mdb"))) + { + Assert.Equal(8192, dbFile.Length); } -#endif } -} +#endif +} \ No newline at end of file diff --git a/src/LightningDB.Tests/LightningDB.Tests.csproj b/src/LightningDB.Tests/LightningDB.Tests.csproj index dae4b86..7a1839c 100644 --- a/src/LightningDB.Tests/LightningDB.Tests.csproj +++ b/src/LightningDB.Tests/LightningDB.Tests.csproj @@ -2,6 +2,7 @@ net6.0;net7.0 + 11 LightningDB.Tests LightningDB.Tests true @@ -11,6 +12,7 @@ + diff --git a/src/LightningDB.Tests/MultiProcessTests.cs b/src/LightningDB.Tests/MultiProcessTests.cs index 842aa56..c241039 100644 --- a/src/LightningDB.Tests/MultiProcessTests.cs +++ b/src/LightningDB.Tests/MultiProcessTests.cs @@ -1,58 +1,55 @@ -using System; -using System.Diagnostics; +using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; using System.Text; using Xunit; -namespace LightningDB.Tests +namespace LightningDB.Tests; + +[Collection("SharedFileSystem")] +public class MultiProcessTests { - [Collection("SharedFileSystem")] - public class MultiProcessTests - { - private readonly SharedFileSystem _fileSystem; + private readonly SharedFileSystem _fileSystem; - public MultiProcessTests(SharedFileSystem fileSystem) - { - _fileSystem = fileSystem; - } + public MultiProcessTests(SharedFileSystem fileSystem) + { + _fileSystem = fileSystem; + } - [Fact(Skip = "Hangs on Linux only for some reason")] - public void can_load_environment_from_multiple_processes() + [Fact] + public void can_load_environment_from_multiple_processes() + { + var name = _fileSystem.CreateNewDirectoryForTest(); + using var env = new LightningEnvironment(name); + env.Open(); + var otherProcessPath = Path.GetFullPath("SecondProcess.dll"); + using var process = new Process { - var name = _fileSystem.CreateNewDirectoryForTest(); - using var env = new LightningEnvironment(name); - env.Open(); - var otherProcessPath = Path.GetFullPath("../../../../SecondProcess/bin/Debug/netcoreapp3.0/SecondProcess.dll"); - using var process = new Process + StartInfo = new ProcessStartInfo { - StartInfo = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"{otherProcessPath} {name}", - RedirectStandardError = true, - RedirectStandardInput = true, - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true, - WorkingDirectory = Directory.GetCurrentDirectory() - } - }; + FileName = "dotnet", + Arguments = $"{otherProcessPath} {name}", + RedirectStandardError = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + WorkingDirectory = Directory.GetCurrentDirectory() + } + }; - var expected = "world"; - using var tx = env.BeginTransaction(); - using var db = tx.OpenDatabase(); - tx.Put(db, Encoding.UTF8.GetBytes("hello"), Encoding.UTF8.GetBytes(expected)); - tx.Commit(); + const string expected = "world"; + using var tx = env.BeginTransaction(); + using var db = tx.OpenDatabase(); + tx.Put(db, "hello"u8.ToArray(), Encoding.UTF8.GetBytes(expected)); + tx.Commit(); - var current = Process.GetCurrentProcess(); - process.Start(); - Assert.NotEqual(current.Id, process.Id); + var current = Process.GetCurrentProcess(); + process.Start(); + Assert.NotEqual(current.Id, process.Id); - var result = process.StandardOutput.ReadLine(); - process.WaitForExit(); - Assert.Equal(expected, result); - } - + var result = process.StandardOutput.ReadLine(); + process.WaitForExit(); + Assert.Equal(expected, result); } -} + +} \ No newline at end of file diff --git a/src/LightningDB.Tests/SharedFileSystem.cs b/src/LightningDB.Tests/SharedFileSystem.cs index abd0543..0b090db 100644 --- a/src/LightningDB.Tests/SharedFileSystem.cs +++ b/src/LightningDB.Tests/SharedFileSystem.cs @@ -2,35 +2,34 @@ using System.IO; using Xunit; -namespace LightningDB.Tests -{ - public class SharedFileSystem : IDisposable - { - private readonly string _testTempDir; +namespace LightningDB.Tests; - public SharedFileSystem() - { - _testTempDir = Path.Combine(Path.GetTempPath(), "ldbtest"); - } +public class SharedFileSystem : IDisposable +{ + private readonly string _testTempDir; - public void Dispose() - { - if (Directory.Exists(_testTempDir)) - { - Directory.Delete(_testTempDir, true); - } - } + public SharedFileSystem() + { + _testTempDir = Path.Combine(Path.GetTempPath(), "ldbtest"); + } - public string CreateNewDirectoryForTest() + public void Dispose() + { + if (Directory.Exists(_testTempDir)) { - var path = Path.Combine(_testTempDir, "TestDb", Guid.NewGuid().ToString()); - Directory.CreateDirectory(path); - return path; + Directory.Delete(_testTempDir, true); } } - [CollectionDefinition("SharedFileSystem")] - public class SharedFileSystemCollection : ICollectionFixture + public string CreateNewDirectoryForTest() { + var path = Path.Combine(_testTempDir, "TestDb", Guid.NewGuid().ToString()); + Directory.CreateDirectory(path); + return path; } +} + +[CollectionDefinition("SharedFileSystem")] +public class SharedFileSystemCollection : ICollectionFixture +{ } \ No newline at end of file diff --git a/src/LightningDB.Tests/TestHelperExtensions.cs b/src/LightningDB.Tests/TestHelperExtensions.cs index d5189e5..9498b6e 100644 --- a/src/LightningDB.Tests/TestHelperExtensions.cs +++ b/src/LightningDB.Tests/TestHelperExtensions.cs @@ -2,69 +2,67 @@ using System.Linq; using System.Collections.Generic; -namespace LightningDB.Tests +namespace LightningDB.Tests; + +public static class TestHelperExtensions { - public static class TestHelperExtensions + public static MDBResultCode Put(this LightningTransaction tx, LightningDatabase db, string key, string value) { - public static MDBResultCode Put(this LightningTransaction tx, LightningDatabase db, string key, string value) - { - var enc = System.Text.Encoding.UTF8; - return tx.Put(db, enc.GetBytes(key), enc.GetBytes(value)); - } + var enc = System.Text.Encoding.UTF8; + return tx.Put(db, enc.GetBytes(key), enc.GetBytes(value)); + } - public static string Get(this LightningTransaction tx, LightningDatabase db, string key) - { - var enc = System.Text.Encoding.UTF8; - var result = tx.Get(db, enc.GetBytes(key)); - return enc.GetString(result.value.CopyToNewArray()); - } + public static string Get(this LightningTransaction tx, LightningDatabase db, string key) + { + var enc = System.Text.Encoding.UTF8; + var result = tx.Get(db, enc.GetBytes(key)); + return enc.GetString(result.value.CopyToNewArray()); + } - public static void Delete(this LightningTransaction tx, LightningDatabase db, string key) - { - var enc = System.Text.Encoding.UTF8; - tx.Delete(db, enc.GetBytes(key)); - } + public static void Delete(this LightningTransaction tx, LightningDatabase db, string key) + { + var enc = System.Text.Encoding.UTF8; + tx.Delete(db, enc.GetBytes(key)); + } - public static bool ContainsKey(this LightningTransaction tx, LightningDatabase db, string key) - { - var enc = System.Text.Encoding.UTF8; - return tx.ContainsKey(db, enc.GetBytes(key)); - } + public static bool ContainsKey(this LightningTransaction tx, LightningDatabase db, string key) + { + var enc = System.Text.Encoding.UTF8; + return tx.ContainsKey(db, enc.GetBytes(key)); + } - public static bool TryGet(this LightningTransaction tx, LightningDatabase db, string key, out string value) - { - var enc = System.Text.Encoding.UTF8; - byte[] result; - var found = tx.TryGet(db, enc.GetBytes(key), out result); - value = enc.GetString(result); - return found; - } + public static bool TryGet(this LightningTransaction tx, LightningDatabase db, string key, out string value) + { + var enc = System.Text.Encoding.UTF8; + var found = tx.TryGet(db, enc.GetBytes(key), out var result); + value = enc.GetString(result); + return found; + } - public static IEnumerable> Split(this IEnumerable list, int parts) - { - return list - .Select((x, i) => new {Index = i, Value = x}) - .GroupBy(x => x.Index / parts) - .Select(x => x.Select(v => v.Value)); - } + public static IEnumerable> Split(this IEnumerable list, int parts) + { + return list + .Select((x, i) => new {Index = i, Value = x}) + .GroupBy(x => x.Index / parts) + .Select(x => x.Select(v => v.Value)); + } - public static void RunCursorScenario(this LightningEnvironment env, - Action scenario, - DatabaseOpenFlags flags = DatabaseOpenFlags.Create, TransactionBeginFlags transactionFlags = TransactionBeginFlags.None) - { - using var tx = env.BeginTransaction(transactionFlags); - using var db = tx.OpenDatabase(configuration: new DatabaseConfiguration { Flags = flags }); - using var cursor = tx.CreateCursor(db); - scenario(tx, db, cursor); - } + public static void RunCursorScenario(this LightningEnvironment env, + Action scenario, + DatabaseOpenFlags flags = DatabaseOpenFlags.Create, TransactionBeginFlags transactionFlags = TransactionBeginFlags.None) + { + using var tx = env.BeginTransaction(transactionFlags); + using var db = tx.OpenDatabase(configuration: new DatabaseConfiguration { Flags = flags }); + using var cursor = tx.CreateCursor(db); + scenario(tx, db, cursor); + } - public static void RunTransactionScenario(this LightningEnvironment env, - Action scenario, - DatabaseOpenFlags flags = DatabaseOpenFlags.Create, TransactionBeginFlags transactionFlags = TransactionBeginFlags.None) - { - using var tx = env.BeginTransaction(transactionFlags); - using var db = tx.OpenDatabase(configuration: new DatabaseConfiguration { Flags = flags }); - scenario(tx, db); - } + public static void RunTransactionScenario(this LightningEnvironment env, + Action scenario, + DatabaseOpenFlags flags = DatabaseOpenFlags.Create, TransactionBeginFlags transactionFlags = TransactionBeginFlags.None) + { + using var tx = env.BeginTransaction(transactionFlags); + using var db = tx.OpenDatabase(configuration: new DatabaseConfiguration { Flags = flags }); + scenario(tx, db); } } \ No newline at end of file diff --git a/src/LightningDB.Tests/TransactionTests.cs b/src/LightningDB.Tests/TransactionTests.cs index 9f896a4..43013f2 100644 --- a/src/LightningDB.Tests/TransactionTests.cs +++ b/src/LightningDB.Tests/TransactionTests.cs @@ -4,223 +4,221 @@ using Xunit; using System.Runtime.InteropServices; -namespace LightningDB.Tests +namespace LightningDB.Tests; + +[Collection("SharedFileSystem")] +public class TransactionTests : IDisposable { - [Collection("SharedFileSystem")] - public class TransactionTests : IDisposable - { - private readonly LightningEnvironment _env; + private readonly LightningEnvironment _env; - public TransactionTests(SharedFileSystem fileSystem) - { - var path = fileSystem.CreateNewDirectoryForTest(); - _env = new LightningEnvironment(path); - _env.Open(); - } + public TransactionTests(SharedFileSystem fileSystem) + { + var path = fileSystem.CreateNewDirectoryForTest(); + _env = new LightningEnvironment(path); + _env.Open(); + } - public void Dispose() - { - _env.Dispose(); - } + public void Dispose() + { + _env.Dispose(); + } - [Fact] - public void CanDeletePreviouslyCommittedWithMultipleValuesByPassingNullForValue() + [Fact] + public void CanDeletePreviouslyCommittedWithMultipleValuesByPassingNullForValue() + { + _env.RunTransactionScenario((tx, db) => { - _env.RunTransactionScenario((tx, db) => - { - var key = MemoryMarshal.Cast("abcd"); - - tx.Put(db, key, MemoryMarshal.Cast("Value1")); - tx.Put(db, key, MemoryMarshal.Cast("Value2"), PutOptions.AppendData); - tx.Commit(); - tx.Dispose(); + var key = MemoryMarshal.Cast("abcd"); + + tx.Put(db, key, MemoryMarshal.Cast("Value1")); + tx.Put(db, key, MemoryMarshal.Cast("Value2"), PutOptions.AppendData); + tx.Commit(); + tx.Dispose(); - using var delTxn = _env.BeginTransaction(); - var result = delTxn.Delete(db, key, null); - Assert.Equal(MDBResultCode.Success, result); - result = delTxn.Commit(); - Assert.Equal(MDBResultCode.Success, result); - }, DatabaseOpenFlags.Create | DatabaseOpenFlags.DuplicatesFixed); - } + using var delTxn = _env.BeginTransaction(); + var result = delTxn.Delete(db, key, null); + Assert.Equal(MDBResultCode.Success, result); + result = delTxn.Commit(); + Assert.Equal(MDBResultCode.Success, result); + }, DatabaseOpenFlags.Create | DatabaseOpenFlags.DuplicatesFixed); + } - [Fact] - public void TransactionShouldBeCreated() + [Fact] + public void TransactionShouldBeCreated() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - Assert.Equal(LightningTransactionState.Active, tx.State); - }); - } + Assert.Equal(LightningTransactionState.Active, tx.State); + }); + } - [Fact] - public void TransactionShouldBeAbortedIfEnvironmentCloses() + [Fact] + public void TransactionShouldBeAbortedIfEnvironmentCloses() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - _env.Dispose(); - Assert.Equal(LightningTransactionState.Aborted, tx.State); - }); - } + _env.Dispose(); + Assert.Equal(LightningTransactionState.Aborted, tx.State); + }); + } - [Fact] - public void TransactionShouldChangeStateOnCommit() + [Fact] + public void TransactionShouldChangeStateOnCommit() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - tx.Commit(); - Assert.Equal(LightningTransactionState.Commited, tx.State); - }); - } + tx.Commit(); + Assert.Equal(LightningTransactionState.Committed, tx.State); + }); + } - [Fact] - public void ChildTransactionShouldBeCreated() + [Fact] + public void ChildTransactionShouldBeCreated() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - var subTxn = tx.BeginTransaction(); - Assert.Equal(LightningTransactionState.Active, subTxn.State); - }); - } + var subTxn = tx.BeginTransaction(); + Assert.Equal(LightningTransactionState.Active, subTxn.State); + }); + } - [Fact] - public void ResetTransactionAbortedOnDispose() + [Fact] + public void ResetTransactionAbortedOnDispose() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - tx.Reset(); - tx.Dispose(); - Assert.Equal(LightningTransactionState.Aborted, tx.State); - }, transactionFlags: TransactionBeginFlags.ReadOnly); - } + tx.Reset(); + tx.Dispose(); + Assert.Equal(LightningTransactionState.Aborted, tx.State); + }, transactionFlags: TransactionBeginFlags.ReadOnly); + } - [Fact] - public void ChildTransactionShouldBeAbortedIfParentIsAborted() + [Fact] + public void ChildTransactionShouldBeAbortedIfParentIsAborted() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - var child = tx.BeginTransaction(); - tx.Abort(); - Assert.Equal(LightningTransactionState.Aborted, child.State); - }); - } + var child = tx.BeginTransaction(); + tx.Abort(); + Assert.Equal(LightningTransactionState.Aborted, child.State); + }); + } - [Fact] - public void ChildTransactionShouldBeAbortedIfParentIsCommited() + [Fact] + public void ChildTransactionShouldBeAbortedIfParentIsCommitted() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - var child = tx.BeginTransaction(); - tx.Commit(); - Assert.Equal(LightningTransactionState.Aborted, child.State); - }); - } + var child = tx.BeginTransaction(); + tx.Commit(); + Assert.Equal(LightningTransactionState.Aborted, child.State); + }); + } - [Fact] - public void ChildTransactionShouldBeAbortedIfEnvironmentIsClosed() + [Fact] + public void ChildTransactionShouldBeAbortedIfEnvironmentIsClosed() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - var child = tx.BeginTransaction(); - _env.Dispose(); - Assert.Equal(LightningTransactionState.Aborted, child.State); - }); - } + var child = tx.BeginTransaction(); + _env.Dispose(); + Assert.Equal(LightningTransactionState.Aborted, child.State); + }); + } - [Fact] - public void ReadOnlyTransactionShouldChangeStateOnReset() + [Fact] + public void ReadOnlyTransactionShouldChangeStateOnReset() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - tx.Reset(); - Assert.Equal(LightningTransactionState.Reseted, tx.State); - }, transactionFlags: TransactionBeginFlags.ReadOnly); - } + tx.Reset(); + Assert.Equal(LightningTransactionState.Reset, tx.State); + }, transactionFlags: TransactionBeginFlags.ReadOnly); + } - [Fact] - public void ReadOnlyTransactionShouldChangeStateOnRenew() + [Fact] + public void ReadOnlyTransactionShouldChangeStateOnRenew() + { + _env.RunTransactionScenario((tx, _) => { - _env.RunTransactionScenario((tx, db) => - { - tx.Reset(); - tx.Renew(); - Assert.Equal(LightningTransactionState.Active, tx.State); - }, transactionFlags: TransactionBeginFlags.ReadOnly); - } + tx.Reset(); + tx.Renew(); + Assert.Equal(LightningTransactionState.Active, tx.State); + }, transactionFlags: TransactionBeginFlags.ReadOnly); + } - [Fact] - public void CanCountTransactionEntries() + [Fact] + public void CanCountTransactionEntries() + { + _env.RunTransactionScenario((tx, db) => { - _env.RunTransactionScenario((tx, db) => - { - const int entriesCount = 10; - for (var i = 0; i < entriesCount; i++) - tx.Put(db, i.ToString(), i.ToString()); - - var count = tx.GetEntriesCount(db); - Assert.Equal(entriesCount, count); - }); - } + const int entriesCount = 10; + for (var i = 0; i < entriesCount; i++) + tx.Put(db, i.ToString(), i.ToString()); + + var count = tx.GetEntriesCount(db); + Assert.Equal(entriesCount, count); + }); + } + + [Fact] + public void TransactionShouldSupportCustomComparer() + { + int Comparison(int l, int r) => l.CompareTo(r); + var options = new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}; + int CompareWith(MDBValue l, MDBValue r) => Comparison(BitConverter.ToInt32(l.CopyToNewArray(), 0), BitConverter.ToInt32(r.CopyToNewArray(), 0)); + options.CompareWith(Comparer.Create(new Comparison((Func)CompareWith))); - [Fact] - public void TransactionShouldSupportCustomComparer() + using (var txnT = _env.BeginTransaction()) + using (var db1 = txnT.OpenDatabase(configuration: options)) { - Func comparison = (l, r) => l.CompareTo(r); - var options = new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create}; - Func compareWith = - (l, r) => comparison(BitConverter.ToInt32(l.CopyToNewArray(), 0), BitConverter.ToInt32(r.CopyToNewArray(), 0)); - options.CompareWith(Comparer.Create(new Comparison(compareWith))); - - using (var txnT = _env.BeginTransaction()) - using (var db1 = txnT.OpenDatabase(configuration: options)) - { - txnT.DropDatabase(db1); - txnT.Commit(); - } - - var txn = _env.BeginTransaction(); - var db = txn.OpenDatabase(configuration: options); - - var keysUnsorted = Enumerable.Range(1, 10000).OrderBy(x => Guid.NewGuid()).ToList(); - var keysSorted = keysUnsorted.ToArray(); - Array.Sort(keysSorted, new Comparison(comparison)); - - GC.Collect(); - for (var i = 0; i < keysUnsorted.Count; i++) - txn.Put(db, BitConverter.GetBytes(keysUnsorted[i]), BitConverter.GetBytes(i)); - - using (var c = txn.CreateCursor(db)) - { - int order = 0; - while (c.Next() == MDBResultCode.Success) - Assert.Equal(keysSorted[order++], BitConverter.ToInt32(c.GetCurrent().key.CopyToNewArray(), 0)); - } + txnT.DropDatabase(db1); + txnT.Commit(); } - [Fact] - public void TransactionShouldSupportCustomDupSorter() + var txn = _env.BeginTransaction(); + var db = txn.OpenDatabase(configuration: options); + + var keysUnsorted = Enumerable.Range(1, 10000).OrderBy(_ => Guid.NewGuid()).ToList(); + var keysSorted = keysUnsorted.ToArray(); + Array.Sort(keysSorted, new Comparison((Func)Comparison)); + + GC.Collect(); + for (var i = 0; i < keysUnsorted.Count; i++) + txn.Put(db, BitConverter.GetBytes(keysUnsorted[i]), BitConverter.GetBytes(i)); + + using (var c = txn.CreateCursor(db)) { - Func comparison = (l, r) => -Math.Sign(l - r); + var order = 0; + while (c.Next() == MDBResultCode.Success) + Assert.Equal(keysSorted[order++], BitConverter.ToInt32(c.GetCurrent().key.CopyToNewArray(), 0)); + } + } - var txn = _env.BeginTransaction(); - var options = new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create | DatabaseOpenFlags.DuplicatesFixed}; - Func compareWith = (l, r) => comparison(BitConverter.ToInt32(l.CopyToNewArray(), 0), BitConverter.ToInt32(r.CopyToNewArray(), 0)); - options.FindDuplicatesWith(Comparer.Create(new Comparison(compareWith))); - var db = txn.OpenDatabase(configuration: options); + [Fact] + public void TransactionShouldSupportCustomDupSorter() + { + int Comparison(int l, int r) => -Math.Sign(l - r); + + var txn = _env.BeginTransaction(); + var options = new DatabaseConfiguration {Flags = DatabaseOpenFlags.Create | DatabaseOpenFlags.DuplicatesFixed}; + int CompareWith(MDBValue l, MDBValue r) => Comparison(BitConverter.ToInt32(l.CopyToNewArray(), 0), BitConverter.ToInt32(r.CopyToNewArray(), 0)); + options.FindDuplicatesWith(Comparer.Create(new Comparison((Func)CompareWith))); + var db = txn.OpenDatabase(configuration: options); - var valuesUnsorted = new [] { 2, 10, 5, 0 }; - var valuesSorted = valuesUnsorted.ToArray(); - Array.Sort(valuesSorted, new Comparison(comparison)); + var valuesUnsorted = new [] { 2, 10, 5, 0 }; + var valuesSorted = valuesUnsorted.ToArray(); + Array.Sort(valuesSorted, new Comparison((Func)Comparison)); - using (var c = txn.CreateCursor(db)) - c.Put(BitConverter.GetBytes(123), valuesUnsorted.Select(BitConverter.GetBytes).ToArray()); + using (var c = txn.CreateCursor(db)) + c.Put(BitConverter.GetBytes(123), valuesUnsorted.Select(BitConverter.GetBytes).ToArray()); - using (var c = txn.CreateCursor(db)) - { - int order = 0; + using (var c = txn.CreateCursor(db)) + { + var order = 0; - while (c.Next() == MDBResultCode.Success) - Assert.Equal(valuesSorted[order++], BitConverter.ToInt32(c.GetCurrent().value.CopyToNewArray(), 0)); - } + while (c.Next() == MDBResultCode.Success) + Assert.Equal(valuesSorted[order++], BitConverter.ToInt32(c.GetCurrent().value.CopyToNewArray(), 0)); } } -} +} \ No newline at end of file diff --git a/src/LightningDB/CursorDeleteOption.cs b/src/LightningDB/CursorDeleteOption.cs index 98666ef..afbe519 100644 --- a/src/LightningDB/CursorDeleteOption.cs +++ b/src/LightningDB/CursorDeleteOption.cs @@ -1,20 +1,19 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Cursor delete operation options +/// +public enum CursorDeleteOption { /// - /// Cursor delete operation options + /// No special behavior /// - public enum CursorDeleteOption - { - /// - /// No special behavior - /// - None = 0, + None = 0, - /// - /// Only for MDB_DUPSORT - /// For put: don't write if the key and data pair already exist. - /// For mdb_cursor_del: remove all duplicate data items. - /// - NoDuplicateData = 0x20 - } -} + /// + /// Only for MDB_DUPSORT + /// For put: don't write if the key and data pair already exist. + /// For mdb_cursor_del: remove all duplicate data items. + /// + NoDuplicateData = 0x20 +} \ No newline at end of file diff --git a/src/LightningDB/CursorOperation.cs b/src/LightningDB/CursorOperation.cs index 0b707d1..61d49ba 100644 --- a/src/LightningDB/CursorOperation.cs +++ b/src/LightningDB/CursorOperation.cs @@ -1,98 +1,97 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Cursor operation types +/// +public enum CursorOperation { /// - /// Cursor operation types - /// - public enum CursorOperation - { - /// - /// Position at first key/data item - /// - First, - - /// - /// Position at first data item of current key. Only for MDB_DUPSORT - /// - FirstDuplicate, - - /// - /// Position at key/data pair. Only for MDB_DUPSORT - /// - GetBoth, - - /// - /// position at key, nearest data. Only for MDB_DUPSORT - /// - GetBothRange, - - /// - /// Return key/data at current cursor position - /// - GetCurrent, - - /// - /// Return all the duplicate data items at the current cursor position. Only for MDB_DUPFIXED - /// - GetMultiple, - - /// - /// Position at last key/data item - /// - Last, - - /// - /// Position at last data item of current key. Only for MDB_DUPSORT - /// - LastDuplicate, - - /// - /// Position at next data item - /// - Next, - - /// - /// Position at next data item of current key. Only for MDB_DUPSORT - /// - NextDuplicate, - - /// - /// Return all duplicate data items at the next cursor position. Only for MDB_DUPFIXED - /// - NextMultiple, - - /// - /// Position at first data item of next key. Only for MDB_DUPSORT - /// - NextNoDuplicate, - - /// - /// Position at previous data item - /// - Previous, - - /// - /// Position at previous data item of current key. Only for MDB_DUPSORT - /// - PreviousDuplicate, - - /// - /// Position at last data item of previous key. Only for MDB_DUPSORT - /// - PreviousNoDuplicate, - - /// - /// Position at specified key - /// - Set, - - /// - /// Position at specified key, return key + data - /// - SetKey, - - /// - /// Position at first key greater than or equal to specified key. - /// - SetRange, - } -} + /// Position at first key/data item + /// + First, + + /// + /// Position at first data item of current key. Only for MDB_DUPSORT + /// + FirstDuplicate, + + /// + /// Position at key/data pair. Only for MDB_DUPSORT + /// + GetBoth, + + /// + /// position at key, nearest data. Only for MDB_DUPSORT + /// + GetBothRange, + + /// + /// Return key/data at current cursor position + /// + GetCurrent, + + /// + /// Return all the duplicate data items at the current cursor position. Only for MDB_DUPFIXED + /// + GetMultiple, + + /// + /// Position at last key/data item + /// + Last, + + /// + /// Position at last data item of current key. Only for MDB_DUPSORT + /// + LastDuplicate, + + /// + /// Position at next data item + /// + Next, + + /// + /// Position at next data item of current key. Only for MDB_DUPSORT + /// + NextDuplicate, + + /// + /// Return all duplicate data items at the next cursor position. Only for MDB_DUPFIXED + /// + NextMultiple, + + /// + /// Position at first data item of next key. Only for MDB_DUPSORT + /// + NextNoDuplicate, + + /// + /// Position at previous data item + /// + Previous, + + /// + /// Position at previous data item of current key. Only for MDB_DUPSORT + /// + PreviousDuplicate, + + /// + /// Position at last data item of previous key. Only for MDB_DUPSORT + /// + PreviousNoDuplicate, + + /// + /// Position at specified key + /// + Set, + + /// + /// Position at specified key, return key + data + /// + SetKey, + + /// + /// Position at first key greater than or equal to specified key. + /// + SetRange +} \ No newline at end of file diff --git a/src/LightningDB/CursorPutOptions.cs b/src/LightningDB/CursorPutOptions.cs index 29e049c..e4802db 100644 --- a/src/LightningDB/CursorPutOptions.cs +++ b/src/LightningDB/CursorPutOptions.cs @@ -1,50 +1,49 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Special options for cursor put operation. +/// +public enum CursorPutOptions { /// - /// Special options for cursor put operation. - /// - public enum CursorPutOptions - { - /// - /// No special behavior. - /// - None = 0, - - /// - /// Overwrite the current key/data pair - /// - Current = 0x40, - - /// - /// Only for MDB_DUPSORT - /// For put: don't write if the key and data pair already exist. - /// For mdb_cursor_del: remove all duplicate data items. - /// - NoDuplicateData = 0x20, - - /// - /// For put: Don't write if the key already exists. - /// - NoOverwrite = 0x10, - - /// - /// For put: Just reserve space for data, don't copy it. Return a pointer to the reserved space. - /// - ReserveSpace = 0x10000, - - /// - /// Data is being appended, don't split full pages. - /// - AppendData = 0x20000, - - /// - /// Duplicate data is being appended, don't split full pages. - /// - AppendDuplicateData = 0x40000, - - /// - /// Store multiple data items in one call. Only for MDB_DUPFIXED. - /// - MultipleData = 0x80000 - } -} + /// No special behavior. + /// + None = 0, + + /// + /// Overwrite the current key/data pair + /// + Current = 0x40, + + /// + /// Only for MDB_DUPSORT + /// For put: don't write if the key and data pair already exist. + /// For mdb_cursor_del: remove all duplicate data items. + /// + NoDuplicateData = 0x20, + + /// + /// For put: Don't write if the key already exists. + /// + NoOverwrite = 0x10, + + /// + /// For put: Just reserve space for data, don't copy it. Return a pointer to the reserved space. + /// + ReserveSpace = 0x10000, + + /// + /// Data is being appended, don't split full pages. + /// + AppendData = 0x20000, + + /// + /// Duplicate data is being appended, don't split full pages. + /// + AppendDuplicateData = 0x40000, + + /// + /// Store multiple data items in one call. Only for MDB_DUPFIXED. + /// + MultipleData = 0x80000 +} \ No newline at end of file diff --git a/src/LightningDB/DatabaseConfiguration.cs b/src/LightningDB/DatabaseConfiguration.cs index d06ed5f..ec13caa 100644 --- a/src/LightningDB/DatabaseConfiguration.cs +++ b/src/LightningDB/DatabaseConfiguration.cs @@ -4,76 +4,74 @@ using LightningDB.Native; using static LightningDB.Native.Lmdb; -namespace LightningDB +namespace LightningDB; + +public class DatabaseConfiguration { - public class DatabaseConfiguration + private IComparer _comparer; + private IComparer _duplicatesComparer; + + public DatabaseConfiguration() { - private IComparer _comparer; - private IComparer _duplicatesComparer; + Flags = DatabaseOpenFlags.None; + } + + public DatabaseOpenFlags Flags { get; set; } - public DatabaseConfiguration() + + internal IDisposable ConfigureDatabase(LightningTransaction tx, LightningDatabase db) + { + var pinnedComparer = new ComparerKeepAlive(); + if (_comparer != null) { - Flags = DatabaseOpenFlags.None; + CompareFunction compare = Compare; + pinnedComparer.AddComparer(compare); + mdb_set_compare(tx.Handle(), db.Handle(), compare); } - public DatabaseOpenFlags Flags { get; set; } + if (_duplicatesComparer == null) return pinnedComparer; + CompareFunction dupCompare = IsDuplicate; + pinnedComparer.AddComparer(dupCompare); + mdb_set_dupsort(tx.Handle(), db.Handle(), dupCompare); + return pinnedComparer; + } + private int Compare(ref MDBValue left, ref MDBValue right) + { + return _comparer.Compare(left, right); + } - internal IDisposable ConfigureDatabase(LightningTransaction tx, LightningDatabase db) - { - var pinnedComparer = new ComparerKeepAlive(); - if (_comparer != null) - { - CompareFunction compare = Compare; - pinnedComparer.AddComparer(compare); - mdb_set_compare(tx.Handle(), db.Handle(), compare); - } - if (_duplicatesComparer != null) - { - CompareFunction dupCompare = IsDuplicate; - pinnedComparer.AddComparer(dupCompare); - mdb_set_dupsort(tx.Handle(), db.Handle(), dupCompare); - } - return pinnedComparer; - } + private int IsDuplicate(ref MDBValue left, ref MDBValue right) + { + return _duplicatesComparer.Compare(left, right); + } - private int Compare(ref MDBValue left, ref MDBValue right) - { - return _comparer.Compare(left, right); - } + public void CompareWith(IComparer comparer) + { + _comparer = comparer; + } - private int IsDuplicate(ref MDBValue left, ref MDBValue right) - { - return _duplicatesComparer.Compare(left, right); - } + public void FindDuplicatesWith(IComparer comparer) + { + _duplicatesComparer = comparer; + } - public void CompareWith(IComparer comparer) - { - _comparer = comparer; - } + private class ComparerKeepAlive : IDisposable + { + private readonly List _comparisons = new(); - public void FindDuplicatesWith(IComparer comparer) + public void AddComparer(CompareFunction compare) { - _duplicatesComparer = comparer; + var handle = GCHandle.Alloc(compare); + _comparisons.Add(handle); } - private class ComparerKeepAlive : IDisposable + public void Dispose() { - private readonly List _comparisons = new List(); - - public void AddComparer(CompareFunction compare) - { - var handle = GCHandle.Alloc(compare); - _comparisons.Add(handle); - } - - public void Dispose() + for (var i = 0; i < _comparisons.Count; ++i) { - for (int i = 0; i < _comparisons.Count; ++i) - { - _comparisons[i].Free(); - } + _comparisons[i].Free(); } } } -} +} \ No newline at end of file diff --git a/src/LightningDB/DatabaseOpenFlags.cs b/src/LightningDB/DatabaseOpenFlags.cs index c56d752..da6d9d5 100644 --- a/src/LightningDB/DatabaseOpenFlags.cs +++ b/src/LightningDB/DatabaseOpenFlags.cs @@ -1,53 +1,52 @@ using System; using LightningDB.Native; -namespace LightningDB +namespace LightningDB; + +/// +/// Flags to open a database with. +/// +[Flags] +public enum DatabaseOpenFlags { /// - /// Flags to open a database with. + /// No special options. /// - [Flags] - public enum DatabaseOpenFlags - { - /// - /// No special options. - /// - None = 0, + None = 0, - /// - /// MDB_REVERSEKEY. Keys are strings to be compared in reverse order, from the end of the strings to the beginning. By default, Keys are treated as strings and compared from beginning to end. - /// - ReverseKey = 0x02, - - /// - /// MDB_DUPSORT. Duplicate keys may be used in the database. (Or, from another perspective, keys may have multiple data items, stored in sorted order.) By default keys must be unique and may have only a single data item. - /// - DuplicatesSort = Lmdb.MDB_DUPSORT, - - /// - /// MDB_INTEGERKEY. Keys are binary integers in native byte order. - /// Setting this option requires all keys to be the same size, typically sizeof(int) or sizeof(size_t). - /// - IntegerKey = 0x08, - - /// - /// MDB_DUPFIXED. This flag may only be used in combination with MDB_DUPSORT. This option tells the library that the data items for this database are all the same size, which allows further optimizations in storage and retrieval. When all data items are the same size, the MDB_GET_MULTIPLE and MDB_NEXT_MULTIPLE cursor operations may be used to retrieve multiple items at once. - /// - DuplicatesFixed = Lmdb.MDB_DUPSORT | Lmdb.MDB_DUPFIXED, - - /// - /// MDB_INTEGERDUP. This option specifies that duplicate data items are also integers, and should be sorted as such. - /// - IntegerDuplicates = 0x20, - - /// - /// MDB_REVERSEDUP. This option specifies that duplicate data items should be compared as strings in reverse order. - /// - ReverseDuplicates = 0x40, - - /// - /// Create the named database if it doesn't exist. This option is not allowed in a read-only transaction or a read-only environment. - /// - Create = 0x40000 - } -} + /// + /// MDB_REVERSEKEY. Keys are strings to be compared in reverse order, from the end of the strings to the beginning. By default, Keys are treated as strings and compared from beginning to end. + /// + ReverseKey = 0x02, + + /// + /// MDB_DUPSORT. Duplicate keys may be used in the database. (Or, from another perspective, keys may have multiple data items, stored in sorted order.) By default keys must be unique and may have only a single data item. + /// + DuplicatesSort = Lmdb.MDB_DUPSORT, + + /// + /// MDB_INTEGERKEY. Keys are binary integers in native byte order. + /// Setting this option requires all keys to be the same size, typically sizeof(int) or sizeof(size_t). + /// + IntegerKey = 0x08, + + /// + /// MDB_DUPFIXED. This flag may only be used in combination with MDB_DUPSORT. This option tells the library that the data items for this database are all the same size, which allows further optimizations in storage and retrieval. When all data items are the same size, the MDB_GET_MULTIPLE and MDB_NEXT_MULTIPLE cursor operations may be used to retrieve multiple items at once. + /// + DuplicatesFixed = Lmdb.MDB_DUPSORT | Lmdb.MDB_DUPFIXED, + + /// + /// MDB_INTEGERDUP. This option specifies that duplicate data items are also integers, and should be sorted as such. + /// + IntegerDuplicates = 0x20, + + /// + /// MDB_REVERSEDUP. This option specifies that duplicate data items should be compared as strings in reverse order. + /// + ReverseDuplicates = 0x40, + + /// + /// Create the named database if it doesn't exist. This option is not allowed in a read-only transaction or a read-only environment. + /// + Create = 0x40000 +} \ No newline at end of file diff --git a/src/LightningDB/EnvironmentConfiguration.cs b/src/LightningDB/EnvironmentConfiguration.cs index 12c1c60..13ccbb3 100644 --- a/src/LightningDB/EnvironmentConfiguration.cs +++ b/src/LightningDB/EnvironmentConfiguration.cs @@ -1,54 +1,53 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Basic environment configuration +/// +public class EnvironmentConfiguration { - /// - /// Basic environment configuration - /// - public class EnvironmentConfiguration - { - private long? _mapSize; - private int? _maxReaders; - private int? _maxDatabases; + private long? _mapSize; + private int? _maxReaders; + private int? _maxDatabases; #if NETCOREAPP3_1_OR_GREATER - private bool? _autoResizeWindows; + private bool? _autoResizeWindows; - public bool AutoResizeWindows - { - get { return _autoResizeWindows ?? false; } - set { _autoResizeWindows = value; } - } + public bool AutoResizeWindows + { + get => _autoResizeWindows ?? false; + set => _autoResizeWindows = value; + } #endif - public long MapSize - { - get { return _mapSize ?? 0; } - set { _mapSize = value; } - } - - public int MaxReaders - { - get { return _maxReaders ?? 0; } - set { _maxReaders = value; } - } - - public int MaxDatabases - { - get { return _maxDatabases ?? 0; } - set { _maxDatabases = value; } - } - - public bool AutoReduceMapSizeIn32BitProcess { get; set; } - - internal void Configure(LightningEnvironment env) - { - if (_mapSize.HasValue) - env.MapSize = _mapSize.Value; - - if (_maxDatabases.HasValue) - env.MaxDatabases = _maxDatabases.Value; - - if (_maxReaders.HasValue) - env.MaxReaders = _maxReaders.Value; - } + public long MapSize + { + get => _mapSize ?? 0; + set => _mapSize = value; + } + + public int MaxReaders + { + get => _maxReaders ?? 0; + set => _maxReaders = value; + } + + public int MaxDatabases + { + get => _maxDatabases ?? 0; + set => _maxDatabases = value; + } + + public bool AutoReduceMapSizeIn32BitProcess { get; set; } + + internal void Configure(LightningEnvironment env) + { + if (_mapSize.HasValue) + env.MapSize = _mapSize.Value; + + if (_maxDatabases.HasValue) + env.MaxDatabases = _maxDatabases.Value; + + if (_maxReaders.HasValue) + env.MaxReaders = _maxReaders.Value; } -} +} \ No newline at end of file diff --git a/src/LightningDB/EnvironmentCopyFlags.cs b/src/LightningDB/EnvironmentCopyFlags.cs index 5818983..cd4e89d 100644 --- a/src/LightningDB/EnvironmentCopyFlags.cs +++ b/src/LightningDB/EnvironmentCopyFlags.cs @@ -1,12 +1,11 @@ -namespace LightningDB +namespace LightningDB; + +public enum EnvironmentCopyFlags { - public enum EnvironmentCopyFlags - { - None = 0, + None = 0, - /// - /// MDB_CP_COMPACT. Compacting copy: Omit free space from copy, and renumber all pages sequentially. - /// - Compact = 0x01 - } -} + /// + /// MDB_CP_COMPACT. Compacting copy: Omit free space from copy, and renumber all pages sequentially. + /// + Compact = 0x01 +} \ No newline at end of file diff --git a/src/LightningDB/EnvironmentInfo.cs b/src/LightningDB/EnvironmentInfo.cs index 3747c04..0396d41 100644 --- a/src/LightningDB/EnvironmentInfo.cs +++ b/src/LightningDB/EnvironmentInfo.cs @@ -1,23 +1,22 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Information about the environment. +/// +public class EnvironmentInfo { /// - /// Information about the environment. + /// ID of the last used page /// - public class EnvironmentInfo - { - /// - /// ID of the last used page - /// - public long LastPageNumber { get; set; } + public long LastPageNumber { get; set; } - /// - /// ID of the last committed transaction - /// - public long LastTransactionId { get; set; } + /// + /// ID of the last committed transaction + /// + public long LastTransactionId { get; set; } - /// - /// Size of the data memory map - /// - public long MapSize { get; set; } - } + /// + /// Size of the data memory map + /// + public long MapSize { get; set; } } \ No newline at end of file diff --git a/src/LightningDB/EnvironmentOpenFlags.cs b/src/LightningDB/EnvironmentOpenFlags.cs index edc66c1..fff272c 100644 --- a/src/LightningDB/EnvironmentOpenFlags.cs +++ b/src/LightningDB/EnvironmentOpenFlags.cs @@ -1,96 +1,95 @@ using System; -namespace LightningDB +namespace LightningDB; + +/// +/// Options to open LMDB environment +/// +[Flags] +public enum EnvironmentOpenFlags { /// - /// Options to open LMDB environment + /// No special options. /// - [Flags] - public enum EnvironmentOpenFlags - { - /// - /// No special options. - /// - None = 0, + None = 0, - /// - /// MDB_FIXEDMAP. use a fixed address for the mmap region. - /// This flag must be specified when creating the environment, and is stored persistently in the environment. - /// If successful, the memory map will always reside at the same virtual address and pointers used to reference data items in the database will be constant across multiple invocations. - /// This option may not always work, depending on how the operating system has allocated memory to shared libraries and other uses. - /// The feature is highly experimental. - /// - FixedMap = 0x01, + /// + /// MDB_FIXEDMAP. use a fixed address for the mmap region. + /// This flag must be specified when creating the environment, and is stored persistently in the environment. + /// If successful, the memory map will always reside at the same virtual address and pointers used to reference data items in the database will be constant across multiple invocations. + /// This option may not always work, depending on how the operating system has allocated memory to shared libraries and other uses. + /// The feature is highly experimental. + /// + FixedMap = 0x01, - /// - /// MDB_NOSUBDIR. By default, MDB creates its environment in a directory whose pathname is given in path, and creates its data and lock files under that directory. - /// With this option, path is used as-is for the database main data file. - /// The database lock file is the path with "-lock" appended. - /// - NoSubDir = 0x4000, + /// + /// MDB_NOSUBDIR. By default, MDB creates its environment in a directory whose pathname is given in path, and creates its data and lock files under that directory. + /// With this option, path is used as-is for the database main data file. + /// The database lock file is the path with "-lock" appended. + /// + NoSubDir = 0x4000, - /// - /// MDB_NOSYNC. Don't flush system buffers to disk when committing a transaction. - /// This optimization means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk. - /// The risk is governed by how often the system flushes dirty buffers to disk and how often mdb_env_sync() is called. - /// However, if the filesystem preserves write order and the MDB_WRITEMAP flag is not used, transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D (durability). - /// I.e. database integrity is maintained, but a system crash may undo the final transactions. - /// Note that (MDB_NOSYNC | MDB_WRITEMAP) leaves the system with no hint for when to write transactions to disk, unless mdb_env_sync() is called. - /// (MDB_MAPASYNC | MDB_WRITEMAP) may be preferable. - /// This flag may be changed at any time using mdb_env_set_flags(). - /// - NoSync = 0x10000, + /// + /// MDB_NOSYNC. Don't flush system buffers to disk when committing a transaction. + /// This optimization means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk. + /// The risk is governed by how often the system flushes dirty buffers to disk and how often mdb_env_sync() is called. + /// However, if the filesystem preserves write order and the MDB_WRITEMAP flag is not used, transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D (durability). + /// I.e. database integrity is maintained, but a system crash may undo the final transactions. + /// Note that (MDB_NOSYNC | MDB_WRITEMAP) leaves the system with no hint for when to write transactions to disk, unless mdb_env_sync() is called. + /// (MDB_MAPASYNC | MDB_WRITEMAP) may be preferable. + /// This flag may be changed at any time using mdb_env_set_flags(). + /// + NoSync = 0x10000, - /// - /// MDB_RDONLY. Open the environment in read-only mode. - /// No write operations will be allowed. - /// MDB will still modify the lock file - except on read-only filesystems, where MDB does not use locks. - /// - ReadOnly = 0x20000, + /// + /// MDB_RDONLY. Open the environment in read-only mode. + /// No write operations will be allowed. + /// MDB will still modify the lock file - except on read-only filesystems, where MDB does not use locks. + /// + ReadOnly = 0x20000, - /// - /// MDB_NOMETASYNC. Flush system buffers to disk only once per transaction, omit the metadata flush. - /// Defer that until the system flushes files to disk, or next non-MDB_RDONLY commit or mdb_env_sync(). - /// This optimization maintains database integrity, but a system crash may undo the last committed transaction. - /// I.e. it preserves the ACI (atomicity, consistency, isolation) but not D (durability) database property. - /// This flag may be changed at any time using mdb_env_set_flags(). - /// - NoMetaSync = 0x40000, + /// + /// MDB_NOMETASYNC. Flush system buffers to disk only once per transaction, omit the metadata flush. + /// Defer that until the system flushes files to disk, or next non-MDB_RDONLY commit or mdb_env_sync(). + /// This optimization maintains database integrity, but a system crash may undo the last committed transaction. + /// I.e. it preserves the ACI (atomicity, consistency, isolation) but not D (durability) database property. + /// This flag may be changed at any time using mdb_env_set_flags(). + /// + NoMetaSync = 0x40000, - /// - /// MDB_WRITEMAP Use a writeable memory map unless MDB_RDONLY is set. - /// This is faster and uses fewer mallocs, but loses protection from application bugs like wild pointer writes and other bad updates into the database. - /// Incompatible with nested transactions. - /// - WriteMap = 0x80000, + /// + /// MDB_WRITEMAP Use a writeable memory map unless MDB_RDONLY is set. + /// This is faster and uses fewer mallocs, but loses protection from application bugs like wild pointer writes and other bad updates into the database. + /// Incompatible with nested transactions. + /// + WriteMap = 0x80000, - /// - /// MDB_MAPASYNC. When using MDB_WRITEMAP, use asynchronous flushes to disk. - /// As with MDB_NOSYNC, a system crash can then corrupt the database or lose the last transactions. - /// Calling mdb_env_sync() ensures on-disk database integrity until next commit. - /// This flag may be changed at any time using mdb_env_set_flags(). - /// - MapAsync = 0x100000, + /// + /// MDB_MAPASYNC. When using MDB_WRITEMAP, use asynchronous flushes to disk. + /// As with MDB_NOSYNC, a system crash can then corrupt the database or lose the last transactions. + /// Calling mdb_env_sync() ensures on-disk database integrity until next commit. + /// This flag may be changed at any time using mdb_env_set_flags(). + /// + MapAsync = 0x100000, - /// - /// MDB_NOTLS. tie reader locktable slots to MDB_txn objects instead of to threads - /// - NoThreadLocalStorage = 0x200000, + /// + /// MDB_NOTLS. tie reader locktable slots to MDB_txn objects instead of to threads + /// + NoThreadLocalStorage = 0x200000, - /// - /// MDB_NOLOCK. don't do any locking, caller must manage their own locks - /// - NoLock = 0x400000, + /// + /// MDB_NOLOCK. don't do any locking, caller must manage their own locks + /// + NoLock = 0x400000, - /// - /// MDB_NORDAHEAD. don't do readahead (no effect on Windows) - /// - NoReadAhead = 0x800000, + /// + /// MDB_NORDAHEAD. don't do readahead (no effect on Windows) + /// + NoReadAhead = 0x800000, - /// - /// MDB_NOMEMINIT. don't initialize malloc'd memory before writing to datafile - /// - NoMemoryInitialization = 0x1000000 + /// + /// MDB_NOMEMINIT. don't initialize malloc'd memory before writing to datafile + /// + NoMemoryInitialization = 0x1000000 - } -} +} \ No newline at end of file diff --git a/src/LightningDB/LightningCursor.cs b/src/LightningDB/LightningCursor.cs index eaf835b..d5c5dda 100644 --- a/src/LightningDB/LightningCursor.cs +++ b/src/LightningDB/LightningCursor.cs @@ -4,532 +4,529 @@ using static LightningDB.Native.Lmdb; -namespace LightningDB +namespace LightningDB; + +/// +/// Cursor to iterate over a database +/// +public class LightningCursor : IDisposable { + private nint _handle; + /// - /// Cursor to iterate over a database + /// Creates new instance of LightningCursor /// - public class LightningCursor : IDisposable + /// Database + /// Transaction + internal LightningCursor(LightningDatabase db, LightningTransaction txn) { - private IntPtr _handle; + if (db == null) + throw new ArgumentNullException(nameof(db)); - /// - /// Creates new instance of LightningCursor - /// - /// Database - /// Transaction - internal LightningCursor(LightningDatabase db, LightningTransaction txn) - { - if (db == null) - throw new ArgumentNullException(nameof(db)); + if (txn == null) + throw new ArgumentNullException(nameof(txn)); - if (txn == null) - throw new ArgumentNullException(nameof(txn)); + mdb_cursor_open(txn.Handle(), db.Handle(), out _handle).ThrowOnError(); - mdb_cursor_open(txn.Handle(), db.Handle(), out _handle).ThrowOnError(); + Transaction = txn; + Transaction.Disposing += Dispose; + } - Transaction = txn; - Transaction.Disposing += Dispose; - } + /// + /// Gets the the native handle of the cursor + /// + public nint Handle() + { + return _handle; + } - /// - /// Gets the the native handle of the cursor - /// - public IntPtr Handle() - { - return _handle; - } + /// + /// Cursor's transaction. + /// + public LightningTransaction Transaction { get; } - /// - /// Cursor's transaction. - /// - public LightningTransaction Transaction { get; } - - /// - /// Position at specified key, if key is not found index will be positioned to closest match. - /// - /// Key - /// Returns - public MDBResultCode Set(byte[] key) - { - return Get(CursorOperation.Set, key).resultCode; - } + /// + /// Position at specified key, if key is not found index will be positioned to closest match. + /// + /// Key + /// Returns + public MDBResultCode Set(byte[] key) + { + return Get(CursorOperation.Set, key).resultCode; + } - /// - /// Position at specified key, if key is not found index will be positioned to closest match. - /// - /// Key - /// Returns - public MDBResultCode Set(ReadOnlySpan key) - { - return Get(CursorOperation.Set, key).resultCode; - } + /// + /// Position at specified key, if key is not found index will be positioned to closest match. + /// + /// Key + /// Returns + public MDBResultCode Set(ReadOnlySpan key) + { + return Get(CursorOperation.Set, key).resultCode; + } - /// - /// Moves to the key and populates Current with the values stored. - /// - /// Key - /// Returns , and key/value - public (MDBResultCode resultCode, MDBValue key, MDBValue value) SetKey(byte[] key) - { - return Get(CursorOperation.SetKey, key); - } + /// + /// Moves to the key and populates Current with the values stored. + /// + /// Key + /// Returns , and key/value + public (MDBResultCode resultCode, MDBValue key, MDBValue value) SetKey(byte[] key) + { + return Get(CursorOperation.SetKey, key); + } - /// - /// Moves to the key and populates Current with the values stored. - /// - /// Key - /// Returns , and key/value - public (MDBResultCode resultCode, MDBValue key, MDBValue value) SetKey(ReadOnlySpan key) - { - return Get(CursorOperation.SetKey, key); - } + /// + /// Moves to the key and populates Current with the values stored. + /// + /// Key + /// Returns , and key/value + public (MDBResultCode resultCode, MDBValue key, MDBValue value) SetKey(ReadOnlySpan key) + { + return Get(CursorOperation.SetKey, key); + } - /// - /// Position at key/data pair. Only for MDB_DUPSORT - /// - /// Key. - /// Value - /// Returns true if the key/value pair was found. - public MDBResultCode GetBoth(byte[] key, byte[] value) - { - return Get(CursorOperation.GetBoth, key, value).resultCode; - } + /// + /// Position at key/data pair. Only for MDB_DUPSORT + /// + /// Key. + /// Value + /// Returns true if the key/value pair was found. + public MDBResultCode GetBoth(byte[] key, byte[] value) + { + return Get(CursorOperation.GetBoth, key, value).resultCode; + } - /// - /// Position at key/data pair. Only for MDB_DUPSORT - /// - /// Key. - /// Value - /// Returns - public MDBResultCode GetBoth(ReadOnlySpan key, ReadOnlySpan value) - { - return Get(CursorOperation.GetBoth, key, value).resultCode; - } + /// + /// Position at key/data pair. Only for MDB_DUPSORT + /// + /// Key. + /// Value + /// Returns + public MDBResultCode GetBoth(ReadOnlySpan key, ReadOnlySpan value) + { + return Get(CursorOperation.GetBoth, key, value).resultCode; + } - /// - /// position at key, nearest data. Only for MDB_DUPSORT - /// - /// Key - /// Value - /// Returns - public MDBResultCode GetBothRange(byte[] key, byte[] value) - { - return Get(CursorOperation.GetBothRange, key, value).resultCode; - } + /// + /// position at key, nearest data. Only for MDB_DUPSORT + /// + /// Key + /// Value + /// Returns + public MDBResultCode GetBothRange(byte[] key, byte[] value) + { + return Get(CursorOperation.GetBothRange, key, value).resultCode; + } - /// - /// position at key, nearest data. Only for MDB_DUPSORT - /// - /// Key - /// Value - /// Returns - public MDBResultCode GetBothRange(ReadOnlySpan key, ReadOnlySpan value) - { - return Get(CursorOperation.GetBothRange, key, value).resultCode; - } + /// + /// position at key, nearest data. Only for MDB_DUPSORT + /// + /// Key + /// Value + /// Returns + public MDBResultCode GetBothRange(ReadOnlySpan key, ReadOnlySpan value) + { + return Get(CursorOperation.GetBothRange, key, value).resultCode; + } - /// - /// Position at first key greater than or equal to specified key. - /// - /// Key - /// Returns - public MDBResultCode SetRange(byte[] key) - { - return Get(CursorOperation.SetRange, key).resultCode; - } + /// + /// Position at first key greater than or equal to specified key. + /// + /// Key + /// Returns + public MDBResultCode SetRange(byte[] key) + { + return Get(CursorOperation.SetRange, key).resultCode; + } - /// - /// Position at first key greater than or equal to specified key. - /// - /// Key - /// Returns - public MDBResultCode SetRange(ReadOnlySpan key) - { - return Get(CursorOperation.SetRange, key).resultCode; - } + /// + /// Position at first key greater than or equal to specified key. + /// + /// Key + /// Returns + public MDBResultCode SetRange(ReadOnlySpan key) + { + return Get(CursorOperation.SetRange, key).resultCode; + } - /// - /// Position at first key/data item - /// - /// Returns - public MDBResultCode First() - { - return Get(CursorOperation.First).resultCode; - } + /// + /// Position at first key/data item + /// + /// Returns + public MDBResultCode First() + { + return Get(CursorOperation.First).resultCode; + } - /// - /// Position at first data item of current key. Only for MDB_DUPSORT - /// - /// Returns - public MDBResultCode FirstDuplicate() - { - return Get(CursorOperation.FirstDuplicate).resultCode; - } + /// + /// Position at first data item of current key. Only for MDB_DUPSORT + /// + /// Returns + public MDBResultCode FirstDuplicate() + { + return Get(CursorOperation.FirstDuplicate).resultCode; + } - /// - /// Position at last key/data item - /// - /// Returns - public MDBResultCode Last() - { - return Get(CursorOperation.Last).resultCode; - } + /// + /// Position at last key/data item + /// + /// Returns + public MDBResultCode Last() + { + return Get(CursorOperation.Last).resultCode; + } - /// - /// Position at last data item of current key. Only for MDB_DUPSORT - /// - /// Returns - public MDBResultCode LastDuplicate() - { - return Get(CursorOperation.LastDuplicate).resultCode; - } + /// + /// Position at last data item of current key. Only for MDB_DUPSORT + /// + /// Returns + public MDBResultCode LastDuplicate() + { + return Get(CursorOperation.LastDuplicate).resultCode; + } - /// - /// Return key/data at current cursor position - /// - /// Key/data at current cursor position - public (MDBResultCode resultCode, MDBValue key, MDBValue value) GetCurrent() - { - return Get(CursorOperation.GetCurrent); - } + /// + /// Return key/data at current cursor position + /// + /// Key/data at current cursor position + public (MDBResultCode resultCode, MDBValue key, MDBValue value) GetCurrent() + { + return Get(CursorOperation.GetCurrent); + } - /// - /// Position at next data item - /// - /// Returns - public MDBResultCode Next() - { - return Get(CursorOperation.Next).resultCode; - } + /// + /// Position at next data item + /// + /// Returns + public MDBResultCode Next() + { + return Get(CursorOperation.Next).resultCode; + } - /// - /// Position at next data item of current key. Only for MDB_DUPSORT - /// - /// Returns - public MDBResultCode NextDuplicate() - { - return Get(CursorOperation.NextDuplicate).resultCode; - } + /// + /// Position at next data item of current key. Only for MDB_DUPSORT + /// + /// Returns + public MDBResultCode NextDuplicate() + { + return Get(CursorOperation.NextDuplicate).resultCode; + } - /// - /// Position at first data item of next key. Only for MDB_DUPSORT. - /// - /// Returns - public MDBResultCode NextNoDuplicate() - { - return Get(CursorOperation.NextNoDuplicate).resultCode; - } + /// + /// Position at first data item of next key. Only for MDB_DUPSORT. + /// + /// Returns + public MDBResultCode NextNoDuplicate() + { + return Get(CursorOperation.NextNoDuplicate).resultCode; + } - /// - /// Return up to a page of duplicate data items at the next cursor position. Only for MDB_DUPFIXED - /// It is assumed you know the array size to break up a single byte[] into byte[][]. - /// - /// Returns , and key will be empty here, values are 2D array - public (MDBResultCode resultCode, MDBValue key, MDBValue value) NextMultiple() - { - return Get(CursorOperation.NextMultiple); - } + /// + /// Return up to a page of duplicate data items at the next cursor position. Only for MDB_DUPFIXED + /// It is assumed you know the array size to break up a single byte[] into byte[][]. + /// + /// Returns , and key will be empty here, values are 2D array + public (MDBResultCode resultCode, MDBValue key, MDBValue value) NextMultiple() + { + return Get(CursorOperation.NextMultiple); + } - /// - /// Position at previous data item. - /// - /// Returns - public MDBResultCode Previous() - { - return Get(CursorOperation.Previous).resultCode; - } + /// + /// Position at previous data item. + /// + /// Returns + public MDBResultCode Previous() + { + return Get(CursorOperation.Previous).resultCode; + } - /// - /// Position at previous data item of current key. Only for MDB_DUPSORT. - /// - /// Returns - public MDBResultCode PreviousDuplicate() - { - return Get(CursorOperation.PreviousDuplicate).resultCode; - } + /// + /// Position at previous data item of current key. Only for MDB_DUPSORT. + /// + /// Returns + public MDBResultCode PreviousDuplicate() + { + return Get(CursorOperation.PreviousDuplicate).resultCode; + } - /// - /// Position at last data item of previous key. Only for MDB_DUPSORT. - /// - /// Returns - public MDBResultCode PreviousNoDuplicate() - { - return Get(CursorOperation.PreviousNoDuplicate).resultCode; - } + /// + /// Position at last data item of previous key. Only for MDB_DUPSORT. + /// + /// Returns + public MDBResultCode PreviousNoDuplicate() + { + return Get(CursorOperation.PreviousNoDuplicate).resultCode; + } - private (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation) - { - var mdbKey = new MDBValue(); - var mdbValue = new MDBValue(); - return (mdb_cursor_get(_handle, ref mdbKey, ref mdbValue, operation), mdbKey, mdbValue); - } + private (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation) + { + var mdbKey = new MDBValue(); + var mdbValue = new MDBValue(); + return (mdb_cursor_get(_handle, ref mdbKey, ref mdbValue, operation), mdbKey, mdbValue); + } - private (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, byte[] key) - { - if (key is null) - throw new ArgumentNullException(nameof(key)); + private (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, byte[] key) + { + if (key is null) + throw new ArgumentNullException(nameof(key)); - return Get(operation, key.AsSpan()); - } + return Get(operation, key.AsSpan()); + } - private unsafe (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, ReadOnlySpan key) + private unsafe (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, ReadOnlySpan key) + { + fixed (byte* keyPtr = key) { - fixed (byte* keyPtr = key) - { - var mdbKey = new MDBValue(key.Length, keyPtr); - var mdbValue = new MDBValue(); - return (mdb_cursor_get(_handle, ref mdbKey, ref mdbValue, operation), mdbKey, mdbValue); - } + var mdbKey = new MDBValue(key.Length, keyPtr); + var mdbValue = new MDBValue(); + return (mdb_cursor_get(_handle, ref mdbKey, ref mdbValue, operation), mdbKey, mdbValue); } + } - private (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, byte[] key, byte[] value) - { - return Get(operation, key.AsSpan(), value.AsSpan()); - } + private (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, byte[] key, byte[] value) + { + return Get(operation, key.AsSpan(), value.AsSpan()); + } - private unsafe (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, ReadOnlySpan key, ReadOnlySpan value) + private unsafe (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(CursorOperation operation, ReadOnlySpan key, ReadOnlySpan value) + { + fixed(byte* keyPtr = key) + fixed(byte* valPtr = value) { - fixed(byte* keyPtr = key) - fixed(byte* valPtr = value) - { - var mdbKey = new MDBValue(key.Length, keyPtr); - var mdbValue = new MDBValue(value.Length, valPtr); - return (mdb_cursor_get(_handle, ref mdbKey, ref mdbValue, operation), mdbKey, mdbValue); - } + var mdbKey = new MDBValue(key.Length, keyPtr); + var mdbValue = new MDBValue(value.Length, valPtr); + return (mdb_cursor_get(_handle, ref mdbKey, ref mdbValue, operation), mdbKey, mdbValue); } + } - /// - /// Store by cursor. - /// This function stores key/data pairs into the database. The cursor is positioned at the new item, or on failure usually near it. - /// Note: Earlier documentation incorrectly said errors would leave the state of the cursor unchanged. - /// If the function fails for any reason, the state of the cursor will be unchanged. - /// If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item. - /// - /// The key operated on. - /// The data operated on. - /// - /// Options for this operation. This parameter must be set to 0 or one of the values described here. - /// CursorPutOptions.Current - overwrite the data of the key/data pair to which the cursor refers with the specified data item. The key parameter is ignored. - /// CursorPutOptions.NoDuplicateData - enter the new key/data pair only if it does not already appear in the database. This flag may only be specified if the database was opened with MDB_DUPSORT. The function will return MDB_KEYEXIST if the key/data pair already appears in the database. - /// CursorPutOptions.NoOverwrite - enter the new key/data pair only if the key does not already appear in the database. The function will return MDB_KEYEXIST if the key already appears in the database, even if the database supports duplicates (MDB_DUPSORT). - /// CursorPutOptions.ReserveSpace - reserve space for data of the given size, but don't copy the given data. Instead, return a pointer to the reserved space, which the caller can fill in later. This saves an extra memcpy if the data is being generated later. - /// CursorPutOptions.AppendData - append the given key/data pair to the end of the database. No key comparisons are performed. This option allows fast bulk loading when keys are already known to be in the correct order. Loading unsorted keys with this flag will cause data corruption. - /// CursorPutOptions.AppendDuplicateData - as above, but for sorted dup data. - /// - /// Returns - public MDBResultCode Put(byte[] key, byte[] value, CursorPutOptions options) - { - return Put(key.AsSpan(), value.AsSpan(), options); - } + /// + /// Store by cursor. + /// This function stores key/data pairs into the database. The cursor is positioned at the new item, or on failure usually near it. + /// Note: Earlier documentation incorrectly said errors would leave the state of the cursor unchanged. + /// If the function fails for any reason, the state of the cursor will be unchanged. + /// If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item. + /// + /// The key operated on. + /// The data operated on. + /// + /// Options for this operation. This parameter must be set to 0 or one of the values described here. + /// CursorPutOptions.Current - overwrite the data of the key/data pair to which the cursor refers with the specified data item. The key parameter is ignored. + /// CursorPutOptions.NoDuplicateData - enter the new key/data pair only if it does not already appear in the database. This flag may only be specified if the database was opened with MDB_DUPSORT. The function will return MDB_KEYEXIST if the key/data pair already appears in the database. + /// CursorPutOptions.NoOverwrite - enter the new key/data pair only if the key does not already appear in the database. The function will return MDB_KEYEXIST if the key already appears in the database, even if the database supports duplicates (MDB_DUPSORT). + /// CursorPutOptions.ReserveSpace - reserve space for data of the given size, but don't copy the given data. Instead, return a pointer to the reserved space, which the caller can fill in later. This saves an extra memcpy if the data is being generated later. + /// CursorPutOptions.AppendData - append the given key/data pair to the end of the database. No key comparisons are performed. This option allows fast bulk loading when keys are already known to be in the correct order. Loading unsorted keys with this flag will cause data corruption. + /// CursorPutOptions.AppendDuplicateData - as above, but for sorted dup data. + /// + /// Returns + public MDBResultCode Put(byte[] key, byte[] value, CursorPutOptions options) + { + return Put(key.AsSpan(), value.AsSpan(), options); + } - /// - /// Store by cursor. - /// This function stores key/data pairs into the database. The cursor is positioned at the new item, or on failure usually near it. - /// Note: Earlier documentation incorrectly said errors would leave the state of the cursor unchanged. - /// If the function fails for any reason, the state of the cursor will be unchanged. - /// If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item. - /// - /// The key operated on. - /// The data operated on. - /// - /// Options for this operation. This parameter must be set to 0 or one of the values described here. - /// CursorPutOptions.Current - overwrite the data of the key/data pair to which the cursor refers with the specified data item. The key parameter is ignored. - /// CursorPutOptions.NoDuplicateData - enter the new key/data pair only if it does not already appear in the database. This flag may only be specified if the database was opened with MDB_DUPSORT. The function will return MDB_KEYEXIST if the key/data pair already appears in the database. - /// CursorPutOptions.NoOverwrite - enter the new key/data pair only if the key does not already appear in the database. The function will return MDB_KEYEXIST if the key already appears in the database, even if the database supports duplicates (MDB_DUPSORT). - /// CursorPutOptions.ReserveSpace - reserve space for data of the given size, but don't copy the given data. Instead, return a pointer to the reserved space, which the caller can fill in later. This saves an extra memcpy if the data is being generated later. - /// CursorPutOptions.AppendData - append the given key/data pair to the end of the database. No key comparisons are performed. This option allows fast bulk loading when keys are already known to be in the correct order. Loading unsorted keys with this flag will cause data corruption. - /// CursorPutOptions.AppendDuplicateData - as above, but for sorted dup data. - /// - /// Returns - public unsafe MDBResultCode Put(ReadOnlySpan key, ReadOnlySpan value, CursorPutOptions options) + /// + /// Store by cursor. + /// This function stores key/data pairs into the database. The cursor is positioned at the new item, or on failure usually near it. + /// Note: Earlier documentation incorrectly said errors would leave the state of the cursor unchanged. + /// If the function fails for any reason, the state of the cursor will be unchanged. + /// If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item. + /// + /// The key operated on. + /// The data operated on. + /// + /// Options for this operation. This parameter must be set to 0 or one of the values described here. + /// CursorPutOptions.Current - overwrite the data of the key/data pair to which the cursor refers with the specified data item. The key parameter is ignored. + /// CursorPutOptions.NoDuplicateData - enter the new key/data pair only if it does not already appear in the database. This flag may only be specified if the database was opened with MDB_DUPSORT. The function will return MDB_KEYEXIST if the key/data pair already appears in the database. + /// CursorPutOptions.NoOverwrite - enter the new key/data pair only if the key does not already appear in the database. The function will return MDB_KEYEXIST if the key already appears in the database, even if the database supports duplicates (MDB_DUPSORT). + /// CursorPutOptions.ReserveSpace - reserve space for data of the given size, but don't copy the given data. Instead, return a pointer to the reserved space, which the caller can fill in later. This saves an extra memcpy if the data is being generated later. + /// CursorPutOptions.AppendData - append the given key/data pair to the end of the database. No key comparisons are performed. This option allows fast bulk loading when keys are already known to be in the correct order. Loading unsorted keys with this flag will cause data corruption. + /// CursorPutOptions.AppendDuplicateData - as above, but for sorted dup data. + /// + /// Returns + public unsafe MDBResultCode Put(ReadOnlySpan key, ReadOnlySpan value, CursorPutOptions options) + { + fixed (byte* keyPtr = key) + fixed (byte* valPtr = value) { - fixed (byte* keyPtr = key) - fixed (byte* valPtr = value) - { - var mdbKey = new MDBValue(key.Length, keyPtr); - var mdbValue = new MDBValue(value.Length, valPtr); + var mdbKey = new MDBValue(key.Length, keyPtr); + var mdbValue = new MDBValue(value.Length, valPtr); - return mdb_cursor_put(_handle, mdbKey, mdbValue, options); - } + return mdb_cursor_put(_handle, mdbKey, mdbValue, options); } + } - /// - /// Store by cursor. - /// This function stores key/data pairs into the database. - /// If the function fails for any reason, the state of the cursor will be unchanged. - /// If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item. - /// - /// The key operated on. - /// The data items operated on. - /// Returns - public unsafe MDBResultCode Put(byte[] key, byte[][] values) - { - const int StackAllocateLimit = 256;//I just made up a number, this can be much more aggressive -arc - - int overallLength = values.Sum(arr => arr.Length);//probably allocates but boy is it handy... + /// + /// Store by cursor. + /// This function stores key/data pairs into the database. + /// If the function fails for any reason, the state of the cursor will be unchanged. + /// If the function succeeds and an item is inserted into the database, the cursor is always positioned to refer to the newly inserted item. + /// + /// The key operated on. + /// The data items operated on. + /// Returns + public unsafe MDBResultCode Put(byte[] key, byte[][] values) + { + const int StackAllocateLimit = 256;//I just made up a number, this can be much more aggressive -arc + var overallLength = values.Sum(arr => arr.Length);//probably allocates but boy is it handy... - //the idea here is to gain some perf by stackallocating the buffer to - //hold the contiguous keys - if (overallLength < StackAllocateLimit) - { - Span contiguousValues = stackalloc byte[overallLength]; - return InnerPutMultiple(contiguousValues); - } - else - { - fixed (byte* contiguousValuesPtr = new byte[overallLength]) - { - Span contiguousValues = new Span(contiguousValuesPtr, overallLength); - return InnerPutMultiple(contiguousValues); - } - } + //the idea here is to gain some perf by stackallocating the buffer to + //hold the contiguous keys + if (overallLength < StackAllocateLimit) + { + Span contiguousValues = stackalloc byte[overallLength]; - //these local methods could be made static, but the compiler will emit these closures - //as structs with very little overhead. Also static local functions isn't available - //until C# 8 so I can't use it anyway... - MDBResultCode InnerPutMultiple(Span contiguousValuesBuffer) - { - FlattenInfo(contiguousValuesBuffer); - var contiguousValuesPtr = (byte*)Unsafe.AsPointer(ref contiguousValuesBuffer.GetPinnableReference()); + return InnerPutMultiple(contiguousValues); + } - var mdbValue = new MDBValue(GetSize(), contiguousValuesPtr); - var mdbCount = new MDBValue(values.Length, (byte*)null); + fixed (byte* contiguousValuesPtr = new byte[overallLength]) + { + var contiguousValues = new Span(contiguousValuesPtr, overallLength); + return InnerPutMultiple(contiguousValues); + } - Span dataBuffer = stackalloc MDBValue[2] { mdbValue, mdbCount }; + //these local methods could be made static, but the compiler will emit these closures + //as structs with very little overhead. Also static local functions isn't available + //until C# 8 so I can't use it anyway... + MDBResultCode InnerPutMultiple(Span contiguousValuesBuffer) + { + FlattenInfo(contiguousValuesBuffer); + var contiguousValuesPtr = (byte*)Unsafe.AsPointer(ref contiguousValuesBuffer.GetPinnableReference()); - fixed (byte* keyPtr = key) - { - var mdbKey = new MDBValue(key.Length, keyPtr); + var mdbValue = new MDBValue(GetSize(), contiguousValuesPtr); + var mdbCount = new MDBValue(values.Length, null); - return mdb_cursor_put(_handle, ref mdbKey, ref dataBuffer, CursorPutOptions.MultipleData); - } - } + Span dataBuffer = stackalloc MDBValue[2] { mdbValue, mdbCount }; - void FlattenInfo(Span targetBuffer) + fixed (byte* keyPtr = key) { - var cursor = targetBuffer; + var mdbKey = new MDBValue(key.Length, keyPtr); - foreach(var buffer in values) - { - buffer.CopyTo(cursor); - cursor = cursor.Slice(buffer.Length); - } + return mdb_cursor_put(_handle, ref mdbKey, ref dataBuffer, CursorPutOptions.MultipleData); } + } - int GetSize() - { - if (values.Length == 0 || values[0] == null || values[0].Length == 0) - return 0; + void FlattenInfo(Span targetBuffer) + { + var cursor = targetBuffer; - return values[0].Length; + foreach(var buffer in values) + { + buffer.CopyTo(cursor); + cursor = cursor.Slice(buffer.Length); } } - /// - /// Return up to a page of the duplicate data items at the current cursor position. Only for MDB_DUPFIXED - /// It is assumed you know the array size to break up a single byte[] into byte[][]. - /// - /// Returns , and key will be empty here, values are 2D array - public (MDBResultCode resultCode, MDBValue key, MDBValue value) GetMultiple() + int GetSize() { - return Get(CursorOperation.GetMultiple); - } + if (values.Length == 0 || values[0] == null || values[0].Length == 0) + return 0; - /// - /// Delete current key/data pair. - /// This function deletes the key/data pair to which the cursor refers. - /// - /// Options for this operation. This parameter must be set to 0 or one of the values described here. - /// MDB_NODUPDATA - delete all of the data items for the current key. This flag may only be specified if the database was opened with MDB_DUPSORT. - private MDBResultCode Delete(CursorDeleteOption option) - { - return mdb_cursor_del(_handle, option); + return values[0].Length; } + } - /// - /// Delete current key/data pair. - /// This function deletes the key/data range for which duplicates are found. - /// - public MDBResultCode DeleteDuplicateData() - { - return Delete(CursorDeleteOption.NoDuplicateData); - } + /// + /// Return up to a page of the duplicate data items at the current cursor position. Only for MDB_DUPFIXED + /// It is assumed you know the array size to break up a single byte[] into byte[][]. + /// + /// Returns , and key will be empty here, values are 2D array + public (MDBResultCode resultCode, MDBValue key, MDBValue value) GetMultiple() + { + return Get(CursorOperation.GetMultiple); + } - /// - /// Delete current key/data pair. - /// This function deletes the key/data pair to which the cursor refers. - /// - public MDBResultCode Delete() - { - return Delete(CursorDeleteOption.None); - } + /// + /// Delete current key/data pair. + /// This function deletes the key/data pair to which the cursor refers. + /// + /// Options for this operation. This parameter must be set to 0 or one of the values described here. + /// MDB_NODUPDATA - delete all of the data items for the current key. This flag may only be specified if the database was opened with MDB_DUPSORT. + private MDBResultCode Delete(CursorDeleteOption option) + { + return mdb_cursor_del(_handle, option); + } - /// - /// Renew a cursor handle. - /// Cursors are associated with a specific transaction and database and may not span threads. - /// Cursors that are only used in read-only transactions may be re-used, to avoid unnecessary malloc/free overhead. - /// The cursor may be associated with a new read-only transaction, and referencing the same database handle as it was created with. - /// - /// Returns - public MDBResultCode Renew() - { - return Renew(Transaction); - } + /// + /// Delete current key/data pair. + /// This function deletes the key/data range for which duplicates are found. + /// + public MDBResultCode DeleteDuplicateData() + { + return Delete(CursorDeleteOption.NoDuplicateData); + } - /// - /// Renew a cursor handle. - /// Cursors are associated with a specific transaction and database and may not span threads. - /// Cursors that are only used in read-only transactions may be re-used, to avoid unnecessary malloc/free overhead. - /// The cursor may be associated with a new read-only transaction, and referencing the same database handle as it was created with. - /// - /// Transaction to renew in. - /// Returns - public MDBResultCode Renew(LightningTransaction txn) - { - if(txn == null) - throw new ArgumentNullException(nameof(txn)); + /// + /// Delete current key/data pair. + /// This function deletes the key/data pair to which the cursor refers. + /// + public MDBResultCode Delete() + { + return Delete(CursorDeleteOption.None); + } + + /// + /// Renew a cursor handle. + /// Cursors are associated with a specific transaction and database and may not span threads. + /// Cursors that are only used in read-only transactions may be re-used, to avoid unnecessary malloc/free overhead. + /// The cursor may be associated with a new read-only transaction, and referencing the same database handle as it was created with. + /// + /// Returns + public MDBResultCode Renew() + { + return Renew(Transaction); + } - if (!txn.IsReadOnly) - throw new InvalidOperationException("Can't renew cursor on non-readonly transaction"); + /// + /// Renew a cursor handle. + /// Cursors are associated with a specific transaction and database and may not span threads. + /// Cursors that are only used in read-only transactions may be re-used, to avoid unnecessary malloc/free overhead. + /// The cursor may be associated with a new read-only transaction, and referencing the same database handle as it was created with. + /// + /// Transaction to renew in. + /// Returns + public MDBResultCode Renew(LightningTransaction txn) + { + if(txn == null) + throw new ArgumentNullException(nameof(txn)); - return mdb_cursor_renew(txn.Handle(), _handle); - } + if (!txn.IsReadOnly) + throw new InvalidOperationException("Can't renew cursor on non-readonly transaction"); - /// - /// Closes the cursor and deallocates all resources associated with it. - /// - /// True if called from Dispose. - private void Dispose(bool disposing) - { - if (_handle == IntPtr.Zero) - return; + return mdb_cursor_renew(txn.Handle(), _handle); + } - if (!disposing) - throw new InvalidOperationException("The LightningCursor was not disposed and cannot be reliably dealt with from the finalizer"); + /// + /// Closes the cursor and deallocates all resources associated with it. + /// + /// True if called from Dispose. + private void Dispose(bool disposing) + { + if (_handle == 0) + return; - mdb_cursor_close(_handle); - _handle = IntPtr.Zero; + if (!disposing) + throw new InvalidOperationException("The LightningCursor was not disposed and cannot be reliably dealt with from the finalizer"); - Transaction.Disposing -= Dispose; + mdb_cursor_close(_handle); + _handle = 0; - GC.SuppressFinalize(this); - } + Transaction.Disposing -= Dispose; - /// - /// Closes the cursor and deallocates all resources associated with it. - /// - public void Dispose() - { - Dispose(true); - } + GC.SuppressFinalize(this); + } - ~LightningCursor() - { - Dispose(false); - } + /// + /// Closes the cursor and deallocates all resources associated with it. + /// + public void Dispose() + { + Dispose(true); + } + + ~LightningCursor() + { + Dispose(false); } -} +} \ No newline at end of file diff --git a/src/LightningDB/LightningDB.csproj b/src/LightningDB/LightningDB.csproj index f181b2c..72db1d1 100644 --- a/src/LightningDB/LightningDB.csproj +++ b/src/LightningDB/LightningDB.csproj @@ -4,7 +4,8 @@ LightningDB 0.14.1 Ilya Lukyanov;Corey Kaylor - netstandard2.0;netcoreapp3.1 + netstandard2.0;netcoreapp3.1;net7.0 + 11 LightningDB LightningDB LICENSE @@ -28,6 +29,6 @@ - + diff --git a/src/LightningDB/LightningDatabase.cs b/src/LightningDB/LightningDatabase.cs index 231cfda..d5b49c1 100644 --- a/src/LightningDB/LightningDatabase.cs +++ b/src/LightningDB/LightningDatabase.cs @@ -1,138 +1,137 @@ using System; using static LightningDB.Native.Lmdb; -namespace LightningDB +namespace LightningDB; + +/// +/// Lightning database. +/// +public sealed class LightningDatabase : IDisposable { + private uint _handle; + private readonly DatabaseConfiguration _configuration; + private readonly bool _closeOnDispose; + private readonly LightningTransaction _transaction; + private readonly IDisposable _pinnedConfig; + /// - /// Lightning database. + /// Creates a LightningDatabase instance. /// - public sealed class LightningDatabase : IDisposable + /// Database name. + /// Active transaction. + /// Options for the database, like encoding, option flags, and comparison logic. + /// Close database handle on dispose + internal LightningDatabase(string name, LightningTransaction transaction, DatabaseConfiguration configuration, + bool closeOnDispose) { - private uint _handle; - private readonly DatabaseConfiguration _configuration; - private readonly bool _closeOnDispose; - private readonly LightningTransaction _transaction; - private readonly IDisposable _pinnedConfig; - - /// - /// Creates a LightningDatabase instance. - /// - /// Database name. - /// Active transaction. - /// Options for the database, like encoding, option flags, and comparison logic. - /// Close database handle on dispose - internal LightningDatabase(string name, LightningTransaction transaction, DatabaseConfiguration configuration, - bool closeOnDispose) - { - if (transaction == null) - throw new ArgumentNullException(nameof(transaction)); - - Name = name; - _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); - _closeOnDispose = closeOnDispose; - Environment = transaction.Environment; - _transaction = transaction; - Environment.Disposing += Dispose; - mdb_dbi_open(transaction.Handle(), name, _configuration.Flags, out _handle).ThrowOnError(); - _pinnedConfig = _configuration.ConfigureDatabase(transaction, this); - IsOpened = true; - } + if (transaction == null) + throw new ArgumentNullException(nameof(transaction)); + + Name = name; + _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + _closeOnDispose = closeOnDispose; + Environment = transaction.Environment; + _transaction = transaction; + Environment.Disposing += Dispose; + mdb_dbi_open(transaction.Handle(), name, _configuration.Flags, out _handle).ThrowOnError(); + _pinnedConfig = _configuration.ConfigureDatabase(transaction, this); + IsOpened = true; + } - public uint Handle() - { - return _handle; - } + public uint Handle() + { + return _handle; + } - /// - /// Whether the database handle has been release from Dispose, or from unsuccessful OpenDatabase call. - /// - public bool IsReleased => _handle == default; + /// + /// Whether the database handle has been release from Dispose, or from unsuccessful OpenDatabase call. + /// + public bool IsReleased => _handle == default; - /// - /// Is database opened. - /// - public bool IsOpened { get; private set; } + /// + /// Is database opened. + /// + public bool IsOpened { get; private set; } - public Stats DatabaseStats + public Stats DatabaseStats + { + get { - get + mdb_stat(_transaction.Handle(), Handle(), out var nativeStat).ThrowOnError(); + return new Stats { - mdb_stat(_transaction.Handle(), Handle(), out var nativeStat).ThrowOnError(); - return new Stats - { - BranchPages = nativeStat.ms_branch_pages.ToInt64(), - BTreeDepth = nativeStat.ms_depth, - Entries = nativeStat.ms_entries.ToInt64(), - LeafPages = nativeStat.ms_leaf_pages.ToInt64(), - OverflowPages = nativeStat.ms_overflow_pages.ToInt64(), - PageSize = nativeStat.ms_psize - }; - } + BranchPages = nativeStat.ms_branch_pages, + BTreeDepth = nativeStat.ms_depth, + Entries = nativeStat.ms_entries, + LeafPages = nativeStat.ms_leaf_pages, + OverflowPages = nativeStat.ms_overflow_pages, + PageSize = nativeStat.ms_psize + }; } + } - /// - /// Database name. - /// - public string Name { get; } + /// + /// Database name. + /// + public string Name { get; } - /// - /// Environment in which the database was opened. - /// - public LightningEnvironment Environment { get; } + /// + /// Environment in which the database was opened. + /// + public LightningEnvironment Environment { get; } - /// - /// Drops the database. - /// - public MDBResultCode Drop(LightningTransaction transaction) - { - var result = mdb_drop(transaction.Handle(), _handle, true); - IsOpened = false; - _handle = default; - return result; - } + /// + /// Drops the database. + /// + public MDBResultCode Drop(LightningTransaction transaction) + { + var result = mdb_drop(transaction.Handle(), _handle, true); + IsOpened = false; + _handle = default; + return result; + } - /// - /// Truncates all data from the database. - /// - public MDBResultCode Truncate(LightningTransaction transaction) - { - return mdb_drop(transaction.Handle(), _handle, false); - } + /// + /// Truncates all data from the database. + /// + public MDBResultCode Truncate(LightningTransaction transaction) + { + return mdb_drop(transaction.Handle(), _handle, false); + } - /// - /// Deallocates resources opened by the database. - /// - /// true if called from Dispose. - private void Dispose(bool disposing) - { - if (_handle == default) - return; + /// + /// Deallocates resources opened by the database. + /// + /// true if called from Dispose. + private void Dispose(bool disposing) + { + if (_handle == default) + return; - if(!disposing) - throw new InvalidOperationException("The LightningDatabase was not disposed and cannot be reliably dealt with from the finalizer"); + if(!disposing) + throw new InvalidOperationException("The LightningDatabase was not disposed and cannot be reliably dealt with from the finalizer"); - Environment.Disposing -= Dispose; - IsOpened = false; - _pinnedConfig.Dispose(); + Environment.Disposing -= Dispose; + IsOpened = false; + _pinnedConfig.Dispose(); - if (_closeOnDispose) - mdb_dbi_close(Environment.Handle(), _handle); + if (_closeOnDispose) + mdb_dbi_close(Environment.Handle(), _handle); - GC.SuppressFinalize(this); - _handle = default; - } + GC.SuppressFinalize(this); + _handle = default; + } - /// - /// Deallocates resources opened by the database. - /// - public void Dispose() - { - Dispose(true); - } + /// + /// Deallocates resources opened by the database. + /// + public void Dispose() + { + Dispose(true); + } - ~LightningDatabase() - { - Dispose(false); - } + ~LightningDatabase() + { + Dispose(false); } -} +} \ No newline at end of file diff --git a/src/LightningDB/LightningEnvironment.cs b/src/LightningDB/LightningEnvironment.cs index a5f7536..061df38 100644 --- a/src/LightningDB/LightningEnvironment.cs +++ b/src/LightningDB/LightningEnvironment.cs @@ -2,317 +2,315 @@ using System.IO; using static LightningDB.Native.Lmdb; -namespace LightningDB +namespace LightningDB; + +/// +/// LMDB Environment. +/// +public sealed class LightningEnvironment : IDisposable { - /// - /// LMDB Environment. - /// - public sealed class LightningEnvironment : IDisposable - { - private readonly EnvironmentConfiguration _config = new EnvironmentConfiguration(); + private readonly EnvironmentConfiguration _config = new(); - private IntPtr _handle; + private nint _handle; - public event Action Disposing; + public event Action Disposing; - /// - /// Creates a new instance of LightningEnvironment. - /// - /// Directory for storing database files. - /// Configuration for the environment. - public LightningEnvironment(string path, EnvironmentConfiguration configuration = null) - { - if (string.IsNullOrWhiteSpace(path)) - throw new ArgumentException("Invalid directory name"); + /// + /// Creates a new instance of LightningEnvironment. + /// + /// Directory for storing database files. + /// Configuration for the environment. + public LightningEnvironment(string path, EnvironmentConfiguration configuration = null) + { + if (string.IsNullOrWhiteSpace(path)) + throw new ArgumentException("Invalid directory name"); - var config = configuration ?? _config; + var config = configuration ?? _config; #if NETCOREAPP3_1_OR_GREATER - if (config.AutoResizeWindows) - { - LoadWindowsAutoResizeLibrary(); - } + if (config.AutoResizeWindows) + { + LoadWindowsAutoResizeLibrary(); + } #endif - mdb_env_create(out _handle).ThrowOnError(); - config.Configure(this); - _config = config; + mdb_env_create(out _handle).ThrowOnError(); + config.Configure(this); + _config = config; - Path = path; + Path = path; - } + } + + public nint Handle() + { + return _handle; + } + + /// + /// Whether the environment is opened. + /// + public bool IsOpened { get; private set; } + + /// + /// Current lmdb version. + /// + public LightningVersionInfo Version => LightningVersionInfo.Get(); - public IntPtr Handle() - { - return _handle; - } - /// - /// Whether the environment is opened. - /// - public bool IsOpened { get; private set; } - - /// - /// Current lmdb version. - /// - public LightningVersionInfo Version => LightningVersionInfo.Get(); - - - /// - /// Gets or Sets the size of the memory map to use for this environment. - /// The size of the memory map is also the maximum size of the database. - /// The size should be a multiple of the OS page size. - /// The default is 10485760 bytes. - /// - /// - /// The value should be chosen as large as possible, to accommodate future growth of the database. - /// This function may only be called before the environment is opened. - /// The size may be changed by closing and reopening the environment. - /// Any attempt to set a size smaller than the space already consumed by the environment will be silently changed to the current size of the used space. - /// - public long MapSize + /// + /// Gets or Sets the size of the memory map to use for this environment. + /// The size of the memory map is also the maximum size of the database. + /// The size should be a multiple of the OS page size. + /// The default is 10485760 bytes. + /// + /// + /// The value should be chosen as large as possible, to accommodate future growth of the database. + /// This function may only be called before the environment is opened. + /// The size may be changed by closing and reopening the environment. + /// Any attempt to set a size smaller than the space already consumed by the environment will be silently changed to the current size of the used space. + /// + public unsafe long MapSize + { + get => _config.MapSize; + set { - get { return _config.MapSize; } - set - { - if (value == _config.MapSize) - return; + if (value == _config.MapSize) + return; - if (_config.AutoReduceMapSizeIn32BitProcess && IntPtr.Size == 4) - _config.MapSize = int.MaxValue; - else - _config.MapSize = value; + if (_config.AutoReduceMapSizeIn32BitProcess && sizeof(nint) == 4) + _config.MapSize = int.MaxValue; + else + _config.MapSize = value; - mdb_env_set_mapsize(_handle, _config.MapSize).ThrowOnError(); - } + mdb_env_set_mapsize(_handle, _config.MapSize).ThrowOnError(); } + } - /// - /// Get the maximum number of threads for the environment. - /// - public int MaxReaders + /// + /// Get the maximum number of threads for the environment. + /// + public int MaxReaders + { + get { - get - { - mdb_env_get_maxreaders(_handle, out var readers).ThrowOnError(); - return (int) readers; - } - set - { - if (IsOpened) - throw new InvalidOperationException("Can't change MaxReaders of opened environment"); + mdb_env_get_maxreaders(_handle, out var readers).ThrowOnError(); + return (int) readers; + } + set + { + if (IsOpened) + throw new InvalidOperationException("Can't change MaxReaders of opened environment"); - mdb_env_set_maxreaders(_handle, (uint)value).ThrowOnError(); + mdb_env_set_maxreaders(_handle, (uint)value).ThrowOnError(); - _config.MaxReaders = value; - } + _config.MaxReaders = value; } + } - /// - /// Set the maximum number of named databases for the environment. - /// This function is only needed if multiple databases will be used in the environment. - /// Simpler applications that use the environment as a single unnamed database can ignore this option. - /// This function may only be called before the environment is opened. - /// - public int MaxDatabases + /// + /// Set the maximum number of named databases for the environment. + /// This function is only needed if multiple databases will be used in the environment. + /// Simpler applications that use the environment as a single unnamed database can ignore this option. + /// This function may only be called before the environment is opened. + /// + public int MaxDatabases + { + get => _config.MaxDatabases; + set { - get { return _config.MaxDatabases; } - set - { - if (IsOpened) - throw new InvalidOperationException("Can't change MaxDatabases of opened environment"); + if (IsOpened) + throw new InvalidOperationException("Can't change MaxDatabases of opened environment"); - if (value == _config.MaxDatabases) - return; + if (value == _config.MaxDatabases) + return; - mdb_env_set_maxdbs(_handle, (uint)value).ThrowOnError(); + mdb_env_set_maxdbs(_handle, (uint)value).ThrowOnError(); - _config.MaxDatabases = value; - } + _config.MaxDatabases = value; } + } - /// - /// Get statistics about the LMDB environment. - /// - public Stats EnvironmentStats + /// + /// Get statistics about the LMDB environment. + /// + public Stats EnvironmentStats + { + get { - get + mdb_env_stat(Handle(), out var nativeStat); + return new Stats { - mdb_env_stat(Handle(), out var nativeStat); - return new Stats - { - BranchPages = nativeStat.ms_branch_pages.ToInt64(), - BTreeDepth = nativeStat.ms_depth, - Entries = nativeStat.ms_entries.ToInt64(), - LeafPages = nativeStat.ms_leaf_pages.ToInt64(), - OverflowPages = nativeStat.ms_overflow_pages.ToInt64(), - PageSize = nativeStat.ms_psize - }; - } + BranchPages = nativeStat.ms_branch_pages, + BTreeDepth = nativeStat.ms_depth, + Entries = nativeStat.ms_entries, + LeafPages = nativeStat.ms_leaf_pages, + OverflowPages = nativeStat.ms_overflow_pages, + PageSize = nativeStat.ms_psize + }; } + } - /// - /// Gets information about the LMDB environment. - /// - public EnvironmentInfo Info + /// + /// Gets information about the LMDB environment. + /// + public EnvironmentInfo Info + { + get { - get + mdb_env_info(Handle(), out var nativeInfo); + return new EnvironmentInfo { - mdb_env_info(Handle(), out var nativeInfo); - return new EnvironmentInfo - { - MapSize = nativeInfo.me_mapsize.ToInt64(), - LastPageNumber = nativeInfo.me_last_pgno.ToInt64(), - LastTransactionId = nativeInfo.me_last_txnid.ToInt64(), - }; - } + MapSize = nativeInfo.me_mapsize, + LastPageNumber = nativeInfo.me_last_pgno, + LastTransactionId = nativeInfo.me_last_txnid + }; } + } - /// - /// Directory path to store database files. - /// - public string Path { get; } - - /// - /// Open the environment. - /// - public void Open(EnvironmentOpenFlags openFlags = EnvironmentOpenFlags.None, UnixAccessMode accessMode = UnixAccessMode.Default) - { - if(IsOpened) - throw new InvalidOperationException("Environment is already opened."); - - if (!openFlags.HasFlag(EnvironmentOpenFlags.NoSubDir) && !Directory.Exists(Path)) - Directory.CreateDirectory(Path); + /// + /// Directory path to store database files. + /// + public string Path { get; } - try - { - mdb_env_open(_handle, Path, openFlags, accessMode).ThrowOnError(); - } - catch(Exception ex) - { - throw new LightningException($"Failed to open environment at path {Path}", ex); - } + /// + /// Open the environment. + /// + public void Open(EnvironmentOpenFlags openFlags = EnvironmentOpenFlags.None, UnixAccessMode accessMode = UnixAccessMode.Default) + { + if(IsOpened) + throw new InvalidOperationException("Environment is already opened."); - IsOpened = true; - } + if (!openFlags.HasFlag(EnvironmentOpenFlags.NoSubDir) && !Directory.Exists(Path)) + Directory.CreateDirectory(Path); - /// - /// Create a transaction for use with the environment. - /// The transaction handle may be discarded using Abort() or Commit(). - /// Note: - /// Transactions may not span threads; a transaction must only be used by a single thread. Also, a thread may only have a single transaction. - /// Cursors may not span transactions; each cursor must be opened and closed within a single transaction. - /// - /// - /// If this parameter is non-NULL, the new transaction will be a nested transaction, with the transaction indicated by parent as its parent. - /// Transactions may be nested to any level. - /// A parent transaction may not issue any other operations besides BeginTransaction, Abort, or Commit while it has active child transactions. - /// - /// - /// Special options for this transaction. - /// - /// - /// New LightningTransaction - /// - public LightningTransaction BeginTransaction(LightningTransaction parent = null, TransactionBeginFlags beginFlags = LightningTransaction.DefaultTransactionBeginFlags) + try { - if (!IsOpened) - throw new InvalidOperationException("Environment must be opened before starting a transaction"); - - return new LightningTransaction(this, parent, beginFlags); + mdb_env_open(_handle, Path, openFlags, accessMode).ThrowOnError(); } - - /// - /// Create a transaction for use with the environment. - /// The transaction handle may be discarded usingAbort() or Commit(). - /// Note: - /// Transactions may not span threads; a transaction must only be used by a single thread. Also, a thread may only have a single transaction. - /// Cursors may not span transactions; each cursor must be opened and closed within a single transaction. - /// - /// - /// Special options for this transaction. - /// - /// - /// New LightningTransaction - /// - public LightningTransaction BeginTransaction(TransactionBeginFlags beginFlags) + catch(Exception ex) { - return BeginTransaction(null, beginFlags); + throw new LightningException($"Failed to open environment at path {Path}", ex); } - /// - /// Copy an MDB environment to the specified path. - /// This function may be used to make a backup of an existing environment. - /// - /// The directory in which the copy will reside. This directory must already exist and be writable but must otherwise be empty. - /// Omit empty pages when copying. - public void CopyTo(string path, bool compact = false) - { - EnsureOpened(); + IsOpened = true; + } - var flags = compact - ? EnvironmentCopyFlags.Compact - : EnvironmentCopyFlags.None; - - mdb_env_copy2(_handle, path, flags); - } + /// + /// Create a transaction for use with the environment. + /// The transaction handle may be discarded using Abort() or Commit(). + /// Note: + /// Transactions may not span threads; a transaction must only be used by a single thread. Also, a thread may only have a single transaction. + /// Cursors may not span transactions; each cursor must be opened and closed within a single transaction. + /// + /// + /// If this parameter is non-NULL, the new transaction will be a nested transaction, with the transaction indicated by parent as its parent. + /// Transactions may be nested to any level. + /// A parent transaction may not issue any other operations besides BeginTransaction, Abort, or Commit while it has active child transactions. + /// + /// + /// Special options for this transaction. + /// + /// + /// New LightningTransaction + /// + public LightningTransaction BeginTransaction(LightningTransaction parent = null, TransactionBeginFlags beginFlags = LightningTransaction.DefaultTransactionBeginFlags) + { + if (!IsOpened) + throw new InvalidOperationException("Environment must be opened before starting a transaction"); - //TODO: tests - /// - /// Flush the data buffers to disk. - /// Data is always written to disk when LightningTransaction.Commit is called, but the operating system may keep it buffered. - /// MDB always flushes the OS buffers upon commit as well, unless the environment was opened with EnvironmentOpenFlags.NoSync or in part EnvironmentOpenFlags.NoMetaSync. - /// - /// If true, force a synchronous flush. Otherwise if the environment has the EnvironmentOpenFlags.NoSync flag set the flushes will be omitted, and with MDB_MAPASYNC they will be asynchronous. - public MDBResultCode Flush(bool force) - { - return mdb_env_sync(_handle, force); - } + return new LightningTransaction(this, parent, beginFlags); + } - private void EnsureOpened() - { - if (!IsOpened) - throw new InvalidOperationException("Environment should be opened"); - } + /// + /// Create a transaction for use with the environment. + /// The transaction handle may be discarded usingAbort() or Commit(). + /// Note: + /// Transactions may not span threads; a transaction must only be used by a single thread. Also, a thread may only have a single transaction. + /// Cursors may not span transactions; each cursor must be opened and closed within a single transaction. + /// + /// + /// Special options for this transaction. + /// + /// + /// New LightningTransaction + /// + public LightningTransaction BeginTransaction(TransactionBeginFlags beginFlags) + { + return BeginTransaction(null, beginFlags); + } - /// - /// Disposes the environment and deallocates all resources associated with it. - /// - /// True if called from Dispose. - private void Dispose(bool disposing) - { - if (_handle == IntPtr.Zero) - return; + /// + /// Copy an MDB environment to the specified path. + /// This function may be used to make a backup of an existing environment. + /// + /// The directory in which the copy will reside. This directory must already exist and be writable but must otherwise be empty. + /// Omit empty pages when copying. + public void CopyTo(string path, bool compact = false) + { + EnsureOpened(); - if(!disposing) - throw new InvalidOperationException("The LightningEnvironment was not disposed and cannot be reliably dealt with from the finalizer"); + var flags = compact + ? EnvironmentCopyFlags.Compact + : EnvironmentCopyFlags.None; + + mdb_env_copy2(_handle, path, flags); + } - var copy = Disposing; - copy?.Invoke(); + /// + /// Flush the data buffers to disk. + /// Data is always written to disk when LightningTransaction.Commit is called, but the operating system may keep it buffered. + /// MDB always flushes the OS buffers upon commit as well, unless the environment was opened with EnvironmentOpenFlags.NoSync or in part EnvironmentOpenFlags.NoMetaSync. + /// + /// If true, force a synchronous flush. Otherwise if the environment has the EnvironmentOpenFlags.NoSync flag set the flushes will be omitted, and with MDB_MAPASYNC they will be asynchronous. + public MDBResultCode Flush(bool force) + { + return mdb_env_sync(_handle, force); + } - if (IsOpened) - { - mdb_env_close(_handle); - IsOpened = false; - } + private void EnsureOpened() + { + if (!IsOpened) + throw new InvalidOperationException("Environment should be opened"); + } - _handle = IntPtr.Zero; - GC.SuppressFinalize(this); - } + /// + /// Disposes the environment and deallocates all resources associated with it. + /// + /// True if called from Dispose. + private void Dispose(bool disposing) + { + if (_handle == 0) + return; - /// - /// Dispose the environment and release the memory map. - /// Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function. - /// Attempts to use any such handles after calling this function will cause a SIGSEGV. - /// The environment handle will be freed and must not be used again after this call. - /// - public void Dispose() - { - Dispose(true); - } + if(!disposing) + throw new InvalidOperationException("The LightningEnvironment was not disposed and cannot be reliably dealt with from the finalizer"); - ~LightningEnvironment() + var copy = Disposing; + copy?.Invoke(); + + if (IsOpened) { - Dispose(false); + mdb_env_close(_handle); + IsOpened = false; } + + _handle = 0; + GC.SuppressFinalize(this); + } + + /// + /// Dispose the environment and release the memory map. + /// Only a single thread may call this function. All transactions, databases, and cursors must already be closed before calling this function. + /// Attempts to use any such handles after calling this function will cause a SIGSEGV. + /// The environment handle will be freed and must not be used again after this call. + /// + public void Dispose() + { + Dispose(true); + } + + ~LightningEnvironment() + { + Dispose(false); } } \ No newline at end of file diff --git a/src/LightningDB/LightningException.cs b/src/LightningDB/LightningException.cs index dda5b9e..7c7a7ef 100644 --- a/src/LightningDB/LightningException.cs +++ b/src/LightningDB/LightningException.cs @@ -1,29 +1,28 @@ using System; -namespace LightningDB +namespace LightningDB; + +/// +/// An exception caused by lmdb operations. +/// +public class LightningException : Exception { - /// - /// An exception caused by lmdb operations. - /// - public class LightningException : Exception + internal LightningException(string message, int statusCode) : base (message) { - internal LightningException(string message, int statusCode) : base (message) - { - StatusCode = statusCode; - } + StatusCode = statusCode; + } - internal LightningException(string message, Exception innerException) : base(message, innerException) - { - } + internal LightningException(string message, Exception innerException) : base(message, innerException) + { + } - /// - /// The status code LMDB returned from an operation. - /// - public int StatusCode { get; } + /// + /// The status code LMDB returned from an operation. + /// + public int StatusCode { get; } - public override string ToString() - { - return $"LightningDB {StatusCode}: {Message}"; - } + public override string ToString() + { + return $"LightningDB {StatusCode}: {Message}"; } -} +} \ No newline at end of file diff --git a/src/LightningDB/LightningExtensions.cs b/src/LightningDB/LightningExtensions.cs index bd63102..6185a50 100644 --- a/src/LightningDB/LightningExtensions.cs +++ b/src/LightningDB/LightningExtensions.cs @@ -4,159 +4,157 @@ using System.Runtime.InteropServices; using LightningDB.Native; -namespace LightningDB +namespace LightningDB; + +public static class LightningExtensions { - public static class LightningExtensions + /// + /// Throws a on anything other than NotFound, or Success + /// + /// The result code to evaluate for errors + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MDBResultCode ThrowOnReadError(this MDBResultCode resultCode) { - /// - /// Throws a on anything other than NotFound, or Success - /// - /// The result code to evaluate for errors - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MDBResultCode ThrowOnReadError(this MDBResultCode resultCode) - { - if (resultCode == MDBResultCode.NotFound) - return resultCode; - return resultCode.ThrowOnError(); - } + return resultCode == MDBResultCode.NotFound + ? resultCode : resultCode.ThrowOnError(); + } - /// - /// Throws a on anything other than Success - /// - /// The result code to evaluate for errors - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MDBResultCode ThrowOnError(this MDBResultCode resultCode) - { - if (resultCode == MDBResultCode.Success) - return resultCode; - var statusCode = (int) resultCode; - var message = mdb_strerror(statusCode); - throw new LightningException(message, statusCode); - } + /// + /// Throws a on anything other than Success + /// + /// The result code to evaluate for errors + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MDBResultCode ThrowOnError(this MDBResultCode resultCode) + { + if (resultCode == MDBResultCode.Success) + return resultCode; + var statusCode = (int) resultCode; + var message = mdb_strerror(statusCode); + throw new LightningException(message, statusCode); + } - /// - /// Throws a on anything other than NotFound, or Success - /// - /// A representing the get result operation - /// The provided if no error occurs - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static (MDBResultCode resultCode, MDBValue key, MDBValue value) ThrowOnReadError( - this ValueTuple result) - { - result.Item1.ThrowOnReadError(); - return result; - } + /// + /// Throws a on anything other than NotFound, or Success + /// + /// A representing the get result operation + /// The provided if no error occurs + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static (MDBResultCode resultCode, MDBValue key, MDBValue value) ThrowOnReadError( + this ValueTuple result) + { + result.Item1.ThrowOnReadError(); + return result; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static string mdb_strerror(int err) - { - var ptr = Lmdb.mdb_strerror(err); - return Marshal.PtrToStringAnsi(ptr); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static string mdb_strerror(int err) + { + var ptr = Lmdb.mdb_strerror(err); + return Marshal.PtrToStringAnsi(ptr); + } - /// - /// Enumerates the key/value pairs of the starting at the current position. - /// - /// - /// key/value pairs of - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IEnumerable> AsEnumerable(this LightningCursor cursor) + /// + /// Enumerates the key/value pairs of the starting at the current position. + /// + /// + /// key/value pairs of + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable> AsEnumerable(this LightningCursor cursor) + { + while(cursor.Next() == MDBResultCode.Success) { - while(cursor.Next() == MDBResultCode.Success) - { - var (resultCode, key, value) = cursor.GetCurrent(); - resultCode.ThrowOnError(); - yield return (key, value); - } + var (resultCode, key, value) = cursor.GetCurrent(); + resultCode.ThrowOnError(); + yield return (key, value); } + } - /// - /// Tries to get a value by its key. - /// - /// The transaction. - /// The database to query. - /// A span containing the key to look up. - /// A byte array containing the value found in the database, if it exists. - /// True if key exists, false if not. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGet(this LightningTransaction tx, LightningDatabase db, byte[] key, out byte[] value) - { - return TryGet(tx, db, key.AsSpan(), out value); - } + /// + /// Tries to get a value by its key. + /// + /// The transaction. + /// The database to query. + /// A span containing the key to look up. + /// A byte array containing the value found in the database, if it exists. + /// True if key exists, false if not. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryGet(this LightningTransaction tx, LightningDatabase db, byte[] key, out byte[] value) + { + return TryGet(tx, db, key.AsSpan(), out value); + } - /// - /// Tries to get a value by its key. - /// - /// The transaction. - /// The database to query. - /// A span containing the key to look up. - /// A byte array containing the value found in the database, if it exists. - /// True if key exists, false if not. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGet(this LightningTransaction tx, LightningDatabase db, ReadOnlySpan key, out byte[] value) + /// + /// Tries to get a value by its key. + /// + /// The transaction. + /// The database to query. + /// A span containing the key to look up. + /// A byte array containing the value found in the database, if it exists. + /// True if key exists, false if not. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryGet(this LightningTransaction tx, LightningDatabase db, ReadOnlySpan key, out byte[] value) + { + var (resultCode, _, mdbValue) = tx.Get(db, key); + if (resultCode == MDBResultCode.Success) { - var (resultCode, _, mdbValue) = tx.Get(db, key); - if (resultCode == MDBResultCode.Success) - { - value = mdbValue.CopyToNewArray(); - return true; - } - value = default; - return false; + value = mdbValue.CopyToNewArray(); + return true; } + value = default; + return false; + } - /// - /// Tries to get a value by its key. - /// - /// The transaction. - /// The database to query. - /// A span containing the key to look up. - /// - /// A buffer to receive the value data retrieved from the database - /// - /// True if key exists, false if not. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGet(this LightningTransaction tx, LightningDatabase db, ReadOnlySpan key, byte[] destinationValueBuffer) - { - var (resultCode, _, mdbValue) = tx.Get(db, key); - if (resultCode != MDBResultCode.Success) - return false; + /// + /// Tries to get a value by its key. + /// + /// The transaction. + /// The database to query. + /// A span containing the key to look up. + /// + /// A buffer to receive the value data retrieved from the database + /// + /// True if key exists, false if not. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryGet(this LightningTransaction tx, LightningDatabase db, ReadOnlySpan key, byte[] destinationValueBuffer) + { + var (resultCode, _, mdbValue) = tx.Get(db, key); + if (resultCode != MDBResultCode.Success) + return false; - var valueSpan = mdbValue.AsSpan(); - if (valueSpan.TryCopyTo(destinationValueBuffer)) - { - return true; - } - throw new LightningException("Incorrect buffer size given in destinationValueBuffer", (int)MDBResultCode.BadValSize); - } - - /// - /// Check whether data exists in database. - /// - /// The transaction. - /// The database to query. - /// A span containing the key to look up. - /// True if key exists, false if not. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsKey(this LightningTransaction tx, LightningDatabase db, ReadOnlySpan key) + var valueSpan = mdbValue.AsSpan(); + if (valueSpan.TryCopyTo(destinationValueBuffer)) { - var (resultCode, _, _) = tx.Get(db, key); - return resultCode == MDBResultCode.Success; + return true; } + throw new LightningException("Incorrect buffer size given in destinationValueBuffer", (int)MDBResultCode.BadValSize); + } - /// - /// Check whether data exists in database. - /// - /// The transaction. - /// The database to query. - /// A span containing the key to look up. - /// True if key exists, false if not. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ContainsKey(this LightningTransaction tx, LightningDatabase db, byte[] key) - { - return ContainsKey(tx, db, key.AsSpan()); - } + /// + /// Check whether data exists in database. + /// + /// The transaction. + /// The database to query. + /// A span containing the key to look up. + /// True if key exists, false if not. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ContainsKey(this LightningTransaction tx, LightningDatabase db, ReadOnlySpan key) + { + var (resultCode, _, _) = tx.Get(db, key); + return resultCode == MDBResultCode.Success; + } + + /// + /// Check whether data exists in database. + /// + /// The transaction. + /// The database to query. + /// A span containing the key to look up. + /// True if key exists, false if not. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ContainsKey(this LightningTransaction tx, LightningDatabase db, byte[] key) + { + return ContainsKey(tx, db, key.AsSpan()); } } \ No newline at end of file diff --git a/src/LightningDB/LightningTransaction.cs b/src/LightningDB/LightningTransaction.cs index 652284a..7e4ee43 100644 --- a/src/LightningDB/LightningTransaction.cs +++ b/src/LightningDB/LightningTransaction.cs @@ -2,398 +2,392 @@ using static LightningDB.Native.Lmdb; -namespace LightningDB +namespace LightningDB; + +/// +/// Represents a transaction. +/// +public sealed class LightningTransaction : IDisposable { /// - /// Represents a transaction. + /// Default options used to begin new transactions. /// - public sealed class LightningTransaction : IDisposable - { - /// - /// Default options used to begin new transactions. - /// - public const TransactionBeginFlags DefaultTransactionBeginFlags = TransactionBeginFlags.None; - - private IntPtr _handle; - private readonly IntPtr _originalHandle; - - /// - /// Created new instance of LightningTransaction - /// - /// Environment. - /// Parent transaction or null. - /// Transaction open options. - internal LightningTransaction(LightningEnvironment environment, LightningTransaction parent, TransactionBeginFlags flags) - { - Environment = environment ?? throw new ArgumentNullException(nameof(environment)); - ParentTransaction = parent; - IsReadOnly = flags == TransactionBeginFlags.ReadOnly; - State = LightningTransactionState.Active; - Environment.Disposing += Dispose; - if (parent != null) - { - parent.Disposing += Dispose; - parent.StateChanging += OnParentStateChanging; - } + public const TransactionBeginFlags DefaultTransactionBeginFlags = TransactionBeginFlags.None; - var parentHandle = parent?.Handle() ?? IntPtr.Zero; - mdb_txn_begin(environment.Handle(), parentHandle, flags, out _handle).ThrowOnError(); - _originalHandle = _handle; - } + private nint _handle; + private readonly nint _originalHandle; - public IntPtr Handle() - { - return _handle; - } - - private void OnParentStateChanging(LightningTransactionState state) + /// + /// Created new instance of LightningTransaction + /// + /// Environment. + /// Parent transaction or null. + /// Transaction open options. + internal LightningTransaction(LightningEnvironment environment, LightningTransaction parent, TransactionBeginFlags flags) + { + Environment = environment ?? throw new ArgumentNullException(nameof(environment)); + ParentTransaction = parent; + IsReadOnly = flags == TransactionBeginFlags.ReadOnly; + State = LightningTransactionState.Active; + Environment.Disposing += Dispose; + if (parent != null) { - switch (state) - { - case LightningTransactionState.Aborted: - case LightningTransactionState.Commited: - Abort(); - break; - default: - break; - } + parent.Disposing += Dispose; + parent.StateChanging += OnParentStateChanging; } - public event Action Disposing; - private event Action StateChanging; + var parentHandle = parent?.Handle() ?? default(nint); + mdb_txn_begin(environment.Handle(), parentHandle, flags, out _handle).ThrowOnError(); + _originalHandle = _handle; + } - /// - /// Current transaction state. - /// - public LightningTransactionState State { get; private set; } + public nint Handle() + { + return _handle; + } - /// - /// Begin a child transaction. - /// - /// Options for a new transaction. - /// New child transaction. - public LightningTransaction BeginTransaction(TransactionBeginFlags beginFlags = DefaultTransactionBeginFlags) + private void OnParentStateChanging(LightningTransactionState state) + { + switch (state) { - return new LightningTransaction(Environment, this, beginFlags); + case LightningTransactionState.Aborted: + case LightningTransactionState.Committed: + Abort(); + break; } + } - /// - /// Opens a database in context of this transaction. - /// - /// Database name (optional). If null then the default name is used. - /// Database open options. - /// Close database handle on dispose - /// Created database wrapper. - public LightningDatabase OpenDatabase(string name = null, DatabaseConfiguration configuration = null, bool closeOnDispose = true) - { - configuration = configuration ?? new DatabaseConfiguration(); - var db = new LightningDatabase(name, this, configuration, closeOnDispose); - return db; - } + public event Action Disposing; + private event Action StateChanging; - /// - /// Drops the database. - /// - public MDBResultCode DropDatabase(LightningDatabase database) - { - return database.Drop(this); - } + /// + /// Current transaction state. + /// + public LightningTransactionState State { get; private set; } - /// - /// Truncates all data from the database. - /// - public MDBResultCode TruncateDatabase(LightningDatabase database) - { - return database.Truncate(this); - } + /// + /// Begin a child transaction. + /// + /// Options for a new transaction. + /// New child transaction. + public LightningTransaction BeginTransaction(TransactionBeginFlags beginFlags = DefaultTransactionBeginFlags) + { + return new LightningTransaction(Environment, this, beginFlags); + } - /// - /// Create a cursor. - /// Cursors are associated with a specific transaction and database and may not span threads. - /// - /// A database. - public LightningCursor CreateCursor(LightningDatabase db) - { - return new LightningCursor(db, this); - } + /// + /// Opens a database in context of this transaction. + /// + /// Database name (optional). If null then the default name is used. + /// Database open options. + /// Close database handle on dispose + /// Created database wrapper. + public LightningDatabase OpenDatabase(string name = null, DatabaseConfiguration configuration = null, bool closeOnDispose = true) + { + configuration ??= new DatabaseConfiguration(); + var db = new LightningDatabase(name, this, configuration, closeOnDispose); + return db; + } - /// - /// Get value from a database. - /// - /// The database to query. - /// An array containing the key to look up. - /// Requested value's byte array if exists, or null if not. - public (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(LightningDatabase db, byte[] key) - {//argument validation delegated to next call - - return Get(db, key.AsSpan()); - } + /// + /// Drops the database. + /// + public MDBResultCode DropDatabase(LightningDatabase database) + { + return database.Drop(this); + } - /// - /// Get value from a database. - /// - /// The database to query. - /// A span containing the key to look up. - /// Requested value's byte array if exists, or null if not. - public unsafe (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(LightningDatabase db, ReadOnlySpan key) - { - if (db == null) - throw new ArgumentNullException(nameof(db)); + /// + /// Truncates all data from the database. + /// + public MDBResultCode TruncateDatabase(LightningDatabase database) + { + return database.Truncate(this); + } - fixed(byte* keyBuffer = key) - { - var mdbKey = new MDBValue(key.Length, keyBuffer); + /// + /// Create a cursor. + /// Cursors are associated with a specific transaction and database and may not span threads. + /// + /// A database. + public LightningCursor CreateCursor(LightningDatabase db) + { + return new LightningCursor(db, this); + } - return (mdb_get(_handle, db.Handle(), ref mdbKey, out var mdbValue), mdbKey, mdbValue); - } - } + /// + /// Get value from a database. + /// + /// The database to query. + /// An array containing the key to look up. + /// Requested value's byte array if exists, or null if not. + public (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(LightningDatabase db, byte[] key) + {//argument validation delegated to next call + + return Get(db, key.AsSpan()); + } - /// - /// Put data into a database. - /// - /// The database to query. - /// A span containing the key to look up. - /// A byte array containing the value found in the database, if it exists. - /// Operation options (optional). - public MDBResultCode Put(LightningDatabase db, byte[] key, byte[] value, PutOptions options = PutOptions.None) - {//argument validation delegated to next call - return Put(db, key.AsSpan(), value.AsSpan(), options); - } + /// + /// Get value from a database. + /// + /// The database to query. + /// A span containing the key to look up. + /// Requested value's byte array if exists, or null if not. + public unsafe (MDBResultCode resultCode, MDBValue key, MDBValue value) Get(LightningDatabase db, ReadOnlySpan key) + { + if (db == null) + throw new ArgumentNullException(nameof(db)); - /// - /// Put data into a database. - /// - /// Database. - /// Key byte array. - /// Value byte array. - /// Operation options (optional). - public unsafe MDBResultCode Put(LightningDatabase db, ReadOnlySpan key, ReadOnlySpan value, PutOptions options = PutOptions.None) + fixed(byte* keyBuffer = key) { - if (db == null) - throw new ArgumentNullException(nameof(db)); - - fixed (byte* keyPtr = key) - fixed (byte* valuePtr = value) - { - var mdbKey = new MDBValue(key.Length, keyPtr); - var mdbValue = new MDBValue(value.Length, valuePtr); + var mdbKey = new MDBValue(key.Length, keyBuffer); - return mdb_put(_handle, db.Handle(), mdbKey, mdbValue, options); - } + return (mdb_get(_handle, db.Handle(), ref mdbKey, out var mdbValue), mdbKey, mdbValue); } + } - /// - /// Delete items from a database. - /// This function removes key/data pairs from the database. - /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. - /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. - /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. - /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. - /// - /// A database handle returned by mdb_dbi_open() - /// The key to delete from the database - /// The data to delete (optional) - public MDBResultCode Delete(LightningDatabase db, byte[] key, byte[] value) - {//argument validation delegated to next call - return Delete(db, key.AsSpan(), value.AsSpan()); - } + /// + /// Put data into a database. + /// + /// The database to query. + /// A span containing the key to look up. + /// A byte array containing the value found in the database, if it exists. + /// Operation options (optional). + public MDBResultCode Put(LightningDatabase db, byte[] key, byte[] value, PutOptions options = PutOptions.None) + {//argument validation delegated to next call + return Put(db, key.AsSpan(), value.AsSpan(), options); + } + + /// + /// Put data into a database. + /// + /// Database. + /// Key byte array. + /// Value byte array. + /// Operation options (optional). + public unsafe MDBResultCode Put(LightningDatabase db, ReadOnlySpan key, ReadOnlySpan value, PutOptions options = PutOptions.None) + { + if (db == null) + throw new ArgumentNullException(nameof(db)); - /// - /// Delete items from a database. - /// This function removes key/data pairs from the database. - /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. - /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. - /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. - /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. - /// - /// A database handle returned by mdb_dbi_open() - /// The key to delete from the database - /// The data to delete (optional) - public unsafe MDBResultCode Delete(LightningDatabase db, ReadOnlySpan key, ReadOnlySpan value) + fixed (byte* keyPtr = key) + fixed (byte* valuePtr = value) { - if (db == null) - throw new ArgumentNullException(nameof(db)); + var mdbKey = new MDBValue(key.Length, keyPtr); + var mdbValue = new MDBValue(value.Length, valuePtr); - fixed (byte* keyPtr = key) - fixed (byte* valuePtr = value) - { - var mdbKey = new MDBValue(key.Length, keyPtr); - if (value == null) - { - return mdb_del(_handle, db.Handle(), mdbKey); - } - var mdbValue = new MDBValue(value.Length, valuePtr); - return mdb_del(_handle, db.Handle(), mdbKey, mdbValue); - } + return mdb_put(_handle, db.Handle(), mdbKey, mdbValue, options); } + } - /// - /// Delete items from a database. - /// This function removes key/data pairs from the database. - /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. - /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. - /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. - /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. - /// - /// A database handle returned by mdb_dbi_open() - /// The key to delete from the database - public MDBResultCode Delete(LightningDatabase db, byte[] key) - { - return Delete(db, key.AsSpan()); - } + /// + /// Delete items from a database. + /// This function removes key/data pairs from the database. + /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. + /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. + /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. + /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. + /// + /// A database handle returned by mdb_dbi_open() + /// The key to delete from the database + /// The data to delete (optional) + public MDBResultCode Delete(LightningDatabase db, byte[] key, byte[] value) + {//argument validation delegated to next call + return Delete(db, key.AsSpan(), value.AsSpan()); + } + /// + /// Delete items from a database. + /// This function removes key/data pairs from the database. + /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. + /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. + /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. + /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. + /// + /// A database handle returned by mdb_dbi_open() + /// The key to delete from the database + /// The data to delete (optional) + public unsafe MDBResultCode Delete(LightningDatabase db, ReadOnlySpan key, ReadOnlySpan value) + { + if (db == null) + throw new ArgumentNullException(nameof(db)); - /// - /// Delete items from a database. - /// This function removes key/data pairs from the database. - /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. - /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. - /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. - /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. - /// - /// A database handle returned by mdb_dbi_open() - /// The key to delete from the database - public unsafe MDBResultCode Delete(LightningDatabase db, ReadOnlySpan key) + fixed (byte* keyPtr = key) + fixed (byte* valuePtr = value) { - fixed(byte* ptr = key) { - var mdbKey = new MDBValue(key.Length, ptr); + var mdbKey = new MDBValue(key.Length, keyPtr); + if (value == null) + { return mdb_del(_handle, db.Handle(), mdbKey); } + var mdbValue = new MDBValue(value.Length, valuePtr); + return mdb_del(_handle, db.Handle(), mdbKey, mdbValue); } + } - /// - /// Reset current transaction. - /// - public void Reset() - { - if (!IsReadOnly) - throw new InvalidOperationException("Can't reset non-readonly transaction"); + /// + /// Delete items from a database. + /// This function removes key/data pairs from the database. + /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. + /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. + /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. + /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. + /// + /// A database handle returned by mdb_dbi_open() + /// The key to delete from the database + public MDBResultCode Delete(LightningDatabase db, byte[] key) + { + return Delete(db, key.AsSpan()); + } - mdb_txn_reset(_handle); - State = LightningTransactionState.Reseted; + + /// + /// Delete items from a database. + /// This function removes key/data pairs from the database. + /// If the database does not support sorted duplicate data items (MDB_DUPSORT) the data parameter is ignored. + /// If the database supports sorted duplicates and the data parameter is NULL, all of the duplicate data items for the key will be deleted. + /// Otherwise, if the data parameter is non-NULL only the matching data item will be deleted. + /// This function will return MDB_NOTFOUND if the specified key/data pair is not in the database. + /// + /// A database handle returned by mdb_dbi_open() + /// The key to delete from the database + public unsafe MDBResultCode Delete(LightningDatabase db, ReadOnlySpan key) + { + fixed(byte* ptr = key) { + var mdbKey = new MDBValue(key.Length, ptr); + return mdb_del(_handle, db.Handle(), mdbKey); } + } - /// - /// Renew current transaction. - /// - public MDBResultCode Renew() - { - if (!IsReadOnly) - throw new InvalidOperationException("Can't renew non-readonly transaction"); + /// + /// Reset current transaction. + /// + public void Reset() + { + if (!IsReadOnly) + throw new InvalidOperationException("Can't reset non-readonly transaction"); - if (State != LightningTransactionState.Reseted) - throw new InvalidOperationException("Transaction should be reset first"); + mdb_txn_reset(_handle); + State = LightningTransactionState.Reset; + } - var result = mdb_txn_renew(_handle); - State = LightningTransactionState.Active; - return result; - } + /// + /// Renew current transaction. + /// + public MDBResultCode Renew() + { + if (!IsReadOnly) + throw new InvalidOperationException("Can't renew non-readonly transaction"); - /// - /// Commit all the operations of a transaction into the database. - /// All cursors opened within the transaction will be closed by this call. - /// The cursors and transaction handle will be freed and must not be used again after this call. - /// - public MDBResultCode Commit() - { - State = LightningTransactionState.Commited; - StateChanging?.Invoke(State); - return mdb_txn_commit(_handle); - } + if (State != LightningTransactionState.Reset) + throw new InvalidOperationException("Transaction should be reset first"); - /// - /// Abandon all the operations of the transaction instead of saving them. - /// All cursors opened within the transaction will be closed by this call. - /// The cursors and transaction handle will be freed and must not be used again after this call. - /// - public void Abort() - { - State = LightningTransactionState.Aborted; - StateChanging?.Invoke(State); - mdb_txn_abort(_handle); - } + var result = mdb_txn_renew(_handle); + State = LightningTransactionState.Active; + return result; + } - /// - /// The number of items in the database. - /// - /// The database we are counting items in. - /// The number of items. - public long GetEntriesCount(LightningDatabase db) - { - mdb_stat(_handle, db.Handle(), out var stat).ThrowOnError(); + /// + /// Commit all the operations of a transaction into the database. + /// All cursors opened within the transaction will be closed by this call. + /// The cursors and transaction handle will be freed and must not be used again after this call. + /// + public MDBResultCode Commit() + { + State = LightningTransactionState.Committed; + StateChanging?.Invoke(State); + return mdb_txn_commit(_handle); + } - return stat.ms_entries.ToInt64(); - } + /// + /// Abandon all the operations of the transaction instead of saving them. + /// All cursors opened within the transaction will be closed by this call. + /// The cursors and transaction handle will be freed and must not be used again after this call. + /// + public void Abort() + { + State = LightningTransactionState.Aborted; + StateChanging?.Invoke(State); + mdb_txn_abort(_handle); + } - /// - /// Environment in which the transaction was opened. - /// - public LightningEnvironment Environment { get; } - - /// - /// Parent transaction of this transaction. - /// - public LightningTransaction ParentTransaction { get; } - - /// - /// Whether this transaction is read-only. - /// - public bool IsReadOnly { get; } - - /// - /// Abort this transaction and deallocate all resources associated with it (including databases). - /// - /// True if called from Dispose. - private void Dispose(bool disposing) - { - if (_handle == IntPtr.Zero) - return; + /// + /// The number of items in the database. + /// + /// The database we are counting items in. + /// The number of items. + public long GetEntriesCount(LightningDatabase db) + { + mdb_stat(_handle, db.Handle(), out var stat).ThrowOnError(); - Environment.Disposing -= Dispose; - if (ParentTransaction != null) - { - ParentTransaction.Disposing -= Dispose; - ParentTransaction.StateChanging -= OnParentStateChanging; - } + return stat.ms_entries; + } - Disposing?.Invoke(); + /// + /// Environment in which the transaction was opened. + /// + public LightningEnvironment Environment { get; } - if (State == LightningTransactionState.Active || State == LightningTransactionState.Reseted) - Abort(); + /// + /// Parent transaction of this transaction. + /// + public LightningTransaction ParentTransaction { get; } - _handle = IntPtr.Zero; + /// + /// Whether this transaction is read-only. + /// + public bool IsReadOnly { get; } - if (disposing) - { - GC.SuppressFinalize(this); - } - } + /// + /// Abort this transaction and deallocate all resources associated with it (including databases). + /// + /// True if called from Dispose. + private void Dispose(bool disposing) + { + if (_handle == 0) + return; - /// - /// Dispose this transaction and deallocate all resources associated with it (including databases). - /// - public void Dispose() + Environment.Disposing -= Dispose; + if (ParentTransaction != null) { - Dispose(true); + ParentTransaction.Disposing -= Dispose; + ParentTransaction.StateChanging -= OnParentStateChanging; } - ~LightningTransaction() - { - Dispose(false); - } + Disposing?.Invoke(); + + if (State is LightningTransactionState.Active or LightningTransactionState.Reset) + Abort(); - public override int GetHashCode() + _handle = 0; + + if (disposing) { - return _originalHandle.GetHashCode(); + GC.SuppressFinalize(this); } + } - public override bool Equals(object obj) - { - var tran = obj as LightningTransaction; - if (tran == null) - return false; + /// + /// Dispose this transaction and deallocate all resources associated with it (including databases). + /// + public void Dispose() + { + Dispose(true); + } - return _handle.Equals(tran._handle); - } + ~LightningTransaction() + { + Dispose(false); + } + + public override int GetHashCode() + { + return _originalHandle.GetHashCode(); + } + + public override bool Equals(object obj) + { + var tran = obj as LightningTransaction; + return tran != null && _handle.Equals(tran._handle); } -} +} \ No newline at end of file diff --git a/src/LightningDB/LightningTransactionState.cs b/src/LightningDB/LightningTransactionState.cs index 6f87ff6..64edf08 100644 --- a/src/LightningDB/LightningTransactionState.cs +++ b/src/LightningDB/LightningTransactionState.cs @@ -1,28 +1,27 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Transaction state. +/// +public enum LightningTransactionState { /// - /// Transaction state. + /// Transaction is currently active. /// - public enum LightningTransactionState - { - /// - /// Transaction is currently active. - /// - Active, + Active, - /// - /// Transaction is currently reseted. - /// - Reseted, + /// + /// Transaction is currently reset. + /// + Reset, - /// - /// Transaction is aborted. - /// - Aborted, + /// + /// Transaction is aborted. + /// + Aborted, - /// - /// Transaction is commited. - /// - Commited - } -} + /// + /// Transaction is committed. + /// + Committed +} \ No newline at end of file diff --git a/src/LightningDB/LightningVersionInfo.cs b/src/LightningDB/LightningVersionInfo.cs index d85373f..658956a 100644 --- a/src/LightningDB/LightningVersionInfo.cs +++ b/src/LightningDB/LightningVersionInfo.cs @@ -1,47 +1,46 @@ using System.Runtime.InteropServices; using static LightningDB.Native.Lmdb; -namespace LightningDB +namespace LightningDB; + +/// +/// Represents lmdb version information. +/// +public class LightningVersionInfo { - /// - /// Represents lmdb version information. - /// - public class LightningVersionInfo + internal static LightningVersionInfo Get() { - internal static LightningVersionInfo Get() + var version = mdb_version(out var major, out var minor, out var patch); + return new LightningVersionInfo { - var version = mdb_version(out var major, out var minor, out var patch); - return new LightningVersionInfo - { - Version = Marshal.PtrToStringAnsi(version), - Major = major, - Minor = minor, - Patch = patch - }; - } + Version = Marshal.PtrToStringAnsi(version), + Major = major, + Minor = minor, + Patch = patch + }; + } - private LightningVersionInfo() - { - } + private LightningVersionInfo() + { + } - /// - /// Major version number. - /// - public int Major { get; private set; } + /// + /// Major version number. + /// + public int Major { get; private set; } - /// - /// Minor version number. - /// - public int Minor { get; private set; } + /// + /// Minor version number. + /// + public int Minor { get; private set; } - /// - /// Patch version number. - /// - public int Patch { get; private set; } + /// + /// Patch version number. + /// + public int Patch { get; private set; } - /// - /// Version string. - /// - public string Version { get; private set; } - } -} + /// + /// Version string. + /// + public string Version { get; private set; } +} \ No newline at end of file diff --git a/src/LightningDB/MDBResultCode.cs b/src/LightningDB/MDBResultCode.cs index 6cb35ee..f277dbd 100644 --- a/src/LightningDB/MDBResultCode.cs +++ b/src/LightningDB/MDBResultCode.cs @@ -1,126 +1,125 @@ -namespace LightningDB +namespace LightningDB; + +public enum MDBResultCode { - public enum MDBResultCode : int - { - /// - /// Successful result - /// - Success = 0, - /// - /// key/data pair already exists - /// - KeyExist = -30799, - /// - /// key/data pair not found (EOF) - /// - NotFound = -30798, - /// - /// Requested page not found - this usually indicates corruption - /// - PageNotFound = -30797, - /// - /// Located page was wrong type - /// - Corrupted = -30796, - /// - /// Update of meta page failed or environment had fatal error - /// - Panic = -30795, - /// - /// Environment version mismatch - /// - VersionMismatch = -30794, - /// - /// File is not a valid LMDB file - /// - Invalid = -30793, - /// - /// Environment mapsize reached - /// - MapFull = -30792, - /// - /// Environment maxdbs reached - /// - DbsFull = -30791, - /// - /// Environment maxreaders reached - /// - ReadersFull = -30790, - /// - /// Too many TLS keys in use - Windows only - /// - TLSFull = -30789, - /// - /// Txn has too many dirty pages - /// - TxnFull = -30788, - /// - /// Cursor stack too deep - internal error - /// - CursorFull = -30787, - /// - /// Page has not enough space - internal error - /// - PageFull = -30786, - /// - /// Database contents grew beyond environment mapsize - /// - MapResized = -30785, - /// - /// Operation and DB incompatible, or DB type changed. This can mean: - /// - The operation expects an #MDB_DUPSORT / #MDB_DUPFIXED database. - /// - Opening a named DB when the unnamed DB has #MDB_DUPSORT / #MDB_INTEGERKEY. - /// - Accessing a data record as a database, or vice versa. - /// - The database was dropped and recreated with different flags. - /// - Incompatible = -30784, - /// - /// Invalid reuse of reader locktable slot - /// - BadRSlot = -30783, - /// - /// Transaction must abort, has a child, or is invalid - /// - BadTxn = -30782, - /// - /// Unsupported size of key/DB name/data, or wrong DUPFIXED size - /// - BadValSize = -30781, - /// - /// The specified DBI was changed unexpectedly - /// - BadDBI = -30780, - /// - /// Unexpected problem - txn should abort - /// - Problem = -30779, - /// - /// ENOENT error from C-runtime - /// - FileNotFound = 2, - /// - /// EIO error from C-runtime - /// - AccessDenied = 5, - /// - /// ENOMEM error from C-runtime - /// - InvalidAccess = 12, - /// - /// EACCES error from C-runtime - /// - InvalidData = 13, - /// - /// EBUSY error from C-runtime - /// - CurrentDirectory = 16, - /// - /// EINVAL error from C-runtime - /// - BadCommand = 22, - /// - /// ENOSPC error from C-runtime - /// - OutOfPaper = 28 - } + /// + /// Successful result + /// + Success = 0, + /// + /// key/data pair already exists + /// + KeyExist = -30799, + /// + /// key/data pair not found (EOF) + /// + NotFound = -30798, + /// + /// Requested page not found - this usually indicates corruption + /// + PageNotFound = -30797, + /// + /// Located page was wrong type + /// + Corrupted = -30796, + /// + /// Update of meta page failed or environment had fatal error + /// + Panic = -30795, + /// + /// Environment version mismatch + /// + VersionMismatch = -30794, + /// + /// File is not a valid LMDB file + /// + Invalid = -30793, + /// + /// Environment mapsize reached + /// + MapFull = -30792, + /// + /// Environment maxdbs reached + /// + DbsFull = -30791, + /// + /// Environment maxreaders reached + /// + ReadersFull = -30790, + /// + /// Too many TLS keys in use - Windows only + /// + TLSFull = -30789, + /// + /// Txn has too many dirty pages + /// + TxnFull = -30788, + /// + /// Cursor stack too deep - internal error + /// + CursorFull = -30787, + /// + /// Page has not enough space - internal error + /// + PageFull = -30786, + /// + /// Database contents grew beyond environment mapsize + /// + MapResized = -30785, + /// + /// Operation and DB incompatible, or DB type changed. This can mean: + /// - The operation expects an #MDB_DUPSORT / #MDB_DUPFIXED database. + /// - Opening a named DB when the unnamed DB has #MDB_DUPSORT / #MDB_INTEGERKEY. + /// - Accessing a data record as a database, or vice versa. + /// - The database was dropped and recreated with different flags. + /// + Incompatible = -30784, + /// + /// Invalid reuse of reader locktable slot + /// + BadRSlot = -30783, + /// + /// Transaction must abort, has a child, or is invalid + /// + BadTxn = -30782, + /// + /// Unsupported size of key/DB name/data, or wrong DUPFIXED size + /// + BadValSize = -30781, + /// + /// The specified DBI was changed unexpectedly + /// + BadDBI = -30780, + /// + /// Unexpected problem - txn should abort + /// + Problem = -30779, + /// + /// ENOENT error from C-runtime + /// + FileNotFound = 2, + /// + /// EIO error from C-runtime + /// + AccessDenied = 5, + /// + /// ENOMEM error from C-runtime + /// + InvalidAccess = 12, + /// + /// EACCES error from C-runtime + /// + InvalidData = 13, + /// + /// EBUSY error from C-runtime + /// + CurrentDirectory = 16, + /// + /// EINVAL error from C-runtime + /// + BadCommand = 22, + /// + /// ENOSPC error from C-runtime + /// + OutOfPaper = 28 } \ No newline at end of file diff --git a/src/LightningDB/MDBValue.cs b/src/LightningDB/MDBValue.cs index 70bc243..0440eac 100644 --- a/src/LightningDB/MDBValue.cs +++ b/src/LightningDB/MDBValue.cs @@ -1,51 +1,50 @@ using System; -namespace LightningDB +namespace LightningDB; + +/// +/// A managed version of the native MDB_val type +/// +/// +/// For Performance and Correctness, the layout of this struct must not be changed. +/// This struct is blittable and is marshalled directly to Native code via +/// P/Invoke. +/// +public unsafe struct MDBValue { - /// - /// A managed version of the native MDB_val type - /// /// - /// For Performance and Correctness, the layout of this struct must not be changed. - /// This struct is blittable and is marshalled directly to Native code via - /// P/Invoke. + /// We only expose this shape constructor to basically force you to use + /// a fixed statement to obtain the pointer. If we accepted a Span or + /// ReadOnlySpan here, we would have to do scarier things to pin/unpin + /// the buffer. Since this library is geared towards safe and easy usage, + /// this way somewhat forces you onto the correct path. + /// /// - public unsafe struct MDBValue + /// The length of the buffer + /// A pointer to a buffer. + /// The underlying memory may be managed(an array), unmanaged or stack-allocated. + /// If it is managed, it **MUST** be pinned via either GCHandle.Alloc or a fixed statement + /// + internal MDBValue(int bufferSize, byte* pinnedOrStackAllocBuffer) { - /// - /// We only expose this shape constructor to basically force you to use - /// a fixed statement to obtain the pointer. If we accepted a Span or - /// ReadOnlySpan here, we would have to do scarier things to pin/unpin - /// the buffer. Since this library is geared towards safe and easy usage, - /// this way somewhat forces you onto the correct path. - /// - /// - /// The length of the buffer - /// A pointer to a buffer. - /// The underlying memory may be managed(an array), unmanaged or stack-allocated. - /// If it is managed, it **MUST** be pinned via either GCHandle.Alloc or a fixed statement - /// - internal MDBValue(int bufferSize, byte* pinnedOrStackAllocBuffer) - { - this.size = (IntPtr)bufferSize; - this.data = pinnedOrStackAllocBuffer; - } - //DO NOT REORDER - internal IntPtr size; + size = bufferSize; + data = pinnedOrStackAllocBuffer; + } + //DO NOT REORDER + internal nint size; - //DO NOT REORDER - internal byte* data; + //DO NOT REORDER + internal byte* data; - /// - /// Gets a span representation of the buffer - /// - public ReadOnlySpan AsSpan() => new ReadOnlySpan(data, checked((int)size)); + /// + /// Gets a span representation of the buffer + /// + public ReadOnlySpan AsSpan() => new (data, (int)size); - /// - /// Copies the data of the buffer to a new array - /// - /// A newly allocated array containing data copied from the de-referenced data pointer - /// Equivalent to AsSpan().ToArray() but makes intent a little more clear - public byte[] CopyToNewArray() => AsSpan().ToArray(); - } -} + /// + /// Copies the data of the buffer to a new array + /// + /// A newly allocated array containing data copied from the de-referenced data pointer + /// Equivalent to AsSpan().ToArray() but makes intent a little more clear + public byte[] CopyToNewArray() => AsSpan().ToArray(); +} \ No newline at end of file diff --git a/src/LightningDB/Native/CompareFunction.cs b/src/LightningDB/Native/CompareFunction.cs index b23f507..74b0dea 100644 --- a/src/LightningDB/Native/CompareFunction.cs +++ b/src/LightningDB/Native/CompareFunction.cs @@ -1,7 +1,6 @@ using System.Runtime.InteropServices; -namespace LightningDB.Native -{ - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate int CompareFunction(ref MDBValue left, ref MDBValue right); -} +namespace LightningDB.Native; + +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public delegate int CompareFunction(ref MDBValue left, ref MDBValue right); \ No newline at end of file diff --git a/src/LightningDB/Native/Lmdb.cs b/src/LightningDB/Native/Lmdb.cs index 4348d5b..660fc89 100644 --- a/src/LightningDB/Native/Lmdb.cs +++ b/src/LightningDB/Native/Lmdb.cs @@ -1,198 +1,342 @@ using System; using System.Runtime.InteropServices; -namespace LightningDB.Native +namespace LightningDB.Native; + +#if NET7_0_OR_GREATER +using System.Runtime.CompilerServices; +public static partial class Lmdb { - public static class Lmdb + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_create(out nint env); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial void mdb_env_close(nint env); + + [LibraryImport(MDB_DLL_NAME, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_open(nint env, string path, EnvironmentOpenFlags flags, UnixAccessMode mode); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_set_mapsize(nint env, nint size); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_get_maxreaders(nint env, out uint readers); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_set_maxreaders(nint env, uint readers); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_set_maxdbs(nint env, uint dbs); + + [LibraryImport(MDB_DLL_NAME, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_dbi_open(nint txn, string name, DatabaseOpenFlags flags, out uint db); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial void mdb_dbi_close(nint env, uint dbi); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_drop(nint txn, uint dbi, [MarshalAs(UnmanagedType.I1)] bool del); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_txn_begin(nint env, nint parent, TransactionBeginFlags flags, out nint txn); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_txn_commit(nint txn); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial void mdb_txn_abort(nint txn); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial void mdb_txn_reset(nint txn); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_txn_renew(nint txn); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial nint mdb_version(out int major, out int minor, out int patch); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial nint mdb_strerror(int err); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_stat(nint txn, uint dbi, out MDBStat stat); + + [LibraryImport(MDB_DLL_NAME, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_copy(nint env, string path); + + [LibraryImport(MDB_DLL_NAME, StringMarshalling = StringMarshalling.Utf8)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_copy2(nint env, string path, EnvironmentCopyFlags copyFlags); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_info(nint env, out MDBEnvInfo stat); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_stat(nint env, out MDBStat stat); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_env_sync(nint env, [MarshalAs(UnmanagedType.I1)] bool force); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_get(nint txn, uint dbi, ref MDBValue key, out MDBValue data); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_put(nint txn, uint dbi, ref MDBValue key, ref MDBValue data, PutOptions flags); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_del(nint txn, uint dbi, ref MDBValue key, ref MDBValue data); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_del(nint txn, uint dbi, ref MDBValue key, nint data); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_cursor_open(nint txn, uint dbi, out nint cursor); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial void mdb_cursor_close(nint cursor); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_cursor_renew(nint txn, nint cursor); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_cursor_get(nint cursor, ref MDBValue key, ref MDBValue data, CursorOperation op); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_cursor_put(nint cursor, ref MDBValue key, ref MDBValue mdbValue, CursorPutOptions flags); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_cursor_del(nint cursor, CursorDeleteOption flags); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_set_compare(nint txn, uint dbi, CompareFunction cmp); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_set_dupsort(nint txn, uint dbi, CompareFunction cmp); + + [LibraryImport(MDB_DLL_NAME)] + [UnmanagedCallConv(CallConvs = new []{typeof(CallConvCdecl)})] + public static partial MDBResultCode mdb_cursor_put(nint cursor, ref MDBValue key, MDBValue[] value, CursorPutOptions flags); +} +#endif + +public static partial class Lmdb +{ + private const string MDB_DLL_NAME = "lmdb"; + /// + /// Duplicate keys may be used in the database. (Or, from another perspective, keys may have multiple data items, stored in sorted order.) By default keys must be unique and may have only a single data item. + /// + public const int MDB_DUPSORT = 0x04; + + /// + /// This flag may only be used in combination with MDB_DUPSORT. This option tells the library that the data items for this database are all the same size, which allows further optimizations in storage and retrieval. When all data items are the same size, the MDB_GET_MULTIPLE and MDB_NEXT_MULTIPLE cursor operations may be used to retrieve multiple items at once. + /// + public const int MDB_DUPFIXED = 0x10; + + public static MDBResultCode mdb_env_set_mapsize(nint env, long size) { - private const string MDB_DLL_NAME = "lmdb"; - /// - /// Duplicate keys may be used in the database. (Or, from another perspective, keys may have multiple data items, stored in sorted order.) By default keys must be unique and may have only a single data item. - /// - public const int MDB_DUPSORT = 0x04; - - /// - /// This flag may only be used in combination with MDB_DUPSORT. This option tells the library that the data items for this database are all the same size, which allows further optimizations in storage and retrieval. When all data items are the same size, the MDB_GET_MULTIPLE and MDB_NEXT_MULTIPLE cursor operations may be used to retrieve multiple items at once. - /// - public const int MDB_DUPFIXED = 0x10; - - public static MDBResultCode mdb_env_set_mapsize(IntPtr env, long size) - { - return mdb_env_set_mapsize(env, new IntPtr(size)); - } + return mdb_env_set_mapsize(env, (nint)size); + } - public static MDBResultCode mdb_put(IntPtr txn, uint dbi, MDBValue key, MDBValue value, PutOptions flags) - { - return mdb_put(txn, dbi, ref key, ref value, flags); - } + public static MDBResultCode mdb_put(nint txn, uint dbi, MDBValue key, MDBValue value, PutOptions flags) + { + return mdb_put(txn, dbi, ref key, ref value, flags); + } - public static MDBResultCode mdb_del(IntPtr txn, uint dbi, MDBValue key, MDBValue value) - { - return mdb_del(txn, dbi, ref key, ref value); - } + public static MDBResultCode mdb_del(nint txn, uint dbi, MDBValue key, MDBValue value) + { + return mdb_del(txn, dbi, ref key, ref value); + } - public static MDBResultCode mdb_del(IntPtr txn, uint dbi, MDBValue key) - { - return mdb_del(txn, dbi, ref key, IntPtr.Zero); - } + public static MDBResultCode mdb_del(nint txn, uint dbi, MDBValue key) + { + return mdb_del(txn, dbi, ref key,0); + } - public static MDBResultCode mdb_cursor_put(IntPtr cursor, MDBValue key, MDBValue value, CursorPutOptions flags) - { - return mdb_cursor_put(cursor, ref key, ref value, flags); - } + public static MDBResultCode mdb_cursor_put(nint cursor, MDBValue key, MDBValue value, CursorPutOptions flags) + { + return mdb_cursor_put(cursor, ref key, ref value, flags); + } - /// - /// store multiple contiguous data elements in a single request. - /// May only be used with MDB_DUPFIXED. - /// - /// Pointer to cursor - /// key - /// This span must be pinned or stackalloc memory - /// - public static MDBResultCode mdb_cursor_put(IntPtr cursor, ref MDBValue key, ref Span data, - CursorPutOptions flags) - { - ref var dataRef = ref MemoryMarshal.GetReference(data); - return mdb_cursor_put(cursor, ref key, ref dataRef, flags); - } + /// + /// store multiple contiguous data elements in a single request. + /// May only be used with MDB_DUPFIXED. + /// + /// Pointer to cursor + /// key + /// This span must be pinned or stackalloc memory + /// + public static MDBResultCode mdb_cursor_put(nint cursor, ref MDBValue key, ref Span data, + CursorPutOptions flags) + { + ref var dataRef = ref MemoryMarshal.GetReference(data); + return mdb_cursor_put(cursor, ref key, ref dataRef, flags); + } +#if !NET7_0_OR_GREATER [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_create(out IntPtr env); + public static extern MDBResultCode mdb_env_create(out nint env); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern void mdb_env_close(IntPtr env); + public static extern void mdb_env_close(nint env); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - internal static extern MDBResultCode mdb_env_open(IntPtr env, string path, EnvironmentOpenFlags flags, + internal static extern MDBResultCode mdb_env_open(nint env, string path, EnvironmentOpenFlags flags, UnixAccessMode mode); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_set_mapsize(IntPtr env, IntPtr size); + public static extern MDBResultCode mdb_env_set_mapsize(nint env, nint size); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_get_maxreaders(IntPtr env, out uint readers); + public static extern MDBResultCode mdb_env_get_maxreaders(nint env, out uint readers); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_set_maxreaders(IntPtr env, uint readers); + public static extern MDBResultCode mdb_env_set_maxreaders(nint env, uint readers); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_set_maxdbs(IntPtr env, uint dbs); + public static extern MDBResultCode mdb_env_set_maxdbs(nint env, uint dbs); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_dbi_open(IntPtr txn, string name, DatabaseOpenFlags flags, out uint db); + public static extern MDBResultCode mdb_dbi_open(nint txn, string name, DatabaseOpenFlags flags, out uint db); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern void mdb_dbi_close(IntPtr env, uint dbi); + public static extern void mdb_dbi_close(nint env, uint dbi); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_drop(IntPtr txn, uint dbi, bool del); + public static extern MDBResultCode mdb_drop(nint txn, uint dbi, bool del); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_txn_begin(IntPtr env, IntPtr parent, TransactionBeginFlags flags, out IntPtr txn); + public static extern MDBResultCode mdb_txn_begin(nint env, nint parent, TransactionBeginFlags flags, out nint txn); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_txn_commit(IntPtr txn); + public static extern MDBResultCode mdb_txn_commit(nint txn); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern void mdb_txn_abort(IntPtr txn); + public static extern void mdb_txn_abort(nint txn); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern void mdb_txn_reset(IntPtr txn); + public static extern void mdb_txn_reset(nint txn); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_txn_renew(IntPtr txn); + public static extern MDBResultCode mdb_txn_renew(nint txn); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr mdb_version(out int major, out int minor, out int patch); + public static extern nint mdb_version(out int major, out int minor, out int patch); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr mdb_strerror(int err); + public static extern nint mdb_strerror(int err); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_stat(IntPtr txn, uint dbi, out MDBStat stat); + public static extern MDBResultCode mdb_stat(nint txn, uint dbi, out MDBStat stat); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_copy(IntPtr env, string path); + public static extern MDBResultCode mdb_env_copy(nint env, string path); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_copy2(IntPtr env, string path, EnvironmentCopyFlags copyFlags); + public static extern MDBResultCode mdb_env_copy2(nint env, string path, EnvironmentCopyFlags copyFlags); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_info(IntPtr env, out MDBEnvInfo stat); + public static extern MDBResultCode mdb_env_info(nint env, out MDBEnvInfo stat); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_stat(IntPtr env, out MDBStat stat); + public static extern MDBResultCode mdb_env_stat(nint env, out MDBStat stat); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_env_sync(IntPtr env, bool force); + public static extern MDBResultCode mdb_env_sync(nint env, bool force); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_get(IntPtr txn, uint dbi, ref MDBValue key, out MDBValue data); + public static extern MDBResultCode mdb_get(nint txn, uint dbi, ref MDBValue key, out MDBValue data); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_put(IntPtr txn, uint dbi, ref MDBValue key, ref MDBValue data, PutOptions flags); + public static extern MDBResultCode mdb_put(nint txn, uint dbi, ref MDBValue key, ref MDBValue data, PutOptions flags); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_del(IntPtr txn, uint dbi, ref MDBValue key, ref MDBValue data); + public static extern MDBResultCode mdb_del(nint txn, uint dbi, ref MDBValue key, ref MDBValue data); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_del(IntPtr txn, uint dbi, ref MDBValue key, IntPtr data); + public static extern MDBResultCode mdb_del(nint txn, uint dbi, ref MDBValue key, nint data); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_cursor_open(IntPtr txn, uint dbi, out IntPtr cursor); + public static extern MDBResultCode mdb_cursor_open(nint txn, uint dbi, out nint cursor); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern void mdb_cursor_close(IntPtr cursor); + public static extern void mdb_cursor_close(nint cursor); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_cursor_renew(IntPtr txn, IntPtr cursor); + public static extern MDBResultCode mdb_cursor_renew(nint txn, nint cursor); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_cursor_get(IntPtr cursor, ref MDBValue key, ref MDBValue data, CursorOperation op); + public static extern MDBResultCode mdb_cursor_get(nint cursor, ref MDBValue key, ref MDBValue data, CursorOperation op); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_cursor_put(IntPtr cursor, ref MDBValue key, ref MDBValue mdbValue, CursorPutOptions flags); + public static extern MDBResultCode mdb_cursor_put(nint cursor, ref MDBValue key, ref MDBValue mdbValue, CursorPutOptions flags); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_cursor_del(IntPtr cursor, CursorDeleteOption flags); + public static extern MDBResultCode mdb_cursor_del(nint cursor, CursorDeleteOption flags); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_set_compare(IntPtr txn, uint dbi, CompareFunction cmp); + public static extern MDBResultCode mdb_set_compare(nint txn, uint dbi, CompareFunction cmp); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_set_dupsort(IntPtr txn, uint dbi, CompareFunction cmp); + public static extern MDBResultCode mdb_set_dupsort(nint txn, uint dbi, CompareFunction cmp); [DllImport(MDB_DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - public static extern MDBResultCode mdb_cursor_put(IntPtr cursor, ref MDBValue key, MDBValue[] value, CursorPutOptions flags); + public static extern MDBResultCode mdb_cursor_put(nint cursor, ref MDBValue key, MDBValue[] value, CursorPutOptions flags); +#endif #if NETCOREAPP3_1_OR_GREATER - static bool _shouldSetDllImportResolver = true; - static object _syncRoot = new object(); + private static bool _shouldSetDllImportResolver = true; + private static readonly object _syncRoot = new(); - public static void LoadWindowsAutoResizeLibrary() + public static void LoadWindowsAutoResizeLibrary() + { + if (!_shouldSetDllImportResolver) return; + lock (_syncRoot) { - if (_shouldSetDllImportResolver) - { - lock (_syncRoot) - { - if (_shouldSetDllImportResolver) - { - NativeLibrary.SetDllImportResolver(System.Reflection.Assembly.GetExecutingAssembly(), DllImportResolver); - _shouldSetDllImportResolver = false; - } - } - } + if (!_shouldSetDllImportResolver) return; + NativeLibrary.SetDllImportResolver(System.Reflection.Assembly.GetExecutingAssembly(), DllImportResolver); + _shouldSetDllImportResolver = false; } + } - private static IntPtr DllImportResolver(string libraryName, System.Reflection.Assembly assembly, DllImportSearchPath? searchPath) - { - if (libraryName == MDB_DLL_NAME) - { - return NativeLibrary.Load($"{MDB_DLL_NAME}autoresize", assembly, searchPath); - } - return IntPtr.Zero; - } -#endif + private static nint DllImportResolver(string libraryName, System.Reflection.Assembly assembly, DllImportSearchPath? searchPath) + { + return libraryName == MDB_DLL_NAME + ? NativeLibrary.Load($"{MDB_DLL_NAME}autoresize", assembly, searchPath) : 0; } -} +#endif +} \ No newline at end of file diff --git a/src/LightningDB/Native/MDBEnvInfo.cs b/src/LightningDB/Native/MDBEnvInfo.cs index 158a0a3..b41bf2f 100644 --- a/src/LightningDB/Native/MDBEnvInfo.cs +++ b/src/LightningDB/Native/MDBEnvInfo.cs @@ -1,40 +1,37 @@ -using System; +namespace LightningDB.Native; -namespace LightningDB.Native +/// +/// Information about the environment. +/// +public struct MDBEnvInfo { /// - /// Information about the environment. + /// Address of map, if fixed /// - public struct MDBEnvInfo - { - /// - /// Address of map, if fixed - /// - public IntPtr me_mapaddr; + public nint me_mapaddr; - /// - /// Size of the data memory map - /// - public IntPtr me_mapsize; + /// + /// Size of the data memory map + /// + public nint me_mapsize; - /// - /// ID of the last used page - /// - public IntPtr me_last_pgno; + /// + /// ID of the last used page + /// + public nint me_last_pgno; - /// - /// ID of the last committed transaction - /// - public IntPtr me_last_txnid; + /// + /// ID of the last committed transaction + /// + public nint me_last_txnid; - /// - /// max reader slots in the environment - /// - public uint me_maxreaders; + /// + /// max reader slots in the environment + /// + public uint me_maxreaders; - /// - /// max reader slots used in the environment - /// - public uint me_numreaders; - } -} + /// + /// max reader slots used in the environment + /// + public uint me_numreaders; +} \ No newline at end of file diff --git a/src/LightningDB/Native/MDBStat.cs b/src/LightningDB/Native/MDBStat.cs index 1133b84..047ec60 100644 --- a/src/LightningDB/Native/MDBStat.cs +++ b/src/LightningDB/Native/MDBStat.cs @@ -1,40 +1,37 @@ -using System; +namespace LightningDB.Native; -namespace LightningDB.Native +/// +/// Statistics for a database in the environment. +/// +public struct MDBStat { /// - /// Statistics for a database in the environment. + /// Size of a database page. This is currently the same for all databases. /// - public struct MDBStat - { - /// - /// Size of a database page. This is currently the same for all databases. - /// - public uint ms_psize; + public uint ms_psize; - /// - /// Depth (height) of the B-tree - /// - public uint ms_depth; + /// + /// Depth (height) of the B-tree + /// + public uint ms_depth; - /// - /// Number of internal (non-leaf) pages - /// - public IntPtr ms_branch_pages; + /// + /// Number of internal (non-leaf) pages + /// + public nint ms_branch_pages; - /// - /// Number of leaf pages - /// - public IntPtr ms_leaf_pages; + /// + /// Number of leaf pages + /// + public nint ms_leaf_pages; - /// - /// Number of overflow pages - /// - public IntPtr ms_overflow_pages; + /// + /// Number of overflow pages + /// + public nint ms_overflow_pages; - /// - /// Number of data items - /// - public IntPtr ms_entries; - } -} + /// + /// Number of data items + /// + public nint ms_entries; +} \ No newline at end of file diff --git a/src/LightningDB/PutOptions.cs b/src/LightningDB/PutOptions.cs index ea2a4ae..29c4dbe 100644 --- a/src/LightningDB/PutOptions.cs +++ b/src/LightningDB/PutOptions.cs @@ -1,43 +1,42 @@ using System; -namespace LightningDB +namespace LightningDB; + +/// +/// Special options for put operation. +/// +[Flags] +public enum PutOptions { /// - /// Special options for put operation. + /// No special behavior. /// - [Flags] - public enum PutOptions - { - /// - /// No special behavior. - /// - None = 0, + None = 0, - /// - /// Only for MDB_DUPSORT - /// For put: don't write if the key and data pair already exist. - /// For mdb_cursor_del: remove all duplicate data items. - /// - NoDuplicateData = 0x20, + /// + /// Only for MDB_DUPSORT + /// For put: don't write if the key and data pair already exist. + /// For mdb_cursor_del: remove all duplicate data items. + /// + NoDuplicateData = 0x20, - /// - /// For put: Don't write if the key already exists. - /// - NoOverwrite = 0x10, + /// + /// For put: Don't write if the key already exists. + /// + NoOverwrite = 0x10, - /// - /// For put: Just reserve space for data, don't copy it. Return a pointer to the reserved space. - /// - ReserveSpace = 0x10000, + /// + /// For put: Just reserve space for data, don't copy it. Return a pointer to the reserved space. + /// + ReserveSpace = 0x10000, - /// - /// Data is being appended, don't split full pages. - /// - AppendData = 0x20000, + /// + /// Data is being appended, don't split full pages. + /// + AppendData = 0x20000, - /// - /// Duplicate data is being appended, don't split full pages. - /// - AppendDuplicateData = 0x40000 - } -} + /// + /// Duplicate data is being appended, don't split full pages. + /// + AppendDuplicateData = 0x40000 +} \ No newline at end of file diff --git a/src/LightningDB/Stats.cs b/src/LightningDB/Stats.cs index 75330d8..752583a 100644 --- a/src/LightningDB/Stats.cs +++ b/src/LightningDB/Stats.cs @@ -1,38 +1,37 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Statistics for a database in the environment. +/// +public class Stats { /// - /// Statistics for a database in the environment. + /// Size of a database page. This is currently the same for all databases. /// - public class Stats - { - /// - /// Size of a database page. This is currently the same for all databases. - /// - public long PageSize { get; set; } + public long PageSize { get; set; } - /// - /// Depth (height) of the B-tree - /// - public long BTreeDepth { get; set; } + /// + /// Depth (height) of the B-tree + /// + public long BTreeDepth { get; set; } - /// - /// Number of internal (non-leaf) pages - /// - public long BranchPages { get; set; } + /// + /// Number of internal (non-leaf) pages + /// + public long BranchPages { get; set; } - /// - /// Number of leaf pages - /// - public long LeafPages { get; set; } + /// + /// Number of leaf pages + /// + public long LeafPages { get; set; } - /// - /// Number of overflow pages - /// - public long OverflowPages { get; set; } + /// + /// Number of overflow pages + /// + public long OverflowPages { get; set; } - /// - /// Number of data items - /// - public long Entries { get; set; } - } + /// + /// Number of data items + /// + public long Entries { get; set; } } \ No newline at end of file diff --git a/src/LightningDB/TransactionBeginFlags.cs b/src/LightningDB/TransactionBeginFlags.cs index 16f213d..8fdde99 100644 --- a/src/LightningDB/TransactionBeginFlags.cs +++ b/src/LightningDB/TransactionBeginFlags.cs @@ -1,41 +1,40 @@ -namespace LightningDB +namespace LightningDB; + +/// +/// Transaction open mode +/// +public enum TransactionBeginFlags { /// - /// Transaction open mode + /// Normal mode /// - public enum TransactionBeginFlags - { - /// - /// Normal mode - /// - None = 0, + None = 0, - /// - /// MDB_NOSYNC. Don't flush system buffers to disk when committing a transaction. - /// This optimization means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk. - /// The risk is governed by how often the system flushes dirty buffers to disk and how often mdb_env_sync() is called. - /// However, if the filesystem preserves write order and the MDB_WRITEMAP flag is not used, transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D (durability). - /// I.e. database integrity is maintained, but a system crash may undo the final transactions. - /// Note that (MDB_NOSYNC | MDB_WRITEMAP) leaves the system with no hint for when to write transactions to disk, unless mdb_env_sync() is called. - /// (MDB_MAPASYNC | MDB_WRITEMAP) may be preferable. - /// This flag may be changed at any time using mdb_env_set_flags(). - /// - NoSync = 0x10000, + /// + /// MDB_NOSYNC. Don't flush system buffers to disk when committing a transaction. + /// This optimization means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk. + /// The risk is governed by how often the system flushes dirty buffers to disk and how often mdb_env_sync() is called. + /// However, if the filesystem preserves write order and the MDB_WRITEMAP flag is not used, transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D (durability). + /// I.e. database integrity is maintained, but a system crash may undo the final transactions. + /// Note that (MDB_NOSYNC | MDB_WRITEMAP) leaves the system with no hint for when to write transactions to disk, unless mdb_env_sync() is called. + /// (MDB_MAPASYNC | MDB_WRITEMAP) may be preferable. + /// This flag may be changed at any time using mdb_env_set_flags(). + /// + NoSync = 0x10000, - /// - /// MDB_RDONLY. Open the environment in read-only mode. - /// No write operations will be allowed. - /// MDB will still modify the lock file - except on read-only filesystems, where MDB does not use locks. - /// - ReadOnly = 0x20000, + /// + /// MDB_RDONLY. Open the environment in read-only mode. + /// No write operations will be allowed. + /// MDB will still modify the lock file - except on read-only filesystems, where MDB does not use locks. + /// + ReadOnly = 0x20000, - /// - /// MDB_NOMETASYNC. Flush system buffers to disk only once per transaction, omit the metadata flush. - /// Defer that until the system flushes files to disk, or next non-MDB_RDONLY commit or mdb_env_sync(). - /// This optimization maintains database integrity, but a system crash may undo the last committed transaction. - /// I.e. it preserves the ACI (atomicity, consistency, isolation) but not D (durability) database property. - /// This flag may be changed at any time using mdb_env_set_flags(). - /// - NoMetaSync = 0x40000, - } -} + /// + /// MDB_NOMETASYNC. Flush system buffers to disk only once per transaction, omit the metadata flush. + /// Defer that until the system flushes files to disk, or next non-MDB_RDONLY commit or mdb_env_sync(). + /// This optimization maintains database integrity, but a system crash may undo the last committed transaction. + /// I.e. it preserves the ACI (atomicity, consistency, isolation) but not D (durability) database property. + /// This flag may be changed at any time using mdb_env_set_flags(). + /// + NoMetaSync = 0x40000 +} \ No newline at end of file diff --git a/src/LightningDB/UnixAccessMode.cs b/src/LightningDB/UnixAccessMode.cs index 11c25c2..06da003 100644 --- a/src/LightningDB/UnixAccessMode.cs +++ b/src/LightningDB/UnixAccessMode.cs @@ -1,61 +1,60 @@ using System; -namespace LightningDB +namespace LightningDB; + +/// +/// Unix file access privileges +/// +[Flags] +public enum UnixAccessMode : uint { /// - /// Unix file access privileges - /// - [Flags] - public enum UnixAccessMode : uint - { - /// - /// S_IRUSR - /// - OwnerRead = 0x0100, - - /// - /// S_IWUSR - /// - OwnerWrite = 0x0080, - - /// - /// S_IXUSR - /// - OwnerExec = 0x0040, - - /// - /// S_IRGRP - /// - GroupRead = 0x0020, - - /// - /// S_IWGRP - /// - GroupWrite = 0x0010, - - /// - /// S_IXGRP - /// - GroupExec = 0x0008, - - /// - /// S_IROTH - /// - OtherRead = 0x0004, - - /// - /// S_IWOTH - /// - OtherWrite = 0x0002, - - /// - /// S_IXOTH - /// - OtherExec = 0x0001, - - /// - /// Owner, Group, Other Read/Write - /// - Default = OwnerRead | OwnerWrite | GroupRead | GroupWrite | OtherRead | OtherWrite - } -} + /// S_IRUSR + /// + OwnerRead = 0x0100, + + /// + /// S_IWUSR + /// + OwnerWrite = 0x0080, + + /// + /// S_IXUSR + /// + OwnerExec = 0x0040, + + /// + /// S_IRGRP + /// + GroupRead = 0x0020, + + /// + /// S_IWGRP + /// + GroupWrite = 0x0010, + + /// + /// S_IXGRP + /// + GroupExec = 0x0008, + + /// + /// S_IROTH + /// + OtherRead = 0x0004, + + /// + /// S_IWOTH + /// + OtherWrite = 0x0002, + + /// + /// S_IXOTH + /// + OtherExec = 0x0001, + + /// + /// Owner, Group, Other Read/Write + /// + Default = OwnerRead | OwnerWrite | GroupRead | GroupWrite | OtherRead | OtherWrite +} \ No newline at end of file diff --git a/src/SecondProcess/Program.cs b/src/SecondProcess/Program.cs index 6c91726..ae173cd 100644 --- a/src/SecondProcess/Program.cs +++ b/src/SecondProcess/Program.cs @@ -3,25 +3,24 @@ using System.Text; using LightningDB; -namespace SecondProcess +namespace SecondProcess; + +class Program { - class Program + static void Main(string[] args) { - static void Main(string[] args) + var name = args.First(); + using var env = new LightningEnvironment(name); + env.Open(EnvironmentOpenFlags.ReadOnly); + byte[] results; + using (var tx = env.BeginTransaction(TransactionBeginFlags.ReadOnly)) { - var name = args.First(); - using var env = new LightningEnvironment(name); - env.Open(EnvironmentOpenFlags.ReadOnly); - byte[] results; - using (var tx = env.BeginTransaction(TransactionBeginFlags.ReadOnly)) - { - using var db = tx.OpenDatabase(); - var result = tx.Get(db, Encoding.UTF8.GetBytes("hello")); - results = result.value.CopyToNewArray(); - tx.Commit(); - } - - Console.WriteLine(Encoding.UTF8.GetString(results)); + using var db = tx.OpenDatabase(); + var result = tx.Get(db, "hello"u8.ToArray()); + results = result.value.CopyToNewArray(); + tx.Commit(); } + + Console.WriteLine(Encoding.UTF8.GetString(results)); } } \ No newline at end of file diff --git a/src/SecondProcess/SecondProcess.csproj b/src/SecondProcess/SecondProcess.csproj index 7856906..24a60d9 100644 --- a/src/SecondProcess/SecondProcess.csproj +++ b/src/SecondProcess/SecondProcess.csproj @@ -3,6 +3,7 @@ Exe net6.0;net7.0 + 11 .\