diff --git a/src/Plugins/LevelDBStore/Cache.cs b/src/Plugins/LevelDBStore/Cache.cs
new file mode 100644
index 0000000000..45022b44eb
--- /dev/null
+++ b/src/Plugins/LevelDBStore/Cache.cs
@@ -0,0 +1,44 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Cache.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace LevelDB
+{
+ ///
+ /// A Cache is an interface that maps keys to values. It has internal
+ /// synchronization and may be safely accessed concurrently from
+ /// multiple threads. It may automatically evict entries to make room
+ /// for new entries. Values have a specified charge against the cache
+ /// capacity. For example, a cache where the values are variable
+ /// length strings, may use the length of the string as the charge for
+ /// the string.
+ ///
+ /// A builtin cache implementation with a least-recently-used eviction
+ /// policy is provided. Clients may use their own implementations if
+ /// they want something more sophisticated (like scan-resistance, a
+ /// custom eviction policy, variable cache sizing, etc.)
+ ///
+ public class Cache : LevelDBHandle
+ {
+ ///
+ /// Create a new cache with a fixed size capacity. This implementation
+ /// of Cache uses a LRU eviction policy.
+ ///
+ public Cache(int capacity)
+ {
+ Handle = LevelDBInterop.leveldb_cache_create_lru(capacity);
+ }
+
+ protected override void FreeUnManagedObjects()
+ {
+ LevelDBInterop.leveldb_cache_destroy(Handle);
+ }
+ }
+}
diff --git a/src/Plugins/LevelDBStore/Comparator.cs b/src/Plugins/LevelDBStore/Comparator.cs
new file mode 100644
index 0000000000..d03fb550db
--- /dev/null
+++ b/src/Plugins/LevelDBStore/Comparator.cs
@@ -0,0 +1,138 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Comparator.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using LevelDB.NativePointer;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace LevelDB
+{
+ public class Comparator : LevelDBHandle
+ {
+ private sealed class Inner : IDisposable
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void Destructor(IntPtr gCHandleThis);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int Compare(IntPtr gCHandleThisg,
+ IntPtr data1, IntPtr size1,
+ IntPtr data2, IntPtr size2);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate IntPtr Name(IntPtr gCHandleThis);
+
+
+ private static readonly Destructor s_destructor = (gCHandleThis) =>
+ {
+ var h = GCHandle.FromIntPtr(gCHandleThis);
+ var @this = h.Target as Inner;
+
+ @this.Dispose();
+ h.Free();
+ };
+
+ private static readonly Compare s_compare = (gCHandleThis, data1, size1, data2, size2) =>
+ {
+ var @this = GCHandle.FromIntPtr(gCHandleThis).Target as Inner;
+ return @this._cmp(new NativeArray { _baseAddr = data1, _byteLength = size1 },
+ new NativeArray { _baseAddr = data2, _byteLength = size2 });
+ };
+
+ private static readonly Name s_nameAccessor = (gCHandleThis) =>
+ {
+ var @this = GCHandle.FromIntPtr(gCHandleThis).Target as Inner;
+ return @this.NameValue;
+ };
+
+ private Func _cmp;
+ private GCHandle _namePinned;
+
+ public IntPtr Init(string name, Func cmp)
+ {
+ // TODO: Complete member initialization
+ _cmp = cmp;
+
+ _namePinned = GCHandle.Alloc(
+ Encoding.ASCII.GetBytes(name),
+ GCHandleType.Pinned);
+
+ var thisHandle = GCHandle.Alloc(this);
+
+ var chandle = LevelDBInterop.leveldb_comparator_create(
+ GCHandle.ToIntPtr(thisHandle),
+ Marshal.GetFunctionPointerForDelegate(s_destructor),
+ Marshal.GetFunctionPointerForDelegate(s_compare),
+ Marshal.GetFunctionPointerForDelegate(s_nameAccessor)
+ );
+
+ if (chandle == default)
+ thisHandle.Free();
+ return chandle;
+ }
+
+ private unsafe IntPtr NameValue
+ {
+ get
+ {
+ // TODO: this is probably not the most effective way to get a pinned string
+ var s = ((byte[])_namePinned.Target);
+ fixed (byte* p = s)
+ {
+ // Note: pinning the GCHandle ensures this value should remain stable
+ // Note: outside of the 'fixed' block.
+ return (IntPtr)p;
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_namePinned.IsAllocated)
+ _namePinned.Free();
+ }
+ }
+
+ private Comparator(string name, Func cmp)
+ {
+ var inner = new Inner();
+ try
+ {
+ Handle = inner.Init(name, cmp);
+ }
+ finally
+ {
+ if (Handle == default)
+ inner.Dispose();
+ }
+ }
+
+ public static Comparator Create(string name, Func cmp)
+ {
+ return new Comparator(name, cmp);
+ }
+ public static Comparator Create(string name, IComparer cmp)
+ {
+ return new Comparator(name, (a, b) => cmp.Compare(a, b));
+ }
+
+ protected override void FreeUnManagedObjects()
+ {
+ if (Handle != default)
+ {
+ // indirectly invoked CleanupInner
+ LevelDBInterop.leveldb_comparator_destroy(Handle);
+ }
+ }
+ }
+}
diff --git a/src/Plugins/LevelDBStore/CompressionLevel.cs b/src/Plugins/LevelDBStore/CompressionLevel.cs
new file mode 100644
index 0000000000..2dbad51c47
--- /dev/null
+++ b/src/Plugins/LevelDBStore/CompressionLevel.cs
@@ -0,0 +1,25 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// CompressionLevel.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace LevelDB
+{
+ ///
+ /// DB contents are stored in a set of blocks, each of which holds a
+ /// sequence of key,value pairs. Each block may be compressed before
+ /// being stored in a file. The following enum describes which
+ /// compression method (if any) is used to compress a block.
+ ///
+ public enum CompressionLevel
+ {
+ NoCompression = 0,
+ SnappyCompression = 1
+ }
+}
diff --git a/src/Plugins/LevelDBStore/DB.cs b/src/Plugins/LevelDBStore/DB.cs
new file mode 100644
index 0000000000..358440b743
--- /dev/null
+++ b/src/Plugins/LevelDBStore/DB.cs
@@ -0,0 +1,480 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// DB.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using LevelDB.NativePointer;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace LevelDB
+{
+ ///
+ /// A DB is a persistent ordered map from keys to values.
+ /// A DB is safe for concurrent access from multiple threads without any external synchronization.
+ ///
+ public class DB : LevelDBHandle, IEnumerable>, IEnumerable>, IEnumerable>
+ {
+ private static readonly Encoding s_uTF8 = Encoding.UTF8;
+ public readonly static Encoding DefaultEncoding = s_uTF8;
+
+ private readonly Cache _cache;
+ private readonly Comparator _comparator;
+ private readonly Encoding _encoding;
+
+ static void Throw(IntPtr error)
+ {
+ Throw(error, msg => new Exception(msg));
+ }
+
+ static void Throw(IntPtr error, Func exception)
+ {
+ if (error != IntPtr.Zero)
+ {
+ try
+ {
+ var msg = Marshal.PtrToStringAnsi(error);
+ throw exception(msg);
+ }
+ finally
+ {
+ LevelDBInterop.leveldb_free(error);
+ }
+ }
+ }
+
+ public DB(string name, Options options)
+ : this(name, DefaultEncoding, options)
+ {
+ }
+
+ ///
+ /// Open the database with the specified "name".
+ ///
+ public DB(string name, Encoding encoding, Options options)
+ {
+ _cache = options.Cache;
+ _comparator = options.Comparator;
+ Handle = LevelDBInterop.leveldb_open(options.Handle, name, out var error);
+ _encoding = encoding;
+
+ Throw(error, msg => new UnauthorizedAccessException(msg));
+ }
+
+ public void Close()
+ {
+ (this as IDisposable).Dispose();
+ }
+
+ ///
+ /// Set the database entry for "key" to "value".
+ /// Note: consider setting new WriteOptions{ Sync = true }.
+ ///
+ public void Put(string key, string value, WriteOptions options)
+ {
+ Put(_encoding.GetBytes(key), _encoding.GetBytes(value), options);
+ }
+
+ ///
+ /// Set the database entry for "key" to "value".
+ ///
+ public void Put(string key, string value)
+ {
+ Put(key, value, new WriteOptions());
+ }
+
+ ///
+ /// Set the database entry for "key" to "value".
+ ///
+ public void Put(byte[] key, byte[] value)
+ {
+ Put(key, value, new WriteOptions());
+ }
+
+ ///
+ /// Set the database entry for "key" to "value".
+ /// Note: consider setting new WriteOptions{ Sync = true }.
+ ///
+ public void Put(byte[] key, byte[] value, WriteOptions options)
+ {
+ LevelDBInterop.leveldb_put(Handle, options.Handle, key, checked((IntPtr)key.LongLength), value, checked((IntPtr)value.LongLength), out var error);
+ Throw(error);
+ }
+
+ ///
+ /// Set the database entry for "key" to "value".
+ /// Note: consider setting new WriteOptions{ Sync = true }.
+ ///
+ public void Put(int key, int[] value)
+ {
+ Put(key, value, new WriteOptions());
+ }
+
+ ///
+ /// Set the database entry for "key" to "value".
+ /// Note: consider setting new WriteOptions{ Sync = true }.
+ ///
+ public void Put(int key, int[] value, WriteOptions options)
+ {
+ LevelDBInterop.leveldb_put(Handle, options.Handle, ref key, sizeof(int), value, checked((IntPtr)(value.LongLength * 4)), out var error);
+ Throw(error);
+ }
+
+ ///
+ /// Remove the database entry (if any) for "key".
+ /// It is not an error if "key" did not exist in the database.
+ ///
+ public void Delete(string key)
+ {
+ Delete(key, new WriteOptions());
+ }
+
+ ///
+ /// Remove the database entry (if any) for "key".
+ /// It is not an error if "key" did not exist in the database.
+ /// Note: consider setting new WriteOptions{ Sync = true }.
+ ///
+ public void Delete(string key, WriteOptions options)
+ {
+ Delete(_encoding.GetBytes(key), options);
+ }
+
+ ///
+ /// Remove the database entry (if any) for "key".
+ /// It is not an error if "key" did not exist in the database.
+ ///
+ public void Delete(byte[] key)
+ {
+ Delete(key, new WriteOptions());
+ }
+
+ ///
+ /// Remove the database entry (if any) for "key".
+ /// It is not an error if "key" did not exist in the database.
+ /// Note: consider setting new WriteOptions{ Sync = true }.
+ ///
+ public void Delete(byte[] key, WriteOptions options)
+ {
+ LevelDBInterop.leveldb_delete(Handle, options.Handle, key, checked((IntPtr)key.LongLength), out var error);
+ Throw(error);
+ }
+
+ public void Write(WriteBatch batch)
+ {
+ Write(batch, new WriteOptions());
+ }
+
+ public void Write(WriteBatch batch, WriteOptions options)
+ {
+ LevelDBInterop.leveldb_write(Handle, options.Handle, batch.Handle, out var error);
+ Throw(error);
+ }
+
+ ///
+ /// If the database contains an entry for "key" return the value,
+ /// otherwise return null.
+ ///
+ public string Get(string key, ReadOptions options)
+ {
+ var value = Get(_encoding.GetBytes(key), options);
+ if (value != null) return _encoding.GetString(value);
+ return null;
+ }
+
+ ///
+ /// If the database contains an entry for "key" return the value,
+ /// otherwise return null.
+ ///
+ public string Get(string key)
+ {
+ return Get(key, ReadOptions.Default);
+ }
+
+ ///
+ /// If the database contains an entry for "key" return the value,
+ /// otherwise return null.
+ ///
+ public byte[] Get(byte[] key)
+ {
+ return Get(key, ReadOptions.Default);
+ }
+
+ ///
+ /// If the database contains an entry for "key" return the value,
+ /// otherwise return null.
+ ///
+ public byte[] Get(byte[] key, ReadOptions options)
+ {
+ var v = LevelDBInterop.leveldb_get(Handle, options.Handle, key, checked((IntPtr)key.LongLength), out var length, out var error);
+ Throw(error);
+
+ if (v != IntPtr.Zero)
+ {
+ try
+ {
+
+ var len = (long)length;
+ int c = 0, si = 0;
+
+ var bytes = new byte[len];
+
+ while (si < len)
+ {
+ if (len - si > int.MaxValue)
+ c += int.MaxValue;
+ else
+ c += checked((int)(len - si));
+
+ // Method has a ~2GB limit.
+ Marshal.Copy(v, bytes, si, c);
+
+ si += c;
+ }
+ return bytes;
+ }
+ finally
+ {
+ LevelDBInterop.leveldb_free(v);
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// If the database contains an entry for "key" return the value,
+ /// otherwise return null.
+ ///
+ public int[] Get(int key)
+ {
+ return Get(key, ReadOptions.Default);
+ }
+
+ ///
+ /// If the database contains an entry for "key" return the value,
+ /// otherwise return null.
+ ///
+ public int[] Get(int key, ReadOptions options)
+ {
+
+ IntPtr v;
+ v = LevelDBInterop.leveldb_get(Handle, options.Handle, ref key, sizeof(int), out var length, out var error);
+ Throw(error);
+
+ if (v != IntPtr.Zero)
+ {
+ try
+ {
+ var len = (long)length / 4;
+ int c = 0, si = 0;
+
+ var bytes = new int[len];
+
+ while (si < len)
+ {
+ if (len - si > int.MaxValue)
+ c += int.MaxValue;
+ else
+ c += checked((int)(len - si));
+
+ // Method has a ~2GB limit.
+ Marshal.Copy(v, bytes, si, c);
+
+ si += c;
+ }
+ return bytes;
+ }
+ finally
+ {
+ LevelDBInterop.leveldb_free(v);
+ }
+ }
+ return null;
+ }
+
+ public bool Contains(byte[] key, ReadOptions options)
+ {
+ var value = LevelDBInterop.leveldb_get(Handle, options.Handle, key, key.Length, out _, out var error);
+ Throw(error);
+
+ if (value != IntPtr.Zero)
+ {
+ LevelDBInterop.leveldb_free(value);
+ return true;
+ }
+
+ return false;
+ }
+
+ public NativeArray GetRaw(NativeArray key)
+ where T : struct
+ {
+ return GetRaw(key, ReadOptions.Default);
+ }
+ public NativeArray GetRaw(NativeArray key, ReadOptions options)
+ where T : struct
+ {
+
+ var handle = new LevelDbFreeHandle();
+
+ // todo: remove typecast to int
+ var v = (Ptr)LevelDBInterop.leveldb_get(
+ Handle,
+ options.Handle,
+ key._baseAddr,
+ key._byteLength,
+ out var length,
+ out _);
+
+ handle.SetHandle((IntPtr)v);
+
+ // round down, truncating the array slightly if needed
+ var count = (IntPtr)((ulong)length / Ptr.SizeofT);
+
+ return new NativeArray { _baseAddr = v, _count = count, _handle = handle };
+ }
+
+ ///
+ /// Return an iterator over the contents of the database.
+ /// The result of CreateIterator is initially invalid (caller must
+ /// call one of the Seek methods on the iterator before using it).
+ ///
+ public Iterator CreateIterator()
+ {
+ return CreateIterator(ReadOptions.Default);
+ }
+
+ ///
+ /// Return an iterator over the contents of the database.
+ /// The result of CreateIterator is initially invalid (caller must
+ /// call one of the Seek methods on the iterator before using it).
+ ///
+ public Iterator CreateIterator(ReadOptions options)
+ {
+ return new Iterator(LevelDBInterop.leveldb_create_iterator(Handle, options.Handle), _encoding);
+ }
+
+ ///
+ /// Return a handle to the current DB state.
+ /// Iterators and Gets created with this handle will all observe a stable snapshot of the current DB state.
+ ///
+ public SnapShot CreateSnapshot()
+ {
+ return new SnapShot(LevelDBInterop.leveldb_create_snapshot(Handle), this);
+ }
+
+ ///
+ /// DB implementations can export properties about their state
+ /// via this method. If "property" is a valid property understood by this
+ /// DB implementation, fills "*value" with its current value and returns
+ /// true. Otherwise returns false.
+ ///
+ /// Valid property names include:
+ ///
+ /// "leveldb.num-files-at-level" - return the number of files at level ,
+ /// where is an ASCII representation of a level number (e.g. "0").
+ /// "leveldb.stats" - returns a multi-line string that describes statistics
+ /// about the internal operation of the DB.
+ ///
+ public string PropertyValue(string name)
+ {
+ string result = null;
+ var ptr = LevelDBInterop.leveldb_property_value(Handle, name);
+ if (ptr != IntPtr.Zero)
+ {
+ try
+ {
+ return Marshal.PtrToStringAnsi(ptr);
+ }
+ finally
+ {
+ LevelDBInterop.leveldb_free(ptr);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// If a DB cannot be opened, you may attempt to call this method to
+ /// resurrect as much of the contents of the database as possible.
+ /// Some data may be lost, so be careful when calling this function
+ /// on a database that contains important information.
+ ///
+ public static void Repair(Options options, string name)
+ {
+ LevelDBInterop.leveldb_repair_db(options.Handle, name, out var error);
+ Throw(error);
+ }
+
+ ///
+ /// Destroy the contents of the specified database.
+ /// Be very careful using this method.
+ ///
+ public static void Destroy(Options options, string name)
+ {
+ LevelDBInterop.leveldb_destroy_db(options.Handle, name, out var error);
+ Throw(error);
+ }
+
+ protected override void FreeUnManagedObjects()
+ {
+ if (Handle != default)
+ LevelDBInterop.leveldb_close(Handle);
+
+ // it's critical that the database be closed first, as the logger and cache may depend on it.
+
+ _cache?.Dispose();
+
+ _comparator?.Dispose();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ using var sn = CreateSnapshot();
+ using var iterator = CreateIterator(new ReadOptions { Snapshot = sn });
+ iterator.SeekToFirst();
+ while (iterator.IsValid())
+ {
+ yield return new KeyValuePair(iterator.KeyAsString(), iterator.ValueAsString());
+ iterator.Next();
+ }
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ using var sn = CreateSnapshot();
+ using var iterator = CreateIterator(new ReadOptions { Snapshot = sn });
+ iterator.SeekToFirst();
+ while (iterator.IsValid())
+ {
+ yield return new KeyValuePair(iterator.Key(), iterator.Value());
+ iterator.Next();
+ }
+ }
+
+ IEnumerator> IEnumerable>.GetEnumerator()
+ {
+ using var sn = CreateSnapshot();
+ using var iterator = CreateIterator(new ReadOptions { Snapshot = sn });
+ iterator.SeekToFirst();
+ while (iterator.IsValid())
+ {
+ yield return new KeyValuePair(iterator.KeyAsInt(), iterator.ValueAsInts());
+ iterator.Next();
+ }
+ }
+ }
+}
diff --git a/src/Plugins/LevelDBStore/DBExtensions.cs b/src/Plugins/LevelDBStore/DBExtensions.cs
new file mode 100644
index 0000000000..b65a419b90
--- /dev/null
+++ b/src/Plugins/LevelDBStore/DBExtensions.cs
@@ -0,0 +1,29 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// DBExtensions.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace LevelDB
+{
+ public static class DBExtensions
+ {
+ public static void CopyToByteArray(this int source, byte[] destination, int offset)
+ {
+ //if (destination == null) throw new ArgumentException("Destination array cannot be null");
+
+ // check if there is enough space for all the 4 bytes we will copy
+ //if (destination.Length < offset + 4) throw new ArgumentException("Not enough room in the destination array");
+
+ destination[offset] = (byte)(source >> 24); // fourth byte
+ destination[offset + 1] = (byte)(source >> 16); // third byte
+ destination[offset + 2] = (byte)(source >> 8); // second byte
+ destination[offset + 3] = (byte)source; // last byte is already in proper position
+ }
+ }
+}
diff --git a/src/Plugins/LevelDBStore/Env.cs b/src/Plugins/LevelDBStore/Env.cs
new file mode 100644
index 0000000000..cd7ea540d4
--- /dev/null
+++ b/src/Plugins/LevelDBStore/Env.cs
@@ -0,0 +1,30 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Env.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+namespace LevelDB
+{
+ ///
+ /// A default environment to access operating system functionality like
+ /// the filesystem etc of the current operating system.
+ ///
+ public class Env : LevelDBHandle
+ {
+ public Env()
+ {
+ Handle = LevelDBInterop.leveldb_create_default_env();
+ }
+
+ protected override void FreeUnManagedObjects()
+ {
+ LevelDBInterop.leveldb_env_destroy(Handle);
+ }
+ }
+}
diff --git a/src/Plugins/LevelDBStore/Helper.cs b/src/Plugins/LevelDBStore/Helper.cs
new file mode 100644
index 0000000000..0e55017ff1
--- /dev/null
+++ b/src/Plugins/LevelDBStore/Helper.cs
@@ -0,0 +1,43 @@
+// Copyright (C) 2015-2024 The Neo Project.
+//
+// Helper.cs file belongs to the neo project and is free
+// software distributed under the MIT software license, see the
+// accompanying file LICENSE in the main directory of the
+// repository or http://www.opensource.org/licenses/mit-license.php
+// for more details.
+//
+// Redistribution and use in source and binary forms with or without
+// modifications are permitted.
+
+using LevelDB;
+using System;
+using System.Collections.Generic;
+
+namespace Neo.IO.Data.LevelDB
+{
+ public static class Helper
+ {
+ public static IEnumerable<(byte[], byte[])> Seek(this DB db, byte[] prefix, ReadOptions options)
+ {
+ using var it = db.CreateIterator(options);
+
+ for (it.Seek(prefix); it.IsValid(); it.Next())
+ yield return new(it.Key(), it.Value());
+ }
+
+ public static IEnumerable<(byte[], byte[])> SeekPrev(this DB db, byte[] prefix, ReadOptions options)
+ {
+ using var it = db.CreateIterator(options);
+
+ it.Seek(prefix);
+
+ if (!it.IsValid())
+ it.SeekToLast();
+ else if (it.Key().AsSpan().SequenceCompareTo(prefix) > 0)
+ it.Prev();
+
+ for (; it.IsValid(); it.Prev())
+ yield return new(it.Key(), it.Value());
+ }
+ }
+}
diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs
deleted file mode 100644
index 60e0e24e5a..0000000000
--- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2015-2024 The Neo Project.
-//
-// DB.cs file belongs to the neo project and is free
-// software distributed under the MIT software license, see the
-// accompanying file LICENSE in the main directory of the
-// repository or http://www.opensource.org/licenses/mit-license.php
-// for more details.
-//
-// Redistribution and use in source and binary forms with or without
-// modifications are permitted.
-
-using System;
-using System.IO;
-
-namespace Neo.IO.Data.LevelDB
-{
- public class DB : IDisposable
- {
- private IntPtr handle;
-
- ///
- /// Return true if haven't got valid handle
- ///
- public bool IsDisposed => handle == IntPtr.Zero;
-
- private DB(IntPtr handle)
- {
- this.handle = handle;
- }
-
- public void Dispose()
- {
- if (handle != IntPtr.Zero)
- {
- Native.leveldb_close(handle);
- handle = IntPtr.Zero;
- }
- }
-
- public void Delete(WriteOptions options, byte[] key)
- {
- Native.leveldb_delete(handle, options.handle, key, (UIntPtr)key.Length, out IntPtr error);
- NativeHelper.CheckError(error);
- }
-
- public byte[] Get(ReadOptions options, byte[] key)
- {
- IntPtr value = Native.leveldb_get(handle, options.handle, key, (UIntPtr)key.Length, out UIntPtr length, out IntPtr error);
- try
- {
- NativeHelper.CheckError(error);
- return value.ToByteArray(length);
- }
- finally
- {
- if (value != IntPtr.Zero) Native.leveldb_free(value);
- }
- }
-
- public bool Contains(ReadOptions options, byte[] key)
- {
- IntPtr value = Native.leveldb_get(handle, options.handle, key, (UIntPtr)key.Length, out _, out IntPtr error);
- NativeHelper.CheckError(error);
-
- if (value != IntPtr.Zero)
- {
- Native.leveldb_free(value);
- return true;
- }
-
- return false;
- }
-
- public Snapshot GetSnapshot()
- {
- return new Snapshot(handle);
- }
-
- public Iterator NewIterator(ReadOptions options)
- {
- return new Iterator(Native.leveldb_create_iterator(handle, options.handle));
- }
-
- public static DB Open(string name)
- {
- return Open(name, Options.Default);
- }
-
- public static DB Open(string name, Options options)
- {
- IntPtr handle = Native.leveldb_open(options.handle, Path.GetFullPath(name), out IntPtr error);
- NativeHelper.CheckError(error);
- return new DB(handle);
- }
-
- public void Put(WriteOptions options, byte[] key, byte[] value)
- {
- Native.leveldb_put(handle, options.handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length, out IntPtr error);
- NativeHelper.CheckError(error);
- }
-
- public static void Repair(string name, Options options)
- {
- Native.leveldb_repair_db(options.handle, Path.GetFullPath(name), out IntPtr error);
- NativeHelper.CheckError(error);
- }
-
- public void Write(WriteOptions options, WriteBatch write_batch)
- {
- Native.leveldb_write(handle, options.handle, write_batch.handle, out IntPtr error);
- NativeHelper.CheckError(error);
- }
- }
-}
diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs
deleted file mode 100644
index 2ac3a0005f..0000000000
--- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2015-2024 The Neo Project.
-//
-// Helper.cs file belongs to the neo project and is free
-// software distributed under the MIT software license, see the
-// accompanying file LICENSE in the main directory of the
-// repository or http://www.opensource.org/licenses/mit-license.php
-// for more details.
-//
-// Redistribution and use in source and binary forms with or without
-// modifications are permitted.
-
-using Neo.Persistence;
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-
-namespace Neo.IO.Data.LevelDB
-{
- public static class Helper
- {
- public static IEnumerable Seek(this DB db, ReadOptions options, byte[] prefix, SeekDirection direction, Func resultSelector)
- {
- using Iterator it = db.NewIterator(options);
- if (direction == SeekDirection.Forward)
- {
- for (it.Seek(prefix); it.Valid(); it.Next())
- yield return resultSelector(it.Key(), it.Value());
- }
- else
- {
- // SeekForPrev
-
- it.Seek(prefix);
- if (!it.Valid())
- it.SeekToLast();
- else if (it.Key().AsSpan().SequenceCompareTo(prefix) > 0)
- it.Prev();
-
- for (; it.Valid(); it.Prev())
- yield return resultSelector(it.Key(), it.Value());
- }
- }
-
- public static IEnumerable FindRange(this DB db, ReadOptions options, byte[] startKey, byte[] endKey, Func resultSelector)
- {
- using Iterator it = db.NewIterator(options);
- for (it.Seek(startKey); it.Valid(); it.Next())
- {
- byte[] key = it.Key();
- if (key.AsSpan().SequenceCompareTo(endKey) > 0) break;
- yield return resultSelector(key, it.Value());
- }
- }
-
- internal static byte[] ToByteArray(this IntPtr data, UIntPtr length)
- {
- if (data == IntPtr.Zero) return null;
- byte[] buffer = new byte[(int)length];
- Marshal.Copy(data, buffer, 0, (int)length);
- return buffer;
- }
- }
-}
diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs
deleted file mode 100644
index 6c025380fb..0000000000
--- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2015-2024 The Neo Project.
-//
-// Iterator.cs file belongs to the neo project and is free
-// software distributed under the MIT software license, see the
-// accompanying file LICENSE in the main directory of the
-// repository or http://www.opensource.org/licenses/mit-license.php
-// for more details.
-//
-// Redistribution and use in source and binary forms with or without
-// modifications are permitted.
-
-using System;
-
-namespace Neo.IO.Data.LevelDB
-{
- public class Iterator : IDisposable
- {
- private IntPtr handle;
-
- internal Iterator(IntPtr handle)
- {
- this.handle = handle;
- }
-
- private void CheckError()
- {
- Native.leveldb_iter_get_error(handle, out IntPtr error);
- NativeHelper.CheckError(error);
- }
-
- public void Dispose()
- {
- if (handle != IntPtr.Zero)
- {
- Native.leveldb_iter_destroy(handle);
- handle = IntPtr.Zero;
- }
- }
-
- public byte[] Key()
- {
- IntPtr key = Native.leveldb_iter_key(handle, out UIntPtr length);
- CheckError();
- return key.ToByteArray(length);
- }
-
- public void Next()
- {
- Native.leveldb_iter_next(handle);
- CheckError();
- }
-
- public void Prev()
- {
- Native.leveldb_iter_prev(handle);
- CheckError();
- }
-
- public void Seek(byte[] target)
- {
- Native.leveldb_iter_seek(handle, target, (UIntPtr)target.Length);
- }
-
- public void SeekToFirst()
- {
- Native.leveldb_iter_seek_to_first(handle);
- }
-
- public void SeekToLast()
- {
- Native.leveldb_iter_seek_to_last(handle);
- }
-
- public bool Valid()
- {
- return Native.leveldb_iter_valid(handle);
- }
-
- public byte[] Value()
- {
- IntPtr value = Native.leveldb_iter_value(handle, out UIntPtr length);
- CheckError();
- return value.ToByteArray(length);
- }
- }
-}
diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs
deleted file mode 100644
index c9cca42070..0000000000
--- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (C) 2015-2024 The Neo Project.
-//
-// LevelDBException.cs file belongs to the neo project and is free
-// software distributed under the MIT software license, see the
-// accompanying file LICENSE in the main directory of the
-// repository or http://www.opensource.org/licenses/mit-license.php
-// for more details.
-//
-// Redistribution and use in source and binary forms with or without
-// modifications are permitted.
-
-using System.Data.Common;
-
-namespace Neo.IO.Data.LevelDB
-{
- public class LevelDBException : DbException
- {
- internal LevelDBException(string message)
- : base(message)
- {
- }
- }
-}
diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs
deleted file mode 100644
index acf8fa82b9..0000000000
--- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright (C) 2015-2024 The Neo Project.
-//
-// Native.cs file belongs to the neo project and is free
-// software distributed under the MIT software license, see the
-// accompanying file LICENSE in the main directory of the
-// repository or http://www.opensource.org/licenses/mit-license.php
-// for more details.
-//
-// Redistribution and use in source and binary forms with or without
-// modifications are permitted.
-
-using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-namespace Neo.IO.Data.LevelDB
-{
- public enum CompressionType : byte
- {
- kNoCompression = 0x0,
- kSnappyCompression = 0x1
- }
-
- public static class Native
- {
- #region Logger
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_logger_destroy(IntPtr /* logger*/ option);
- #endregion
-
- #region DB
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_open(IntPtr /* Options*/ options, string name, out IntPtr error);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_close(IntPtr /*DB */ db);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_put(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen, out IntPtr errptr);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_delete(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, out IntPtr errptr);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_write(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, IntPtr /* WriteBatch */ batch, out IntPtr errptr);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, byte[] key, UIntPtr keylen, out UIntPtr vallen, out IntPtr errptr);
-
- //[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- //static extern void leveldb_approximate_sizes(IntPtr /* DB */ db, int num_ranges, byte[] range_start_key, long range_start_key_len, byte[] range_limit_key, long range_limit_key_len, out long sizes);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_create_iterator(IntPtr /* DB */ db, IntPtr /* ReadOption */ options);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_create_snapshot(IntPtr /* DB */ db);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_release_snapshot(IntPtr /* DB */ db, IntPtr /* SnapShot*/ snapshot);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_property_value(IntPtr /* DB */ db, string propname);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_repair_db(IntPtr /* Options*/ options, string name, out IntPtr error);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_destroy_db(IntPtr /* Options*/ options, string name, out IntPtr error);
-
- #region extensions
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_free(IntPtr /* void */ ptr);
-
- #endregion
-
-
- #endregion
-
- #region Env
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_create_default_env();
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_env_destroy(IntPtr /*Env*/ cache);
- #endregion
-
- #region Iterator
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_destroy(IntPtr /*Iterator*/ iterator);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.U1)]
- public static extern bool leveldb_iter_valid(IntPtr /*Iterator*/ iterator);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_seek_to_first(IntPtr /*Iterator*/ iterator);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_seek_to_last(IntPtr /*Iterator*/ iterator);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, byte[] key, UIntPtr length);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_next(IntPtr /*Iterator*/ iterator);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_prev(IntPtr /*Iterator*/ iterator);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_iter_key(IntPtr /*Iterator*/ iterator, out UIntPtr length);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_iter_value(IntPtr /*Iterator*/ iterator, out UIntPtr length);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_iter_get_error(IntPtr /*Iterator*/ iterator, out IntPtr error);
- #endregion
-
- #region Options
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_options_create();
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_destroy(IntPtr /*Options*/ options);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_create_if_missing(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_error_if_exists(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_info_log(IntPtr /*Options*/ options, IntPtr /* Logger */ logger);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_paranoid_checks(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_env(IntPtr /*Options*/ options, IntPtr /*Env*/ env);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_write_buffer_size(IntPtr /*Options*/ options, UIntPtr size);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_max_open_files(IntPtr /*Options*/ options, int max);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_cache(IntPtr /*Options*/ options, IntPtr /*Cache*/ cache);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_block_size(IntPtr /*Options*/ options, UIntPtr size);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_block_restart_interval(IntPtr /*Options*/ options, int interval);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_compression(IntPtr /*Options*/ options, CompressionType level);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_comparator(IntPtr /*Options*/ options, IntPtr /*Comparator*/ comparer);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_options_set_filter_policy(IntPtr /*Options*/ options, IntPtr /*FilterPolicy*/ policy);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_filterpolicy_create_bloom(int bits_per_key);
- #endregion
-
- #region ReadOptions
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_readoptions_create();
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_readoptions_destroy(IntPtr /*ReadOptions*/ options);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_readoptions_set_verify_checksums(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_readoptions_set_fill_cache(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_readoptions_set_snapshot(IntPtr /*ReadOptions*/ options, IntPtr /*SnapShot*/ snapshot);
- #endregion
-
- #region WriteBatch
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern IntPtr leveldb_writebatch_create();
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_writebatch_destroy(IntPtr /* WriteBatch */ batch);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_writebatch_clear(IntPtr /* WriteBatch */ batch);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_writebatch_put(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_writebatch_delete(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen);
-
- [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
- public static extern void leveldb_writebatch_iterate(IntPtr /* WriteBatch */ batch, object state, Action