diff --git a/src/Neo/Persistence/IReadOnlyStore.cs b/src/Neo/Persistence/IReadOnlyStore.cs index 4b6c3fe0ec..e52ccbb7c3 100644 --- a/src/Neo/Persistence/IReadOnlyStore.cs +++ b/src/Neo/Persistence/IReadOnlyStore.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using System; using System.Collections.Generic; namespace Neo.Persistence @@ -31,8 +32,17 @@ public interface IReadOnlyStore /// /// The key of the entry. /// The data of the entry. Or if it doesn't exist. + /// . Obsolete it later for avoiding complier warning. byte[] TryGet(byte[] key); + /// + /// Reads a specified entry from the database. + /// + /// The key of the entry. + /// The data of the entry. + /// if the entry exists; otherwise, . + bool TryGet(byte[] key, out byte[] value); + /// /// Determines whether the database contains the specified entry. /// diff --git a/src/Neo/Persistence/MemorySnapshot.cs b/src/Neo/Persistence/MemorySnapshot.cs index 096431552c..b3d06798d4 100644 --- a/src/Neo/Persistence/MemorySnapshot.cs +++ b/src/Neo/Persistence/MemorySnapshot.cs @@ -69,6 +69,11 @@ public byte[] TryGet(byte[] key) return value?[..]; } + public bool TryGet(byte[] key, out byte[] value) + { + return immutableData.TryGetValue(key, out value); + } + public bool Contains(byte[] key) { return immutableData.ContainsKey(key); diff --git a/src/Neo/Persistence/MemoryStore.cs b/src/Neo/Persistence/MemoryStore.cs index 191b89ea4f..378d5374f1 100644 --- a/src/Neo/Persistence/MemoryStore.cs +++ b/src/Neo/Persistence/MemoryStore.cs @@ -66,6 +66,12 @@ public byte[] TryGet(byte[] key) return value[..]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGet(byte[] key, out byte[] value) + { + return _innerData.TryGetValue(key, out value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(byte[] key) { diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index 0b0a63b885..f66164285b 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -65,5 +65,11 @@ public byte[] TryGet(byte[] key) { return db.Get(options, key); } + + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(options, key); + return value != null; + } } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs index 27b12a8b64..175b71a8fe 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -63,5 +63,11 @@ public byte[] TryGet(byte[] key) { return db.Get(ReadOptions.Default, key); } + + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(ReadOptions.Default, key); + return value != null; + } } } diff --git a/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs index 7423f6ae4a..4da29e41fe 100644 --- a/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/RocksDBStore/Plugins/Storage/Snapshot.cs @@ -73,6 +73,12 @@ public byte[] TryGet(byte[] key) return db.Get(key, readOptions: options); } + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(key, readOptions: options); + return value != null; + } + public void Dispose() { snapshot.Dispose(); diff --git a/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs b/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs index ebf160dab0..3f8121ca07 100644 --- a/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/RocksDBStore/Plugins/Storage/Store.cs @@ -59,6 +59,12 @@ public byte[] TryGet(byte[] key) return db.Get(key); } + public bool TryGet(byte[] key, out byte[] value) + { + value = db.Get(key); + return value != null; + } + public void Delete(byte[] key) { db.Remove(key); diff --git a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs index 2e53456545..d7fcb92775 100644 --- a/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs +++ b/tests/Neo.Cryptography.MPTTrie.Tests/Cryptography/MPTTrie/UT_Trie.cs @@ -52,6 +52,11 @@ public byte[] TryGet(byte[] key) return null; } + public bool TryGet(byte[] key, out byte[] value) + { + return store.TryGetValue(StoreKey(key), out value); + } + public void Dispose() { throw new System.NotImplementedException(); } public int Size => store.Count; diff --git a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs index 189e212bc2..ed44faab53 100644 --- a/tests/Neo.Plugins.Storage.Tests/StoreTest.cs +++ b/tests/Neo.Plugins.Storage.Tests/StoreTest.cs @@ -85,6 +85,8 @@ public void TestLevelDbSnapshot() snapshot.Put(testKey, testValue); // Data saved to the leveldb snapshot shall not be visible to the store Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out var got)); + Assert.IsNull(got); // Value is in the write batch, not visible to the store and snapshot Assert.AreEqual(false, snapshot.Contains(testKey)); @@ -94,7 +96,13 @@ public void TestLevelDbSnapshot() // After commit, the data shall be visible to the store but not to the snapshot Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out got)); + Assert.IsNull(got); + CollectionAssert.AreEqual(testValue, store.TryGet(testKey)); + Assert.IsTrue(store.TryGet(testKey, out got)); + CollectionAssert.AreEqual(testValue, got); + Assert.AreEqual(false, snapshot.Contains(testKey)); Assert.AreEqual(true, store.Contains(testKey)); @@ -154,7 +162,12 @@ public void TestRocksDbSnapshot() snapshot.Put(testKey, testValue); // Data saved to the leveldb snapshot shall not be visible Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out var got)); + Assert.IsNull(got); + Assert.IsNull(store.TryGet(testKey)); + Assert.IsFalse(store.TryGet(testKey, out got)); + Assert.IsNull(got); // Value is in the write batch, not visible to the store and snapshot Assert.AreEqual(false, snapshot.Contains(testKey)); @@ -164,7 +177,13 @@ public void TestRocksDbSnapshot() // After commit, the data shall be visible to the store but not to the snapshot Assert.IsNull(snapshot.TryGet(testKey)); + Assert.IsFalse(snapshot.TryGet(testKey, out got)); + Assert.IsNull(got); + CollectionAssert.AreEqual(testValue, store.TryGet(testKey)); + Assert.IsTrue(store.TryGet(testKey, out got)); + CollectionAssert.AreEqual(testValue, got); + Assert.AreEqual(false, snapshot.Contains(testKey)); Assert.AreEqual(true, store.Contains(testKey)); diff --git a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs index 629e2bc374..8eea33c9a7 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemorySnapshot.cs @@ -97,6 +97,14 @@ public void MultiSnapshotTest() Assert.IsNull(_snapshot.TryGet(key1)); CollectionAssert.AreEqual(value1, snapshot2.TryGet(key1)); + Assert.IsFalse(_snapshot.TryGet(key1, out var value2)); + + Assert.IsTrue(snapshot2.TryGet(key1, out value2)); + CollectionAssert.AreEqual(value1, value2); + + Assert.IsTrue(_memoryStore.TryGet(key1, out value2)); + CollectionAssert.AreEqual(value1, value2); + _snapshot.Delete(key1); // Deleted value can not being found from the snapshot but can still get from the store and snapshot2 diff --git a/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs index 133d9eb66c..4473ae63a8 100644 --- a/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs +++ b/tests/Neo.UnitTests/Persistence/UT_MemoryStore.cs @@ -51,6 +51,9 @@ public void StoreTest() store.Delete([1]); Assert.AreEqual(null, store.TryGet([1])); + Assert.IsFalse(store.TryGet([1], out var got)); + Assert.AreEqual(null, got); + store.Put([1], [1, 2, 3]); CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, store.TryGet([1])); @@ -79,13 +82,18 @@ public void NeoSystemStoreViewTest() var store = _neoSystem.StoreView; var key = new StorageKey(Encoding.UTF8.GetBytes("testKey")); var value = new StorageItem(Encoding.UTF8.GetBytes("testValue")); + store.Add(key, value); store.Commit(); + var result = store.TryGet(key); // The StoreView is a readonly view of the store, here it will have value in the cache Assert.AreEqual("testValue", Encoding.UTF8.GetString(result.Value.ToArray())); - // But the value will not be written to the underlying store even its committed. + + // But the value will not be written to the underlying store even its committed. Assert.IsNull(_memoryStore.TryGet(key.ToArray())); + Assert.IsFalse(_memoryStore.TryGet(key.ToArray(), out var got)); + Assert.AreEqual(null, got); } [TestMethod]