diff --git a/RocksDbSharp/ColumnFamilyOptions.cs b/RocksDbSharp/ColumnFamilyOptions.cs index 4be9690..f2ca999 100644 --- a/RocksDbSharp/ColumnFamilyOptions.cs +++ b/RocksDbSharp/ColumnFamilyOptions.cs @@ -298,16 +298,16 @@ private static MergeOperator GetMergeOperatorFromPtr(IntPtr getMergeOperatorPtr) return getMergeOperator(); } - private unsafe static IntPtr MergeOperator_PartialMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength) + private unsafe static IntPtr MergeOperator_PartialMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) { var mergeOperator = GetMergeOperatorFromPtr((*((MergeOperatorState*)state)).GetMergeOperatorPtr); - return mergeOperator.PartialMerge(key, keyLength, operandsList, operandsListLength, numOperands, success, newValueLength); + return mergeOperator.PartialMerge(key, keyLength, operandsList, operandsListLength, numOperands, out success, out newValueLength); } - private unsafe static IntPtr MergeOperator_FullMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength) + private unsafe static IntPtr MergeOperator_FullMerge(IntPtr state, IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) { var mergeOperator = GetMergeOperatorFromPtr((*((MergeOperatorState*)state)).GetMergeOperatorPtr); - return mergeOperator.FullMerge(key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, success, newValueLength); + return mergeOperator.FullMerge(key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, out success, out newValueLength); } private unsafe static void MergeOperator_DeleteValue(IntPtr state, IntPtr value, UIntPtr valueLength) diff --git a/RocksDbSharp/MergeOperator.cs b/RocksDbSharp/MergeOperator.cs index 19a9261..f1ac495 100644 --- a/RocksDbSharp/MergeOperator.cs +++ b/RocksDbSharp/MergeOperator.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Text; namespace RocksDbSharp @@ -7,8 +8,8 @@ namespace RocksDbSharp public interface MergeOperator { string Name { get; } - IntPtr PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); - IntPtr FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); + IntPtr PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength); + IntPtr FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength); void DeleteValue(IntPtr value, UIntPtr valueLength); } @@ -22,38 +23,28 @@ public static class MergeOperators /// This is called to combine two-merge operands (if possible) /// /// The key that's associated with this merge operation - /// - /// the sequence of merge operations to apply, front() first - /// - /// + /// the sequence of merge operations to apply, front() first /// Client is responsible for filling the merge result here - /// /// - public delegate IntPtr PartialMergeFunc(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); + public delegate byte[] PartialMergeFunc(byte[] key, byte[][] operands, out bool success); + /// /// Gives the client a way to express the read -> modify -> write semantics. /// Called when a Put/Delete is the *existing_value (or nullptr) /// /// The key that's associated with this merge operation. - /// /// null indicates that the key does not exist before this op - /// - /// the sequence of merge operations to apply, front() first. - /// - /// + /// the sequence of merge operations to apply, front() first. /// Client is responsible for filling the merge result here - /// /// - public delegate IntPtr FullMergeFunc(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); - public delegate void DeleteValueFunc(IntPtr value, UIntPtr valueLength); + public delegate byte[] FullMergeFunc(byte[] key, byte[] existingValue, byte[][] operands, out bool success); public static MergeOperator Create( string name, PartialMergeFunc partialMerge, - FullMergeFunc fullMerge, - DeleteValueFunc deleteValue) + FullMergeFunc fullMerge) { - return new MergeOperatorImpl(name, partialMerge, fullMerge, deleteValue); + return new MergeOperatorImpl(name, partialMerge, fullMerge); } private class MergeOperatorImpl : MergeOperator @@ -61,24 +52,83 @@ private class MergeOperatorImpl : MergeOperator public string Name { get; } private PartialMergeFunc PartialMerge { get; } private FullMergeFunc FullMerge { get; } - private DeleteValueFunc DeleteValue { get; } - public MergeOperatorImpl(string name, PartialMergeFunc partialMerge, FullMergeFunc fullMerge, DeleteValueFunc deleteValue) + public MergeOperatorImpl(string name, PartialMergeFunc partialMerge, FullMergeFunc fullMerge) { Name = name; PartialMerge = partialMerge; FullMerge = fullMerge; - DeleteValue = deleteValue; } - IntPtr MergeOperator.PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength) - => PartialMerge(key, keyLength, operandsList, operandsListLength, numOperands, success, newValueLength); + IntPtr MergeOperator.PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) + { + var _key = new byte[(uint)keyLength]; + Marshal.Copy(key, _key, 0, _key.Length); + + var _operandsList = new IntPtr[numOperands]; + Marshal.Copy(operandsList, _operandsList, 0, _operandsList.Length); + + var _operandsListLength = new long[numOperands]; + Marshal.Copy(operandsListLength, _operandsListLength, 0, _operandsListLength.Length); + + var operands = new byte[numOperands][]; + for (int i = 0; i < numOperands; i++) + { + var operand = new byte[_operandsListLength[i]]; + Marshal.Copy(_operandsList[i], operand, 0, operand.Length); + operands[i] = operand; + } + + var value = PartialMerge(_key, operands, out var _success); - IntPtr MergeOperator.FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength) - => FullMerge(key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, success, newValueLength); + var ret = Marshal.AllocHGlobal(value.Length); + Marshal.Copy(value, 0, ret, value.Length); + newValueLength = (IntPtr)value.Length; + + success = (IntPtr)Convert.ToInt32(_success); + + return ret; + } + + IntPtr MergeOperator.FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, out IntPtr success, out IntPtr newValueLength) + { + var _key = new byte[(uint)keyLength]; + Marshal.Copy(key, _key, 0, _key.Length); + + byte[] _existingValue = null; + if (existingValue != IntPtr.Zero) + { + _existingValue = new byte[(uint)existingValueLength]; + Marshal.Copy(existingValue, _existingValue, 0, _existingValue.Length); + } + + var _operandsList = new IntPtr[numOperands]; + Marshal.Copy(operandsList, _operandsList, 0, _operandsList.Length); + + var _operandsListLength = new long[numOperands]; + Marshal.Copy(operandsListLength, _operandsListLength, 0, _operandsListLength.Length); + + var operands = new byte[numOperands][]; + + for (int i = 0; i < numOperands; i++) + { + var operand = new byte[_operandsListLength[i]]; + Marshal.Copy(_operandsList[i], operand, 0, operand.Length); + operands[i] = operand; + } + + var value = FullMerge(_key, _existingValue, operands, out var _success); + + var ret = Marshal.AllocHGlobal(value.Length); + Marshal.Copy(value, 0, ret, value.Length); + newValueLength = (IntPtr)value.Length; + + success = (IntPtr)Convert.ToInt32(_success); + + return ret; + } - void MergeOperator.DeleteValue(IntPtr value, UIntPtr valueLength) - => DeleteValue(value, valueLength); + void MergeOperator.DeleteValue(IntPtr value, UIntPtr valueLength) => Marshal.FreeHGlobal(value); } } -} +} \ No newline at end of file diff --git a/RocksDbSharp/Native.cs b/RocksDbSharp/Native.cs index 22034bd..d7e72b5 100644 --- a/RocksDbSharp/Native.cs +++ b/RocksDbSharp/Native.cs @@ -107,8 +107,8 @@ namespace RocksDbSharp public delegate char_ptr CreateFilterDelegate(void_ptr p0, const_char_ptr_const_ptr key_array, const_size_t_ptr key_length_array, int num_keys, size_t_ptr filter_length); public delegate char KeyMayMatchDelegate(void_ptr p0, const_char_ptr key, size_t length, const_char_ptr filter, size_t filter_length); public delegate void DeleteFilterDelegate(void_ptr p0, const_char_ptr filter, size_t filter_length); - public delegate char_ptr FullMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr existing_value, size_t existing_value_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, unsigned_char_ptr success, size_t_ptr new_value_length); - public delegate char_ptr PartialMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, unsigned_char_ptr success, size_t_ptr new_value_length); + public delegate char_ptr FullMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr existing_value, size_t existing_value_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, out unsigned_char_ptr success, out size_t_ptr new_value_length); + public delegate char_ptr PartialMergeDelegate(void_ptr p0, const_char_ptr key, size_t key_length, const_char_ptr_const_ptr operands_list, const_size_t_ptr operands_list_length, int num_operands, out unsigned_char_ptr success, out size_t_ptr new_value_length); public delegate void DeleteValueDelegate(void_ptr p0, const_char_ptr value, size_t value_length); public delegate char_ptr TransformDelegate(void_ptr p0, const_char_ptr key, size_t length, size_t_ptr dst_length); public delegate char InDomainDelegate(void_ptr p0, const_char_ptr key, size_t length); diff --git a/tests/RocksDbSharpTest/FunctionalTests.cs b/tests/RocksDbSharpTest/FunctionalTests.cs index d90568e..1d78176 100644 --- a/tests/RocksDbSharpTest/FunctionalTests.cs +++ b/tests/RocksDbSharpTest/FunctionalTests.cs @@ -429,9 +429,8 @@ public void FunctionalTest() .SetCreateIfMissing(true) .SetMergeOperator(MergeOperators.Create( name: "test-merge-operator", - partialMerge: (key, keyLength, operandsList, operandsListLength, numOperands, success, newValueLength) => IntPtr.Zero, - fullMerge: (key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, success, newValueLength) => IntPtr.Zero, - deleteValue: (value, valueLength) => { } + partialMerge: PartialMerge, + fullMerge: FullMerge )); GC.Collect(); using (var db = RocksDbSharp.RocksDb.Open(optsTest, dbname)) @@ -474,5 +473,19 @@ class IntegerStringComparator : StringComparatorBase public override int Compare(string a, string b) => Comparer(long.TryParse(a, out long avalue) ? avalue : 0, long.TryParse(b, out long bvalue) ? bvalue : 0); } + + private static byte[] PartialMerge(byte[] key, byte[][] operands, out bool success) + { + success = true; + + return operands[operands.Length - 1]; + } + + private static byte[] FullMerge(byte[] key, byte[] existingValue, byte[][] operands, out bool success) + { + success = true; + + return operands[operands.Length - 1]; + } } }