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 put, Action deleted); - #endregion - - #region WriteOptions - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_writeoptions_create(); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_destroy(IntPtr /*WriteOptions*/ options); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_set_sync(IntPtr /*WriteOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - #endregion - - #region Cache - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_cache_create_lru(int capacity); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_cache_destroy(IntPtr /*Cache*/ cache); - #endregion - - #region Comparator - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr /* leveldb_comparator_t* */ - leveldb_comparator_create( - IntPtr /* void* */ state, - IntPtr /* void (*)(void*) */ destructor, - IntPtr - /* int (*compare)(void*, - const char* a, size_t alen, - const char* b, size_t blen) */ - compare, - IntPtr /* const char* (*)(void*) */ name); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_comparator_destroy(IntPtr /* leveldb_comparator_t* */ cmp); - - #endregion - } - - internal static class NativeHelper - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CheckError(IntPtr error) - { - if (error != IntPtr.Zero) - { - string message = Marshal.PtrToStringAnsi(error); - Native.leveldb_free(error); - throw new LevelDBException(message); - } - } - } -} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs deleted file mode 100644 index 989987eeed..0000000000 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// Options.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 Options - { - public static readonly Options Default = new Options(); - internal readonly IntPtr handle = Native.leveldb_options_create(); - - public bool CreateIfMissing - { - set - { - Native.leveldb_options_set_create_if_missing(handle, value); - } - } - - public bool ErrorIfExists - { - set - { - Native.leveldb_options_set_error_if_exists(handle, value); - } - } - - public bool ParanoidChecks - { - set - { - Native.leveldb_options_set_paranoid_checks(handle, value); - } - } - - public int WriteBufferSize - { - set - { - Native.leveldb_options_set_write_buffer_size(handle, (UIntPtr)value); - } - } - - public int MaxOpenFiles - { - set - { - Native.leveldb_options_set_max_open_files(handle, value); - } - } - - public int BlockSize - { - set - { - Native.leveldb_options_set_block_size(handle, (UIntPtr)value); - } - } - - public int BlockRestartInterval - { - set - { - Native.leveldb_options_set_block_restart_interval(handle, value); - } - } - - public CompressionType Compression - { - set - { - Native.leveldb_options_set_compression(handle, value); - } - } - - public IntPtr FilterPolicy - { - set - { - Native.leveldb_options_set_filter_policy(handle, value); - } - } - - ~Options() - { - Native.leveldb_options_destroy(handle); - } - } -} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs deleted file mode 100644 index 727ae9f02a..0000000000 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// ReadOptions.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 ReadOptions - { - public static readonly ReadOptions Default = new ReadOptions(); - internal readonly IntPtr handle = Native.leveldb_readoptions_create(); - - public bool VerifyChecksums - { - set - { - Native.leveldb_readoptions_set_verify_checksums(handle, value); - } - } - - public bool FillCache - { - set - { - Native.leveldb_readoptions_set_fill_cache(handle, value); - } - } - - public Snapshot Snapshot - { - set - { - Native.leveldb_readoptions_set_snapshot(handle, value.handle); - } - } - - ~ReadOptions() - { - Native.leveldb_readoptions_destroy(handle); - } - } -} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs deleted file mode 100644 index 14280fbc8f..0000000000 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// Snapshot.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 Snapshot : IDisposable - { - internal IntPtr db, handle; - - internal Snapshot(IntPtr db) - { - this.db = db; - handle = Native.leveldb_create_snapshot(db); - } - - public void Dispose() - { - if (handle != IntPtr.Zero) - { - Native.leveldb_release_snapshot(db, handle); - handle = IntPtr.Zero; - } - } - } -} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs deleted file mode 100644 index ad82dad450..0000000000 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// WriteBatch.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 WriteBatch - { - internal readonly IntPtr handle = Native.leveldb_writebatch_create(); - - ~WriteBatch() - { - Native.leveldb_writebatch_destroy(handle); - } - - public void Clear() - { - Native.leveldb_writebatch_clear(handle); - } - - public void Delete(byte[] key) - { - Native.leveldb_writebatch_delete(handle, key, (UIntPtr)key.Length); - } - - public void Put(byte[] key, byte[] value) - { - Native.leveldb_writebatch_put(handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); - } - } -} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs deleted file mode 100644 index 48915ba480..0000000000 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2015-2024 The Neo Project. -// -// WriteOptions.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 WriteOptions - { - public static readonly WriteOptions Default = new WriteOptions(); - public static readonly WriteOptions SyncWrite = new WriteOptions { Sync = true }; - - internal readonly IntPtr handle = Native.leveldb_writeoptions_create(); - - public bool Sync - { - set - { - Native.leveldb_writeoptions_set_sync(handle, value); - } - } - - ~WriteOptions() - { - Native.leveldb_writeoptions_destroy(handle); - } - } -} diff --git a/src/Plugins/LevelDBStore/Iterator.cs b/src/Plugins/LevelDBStore/Iterator.cs new file mode 100644 index 0000000000..dfb3ac39d9 --- /dev/null +++ b/src/Plugins/LevelDBStore/Iterator.cs @@ -0,0 +1,211 @@ +// 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; +using System.Runtime.InteropServices; +using System.Text; +namespace LevelDB +{ + /// + /// An iterator yields a sequence of key/value pairs from a database. + /// + public class Iterator : LevelDBHandle + { + private readonly Encoding _encoding; + + internal Iterator(IntPtr handle, Encoding encoding) + { + _encoding = encoding; + Handle = handle; + } + + /// + /// An iterator is either positioned at a key/value pair, or + /// not valid. + /// + /// This method returns true iff the iterator is valid. + public bool IsValid() + { + return LevelDBInterop.leveldb_iter_valid(Handle) != 0; + } + + /// + /// Position at the first key in the source. + /// The iterator is Valid() after this call iff the source is not empty. + /// + public void SeekToFirst() + { + LevelDBInterop.leveldb_iter_seek_to_first(Handle); + Throw(); + } + + /// + /// Position at the last key in the source. + /// The iterator is Valid() after this call iff the source is not empty. + /// + public void SeekToLast() + { + LevelDBInterop.leveldb_iter_seek_to_last(Handle); + Throw(); + } + + /// + /// Position at the first key in the source that at or past target + /// The iterator is Valid() after this call iff the source contains + /// an entry that comes at or past target. + /// + public void Seek(byte[] key) + { + LevelDBInterop.leveldb_iter_seek(Handle, key, key.Length); + Throw(); + } + + /// + /// Position at the first key in the source that at or past target + /// The iterator is Valid() after this call iff the source contains + /// an entry that comes at or past target. + /// + public void Seek(string key) + { + Seek(_encoding.GetBytes(key)); + } + + /// + /// Position at the first key in the source that at or past target + /// The iterator is Valid() after this call iff the source contains + /// an entry that comes at or past target. + /// + public void Seek(int key) + { + LevelDBInterop.leveldb_iter_seek(Handle, ref key, 4); + Throw(); + } + + /// + /// Moves to the next entry in the source. + /// After this call, Valid() is true iff the iterator was not positioned at the last entry in the source. + /// REQUIRES: Valid() + /// + public void Next() + { + LevelDBInterop.leveldb_iter_next(Handle); + Throw(); + } + + /// + /// Moves to the previous entry in the source. + /// After this call, Valid() is true iff the iterator was not positioned at the first entry in source. + /// REQUIRES: Valid() + /// + public void Prev() + { + LevelDBInterop.leveldb_iter_prev(Handle); + Throw(); + } + + + /// + /// Return the key for the current entry. + /// REQUIRES: Valid() + /// + public int KeyAsInt() + { + var key = LevelDBInterop.leveldb_iter_key(Handle, out var length); + Throw(); + + if (length != 4) throw new Exception("Key is not an integer"); + + return Marshal.ReadInt32(key); + } + + /// + /// Return the key for the current entry. + /// REQUIRES: Valid() + /// + public string KeyAsString() + { + return _encoding.GetString(Key()); + } + + /// + /// Return the key for the current entry. + /// REQUIRES: Valid() + /// + public byte[] Key() + { + var key = LevelDBInterop.leveldb_iter_key(Handle, out var length); + Throw(); + + var bytes = new byte[length]; + Marshal.Copy(key, bytes, 0, length); + return bytes; + } + + /// + /// Return the value for the current entry. + /// REQUIRES: Valid() + /// + public int[] ValueAsInts() + { + var value = LevelDBInterop.leveldb_iter_value(Handle, out var length); + Throw(); + + var bytes = new int[length / 4]; + Marshal.Copy(value, bytes, 0, length / 4); + return bytes; + } + + /// + /// Return the value for the current entry. + /// REQUIRES: Valid() + /// + public string ValueAsString() + { + return _encoding.GetString(Value()); + } + + /// + /// Return the value for the current entry. + /// REQUIRES: Valid() + /// + public byte[] Value() + { + var value = LevelDBInterop.leveldb_iter_value(Handle, out var length); + Throw(); + + var bytes = new byte[length]; + Marshal.Copy(value, bytes, 0, length); + return bytes; + } + + /// + /// If an error has occurred, throw it. + /// + void Throw() + { + Throw(msg => new Exception(msg)); + } + + /// + /// If an error has occurred, throw it. + /// + void Throw(Func exception) + { + LevelDBInterop.leveldb_iter_get_error(Handle, out var error); + if (error != IntPtr.Zero) throw exception(Marshal.PtrToStringAnsi(error)); + } + + protected override void FreeUnManagedObjects() + { + LevelDBInterop.leveldb_iter_destroy(Handle); + } + } +} diff --git a/src/Plugins/LevelDBStore/LevelDBHandle.cs b/src/Plugins/LevelDBStore/LevelDBHandle.cs new file mode 100644 index 0000000000..2c679b47a0 --- /dev/null +++ b/src/Plugins/LevelDBStore/LevelDBHandle.cs @@ -0,0 +1,61 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// LevelDBHandle.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 LevelDB +{ + /// + /// Base class for all LevelDB objects + /// Implement IDisposable as prescribed by http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx by overriding the two additional virtual methods + /// + public abstract class LevelDBHandle : IDisposable + { + public IntPtr Handle { protected set; get; } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void FreeManagedObjects() + { + } + + protected virtual void FreeUnManagedObjects() + { + } + + bool _disposed = false; + void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + FreeManagedObjects(); + } + if (Handle != IntPtr.Zero) + { + FreeUnManagedObjects(); + Handle = IntPtr.Zero; + } + _disposed = true; + } + } + + ~LevelDBHandle() + { + Dispose(false); + } + } +} diff --git a/src/Plugins/LevelDBStore/LevelDBInterop.cs b/src/Plugins/LevelDBStore/LevelDBInterop.cs new file mode 100644 index 0000000000..472f174dbe --- /dev/null +++ b/src/Plugins/LevelDBStore/LevelDBInterop.cs @@ -0,0 +1,253 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// LevelDBInterop.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.InteropServices; + +namespace LevelDB +{ + public static class LevelDBInterop + { + #region Logger + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_logger_destroy(IntPtr /* logger*/ option); + #endregion + + #region DB + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_open(IntPtr /* Options*/ options, string name, out IntPtr error); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_close(IntPtr /*DB */ db); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_put(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, IntPtr keylen, byte[] val, IntPtr vallen, out IntPtr errptr); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_put(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, ref int key, IntPtr keylen, int[] val, IntPtr vallen, out IntPtr errptr); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_delete(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, IntPtr keylen, out IntPtr errptr); + + [DllImport("leveldb", 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("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, byte[] key, IntPtr keylen, out IntPtr vallen, out IntPtr errptr); + + [DllImport("leveldb", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, ref int key, IntPtr keylen, out IntPtr vallen, out IntPtr errptr); + + [DllImport("leveldb", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, IntPtr key, IntPtr keylen, out IntPtr vallen, out IntPtr errptr); + + //[DllImport("leveldb", 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("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_create_iterator(IntPtr /* DB */ db, IntPtr /* ReadOption */ options); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_create_snapshot(IntPtr /* DB */ db); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_release_snapshot(IntPtr /* DB */ db, IntPtr /* SnapShot*/ snapshot); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_property_value(IntPtr /* DB */ db, string propname); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_repair_db(IntPtr /* Options*/ options, string name, out IntPtr error); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_destroy_db(IntPtr /* Options*/ options, string name, out IntPtr error); + + #region extensions + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_free(IntPtr /* void */ ptr); + + #endregion + + + #endregion + + #region Env + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_create_default_env(); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_env_destroy(IntPtr /*Env*/ cache); + #endregion + + #region Iterator + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_destroy(IntPtr /*Iterator*/ iterator); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern byte leveldb_iter_valid(IntPtr /*Iterator*/ iterator); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_seek_to_first(IntPtr /*Iterator*/ iterator); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_seek_to_last(IntPtr /*Iterator*/ iterator); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, byte[] key, int length); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, ref int key, int length); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_next(IntPtr /*Iterator*/ iterator); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_prev(IntPtr /*Iterator*/ iterator); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_iter_key(IntPtr /*Iterator*/ iterator, out int length); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_iter_value(IntPtr /*Iterator*/ iterator, out int length); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_get_error(IntPtr /*Iterator*/ iterator, out IntPtr error); + #endregion + + #region Options + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_options_create(); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_destroy(IntPtr /*Options*/ options); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_create_if_missing(IntPtr /*Options*/ options, byte o); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_error_if_exists(IntPtr /*Options*/ options, byte o); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_info_log(IntPtr /*Options*/ options, IntPtr /* Logger */ logger); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_paranoid_checks(IntPtr /*Options*/ options, byte o); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_env(IntPtr /*Options*/ options, IntPtr /*Env*/ env); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_write_buffer_size(IntPtr /*Options*/ options, long size); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_max_open_files(IntPtr /*Options*/ options, int max); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_cache(IntPtr /*Options*/ options, IntPtr /*Cache*/ cache); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_block_size(IntPtr /*Options*/ options, long size); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_block_restart_interval(IntPtr /*Options*/ options, int interval); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_compression(IntPtr /*Options*/ options, int level); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_filter_policy(IntPtr /*Options*/ options, IntPtr /*FilterPolicy*/ policy); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_options_set_comparator(IntPtr /*Options*/ options, IntPtr /*Comparator*/ comparer); + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_filterpolicy_create_bloom(int bits_per_key); + #endregion + + #region ReadOptions + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_readoptions_create(); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_readoptions_destroy(IntPtr /*ReadOptions*/ options); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_readoptions_set_verify_checksums(IntPtr /*ReadOptions*/ options, byte o); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_readoptions_set_fill_cache(IntPtr /*ReadOptions*/ options, byte o); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_readoptions_set_snapshot(IntPtr /*ReadOptions*/ options, IntPtr /*SnapShot*/ snapshot); + #endregion + + #region WriteBatch + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_writebatch_create(); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writebatch_destroy(IntPtr /* WriteBatch */ batch); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writebatch_clear(IntPtr /* WriteBatch */ batch); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writebatch_put(IntPtr /* WriteBatch */ batch, byte[] key, int keylen, byte[] val, int vallen); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writebatch_delete(IntPtr /* WriteBatch */ batch, byte[] key, int keylen); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writebatch_iterate(IntPtr /* WriteBatch */ batch, object state, Action put, Action deleted); + #endregion + + #region WriteOptions + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_writeoptions_create(); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writeoptions_destroy(IntPtr /*WriteOptions*/ options); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_writeoptions_set_sync(IntPtr /*WriteOptions*/ options, byte o); + #endregion + + #region Cache + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr leveldb_cache_create_lru(int capacity); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_cache_destroy(IntPtr /*Cache*/ cache); + #endregion + + #region Comparator + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr /* leveldb_comparator_t* */ + leveldb_comparator_create( + IntPtr /* void* */ state, + IntPtr /* void (*)(void*) */ destructor, + IntPtr + /* int (*compare)(void*, + const char* a, size_t alen, + const char* b, size_t blen) */ + compare, + IntPtr /* const char* (*)(void*) */ name); + + [DllImport("leveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_comparator_destroy(IntPtr /* leveldb_comparator_t* */ cmp); + + #endregion + } +} diff --git a/src/Plugins/LevelDBStore/LevelDbFreeHandle.cs b/src/Plugins/LevelDBStore/LevelDbFreeHandle.cs new file mode 100644 index 0000000000..ec55ce04ec --- /dev/null +++ b/src/Plugins/LevelDBStore/LevelDbFreeHandle.cs @@ -0,0 +1,48 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// LevelDbFreeHandle.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.InteropServices; + +namespace LevelDB +{ + // Wraps pointers to be freed with leveldb_free (e.g. returned by leveldb_get) + // + // reference on safe handles: http://blogs.msdn.com/b/bclteam/archive/2006/06/23/644343.aspx + internal class LevelDbFreeHandle : SafeHandle + { + public LevelDbFreeHandle() + : base(default, true) + { + } + + override protected bool ReleaseHandle() + { + if (handle != default) + LevelDBInterop.leveldb_free(handle); + handle = default; + return true; + } + + public override bool IsInvalid + { + get { return handle != default; } + } + + public new void SetHandle(IntPtr p) + { + if (handle != default) + ReleaseHandle(); + + base.SetHandle(p); + } + } +} diff --git a/src/Plugins/LevelDBStore/NativePointer.cs b/src/Plugins/LevelDBStore/NativePointer.cs new file mode 100644 index 0000000000..880459c0a8 --- /dev/null +++ b/src/Plugins/LevelDBStore/NativePointer.cs @@ -0,0 +1,358 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// NativePointer.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.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace LevelDB.NativePointer +{ + // note: sizeof(Ptr<>) == sizeof(IntPtr) allows you to create Ptr> of arbitrary depth and "it just works" + // IntPtr severely lacks appropriate arithmetic operators; up-promotions to ulong used instead. + public struct Ptr(IntPtr addr) + where T : struct + { + private IntPtr _addr = addr; + + // cannot use 'sizeof' operator on generic type parameters + public static readonly uint SizeofT = (uint)Marshal.SizeOf(typeof(T)); + private static readonly IDeref s_deref = GetDeref(); + + private static IDeref GetDeref() + { + if (typeof(T) == typeof(int)) + return (IDeref)new IntDeref(); + + // TODO: other concrete implementations of IDeref. + // (can't be made generic; will not type check) + + // fallback + return new MarshalDeref(); + } + + public static explicit operator Ptr(IntPtr p) + { + return new Ptr(p); + } + public static explicit operator IntPtr(Ptr p) + { + return p._addr; + } + + // operator Ptr(Ptr) + public static Ptr Cast(Ptr p) + where U : struct + { + return new Ptr(p._addr); + } + + public void Inc() { Advance(1); } + public void Dec() { Advance(-1); } + + public void Advance(IntPtr d) + { + _addr = (IntPtr)((ulong)_addr + SizeofT * (ulong)d); + } + public readonly IntPtr Diff(Ptr p2) + { + var diff = (long)(((ulong)_addr) - ((ulong)p2._addr)); + Debug.Assert(diff % SizeofT == 0); + + return checked((IntPtr)(diff / SizeofT)); + } + public readonly T Deref() + { + return s_deref.Deref(_addr); + } + public readonly void DerefWrite(T newValue) + { + s_deref.DerefWrite(_addr, newValue); + } + + // C-style pointer arithmetic. IntPtr is used in place of C's ptrdiff_t + #region pointer/intptr arithmetic + public static Ptr operator ++(Ptr p) + { + p.Inc(); + return p; + } + public static Ptr operator --(Ptr p) + { + p.Dec(); + return p; + } + public static Ptr operator +(Ptr p, IntPtr offset) + { + p.Advance(offset); + return p; + } + public static Ptr operator +(IntPtr offset, Ptr p) + { + p.Advance(offset); + return p; + } + public static Ptr operator -(Ptr p, IntPtr offset) + { + p.Advance((IntPtr)(0 - (ulong)offset)); + return p; + } + public static IntPtr operator -(Ptr p, Ptr p2) + { + return p.Diff(p2); + } + public readonly T this[IntPtr offset] + { + get { return (this + offset).Deref(); } + + set { (this + offset).DerefWrite(value); } + } + #endregion + + #region comparisons + public override readonly bool Equals(object obj) + { + if (obj is not Ptr) + return false; + return this == (Ptr)obj; + } + public override readonly int GetHashCode() + { + return checked((int)_addr ^ (int)(IntPtr)((long)_addr >> 6)); + } + public static bool operator ==(Ptr p, Ptr p2) + { + return (IntPtr)p == (IntPtr)p2; + } + public static bool operator !=(Ptr p, Ptr p2) + { + return (IntPtr)p != (IntPtr)p2; + } + public static bool operator <(Ptr p, Ptr p2) + { + return (ulong)(IntPtr)p < (ulong)(IntPtr)p2; + } + public static bool operator >(Ptr p, Ptr p2) + { + return (ulong)(IntPtr)p > (ulong)(IntPtr)p2; + } + public static bool operator <=(Ptr p, Ptr p2) + { + return (ulong)(IntPtr)p <= (ulong)(IntPtr)p2; + } + public static bool operator >=(Ptr p, Ptr p2) + { + return (ulong)(IntPtr)p >= (ulong)(IntPtr)p2; + } + #endregion + + #region pointer/int/long arithmetic (convenience) + public static Ptr operator +(Ptr p, long offset) + { + return p + checked((IntPtr)offset); + } + public static Ptr operator +(long offset, Ptr p) + { + return p + checked((IntPtr)offset); + } + public static Ptr operator -(Ptr p, long offset) + { + return p - checked((IntPtr)offset); + } + public T this[long offset] + { + readonly get { return this[checked((IntPtr)offset)]; } + set { this[checked((IntPtr)offset)] = value; } + } + #endregion + } + + public struct NativeArray + : IDisposable + { + public IntPtr _baseAddr; + public IntPtr _byteLength; + + public SafeHandle _handle; + + public readonly void Dispose() + { + _handle?.Dispose(); + } + + public static NativeArray FromArray(T[] arr, long start = 0, long count = -1) + where T : struct + { + if (count < 0) count = arr.LongLength - start; + + var h = new PinnedSafeHandle(arr); + return new NativeArray { _baseAddr = h.Ptr + start, _count = checked((IntPtr)count), _handle = h }; + } + } + + public struct NativeArray + : IEnumerable + , IDisposable + where T : struct + { + public Ptr _baseAddr; + public IntPtr _count; + + public SafeHandle _handle; + + public static implicit operator NativeArray(NativeArray arr) + { + return new NativeArray + { + _baseAddr = (IntPtr)arr._baseAddr, + _byteLength = (IntPtr)((ulong)(IntPtr)(arr._baseAddr + arr._count) - (ulong)(IntPtr)(arr._baseAddr)), + _handle = arr._handle + }; + } + public static explicit operator NativeArray(NativeArray arr) + { + var baseAddr = (Ptr)arr._baseAddr; + var count = ((Ptr)(IntPtr)((ulong)arr._baseAddr + (ulong)arr._byteLength)) - baseAddr; + + return new NativeArray { _baseAddr = baseAddr, _count = count, _handle = arr._handle }; + } + + #region IEnumerable + + public readonly IEnumerator GetEnumerator() + { + return new Enumerator(_baseAddr, _baseAddr + _count, _handle); + } + + readonly System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private class Enumerator(Ptr start, Ptr end, SafeHandle handle) : IEnumerator + { + private Ptr _current = start; + private Ptr _end = end; + private int _state = 0; + private readonly SafeHandle _handle = handle; + + public void Dispose() + { + GC.KeepAlive(_handle); + } + + public T Current + { + get + { + if (_handle != null && _handle.IsClosed) + throw new InvalidOperationException("Dereferencing a closed handle"); + if (_state != 1) + throw new InvalidOperationException("Attempt to invoke Current on invalid enumerator"); + return _current.Deref(); + } + } + + public bool MoveNext() + { + switch (_state) + { + case 0: + _state = 1; + return _current != _end; + case 1: + ++_current; + if (_current == _end) + _state = 2; + return _current != _end; + case 2: + default: + return false; + } + } + + public void Reset() + { + throw new NotImplementedException(); + } + + object System.Collections.IEnumerator.Current + { + get { return Current; } + } + } + + #endregion + + public T this[IntPtr offset] + { + readonly get + { + if ((ulong)offset >= (ulong)_count) + throw new IndexOutOfRangeException("offest"); + var val = _baseAddr[offset]; + GC.KeepAlive(this); + return val; + } + set + { + if ((ulong)offset >= (ulong)_count) + throw new IndexOutOfRangeException("offest"); + _baseAddr[offset] = value; + GC.KeepAlive(this); + } + } + public T this[long offset] + { + readonly get { return this[checked((IntPtr)offset)]; } + set { this[checked((IntPtr)offset)] = value; } + } + + public readonly void Dispose() + { + _handle?.Dispose(); + } + } + + #region dereferencing abstraction + interface IDeref + { + T Deref(IntPtr addr); + void DerefWrite(IntPtr addr, T newValue); + } + internal unsafe class IntDeref : IDeref + { + public int Deref(IntPtr addr) + { + var p = (int*)addr; + return *p; + } + + public void DerefWrite(IntPtr addr, int newValue) + { + var p = (int*)addr; + *p = newValue; + } + } + internal class MarshalDeref : IDeref + { + public T Deref(IntPtr addr) + { + return (T)Marshal.PtrToStructure(addr, typeof(T)); + } + + public void DerefWrite(IntPtr addr, T newValue) + { + Marshal.StructureToPtr(newValue, addr, false); + } + } + #endregion +} diff --git a/src/Plugins/LevelDBStore/Options.cs b/src/Plugins/LevelDBStore/Options.cs new file mode 100644 index 0000000000..2cb24ee421 --- /dev/null +++ b/src/Plugins/LevelDBStore/Options.cs @@ -0,0 +1,196 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Options.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 +{ + /// + /// Options to control the behavior of a database (passed to Open) + /// + /// the setter methods for InfoLogger, Env, and Cache only "safe to clean up guarantee". Do not + /// use Option object if throws. + /// + public class Options : LevelDBHandle + { + public static readonly Options Default = new Options(); + + public Options() + { + Handle = LevelDBInterop.leveldb_options_create(); + } + + /// + /// If true, the database will be created if it is missing. + /// + public bool CreateIfMissing + { + set { LevelDBInterop.leveldb_options_set_create_if_missing(Handle, value ? (byte)1 : (byte)0); } + } + + /// + /// If true, an error is raised if the database already exists. + /// + public bool ErrorIfExists + { + set { LevelDBInterop.leveldb_options_set_error_if_exists(Handle, value ? (byte)1 : (byte)0); } + } + + /// + /// If true, the implementation will do aggressive checking of the + /// data it is processing and will stop early if it detects any + /// errors. This may have unforeseen ramifications: for example, a + /// corruption of one DB entry may cause a large number of entries to + /// become unreadable or for the entire DB to become unopenable. + /// + public bool ParanoidChecks + { + set { LevelDBInterop.leveldb_options_set_paranoid_checks(Handle, value ? (byte)1 : (byte)0); } + } + + /// + /// Use the specified Env object to interact with the environment, + /// e.g. to read/write files, schedule background work, etc. + /// + public Env Env + { + set + { + LevelDBInterop.leveldb_options_set_env(Handle, value.Handle); + _Env = value; + } + get { return _Env; } + } + + // Any internal progress/error information generated by the db will + // be written to info_log if it is non-NULL, or to a file stored + // in the same directory as the DB contents if info_log is NULL. + + /// + /// Amount of data to build up in memory (backed by an unsorted log + /// on disk) before converting to a sorted on-disk file. + /// + /// Larger values increase performance, especially during bulk loads. + /// Up to two write buffers may be held in memory at the same time, + /// so you may wish to adjust this parameter to control memory usage. + /// Also, a larger write buffer will result in a longer recovery time + /// the next time the database is opened. + /// + /// Default: 4MB + /// + public long WriteBufferSize + { + set { LevelDBInterop.leveldb_options_set_write_buffer_size(Handle, value); } + } + + /// + /// Number of open files that can be used by the DB. You may need to + /// increase this if your database has a large working set (budget + /// one open file per 2MB of working set). + /// + /// Default: 1000 + /// + public int MaxOpenFiles + { + set { LevelDBInterop.leveldb_options_set_max_open_files(Handle, value); } + } + + /// + /// Control over blocks (user data is stored in a set of blocks, and + /// a block is the unit of reading from disk). + /// + /// If not set, leveldb will automatically create and use an 8MB internal cache. + /// + public Cache Cache + { + set + { + LevelDBInterop.leveldb_options_set_cache(Handle, value.Handle); + _Cache = value; + } + get { return _Cache; } + } + + public Comparator Comparator + { + set + { + LevelDBInterop.leveldb_options_set_comparator(Handle, value.Handle); + _Comparator = value; + } + get { return _Comparator; } + } + + /// + /// Approximate size of user data packed per block. Note that the + /// block size specified here corresponds to uncompressed data. The + /// actual size of the unit read from disk may be smaller if + /// compression is enabled. This parameter can be changed dynamically. + /// + /// Default: 4K + /// + public long BlockSize + { + set { LevelDBInterop.leveldb_options_set_block_size(Handle, value); } + } + + /// + /// Number of keys between restart points for delta encoding of keys. + /// This parameter can be changed dynamically. + /// Most clients should leave this parameter alone. + /// + /// Default: 16 + /// + public int RestartInterval + { + set { LevelDBInterop.leveldb_options_set_block_restart_interval(Handle, value); } + } + + /// + /// Compress blocks using the specified compression algorithm. + /// This parameter can be changed dynamically. + /// + /// Default: kSnappyCompression, which gives lightweight but fast compression. + /// + /// Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz: + /// ~200-500MB/s compression + /// ~400-800MB/s decompression + /// Note that these speeds are significantly faster than most + /// persistent storage speeds, and therefore it is typically never + /// worth switching to kNoCompression. Even if the input data is + /// incompressible, the kSnappyCompression implementation will + /// efficiently detect that and will switch to uncompressed mode. + /// + public CompressionLevel CompressionLevel + { + set { LevelDBInterop.leveldb_options_set_compression(Handle, (int)value); } + } + + public int FilterPolicy + { + set { LevelDBInterop.leveldb_options_set_filter_policy(Handle, LevelDBInterop.leveldb_filterpolicy_create_bloom(value)); } + } + + protected override void FreeUnManagedObjects() + { + if (Handle != default) + LevelDBInterop.leveldb_options_destroy(Handle); + } + + #region "managed wrappers -- must be detached upon successful DB.Create" + + public LevelDB.Env _Env { get; set; } + + public LevelDB.Cache _Cache { get; set; } + + public LevelDB.Comparator _Comparator { get; set; } + + #endregion + } +} diff --git a/src/Plugins/LevelDBStore/PinnedSafeHandle.cs b/src/Plugins/LevelDBStore/PinnedSafeHandle.cs new file mode 100644 index 0000000000..37b1429407 --- /dev/null +++ b/src/Plugins/LevelDBStore/PinnedSafeHandle.cs @@ -0,0 +1,52 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// PinnedSafeHandle.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.Runtime.InteropServices; + +namespace LevelDB +{ + internal class PinnedSafeHandle : SafeHandle + where T : struct + { + private GCHandle pinnedRawData; + + public PinnedSafeHandle(T[] arr) + : base(default(IntPtr), true) + { + pinnedRawData = GCHandle.Alloc(arr, GCHandleType.Pinned); + + // initialize handle last; ensure we only free initialized GCHandles. + handle = pinnedRawData.AddrOfPinnedObject(); + } + + public Ptr Ptr + { + get { return (Ptr)handle; } + } + + public override bool IsInvalid + { + get { return handle == default(IntPtr); } + } + + protected override bool ReleaseHandle() + { + if (handle != default(IntPtr)) + { + pinnedRawData.Free(); + handle = default(IntPtr); + } + return true; + } + } +} diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs b/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs index 9c676e8a7f..947981dfb6 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs @@ -9,7 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Data.LevelDB; +using LevelDB; using Neo.Persistence; using System; using System.Linq; @@ -28,7 +28,7 @@ public LevelDBStore() public IStore GetStore(string path) { if (Environment.CommandLine.Split(' ').Any(p => p == "/repair" || p == "--repair")) - DB.Repair(path, Options.Default); + DB.Repair(Options.Default, path); return new Store(path); } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index 0b0a63b885..2632546274 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -9,61 +9,49 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using LevelDB; using Neo.IO.Data.LevelDB; using Neo.Persistence; using System.Collections.Generic; -using LSnapshot = Neo.IO.Data.LevelDB.Snapshot; namespace Neo.Plugins.Storage { internal class Snapshot : ISnapshot { - private readonly DB db; - private readonly LSnapshot snapshot; - private readonly ReadOptions options; - private readonly WriteBatch batch; + private readonly DB _db; + private readonly SnapShot _snapshot; + private readonly WriteBatch _batch; + private readonly ReadOptions _readOptions; public Snapshot(DB db) { - this.db = db; - snapshot = db.GetSnapshot(); - options = new ReadOptions { FillCache = false, Snapshot = snapshot }; - batch = new WriteBatch(); + _db = db; + _snapshot = db.CreateSnapshot(); + _batch = new(); + _readOptions = new ReadOptions { FillCache = false, Snapshot = _snapshot }; } - public void Commit() - { - db.Write(WriteOptions.Default, batch); - } + public void Commit() => + _db.Write(_batch); - public void Delete(byte[] key) - { - batch.Delete(key); - } + public void Delete(byte[] key) => + _batch.Delete(key); - public void Dispose() - { - snapshot.Dispose(); - } + public void Dispose() => + _snapshot.Dispose(); - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) - { - return db.Seek(options, prefix, direction, (k, v) => (k, v)); - } + public void Put(byte[] key, byte[] value) => + _batch.Put(key, value); - public void Put(byte[] key, byte[] value) - { - batch.Put(key, value); - } + public bool Contains(byte[] key) => + _db.Contains(key, _readOptions); - public bool Contains(byte[] key) - { - return db.Contains(options, key); - } + public byte[] TryGet(byte[] key) => + _db.Get(key, _readOptions); - public byte[] TryGet(byte[] key) - { - return db.Get(options, key); - } + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) => + direction == SeekDirection.Forward + ? _db.Seek(prefix, new ReadOptions { FillCache = false, }) + : _db.SeekPrev(prefix, new ReadOptions { FillCache = false, }); } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs index 27b12a8b64..87994b9829 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -9,59 +9,66 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using LevelDB; using Neo.IO.Data.LevelDB; using Neo.Persistence; +using System.Collections; using System.Collections.Generic; namespace Neo.Plugins.Storage { - internal class Store : IStore + internal class Store : IStore, IEnumerable> { - private readonly DB db; + private readonly DB _db; - public Store(string path) + public Store(string dir) { - db = DB.Open(path, new Options { CreateIfMissing = true, FilterPolicy = Native.leveldb_filterpolicy_create_bloom(15) }); + _db = new(dir, new() + { + CreateIfMissing = true, + // Keep whole blockchain open plus future files + // at lease up to block index 10_000_000 + MaxOpenFiles = 4096, + FilterPolicy = 10, + CompressionLevel = CompressionLevel.SnappyCompression, + }); } - public void Delete(byte[] key) + public Store(string dir, Options options) { - db.Delete(WriteOptions.Default, key); + _db = new DB(dir, options); } - public void Dispose() - { - db.Dispose(); - } + public void Dispose() => + _db.Dispose(); - public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) - { - return db.Seek(ReadOptions.Default, prefix, direction, (k, v) => (k, v)); - } + public ISnapshot GetSnapshot() => + new Snapshot(_db); - public ISnapshot GetSnapshot() - { - return new Snapshot(db); - } + public void Delete(byte[] key) => + _db.Delete(key, WriteOptions.Default); - public void Put(byte[] key, byte[] value) - { - db.Put(WriteOptions.Default, key, value); - } + public void Put(byte[] key, byte[] value) => + _db.Put(key, value, WriteOptions.Default); - public void PutSync(byte[] key, byte[] value) - { - db.Put(WriteOptions.SyncWrite, key, value); - } + public void PutSync(byte[] key, byte[] value) => + _db.Put(key, value, WriteOptions.SyncWrite); - public bool Contains(byte[] key) - { - return db.Contains(ReadOptions.Default, key); - } + public bool Contains(byte[] key) => + _db.Contains(key, ReadOptions.Default); - public byte[] TryGet(byte[] key) - { - return db.Get(ReadOptions.Default, key); - } + public byte[] TryGet(byte[] key) => + _db.Get(key, ReadOptions.Default); + + public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) => + direction == SeekDirection.Forward + ? _db.Seek(prefix, ReadOptions.Default) + : _db.SeekPrev(prefix, ReadOptions.Default); + + public IEnumerator> GetEnumerator() => + _db.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); } } diff --git a/src/Plugins/LevelDBStore/ReadOptions.cs b/src/Plugins/LevelDBStore/ReadOptions.cs new file mode 100644 index 0000000000..c5b73825db --- /dev/null +++ b/src/Plugins/LevelDBStore/ReadOptions.cs @@ -0,0 +1,62 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// ReadOptions.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 +{ + /// + /// Options that control read operations. + /// + public class ReadOptions : LevelDBHandle + { + public static readonly ReadOptions Default = new ReadOptions(); + + public ReadOptions() + { + Handle = LevelDBInterop.leveldb_readoptions_create(); + } + + /// + /// If true, all data read from underlying storage will be + /// verified against corresponding checksums. + /// + public bool VerifyCheckSums + { + set { LevelDBInterop.leveldb_readoptions_set_verify_checksums(Handle, value ? (byte)1 : (byte)0); } + } + + /// + /// Should the data read for this iteration be cached in memory? + /// Callers may wish to set this field to false for bulk scans. + /// Default: true + /// + public bool FillCache + { + set { LevelDBInterop.leveldb_readoptions_set_fill_cache(Handle, value ? (byte)1 : (byte)0); } + } + + /// + /// If "snapshot" is provides, read as of the supplied snapshot + /// (which must belong to the DB that is being read and which must + /// not have been released). + /// If "snapshot" is not set, use an implicit + /// snapshot of the state at the beginning of this read operation. + /// + public SnapShot Snapshot + { + set { LevelDBInterop.leveldb_readoptions_set_snapshot(Handle, value.Handle); } + } + + protected override void FreeUnManagedObjects() + { + LevelDBInterop.leveldb_readoptions_destroy(Handle); + } + } +} diff --git a/src/Plugins/LevelDBStore/SnapShot.cs b/src/Plugins/LevelDBStore/SnapShot.cs new file mode 100644 index 0000000000..14af56cf9f --- /dev/null +++ b/src/Plugins/LevelDBStore/SnapShot.cs @@ -0,0 +1,45 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// SnapShot.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 LevelDB +{ + /// + /// A Snapshot is an immutable object and can therefore be safely + /// accessed from multiple threads without any external synchronization. + /// + public class SnapShot : LevelDBHandle + { + // pointer to parent so that we can call ReleaseSnapshot(this) when disposed + public WeakReference _parent; // as DB + + internal SnapShot(IntPtr handle, DB parent) + { + Handle = handle; + _parent = new WeakReference(parent); + } + + internal SnapShot(IntPtr handle) + { + Handle = handle; + _parent = new WeakReference(null); + } + + protected override void FreeUnManagedObjects() + { + if (_parent.IsAlive) + { + if (_parent.Target is DB parent) LevelDBInterop.leveldb_release_snapshot(parent.Handle, Handle); + } + } + } +} diff --git a/src/Plugins/LevelDBStore/WriteBatch.cs b/src/Plugins/LevelDBStore/WriteBatch.cs new file mode 100644 index 0000000000..ecd994610b --- /dev/null +++ b/src/Plugins/LevelDBStore/WriteBatch.cs @@ -0,0 +1,102 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// WriteBatch.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.Text; + +namespace LevelDB +{ + /// + /// WriteBatch holds a collection of updates to apply atomically to a DB. + /// + /// The updates are applied in the order in which they are added + /// to the WriteBatch. For example, the value of "key" will be "v3" + /// after the following batch is written: + /// + /// batch.Put("key", "v1"); + /// batch.Delete("key"); + /// batch.Put("key", "v2"); + /// batch.Put("key", "v3"); + /// + public class WriteBatch : LevelDBHandle + { + private readonly Encoding _encoding; + + public WriteBatch() + : this(DB.DefaultEncoding) + { + } + + public WriteBatch(Encoding encoding) + { + _encoding = encoding; + Handle = LevelDBInterop.leveldb_writebatch_create(); + } + + /// + /// Clear all updates buffered in this batch. + /// + public void Clear() + { + LevelDBInterop.leveldb_writebatch_clear(Handle); + } + + /// + /// Store the mapping "key->value" in the database. + /// + public WriteBatch Put(string key, string value) + { + return Put(_encoding.GetBytes(key), _encoding.GetBytes(value)); + } + + /// + /// Store the mapping "key->value" in the database. + /// + public WriteBatch Put(byte[] key, byte[] value) + { + LevelDBInterop.leveldb_writebatch_put(Handle, key, key.Length, value, value.Length); + return this; + } + + /// + /// If the database contains a mapping for "key", erase it. + /// Else do nothing. + /// + public WriteBatch Delete(string key) + { + return Delete(_encoding.GetBytes(key)); + } + + /// + /// If the database contains a mapping for "key", erase it. + /// Else do nothing. + /// + public WriteBatch Delete(byte[] key) + { + LevelDBInterop.leveldb_writebatch_delete(Handle, key, key.Length); + return this; + } + + /// + /// Support for iterating over a batch. + /// + public void Iterate(object state, Action put, Action deleted) + { + LevelDBInterop.leveldb_writebatch_iterate(Handle, state, put, deleted); + } + + protected override void FreeUnManagedObjects() + { + LevelDBInterop.leveldb_writebatch_destroy(Handle); + } + + } +} diff --git a/src/Plugins/LevelDBStore/WriteOptions.cs b/src/Plugins/LevelDBStore/WriteOptions.cs new file mode 100644 index 0000000000..b6542a0b65 --- /dev/null +++ b/src/Plugins/LevelDBStore/WriteOptions.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// WriteOptions.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 +{ + /// + /// Options that control write operations. + /// + public class WriteOptions : LevelDBHandle + { + public static readonly WriteOptions Default = new WriteOptions(); + public static readonly WriteOptions SyncWrite = new WriteOptions { Sync = true }; + + public WriteOptions() + { + Handle = LevelDBInterop.leveldb_writeoptions_create(); + } + + /// + /// If true, the write will be flushed from the operating system + /// buffer cache (by calling WritableFile::Sync()) before the write + /// is considered complete. If this flag is true, writes will be + /// slower. + /// + /// If this flag is false, and the machine crashes, some recent + /// writes may be lost. Note that if it is just the process that + /// crashes (i.e., the machine does not reboot), no writes will be + /// lost even if sync==false. + /// + /// In other words, a DB write with sync==false has similar + /// crash semantics as the "write()" system call. A DB write + /// with sync==true has similar crash semantics to a "write()" + /// system call followed by "fsync()". + /// + public bool Sync + { + set { LevelDBInterop.leveldb_writeoptions_set_sync(Handle, value ? (byte)1 : (byte)0); } + } + + protected override void FreeUnManagedObjects() + { + LevelDBInterop.leveldb_writeoptions_destroy(Handle); + } + } +}