Skip to content

Commit

Permalink
Nullable leveldb (#3685)
Browse files Browse the repository at this point in the history
* Nullable leveldb

* Add Seek

* Fix warnings

* Unify

* Update Helper.cs
  • Loading branch information
shargon authored Jan 22, 2025
1 parent 23f6b06 commit f07fcc4
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 33 deletions.
9 changes: 6 additions & 3 deletions src/Neo/Persistence/IReadOnlyStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

#nullable enable

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Neo.Persistence
{
Expand All @@ -25,23 +28,23 @@ public interface IReadOnlyStore
/// <param name="keyOrPrefix">The key(i.e. start key) or prefix to be sought.</param>
/// <param name="direction">The direction of seek.</param>
/// <returns>An enumerator containing all the entries after (Forward) or before(Backward) seeking.</returns>
IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction);
IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[]? keyOrPrefix, SeekDirection direction);

/// <summary>
/// Reads a specified entry from the database.
/// </summary>
/// <param name="key">The key of the entry.</param>
/// <returns>The data of the entry. Or <see langword="null"/> if it doesn't exist.</returns>
[Obsolete("use TryGet(byte[] key, [NotNullWhen(true)] out byte[]? value) instead.")]
byte[] TryGet(byte[] key);
byte[]? TryGet(byte[] key);

/// <summary>
/// Reads a specified entry from the database.
/// </summary>
/// <param name="key">The key of the entry.</param>
/// <param name="value">The data of the entry.</param>
/// <returns><see langword="true"/> if the entry exists; otherwise, <see langword="false"/>.</returns>
bool TryGet(byte[] key, out byte[] value);
bool TryGet(byte[] key, [NotNullWhen(true)] out byte[]? value);

/// <summary>
/// Determines whether the database contains the specified entry.
Expand Down
11 changes: 5 additions & 6 deletions src/Neo/Persistence/MemoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ public void Delete(byte[] key)
_innerData.TryRemove(key, out _);
}

public void Dispose()
{
}
public void Dispose() { }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ISnapshot GetSnapshot()
Expand All @@ -50,13 +48,14 @@ public void Put(byte[] key, byte[] value)
}

