From 8add00d5b8fe98323cc2f3a24f2508d8458fdeb8 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 8 Jul 2024 09:24:03 -0400 Subject: [PATCH 1/4] Added `LevelDBHandle` --- .../LevelDBStore/IO/Data/LevelDB/DB.cs | 48 ++---- .../LevelDBStore/IO/Data/LevelDB/Helper.cs | 4 +- .../LevelDBStore/IO/Data/LevelDB/Iterator.cs | 41 ++--- .../IO/Data/LevelDB/LevelDBHandle.cs | 60 +++++++ .../LevelDBStore/IO/Data/LevelDB/Native.cs | 150 ++++++++++-------- .../LevelDBStore/IO/Data/LevelDB/Options.cs | 10 +- .../IO/Data/LevelDB/ReadOptions.cs | 2 +- .../LevelDBStore/IO/Data/LevelDB/Snapshot.cs | 8 +- .../IO/Data/LevelDB/WriteBatch.cs | 57 +++++-- .../IO/Data/LevelDB/WriteOptions.cs | 4 +- .../LevelDBStore/Plugins/Storage/Store.cs | 2 +- 11 files changed, 231 insertions(+), 155 deletions(-) create mode 100644 src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs index 60e0e24e5a..9703e7cbe3 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs @@ -9,60 +9,44 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; +using Neo.Plugins.Storage.IO.Data.LevelDB; using System.IO; namespace Neo.IO.Data.LevelDB { - public class DB : IDisposable + public class DB : LevelDBHandle { - private IntPtr handle; - - /// - /// Return true if haven't got valid handle - /// - public bool IsDisposed => handle == IntPtr.Zero; - - private DB(IntPtr handle) + public DB(nint handle) { - this.handle = handle; - } - - public void Dispose() - { - if (handle != IntPtr.Zero) - { - Native.leveldb_close(handle); - handle = IntPtr.Zero; - } + Handle = handle; } public void Delete(WriteOptions options, byte[] key) { - Native.leveldb_delete(handle, options.handle, key, (UIntPtr)key.Length, out IntPtr error); + Native.leveldb_delete(Handle, options.handle, key, key.Length, out var 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); + var value = Native.leveldb_get(Handle, options.handle, key, key.Length, out var length, out var error); try { NativeHelper.CheckError(error); - return value.ToByteArray(length); + return value.ToByteArray((nuint)length); } finally { - if (value != IntPtr.Zero) Native.leveldb_free(value); + if (value != nint.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); + var value = Native.leveldb_get(Handle, options.handle, key, key.Length, out _, out var error); NativeHelper.CheckError(error); - if (value != IntPtr.Zero) + if (value != nint.Zero) { Native.leveldb_free(value); return true; @@ -73,12 +57,12 @@ public bool Contains(ReadOptions options, byte[] key) public Snapshot GetSnapshot() { - return new Snapshot(handle); + return new Snapshot(Handle); } public Iterator NewIterator(ReadOptions options) { - return new Iterator(Native.leveldb_create_iterator(handle, options.handle)); + return new Iterator(Native.leveldb_create_iterator(Handle, options.handle)); } public static DB Open(string name) @@ -88,26 +72,26 @@ public static DB Open(string name) public static DB Open(string name, Options options) { - IntPtr handle = Native.leveldb_open(options.handle, Path.GetFullPath(name), out IntPtr error); + var handle = Native.leveldb_open(options.handle, Path.GetFullPath(name), out var 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); + Native.leveldb_put(Handle, options.handle, key, key.Length, value, value.Length, out var error); NativeHelper.CheckError(error); } public static void Repair(string name, Options options) { - Native.leveldb_repair_db(options.handle, Path.GetFullPath(name), out IntPtr error); + Native.leveldb_repair_db(options.handle, Path.GetFullPath(name), out var error); NativeHelper.CheckError(error); } public void Write(WriteOptions options, WriteBatch write_batch) { - Native.leveldb_write(handle, options.handle, write_batch.handle, out IntPtr error); + Native.leveldb_write(Handle, options.handle, write_batch.Handle, out var error); NativeHelper.CheckError(error); } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs index 2ac3a0005f..8cf6b910af 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs @@ -52,9 +52,9 @@ public static IEnumerable FindRange(this DB db, ReadOptions options, byte[ } } - internal static byte[] ToByteArray(this IntPtr data, UIntPtr length) + internal static byte[] ToByteArray(this nint data, UIntPtr length) { - if (data == IntPtr.Zero) return null; + if (data == nint.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 index 6c025380fb..e66fd61ec3 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs @@ -9,78 +9,67 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; +using Neo.Plugins.Storage.IO.Data.LevelDB; namespace Neo.IO.Data.LevelDB { - public class Iterator : IDisposable + public class Iterator : LevelDBHandle { - private IntPtr handle; - - internal Iterator(IntPtr handle) + internal Iterator(nint handle) { - this.handle = handle; + Handle = handle; } private void CheckError() { - Native.leveldb_iter_get_error(handle, out IntPtr error); + Native.leveldb_iter_get_error(Handle, out nint 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); + var key = Native.leveldb_iter_key(Handle, out var length); CheckError(); - return key.ToByteArray(length); + return key.ToByteArray((nuint)length); } public void Next() { - Native.leveldb_iter_next(handle); + Native.leveldb_iter_next(Handle); CheckError(); } public void Prev() { - Native.leveldb_iter_prev(handle); + Native.leveldb_iter_prev(Handle); CheckError(); } public void Seek(byte[] target) { - Native.leveldb_iter_seek(handle, target, (UIntPtr)target.Length); + Native.leveldb_iter_seek(Handle, target, target.Length); } public void SeekToFirst() { - Native.leveldb_iter_seek_to_first(handle); + Native.leveldb_iter_seek_to_first(Handle); } public void SeekToLast() { - Native.leveldb_iter_seek_to_last(handle); + Native.leveldb_iter_seek_to_last(Handle); } public bool Valid() { - return Native.leveldb_iter_valid(handle); + return Native.leveldb_iter_valid(Handle); } public byte[] Value() { - IntPtr value = Native.leveldb_iter_value(handle, out UIntPtr length); + var value = Native.leveldb_iter_value(Handle, out var length); CheckError(); - return value.ToByteArray(length); + return value.ToByteArray((nuint)length); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs new file mode 100644 index 0000000000..8716819932 --- /dev/null +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs @@ -0,0 +1,60 @@ +// 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 Neo.Plugins.Storage.IO.Data.LevelDB +{ + /// + /// Base class for all LevelDB objects + /// + public abstract class LevelDBHandle : IDisposable + { + public nint 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 != nint.Zero) + { + FreeUnManagedObjects(); + Handle = nint.Zero; + } + _disposed = true; + } + } + + ~LevelDBHandle() + { + Dispose(false); + } + } +} diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs index acf8fa82b9..69e49b280a 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs @@ -25,56 +25,65 @@ public static class Native { #region Logger [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger); + public static extern nint leveldb_logger_create(nint /* Action */ logger); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_logger_destroy(IntPtr /* logger*/ option); + public static extern void leveldb_logger_destroy(nint /* 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); + public static extern nint leveldb_open(nint /* Options*/ options, string name, out nint error); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_close(IntPtr /*DB */ db); + public static extern void leveldb_close(nint /*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); + public static extern void leveldb_put(nint /* DB */ db, nint /* WriteOptions*/ options, byte[] key, nint keylen, byte[] val, nint vallen, out nint 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); + public static extern void leveldb_put(nint /* DB */ db, nint /* WriteOptions*/ options, ref int key, nint keylen, int[] val, nint vallen, out nint 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); + public static extern void leveldb_delete(nint /* DB */ db, nint /* WriteOptions*/ options, byte[] key, nint keylen, out nint 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); + public static extern void leveldb_write(nint /* DB */ db, nint /* WriteOptions*/ options, nint /* WriteBatch */ batch, out nint errptr); + + [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern nint leveldb_get(nint /* DB */ db, nint /* ReadOptions*/ options, byte[] key, nint keylen, out nint vallen, out nint errptr); + + [DllImport("libleveldb", CallingConvention = CallingConvention.Cdecl)] + public static extern nint leveldb_get(nint /* DB */ db, nint /* ReadOptions*/ options, ref int key, nint keylen, out nint vallen, out nint errptr); + + [DllImport("libleveldb", CallingConvention = CallingConvention.Cdecl)] + public static extern nint leveldb_get(nint /* DB */ db, nint /* ReadOptions*/ options, nint key, nint keylen, out nint vallen, out nint 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); + //static extern void leveldb_approximate_sizes(nint /* 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); + public static extern nint leveldb_create_iterator(nint /* DB */ db, nint /* ReadOption */ options); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_snapshot(IntPtr /* DB */ db); + public static extern nint leveldb_create_snapshot(nint /* DB */ db); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_release_snapshot(IntPtr /* DB */ db, IntPtr /* SnapShot*/ snapshot); + public static extern void leveldb_release_snapshot(nint /* DB */ db, nint /* SnapShot*/ snapshot); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_property_value(IntPtr /* DB */ db, string propname); + public static extern nint leveldb_property_value(nint /* 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); + public static extern void leveldb_repair_db(nint /* Options*/ options, string name, out nint error); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_destroy_db(IntPtr /* Options*/ options, string name, out IntPtr error); + public static extern void leveldb_destroy_db(nint /* Options*/ options, string name, out nint error); #region extensions [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_free(IntPtr /* void */ ptr); + public static extern void leveldb_free(nint /* void */ ptr); #endregion @@ -83,167 +92,168 @@ public static class Native #region Env [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_default_env(); + public static extern nint leveldb_create_default_env(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_env_destroy(IntPtr /*Env*/ cache); + public static extern void leveldb_env_destroy(nint /*Env*/ cache); #endregion #region Iterator [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_destroy(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_destroy(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool leveldb_iter_valid(IntPtr /*Iterator*/ iterator); + public static extern bool leveldb_iter_valid(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek_to_first(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_seek_to_first(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek_to_last(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_seek_to_last(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, byte[] key, UIntPtr length); + public static extern void leveldb_iter_seek(nint /*Iterator*/ iterator, byte[] key, int length); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_next(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_seek(nint /*Iterator*/ iterator, ref int key, int length); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_prev(IntPtr /*Iterator*/ iterator); + public static extern void leveldb_iter_next(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_iter_key(IntPtr /*Iterator*/ iterator, out UIntPtr length); + public static extern void leveldb_iter_prev(nint /*Iterator*/ iterator); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_iter_value(IntPtr /*Iterator*/ iterator, out UIntPtr length); + public static extern nint leveldb_iter_key(nint /*Iterator*/ iterator, out int length); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_get_error(IntPtr /*Iterator*/ iterator, out IntPtr error); + public static extern nint leveldb_iter_value(nint /*Iterator*/ iterator, out int length); + + [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern void leveldb_iter_get_error(nint /*Iterator*/ iterator, out nint error); #endregion #region Options [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_options_create(); + public static extern nint leveldb_options_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_destroy(IntPtr /*Options*/ options); + public static extern void leveldb_options_destroy(nint /*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); + public static extern void leveldb_options_set_create_if_missing(nint /*Options*/ options, 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); + public static extern void leveldb_options_set_error_if_exists(nint /*Options*/ options, 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); + public static extern void leveldb_options_set_info_log(nint /*Options*/ options, nint /* 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); + public static extern void leveldb_options_set_paranoid_checks(nint /*Options*/ options, bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_env(IntPtr /*Options*/ options, IntPtr /*Env*/ env); + public static extern void leveldb_options_set_env(nint /*Options*/ options, nint /*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); + public static extern void leveldb_options_set_write_buffer_size(nint /*Options*/ options, long size); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_max_open_files(IntPtr /*Options*/ options, int max); + public static extern void leveldb_options_set_max_open_files(nint /*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); + public static extern void leveldb_options_set_cache(nint /*Options*/ options, nint /*Cache*/ cache); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_block_size(IntPtr /*Options*/ options, UIntPtr size); + public static extern void leveldb_options_set_block_size(nint /*Options*/ options, long size); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_block_restart_interval(IntPtr /*Options*/ options, int interval); + public static extern void leveldb_options_set_block_restart_interval(nint /*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); + public static extern void leveldb_options_set_compression(nint /*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); + public static extern void leveldb_options_set_filter_policy(nint /*Options*/ options, nint /*FilterPolicy*/ policy); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_filter_policy(IntPtr /*Options*/ options, IntPtr /*FilterPolicy*/ policy); - + public static extern void leveldb_options_set_comparator(nint /*Options*/ options, nint /*Comparator*/ comparer); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_filterpolicy_create_bloom(int bits_per_key); + public static extern nint 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(); + public static extern nint leveldb_readoptions_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_destroy(IntPtr /*ReadOptions*/ options); + public static extern void leveldb_readoptions_destroy(nint /*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); + public static extern void leveldb_readoptions_set_verify_checksums(nint /*ReadOptions*/ options, 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); + public static extern void leveldb_readoptions_set_fill_cache(nint /*ReadOptions*/ options, bool o); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_snapshot(IntPtr /*ReadOptions*/ options, IntPtr /*SnapShot*/ snapshot); + public static extern void leveldb_readoptions_set_snapshot(nint /*ReadOptions*/ options, nint /*SnapShot*/ snapshot); #endregion #region WriteBatch [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_writebatch_create(); + public static extern nint leveldb_writebatch_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_destroy(IntPtr /* WriteBatch */ batch); + public static extern void leveldb_writebatch_destroy(nint /* WriteBatch */ batch); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_clear(IntPtr /* WriteBatch */ batch); + public static extern void leveldb_writebatch_clear(nint /* 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); + public static extern void leveldb_writebatch_put(nint /* WriteBatch */ batch, byte[] key, int keylen, byte[] val, int vallen); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_delete(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen); + public static extern void leveldb_writebatch_delete(nint /* WriteBatch */ batch, byte[] key, int 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); + public static extern void leveldb_writebatch_iterate(nint /* 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(); + public static extern nint leveldb_writeoptions_create(); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_destroy(IntPtr /*WriteOptions*/ options); + public static extern void leveldb_writeoptions_destroy(nint /*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); + public static extern void leveldb_writeoptions_set_sync(nint /*WriteOptions*/ options, bool o); #endregion #region Cache [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_cache_create_lru(int capacity); + public static extern nint leveldb_cache_create_lru(int capacity); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_cache_destroy(IntPtr /*Cache*/ cache); + public static extern void leveldb_cache_destroy(nint /*Cache*/ cache); #endregion #region Comparator [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr /* leveldb_comparator_t* */ + public static extern nint /* leveldb_comparator_t* */ leveldb_comparator_create( - IntPtr /* void* */ state, - IntPtr /* void (*)(void*) */ destructor, - IntPtr + nint /* void* */ state, + nint /* void (*)(void*) */ destructor, + nint /* int (*compare)(void*, const char* a, size_t alen, const char* b, size_t blen) */ compare, - IntPtr /* const char* (*)(void*) */ name); + nint /* const char* (*)(void*) */ name); [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_comparator_destroy(IntPtr /* leveldb_comparator_t* */ cmp); + public static extern void leveldb_comparator_destroy(nint /* leveldb_comparator_t* */ cmp); #endregion } @@ -251,9 +261,9 @@ public static extern IntPtr /* leveldb_comparator_t* */ internal static class NativeHelper { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CheckError(IntPtr error) + public static void CheckError(nint error) { - if (error != IntPtr.Zero) + if (error != nint.Zero) { string message = Marshal.PtrToStringAnsi(error); Native.leveldb_free(error); diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs index 989987eeed..ca82d3bd1d 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs @@ -9,14 +9,12 @@ // 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(); + internal readonly nint handle = Native.leveldb_options_create(); public bool CreateIfMissing { @@ -46,7 +44,7 @@ public int WriteBufferSize { set { - Native.leveldb_options_set_write_buffer_size(handle, (UIntPtr)value); + Native.leveldb_options_set_write_buffer_size(handle, value); } } @@ -62,7 +60,7 @@ public int BlockSize { set { - Native.leveldb_options_set_block_size(handle, (UIntPtr)value); + Native.leveldb_options_set_block_size(handle, value); } } @@ -82,7 +80,7 @@ public CompressionType Compression } } - public IntPtr FilterPolicy + public nint FilterPolicy { set { diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs index 727ae9f02a..fc3313b4f3 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs @@ -16,7 +16,7 @@ namespace Neo.IO.Data.LevelDB public class ReadOptions { public static readonly ReadOptions Default = new ReadOptions(); - internal readonly IntPtr handle = Native.leveldb_readoptions_create(); + internal readonly nint handle = Native.leveldb_readoptions_create(); public bool VerifyChecksums { diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs index 14280fbc8f..6f43c76166 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs @@ -15,9 +15,9 @@ namespace Neo.IO.Data.LevelDB { public class Snapshot : IDisposable { - internal IntPtr db, handle; + internal nint db, handle; - internal Snapshot(IntPtr db) + internal Snapshot(nint db) { this.db = db; handle = Native.leveldb_create_snapshot(db); @@ -25,10 +25,10 @@ internal Snapshot(IntPtr db) public void Dispose() { - if (handle != IntPtr.Zero) + if (handle != nint.Zero) { Native.leveldb_release_snapshot(db, handle); - handle = IntPtr.Zero; + handle = nint.Zero; } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs index ad82dad450..ec8c3065d3 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs @@ -9,32 +9,69 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Neo.Plugins.Storage.IO.Data.LevelDB; using System; namespace Neo.IO.Data.LevelDB { - public class WriteBatch + /// + /// 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 { - internal readonly IntPtr handle = Native.leveldb_writebatch_create(); - - ~WriteBatch() + public WriteBatch() { - Native.leveldb_writebatch_destroy(handle); + Handle = Native.leveldb_writebatch_create(); } + /// + /// Clear all updates buffered in this batch. + /// public void Clear() { - Native.leveldb_writebatch_clear(handle); + Native.leveldb_writebatch_clear(Handle); + } + + /// + /// Store the mapping "key->value" in the database. + /// + public WriteBatch Put(byte[] key, byte[] value) + { + Native.leveldb_writebatch_put(Handle, key, key.Length, value, value.Length); + return this; } - public void Delete(byte[] key) + /// + /// If the database contains a mapping for "key", erase it. + /// Else do nothing. + /// + public WriteBatch Delete(byte[] key) { - Native.leveldb_writebatch_delete(handle, key, (UIntPtr)key.Length); + Native.leveldb_writebatch_delete(Handle, key, key.Length); + return this; } - public void Put(byte[] key, byte[] value) + /// + /// Support for iterating over a batch. + /// + public void Iterate(object state, Action put, Action deleted) { - Native.leveldb_writebatch_put(handle, key, (UIntPtr)key.Length, value, (UIntPtr)value.Length); + Native.leveldb_writebatch_iterate(Handle, state, put, deleted); } + + protected override void FreeUnManagedObjects() + { + Native.leveldb_writebatch_destroy(Handle); + } + } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs index 48915ba480..4d23ec42b9 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs @@ -9,8 +9,6 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - namespace Neo.IO.Data.LevelDB { public class WriteOptions @@ -18,7 +16,7 @@ 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(); + internal readonly nint handle = Native.leveldb_writeoptions_create(); public bool Sync { diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs index 27b12a8b64..2c856afda8 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -31,7 +31,7 @@ public void Delete(byte[] key) public void Dispose() { - db.Dispose(); + ((System.IDisposable)db).Dispose(); } public IEnumerable<(byte[], byte[])> Seek(byte[] prefix, SeekDirection direction = SeekDirection.Forward) From 431609f329aab6a93d4eeb3e7773a670e36fcdd5 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 8 Jul 2024 10:01:28 -0400 Subject: [PATCH 2/4] Fixed store --- .../LevelDBStore/IO/Data/LevelDB/DB.cs | 262 +++++++++++++++--- .../LevelDBStore/IO/Data/LevelDB/Helper.cs | 53 ++-- .../LevelDBStore/IO/Data/LevelDB/Iterator.cs | 111 ++++++-- .../IO/Data/LevelDB/LevelDBException.cs | 2 +- .../IO/Data/LevelDB/LevelDBHandle.cs | 2 +- .../LevelDBStore/IO/Data/LevelDB/Native.cs | 8 +- .../LevelDBStore/IO/Data/LevelDB/Options.cs | 144 ++++++---- .../IO/Data/LevelDB/ReadOptions.cs | 56 ++-- .../LevelDBStore/IO/Data/LevelDB/Snapshot.cs | 30 +- .../IO/Data/LevelDB/WriteBatch.cs | 3 +- .../IO/Data/LevelDB/WriteOptions.cs | 41 ++- .../Plugins/Storage/LevelDBStore.cs | 4 +- .../LevelDBStore/Plugins/Storage/Snapshot.cs | 63 ++--- .../LevelDBStore/Plugins/Storage/Store.cs | 76 ++--- 14 files changed, 581 insertions(+), 274 deletions(-) diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs index 9703e7cbe3..9f3e34345c 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs @@ -9,44 +9,159 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Plugins.Storage.IO.Data.LevelDB; -using System.IO; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class DB : LevelDBHandle + /// + /// 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> { - public DB(nint handle) + static void Throw(IntPtr error) { - Handle = handle; + Throw(error, msg => new Exception(msg)); } - public void Delete(WriteOptions options, byte[] key) + static void Throw(IntPtr error, Func exception) { - Native.leveldb_delete(Handle, options.handle, key, key.Length, out var error); - NativeHelper.CheckError(error); + if (error != IntPtr.Zero) + { + try + { + var msg = Marshal.PtrToStringAnsi(error); + throw exception(msg); + } + finally + { + Native.leveldb_free(error); + } + } } - public byte[] Get(ReadOptions options, byte[] key) + /// + /// Open the database with the specified "name". + /// + public DB(string name, Options options) { - var value = Native.leveldb_get(Handle, options.handle, key, key.Length, out var length, out var error); - try - { - NativeHelper.CheckError(error); - return value.ToByteArray((nuint)length); - } - finally + Handle = Native.leveldb_open(options.Handle, name, out var error); + Throw(error, msg => new UnauthorizedAccessException(msg)); + } + + public void Close() + { + (this as IDisposable).Dispose(); + } + + /// + /// 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) + { + Native.leveldb_put(Handle, options.Handle, key, checked((IntPtr)key.LongLength), value, checked((IntPtr)value.LongLength), 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(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) + { + Native.leveldb_delete(Handle, options.Handle, key, checked((nint)key.LongLength), out var error); + Throw(error); + } + + public void Write(WriteBatch batch) + { + Write(batch, new WriteOptions()); + } + + public void Write(WriteBatch batch, WriteOptions options) + { + Native.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 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 = Native.leveldb_get(Handle, options.Handle, key, checked((IntPtr)key.LongLength), out var length, out var error); + Throw(error); + + if (v != IntPtr.Zero) { - if (value != nint.Zero) Native.leveldb_free(value); + 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 + { + Native.leveldb_free(v); + } } + return null; } - public bool Contains(ReadOptions options, byte[] key) + public bool Contains(byte[] key, ReadOptions options) { - var value = Native.leveldb_get(Handle, options.handle, key, key.Length, out _, out var error); - NativeHelper.CheckError(error); + var value = Native.leveldb_get(Handle, options.Handle, key, key.Length, out _, out var error); + Throw(error); - if (value != nint.Zero) + if (value != IntPtr.Zero) { Native.leveldb_free(value); return true; @@ -55,44 +170,109 @@ public bool Contains(ReadOptions options, byte[] key) return false; } - public Snapshot GetSnapshot() + /// + /// 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 Snapshot(Handle); + return new Iterator(Native.leveldb_create_iterator(Handle, options.Handle)); } - public Iterator NewIterator(ReadOptions options) + /// + /// 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 Iterator(Native.leveldb_create_iterator(Handle, options.handle)); + return new SnapShot(Native.leveldb_create_snapshot(Handle), this); } - public static DB Open(string name) + /// + /// 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) { - return Open(name, Options.Default); + string result = null; + var ptr = Native.leveldb_property_value(Handle, name); + if (ptr != IntPtr.Zero) + { + try + { + return Marshal.PtrToStringAnsi(ptr); + } + finally + { + Native.leveldb_free(ptr); + } + } + return result; } - public static DB Open(string name, Options options) + /// + /// 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) { - var handle = Native.leveldb_open(options.handle, Path.GetFullPath(name), out var error); - NativeHelper.CheckError(error); - return new DB(handle); + Native.leveldb_repair_db(options.Handle, name, out var error); + Throw(error); } - public void Put(WriteOptions options, byte[] key, byte[] value) + /// + /// Destroy the contents of the specified database. + /// Be very careful using this method. + /// + public static void Destroy(Options options, string name) { - Native.leveldb_put(Handle, options.handle, key, key.Length, value, value.Length, out var error); - NativeHelper.CheckError(error); + Native.leveldb_destroy_db(options.Handle, name, out var error); + Throw(error); } - public static void Repair(string name, Options options) + protected override void FreeUnManagedObjects() { - Native.leveldb_repair_db(options.handle, Path.GetFullPath(name), out var error); - NativeHelper.CheckError(error); + if (Handle != default) + Native.leveldb_close(Handle); + } + + 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(); + } } - public void Write(WriteOptions options, WriteBatch write_batch) + IEnumerator IEnumerable.GetEnumerator() { - Native.leveldb_write(Handle, options.handle, write_batch.Handle, out var error); - NativeHelper.CheckError(error); + return GetEnumerator(); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs index 8cf6b910af..52e7b43407 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs @@ -9,55 +9,34 @@ // 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 +namespace Neo.IO.Storage.LevelDB { public static class Helper { - public static IEnumerable Seek(this DB db, ReadOptions options, byte[] prefix, SeekDirection direction, Func resultSelector) + public static IEnumerable<(byte[], byte[])> Seek(this DB db, byte[] prefix, ReadOptions options) { - 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 + using var it = db.CreateIterator(options); - 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()); - } + for (it.Seek(prefix); it.IsValid(); it.Next()) + yield return new(it.Key(), it.Value()); } - public static IEnumerable FindRange(this DB db, ReadOptions options, byte[] startKey, byte[] endKey, Func resultSelector) + public static IEnumerable<(byte[], byte[])> SeekPrev(this DB db, byte[] prefix, ReadOptions options) { - 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()); - } - } + using var it = db.CreateIterator(options); - internal static byte[] ToByteArray(this nint data, UIntPtr length) - { - if (data == nint.Zero) return null; - byte[] buffer = new byte[(int)length]; - Marshal.Copy(data, buffer, 0, (int)length); - return buffer; + 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/Iterator.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs index e66fd61ec3..5c307964dd 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs @@ -9,10 +9,14 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Plugins.Storage.IO.Data.LevelDB; +using System; +using System.Runtime.InteropServices; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { + /// + /// An iterator yields a sequence of key/value pairs from a database. + /// public class Iterator : LevelDBHandle { internal Iterator(nint handle) @@ -20,56 +24,117 @@ internal Iterator(nint handle) Handle = handle; } - private void CheckError() + /// + /// 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() { - Native.leveldb_iter_get_error(Handle, out nint error); - NativeHelper.CheckError(error); + return Native.leveldb_iter_valid(Handle); } - public byte[] Key() + /// + /// Position at the first key in the source. + /// The iterator is Valid() after this call iff the source is not empty. + /// + public void SeekToFirst() { - var key = Native.leveldb_iter_key(Handle, out var length); - CheckError(); - return key.ToByteArray((nuint)length); + Native.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() + { + Native.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) + { + Native.leveldb_iter_seek(Handle, key, key.Length); + 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() { Native.leveldb_iter_next(Handle); - CheckError(); + 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() { Native.leveldb_iter_prev(Handle); - CheckError(); + Throw(); } - public void Seek(byte[] target) + /// + /// Return the key for the current entry. + /// REQUIRES: Valid() + /// + public byte[] Key() { - Native.leveldb_iter_seek(Handle, target, target.Length); + var key = Native.leveldb_iter_key(Handle, out var length); + Throw(); + + var bytes = new byte[length]; + Marshal.Copy(key, bytes, 0, length); + return bytes; } - public void SeekToFirst() + /// + /// Return the value for the current entry. + /// REQUIRES: Valid() + /// + public byte[] Value() { - Native.leveldb_iter_seek_to_first(Handle); + var value = Native.leveldb_iter_value(Handle, out var length); + Throw(); + + var bytes = new byte[length]; + Marshal.Copy(value, bytes, 0, length); + return bytes; } - public void SeekToLast() + /// + /// If an error has occurred, throw it. + /// + void Throw() { - Native.leveldb_iter_seek_to_last(Handle); + Throw(msg => new Exception(msg)); } - public bool Valid() + /// + /// If an error has occurred, throw it. + /// + void Throw(Func exception) { - return Native.leveldb_iter_valid(Handle); + Native.leveldb_iter_get_error(Handle, out var error); + if (error != IntPtr.Zero) throw exception(Marshal.PtrToStringAnsi(error)); } - public byte[] Value() + protected override void FreeUnManagedObjects() { - var value = Native.leveldb_iter_value(Handle, out var length); - CheckError(); - return value.ToByteArray((nuint)length); + Native.leveldb_iter_destroy(Handle); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs index c9cca42070..ba900a655e 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBException.cs @@ -11,7 +11,7 @@ using System.Data.Common; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { public class LevelDBException : DbException { diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs index 8716819932..e2b8b338c4 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/LevelDBHandle.cs @@ -11,7 +11,7 @@ using System; -namespace Neo.Plugins.Storage.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { /// /// Base class for all LevelDB objects diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs index 69e49b280a..1c49679089 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs @@ -13,12 +13,12 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public enum CompressionType : byte + public enum CompressionType : int { - kNoCompression = 0x0, - kSnappyCompression = 0x1 + NoCompression = 0, + SnappyCompression = 1, } public static class Native diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs index ca82d3bd1d..9bceee9c05 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Options.cs @@ -9,88 +9,138 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class Options + /// + /// 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(); - internal readonly nint handle = Native.leveldb_options_create(); + public static readonly Options Default = new(); + public Options() + { + Handle = Native.leveldb_options_create(); + } + + /// + /// If true, the database will be created if it is missing. + /// public bool CreateIfMissing { - set - { - Native.leveldb_options_set_create_if_missing(handle, value); - } + set { Native.leveldb_options_set_create_if_missing(Handle, value); } } + /// + /// If true, an error is raised if the database already exists. + /// public bool ErrorIfExists { - set - { - Native.leveldb_options_set_error_if_exists(handle, value); - } + set { Native.leveldb_options_set_error_if_exists(Handle, value); } } + /// + /// 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 - { - Native.leveldb_options_set_paranoid_checks(handle, value); - } + set { Native.leveldb_options_set_paranoid_checks(Handle, value); } } - public int WriteBufferSize + // 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 - { - Native.leveldb_options_set_write_buffer_size(handle, value); - } + set { Native.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 - { - Native.leveldb_options_set_max_open_files(handle, value); - } + set { Native.leveldb_options_set_max_open_files(Handle, value); } } - public int BlockSize + /// + /// 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 - { - Native.leveldb_options_set_block_size(handle, value); - } + set { Native.leveldb_options_set_block_size(Handle, value); } } - public int BlockRestartInterval + /// + /// 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 - { - Native.leveldb_options_set_block_restart_interval(handle, value); - } + set { Native.leveldb_options_set_block_restart_interval(Handle, value); } } - public CompressionType Compression + /// + /// 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 CompressionType CompressionLevel { - set - { - Native.leveldb_options_set_compression(handle, value); - } + set { Native.leveldb_options_set_compression(Handle, value); } } - public nint FilterPolicy + public int FilterPolicy { - set - { - Native.leveldb_options_set_filter_policy(handle, value); - } + set { Native.leveldb_options_set_filter_policy(Handle, Native.leveldb_filterpolicy_create_bloom(value)); } } - ~Options() + protected override void FreeUnManagedObjects() { - Native.leveldb_options_destroy(handle); + if (Handle != default) + 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 index fc3313b4f3..89aab9e77c 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/ReadOptions.cs @@ -9,42 +9,54 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using System; - -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class ReadOptions + /// + /// Options that control read operations. + /// + public class ReadOptions : LevelDBHandle { - public static readonly ReadOptions Default = new ReadOptions(); - internal readonly nint handle = Native.leveldb_readoptions_create(); + public static readonly ReadOptions Default = new(); + + public ReadOptions() + { + Handle = Native.leveldb_readoptions_create(); + } - public bool VerifyChecksums + /// + /// If true, all data read from underlying storage will be + /// verified against corresponding checksums. + /// + public bool VerifyCheckSums { - set - { - Native.leveldb_readoptions_set_verify_checksums(handle, value); - } + set { Native.leveldb_readoptions_set_verify_checksums(Handle, value); } } + /// + /// 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 - { - Native.leveldb_readoptions_set_fill_cache(handle, value); - } + set { Native.leveldb_readoptions_set_fill_cache(Handle, value); } } - public Snapshot Snapshot + /// + /// 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 - { - Native.leveldb_readoptions_set_snapshot(handle, value.handle); - } + set { Native.leveldb_readoptions_set_snapshot(Handle, value.Handle); } } - ~ReadOptions() + protected override void FreeUnManagedObjects() { - Native.leveldb_readoptions_destroy(handle); + 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 index 6f43c76166..4a36f83b1a 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Snapshot.cs @@ -11,24 +11,34 @@ using System; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class Snapshot : IDisposable + /// + /// A Snapshot is an immutable object and can therefore be safely + /// accessed from multiple threads without any external synchronization. + /// + public class SnapShot : LevelDBHandle { - internal nint db, handle; + // pointer to parent so that we can call ReleaseSnapshot(this) when disposed + public WeakReference _parent; // as DB - internal Snapshot(nint db) + internal SnapShot(nint handle, DB parent) { - this.db = db; - handle = Native.leveldb_create_snapshot(db); + Handle = handle; + _parent = new WeakReference(parent); } - public void Dispose() + internal SnapShot(nint handle) { - if (handle != nint.Zero) + Handle = handle; + _parent = new WeakReference(null); + } + + protected override void FreeUnManagedObjects() + { + if (_parent.IsAlive) { - Native.leveldb_release_snapshot(db, handle); - handle = nint.Zero; + if (_parent.Target is DB parent) Native.leveldb_release_snapshot(parent.Handle, Handle); } } } diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs index ec8c3065d3..09823fabb8 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteBatch.cs @@ -9,10 +9,9 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.Plugins.Storage.IO.Data.LevelDB; using System; -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { /// /// WriteBatch holds a collection of updates to apply atomically to a DB. diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs index 4d23ec42b9..7997ed9581 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/WriteOptions.cs @@ -9,26 +9,45 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -namespace Neo.IO.Data.LevelDB +namespace Neo.IO.Storage.LevelDB { - public class WriteOptions + /// + /// 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 static readonly WriteOptions Default = new(); + public static readonly WriteOptions SyncWrite = new() { Sync = true }; - internal readonly nint handle = Native.leveldb_writeoptions_create(); + public WriteOptions() + { + Handle = Native.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 - { - Native.leveldb_writeoptions_set_sync(handle, value); - } + set { Native.leveldb_writeoptions_set_sync(Handle, value); } } - ~WriteOptions() + protected override void FreeUnManagedObjects() { - Native.leveldb_writeoptions_destroy(handle); + Native.leveldb_writeoptions_destroy(Handle); } } } diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs b/src/Plugins/LevelDBStore/Plugins/Storage/LevelDBStore.cs index 9c676e8a7f..489bc5e5b0 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 Neo.IO.Storage.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..1006adb83b 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -9,61 +9,48 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Data.LevelDB; +using Neo.IO.Storage.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 2c856afda8..42b9b706e3 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Store.cs @@ -9,59 +9,65 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -using Neo.IO.Data.LevelDB; +using Neo.IO.Storage.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 = CompressionType.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() - { - ((System.IDisposable)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(); } } From c6118a26f1969232e709e29ebf53b82ce6737f24 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 8 Jul 2024 10:38:58 -0400 Subject: [PATCH 3/4] Fixed Exception type --- src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs | 2 +- .../LevelDBStore/IO/Data/LevelDB/Native.cs | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs index 9f3e34345c..531cc542a8 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs @@ -24,7 +24,7 @@ public class DB : LevelDBHandle, IEnumerable> { static void Throw(IntPtr error) { - Throw(error, msg => new Exception(msg)); + Throw(error, msg => new LevelDBException(msg)); } static void Throw(IntPtr error, Func exception) diff --git a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs index 1c49679089..4605f0bdcc 100644 --- a/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs +++ b/src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs @@ -10,7 +10,6 @@ // modifications are permitted. using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Neo.IO.Storage.LevelDB @@ -257,18 +256,4 @@ public static extern nint /* leveldb_comparator_t* */ #endregion } - - internal static class NativeHelper - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CheckError(nint error) - { - if (error != nint.Zero) - { - string message = Marshal.PtrToStringAnsi(error); - Native.leveldb_free(error); - throw new LevelDBException(message); - } - } - } } From 8e9cd4aa22242093cc7bca9122c84d743c5ab8ac Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Mon, 29 Jul 2024 23:25:48 -0400 Subject: [PATCH 4/4] Fixed `snapshot` thread safe batching and fixed bug with `seek` --- .../LevelDBStore/Plugins/Storage/Snapshot.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs index 1006adb83b..b9655b06f6 100644 --- a/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs +++ b/src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs @@ -22,6 +22,8 @@ internal class Snapshot : ISnapshot private readonly WriteBatch _batch; private readonly ReadOptions _readOptions; + private readonly object _lock = new(); + public Snapshot(DB db) { _db = db; @@ -30,17 +32,30 @@ public Snapshot(DB db) _readOptions = new ReadOptions { FillCache = false, Snapshot = _snapshot }; } - public void Commit() => - _db.Write(_batch); + public void Commit() + { + lock (_lock) + _db.Write(_batch); + } - public void Delete(byte[] key) => - _batch.Delete(key); + public void Delete(byte[] key) + { + lock (_lock) + _batch.Delete(key); + } - public void Dispose() => + public void Dispose() + { _snapshot.Dispose(); + _batch.Dispose(); + _readOptions.Dispose(); + } - public void Put(byte[] key, byte[] value) => - _batch.Put(key, value); + public void Put(byte[] key, byte[] value) + { + lock (_lock) + _batch.Put(key, value); + } public bool Contains(byte[] key) => _db.Contains(key, _readOptions); @@ -50,7 +65,7 @@ public byte[] TryGet(byte[] 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, }); + ? _db.Seek(prefix, _readOptions) + : _db.SeekPrev(prefix, _readOptions); } }