From f826dc49dfb3fd4246c358017be391637c264f31 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Thu, 16 Jul 2020 09:56:16 +0800 Subject: [PATCH 1/3] add db.seek ut --- tests/Neo.Plugins.Storage.Tests/StoreTest.cs | 30 +++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs index ee83c8a42..555603738 100644 --- a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs +++ b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs @@ -43,7 +43,7 @@ public void TestRocksDb() } /// - /// Test Put/Delete/TryGet + /// Test Put/Delete/TryGet/Seek /// /// Store private void TestStorage(IStore store) @@ -64,6 +64,34 @@ private void TestStorage(IStore store) ret = store.TryGet(0, new byte[] { 0x01, 0x02 }); Assert.IsNull(ret); + + // Test seek + + store.Put(1, new byte[] { 0x00, 0x00, 0x00 }, new byte[] { 0x00 }); + store.Put(1, new byte[] { 0x00, 0x00, 0x01 }, new byte[] { 0x01 }); + store.Put(1, new byte[] { 0x00, 0x00, 0x02 }, new byte[] { 0x02 }); + store.Put(1, new byte[] { 0x00, 0x00, 0x03 }, new byte[] { 0x03 }); + store.Put(1, new byte[] { 0x00, 0x00, 0x04 }, new byte[] { 0x04 }); + + // Seek Forward + + var enumerator = store.Seek(1, new byte[] { 0x00, 0x00, 0x02 }, IO.Caching.SeekDirection.Forward).GetEnumerator(); + Assert.IsTrue(enumerator.MoveNext()); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x02 }, enumerator.Current.Key); + CollectionAssert.AreEqual(new byte[] { 0x02 }, enumerator.Current.Value); + Assert.IsTrue(enumerator.MoveNext()); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x03 }, enumerator.Current.Key); + CollectionAssert.AreEqual(new byte[] { 0x03 }, enumerator.Current.Value); + + // Seek Backward + + enumerator = store.Seek(1, new byte[] { 0x00, 0x00, 0x02 }, IO.Caching.SeekDirection.Backward).GetEnumerator(); + Assert.IsTrue(enumerator.MoveNext()); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x02 }, enumerator.Current.Key); + CollectionAssert.AreEqual(new byte[] { 0x02 }, enumerator.Current.Value); + Assert.IsTrue(enumerator.MoveNext()); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x01 }, enumerator.Current.Key); + CollectionAssert.AreEqual(new byte[] { 0x01 }, enumerator.Current.Value); } } From 82cc20b9f5539011db09101c26ad981a85fdcfe3 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Thu, 16 Jul 2020 11:01:00 +0800 Subject: [PATCH 2/3] fix db.seek --- src/LevelDBStore/IO/Data/LevelDB/Helper.cs | 41 ++++++++++++++++---- src/RocksDBStore/Plugins/Storage/Snapshot.cs | 14 +++---- src/RocksDBStore/Plugins/Storage/Store.cs | 17 ++++---- tests/Neo.Plugins.Storage.Tests/StoreTest.cs | 15 +++++++ 4 files changed, 62 insertions(+), 25 deletions(-) diff --git a/src/LevelDBStore/IO/Data/LevelDB/Helper.cs b/src/LevelDBStore/IO/Data/LevelDB/Helper.cs index 0ed2f9543..ad161c362 100644 --- a/src/LevelDBStore/IO/Data/LevelDB/Helper.cs +++ b/src/LevelDBStore/IO/Data/LevelDB/Helper.cs @@ -7,19 +7,46 @@ namespace Neo.IO.Data.LevelDB { public static class Helper { + private static int ByteArrayCompare(byte[] x, byte[] y) + { + int length = Math.Min(x.Length, y.Length); + for (int i = 0; i < length; i++) + { + int r = x[i].CompareTo(y[i]); + if (r != 0) return r; + } + return x.Length.CompareTo(y.Length); + } + public static IEnumerable Seek(this DB db, ReadOptions options, byte table, byte[] prefix, SeekDirection direction, Func resultSelector) { using Iterator it = db.NewIterator(options); - for (it.Seek(CreateKey(table, prefix)); it.Valid();) + byte[] target = CreateKey(table, prefix); + if (direction == SeekDirection.Forward) + { + for (it.Seek(target); it.Valid(); it.Next()) + { + var key = it.Key(); + if (key.Length < 1 || key[0] != table) break; + yield return resultSelector(it.Key(), it.Value()); + } + } + else { - var key = it.Key(); - if (key.Length < 1 || key[0] != table) break; - yield return resultSelector(it.Key(), it.Value()); + // SeekForPrev - if (direction == SeekDirection.Forward) - it.Next(); - else + it.Seek(target); + if (!it.Valid()) + it.SeekToLast(); + else if (ByteArrayCompare(it.Key(), target) > 0) it.Prev(); + + for (; it.Valid(); it.Prev()) + { + var key = it.Key(); + if (key.Length < 1 || key[0] != table) break; + yield return resultSelector(it.Key(), it.Value()); + } } } diff --git a/src/RocksDBStore/Plugins/Storage/Snapshot.cs b/src/RocksDBStore/Plugins/Storage/Snapshot.cs index fba384598..834dbc0a3 100644 --- a/src/RocksDBStore/Plugins/Storage/Snapshot.cs +++ b/src/RocksDBStore/Plugins/Storage/Snapshot.cs @@ -43,15 +43,13 @@ public void Put(byte table, byte[] key, byte[] value) public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte table, byte[] keyOrPrefix, SeekDirection direction) { using var it = db.NewIterator(store.GetFamily(table), options); - for (it.Seek(keyOrPrefix); it.Valid();) - { - yield return (it.Key(), it.Value()); - if (direction == SeekDirection.Forward) - it.Next(); - else - it.Prev(); - } + if (direction == SeekDirection.Forward) + for (it.Seek(keyOrPrefix); it.Valid(); it.Next()) + yield return (it.Key(), it.Value()); + else + for (it.SeekForPrev(keyOrPrefix); it.Valid(); it.Prev()) + yield return (it.Key(), it.Value()); } public byte[] TryGet(byte table, byte[] key) diff --git a/src/RocksDBStore/Plugins/Storage/Store.cs b/src/RocksDBStore/Plugins/Storage/Store.cs index 3cad88249..5fb255c11 100644 --- a/src/RocksDBStore/Plugins/Storage/Store.cs +++ b/src/RocksDBStore/Plugins/Storage/Store.cs @@ -82,18 +82,15 @@ public ISnapshot GetSnapshot() return new Snapshot(this, db); } - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte table, byte[] prefix, SeekDirection direction = SeekDirection.Forward) + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte table, byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) { using var it = db.NewIterator(GetFamily(table), Options.ReadDefault); - for (it.Seek(prefix); it.Valid();) - { - yield return (it.Key(), it.Value()); - - if (direction == SeekDirection.Forward) - it.Next(); - else - it.Prev(); - } + if (direction == SeekDirection.Forward) + for (it.Seek(keyOrPrefix); it.Valid(); it.Next()) + yield return (it.Key(), it.Value()); + else + for (it.SeekForPrev(keyOrPrefix); it.Valid(); it.Prev()) + yield return (it.Key(), it.Value()); } public byte[] TryGet(byte table, byte[] key) diff --git a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs index 555603738..d19290dc6 100644 --- a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs +++ b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Persistence; +using System; namespace Neo.Plugins.Storage.Tests { @@ -92,6 +93,20 @@ private void TestStorage(IStore store) Assert.IsTrue(enumerator.MoveNext()); CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x01 }, enumerator.Current.Key); CollectionAssert.AreEqual(new byte[] { 0x01 }, enumerator.Current.Value); + + // Seek Backward + + store.Put(2, new byte[] { 0x00, 0x00, 0x00 }, new byte[] { 0x00 }); + store.Put(2, new byte[] { 0x00, 0x00, 0x01 }, new byte[] { 0x01 }); + store.Put(2, new byte[] { 0x00, 0x01, 0x02 }, new byte[] { 0x02 }); + + enumerator = store.Seek(2, new byte[] { 0x00, 0x00, 0x03 }, IO.Caching.SeekDirection.Backward).GetEnumerator(); + Assert.IsTrue(enumerator.MoveNext()); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x01 }, enumerator.Current.Key); + CollectionAssert.AreEqual(new byte[] { 0x01 }, enumerator.Current.Value); + Assert.IsTrue(enumerator.MoveNext()); + CollectionAssert.AreEqual(new byte[] { 0x00, 0x00, 0x00 }, enumerator.Current.Key); + CollectionAssert.AreEqual(new byte[] { 0x00 }, enumerator.Current.Value); } } From ce886ed83b3acf93dbc1f093fda87d4736b06960 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Fri, 17 Jul 2020 07:32:17 +0800 Subject: [PATCH 3/3] remove ByteArrayCompare --- src/LevelDBStore/IO/Data/LevelDB/Helper.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/LevelDBStore/IO/Data/LevelDB/Helper.cs b/src/LevelDBStore/IO/Data/LevelDB/Helper.cs index ad161c362..bd28be11c 100644 --- a/src/LevelDBStore/IO/Data/LevelDB/Helper.cs +++ b/src/LevelDBStore/IO/Data/LevelDB/Helper.cs @@ -7,17 +7,6 @@ namespace Neo.IO.Data.LevelDB { public static class Helper { - private static int ByteArrayCompare(byte[] x, byte[] y) - { - int length = Math.Min(x.Length, y.Length); - for (int i = 0; i < length; i++) - { - int r = x[i].CompareTo(y[i]); - if (r != 0) return r; - } - return x.Length.CompareTo(y.Length); - } - public static IEnumerable Seek(this DB db, ReadOptions options, byte table, byte[] prefix, SeekDirection direction, Func resultSelector) { using Iterator it = db.NewIterator(options); @@ -38,7 +27,7 @@ public static IEnumerable Seek(this DB db, ReadOptions options, byte table it.Seek(target); if (!it.Valid()) it.SeekToLast(); - else if (ByteArrayCompare(it.Key(), target) > 0) + else if (it.Key().AsSpan().SequenceCompareTo(target) > 0) it.Prev(); for (; it.Valid(); it.Prev())