/// <inheritdoc/>
public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward)
public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[]? keyOrPrefix, SeekDirection direction = SeekDirection.Forward)
{
if (direction == SeekDirection.Backward && keyOrPrefix?.Length == 0) yield break;
keyOrPrefix ??= [];
if (direction == SeekDirection.Backward && keyOrPrefix.Length == 0) yield break;

var comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse;
IEnumerable<KeyValuePair<byte[], byte[]>> records = _innerData;
if (keyOrPrefix?.Length > 0)
if (keyOrPrefix.Length > 0)
records = records.Where(p => comparer.Compare(p.Key, keyOrPrefix) >= 0);
records = records.OrderBy(p => p.Key, comparer);
foreach (var pair in records)
Expand Down
6 changes: 2 additions & 4 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/DB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

#nullable enable

using System.Collections;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -49,7 +47,7 @@ public void Delete(WriteOptions options, byte[] key)
/// If the database contains an entry for "key" return the value,
/// otherwise return null.
/// </summary>
public byte[] Get(ReadOptions options, byte[] key)
public byte[]? Get(ReadOptions options, byte[] key)
{
var value = Native.leveldb_get(Handle, options.Handle, key, (nuint)key.Length, out var length, out var error);
try
Expand Down Expand Up @@ -131,7 +129,7 @@ public IEnumerator<KeyValuePair<byte[], byte[]>> GetEnumerator()
{
using var iterator = CreateIterator(ReadOptions.Default);
for (iterator.SeekToFirst(); iterator.Valid(); iterator.Next())
yield return new KeyValuePair<byte[], byte[]>(iterator.Key(), iterator.Value());
yield return new KeyValuePair<byte[], byte[]>(iterator.Key()!, iterator.Value()!);
}

IEnumerator IEnumerable.GetEnumerator() =>
Expand Down
14 changes: 7 additions & 7 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ namespace Neo.IO.Storage.LevelDB
{
public static class Helper
{
public static IEnumerable<(byte[], byte[])> Seek(this DB db, ReadOptions options, byte[] keyOrPrefix, SeekDirection direction)
public static IEnumerable<(byte[], byte[])> Seek(this DB db, ReadOptions options, byte[]? keyOrPrefix, SeekDirection direction)
{
if (keyOrPrefix == null) keyOrPrefix = [];
keyOrPrefix ??= [];

using Iterator it = db.CreateIterator(options);
using var it = db.CreateIterator(options);
if (direction == SeekDirection.Forward)
{
for (it.Seek(keyOrPrefix); it.Valid(); it.Next())
yield return new(it.Key(), it.Value());
yield return new(it.Key()!, it.Value()!);
}
else
{
Expand All @@ -38,14 +38,14 @@ public static class Helper
it.Prev();

for (; it.Valid(); it.Prev())
yield return new(it.Key(), it.Value());
yield return new(it.Key()!, it.Value()!);
}
}

internal static byte[] ToByteArray(this IntPtr data, UIntPtr length)
internal static byte[]? ToByteArray(this IntPtr data, UIntPtr length)
{
if (data == IntPtr.Zero) return null;
byte[] buffer = new byte[(int)length];
var buffer = new byte[(int)length];
Marshal.Copy(data, buffer, 0, (int)length);
return buffer;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Iterator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected override void FreeUnManagedObjects()
/// Return the key for the current entry.
/// REQUIRES: Valid()
/// </summary>
public byte[] Key()
public byte[]? Key()
{
var key = Native.leveldb_iter_key(Handle, out var length);
CheckError();
Expand Down Expand Up @@ -89,7 +89,7 @@ public bool Valid()
return Native.leveldb_iter_valid(Handle);
}

public byte[] Value()
public byte[]? Value()
{
var value = Native.leveldb_iter_value(Handle, out var length);
CheckError();
Expand Down
23 changes: 19 additions & 4 deletions src/Plugins/LevelDBStore/IO/Data/LevelDB/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ public enum CompressionType : byte
public static class Native
{
#region Logger

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern nint leveldb_logger_create(nint /* Action<string> */ logger);

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void leveldb_logger_destroy(nint /* logger*/ option);

#endregion

#region DB

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern nint leveldb_open(nint /* Options*/ options, string name, out nint error);

Expand Down Expand Up @@ -77,19 +80,20 @@ public static class Native
public static extern void leveldb_free(nint /* void */ ptr);

#endregion


#endregion

#region Env

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern nint leveldb_create_default_env();

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
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(nint /*Iterator*/ iterator);

Expand Down Expand Up @@ -120,9 +124,11 @@ public static class Native

[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 nint leveldb_options_create();

Expand Down Expand Up @@ -170,9 +176,11 @@ public static class Native

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
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 nint leveldb_readoptions_create();

Expand All @@ -187,9 +195,11 @@ public static class Native

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
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 nint leveldb_writebatch_create();

Expand All @@ -207,9 +217,11 @@ public static class Native

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void leveldb_writebatch_iterate(nint /* WriteBatch */ batch, object state, Action<object, byte[], int, byte[], int> put, Action<object, byte[], int> deleted);

#endregion

#region WriteOptions

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern nint leveldb_writeoptions_create();

Expand All @@ -218,14 +230,17 @@ public static class Native

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void leveldb_writeoptions_set_sync(nint /*WriteOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o);

#endregion

#region Cache

[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
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(nint /*Cache*/ cache);

#endregion

#region Comparator
Expand Down Expand Up @@ -255,9 +270,9 @@ public static void CheckError(nint error)
{
if (error != nint.Zero)
{
string message = Marshal.PtrToStringAnsi(error);
var message = Marshal.PtrToStringAnsi(error);
Native.leveldb_free(error);
throw new LevelDBException(message);
throw new LevelDBException(message ?? string.Empty);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Plugins/LevelDBStore/LevelDBStore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<PackageId>Neo.Plugins.Storage.LevelDBStore</PackageId>
<RootNamespace>Neo.Plugins.Storage</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OutputPath>../../../bin/$(PackageId)</OutputPath>
</PropertyGroup>

Expand Down
9 changes: 5 additions & 4 deletions src/Plugins/LevelDBStore/Plugins/Storage/Snapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Neo.Persistence;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using LSnapshot = Neo.IO.Storage.LevelDB.Snapshot;

namespace Neo.Plugins.Storage
Expand Down Expand Up @@ -70,7 +71,7 @@ public void Dispose()
}

/// <inheritdoc/>
public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward)
public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[]? keyOrPrefix, SeekDirection direction = SeekDirection.Forward)
{
return _db.Seek(_readOptions, keyOrPrefix, direction);
}
Expand All @@ -80,12 +81,12 @@ public bool Contains(byte[] key)
return _db.Contains(_readOptions, key);
}

public byte[] TryGet(byte[] key)
public byte[]? TryGet(byte[] key)
{
return _db.Get(_readOptions, key);
}

public bool TryGet(byte[] key, out byte[] value)
public bool TryGet(byte[] key, [NotNullWhen(true)] out byte[]? value)
{
value = _db.Get(_readOptions, key);
return value != null;
Expand All @@ -95,7 +96,7 @@ public IEnumerator<KeyValuePair<byte[], byte[]>> GetEnumerator()
{
using var iterator = _db.CreateIterator(_readOptions);
for (iterator.SeekToFirst(); iterator.Valid(); iterator.Next())
yield return new KeyValuePair<byte[], byte[]>(iterator.Key(), iterator.Value());
yield return new KeyValuePair<byte[], byte[]>(iterator.Key()!, iterator.Value()!);
}

IEnumerator IEnumerable.GetEnumerator() =>
Expand Down
7 changes: 4 additions & 3 deletions src/Plugins/LevelDBStore/Plugins/Storage/Store.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Neo.Persistence;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Neo.Plugins.Storage
{
Expand Down Expand Up @@ -58,17 +59,17 @@ public void PutSync(byte[] key, byte[] value) =>
public bool Contains(byte[] key) =>
_db.Contains(ReadOptions.Default, key);

public byte[] TryGet(byte[] key) =>
public byte[]? TryGet(byte[] key) =>
_db.Get(ReadOptions.Default, key);

public bool TryGet(byte[] key, out byte[] value)
public bool TryGet(byte[] key, [NotNullWhen(true)] out byte[]? value)
{
value = _db.Get(ReadOptions.Default, key);
return value != null;
}

/// <inheritdoc/>
public IEnumerable<(byte[], byte[])> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) =>
public IEnumerable<(byte[], byte[])> Seek(byte[]? keyOrPrefix, SeekDirection direction = SeekDirection.Forward) =>
_db.Seek(ReadOptions.Default, keyOrPrefix, direction);

public IEnumerator<KeyValuePair<byte[], byte[]>> GetEnumerator() =>
Expand Down

0 comments on commit f07fcc4

Please sign in to comment.