├── .gitignore ├── LICENSE ├── README.md ├── RocksDbNative ├── NativePackage.cs ├── RocksDbNative.csproj └── build │ ├── net40 │ └── RocksDbNative.targets │ └── net45 │ └── RocksDbNative.targets ├── RocksDbSharp.sln ├── RocksDbSharp ├── AutoNativeImport.cs ├── BinaryComparer.cs ├── BlockBasedTableOptions.cs ├── BloomFilterPolicy.cs ├── Cache.cs ├── Checkpoint.cs ├── ColumnFamilies.cs ├── ColumnFamilyHandle.cs ├── ColumnFamilyOptions.cs ├── Comparator.cs ├── DbOptions.cs ├── Env.cs ├── EnvOptions.cs ├── IngestExternalFileOptions.cs ├── Iterator.cs ├── MergeOperator.cs ├── Native.Load.cs ├── Native.Marshaled.cs ├── Native.Wrap.cs ├── Native.cs ├── Options.cs ├── ReadOptions.cs ├── RocksDb.cs ├── RocksDbException.cs ├── RocksDbSharp.csproj ├── RocksDbSharpException.cs ├── SliceTransform.cs ├── Snapshot.cs ├── SstFileWriter.cs ├── Transitional.cs ├── WriteBatch.cs ├── WriteBatchWithIndex.cs └── WriteOptions.cs ├── Versions.targets.include ├── examples ├── ColumnFamilyExample │ ├── ColumnFamilyExample.csproj │ └── Program.cs ├── PrefixExample │ ├── PrefixExample.csproj │ └── Program.cs ├── SimpleExampleHighLevel │ ├── Program.cs │ └── SimpleExampleHighLevel.csproj └── SimpleExampleLowLevel │ ├── Program.cs │ └── SimpleExampleLowLevel.csproj ├── nuget ├── build-and-pack.cmd └── instructions.txt └── tests └── RocksDbSharpTest ├── FunctionalTests.cs ├── RocksDbSharpTest.csproj └── TestBinaryComparer.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | [Bb]in/ 15 | [Oo]bj/ 16 | 17 | # NuGet Packages 18 | *.nupkg 19 | # The packages folder can be ignored because of Package Restore 20 | **/packages/* 21 | # except build/, which is used as an MSBuild target. 22 | !**/packages/build/ 23 | # Uncomment if necessary however generally it will be regenerated when needed 24 | #!**/packages/repositories.config 25 | 26 | # MSTest test Results 27 | [Tt]est[Rr]esult*/ 28 | [Bb]uild[Ll]og.* 29 | 30 | *_i.c 31 | *_p.c 32 | *.ilk 33 | *.meta 34 | *.obj 35 | *.pch 36 | *.pdb 37 | *.pgc 38 | *.pgd 39 | *.rsp 40 | *.sbr 41 | *.tlb 42 | *.tli 43 | *.tlh 44 | *.tmp 45 | *.tmp_proj 46 | *.log 47 | *.vspscc 48 | *.vssscc 49 | .builds 50 | *.pidb 51 | *.log 52 | *.scc 53 | 54 | # OS generated files # 55 | .DS_Store* 56 | ehthumbs.db 57 | Icon? 58 | Thumbs.db 59 | 60 | # Visual C++ cache files 61 | ipch/ 62 | *.aps 63 | *.ncb 64 | *.opensdf 65 | *.sdf 66 | *.cachefile 67 | 68 | # Visual Studio profiler 69 | *.psess 70 | *.vsp 71 | *.vspx 72 | 73 | # Guidance Automation Toolkit 74 | *.gpState 75 | 76 | # ReSharper is a .NET coding add-in 77 | _ReSharper*/ 78 | *.[Rr]e[Ss]harper 79 | 80 | # TeamCity is a build add-in 81 | _TeamCity* 82 | 83 | # DotCover is a Code Coverage Tool 84 | *.dotCover 85 | 86 | # NCrunch 87 | *.ncrunch* 88 | .*crunch*.local.xml 89 | 90 | # Installshield output folder 91 | [Ee]xpress/ 92 | 93 | # DocProject is a documentation generator add-in 94 | DocProject/buildhelp/ 95 | DocProject/Help/*.HxT 96 | DocProject/Help/*.HxC 97 | DocProject/Help/*.hhc 98 | DocProject/Help/*.hhk 99 | DocProject/Help/*.hhp 100 | DocProject/Help/Html2 101 | DocProject/Help/html 102 | 103 | # Click-Once directory 104 | publish/ 105 | 106 | # Publish Web Output 107 | *.Publish.xml 108 | 109 | # Windows Azure Build Output 110 | csx 111 | *.build.csdef 112 | 113 | # Windows Store app package directory 114 | AppPackages/ 115 | 116 | # Others 117 | sql/ 118 | *.Cache 119 | ClientBin/ 120 | [Ss]tyle[Cc]op.* 121 | ~$* 122 | *~ 123 | *.dbmdl 124 | *.[Pp]ublish.xml 125 | *.pfx 126 | *.publishsettings 127 | modulesbin/ 128 | tempbin/ 129 | 130 | # EPiServer Site file (VPP) 131 | AppData/ 132 | 133 | # RIA/Silverlight projects 134 | Generated_Code/ 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | UpgradeLog*.htm 142 | 143 | # vim 144 | *.txt~ 145 | *.swp 146 | *.swo 147 | 148 | # svn 149 | .svn 150 | 151 | # SQL Server files 152 | **/App_Data/*.mdf 153 | **/App_Data/*.ldf 154 | **/App_Data/*.sdf 155 | 156 | 157 | #LightSwitch generated files 158 | GeneratedArtifacts/ 159 | _Pvt_Extensions/ 160 | ModelManifest.xml 161 | 162 | # ========================= 163 | # Windows detritus 164 | # ========================= 165 | 166 | # Windows image file caches 167 | Thumbs.db 168 | ehthumbs.db 169 | 170 | # Folder config file 171 | Desktop.ini 172 | 173 | # Recycle Bin used on file shares 174 | $RECYCLE.BIN/ 175 | 176 | # Mac desktop service store files 177 | .DS_Store 178 | 179 | # SASS Compiler cache 180 | .sass-cache 181 | 182 | # Visual Studio 2014 CTP 183 | **/*.sln.ide 184 | .vs/ 185 | 186 | amd64/rocksdb.dll 187 | native/**/*.dll 188 | native/ 189 | native*/ 190 | *.xz 191 | *.zip 192 | *.userprefs 193 | rocksdbversion 194 | version 195 | 196 | *.orig 197 | .ionide/ 198 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (Simplified BSD License) 2 | 3 | Copyright (c) 2015, Warren Falk 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rocksdb-sharp 2 | 3 | ## RocksDb for C# # 4 | RocksDB is a key-value database with a log-structured-merge design, optimized for flash and RAM storage, 5 | which can be tuned to balance write-, read-, and space-amplification factors. 6 | 7 | RocksDB is developed by Facebook and is based on LevelDB. 8 | For more information about RocksDB, visit [RocksDB](http://rocksdb.org/) and on [GitHub](https://github.com/facebook/rocksdb) 9 | 10 | This library provides C# bindings for rocksdb, implemented as a wrapper for the native rocksdb DLL (unmanaged C++) via the rocksdb C API. 11 | 12 | This is a multi-level binding, 13 | providing direct access to the C API functions (low level) 14 | plus some helper wrappers on those to aid in marshaling and exception handling (mid level) 15 | plus an idiomatic C# class hierarchy for ease of use (high level). 16 | 17 | ### Example (High Level) 18 | 19 | ```csharp 20 | var options = new DbOptions() 21 | .SetCreateIfMissing(true); 22 | using (var db = RocksDb.Open(options, path)) 23 | { 24 | // Using strings below, but can also use byte arrays for both keys and values 25 | // much care has been taken to minimize buffer copying 26 | db.Put("key", "value"); 27 | string value = db.Get("key"); 28 | db.Remove("key"); 29 | } 30 | ``` 31 | ### Usage 32 | 33 | #### Using NuGet: 34 | 35 | ``` 36 | install-package RocksDbSharp 37 | ``` 38 | 39 | This will install the managed library which will use the unmanaged library installed on 40 | the machine at runtime. If you do not want to install the managed library, you can 41 | include it by additionally installing the "RocksDbNative" package. 42 | 43 | ``` 44 | install-package RocksDbNative 45 | ``` 46 | 47 | ### Requirements 48 | 49 | On Linux and Mac, the snappy library (libsnappy) must be installed. 50 | 51 | ## Caveats and Warnings: 52 | 53 | ### 64-bit only (Especially on Windows) 54 | RocksDb is supported only in 64-bit mode. Although I contributed a fix that allows it to compile in 32-bit mode, this is untested and unsupported, may not work at all, and almost certainly will have at least some major issues and should not be attempted in production. 55 | 56 | ## Extras 57 | 58 | Windows Build Script: Building rocksdb for Windows is hard; this project contains a build script to make it easy. 59 | 60 | ## Building Native Library 61 | 62 | Pre-built native binaries can be downloaded from the releases page. You may also build them yourself. 63 | 64 | (This is only a high level (and low level) wrapper on the unmanaged library and is not a managed C# port of the rocksdb database. The difficulty of a potential managed port library almost certainly far exceeds any usefulness of such a thing and so is not planned and will probably never exist). 65 | 66 | This is now buildable on Windows thanks to the Bing team at Microsoft who are actively using rocksdb. Rocksdb-sharp should work on any platform provided the native unmanaged library is available. 67 | 68 | ### Windows Native Build Instructions 69 | 70 | #### Prerequisities: 71 | * Git for Windows (specifically, the git bash environment) 72 | * CMake 73 | * Visual Studio 2017 (older versions may work but are not tested) 74 | 75 | #### Build Instructions: 76 | 1. Open "Developer Command Prompt for VS2017" 77 | 2. Run git's ```bash.exe``` 78 | 3. cd to the ```native-build``` folder within the repository 79 | 4. execute ```./build-rocksdb.sh``` 80 | 81 | This will create a rocksdb.dll and copy it to the where the .sln file is expecting it to be. (If you only need to run this in Windows, you can remove the references to the other two platform binaries from the .sln) 82 | 83 | ### Linux Native Build Instructions 84 | 85 | 1. ```cd native-build``` 86 | 2. ```./build-rocksdb.sh``` 87 | 88 | ### Mac Native Build Instructions 89 | 90 | 1. ```cd native-build``` 91 | 2. ```./build-rocksdb.sh``` 92 | 93 | Note: On a Mac, although a change I contributed now allows RocksDb to compile in 32 bit, it is not supported and may not work. You should definitely only run 64-bit Mono to use RocksDb with Mono on Mac. 94 | 95 | ## TODO 96 | 97 | * Many of the less-commonly-used C-API functions imports are yet to be included. 98 | 99 | -------------------------------------------------------------------------------- /RocksDbNative/NativePackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public class NativePackage 8 | { 9 | public static string GetCodeBase() 10 | { 11 | #if NETSTANDARD1_6 12 | var assemblyLocation = typeof(NativePackage).GetTypeInfo().Assembly.Location; 13 | var assemblyDir = Path.GetDirectoryName(assemblyLocation); 14 | return 15 | Directory.Exists(Path.Combine(assemblyDir, "runtimes")) ? assemblyDir : 16 | Directory.Exists(Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(assemblyDir)), "runtimes")) ? Path.GetDirectoryName(Path.GetDirectoryName(assemblyDir)) : 17 | assemblyDir; 18 | #else 19 | return AppDomain.CurrentDomain.BaseDirectory; 20 | #endif 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /RocksDbNative/RocksDbNative.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | RocksDbNative 5 | netstandard1.6;net40;net45 6 | $(RocksDbVersion).$(RocksDbNativeBuild) 7 | $(Version) 8 | $(Version) 9 | Native RocksDb Binaries (Install RocksDbSharp package for managed wrapper) 10 | Warren Falk 11 | Warren Falk 12 | Warren Falk 13 | BSD-2-Clause 14 | https://github.com/warrenfalk/rocksdb-sharp 15 | http://rocksdb.org/static/logo.svg 16 | https://github.com/warrenfalk/rocksdb-sharp.git 17 | rocksdb leveldb embedded database 18 | Copyright 2016 19 | Native RocksDb Binaries (Install RocksDbSharp package for managed wraper) 20 | False 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 53 | true 54 | PreserveNewest 55 | 56 | 58 | 59 | 60 | true 61 | build/net40 62 | 63 | 64 | true 65 | build/net45 66 | 67 | 68 | -------------------------------------------------------------------------------- /RocksDbNative/build/net40/RocksDbNative.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | PreserveNewest 6 | runtimes\win-x64\native\rocksdb.dll 7 | false 8 | 9 | 10 | PreserveNewest 11 | runtimes\linux-x64\native\librocksdb.so 12 | false 13 | 14 | 15 | PreserveNewest 16 | runtimes\osx-x64\native\librocksdb.dylib 17 | false 18 | 19 | 20 | -------------------------------------------------------------------------------- /RocksDbNative/build/net45/RocksDbNative.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | PreserveNewest 6 | runtimes\win-x64\native\rocksdb.dll 7 | false 8 | 9 | 10 | PreserveNewest 11 | runtimes\linux-x64\native\librocksdb.so 12 | false 13 | 14 | 15 | PreserveNewest 16 | runtimes\osx-x64\native\librocksdb.dylib 17 | false 18 | 19 | 20 | -------------------------------------------------------------------------------- /RocksDbSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleExampleLowLevel", "examples\SimpleExampleLowLevel\SimpleExampleLowLevel.csproj", "{904017BB-5660-418B-B132-46EA6DF54788}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleExampleHighLevel", "examples\SimpleExampleHighLevel\SimpleExampleHighLevel.csproj", "{3504FBA2-3EB1-4B50-9E73-92E7923839B0}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrefixExample", "examples\PrefixExample\PrefixExample.csproj", "{8731668E-3F6B-4B6D-BC95-552804EDDB4D}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColumnFamilyExample", "examples\ColumnFamilyExample\ColumnFamilyExample.csproj", "{AF539614-596B-4271-B1BC-A96F58B22329}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RocksDbSharpTest", "tests\RocksDbSharpTest\RocksDbSharpTest.csproj", "{044901FD-A7BA-48A9-A5BE-8CB40A13CE02}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RocksDbSharp", "RocksDbSharp\RocksDbSharp.csproj", "{53056B3C-FAC0-41ED-9C5B-8BA41DF85F61}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RocksDbNative", "RocksDbNative\RocksDbNative.csproj", "{5DE4B734-CD28-435A-A552-257FDCCDE50A}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {904017BB-5660-418B-B132-46EA6DF54788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {904017BB-5660-418B-B132-46EA6DF54788}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {904017BB-5660-418B-B132-46EA6DF54788}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {904017BB-5660-418B-B132-46EA6DF54788}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {3504FBA2-3EB1-4B50-9E73-92E7923839B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {3504FBA2-3EB1-4B50-9E73-92E7923839B0}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {3504FBA2-3EB1-4B50-9E73-92E7923839B0}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {3504FBA2-3EB1-4B50-9E73-92E7923839B0}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {8731668E-3F6B-4B6D-BC95-552804EDDB4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {8731668E-3F6B-4B6D-BC95-552804EDDB4D}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {8731668E-3F6B-4B6D-BC95-552804EDDB4D}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {8731668E-3F6B-4B6D-BC95-552804EDDB4D}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {AF539614-596B-4271-B1BC-A96F58B22329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {AF539614-596B-4271-B1BC-A96F58B22329}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {AF539614-596B-4271-B1BC-A96F58B22329}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {AF539614-596B-4271-B1BC-A96F58B22329}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {044901FD-A7BA-48A9-A5BE-8CB40A13CE02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {044901FD-A7BA-48A9-A5BE-8CB40A13CE02}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {044901FD-A7BA-48A9-A5BE-8CB40A13CE02}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {044901FD-A7BA-48A9-A5BE-8CB40A13CE02}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {53056B3C-FAC0-41ED-9C5B-8BA41DF85F61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {53056B3C-FAC0-41ED-9C5B-8BA41DF85F61}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {53056B3C-FAC0-41ED-9C5B-8BA41DF85F61}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {53056B3C-FAC0-41ED-9C5B-8BA41DF85F61}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {5DE4B734-CD28-435A-A552-257FDCCDE50A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {5DE4B734-CD28-435A-A552-257FDCCDE50A}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {5DE4B734-CD28-435A-A552-257FDCCDE50A}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {5DE4B734-CD28-435A-A552-257FDCCDE50A}.Release|Any CPU.Build.0 = Release|Any CPU 54 | EndGlobalSection 55 | GlobalSection(SolutionProperties) = preSolution 56 | HideSolutionNode = FALSE 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /RocksDbSharp/AutoNativeImport.cs: -------------------------------------------------------------------------------- 1 | /* 2 | License: MIT - see license file at https://github.com/warrenfalk/auto-native-import/blob/master/LICENSE 3 | Author: Warren Falk 4 | */ 5 | using System; 6 | using System.IO; 7 | using System.Reflection; 8 | using System.Runtime.InteropServices; 9 | using System.Reflection.Emit; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using Transitional; 13 | using System.Linq.Expressions; 14 | 15 | namespace NativeImport 16 | { 17 | public static class Auto 18 | { 19 | /// 20 | /// Imports the library by name (without extensions) locating it based on platform. 21 | /// 22 | /// Use suppressUnload to prevent the dll from unloading at finalization, 23 | /// which can be useful if you need to call the imported functions in finalizers of 24 | /// other instances and can't predict in which order the finalization will occur 25 | /// 26 | /// 27 | /// 28 | /// true to prevent unloading on finalization 29 | /// 30 | public static T Import(string name, string version, bool suppressUnload = false) where T : class 31 | { 32 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 33 | return Importers.Import(Importers.Windows, name, version, suppressUnload); 34 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 35 | return Importers.Import(Importers.Posix, name, version, suppressUnload); 36 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 37 | return Importers.Import(Importers.Posix, name, version, suppressUnload); 38 | else 39 | return Importers.Import(Importers.Windows, name, version, suppressUnload); 40 | } 41 | } 42 | 43 | public interface INativeLibImporter 44 | { 45 | IntPtr LoadLibrary(string name); 46 | IntPtr GetProcAddress(IntPtr lib, string entryPoint); 47 | void FreeLibrary(IntPtr lib); 48 | string Translate(string name); 49 | object GetDelegate(IntPtr lib, string entryPoint, Type delegateType); 50 | } 51 | 52 | public static class Importers 53 | { 54 | private static Lazy WindowsShared { get; } = new Lazy(() => new WindowsImporter()); 55 | private static Lazy PosixShared { get; } = new Lazy(() => new PosixImporter()); 56 | 57 | public static INativeLibImporter Windows => WindowsShared.Value; 58 | public static INativeLibImporter Posix => PosixShared.Value; 59 | 60 | static object GetDelegate(INativeLibImporter importer, IntPtr lib, string entryPoint, Type delegateType) 61 | { 62 | IntPtr procAddress = importer.GetProcAddress(lib, entryPoint); 63 | if (procAddress == IntPtr.Zero) 64 | return null; 65 | var method = typeof(CurrentFramework).GetTypeInfo().GetMethod(nameof(CurrentFramework.GetDelegateForFunctionPointer)).MakeGenericMethod(delegateType); 66 | return method.Invoke(null, new object[] { procAddress }); 67 | } 68 | 69 | private class WindowsImporter : INativeLibImporter 70 | { 71 | [DllImport("kernel32.dll", EntryPoint = "LoadLibrary", SetLastError = true)] 72 | public static extern IntPtr WinLoadLibrary(string dllToLoad); 73 | 74 | [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")] 75 | public static extern IntPtr WinGetProcAddress(IntPtr hModule, string procedureName); 76 | 77 | [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")] 78 | public static extern bool WinFreeLibrary(IntPtr hModule); 79 | 80 | public IntPtr LoadLibrary(string path) 81 | { 82 | var result = WinLoadLibrary(path); 83 | if (result == IntPtr.Zero) 84 | throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); 85 | return result; 86 | } 87 | 88 | public IntPtr GetProcAddress(IntPtr lib, string entryPoint) 89 | { 90 | var address = WinGetProcAddress(lib, entryPoint); 91 | return address; 92 | } 93 | 94 | public object GetDelegate(IntPtr lib, string entryPoint, Type delegateType) 95 | => Importers.GetDelegate(this, lib, entryPoint, delegateType); 96 | 97 | public void FreeLibrary(IntPtr lib) 98 | { 99 | WinFreeLibrary(lib); 100 | } 101 | 102 | public string Translate(string name) 103 | { 104 | return name + ".dll"; 105 | } 106 | } 107 | 108 | private class PosixImporter : INativeLibImporter 109 | { 110 | public string LibraryExtension { get; } 111 | 112 | #pragma warning disable IDE1006 // Intentionally violating naming conventions because this is meant to match the library being loaded 113 | [DllImport("libdl")] 114 | private static extern IntPtr dlopen(String fileName, int flags); 115 | 116 | [DllImport("libdl")] 117 | private static extern IntPtr dlsym(IntPtr handle, String symbol); 118 | 119 | [DllImport("libdl")] 120 | private static extern int dlclose(IntPtr handle); 121 | 122 | [DllImport("libdl")] 123 | private static extern IntPtr dlerror(); 124 | 125 | [DllImport("libc")] 126 | private static extern int uname(IntPtr buf); 127 | #pragma warning restore IDE1006 128 | 129 | public PosixImporter() 130 | { 131 | var platform = GetPlatform(); 132 | if (platform.StartsWith("Darwin")) 133 | LibraryExtension = "dylib"; 134 | else 135 | LibraryExtension = "so"; 136 | } 137 | 138 | static string GetPlatform() 139 | { 140 | IntPtr buf = IntPtr.Zero; 141 | try 142 | { 143 | buf = Marshal.AllocHGlobal(8192); 144 | return (0 == uname(buf)) ? Marshal.PtrToStringAnsi(buf) : "Unknown"; 145 | } 146 | catch 147 | { 148 | return "Unknown"; 149 | } 150 | finally 151 | { 152 | if (buf != IntPtr.Zero) 153 | Marshal.FreeHGlobal(buf); 154 | } 155 | } 156 | 157 | public IntPtr LoadLibrary(string path) 158 | { 159 | dlerror(); 160 | var lib = dlopen(path, 2); 161 | var errPtr = dlerror(); 162 | if (errPtr != IntPtr.Zero) 163 | throw new NativeLoadException("dlopen: " + Marshal.PtrToStringAnsi(errPtr), null); 164 | return lib; 165 | } 166 | 167 | public IntPtr GetProcAddress(IntPtr lib, string entryPoint) 168 | { 169 | dlerror(); 170 | IntPtr address = dlsym(lib, entryPoint); 171 | return address; 172 | } 173 | 174 | public object GetDelegate(IntPtr lib, string entryPoint, Type delegateType) 175 | => Importers.GetDelegate(this, lib, entryPoint, delegateType); 176 | 177 | public void FreeLibrary(IntPtr lib) 178 | { 179 | dlclose(lib); 180 | } 181 | 182 | public string Translate(string name) 183 | { 184 | return "lib" + name + "." + LibraryExtension; 185 | } 186 | } 187 | 188 | public static string GetArchName(Architecture arch) 189 | { 190 | switch (arch) 191 | { 192 | case Architecture.X86: 193 | return "i386"; 194 | case Architecture.X64: 195 | return "amd64"; 196 | default: 197 | return arch.ToString().ToLower(); 198 | } 199 | } 200 | 201 | private static string GetRuntimeId(Architecture processArchitecture) 202 | { 203 | var arch = processArchitecture.ToString().ToLower(); 204 | var os = 205 | RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx" : 206 | RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : 207 | "win"; 208 | return $"{os}-{arch}"; 209 | } 210 | 211 | 212 | 213 | public static T Import(INativeLibImporter importer, string libName, string version, bool suppressUnload) where T : class 214 | { 215 | var subdir = GetArchName(RuntimeInformation.ProcessArchitecture); 216 | var runtimeId = GetRuntimeId(RuntimeInformation.ProcessArchitecture); 217 | 218 | var assemblyName = new AssemblyName("DynamicLink"); 219 | var assemblyBuilder = CurrentFramework.DefineDynamicAssembly(assemblyName, System.Reflection.Emit.AssemblyBuilderAccess.Run); 220 | var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynLinkModule"); 221 | string typeName = typeof(T).Name + "_impl"; 222 | var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, typeof(T)); 223 | 224 | FieldBuilder field_importer = typeBuilder.DefineField("importer", typeof(INativeLibImporter), FieldAttributes.Private | FieldAttributes.InitOnly); 225 | FieldBuilder field_libraryHandle = typeBuilder.DefineField("libraryHandle", typeof(IntPtr), FieldAttributes.Private | FieldAttributes.InitOnly); 226 | 227 | var methods = typeof(T).GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(m => m.IsAbstract && !m.IsGenericMethod).ToArray(); 228 | 229 | // Define delegate types for each of the method signatures 230 | var delegateMap = new Dictionary(); 231 | foreach (var method in methods) 232 | { 233 | var sig = GetMethodSig(method); 234 | if (delegateMap.ContainsKey(sig)) 235 | continue; 236 | var delegateTypeInfo = CreateDelegateType(moduleBuilder, method); 237 | delegateMap.Add(sig, delegateTypeInfo.AsType()); 238 | } 239 | 240 | // Define one field for each method to hold a delegate 241 | var delegates = methods.Select(m => new { 242 | MethodInfo = m, 243 | DelegateType = delegateMap[GetMethodSig(m)], 244 | }).ToArray(); 245 | var delegateFields = delegates.Select((d, i) => typeBuilder.DefineField($"{d.MethodInfo.Name}_func_{i}", d.DelegateType, FieldAttributes.Private)).ToArray(); 246 | 247 | // Create the constructor which will initialize the importer and library handle 248 | // and also use the importer to populate each of the delegate fields 249 | ConstructorBuilder constructor = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(INativeLibImporter), typeof(IntPtr) }); 250 | 251 | { 252 | var baseConstructor = typeof(T).GetTypeInfo().GetConstructors().Where(con => con.GetParameters().Length == 0).First(); 253 | ILGenerator il = constructor.GetILGenerator(); 254 | 255 | // Call base constructor 256 | il.Emit(OpCodes.Ldarg_0); // this 257 | il.Emit(OpCodes.Call, baseConstructor); 258 | 259 | // Store importer field 260 | il.Emit(OpCodes.Ldarg_0); // this 261 | il.Emit(OpCodes.Ldarg_1); // importer 262 | il.Emit(OpCodes.Stfld, field_importer); 263 | 264 | // Load and store library handle 265 | il.Emit(OpCodes.Ldarg_0); // this 266 | il.Emit(OpCodes.Ldarg_2); // library handle 267 | il.Emit(OpCodes.Stfld, field_libraryHandle); 268 | 269 | var getDelegateMethod = typeof(INativeLibImporter).GetTypeInfo().GetMethod("GetDelegate"); 270 | // Initialize each delegate field 271 | for (int i = 0; i < delegateFields.Length; i++) 272 | { 273 | var delegateType = delegates[i].DelegateType; 274 | 275 | il.Emit(OpCodes.Ldarg_0); // this 276 | 277 | il.Emit(OpCodes.Ldarg_1); // importer 278 | il.Emit(OpCodes.Ldarg_0); // this 279 | il.Emit(OpCodes.Ldfld, field_libraryHandle); 280 | il.Emit(OpCodes.Ldstr, delegates[i].MethodInfo.Name); // use method name from original class as entry point 281 | il.Emit(OpCodes.Ldtoken, delegateType); // the delegate type 282 | il.Emit(OpCodes.Call, typeof(System.Type).GetTypeInfo().GetMethod("GetTypeFromHandle")); // typeof() 283 | il.Emit(OpCodes.Callvirt, getDelegateMethod); // importer.GetDelegate() 284 | il.Emit(OpCodes.Isinst, delegateType); // as 285 | il.Emit(OpCodes.Stfld, delegateFields[i]); 286 | } 287 | 288 | // End of constructor 289 | il.Emit(OpCodes.Ret); 290 | } 291 | 292 | // Create destructor 293 | var destructor = typeBuilder.DefineMethod("Finalize", MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig); 294 | { 295 | var baseDestructor = typeof(T).GetTypeInfo().GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance); 296 | var il = destructor.GetILGenerator(); 297 | var end = il.DefineLabel(); 298 | 299 | il.BeginExceptionBlock(); 300 | if (!suppressUnload) 301 | { 302 | il.Emit(OpCodes.Ldarg_0); // this 303 | il.Emit(OpCodes.Ldfld, field_importer); // .importer 304 | il.Emit(OpCodes.Ldarg_0); // this 305 | il.Emit(OpCodes.Ldfld, field_libraryHandle); // .libraryHandle 306 | il.Emit(OpCodes.Callvirt, typeof(INativeLibImporter).GetTypeInfo().GetMethod("FreeLibrary")); // INativeLibImporter::FreeLibrary() 307 | } 308 | //il.Emit(OpCodes.Leave, end); 309 | il.BeginFinallyBlock(); 310 | il.Emit(OpCodes.Ldarg_0); // this 311 | il.Emit(OpCodes.Call, baseDestructor); // object::Finalize() 312 | //il.Emit(OpCodes.Endfinally); 313 | il.EndExceptionBlock(); 314 | il.MarkLabel(end); 315 | il.Emit(OpCodes.Ret); 316 | } 317 | 318 | var nativeFunctionMissingExceptionConstructor = typeof(NativeFunctionMissingException).GetTypeInfo().GetConstructor(new[] { typeof(string) }); 319 | // Now override each method from the base class 320 | for (int i = 0; i < delegateFields.Length; i++) 321 | { 322 | var baseMethod = delegates[i].MethodInfo; 323 | var args = baseMethod.GetParameters(); 324 | var omethod = typeBuilder.DefineMethod( 325 | baseMethod.Name, 326 | (baseMethod.Attributes & ~(MethodAttributes.Abstract | MethodAttributes.NewSlot)) | MethodAttributes.Virtual, 327 | baseMethod.CallingConvention, 328 | baseMethod.ReturnType, 329 | args.Select(arg => arg.ParameterType).ToArray() 330 | ); 331 | var il = omethod.GetILGenerator(); 332 | il.Emit(OpCodes.Ldarg_0); // this 333 | il.Emit(OpCodes.Ldfld, delegateFields[i]); // {field} 334 | il.Emit(OpCodes.Dup); 335 | var error = il.DefineLabel(); 336 | il.Emit(OpCodes.Brfalse_S, error); 337 | if (args.Length >= 1) 338 | il.Emit(OpCodes.Ldarg_1); 339 | if (args.Length >= 2) 340 | il.Emit(OpCodes.Ldarg_2); 341 | if (args.Length >= 3) 342 | il.Emit(OpCodes.Ldarg_3); 343 | for (short argNum = 4; argNum <= args.Length; argNum++) 344 | il.Emit(OpCodes.Ldarg_S, argNum); 345 | il.Emit(OpCodes.Tailcall); 346 | il.Emit(OpCodes.Callvirt, delegates[i].DelegateType.GetTypeInfo().GetMethod("Invoke")); 347 | il.Emit(OpCodes.Ret); 348 | il.MarkLabel(error); 349 | il.Emit(OpCodes.Ldstr, baseMethod.ToString()); 350 | il.Emit(OpCodes.Newobj, nativeFunctionMissingExceptionConstructor); 351 | il.Emit(OpCodes.Throw); 352 | } 353 | 354 | var type = typeBuilder.CreateTypeInfo(); 355 | 356 | var versionParts = version.Split('.'); 357 | var names = versionParts.Select((p, i) => libName + "-" + string.Join(".", versionParts.Take(i + 1))) 358 | .Reverse() 359 | .Concat(Enumerable.Repeat(libName, 1)); 360 | 361 | // try to load locally 362 | var paths = new[] 363 | { 364 | Path.Combine("runtimes", runtimeId, "native"), 365 | Path.Combine("native", subdir), 366 | "native", 367 | subdir, 368 | "", 369 | }; 370 | 371 | // If the RocksDbNative package is referenced, then dynamically load it here so that it can tell us where the native libraries are 372 | string nativeCodeBase = null; 373 | try 374 | { 375 | var nativeLibName = new AssemblyName("RocksDbNative"); 376 | var native = Assembly.Load(nativeLibName); 377 | var nativePkgClass = native.GetTypes().First(t => t.FullName == "RocksDbSharp.NativePackage"); 378 | var getCodeBaseMethod = nativePkgClass.GetTypeInfo().GetMethod("GetCodeBase"); 379 | var getCodeBase = getCodeBaseMethod.CreateDelegate>(); 380 | nativeCodeBase = getCodeBase(); 381 | } 382 | catch (Exception) 383 | { 384 | } 385 | 386 | var basePaths = new string[] { 387 | nativeCodeBase, 388 | Path.GetDirectoryName(UriToPath(Transitional.CurrentFramework.GetBaseDirectory())), 389 | Path.GetDirectoryName(UriToPath(Assembly.GetEntryAssembly()?.CodeBase)), 390 | Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location), 391 | Path.GetDirectoryName(UriToPath(typeof(PosixImporter).GetTypeInfo().Assembly.CodeBase)), 392 | Path.GetDirectoryName(typeof(PosixImporter).GetTypeInfo().Assembly.Location), 393 | }; 394 | var search = basePaths 395 | .Where(p => p != null) 396 | .Distinct() 397 | .SelectMany(basePath => 398 | paths.SelectMany(path => names.Select(n => Path.Combine(basePath, path, importer.Translate(n)))) 399 | .Concat(names.Select(n => importer.Translate(n))) 400 | ) 401 | .Select(path => new SearchPath { Path = path }) 402 | .ToArray(); 403 | 404 | foreach (var spec in search) 405 | { 406 | var construct = type.GetConstructor(new Type[] { typeof(INativeLibImporter), typeof(IntPtr) }); 407 | IntPtr lib = IntPtr.Zero; 408 | try 409 | { 410 | lib = importer.LoadLibrary(spec.Path); 411 | if (lib == IntPtr.Zero) 412 | throw new NativeLoadException("LoadLibrary returned 0", null); 413 | } 414 | catch (TargetInvocationException tie) 415 | { 416 | spec.Error = tie.InnerException; 417 | continue; 418 | } 419 | catch (Exception e) 420 | { 421 | spec.Error = e; 422 | continue; 423 | } 424 | var obj = construct.Invoke(new object[] { importer, lib }); 425 | var t = obj as T; 426 | return t; 427 | } 428 | 429 | throw new NativeLoadException("Unable to locate rocksdb native library, either install it, or use RocksDbNative nuget package\nSearched:\n" + string.Join("\n", search.Select(s => $"{s.Path}: ({s.Error.GetType().Name}) {s.Error.Message}")), null); 430 | } 431 | 432 | private static string UriToPath(string uriString) 433 | { 434 | if (uriString == null || !Uri.IsWellFormedUriString(uriString, UriKind.RelativeOrAbsolute)) 435 | return null; 436 | var uri = new Uri(uriString); 437 | return uri.LocalPath; 438 | } 439 | 440 | private class SearchPath 441 | { 442 | public string Path { get; set; } 443 | public Exception Error { get; set; } 444 | } 445 | 446 | private static string GetMethodSig(MethodInfo m) 447 | { 448 | return string.Join("_", Enumerable.Repeat(m.ReturnType.Name, 1).Concat(m.GetParameters().Select(p => p.ParameterType.Name))); 449 | } 450 | 451 | private static TypeInfo CreateDelegateType(ModuleBuilder moduleBuilder, MethodInfo methodTemplate) 452 | { 453 | var sig = GetMethodSig(methodTemplate); 454 | var typeBuilder = moduleBuilder.DefineType(sig, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Sealed, typeof(System.MulticastDelegate)); 455 | var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(Object), typeof(IntPtr) }); 456 | constructor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); 457 | 458 | var parameters = methodTemplate.GetParameters().Select(pi => pi.ParameterType).ToArray(); 459 | var methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, methodTemplate.ReturnType, parameters); 460 | methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); 461 | 462 | return typeBuilder.CreateTypeInfo(); 463 | } 464 | 465 | } 466 | 467 | public class NativeFunctionMissingException : Exception 468 | { 469 | public NativeFunctionMissingException() 470 | : base("Failed to find entry point") 471 | { 472 | } 473 | 474 | public NativeFunctionMissingException(string entryPoint) 475 | : base($"Failed to find entry point for {entryPoint}", null) 476 | { 477 | } 478 | } 479 | 480 | public class NativeLoadException : Exception 481 | { 482 | public NativeLoadException(string message, Exception inner) 483 | : base(message, inner) 484 | { 485 | } 486 | } 487 | } 488 | 489 | -------------------------------------------------------------------------------- /RocksDbSharp/BinaryComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace RocksDbSharp 7 | { 8 | // TODO: consider somehow reusing the actual unmanaged comparer 9 | public class BinaryComparer : IEqualityComparer, IComparer 10 | { 11 | public static BinaryComparer Default { get; } = new BinaryComparer(); 12 | 13 | public int Compare(byte[] a1, byte[] a2) 14 | { 15 | int length = Math.Min(a1.Length, a2.Length); 16 | unsafe 17 | { 18 | fixed (byte* p1 = a1, p2 = a2) 19 | { 20 | byte* c1 = p1, c2 = p2; 21 | byte* end = c1 + length; 22 | byte* end1 = c1 + a1.Length, end2 = c2 + a2.Length; 23 | for (; c1 < end && *c1 == *c2; c1++, c2++) ; 24 | if (c1 == end1) 25 | return c2 == end2 ? 0 : -1; 26 | if (c2 == end2) 27 | return 1; 28 | return (*c1 < *c2) ? -1 : 1; 29 | } 30 | } 31 | } 32 | 33 | public bool Equals(byte[] a1, byte[] a2) 34 | { 35 | if (ReferenceEquals(a1, a2)) 36 | return true; 37 | if (a1 == null || a2 == null || a1.Length != a2.Length) 38 | return false; 39 | unsafe 40 | { 41 | fixed (byte* p1 = a1, p2 = a2) 42 | { 43 | byte* x1 = p1, x2 = p2; 44 | int l = a1.Length; 45 | for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) 46 | if (*((long*)x1) != *((long*)x2)) return false; 47 | if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } 48 | if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } 49 | if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false; 50 | return true; 51 | } 52 | } 53 | } 54 | 55 | public bool PrefixEquals(byte[] a1, byte[] a2, int prefix) 56 | { 57 | if (ReferenceEquals(a1, a2)) 58 | return true; 59 | prefix = Math.Min(prefix, Math.Max(a1.Length, a2.Length)); 60 | var a1length = Math.Min(prefix, a1.Length); 61 | var a2length = Math.Min(prefix, a2.Length); 62 | if (a1 == null || a2 == null || a1length != a2length) 63 | return false; 64 | unsafe 65 | { 66 | fixed (byte* p1 = a1, p2 = a2) 67 | { 68 | byte* x1 = p1, x2 = p2; 69 | int l = a1length; 70 | for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) 71 | if (*((long*)x1) != *((long*)x2)) return false; 72 | if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } 73 | if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } 74 | if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false; 75 | return true; 76 | } 77 | } 78 | } 79 | 80 | public int GetHashCode(byte[] obj) 81 | { 82 | return MurMurHash3.Hash(new MemoryStream(obj)); 83 | } 84 | } 85 | 86 | /* 87 | This code is public domain. 88 | 89 | The MurmurHash3 algorithm was created by Austin Appleby and put into the public domain. See http://code.google.com/p/smhasher/ 90 | 91 | This C# variant was authored by 92 | Elliott B. Edwards and was placed into the public domain as a gist 93 | Status...Working on verification (Test Suite) 94 | Set up to run as a LinqPad (linqpad.net) script (thus the ".Dump()" call) 95 | */ 96 | public static class MurMurHash3 97 | { 98 | //Change to suit your needs 99 | const uint seed = 144; 100 | 101 | // TODO: optimize this to use a byte pointer 102 | public static int Hash(Stream stream) 103 | { 104 | const uint c1 = 0xcc9e2d51; 105 | const uint c2 = 0x1b873593; 106 | 107 | uint h1 = seed; 108 | uint k1 = 0; 109 | uint streamLength = 0; 110 | 111 | using (BinaryReader reader = new BinaryReader(stream)) 112 | { 113 | byte[] chunk = reader.ReadBytes(4); 114 | while (chunk.Length > 0) 115 | { 116 | streamLength += (uint)chunk.Length; 117 | switch (chunk.Length) 118 | { 119 | case 4: 120 | /* Get four bytes from the input into an uint */ 121 | k1 = (uint) 122 | (chunk[0] 123 | | chunk[1] << 8 124 | | chunk[2] << 16 125 | | chunk[3] << 24); 126 | 127 | /* bitmagic hash */ 128 | k1 *= c1; 129 | k1 = Rotl32(k1, 15); 130 | k1 *= c2; 131 | 132 | h1 ^= k1; 133 | h1 = Rotl32(h1, 13); 134 | h1 = h1 * 5 + 0xe6546b64; 135 | break; 136 | case 3: 137 | k1 = (uint) 138 | (chunk[0] 139 | | chunk[1] << 8 140 | | chunk[2] << 16); 141 | k1 *= c1; 142 | k1 = Rotl32(k1, 15); 143 | k1 *= c2; 144 | h1 ^= k1; 145 | break; 146 | case 2: 147 | k1 = (uint) 148 | (chunk[0] 149 | | chunk[1] << 8); 150 | k1 *= c1; 151 | k1 = Rotl32(k1, 15); 152 | k1 *= c2; 153 | h1 ^= k1; 154 | break; 155 | case 1: 156 | k1 = (uint)(chunk[0]); 157 | k1 *= c1; 158 | k1 = Rotl32(k1, 15); 159 | k1 *= c2; 160 | h1 ^= k1; 161 | break; 162 | 163 | } 164 | chunk = reader.ReadBytes(4); 165 | } 166 | } 167 | 168 | // finalization, magic chants to wrap it all up 169 | h1 ^= streamLength; 170 | h1 = Fmix(h1); 171 | 172 | unchecked //ignore overflow 173 | { 174 | return (int)h1; 175 | } 176 | } 177 | 178 | private static uint Rotl32(uint x, byte r) 179 | { 180 | return (x << r) | (x >> (32 - r)); 181 | } 182 | 183 | private static uint Fmix(uint h) 184 | { 185 | h ^= h >> 16; 186 | h *= 0x85ebca6b; 187 | h ^= h >> 13; 188 | h *= 0xc2b2ae35; 189 | h ^= h >> 16; 190 | return h; 191 | } 192 | } 193 | } 194 | 195 | -------------------------------------------------------------------------------- /RocksDbSharp/BlockBasedTableOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | 4 | namespace RocksDbSharp 5 | { 6 | public class BlockBasedTableOptions 7 | { 8 | public IntPtr Handle { get; protected set; } 9 | 10 | // The following exists only to retain a reference to those types which are used in-place by rocksdb 11 | // and not copied (or reference things that are used in-place). The idea is to have managed references 12 | // track the behavior of the unmanaged reference as much as possible. This prevents access violations 13 | // when the garbage collector cleans up the last managed reference 14 | internal dynamic References { get; } = new ExpandoObject(); 15 | 16 | public BlockBasedTableOptions() 17 | { 18 | this.Handle = Native.Instance.rocksdb_block_based_options_create(); 19 | } 20 | 21 | ~BlockBasedTableOptions() 22 | { 23 | if (Handle != IntPtr.Zero) 24 | { 25 | #if !NODESTROY 26 | Native.Instance.rocksdb_block_based_options_destroy(Handle); 27 | #endif 28 | Handle = IntPtr.Zero; 29 | } 30 | } 31 | 32 | public BlockBasedTableOptions SetBlockSize(ulong blockSize) 33 | { 34 | Native.Instance.rocksdb_block_based_options_set_block_size(Handle, (UIntPtr)blockSize); 35 | return this; 36 | } 37 | 38 | public BlockBasedTableOptions SetBlockSizeDeviation(int blockSizeDeviation) 39 | { 40 | Native.Instance.rocksdb_block_based_options_set_block_size_deviation(Handle, blockSizeDeviation); 41 | return this; 42 | } 43 | 44 | public BlockBasedTableOptions SetBlockRestartInterval(int blockRestartInterval) 45 | { 46 | Native.Instance.rocksdb_block_based_options_set_block_restart_interval(Handle, blockRestartInterval); 47 | return this; 48 | } 49 | 50 | public BlockBasedTableOptions SetFilterPolicy(IntPtr filterPolicy) 51 | { 52 | Native.Instance.rocksdb_block_based_options_set_filter_policy(Handle, filterPolicy); 53 | return this; 54 | } 55 | 56 | public BlockBasedTableOptions SetFilterPolicy(BloomFilterPolicy filterPolicy) 57 | { 58 | // store a managed reference to prevent garbage collection 59 | References.FilterPolicy = filterPolicy; 60 | Native.Instance.rocksdb_block_based_options_set_filter_policy(Handle, filterPolicy.Handle); 61 | return this; 62 | } 63 | 64 | public BlockBasedTableOptions SetNoBlockCache(bool noBlockCache) 65 | { 66 | Native.Instance.rocksdb_block_based_options_set_no_block_cache(Handle, noBlockCache); 67 | return this; 68 | } 69 | 70 | public BlockBasedTableOptions SetBlockCache(IntPtr blockCache) 71 | { 72 | Native.Instance.rocksdb_block_based_options_set_block_cache(Handle, blockCache); 73 | return this; 74 | } 75 | 76 | public BlockBasedTableOptions SetBlockCache(Cache blockCache) 77 | { 78 | References.BlockCache = blockCache; 79 | Native.Instance.rocksdb_block_based_options_set_block_cache(Handle, blockCache.Handle); 80 | return this; 81 | } 82 | 83 | public BlockBasedTableOptions SetBlockCacheCompressed(IntPtr blockCacheCompressed) 84 | { 85 | Native.Instance.rocksdb_block_based_options_set_block_cache_compressed(Handle, blockCacheCompressed); 86 | return this; 87 | } 88 | 89 | public BlockBasedTableOptions SetWholeKeyFiltering(bool wholeKeyFiltering) 90 | { 91 | Native.Instance.rocksdb_block_based_options_set_whole_key_filtering(Handle, wholeKeyFiltering); 92 | return this; 93 | } 94 | 95 | public BlockBasedTableOptions SetFormatVersion(int formatVersion) 96 | { 97 | Native.Instance.rocksdb_block_based_options_set_format_version(Handle, formatVersion); 98 | return this; 99 | } 100 | 101 | public BlockBasedTableOptions SetIndexType(BlockBasedTableIndexType indexType) 102 | { 103 | Native.Instance.rocksdb_block_based_options_set_index_type(Handle, indexType); 104 | return this; 105 | } 106 | 107 | public BlockBasedTableOptions SetHashIndexAllowCollision(bool allowCollision) 108 | { 109 | Native.Instance.rocksdb_block_based_options_set_hash_index_allow_collision(Handle, allowCollision); 110 | return this; 111 | } 112 | 113 | public BlockBasedTableOptions SetCacheIndexAndFilterBlocks(bool cacheIndexAndFilterBlocks) 114 | { 115 | Native.Instance.rocksdb_block_based_options_set_cache_index_and_filter_blocks(Handle, cacheIndexAndFilterBlocks); 116 | return this; 117 | } 118 | 119 | public BlockBasedTableOptions SetPinL0FilterAndIndexBlocksInCache(bool pinL0FilterAndIndexBlocksInCache) 120 | { 121 | Native.Instance.rocksdb_block_based_options_set_pin_l0_filter_and_index_blocks_in_cache(Handle, pinL0FilterAndIndexBlocksInCache); 122 | return this; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /RocksDbSharp/BloomFilterPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RocksDbSharp 4 | { 5 | public class BloomFilterPolicy 6 | { 7 | public IntPtr Handle { get; protected set; } 8 | 9 | private BloomFilterPolicy(IntPtr handle) 10 | { 11 | this.Handle = handle; 12 | } 13 | 14 | ~BloomFilterPolicy() 15 | { 16 | if (Handle != IntPtr.Zero) 17 | { 18 | #if !NODESTROY 19 | // Commented out until a solution is found to rocksdb issue #1095 (https://github.com/facebook/rocksdb/issues/1095) 20 | // If you create one of these, use it in an Option which will destroy it when finished 21 | // Otherwise don't create one or it will leak 22 | //Native.Instance.rocksdb_filterpolicy_destroy(Handle); 23 | #endif 24 | Handle = IntPtr.Zero; 25 | } 26 | } 27 | 28 | /// 29 | /// Return a new filter policy that uses a bloom filter with approximately 30 | /// the specified number of bits per key. 31 | /// bits_per_key: bits per key in bloom filter. A good value for bits_per_key 32 | /// is 10, which yields a filter with ~ 1% false positive rate. 33 | /// use_block_based_builder: use block based filter rather than full fiter. 34 | /// If you want to builder full filter, it needs to be set to false. 35 | /// Callers must delete the result after any database that is using the 36 | /// result has been closed. 37 | /// Note: if you are using a custom comparator that ignores some parts 38 | /// of the keys being compared, you must not use NewBloomFilterPolicy() 39 | /// and must provide your own FilterPolicy that also ignores the 40 | /// corresponding parts of the keys. For example, if the comparator 41 | /// ignores trailing spaces, it would be incorrect to use a 42 | /// FilterPolicy (like NewBloomFilterPolicy) that does not ignore 43 | /// trailing spaces in keys. 44 | /// 45 | /// Bits per key. 46 | public static BloomFilterPolicy Create(int bits_per_key = 10, bool use_block_based_builder = true) { 47 | IntPtr handle = use_block_based_builder ? Native.Instance.rocksdb_filterpolicy_create_bloom(bits_per_key) : Native.Instance.rocksdb_filterpolicy_create_bloom_full(bits_per_key); 48 | return new BloomFilterPolicy(handle); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /RocksDbSharp/Cache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public class Cache 8 | { 9 | public IntPtr Handle { get; protected set; } 10 | 11 | private Cache(IntPtr handle) 12 | { 13 | this.Handle = handle; 14 | } 15 | 16 | ~Cache() 17 | { 18 | if (Handle != IntPtr.Zero) 19 | { 20 | Native.Instance.rocksdb_cache_destroy(Handle); 21 | Handle = IntPtr.Zero; 22 | } 23 | } 24 | 25 | public static Cache CreateLru(ulong capacity) 26 | { 27 | IntPtr handle = Native.Instance.rocksdb_cache_create_lru(new UIntPtr(capacity)); 28 | return new Cache(handle); 29 | } 30 | 31 | public Cache SetCapacity(ulong capacity) 32 | { 33 | Native.Instance.rocksdb_cache_set_capacity(Handle, new UIntPtr(capacity)); 34 | return this; 35 | } 36 | 37 | public ulong GetUsage() 38 | { 39 | return Native.Instance.rocksdb_cache_get_usage(Handle).ToUInt64(); 40 | } 41 | 42 | public ulong GetPinnedUsage() 43 | { 44 | return Native.Instance.rocksdb_cache_get_pinned_usage(Handle).ToUInt64(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /RocksDbSharp/Checkpoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public class Checkpoint : IDisposable 8 | { 9 | public IntPtr Handle { get; } 10 | 11 | public Checkpoint(IntPtr handle) 12 | { 13 | Handle = handle; 14 | } 15 | 16 | public void Save(string checkpointDir, ulong logSizeForFlush = 0) 17 | => Native.Instance.rocksdb_checkpoint_create(Handle, checkpointDir, logSizeForFlush); 18 | 19 | public void Dispose() 20 | { 21 | Native.Instance.rocksdb_checkpoint_object_destroy(Handle); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /RocksDbSharp/ColumnFamilies.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RocksDbSharp 9 | { 10 | public class ColumnFamilies : IEnumerable 11 | { 12 | private List Descriptors { get; } = new List(); 13 | 14 | public static readonly string DefaultName = "default"; 15 | 16 | public class Descriptor 17 | { 18 | public string Name { get; } 19 | public ColumnFamilyOptions Options { get; } 20 | 21 | public Descriptor(string name, ColumnFamilyOptions options) 22 | { 23 | this.Name = name; 24 | this.Options = options; 25 | } 26 | } 27 | 28 | public ColumnFamilies(ColumnFamilyOptions options = null) 29 | { 30 | Descriptors.Add(new Descriptor(DefaultName, options ?? new ColumnFamilyOptions())); 31 | } 32 | 33 | public IEnumerable Names => this.Select(cfd => cfd.Name); 34 | 35 | public IEnumerable OptionHandles => this.Select(cfd => cfd.Options.Handle); 36 | 37 | public void Add(Descriptor descriptor) 38 | { 39 | if (descriptor.Name == DefaultName) 40 | Descriptors[0] = descriptor; 41 | else 42 | Descriptors.Add(descriptor); 43 | } 44 | 45 | public void Add(string name, ColumnFamilyOptions options) 46 | { 47 | Add(new Descriptor(name, options)); 48 | } 49 | 50 | public IEnumerator GetEnumerator() 51 | { 52 | return Descriptors.GetEnumerator(); 53 | } 54 | 55 | IEnumerator IEnumerable.GetEnumerator() 56 | { 57 | return Descriptors.GetEnumerator(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /RocksDbSharp/ColumnFamilyHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RocksDbSharp 8 | { 9 | #pragma warning disable IDE1006 // Naming (missing I) for backward source-compatibility reasons 10 | public interface ColumnFamilyHandle 11 | #pragma warning restore IDE1006 12 | { 13 | IntPtr Handle { get; } 14 | } 15 | 16 | class ColumnFamilyHandleInternal : ColumnFamilyHandle, IDisposable 17 | { 18 | public ColumnFamilyHandleInternal(IntPtr handle) 19 | { 20 | this.Handle = handle; 21 | } 22 | 23 | public IntPtr Handle { get; protected set; } 24 | 25 | public void Dispose() 26 | { 27 | if (Handle != IntPtr.Zero) 28 | { 29 | #if !NODESTROY 30 | Native.Instance.rocksdb_column_family_handle_destroy(Handle); 31 | #endif 32 | Handle = IntPtr.Zero; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /RocksDbSharp/Comparator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using Transitional; 6 | 7 | namespace RocksDbSharp 8 | { 9 | public interface Comparator 10 | { 11 | string Name { get; } 12 | int Compare(IntPtr a, UIntPtr alen, IntPtr b, UIntPtr blen); 13 | } 14 | 15 | public abstract class StringComparatorBase : Comparator 16 | { 17 | public Encoding Encoding { get; } 18 | 19 | public string Name { get; } 20 | 21 | public StringComparatorBase(Encoding encoding = null, string name = null, IntPtr state = default(IntPtr)) 22 | { 23 | Name = name ?? typeof(StringComparatorBase).Name; 24 | Encoding = encoding ?? Encoding.UTF8; 25 | } 26 | 27 | public abstract int Compare(string a, string b); 28 | 29 | public unsafe int Compare(IntPtr a, UIntPtr alen, IntPtr b, UIntPtr blen) 30 | { 31 | var astr = Encoding.GetString((byte*)a, (int)alen); 32 | var bstr = Encoding.GetString((byte*)b, (int)blen); 33 | return Compare(astr, bstr); 34 | } 35 | } 36 | 37 | public class StringComparator : StringComparatorBase 38 | { 39 | public Comparison CompareFunc { get; } 40 | 41 | public StringComparator(IComparer comparer = null, Encoding encoding = null, string name = null) 42 | : base(encoding, name) 43 | { 44 | if (comparer == null) 45 | comparer = StringComparer.CurrentCulture; 46 | CompareFunc = comparer.Compare; 47 | } 48 | 49 | public StringComparator(bool ignoreCase, Encoding encoding = null, string name = null) 50 | : this(ignoreCase ? StringComparer.CurrentCultureIgnoreCase : StringComparer.CurrentCulture, encoding, name) 51 | { 52 | } 53 | 54 | public override int Compare(string a, string b) => CompareFunc(a, b); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /RocksDbSharp/Env.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public class Env 8 | { 9 | public IntPtr Handle { get; protected set; } 10 | 11 | private Env(IntPtr handle) 12 | { 13 | Handle = handle; 14 | } 15 | 16 | public static Env CreateDefaultEnv() 17 | { 18 | return new Env(Native.Instance.rocksdb_create_default_env()); 19 | } 20 | 21 | public static Env CreateMemEnv() 22 | { 23 | return new Env(Native.Instance.rocksdb_create_mem_env()); 24 | } 25 | 26 | public Env SetBackgroundThreads(int value) 27 | { 28 | Native.Instance.rocksdb_env_set_background_threads(Handle, value); 29 | return this; 30 | } 31 | 32 | public Env SetHighPriorityBackgroundThreads(int value) 33 | { 34 | Native.Instance.rocksdb_env_set_high_priority_background_threads(Handle, value); 35 | return this; 36 | } 37 | 38 | public void JoinAllThreads() 39 | { 40 | Native.Instance.rocksdb_env_join_all_threads(Handle); 41 | } 42 | 43 | ~Env() 44 | { 45 | if (Handle != IntPtr.Zero) 46 | { 47 | #if !NODESTROY 48 | Native.Instance.rocksdb_env_destroy(Handle); 49 | #endif 50 | Handle = IntPtr.Zero; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /RocksDbSharp/EnvOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public class EnvOptions 8 | { 9 | public IntPtr Handle { get; protected set; } 10 | 11 | public EnvOptions() 12 | { 13 | Handle = Native.Instance.rocksdb_envoptions_create(); 14 | } 15 | 16 | ~EnvOptions() 17 | { 18 | if (Handle != IntPtr.Zero) 19 | { 20 | #if !NODESTROY 21 | Native.Instance.rocksdb_envoptions_destroy(Handle); 22 | #endif 23 | Handle = IntPtr.Zero; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /RocksDbSharp/IngestExternalFileOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public class IngestExternalFileOptions 8 | { 9 | public IntPtr Handle { get; protected set; } 10 | 11 | public IngestExternalFileOptions() 12 | { 13 | Handle = Native.Instance.rocksdb_ingestexternalfileoptions_create(); 14 | } 15 | 16 | ~IngestExternalFileOptions() 17 | { 18 | if (Handle != IntPtr.Zero) 19 | { 20 | #if !NODESTROY 21 | Native.Instance.rocksdb_ingestexternalfileoptions_destroy(Handle); 22 | #endif 23 | Handle = IntPtr.Zero; 24 | } 25 | } 26 | 27 | public IngestExternalFileOptions SetMoveFiles(bool moveFiles) 28 | { 29 | Native.Instance.rocksdb_ingestexternalfileoptions_set_move_files(Handle, moveFiles); 30 | return this; 31 | } 32 | 33 | public IngestExternalFileOptions SetSnapshotConsistency(bool snapshotConsistency) 34 | { 35 | Native.Instance.rocksdb_ingestexternalfileoptions_set_snapshot_consistency(Handle, snapshotConsistency); 36 | return this; 37 | } 38 | 39 | public IngestExternalFileOptions SetAllowGlobalSeqno(bool allow) 40 | { 41 | Native.Instance.rocksdb_ingestexternalfileoptions_set_allow_global_seqno(Handle, allow); 42 | return this; 43 | } 44 | 45 | public IngestExternalFileOptions SetAllowBlockingFlush(bool allow) 46 | { 47 | Native.Instance.rocksdb_ingestexternalfileoptions_set_allow_blocking_flush(Handle, allow); 48 | return this; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /RocksDbSharp/Iterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Transitional; 8 | 9 | namespace RocksDbSharp 10 | { 11 | public class Iterator : IDisposable 12 | { 13 | private IntPtr handle; 14 | #pragma warning disable CS0414 15 | private ReadOptions readOptions; 16 | #pragma warning restore CS0414 17 | 18 | internal Iterator(IntPtr handle) 19 | { 20 | this.handle = handle; 21 | } 22 | 23 | internal Iterator(IntPtr handle, ReadOptions readOptions) : this(handle) 24 | { 25 | // Note: passing readOptions in here has no actual effect except to keep readOptions 26 | // from being garbage collected whilst the Iterator is still alive because the 27 | // the iterator on the native side will actually read things from some of the readOptions 28 | // directly 29 | this.readOptions = readOptions; 30 | } 31 | 32 | public IntPtr Handle { get { return handle; } } 33 | 34 | public void Dispose() 35 | { 36 | if (handle != IntPtr.Zero) 37 | { 38 | #if !NODESTROY 39 | Native.Instance.rocksdb_iter_destroy(handle); 40 | #endif 41 | handle = IntPtr.Zero; 42 | } 43 | } 44 | 45 | /// 46 | /// Detach the iterator from its handle but don't dispose the handle 47 | /// 48 | /// 49 | public IntPtr Detach() 50 | { 51 | var r = handle; 52 | handle = IntPtr.Zero; 53 | return r; 54 | } 55 | 56 | public bool Valid() 57 | { 58 | return Native.Instance.rocksdb_iter_valid(handle); 59 | } 60 | 61 | public Iterator SeekToFirst() 62 | { 63 | Native.Instance.rocksdb_iter_seek_to_first(handle); 64 | return this; 65 | } 66 | 67 | public Iterator SeekToLast() 68 | { 69 | Native.Instance.rocksdb_iter_seek_to_last(handle); 70 | return this; 71 | } 72 | 73 | public unsafe Iterator Seek(byte *key, ulong klen) 74 | { 75 | Native.Instance.rocksdb_iter_seek(handle, key, (UIntPtr)klen); 76 | return this; 77 | } 78 | 79 | public Iterator Seek(byte[] key) 80 | { 81 | return Seek(key, (ulong)key.GetLongLength(0)); 82 | } 83 | 84 | public Iterator Seek(byte[] key, ulong klen) 85 | { 86 | Native.Instance.rocksdb_iter_seek(handle, key, (UIntPtr)klen); 87 | return this; 88 | } 89 | 90 | public Iterator Seek(string key) 91 | { 92 | Native.Instance.rocksdb_iter_seek(handle, key); 93 | return this; 94 | } 95 | 96 | public unsafe Iterator SeekForPrev(byte* key, ulong klen) 97 | { 98 | Native.Instance.rocksdb_iter_seek_for_prev(handle, key, (UIntPtr)klen); 99 | return this; 100 | } 101 | 102 | public Iterator SeekForPrev(byte[] key) 103 | { 104 | SeekForPrev(key, (ulong)key.Length); 105 | return this; 106 | } 107 | 108 | public Iterator SeekForPrev(byte[] key, ulong klen) 109 | { 110 | Native.Instance.rocksdb_iter_seek_for_prev(handle, key, (UIntPtr)klen); 111 | return this; 112 | } 113 | 114 | public Iterator SeekForPrev(string key) 115 | { 116 | Native.Instance.rocksdb_iter_seek_for_prev(handle, key); 117 | return this; 118 | } 119 | 120 | public Iterator Next() 121 | { 122 | Native.Instance.rocksdb_iter_next(handle); 123 | return this; 124 | } 125 | 126 | public Iterator Prev() 127 | { 128 | Native.Instance.rocksdb_iter_prev(handle); 129 | return this; 130 | } 131 | 132 | public byte[] Key() 133 | { 134 | return Native.Instance.rocksdb_iter_key(handle); 135 | } 136 | 137 | public byte[] Value() 138 | { 139 | return Native.Instance.rocksdb_iter_value(handle); 140 | } 141 | 142 | public string StringKey() 143 | { 144 | return Native.Instance.rocksdb_iter_key_string(handle); 145 | } 146 | 147 | public string StringValue() 148 | { 149 | return Native.Instance.rocksdb_iter_value_string(handle); 150 | } 151 | 152 | // TODO: figure out how to best implement rocksdb_iter_get_error 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /RocksDbSharp/MergeOperator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace RocksDbSharp 6 | { 7 | public interface MergeOperator 8 | { 9 | string Name { get; } 10 | IntPtr PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); 11 | IntPtr FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); 12 | void DeleteValue(IntPtr value, UIntPtr valueLength); 13 | } 14 | 15 | public static class MergeOperators 16 | { 17 | /// 18 | /// This function performs merge(left_op, right_op) 19 | /// when both the operands are themselves merge operation types. 20 | /// Save the result in *new_value and return true. If it is impossible 21 | /// or infeasible to combine the two operations, return false instead. 22 | /// This is called to combine two-merge operands (if possible) 23 | /// 24 | /// The key that's associated with this merge operation 25 | /// 26 | /// the sequence of merge operations to apply, front() first 27 | /// 28 | /// 29 | /// Client is responsible for filling the merge result here 30 | /// 31 | /// 32 | public delegate IntPtr PartialMergeFunc(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); 33 | /// 34 | /// Gives the client a way to express the read -> modify -> write semantics. 35 | /// Called when a Put/Delete is the *existing_value (or nullptr) 36 | /// 37 | /// The key that's associated with this merge operation. 38 | /// 39 | /// null indicates that the key does not exist before this op 40 | /// 41 | /// the sequence of merge operations to apply, front() first. 42 | /// 43 | /// 44 | /// Client is responsible for filling the merge result here 45 | /// 46 | /// 47 | public delegate IntPtr FullMergeFunc(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength); 48 | public delegate void DeleteValueFunc(IntPtr value, UIntPtr valueLength); 49 | 50 | public static MergeOperator Create( 51 | string name, 52 | PartialMergeFunc partialMerge, 53 | FullMergeFunc fullMerge, 54 | DeleteValueFunc deleteValue) 55 | { 56 | return new MergeOperatorImpl(name, partialMerge, fullMerge, deleteValue); 57 | } 58 | 59 | private class MergeOperatorImpl : MergeOperator 60 | { 61 | public string Name { get; } 62 | private PartialMergeFunc PartialMerge { get; } 63 | private FullMergeFunc FullMerge { get; } 64 | private DeleteValueFunc DeleteValue { get; } 65 | 66 | public MergeOperatorImpl(string name, PartialMergeFunc partialMerge, FullMergeFunc fullMerge, DeleteValueFunc deleteValue) 67 | { 68 | Name = name; 69 | PartialMerge = partialMerge; 70 | FullMerge = fullMerge; 71 | DeleteValue = deleteValue; 72 | } 73 | 74 | IntPtr MergeOperator.PartialMerge(IntPtr key, UIntPtr keyLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength) 75 | => PartialMerge(key, keyLength, operandsList, operandsListLength, numOperands, success, newValueLength); 76 | 77 | IntPtr MergeOperator.FullMerge(IntPtr key, UIntPtr keyLength, IntPtr existingValue, UIntPtr existingValueLength, IntPtr operandsList, IntPtr operandsListLength, int numOperands, IntPtr success, IntPtr newValueLength) 78 | => FullMerge(key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, success, newValueLength); 79 | 80 | void MergeOperator.DeleteValue(IntPtr value, UIntPtr valueLength) 81 | => DeleteValue(value, valueLength); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /RocksDbSharp/Native.Load.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace RocksDbSharp 9 | { 10 | public abstract partial class Native 11 | { 12 | public static Native Instance; 13 | 14 | static Native() 15 | { 16 | if (RuntimeInformation.ProcessArchitecture == Architecture.X86 && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 17 | throw new RocksDbSharpException("Rocksdb on windows is not supported for 32 bit applications"); 18 | Instance = NativeImport.Auto.Import("rocksdb", "6.2.2", true); 19 | } 20 | 21 | public Native() 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /RocksDbSharp/Native.Wrap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using size_t = System.UIntPtr; 6 | 7 | #pragma warning disable IDE1006 // Intentionally violating naming conventions because this is meant to match the C API 8 | namespace RocksDbSharp 9 | { 10 | /* 11 | These wrappers provide translation from the error output of the C API into exceptions 12 | */ 13 | public abstract partial class Native 14 | { 15 | public void rocksdb_put( 16 | /*rocksdb_t**/ IntPtr db, 17 | /*const rocksdb_writeoptions_t**/ IntPtr writeOptions, 18 | string key, 19 | string val, 20 | ColumnFamilyHandle cf = null, 21 | System.Text.Encoding encoding = null) 22 | { 23 | rocksdb_put(db, writeOptions, key, val, out IntPtr errptr, cf, encoding); 24 | if (errptr != IntPtr.Zero) 25 | throw new RocksDbException(errptr); 26 | } 27 | 28 | public void rocksdb_put( 29 | IntPtr db, 30 | IntPtr writeOptions, 31 | byte[] key, 32 | long keyLength, 33 | byte[] value, 34 | long valueLength, 35 | ColumnFamilyHandle cf) 36 | { 37 | IntPtr errptr; 38 | UIntPtr sklength = (UIntPtr)keyLength; 39 | UIntPtr svlength = (UIntPtr)valueLength; 40 | if (cf == null) 41 | rocksdb_put(db, writeOptions, key, sklength, value, svlength, out errptr); 42 | else 43 | rocksdb_put_cf(db, writeOptions, cf.Handle, key, sklength, value, svlength, out errptr); 44 | if (errptr != IntPtr.Zero) 45 | throw new RocksDbException(errptr); 46 | } 47 | 48 | 49 | public string rocksdb_get( 50 | /*rocksdb_t**/ IntPtr db, 51 | /*const rocksdb_readoptions_t**/ IntPtr read_options, 52 | string key, 53 | ColumnFamilyHandle cf, 54 | System.Text.Encoding encoding = null) 55 | { 56 | var result = rocksdb_get(db, read_options, key, out IntPtr errptr, cf, encoding); 57 | if (errptr != IntPtr.Zero) 58 | throw new RocksDbException(errptr); 59 | return result; 60 | } 61 | 62 | public IntPtr rocksdb_get( 63 | IntPtr db, 64 | IntPtr read_options, 65 | byte[] key, 66 | long keyLength, 67 | out long vallen, 68 | ColumnFamilyHandle cf) 69 | { 70 | UIntPtr sklength = (UIntPtr)keyLength; 71 | var result = cf == null 72 | ? rocksdb_get(db, read_options, key, sklength, out UIntPtr valLength, out IntPtr errptr) 73 | : rocksdb_get_cf(db, read_options, cf.Handle, key, sklength, out valLength, out errptr); 74 | if (errptr != IntPtr.Zero) 75 | throw new RocksDbException(errptr); 76 | vallen = (long)valLength; 77 | return result; 78 | } 79 | 80 | public byte[] rocksdb_get( 81 | IntPtr db, 82 | IntPtr read_options, 83 | byte[] key, 84 | long keyLength = 0, 85 | ColumnFamilyHandle cf = null) 86 | { 87 | var result = rocksdb_get(db, read_options, key, keyLength == 0 ? key.Length : keyLength, out IntPtr errptr, cf); 88 | if (errptr != IntPtr.Zero) 89 | throw new RocksDbException(errptr); 90 | return result; 91 | } 92 | 93 | public System.Collections.Generic.KeyValuePair[] rocksdb_multi_get( 94 | IntPtr db, 95 | IntPtr read_options, 96 | string[] keys, 97 | ColumnFamilyHandle[] cf = null, 98 | System.Text.Encoding encoding = null) 99 | { 100 | if (encoding == null) 101 | encoding = System.Text.Encoding.UTF8; 102 | IntPtr[] errptrs = new IntPtr[keys.Length]; 103 | var result = rocksdb_multi_get(db, read_options, keys, cf: cf, errptrs: errptrs, encoding: encoding); 104 | foreach (var errptr in errptrs) 105 | if (errptr != IntPtr.Zero) 106 | throw new RocksDbException(errptr); 107 | return result; 108 | } 109 | 110 | 111 | public System.Collections.Generic.KeyValuePair[] rocksdb_multi_get( 112 | IntPtr db, 113 | IntPtr read_options, 114 | byte[][] keys, 115 | ulong[] keyLengths = null, 116 | ColumnFamilyHandle[] cf = null) 117 | { 118 | IntPtr[] errptrs = new IntPtr[keys.Length]; 119 | var result = rocksdb_multi_get(db, read_options, keys, keyLengths: keyLengths, cf: cf, errptrs: errptrs); 120 | foreach (var errptr in errptrs) 121 | if (errptr != IntPtr.Zero) 122 | throw new RocksDbException(errptr); 123 | return result; 124 | } 125 | 126 | public void rocksdb_delete( 127 | /*rocksdb_t**/ IntPtr db, 128 | /*const rocksdb_writeoptions_t**/ IntPtr writeOptions, 129 | /*const*/ string key, 130 | ColumnFamilyHandle cf) 131 | { 132 | rocksdb_delete(db, writeOptions, key, out IntPtr errptr, cf); 133 | if (errptr != IntPtr.Zero) 134 | throw new RocksDbException(errptr); 135 | } 136 | 137 | [Obsolete("Use UIntPtr version instead")] 138 | public void rocksdb_delete( 139 | /*rocksdb_t**/ IntPtr db, 140 | /*const rocksdb_writeoptions_t**/ IntPtr writeOptions, 141 | /*const*/ byte[] key, 142 | long keylen) 143 | { 144 | UIntPtr sklength = (UIntPtr)keylen; 145 | rocksdb_delete(db, writeOptions, key, sklength, out IntPtr errptr); 146 | if (errptr != IntPtr.Zero) 147 | throw new RocksDbException(errptr); 148 | } 149 | 150 | [Obsolete("Use UIntPtr version instead")] 151 | public void rocksdb_delete_cf( 152 | /*rocksdb_t**/ IntPtr db, 153 | /*const rocksdb_writeoptions_t**/ IntPtr writeOptions, 154 | /*const*/ byte[] key, 155 | long keylen, 156 | ColumnFamilyHandle cf) 157 | { 158 | rocksdb_delete_cf(db, writeOptions, cf.Handle, key, (UIntPtr)keylen, out IntPtr errptr); 159 | if (errptr != IntPtr.Zero) 160 | throw new RocksDbException(errptr); 161 | } 162 | 163 | [Obsolete("Use UIntPtr version instead")] 164 | public void rocksdb_ingest_external_file(IntPtr db, string[] file_list, ulong list_len, IntPtr opt) 165 | { 166 | UIntPtr llen = (UIntPtr)list_len; 167 | rocksdb_ingest_external_file(db, file_list, llen, opt, out IntPtr errptr); 168 | if (errptr != IntPtr.Zero) 169 | throw new RocksDbException(errptr); 170 | } 171 | 172 | [Obsolete("Use UIntPtr version instead")] 173 | public void rocksdb_ingest_external_file_cf(IntPtr db, IntPtr handle, string[] file_list, ulong list_len, IntPtr opt) 174 | { 175 | UIntPtr llen = (UIntPtr)list_len; 176 | rocksdb_ingest_external_file_cf(db, handle, file_list, llen, opt, out IntPtr errptr); 177 | if (errptr != IntPtr.Zero) 178 | throw new RocksDbException(errptr); 179 | } 180 | 181 | [Obsolete("Use UIntPtr version instead")] 182 | public void rocksdb_sstfilewriter_add( 183 | IntPtr writer, 184 | byte[] key, 185 | ulong keylen, 186 | byte[] val, 187 | ulong vallen) 188 | { 189 | UIntPtr sklength = (UIntPtr)keylen; 190 | UIntPtr svlength = (UIntPtr)vallen; 191 | rocksdb_sstfilewriter_add(writer, key, sklength, val, svlength, out IntPtr errptr); 192 | if (errptr != IntPtr.Zero) 193 | throw new RocksDbException(errptr); 194 | } 195 | 196 | public unsafe void rocksdb_sstfilewriter_add( 197 | IntPtr writer, 198 | string key, 199 | string val) 200 | { 201 | rocksdb_sstfilewriter_add(writer, key, val, out IntPtr errptr); 202 | if (errptr != IntPtr.Zero) 203 | throw new RocksDbException(errptr); 204 | } 205 | 206 | public string rocksdb_writebatch_wi_get_from_batch( 207 | IntPtr wb, 208 | IntPtr options, 209 | string key, 210 | ColumnFamilyHandle cf, 211 | System.Text.Encoding encoding = null) 212 | { 213 | var result = rocksdb_writebatch_wi_get_from_batch(wb, options, key, out IntPtr errptr, cf, encoding); 214 | if (errptr != IntPtr.Zero) 215 | throw new RocksDbException(errptr); 216 | return result; 217 | } 218 | 219 | public IntPtr rocksdb_writebatch_wi_get_from_batch( 220 | IntPtr wb, 221 | IntPtr options, 222 | byte[] key, 223 | ulong keyLength, 224 | out ulong vallen, 225 | ColumnFamilyHandle cf) 226 | { 227 | UIntPtr sklength = (UIntPtr)keyLength; 228 | var result = cf == null 229 | ? rocksdb_writebatch_wi_get_from_batch(wb, options, key, sklength, out UIntPtr valLength, out IntPtr errptr) 230 | : rocksdb_writebatch_wi_get_from_batch_cf(wb, options, cf.Handle, key, sklength, out valLength, out errptr); 231 | if (errptr != IntPtr.Zero) 232 | throw new RocksDbException(errptr); 233 | vallen = (ulong)valLength; 234 | return result; 235 | } 236 | 237 | public byte[] rocksdb_writebatch_wi_get_from_batch( 238 | IntPtr wb, 239 | IntPtr options, 240 | byte[] key, 241 | ulong keyLength = 0, 242 | ColumnFamilyHandle cf = null) 243 | { 244 | var result = rocksdb_writebatch_wi_get_from_batch(wb, options, key, keyLength == 0 ? (ulong)key.Length : keyLength, out IntPtr errptr, cf); 245 | if (errptr != IntPtr.Zero) 246 | throw new RocksDbException(errptr); 247 | return result; 248 | } 249 | 250 | public string rocksdb_writebatch_wi_get_from_batch_and_db( 251 | IntPtr wb, 252 | IntPtr db, 253 | IntPtr read_options, 254 | string key, 255 | ColumnFamilyHandle cf, 256 | System.Text.Encoding encoding = null) 257 | { 258 | var result = rocksdb_writebatch_wi_get_from_batch_and_db(wb, db, read_options, key, out IntPtr errptr, cf, encoding); 259 | if (errptr != IntPtr.Zero) 260 | throw new RocksDbException(errptr); 261 | return result; 262 | } 263 | 264 | public IntPtr rocksdb_writebatch_wi_get_from_batch_and_db( 265 | IntPtr wb, 266 | IntPtr db, 267 | IntPtr read_options, 268 | byte[] key, 269 | ulong keyLength, 270 | out ulong vallen, 271 | ColumnFamilyHandle cf) 272 | { 273 | UIntPtr sklength = (UIntPtr)keyLength; 274 | var result = cf == null 275 | ? rocksdb_writebatch_wi_get_from_batch_and_db(wb, db, read_options, key, sklength, out UIntPtr valLength, out IntPtr errptr) 276 | : rocksdb_writebatch_wi_get_from_batch_and_db_cf(wb, db, read_options, cf.Handle, key, sklength, out valLength, out errptr); 277 | if (errptr != IntPtr.Zero) 278 | throw new RocksDbException(errptr); 279 | vallen = (ulong)valLength; 280 | return result; 281 | } 282 | 283 | public byte[] rocksdb_writebatch_wi_get_from_batch_and_db( 284 | IntPtr wb, 285 | IntPtr db, 286 | IntPtr read_options, 287 | byte[] key, 288 | ulong keyLength = 0, 289 | ColumnFamilyHandle cf = null) 290 | { 291 | var result = rocksdb_writebatch_wi_get_from_batch_and_db(wb, db, read_options, key, keyLength == 0 ? (ulong)key.Length : keyLength, out IntPtr errptr, cf); 292 | if (errptr != IntPtr.Zero) 293 | throw new RocksDbException(errptr); 294 | return result; 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /RocksDbSharp/Options.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | 4 | namespace RocksDbSharp 5 | { 6 | /* 7 | Configure options for a RocksDb store. 8 | 9 | Note on SetXXX() syntax: 10 | Why not syntax like new Options { XXX = ... } instead? Two reasons 11 | 1. The rocksdb C API does not support reading the options and so a class with properties is not an appropriate representation 12 | 2. The API functions are named as imperatives and don't always begin with "set" so one like "OptimizeLevelStyleCompaction" wouldn't work right 13 | */ 14 | public abstract class OptionsHandle 15 | { 16 | // The following exists only to retain a reference to those types which are used in-place by rocksdb 17 | // and not copied (or reference things that are used in-place). The idea is to have managed references 18 | // track the behavior of the unmanaged reference as much as possible. This prevents access violations 19 | // when the garbage collector cleans up the last managed reference 20 | internal dynamic References { get; } = new ExpandoObject(); 21 | 22 | public IntPtr Handle { get; private set; } 23 | 24 | public OptionsHandle() 25 | { 26 | this.Handle = Native.Instance.rocksdb_options_create(); 27 | } 28 | 29 | ~OptionsHandle() 30 | { 31 | if (Handle != IntPtr.Zero) 32 | { 33 | #if !NODESTROY 34 | Native.Instance.rocksdb_options_destroy(Handle); 35 | #endif 36 | Handle = IntPtr.Zero; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /RocksDbSharp/ReadOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Transitional; 8 | 9 | namespace RocksDbSharp 10 | { 11 | public class ReadOptions 12 | { 13 | private IntPtr iterateUpperBound; 14 | 15 | public ReadOptions() 16 | { 17 | Handle = Native.Instance.rocksdb_readoptions_create(); 18 | } 19 | 20 | public IntPtr Handle { get; protected set; } 21 | 22 | ~ReadOptions() 23 | { 24 | if (Handle != IntPtr.Zero) 25 | { 26 | #if !NODESTROY 27 | Native.Instance.rocksdb_readoptions_destroy(Handle); 28 | if (iterateUpperBound != IntPtr.Zero) 29 | Marshal.FreeHGlobal(iterateUpperBound); 30 | #endif 31 | Handle = IntPtr.Zero; 32 | } 33 | } 34 | 35 | public ReadOptions SetVerifyChecksums(bool value) 36 | { 37 | Native.Instance.rocksdb_readoptions_set_verify_checksums(Handle, value); 38 | return this; 39 | } 40 | 41 | public ReadOptions SetFillCache(bool value) 42 | { 43 | Native.Instance.rocksdb_readoptions_set_fill_cache(Handle, value); 44 | return this; 45 | } 46 | 47 | public ReadOptions SetSnapshot(Snapshot snapshot) 48 | { 49 | Native.Instance.rocksdb_readoptions_set_snapshot(Handle, snapshot.Handle); 50 | return this; 51 | } 52 | 53 | /// 54 | /// Enforce that the iterator only iterates over the same prefix as the seek. 55 | /// This option is effective only for prefix seeks, i.e. prefix_extractor is 56 | /// non-null for the column family and total_order_seek is false. Unlike 57 | /// iterate_upper_bound, prefix_same_as_start only works within a prefix 58 | /// but in both directions. 59 | /// Default: false 60 | /// 61 | /// 62 | /// 63 | public ReadOptions SetPrefixSameAsStart(bool prefixSameAsStart) 64 | { 65 | Native.Instance.rocksdb_readoptions_set_prefix_same_as_start(Handle, prefixSameAsStart); 66 | return this; 67 | } 68 | 69 | public unsafe ReadOptions SetIterateUpperBound(byte* key, ulong keylen) 70 | { 71 | UIntPtr klen = (UIntPtr)keylen; 72 | Native.Instance.rocksdb_readoptions_set_iterate_upper_bound(Handle, key, klen); 73 | return this; 74 | } 75 | 76 | public ReadOptions SetIterateUpperBound(byte[] key, ulong keyLen) 77 | { 78 | if (iterateUpperBound != IntPtr.Zero) 79 | Marshal.FreeHGlobal(iterateUpperBound); 80 | iterateUpperBound = Marshal.AllocHGlobal(key.Length); 81 | Marshal.Copy(key, 0, iterateUpperBound, key.Length); 82 | UIntPtr klen = (UIntPtr)keyLen; 83 | Native.Instance.rocksdb_readoptions_set_iterate_upper_bound(Handle, iterateUpperBound, klen); 84 | return this; 85 | } 86 | 87 | public ReadOptions SetIterateUpperBound(byte[] key) 88 | { 89 | return SetIterateUpperBound(key, (ulong)key.GetLongLength(0)); 90 | } 91 | 92 | public unsafe ReadOptions SetIterateUpperBound(string stringKey, Encoding encoding = null) 93 | { 94 | var key = (encoding ?? Encoding.UTF8).GetBytes(stringKey); 95 | return SetIterateUpperBound(key); 96 | } 97 | 98 | public ReadOptions SetReadTier(int value) 99 | { 100 | Native.Instance.rocksdb_readoptions_set_read_tier(Handle, value); 101 | return this; 102 | } 103 | 104 | public ReadOptions SetTailing(bool value) 105 | { 106 | Native.Instance.rocksdb_readoptions_set_tailing(Handle, value); 107 | return this; 108 | } 109 | 110 | public ReadOptions SetReadaheadSize(ulong size) 111 | { 112 | UIntPtr readaheadSize = (UIntPtr)size; 113 | Native.Instance.rocksdb_readoptions_set_readahead_size(Handle, readaheadSize); 114 | return this; 115 | } 116 | 117 | public ReadOptions SetPinData(bool enable) 118 | { 119 | Native.Instance.rocksdb_readoptions_set_pin_data(Handle, enable); 120 | return this; 121 | } 122 | 123 | public ReadOptions SetTotalOrderSeek(bool enable) 124 | { 125 | Native.Instance.rocksdb_readoptions_set_total_order_seek(Handle, enable); 126 | return this; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /RocksDbSharp/RocksDb.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Dynamic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using Transitional; 8 | 9 | namespace RocksDbSharp 10 | { 11 | public class RocksDb : IDisposable 12 | { 13 | internal static ReadOptions DefaultReadOptions { get; } = new ReadOptions(); 14 | internal static OptionsHandle DefaultOptions { get; } = new DbOptions(); 15 | internal static WriteOptions DefaultWriteOptions { get; } = new WriteOptions(); 16 | internal static Encoding DefaultEncoding => Encoding.UTF8; 17 | private Dictionary columnFamilies; 18 | 19 | // Managed references to unmanaged resources that need to live at least as long as the db 20 | internal dynamic References { get; } = new ExpandoObject(); 21 | 22 | public IntPtr Handle { get; protected set; } 23 | 24 | private RocksDb(IntPtr handle, dynamic optionsReferences, dynamic cfOptionsRefs, Dictionary columnFamilies = null) 25 | { 26 | this.Handle = handle; 27 | References.Options = optionsReferences; 28 | References.CfOptions = cfOptionsRefs; 29 | this.columnFamilies = columnFamilies; 30 | } 31 | 32 | public void Dispose() 33 | { 34 | if (columnFamilies != null) 35 | { 36 | foreach (var cfh in columnFamilies.Values) 37 | cfh.Dispose(); 38 | } 39 | Native.Instance.rocksdb_close(Handle); 40 | } 41 | 42 | public static RocksDb Open(OptionsHandle options, string path) 43 | { 44 | IntPtr db = Native.Instance.rocksdb_open(options.Handle, path); 45 | return new RocksDb(db, optionsReferences: null, cfOptionsRefs: null); 46 | } 47 | 48 | public static RocksDb OpenReadOnly(OptionsHandle options, string path, bool errorIfLogFileExists) 49 | { 50 | IntPtr db = Native.Instance.rocksdb_open_for_read_only(options.Handle, path, errorIfLogFileExists); 51 | return new RocksDb(db, optionsReferences: null, cfOptionsRefs: null); 52 | } 53 | 54 | public static RocksDb OpenWithTtl(OptionsHandle options, string path, int ttlSeconds) 55 | { 56 | IntPtr db = Native.Instance.rocksdb_open_with_ttl(options.Handle, path, ttlSeconds); 57 | return new RocksDb(db, optionsReferences: null, cfOptionsRefs: null); 58 | } 59 | 60 | public static RocksDb Open(DbOptions options, string path, ColumnFamilies columnFamilies) 61 | { 62 | string[] cfnames = columnFamilies.Names.ToArray(); 63 | IntPtr[] cfoptions = columnFamilies.OptionHandles.ToArray(); 64 | IntPtr[] cfhandles = new IntPtr[cfnames.Length]; 65 | IntPtr db = Native.Instance.rocksdb_open_column_families(options.Handle, path, cfnames.Length, cfnames, cfoptions, cfhandles); 66 | var cfHandleMap = new Dictionary(); 67 | foreach (var pair in cfnames.Zip(cfhandles.Select(cfh => new ColumnFamilyHandleInternal(cfh)), (name, cfh) => new { Name = name, Handle = cfh })) 68 | cfHandleMap.Add(pair.Name, pair.Handle); 69 | return new RocksDb(db, 70 | optionsReferences: options.References, 71 | cfOptionsRefs: columnFamilies.Select(cfd => cfd.Options.References).ToArray(), 72 | columnFamilies: cfHandleMap); 73 | } 74 | 75 | public static RocksDb OpenReadOnly(DbOptions options, string path, ColumnFamilies columnFamilies, bool errIfLogFileExists) 76 | { 77 | string[] cfnames = columnFamilies.Names.ToArray(); 78 | IntPtr[] cfoptions = columnFamilies.OptionHandles.ToArray(); 79 | IntPtr[] cfhandles = new IntPtr[cfnames.Length]; 80 | IntPtr db = Native.Instance.rocksdb_open_for_read_only_column_families(options.Handle, path, cfnames.Length, cfnames, cfoptions, cfhandles, errIfLogFileExists); 81 | var cfHandleMap = new Dictionary(); 82 | foreach (var pair in cfnames.Zip(cfhandles.Select(cfh => new ColumnFamilyHandleInternal(cfh)), (name, cfh) => new { Name = name, Handle = cfh })) 83 | cfHandleMap.Add(pair.Name, pair.Handle); 84 | return new RocksDb(db, 85 | optionsReferences: options.References, 86 | cfOptionsRefs: columnFamilies.Select(cfd => cfd.Options.References).ToArray(), 87 | columnFamilies: cfHandleMap); 88 | } 89 | 90 | /// 91 | /// Usage: 92 | /// 98 | /// 99 | /// 100 | public Checkpoint Checkpoint() 101 | { 102 | var checkpoint = Native.Instance.rocksdb_checkpoint_object_create(Handle); 103 | return new Checkpoint(checkpoint); 104 | } 105 | 106 | public void SetOptions(IEnumerable> options) 107 | { 108 | var keys = options.Select(e => e.Key).ToArray(); 109 | var values = options.Select(e => e.Value).ToArray(); 110 | Native.Instance.rocksdb_set_options(Handle, keys.Length, keys, values); 111 | } 112 | 113 | public string Get(string key, ColumnFamilyHandle cf = null, ReadOptions readOptions = null, Encoding encoding = null) 114 | { 115 | return Native.Instance.rocksdb_get(Handle, (readOptions ?? DefaultReadOptions).Handle, key, cf, encoding ?? DefaultEncoding); 116 | } 117 | 118 | public byte[] Get(byte[] key, ColumnFamilyHandle cf = null, ReadOptions readOptions = null) 119 | { 120 | return Get(key, key.GetLongLength(0), cf, readOptions); 121 | } 122 | 123 | public byte[] Get(byte[] key, long keyLength, ColumnFamilyHandle cf = null, ReadOptions readOptions = null) 124 | { 125 | return Native.Instance.rocksdb_get(Handle, (readOptions ?? DefaultReadOptions).Handle, key, keyLength, cf); 126 | } 127 | 128 | /// 129 | /// Reads the contents of the database value associated with , if present, into the supplied 130 | /// at up to bytes, returning the 131 | /// length of the value in the database, or -1 if the key is not present. 132 | /// 133 | /// 134 | /// 135 | /// 136 | /// 137 | /// 138 | /// 139 | /// The actual length of the database field if it exists, otherwise -1 140 | public long Get(byte[] key, byte[] buffer, long offset, long length, ColumnFamilyHandle cf = null, ReadOptions readOptions = null) 141 | { 142 | return Get(key, key.GetLongLength(0), buffer, offset, length, cf, readOptions); 143 | } 144 | 145 | /// 146 | /// Reads the contents of the database value associated with , if present, into the supplied 147 | /// at up to bytes, returning the 148 | /// length of the value in the database, or -1 if the key is not present. 149 | /// 150 | /// 151 | /// 152 | /// 153 | /// 154 | /// 155 | /// 156 | /// The actual length of the database field if it exists, otherwise -1 157 | public long Get(byte[] key, long keyLength, byte[] buffer, long offset, long length, ColumnFamilyHandle cf = null, ReadOptions readOptions = null) 158 | { 159 | unsafe 160 | { 161 | var ptr = Native.Instance.rocksdb_get(Handle, (readOptions ?? DefaultReadOptions).Handle, key, keyLength, out long valLength, cf); 162 | if (ptr == IntPtr.Zero) 163 | return -1; 164 | var copyLength = Math.Min(length, valLength); 165 | Marshal.Copy(ptr, buffer, (int)offset, (int)copyLength); 166 | Native.Instance.rocksdb_free(ptr); 167 | return valLength; 168 | } 169 | } 170 | 171 | public KeyValuePair[] MultiGet(byte[][] keys, ColumnFamilyHandle[] cf = null, ReadOptions readOptions = null) 172 | { 173 | return Native.Instance.rocksdb_multi_get(Handle, (readOptions ?? DefaultReadOptions).Handle, keys); 174 | } 175 | 176 | public KeyValuePair[] MultiGet(string[] keys, ColumnFamilyHandle[] cf = null, ReadOptions readOptions = null) 177 | { 178 | return Native.Instance.rocksdb_multi_get(Handle, (readOptions ?? DefaultReadOptions).Handle, keys); 179 | } 180 | 181 | public void Write(WriteBatch writeBatch, WriteOptions writeOptions = null) 182 | { 183 | Native.Instance.rocksdb_write(Handle, (writeOptions ?? DefaultWriteOptions).Handle, writeBatch.Handle); 184 | } 185 | 186 | public void Write(WriteBatchWithIndex writeBatch, WriteOptions writeOptions = null) 187 | { 188 | Native.Instance.rocksdb_write_writebatch_wi(Handle, (writeOptions ?? DefaultWriteOptions).Handle, writeBatch.Handle); 189 | } 190 | 191 | public void Remove(string key, ColumnFamilyHandle cf = null, WriteOptions writeOptions = null) 192 | { 193 | Native.Instance.rocksdb_delete(Handle, (writeOptions ?? DefaultWriteOptions).Handle, key, cf); 194 | } 195 | 196 | public void Remove(byte[] key, ColumnFamilyHandle cf = null, WriteOptions writeOptions = null) 197 | { 198 | Remove(key, key.Length, cf, writeOptions); 199 | } 200 | 201 | public void Remove(byte[] key, long keyLength, ColumnFamilyHandle cf = null, WriteOptions writeOptions = null) 202 | { 203 | if (cf == null) 204 | Native.Instance.rocksdb_delete(Handle, (writeOptions ?? DefaultWriteOptions).Handle, key, (UIntPtr)keyLength); 205 | else 206 | Native.Instance.rocksdb_delete_cf(Handle, (writeOptions ?? DefaultWriteOptions).Handle, cf.Handle, key, (UIntPtr)keyLength); 207 | } 208 | 209 | public void Put(string key, string value, ColumnFamilyHandle cf = null, WriteOptions writeOptions = null, Encoding encoding = null) 210 | { 211 | Native.Instance.rocksdb_put(Handle, (writeOptions ?? DefaultWriteOptions).Handle, key, value, cf, encoding ?? DefaultEncoding); 212 | } 213 | 214 | public void Put(byte[] key, byte[] value, ColumnFamilyHandle cf = null, WriteOptions writeOptions = null) 215 | { 216 | Put(key, key.GetLongLength(0), value, value.GetLongLength(0), cf, writeOptions); 217 | } 218 | 219 | public void Put(byte[] key, long keyLength, byte[] value, long valueLength, ColumnFamilyHandle cf = null, WriteOptions writeOptions = null) 220 | { 221 | Native.Instance.rocksdb_put(Handle, (writeOptions ?? DefaultWriteOptions).Handle, key, keyLength, value, valueLength, cf); 222 | } 223 | 224 | public Iterator NewIterator(ColumnFamilyHandle cf = null, ReadOptions readOptions = null) 225 | { 226 | IntPtr iteratorHandle = cf == null 227 | ? Native.Instance.rocksdb_create_iterator(Handle, (readOptions ?? DefaultReadOptions).Handle) 228 | : Native.Instance.rocksdb_create_iterator_cf(Handle, (readOptions ?? DefaultReadOptions).Handle, cf.Handle); 229 | // Note: passing in read options here only to ensure that it is not collected before the iterator 230 | return new Iterator(iteratorHandle, readOptions); 231 | } 232 | 233 | public Iterator[] NewIterators(ColumnFamilyHandle[] cfs, ReadOptions[] readOptions) 234 | { 235 | throw new NotImplementedException("TODO: Implement NewIterators()"); 236 | // See rocksdb_create_iterators 237 | } 238 | 239 | public Snapshot CreateSnapshot() 240 | { 241 | IntPtr snapshotHandle = Native.Instance.rocksdb_create_snapshot(Handle); 242 | return new Snapshot(Handle, snapshotHandle); 243 | } 244 | 245 | public static IEnumerable ListColumnFamilies(DbOptions options, string name) 246 | { 247 | return Native.Instance.rocksdb_list_column_families(options.Handle, name); 248 | } 249 | 250 | public ColumnFamilyHandle CreateColumnFamily(ColumnFamilyOptions cfOptions, string name) 251 | { 252 | var cfh = Native.Instance.rocksdb_create_column_family(Handle, cfOptions.Handle, name); 253 | var cfhw = new ColumnFamilyHandleInternal(cfh); 254 | columnFamilies.Add(name, cfhw); 255 | return cfhw; 256 | } 257 | 258 | public void DropColumnFamily(string name) 259 | { 260 | var cf = GetColumnFamily(name); 261 | Native.Instance.rocksdb_drop_column_family(Handle, cf.Handle); 262 | columnFamilies.Remove(name); 263 | } 264 | 265 | public ColumnFamilyHandle GetDefaultColumnFamily() 266 | { 267 | return GetColumnFamily(ColumnFamilies.DefaultName); 268 | } 269 | 270 | public ColumnFamilyHandle GetColumnFamily(string name) 271 | { 272 | if (columnFamilies == null) 273 | throw new RocksDbSharpException("Database not opened for column families"); 274 | return columnFamilies[name]; 275 | } 276 | 277 | public string GetProperty(string propertyName) 278 | { 279 | return Native.Instance.rocksdb_property_value_string(Handle, propertyName); 280 | } 281 | 282 | public string GetProperty(string propertyName, ColumnFamilyHandle cf) 283 | { 284 | return Native.Instance.rocksdb_property_value_cf_string(Handle, cf.Handle, propertyName); 285 | } 286 | 287 | public void IngestExternalFiles(string[] files, IngestExternalFileOptions ingestOptions, ColumnFamilyHandle cf = null) 288 | { 289 | if (cf == null) 290 | Native.Instance.rocksdb_ingest_external_file(Handle, files, (UIntPtr)files.GetLongLength(0), ingestOptions.Handle); 291 | else 292 | Native.Instance.rocksdb_ingest_external_file_cf(Handle, cf.Handle, files, (UIntPtr)files.GetLongLength(0), ingestOptions.Handle); 293 | } 294 | 295 | public void CompactRange(byte[] start, byte[] limit, ColumnFamilyHandle cf = null) 296 | { 297 | if (cf == null) 298 | Native.Instance.rocksdb_compact_range(Handle, start, (UIntPtr)(start?.GetLongLength(0) ?? 0L), limit, (UIntPtr)(limit?.GetLongLength(0) ?? 0L)); 299 | else 300 | Native.Instance.rocksdb_compact_range_cf(Handle, cf.Handle, start, (UIntPtr)(start?.GetLongLength(0) ?? 0L), limit, (UIntPtr)(limit?.GetLongLength(0) ?? 0L)); 301 | } 302 | 303 | public void CompactRange(string start, string limit, ColumnFamilyHandle cf = null, Encoding encoding = null) 304 | { 305 | if (encoding == null) 306 | encoding = Encoding.UTF8; 307 | CompactRange(start == null ? null : encoding.GetBytes(start), limit == null ? null : encoding.GetBytes(limit), cf); 308 | } 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /RocksDbSharp/RocksDbException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace RocksDbSharp 5 | { 6 | public class RocksDbException : RocksDbSharpException 7 | { 8 | public RocksDbException(IntPtr errptr) 9 | : base(Marshal.PtrToStringAnsi(errptr)) 10 | { 11 | Native.Instance.rocksdb_free(errptr); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /RocksDbSharp/RocksDbSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | RocksDbSharp 5 | netstandard1.6;net40;net45 6 | True 7 | $(RocksDbVersion).$(RocksDbSharpBuild) 8 | $(Version) 9 | $(Version) 10 | .Net Bindings for RocksDb. See the Project Site for more information. (Note: Also install RocksDbNative package to have native binaries included with build) 11 | Warren Falk 12 | Warren Falk 13 | Warren Falk 14 | BSD-2-Clause 15 | https://github.com/warrenfalk/rocksdb-sharp 16 | http://rocksdb.org/static/logo.svg 17 | https://github.com/warrenfalk/rocksdb-sharp.git 18 | rocksdb leveldb embedded database 19 | Copyright 2016 20 | .Net Bindings for RocksDb. See the Project Site for more information. (Note: Also install RocksDbNative package to have native binaries included with build) 21 | False 22 | 23 | 24 | 25 | 26 | 27 | 28 | TRACE;DEBUG 29 | 30 | 31 | 32 | TRACE 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /RocksDbSharp/RocksDbSharpException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace RocksDbSharp 5 | { 6 | public class RocksDbSharpException : Exception 7 | { 8 | public RocksDbSharpException(string message) 9 | : base(message) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /RocksDbSharp/SliceTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RocksDbSharp 4 | { 5 | public class SliceTransform 6 | { 7 | public IntPtr Handle { get; protected set; } 8 | 9 | private SliceTransform(IntPtr handle) 10 | { 11 | this.Handle = handle; 12 | } 13 | 14 | public static SliceTransform CreateFixedPrefix(/*(size_t)*/ ulong fixed_prefix_length) 15 | { 16 | UIntPtr fixedPrefix = (UIntPtr)fixed_prefix_length; 17 | IntPtr handle = Native.Instance.rocksdb_slicetransform_create_fixed_prefix(fixedPrefix); 18 | return new SliceTransform(handle); 19 | } 20 | 21 | public static SliceTransform CreateNoOp() 22 | { 23 | IntPtr handle = Native.Instance.rocksdb_slicetransform_create_noop(); 24 | return new SliceTransform(handle); 25 | } 26 | 27 | ~SliceTransform() 28 | { 29 | if (Handle != IntPtr.Zero) 30 | { 31 | #if !NODESTROY 32 | // Commented out until a solution is found to rocksdb issue #1095 (https://github.com/facebook/rocksdb/issues/1095) 33 | // If you create one of these, use it in an Option which will destroy it when finished 34 | // Otherwise don't create one or it will leak 35 | //Native.Instance.rocksdb_slicetransform_destroy(Handle); 36 | #endif 37 | Handle = IntPtr.Zero; 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /RocksDbSharp/Snapshot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace RocksDbSharp 8 | { 9 | /// 10 | /// A Snapshot is an immutable object and can therefore be safely 11 | /// accessed from multiple threads without any external synchronization. 12 | /// 13 | public class Snapshot : IDisposable 14 | { 15 | private IntPtr dbHandle; 16 | public IntPtr Handle { get; private set; } 17 | 18 | internal Snapshot(IntPtr dbHandle, IntPtr snapshotHandle) 19 | { 20 | this.dbHandle = dbHandle; 21 | Handle = snapshotHandle; 22 | } 23 | 24 | public void Dispose() 25 | { 26 | if (Handle != IntPtr.Zero) 27 | { 28 | #if !NODESTROY 29 | Native.Instance.rocksdb_release_snapshot(dbHandle, Handle); 30 | #endif 31 | Handle = IntPtr.Zero; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /RocksDbSharp/SstFileWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Dynamic; 4 | using System.Text; 5 | using Transitional; 6 | 7 | namespace RocksDbSharp 8 | { 9 | public class SstFileWriter : IDisposable 10 | { 11 | public IntPtr Handle { get; protected set; } 12 | 13 | internal dynamic References { get; } = new ExpandoObject(); 14 | 15 | public SstFileWriter(EnvOptions envOptions = null, ColumnFamilyOptions ioOptions = null) 16 | { 17 | if (envOptions == null) 18 | envOptions = new EnvOptions(); 19 | var opts = ioOptions ?? new ColumnFamilyOptions(); 20 | References.EnvOptions = envOptions; 21 | References.IoOptions = ioOptions; 22 | Handle = Native.Instance.rocksdb_sstfilewriter_create(envOptions.Handle, opts.Handle); 23 | } 24 | 25 | public void Dispose() 26 | { 27 | if (Handle != IntPtr.Zero) 28 | { 29 | var handle = Handle; 30 | Handle = IntPtr.Zero; 31 | Native.Instance.rocksdb_sstfilewriter_destroy(handle); 32 | } 33 | } 34 | 35 | public void Open(string filename) 36 | { 37 | Native.Instance.rocksdb_sstfilewriter_open(Handle, filename); 38 | } 39 | 40 | public void Add(string key, string val) 41 | { 42 | Native.Instance.rocksdb_sstfilewriter_add(Handle, key, val); 43 | } 44 | 45 | public void Add(byte[] key, byte[] val) 46 | { 47 | Native.Instance.rocksdb_sstfilewriter_add(Handle, key, (UIntPtr)key.GetLongLength(0), val, (UIntPtr)val.GetLongLength(0)); 48 | } 49 | 50 | public void Finish() 51 | { 52 | Native.Instance.rocksdb_sstfilewriter_finish(Handle); 53 | } 54 | 55 | public void Put(byte[] key, byte[] val) 56 | { 57 | Native.Instance.rocksdb_sstfilewriter_put(Handle, key, (UIntPtr)key.Length, val, (UIntPtr)val.Length); 58 | } 59 | 60 | public void Merge(byte[] key, byte[] val) 61 | { 62 | Native.Instance.rocksdb_sstfilewriter_merge(Handle, key, (UIntPtr)key.Length, val, (UIntPtr)val.Length); 63 | } 64 | 65 | public void Delete(byte[] key) 66 | { 67 | Native.Instance.rocksdb_sstfilewriter_delete(Handle, key, (UIntPtr)key.Length); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /RocksDbSharp/Transitional.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Reflection.Emit; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | /// 8 | /// The purpose of this file is to ease the transition from framework to framework. 9 | /// As much as possible, in this shared code project, we'll try to use .Net Core compatible code 10 | /// And then add code here to make that work. 11 | /// When not possible, we'll create our own wrapper functions and then create different implementations 12 | /// based on preprocessor defines 13 | /// 14 | 15 | #if NET40 16 | namespace System 17 | { 18 | /// 19 | /// Shim in the new TypeInfo API as just a reference back to Type 20 | /// 21 | internal class TypeInfo 22 | { 23 | private Type Type { get; } 24 | 25 | internal TypeInfo(Type type) 26 | { 27 | Type = type; 28 | } 29 | 30 | internal MethodInfo GetMethod(string name) => Type.GetMethod(name); 31 | 32 | internal MethodInfo GetMethod(string name, BindingFlags flags) => Type.GetMethod(name, flags); 33 | 34 | internal MethodInfo[] GetMethods(BindingFlags bindingFlags) => Type.GetMethods(bindingFlags); 35 | 36 | public Type AsType() => Type; 37 | 38 | public ConstructorInfo[] GetConstructors() => Type.GetConstructors(); 39 | 40 | public ConstructorInfo GetConstructor(Type[] args) => Type.GetConstructor(args); 41 | 42 | public Assembly Assembly => Type.Assembly; 43 | } 44 | } 45 | #endif 46 | 47 | namespace Transitional 48 | { 49 | internal static class CurrentFramework 50 | { 51 | public static AssemblyBuilder DefineDynamicAssembly(AssemblyName name, AssemblyBuilderAccess access) 52 | #if NET40 53 | => AppDomain.CurrentDomain.DefineDynamicAssembly(name, access); 54 | #else 55 | => AssemblyBuilder.DefineDynamicAssembly(name, access); 56 | #endif 57 | 58 | public static unsafe string CreateString(sbyte* value, int startIndex, int length, System.Text.Encoding enc) 59 | #if NETSTANDARD1_6 60 | { 61 | int vlength = enc.GetCharCount((byte*)value, length); 62 | if (vlength == 0) 63 | { 64 | return string.Empty; 65 | } 66 | fixed (char* v = new char[vlength]) 67 | { 68 | enc.GetChars((byte*)value, length, v, vlength); 69 | return new string(v, 0, vlength); 70 | } 71 | } 72 | #else 73 | => new string(value, startIndex, length, enc); 74 | #endif 75 | 76 | public static T GetDelegateForFunctionPointer(IntPtr ptr) 77 | #if NETSTANDARD1_6 78 | => Marshal.GetDelegateForFunctionPointer(ptr); 79 | #else 80 | => (T)(object)Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)); 81 | #endif 82 | 83 | public static IntPtr GetFunctionPointerForDelegate(T func) 84 | #if NETSTANDARD1_6 85 | => Marshal.GetFunctionPointerForDelegate(func); 86 | #else 87 | => Marshal.GetFunctionPointerForDelegate((Delegate)(object)func); 88 | #endif 89 | 90 | public static string GetBaseDirectory() 91 | #if NETSTANDARD1_6 92 | => AppContext.BaseDirectory; 93 | #else 94 | => AppDomain.CurrentDomain.BaseDirectory; 95 | #endif 96 | 97 | } 98 | 99 | internal static class TransitionalExtensions 100 | { 101 | #if NET40 || NETSTANDARD1_6 102 | public static long GetLongLength(this T[] array, int dimension) => array.GetLength(dimension); 103 | #endif 104 | 105 | public static T CreateDelegate(this MethodInfo methodInfo) 106 | #if NET40 107 | => (T)(object)Delegate.CreateDelegate(typeof(T), methodInfo); 108 | #else 109 | => (T)(object)methodInfo.CreateDelegate(typeof(T)); 110 | #endif 111 | 112 | 113 | #if NET40 114 | public static TypeInfo CreateTypeInfo(this TypeBuilder builder) 115 | { 116 | var type = builder.CreateType(); 117 | return new TypeInfo(type); 118 | } 119 | 120 | public static TypeInfo GetTypeInfo(this Type type) 121 | { 122 | return new TypeInfo(type); 123 | } 124 | #endif 125 | 126 | #if NET45 || NET40 127 | public unsafe static string GetString(this Encoding encoding, byte* bytes, int count) 128 | { 129 | var ptr = new IntPtr((void*)bytes); 130 | if (ReferenceEquals(encoding, Encoding.ASCII)) 131 | return Marshal.PtrToStringAnsi(ptr, count); 132 | // An ugly double copy which is necessary in older frameworks without the unsafe GetString() 133 | byte[] temp = new byte[count]; 134 | Marshal.Copy(ptr, temp, 0, count); 135 | return encoding.GetString(temp); 136 | } 137 | #endif 138 | } 139 | } 140 | 141 | 142 | #if !NETSTANDARD1_6 143 | namespace System.Runtime.InteropServices 144 | { 145 | public static class OSPlatform 146 | { 147 | public static string Linux { get; } = "Linux"; 148 | public static string OSX { get; } = "OSX"; 149 | public static string Windows { get; } = "Windows"; 150 | } 151 | 152 | public enum Architecture 153 | { 154 | X86 = 0, 155 | X64 = 1, 156 | Arm = 2, 157 | Arm64 = 3 158 | } 159 | 160 | internal static class RuntimeInformation 161 | { 162 | internal static bool IsOSPlatform(string osplatform) 163 | { 164 | switch ((int)Environment.OSVersion.Platform) 165 | { 166 | case (int)PlatformID.Win32Windows: // Win9x supported? 167 | case (int)PlatformID.Win32S: // Win16 NTVDM on Win x86? 168 | case (int)PlatformID.Win32NT: // Windows NT 169 | case (int)PlatformID.WinCE: 170 | return osplatform == OSPlatform.Windows; 171 | case (int)PlatformID.Unix: 172 | return osplatform == OSPlatform.Linux; 173 | case (int)PlatformID.MacOSX: 174 | case 128: // Mono Mac 175 | return osplatform == OSPlatform.OSX; 176 | default: 177 | return false; 178 | } 179 | } 180 | 181 | internal static Architecture ProcessArchitecture => Environment.Is64BitProcess? Architecture.X64 : Architecture.X86; 182 | } 183 | } 184 | #endif -------------------------------------------------------------------------------- /RocksDbSharp/WriteBatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace RocksDbSharp 5 | { 6 | public interface IWriteBatch : IDisposable 7 | { 8 | IntPtr Handle { get; } 9 | IWriteBatch Clear(); 10 | int Count(); 11 | IWriteBatch Put(string key, string val, Encoding encoding = null); 12 | IWriteBatch Put(byte[] key, byte[] val, ColumnFamilyHandle cf = null); 13 | IWriteBatch Put(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf = null); 14 | unsafe void Put(byte* key, ulong klen, byte* val, ulong vlen, ColumnFamilyHandle cf = null); 15 | IWriteBatch Putv(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes); 16 | IWriteBatch PutvCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes); 17 | IWriteBatch Merge(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf = null); 18 | unsafe void Merge(byte* key, ulong klen, byte* val, ulong vlen, ColumnFamilyHandle cf = null); 19 | IWriteBatch MergeCf(IntPtr columnFamily, byte[] key, ulong klen, byte[] val, ulong vlen); 20 | unsafe void MergeCf(IntPtr columnFamily, byte* key, ulong klen, byte* val, ulong vlen); 21 | IWriteBatch Mergev(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes); 22 | IWriteBatch MergevCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes); 23 | IWriteBatch Delete(byte[] key, ColumnFamilyHandle cf = null); 24 | IWriteBatch Delete(byte[] key, ulong klen, ColumnFamilyHandle cf = null); 25 | unsafe void Delete(byte* key, ulong klen, ColumnFamilyHandle cf = null); 26 | unsafe void Deletev(int numKeys, IntPtr keysList, IntPtr keysListSizes, ColumnFamilyHandle cf = null); 27 | IWriteBatch DeleteRange(byte[] startKey, ulong sklen, byte[] endKey, ulong eklen, ColumnFamilyHandle cf = null); 28 | unsafe void DeleteRange(byte* startKey, ulong sklen, byte* endKey, ulong eklen, ColumnFamilyHandle cf = null); 29 | unsafe void DeleteRangev(int numKeys, IntPtr startKeysList, IntPtr startKeysListSizes, IntPtr endKeysList, IntPtr endKeysListSizes, ColumnFamilyHandle cf = null); 30 | IWriteBatch PutLogData(byte[] blob, ulong len); 31 | IWriteBatch Iterate(IntPtr state, PutDelegate put, DeletedDelegate deleted); 32 | byte[] ToBytes(); 33 | byte[] ToBytes(byte[] buffer, int offset = 0, int size = -1); 34 | void SetSavePoint(); 35 | void RollbackToSavePoint(); 36 | } 37 | 38 | public class WriteBatch : IWriteBatch, IDisposable 39 | { 40 | private IntPtr handle; 41 | private Encoding defaultEncoding = Encoding.UTF8; 42 | 43 | public WriteBatch() 44 | : this(Native.Instance.rocksdb_writebatch_create()) 45 | { 46 | } 47 | 48 | public WriteBatch(byte[] rep, long size = -1) 49 | : this(Native.Instance.rocksdb_writebatch_create_from(rep, size < 0 ? (UIntPtr)rep.Length : (UIntPtr)size)) 50 | { 51 | } 52 | 53 | private WriteBatch(IntPtr handle) 54 | { 55 | this.handle = handle; 56 | } 57 | 58 | public IntPtr Handle { get { return handle; } } 59 | 60 | public void Dispose() 61 | { 62 | if (handle != IntPtr.Zero) 63 | { 64 | #if !NODESTROY 65 | Native.Instance.rocksdb_writebatch_destroy(handle); 66 | #endif 67 | handle = IntPtr.Zero; 68 | } 69 | } 70 | 71 | public WriteBatch Clear() 72 | { 73 | Native.Instance.rocksdb_writebatch_clear(handle); 74 | return this; 75 | } 76 | 77 | public int Count() 78 | { 79 | return Native.Instance.rocksdb_writebatch_count(handle); 80 | } 81 | 82 | public WriteBatch Put(string key, string val, Encoding encoding = null) 83 | { 84 | if (encoding == null) 85 | encoding = defaultEncoding; 86 | Native.Instance.rocksdb_writebatch_put(handle, key, val, encoding); 87 | return this; 88 | } 89 | 90 | public WriteBatch Put(byte[] key, byte[] val, ColumnFamilyHandle cf = null) 91 | { 92 | return Put(key, (ulong)key.Length, val, (ulong)val.Length, cf); 93 | } 94 | 95 | public WriteBatch Put(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf = null) 96 | { 97 | if (cf == null) 98 | Native.Instance.rocksdb_writebatch_put(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 99 | else 100 | Native.Instance.rocksdb_writebatch_put_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 101 | return this; 102 | } 103 | 104 | public unsafe void Put(byte* key, ulong klen, byte* val, ulong vlen, ColumnFamilyHandle cf = null) 105 | { 106 | if (cf == null) 107 | Native.Instance.rocksdb_writebatch_put(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 108 | else 109 | Native.Instance.rocksdb_writebatch_put_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 110 | } 111 | 112 | public WriteBatch Putv(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 113 | { 114 | Native.Instance.rocksdb_writebatch_putv(handle, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 115 | return this; 116 | } 117 | 118 | public WriteBatch PutvCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 119 | { 120 | Native.Instance.rocksdb_writebatch_putv_cf(handle, columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 121 | return this; 122 | } 123 | 124 | public WriteBatch Merge(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf = null) 125 | { 126 | if (cf == null) 127 | Native.Instance.rocksdb_writebatch_merge(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 128 | else 129 | Native.Instance.rocksdb_writebatch_merge_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 130 | return this; 131 | } 132 | 133 | public unsafe void Merge(byte* key, ulong klen, byte* val, ulong vlen, ColumnFamilyHandle cf = null) 134 | { 135 | if (cf == null) 136 | Native.Instance.rocksdb_writebatch_merge(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 137 | else 138 | Native.Instance.rocksdb_writebatch_merge_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 139 | } 140 | 141 | public WriteBatch MergeCf(IntPtr columnFamily, byte[] key, ulong klen, byte[] val, ulong vlen) 142 | { 143 | Native.Instance.rocksdb_writebatch_merge_cf(handle, columnFamily, key, (UIntPtr)klen, val, (UIntPtr)vlen); 144 | return this; 145 | } 146 | 147 | public unsafe void MergeCf(IntPtr columnFamily, byte* key, ulong klen, byte* val, ulong vlen) 148 | { 149 | Native.Instance.rocksdb_writebatch_merge_cf(handle, columnFamily, key, (UIntPtr)klen, val, (UIntPtr)vlen); 150 | } 151 | 152 | public WriteBatch Mergev(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 153 | { 154 | Native.Instance.rocksdb_writebatch_mergev(handle, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 155 | return this; 156 | } 157 | 158 | public WriteBatch MergevCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 159 | { 160 | Native.Instance.rocksdb_writebatch_mergev_cf(handle, columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 161 | return this; 162 | } 163 | 164 | public WriteBatch Delete(byte[] key, ColumnFamilyHandle cf = null) 165 | { 166 | return Delete(key, (ulong)key.Length, cf); 167 | } 168 | 169 | public WriteBatch Delete(byte[] key, ulong klen, ColumnFamilyHandle cf = null) 170 | { 171 | if (cf == null) 172 | Native.Instance.rocksdb_writebatch_delete(handle, key, (UIntPtr)klen); 173 | else 174 | Native.Instance.rocksdb_writebatch_delete_cf(handle, cf.Handle, key, (UIntPtr)klen); 175 | return this; 176 | } 177 | 178 | public unsafe void Delete(byte* key, ulong klen, ColumnFamilyHandle cf = null) 179 | { 180 | if (cf == null) 181 | Native.Instance.rocksdb_writebatch_delete(handle, key, (UIntPtr)klen); 182 | else 183 | Native.Instance.rocksdb_writebatch_delete_cf(handle, cf.Handle, key, (UIntPtr)klen); 184 | } 185 | 186 | public unsafe void Deletev(int numKeys, IntPtr keysList, IntPtr keysListSizes, ColumnFamilyHandle cf = null) 187 | { 188 | if (cf == null) 189 | Native.Instance.rocksdb_writebatch_deletev(handle, numKeys, keysList, keysListSizes); 190 | else 191 | Native.Instance.rocksdb_writebatch_deletev_cf(handle, cf.Handle, numKeys, keysList, keysListSizes); 192 | } 193 | 194 | public WriteBatch DeleteRange(byte[] startKey, ulong sklen, byte[] endKey, ulong eklen, ColumnFamilyHandle cf = null) 195 | { 196 | if (cf == null) 197 | Native.Instance.rocksdb_writebatch_delete_range(handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 198 | else 199 | Native.Instance.rocksdb_writebatch_delete_range_cf(handle, cf.Handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 200 | return this; 201 | } 202 | 203 | public unsafe void DeleteRange(byte* startKey, ulong sklen, byte* endKey, ulong eklen, ColumnFamilyHandle cf = null) 204 | { 205 | if (cf == null) 206 | Native.Instance.rocksdb_writebatch_delete_range(handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 207 | else 208 | Native.Instance.rocksdb_writebatch_delete_range_cf(handle, cf.Handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 209 | } 210 | 211 | public unsafe void DeleteRangev(int numKeys, IntPtr startKeysList, IntPtr startKeysListSizes, IntPtr endKeysList, IntPtr endKeysListSizes, ColumnFamilyHandle cf = null) 212 | { 213 | if (cf == null) 214 | Native.Instance.rocksdb_writebatch_delete_rangev(handle, numKeys, startKeysList, startKeysListSizes, endKeysList, endKeysListSizes); 215 | else 216 | Native.Instance.rocksdb_writebatch_delete_rangev_cf(handle, cf.Handle, numKeys, startKeysList, startKeysListSizes, endKeysList, endKeysListSizes); 217 | } 218 | 219 | public WriteBatch PutLogData(byte[] blob, ulong len) 220 | { 221 | Native.Instance.rocksdb_writebatch_put_log_data(handle, blob, (UIntPtr)len); 222 | return this; 223 | } 224 | 225 | public WriteBatch Iterate(IntPtr state, PutDelegate put, DeletedDelegate deleted) 226 | { 227 | Native.Instance.rocksdb_writebatch_iterate(handle, state, put, deleted); 228 | return this; 229 | } 230 | 231 | /// 232 | /// Get the write batch as bytes 233 | /// 234 | /// 235 | public byte[] ToBytes() 236 | { 237 | return Native.Instance.rocksdb_writebatch_data(handle); 238 | } 239 | 240 | /// 241 | /// Get the write batch as bytes 242 | /// 243 | /// 244 | /// 245 | /// 246 | /// null if size was not large enough to hold the data 247 | public byte[] ToBytes(byte[] buffer, int offset = 0, int size = -1) 248 | { 249 | if (size < 0) 250 | size = buffer.Length; 251 | if (Native.Instance.rocksdb_writebatch_data(handle, buffer, 0, size) > 0) 252 | return buffer; 253 | return null; 254 | } 255 | 256 | public void SetSavePoint() 257 | { 258 | Native.Instance.rocksdb_writebatch_set_save_point(handle); 259 | } 260 | 261 | public void RollbackToSavePoint() 262 | { 263 | Native.Instance.rocksdb_writebatch_rollback_to_save_point(handle); 264 | } 265 | 266 | public void PopSavePoint() 267 | { 268 | Native.Instance.rocksdb_writebatch_pop_save_point(handle); 269 | } 270 | 271 | IWriteBatch IWriteBatch.Clear() 272 | => Clear(); 273 | IWriteBatch IWriteBatch.Put(string key, string val, Encoding encoding) 274 | => Put(key, val, encoding); 275 | IWriteBatch IWriteBatch.Put(byte[] key, byte[] val, ColumnFamilyHandle cf) 276 | => Put(key, val, cf); 277 | IWriteBatch IWriteBatch.Put(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf) 278 | => Put(key, klen, val, vlen, cf); 279 | IWriteBatch IWriteBatch.Putv(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 280 | => Putv(numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 281 | IWriteBatch IWriteBatch.PutvCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 282 | => PutvCf(columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 283 | IWriteBatch IWriteBatch.Merge(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf) 284 | => Merge(key, klen, val, vlen, cf); 285 | IWriteBatch IWriteBatch.MergeCf(IntPtr columnFamily, byte[] key, ulong klen, byte[] val, ulong vlen) 286 | => MergeCf(columnFamily, key, klen, val, vlen); 287 | IWriteBatch IWriteBatch.Mergev(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 288 | => Mergev(numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 289 | IWriteBatch IWriteBatch.MergevCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 290 | => MergevCf(columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 291 | IWriteBatch IWriteBatch.Delete(byte[] key, ColumnFamilyHandle cf) 292 | => Delete(key, cf); 293 | IWriteBatch IWriteBatch.Delete(byte[] key, ulong klen, ColumnFamilyHandle cf) 294 | => Delete(key, klen, cf); 295 | IWriteBatch IWriteBatch.DeleteRange(byte[] startKey, ulong sklen, byte[] endKey, ulong eklen, ColumnFamilyHandle cf) 296 | => DeleteRange(startKey, sklen, endKey, eklen, cf); 297 | IWriteBatch IWriteBatch.PutLogData(byte[] blob, ulong len) 298 | => PutLogData(blob, len); 299 | IWriteBatch IWriteBatch.Iterate(IntPtr state, PutDelegate put, DeletedDelegate deleted) 300 | => Iterate(state, put, deleted); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /RocksDbSharp/WriteBatchWithIndex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | using Transitional; 6 | 7 | namespace RocksDbSharp 8 | { 9 | public class WriteBatchWithIndex : IWriteBatch 10 | { 11 | private IntPtr handle; 12 | private Encoding defaultEncoding = Encoding.UTF8; 13 | 14 | public WriteBatchWithIndex(ulong reservedBytes = 0, bool overwriteKeys = true) 15 | : this(Native.Instance.rocksdb_writebatch_wi_create((UIntPtr)reservedBytes, overwriteKeys)) 16 | { 17 | } 18 | 19 | private WriteBatchWithIndex(IntPtr handle) 20 | { 21 | this.handle = handle; 22 | } 23 | 24 | public IntPtr Handle { get { return handle; } } 25 | 26 | public void Dispose() 27 | { 28 | if (handle != IntPtr.Zero) 29 | { 30 | #if !NODESTROY 31 | Native.Instance.rocksdb_writebatch_wi_destroy(handle); 32 | #endif 33 | handle = IntPtr.Zero; 34 | } 35 | } 36 | 37 | public WriteBatchWithIndex Clear() 38 | { 39 | Native.Instance.rocksdb_writebatch_wi_clear(handle); 40 | return this; 41 | } 42 | 43 | public int Count() 44 | { 45 | return Native.Instance.rocksdb_writebatch_wi_count(handle); 46 | } 47 | 48 | public Iterator CreateIteratorWithBase(Iterator baseIterator, ColumnFamilyHandle cf = null) 49 | { 50 | var handle = cf == null 51 | ? Native.Instance.rocksdb_writebatch_wi_create_iterator_with_base(Handle, baseIterator.Handle) 52 | : Native.Instance.rocksdb_writebatch_wi_create_iterator_with_base_cf(Handle, baseIterator.Handle, cf.Handle); 53 | return new Iterator(handle); 54 | } 55 | 56 | public string Get(string key, ColumnFamilyHandle cf = null, OptionsHandle options = null, Encoding encoding = null) 57 | { 58 | return Native.Instance.rocksdb_writebatch_wi_get_from_batch(Handle, (options ?? RocksDb.DefaultOptions).Handle, key, cf, encoding ?? defaultEncoding); 59 | } 60 | 61 | public byte[] Get(byte[] key, ColumnFamilyHandle cf = null, OptionsHandle options = null) 62 | { 63 | return Get(key, (ulong)key.GetLongLength(0), cf, options); 64 | } 65 | 66 | public byte[] Get(byte[] key, ulong keyLength, ColumnFamilyHandle cf = null, OptionsHandle options = null) 67 | { 68 | return Native.Instance.rocksdb_writebatch_wi_get_from_batch(Handle, (options ?? RocksDb.DefaultOptions).Handle, key, keyLength, cf); 69 | } 70 | 71 | public ulong Get(byte[] key, byte[] buffer, ulong offset, ulong length, ColumnFamilyHandle cf = null, OptionsHandle options = null) 72 | { 73 | return Get(key, (ulong)key.GetLongLength(0), buffer, offset, length, cf, options); 74 | } 75 | 76 | public ulong Get(byte[] key, ulong keyLength, byte[] buffer, ulong offset, ulong length, ColumnFamilyHandle cf = null, OptionsHandle options = null) 77 | { 78 | unsafe 79 | { 80 | var ptr = Native.Instance.rocksdb_writebatch_wi_get_from_batch(Handle, (options ?? RocksDb.DefaultOptions).Handle, key, keyLength, out ulong valLength, cf); 81 | valLength = Math.Min(length, valLength); 82 | Marshal.Copy(ptr, buffer, (int)offset, (int)valLength); 83 | Native.Instance.rocksdb_free(ptr); 84 | return valLength; 85 | } 86 | } 87 | 88 | public string Get(RocksDb db, string key, ColumnFamilyHandle cf = null, ReadOptions options = null, Encoding encoding = null) 89 | { 90 | return Native.Instance.rocksdb_writebatch_wi_get_from_batch_and_db(Handle, db.Handle, (options ?? RocksDb.DefaultReadOptions).Handle, key, cf, encoding ?? defaultEncoding); 91 | } 92 | 93 | public byte[] Get(RocksDb db, byte[] key, ColumnFamilyHandle cf = null, ReadOptions options = null) 94 | { 95 | return Get(db, key, (ulong)key.GetLongLength(0), cf, options); 96 | } 97 | 98 | public byte[] Get(RocksDb db, byte[] key, ulong keyLength, ColumnFamilyHandle cf = null, ReadOptions options = null) 99 | { 100 | return Native.Instance.rocksdb_writebatch_wi_get_from_batch_and_db(Handle, db.Handle, (options ?? RocksDb.DefaultReadOptions).Handle, key, keyLength, cf); 101 | } 102 | 103 | public ulong Get(RocksDb db, byte[] key, byte[] buffer, ulong offset, ulong length, ColumnFamilyHandle cf = null, ReadOptions options = null) 104 | { 105 | return Get(db, key, (ulong)key.GetLongLength(0), buffer, offset, length, cf, options); 106 | } 107 | 108 | public ulong Get(RocksDb db, byte[] key, ulong keyLength, byte[] buffer, ulong offset, ulong length, ColumnFamilyHandle cf = null, ReadOptions options = null) 109 | { 110 | unsafe 111 | { 112 | var ptr = Native.Instance.rocksdb_writebatch_wi_get_from_batch_and_db(Handle, db.Handle, (options ?? RocksDb.DefaultReadOptions).Handle, key, keyLength, out ulong valLength, cf); 113 | valLength = Math.Min(length, valLength); 114 | Marshal.Copy(ptr, buffer, (int)offset, (int)valLength); 115 | Native.Instance.rocksdb_free(ptr); 116 | return valLength; 117 | } 118 | } 119 | 120 | public Iterator NewIterator(Iterator baseIterator, ColumnFamilyHandle cf = null) 121 | { 122 | IntPtr iteratorHandle = cf == null 123 | ? Native.Instance.rocksdb_writebatch_wi_create_iterator_with_base(Handle, baseIterator.Handle) 124 | : Native.Instance.rocksdb_writebatch_wi_create_iterator_with_base_cf(Handle, baseIterator.Handle, cf.Handle); 125 | baseIterator.Detach(); 126 | // Note: passing in base iterator here only to ensure that it is not collected before the iterator 127 | return new Iterator(iteratorHandle); 128 | } 129 | 130 | public WriteBatchWithIndex Put(string key, string val, Encoding encoding = null) 131 | { 132 | if (encoding == null) 133 | encoding = defaultEncoding; 134 | Native.Instance.rocksdb_writebatch_wi_put(handle, key, val, encoding); 135 | return this; 136 | } 137 | 138 | public WriteBatchWithIndex Put(byte[] key, byte[] val, ColumnFamilyHandle cf = null) 139 | { 140 | return Put(key, (ulong)key.Length, val, (ulong)val.Length, cf); 141 | } 142 | 143 | public WriteBatchWithIndex Put(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf = null) 144 | { 145 | if (cf == null) 146 | Native.Instance.rocksdb_writebatch_wi_put(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 147 | else 148 | Native.Instance.rocksdb_writebatch_wi_put_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 149 | return this; 150 | } 151 | 152 | public unsafe void Put(byte* key, ulong klen, byte* val, ulong vlen, ColumnFamilyHandle cf = null) 153 | { 154 | if (cf == null) 155 | Native.Instance.rocksdb_writebatch_wi_put(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 156 | else 157 | Native.Instance.rocksdb_writebatch_wi_put_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 158 | } 159 | 160 | public WriteBatchWithIndex Putv(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 161 | { 162 | Native.Instance.rocksdb_writebatch_wi_putv(handle, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 163 | return this; 164 | } 165 | 166 | public WriteBatchWithIndex PutvCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 167 | { 168 | Native.Instance.rocksdb_writebatch_wi_putv_cf(handle, columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 169 | return this; 170 | } 171 | 172 | public WriteBatchWithIndex Merge(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf = null) 173 | { 174 | if (cf == null) 175 | Native.Instance.rocksdb_writebatch_wi_merge(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 176 | else 177 | Native.Instance.rocksdb_writebatch_wi_merge_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 178 | return this; 179 | } 180 | 181 | public unsafe void Merge(byte* key, ulong klen, byte* val, ulong vlen, ColumnFamilyHandle cf = null) 182 | { 183 | if (cf == null) 184 | Native.Instance.rocksdb_writebatch_wi_merge(handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 185 | else 186 | Native.Instance.rocksdb_writebatch_wi_merge_cf(handle, cf.Handle, key, (UIntPtr)klen, val, (UIntPtr)vlen); 187 | } 188 | 189 | public WriteBatchWithIndex MergeCf(IntPtr columnFamily, byte[] key, ulong klen, byte[] val, ulong vlen) 190 | { 191 | Native.Instance.rocksdb_writebatch_wi_merge_cf(handle, columnFamily, key, (UIntPtr)klen, val, (UIntPtr)vlen); 192 | return this; 193 | } 194 | 195 | public unsafe void MergeCf(IntPtr columnFamily, byte* key, ulong klen, byte* val, ulong vlen) 196 | { 197 | Native.Instance.rocksdb_writebatch_wi_merge_cf(handle, columnFamily, key, (UIntPtr)klen, val, (UIntPtr)vlen); 198 | } 199 | 200 | public WriteBatchWithIndex Mergev(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 201 | { 202 | Native.Instance.rocksdb_writebatch_wi_mergev(handle, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 203 | return this; 204 | } 205 | 206 | public WriteBatchWithIndex MergevCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 207 | { 208 | Native.Instance.rocksdb_writebatch_wi_mergev_cf(handle, columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 209 | return this; 210 | } 211 | 212 | public WriteBatchWithIndex Delete(byte[] key, ColumnFamilyHandle cf = null) 213 | { 214 | return Delete(key, (ulong)key.Length, cf); 215 | } 216 | 217 | public WriteBatchWithIndex Delete(byte[] key, ulong klen, ColumnFamilyHandle cf = null) 218 | { 219 | if (cf == null) 220 | Native.Instance.rocksdb_writebatch_wi_delete(handle, key, (UIntPtr)klen); 221 | else 222 | Native.Instance.rocksdb_writebatch_wi_delete_cf(handle, cf.Handle, key, (UIntPtr)klen); 223 | return this; 224 | } 225 | 226 | public unsafe void Delete(byte* key, ulong klen, ColumnFamilyHandle cf = null) 227 | { 228 | if (cf == null) 229 | Native.Instance.rocksdb_writebatch_wi_delete(handle, key, (UIntPtr)klen); 230 | else 231 | Native.Instance.rocksdb_writebatch_wi_delete_cf(handle, cf.Handle, key, (UIntPtr)klen); 232 | } 233 | 234 | public unsafe void Deletev(int numKeys, IntPtr keysList, IntPtr keysListSizes, ColumnFamilyHandle cf = null) 235 | { 236 | if (cf == null) 237 | Native.Instance.rocksdb_writebatch_wi_deletev(handle, numKeys, keysList, keysListSizes); 238 | else 239 | Native.Instance.rocksdb_writebatch_wi_deletev_cf(handle, cf.Handle, numKeys, keysList, keysListSizes); 240 | } 241 | 242 | public WriteBatchWithIndex DeleteRange(byte[] startKey, ulong sklen, byte[] endKey, ulong eklen, ColumnFamilyHandle cf = null) 243 | { 244 | if (cf == null) 245 | Native.Instance.rocksdb_writebatch_wi_delete_range(handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 246 | else 247 | Native.Instance.rocksdb_writebatch_wi_delete_range_cf(handle, cf.Handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 248 | return this; 249 | } 250 | 251 | public unsafe void DeleteRange(byte* startKey, ulong sklen, byte* endKey, ulong eklen, ColumnFamilyHandle cf = null) 252 | { 253 | if (cf == null) 254 | Native.Instance.rocksdb_writebatch_wi_delete_range(handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 255 | else 256 | Native.Instance.rocksdb_writebatch_wi_delete_range_cf(handle, cf.Handle, startKey, (UIntPtr)sklen, endKey, (UIntPtr)eklen); 257 | } 258 | 259 | public unsafe void DeleteRangev(int numKeys, IntPtr startKeysList, IntPtr startKeysListSizes, IntPtr endKeysList, IntPtr endKeysListSizes, ColumnFamilyHandle cf = null) 260 | { 261 | if (cf == null) 262 | Native.Instance.rocksdb_writebatch_wi_delete_rangev(handle, numKeys, startKeysList, startKeysListSizes, endKeysList, endKeysListSizes); 263 | else 264 | Native.Instance.rocksdb_writebatch_wi_delete_rangev_cf(handle, cf.Handle, numKeys, startKeysList, startKeysListSizes, endKeysList, endKeysListSizes); 265 | } 266 | 267 | public WriteBatchWithIndex PutLogData(byte[] blob, ulong len) 268 | { 269 | Native.Instance.rocksdb_writebatch_wi_put_log_data(handle, blob, (UIntPtr)len); 270 | return this; 271 | } 272 | 273 | public WriteBatchWithIndex Iterate(IntPtr state, PutDelegate put, DeletedDelegate deleted) 274 | { 275 | Native.Instance.rocksdb_writebatch_wi_iterate(handle, state, put, deleted); 276 | return this; 277 | } 278 | 279 | /// 280 | /// Get the write batch as bytes 281 | /// 282 | /// 283 | public byte[] ToBytes() 284 | { 285 | return Native.Instance.rocksdb_writebatch_wi_data(handle); 286 | } 287 | 288 | /// 289 | /// Get the write batch as bytes 290 | /// 291 | /// 292 | /// 293 | /// 294 | /// null if size was not large enough to hold the data 295 | public byte[] ToBytes(byte[] buffer, int offset = 0, int size = -1) 296 | { 297 | if (size < 0) 298 | size = buffer.Length; 299 | if (Native.Instance.rocksdb_writebatch_wi_data(handle, buffer, 0, size) > 0) 300 | return buffer; 301 | return null; 302 | } 303 | 304 | public void SetSavePoint() 305 | { 306 | Native.Instance.rocksdb_writebatch_wi_set_save_point(handle); 307 | } 308 | 309 | public void RollbackToSavePoint() 310 | { 311 | Native.Instance.rocksdb_writebatch_wi_rollback_to_save_point(handle); 312 | } 313 | 314 | 315 | IWriteBatch IWriteBatch.Clear() 316 | => Clear(); 317 | IWriteBatch IWriteBatch.Put(string key, string val, Encoding encoding) 318 | => Put(key, val, encoding); 319 | IWriteBatch IWriteBatch.Put(byte[] key, byte[] val, ColumnFamilyHandle cf) 320 | => Put(key, val, cf); 321 | IWriteBatch IWriteBatch.Put(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf) 322 | => Put(key, klen, val, vlen, cf); 323 | IWriteBatch IWriteBatch.Putv(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 324 | => Putv(numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 325 | IWriteBatch IWriteBatch.PutvCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 326 | => PutvCf(columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 327 | IWriteBatch IWriteBatch.Merge(byte[] key, ulong klen, byte[] val, ulong vlen, ColumnFamilyHandle cf) 328 | => Merge(key, klen, val, vlen, cf); 329 | IWriteBatch IWriteBatch.MergeCf(IntPtr columnFamily, byte[] key, ulong klen, byte[] val, ulong vlen) 330 | => MergeCf(columnFamily, key, klen, val, vlen); 331 | IWriteBatch IWriteBatch.Mergev(int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 332 | => Mergev(numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 333 | IWriteBatch IWriteBatch.MergevCf(IntPtr columnFamily, int numKeys, IntPtr keysList, IntPtr keysListSizes, int numValues, IntPtr valuesList, IntPtr valuesListSizes) 334 | => MergevCf(columnFamily, numKeys, keysList, keysListSizes, numValues, valuesList, valuesListSizes); 335 | IWriteBatch IWriteBatch.Delete(byte[] key, ColumnFamilyHandle cf) 336 | => Delete(key, cf); 337 | IWriteBatch IWriteBatch.Delete(byte[] key, ulong klen, ColumnFamilyHandle cf) 338 | => Delete(key, klen, cf); 339 | IWriteBatch IWriteBatch.DeleteRange(byte[] startKey, ulong sklen, byte[] endKey, ulong eklen, ColumnFamilyHandle cf) 340 | => DeleteRange(startKey, sklen, endKey, eklen, cf); 341 | IWriteBatch IWriteBatch.PutLogData(byte[] blob, ulong len) 342 | => PutLogData(blob, len); 343 | IWriteBatch IWriteBatch.Iterate(IntPtr state, PutDelegate put, DeletedDelegate deleted) 344 | => Iterate(state, put, deleted); 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /RocksDbSharp/WriteOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RocksDbSharp 4 | { 5 | public class WriteOptions 6 | { 7 | public WriteOptions() 8 | { 9 | Handle = Native.Instance.rocksdb_writeoptions_create(); 10 | } 11 | 12 | public IntPtr Handle { get; protected set; } 13 | 14 | ~WriteOptions() 15 | { 16 | if (Handle != IntPtr.Zero) 17 | { 18 | #if !NODESTROY 19 | Native.Instance.rocksdb_writeoptions_destroy(Handle); 20 | #endif 21 | Handle = IntPtr.Zero; 22 | } 23 | } 24 | 25 | public WriteOptions SetSync(bool value) 26 | { 27 | Native.Instance.rocksdb_writeoptions_set_sync(Handle, value); 28 | return this; 29 | } 30 | 31 | public WriteOptions DisableWal(int disable) 32 | { 33 | Native.Instance.rocksdb_writeoptions_disable_WAL(Handle, disable); 34 | return this; 35 | } 36 | 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Versions.targets.include: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6.2.2 4 | 0 5 | 0 6 | 7 | -------------------------------------------------------------------------------- /examples/ColumnFamilyExample/ColumnFamilyExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net45 4 | Exe 5 | x64 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/ColumnFamilyExample/Program.cs: -------------------------------------------------------------------------------- 1 | using RocksDbSharp; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ColumnFamilyExample 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | string temp = Path.GetTempPath(); 16 | string path = Environment.ExpandEnvironmentVariables(Path.Combine(temp, "rocksdb_cf_example")); 17 | 18 | var options = new DbOptions() 19 | .SetCreateIfMissing(true) 20 | .SetCreateMissingColumnFamilies(true); 21 | 22 | var columnFamilies = new ColumnFamilies 23 | { 24 | { "reverse", new ColumnFamilyOptions() }, 25 | }; 26 | 27 | using (var db = RocksDb.Open(options, path, columnFamilies)) 28 | { 29 | var reverse = db.GetColumnFamily("reverse"); 30 | 31 | db.Put("one", "uno"); 32 | db.Put("two", "dos"); 33 | db.Put("three", "tres"); 34 | 35 | db.Put("uno", "one", cf: reverse); 36 | db.Put("dos", "two", cf: reverse); 37 | db.Put("tres", "three", cf: reverse); 38 | } 39 | 40 | using (var db = RocksDb.Open(options, path, columnFamilies)) 41 | { 42 | var reverse = db.GetColumnFamily("reverse"); 43 | 44 | string uno = db.Get("one"); 45 | string one = db.Get("uno", cf: reverse); 46 | string nada; 47 | nada = db.Get("uno"); 48 | nada = db.Get("one", cf: reverse); 49 | } 50 | 51 | using (var db = RocksDb.Open(options, path, columnFamilies)) 52 | { 53 | db.DropColumnFamily("reverse"); 54 | var reverse = db.CreateColumnFamily(new ColumnFamilyOptions(), "reverse"); 55 | var nada = db.Get("uno", cf: reverse); 56 | db.Put("red", "rouge", cf: reverse); 57 | } 58 | 59 | using (var db = RocksDb.Open(options, path, columnFamilies)) 60 | { 61 | var reverse = db.GetColumnFamily("reverse"); 62 | var nada = db.Get("uno", cf: reverse); 63 | var rouge = db.Get("red", cf: reverse); 64 | } 65 | 66 | using (var db = RocksDb.OpenReadOnly(options, path, columnFamilies, false)) 67 | { 68 | string uno = db.Get("one"); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/PrefixExample/PrefixExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net45 4 | Exe 5 | x64 6 | 1.0.0 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/PrefixExample/Program.cs: -------------------------------------------------------------------------------- 1 | using RocksDbSharp; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace PrefixExample 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | string temp = Path.GetTempPath(); 14 | string path = Environment.ExpandEnvironmentVariables(Path.Combine(temp, "rocksdb_prefix_example")); 15 | var bbto = new BlockBasedTableOptions() 16 | .SetFilterPolicy(BloomFilterPolicy.Create(10, false)) 17 | .SetWholeKeyFiltering(false) 18 | ; 19 | var options = new DbOptions() 20 | .SetCreateIfMissing(true) 21 | .SetCreateMissingColumnFamilies(true) 22 | ; 23 | var columnFamilies = new ColumnFamilies 24 | { 25 | { "default", new ColumnFamilyOptions().OptimizeForPointLookup(256) }, 26 | { "test", new ColumnFamilyOptions() 27 | //.SetWriteBufferSize(writeBufferSize) 28 | //.SetMaxWriteBufferNumber(maxWriteBufferNumber) 29 | //.SetMinWriteBufferNumberToMerge(minWriteBufferNumberToMerge) 30 | .SetMemtableHugePageSize(2 * 1024 * 1024) 31 | .SetPrefixExtractor(SliceTransform.CreateFixedPrefix((ulong)8)) 32 | .SetBlockBasedTableFactory(bbto) 33 | }, 34 | }; 35 | using (var db = RocksDb.Open(options, path, columnFamilies)) 36 | { 37 | var cf = db.GetColumnFamily("test"); 38 | 39 | db.Put("00000000Zero", "", cf: cf); 40 | db.Put("00000000One", "", cf: cf); 41 | db.Put("00000000Two", "", cf: cf); 42 | db.Put("00000000Three", "", cf: cf); 43 | db.Put("00000001Red", "", cf: cf); 44 | db.Put("00000001Green", "", cf: cf); 45 | db.Put("00000001Black", "", cf: cf); 46 | db.Put("00000002Apple", "", cf: cf); 47 | db.Put("00000002Cranberry", "", cf: cf); 48 | db.Put("00000002Banana", "", cf: cf); 49 | 50 | var readOptions = new ReadOptions(); 51 | using (var iter = db.NewIterator(readOptions: readOptions, cf: cf)) 52 | { 53 | GC.Collect(); 54 | GC.WaitForPendingFinalizers(); 55 | var b = Encoding.UTF8.GetBytes("00000001"); 56 | iter.Seek(b); 57 | while (iter.Valid()) 58 | { 59 | Console.WriteLine(iter.StringKey()); 60 | iter.Next(); 61 | } 62 | } 63 | } 64 | Console.WriteLine("Done..."); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/SimpleExampleHighLevel/Program.cs: -------------------------------------------------------------------------------- 1 | using RocksDbSharp; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace SimpleExampleHighLevel 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | string temp = Path.GetTempPath(); 14 | string path = Environment.ExpandEnvironmentVariables(Path.Combine(temp, "rocksdb_simple_hl_example")); 15 | // the Options class contains a set of configurable DB options 16 | // that determines the behavior of a database 17 | // Why is the syntax, SetXXX(), not very C#-like? See Options for an explanation 18 | var options = new DbOptions() 19 | .SetCreateIfMissing(true) 20 | .EnableStatistics(); 21 | using (var db = RocksDb.Open(options, path)) 22 | { 23 | try 24 | { 25 | { 26 | // With strings 27 | string value = db.Get("key"); 28 | db.Put("key", "value"); 29 | value = db.Get("key"); 30 | string iWillBeNull = db.Get("non-existent-key"); 31 | db.Remove("key"); 32 | } 33 | 34 | { 35 | // With bytes 36 | var key = Encoding.UTF8.GetBytes("key"); 37 | byte[] value = Encoding.UTF8.GetBytes("value"); 38 | db.Put(key, value); 39 | value = db.Get(key); 40 | byte[] iWillBeNull = db.Get(new byte[] { 0, 1, 2 }); 41 | db.Remove(key); 42 | 43 | db.Put(key, new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }); 44 | } 45 | 46 | { 47 | // With buffers 48 | var key = Encoding.UTF8.GetBytes("key"); 49 | var buffer = new byte[100]; 50 | long length = db.Get(key, buffer, 0, buffer.Length); 51 | } 52 | 53 | { 54 | // Removal of non-existent keys 55 | db.Remove("I don't exist"); 56 | } 57 | 58 | { 59 | // Write batches 60 | // With strings 61 | using (WriteBatch batch = new WriteBatch() 62 | .Put("one", "uno") 63 | .Put("two", "deuce") 64 | .Put("two", "dos") 65 | .Put("three", "tres")) 66 | { 67 | db.Write(batch); 68 | } 69 | 70 | // With bytes 71 | var utf8 = Encoding.UTF8; 72 | using (WriteBatch batch = new WriteBatch() 73 | .Put(utf8.GetBytes("four"), new byte[] { 4, 4, 4 } ) 74 | .Put(utf8.GetBytes("five"), new byte[] { 5, 5, 5 } )) 75 | { 76 | db.Write(batch); 77 | } 78 | } 79 | 80 | { 81 | // Snapshots 82 | using (var snapshot = db.CreateSnapshot()) 83 | { 84 | var before = db.Get("one"); 85 | db.Put("one", "1"); 86 | 87 | var useSnapshot = new ReadOptions() 88 | .SetSnapshot(snapshot); 89 | 90 | // the database value was written 91 | Debug.Assert(db.Get("one") == "1"); 92 | // but the snapshot still sees the old version 93 | var after = db.Get("one", readOptions: useSnapshot); 94 | Debug.Assert(after == before); 95 | } 96 | } 97 | 98 | var two = db.Get("two"); 99 | Debug.Assert(two == "dos"); 100 | 101 | { 102 | // Iterators 103 | using (var iterator = db.NewIterator( 104 | readOptions: new ReadOptions() 105 | .SetIterateUpperBound("t") 106 | )) 107 | { 108 | iterator.Seek("k"); 109 | Debug.Assert(iterator.Valid()); 110 | Debug.Assert(iterator.StringKey() == "key"); 111 | iterator.Next(); 112 | Debug.Assert(iterator.Valid()); 113 | Debug.Assert(iterator.StringKey() == "one"); 114 | Debug.Assert(iterator.StringValue() == "1"); 115 | iterator.Next(); 116 | Debug.Assert(!iterator.Valid()); 117 | } 118 | } 119 | 120 | } 121 | catch (RocksDbException) 122 | { 123 | 124 | } 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /examples/SimpleExampleHighLevel/SimpleExampleHighLevel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net45 4 | Exe 5 | x64 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /examples/SimpleExampleLowLevel/Program.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Simple Low Level Example. 3 | 4 | The low level api has been ported from the rocksdb C API (see Native0.cls in RocksDbSharp project). 5 | 6 | This is therefore intended to be a direct port from the c_simple_example.c at 7 | https://github.com/facebook/rocksdb/blob/ccc8c10/examples/c_simple_example.c 8 | 9 | */ 10 | using RocksDbSharp; 11 | using System; 12 | using System.Diagnostics; 13 | using System.IO; 14 | using System.Linq; 15 | using System.Runtime.InteropServices; 16 | using System.Text; 17 | 18 | namespace SimpleExampleLowLevel 19 | { 20 | class Program 21 | { 22 | static string temp = Path.GetTempPath(); 23 | static string DBPath = Environment.ExpandEnvironmentVariables(Path.Combine(temp, "rocksdb_simple_example")); 24 | static string DBBackupPath = Environment.ExpandEnvironmentVariables(Path.Combine(temp, "rocksdb_simple_example_backup")); 25 | 26 | static void Main(string[] args) 27 | { 28 | IntPtr db; 29 | IntPtr be; 30 | IntPtr options = Native.Instance.rocksdb_options_create(); 31 | // Optimize RocksDB. This is the easiest way to 32 | // get RocksDB to perform well 33 | int cpus = Environment.ProcessorCount; 34 | Native.Instance.rocksdb_options_increase_parallelism(options, cpus); 35 | Native.Instance.rocksdb_options_optimize_level_style_compaction(options, 0); 36 | // create the DB if it's not already present 37 | Native.Instance.rocksdb_options_set_create_if_missing(options, true); 38 | 39 | // open DB 40 | IntPtr err = IntPtr.Zero; 41 | db = Native.Instance.rocksdb_open(options, DBPath, out err); 42 | Debug.Assert(err == IntPtr.Zero); 43 | 44 | // open Backup Engine that we will use for backing up our database 45 | be = Native.Instance.rocksdb_backup_engine_open(options, DBBackupPath, out err); 46 | Debug.Assert(err == IntPtr.Zero); 47 | 48 | // Put key-value 49 | IntPtr writeoptions = Native.Instance.rocksdb_writeoptions_create(); 50 | string key = "key"; 51 | string value = "value"; 52 | Native.Instance.rocksdb_put(db, writeoptions, key, value, 53 | out err); 54 | Debug.Assert(err == IntPtr.Zero); 55 | // Get value 56 | IntPtr readoptions = Native.Instance.rocksdb_readoptions_create(); 57 | string returned_value = 58 | Native.Instance.rocksdb_get(db, readoptions, key, out err); 59 | Debug.Assert(err == IntPtr.Zero); 60 | Debug.Assert(returned_value == "value"); 61 | 62 | // create new backup in a directory specified by DBBackupPath 63 | Native.Instance.rocksdb_backup_engine_create_new_backup(be, db, out err); 64 | Debug.Assert(err == IntPtr.Zero); 65 | 66 | Native.Instance.rocksdb_close(db); 67 | 68 | // If something is wrong, you might want to restore data from last backup 69 | IntPtr restore_options = Native.Instance.rocksdb_restore_options_create(); 70 | Native.Instance.rocksdb_backup_engine_restore_db_from_latest_backup(be, DBPath, DBPath, 71 | restore_options, out err); 72 | Debug.Assert(err == IntPtr.Zero); 73 | Native.Instance.rocksdb_restore_options_destroy(restore_options); 74 | 75 | db = Native.Instance.rocksdb_open(options, DBPath, out err); 76 | Debug.Assert(err == IntPtr.Zero); 77 | 78 | // cleanup 79 | Native.Instance.rocksdb_writeoptions_destroy(writeoptions); 80 | Native.Instance.rocksdb_readoptions_destroy(readoptions); 81 | Native.Instance.rocksdb_options_destroy(options); 82 | Native.Instance.rocksdb_backup_engine_close(be); 83 | Native.Instance.rocksdb_close(db); 84 | 85 | OtherExamples(); 86 | } 87 | 88 | static void OtherExamples() 89 | { 90 | MultiGetExample(); 91 | } 92 | 93 | static void MultiGetExample() 94 | { 95 | // Multiget 96 | IntPtr db; 97 | IntPtr options = Native.Instance.rocksdb_options_create(); 98 | int cpus = Environment.ProcessorCount; 99 | Native.Instance.rocksdb_options_increase_parallelism(options, cpus); 100 | Native.Instance.rocksdb_options_optimize_level_style_compaction(options, 0); 101 | // create the DB if it's not already present 102 | Native.Instance.rocksdb_options_set_create_if_missing(options, true); 103 | 104 | // open DB 105 | IntPtr err = IntPtr.Zero; 106 | db = Native.Instance.rocksdb_open(options, DBPath, out err); 107 | Debug.Assert(err == IntPtr.Zero); 108 | 109 | // Put key-value 110 | IntPtr writeoptions = Native.Instance.rocksdb_writeoptions_create(); 111 | Native.Instance.rocksdb_put(db, writeoptions, "one", "uno", out err); 112 | Debug.Assert(err == IntPtr.Zero); 113 | Native.Instance.rocksdb_put(db, writeoptions, "two", "dos", out err); 114 | Debug.Assert(err == IntPtr.Zero); 115 | Native.Instance.rocksdb_put(db, writeoptions, "three", "tres", out err); 116 | Debug.Assert(err == IntPtr.Zero); 117 | Native.Instance.rocksdb_put(db, writeoptions, "five", "五", out err); 118 | Debug.Assert(err == IntPtr.Zero); 119 | 120 | // Get value 121 | IntPtr readoptions = Native.Instance.rocksdb_readoptions_create(); 122 | var values = Native.Instance.rocksdb_multi_get(db, readoptions, new[] { "two", "four", "five" }); 123 | 124 | Debug.Assert(values[2].Value == "五"); 125 | 126 | string returned_value = 127 | Native.Instance.rocksdb_get(db, readoptions, "one", out err); 128 | Debug.Assert(err == IntPtr.Zero); 129 | Debug.Assert(returned_value == "uno"); 130 | 131 | // cleanup 132 | Native.Instance.rocksdb_writeoptions_destroy(writeoptions); 133 | Native.Instance.rocksdb_readoptions_destroy(readoptions); 134 | Native.Instance.rocksdb_options_destroy(options); 135 | Native.Instance.rocksdb_close(db); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /examples/SimpleExampleLowLevel/SimpleExampleLowLevel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net45 4 | Exe 5 | x64 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /nuget/build-and-pack.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd "%~dp0" 3 | pushd "%~dp0" 4 | where /q msbuild 5 | IF ERRORLEVEL 1 ( 6 | call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsMSBuildCmd.bat" 7 | ) 8 | popd 9 | 10 | cd .. 11 | 12 | REM echo "Downloading native..." 13 | REM call download-native.cmd 14 | 15 | @REM -------------------------------- 16 | @echo Building RocksDbNative 17 | cd RocksDbNative 18 | @echo Restoring... 19 | msbuild /t:Restore 20 | @if %errorlevel% neq 0 goto oops 21 | @echo Building... 22 | msbuild /p:Configuration=Release /t:Rebuild,Pack 23 | @if %errorlevel% neq 0 goto oops 24 | @echo Installing... 25 | move /y bin\Release\*.nupkg ..\nuget\ 26 | @if %errorlevel% neq 0 goto oops 27 | cd .. 28 | 29 | @REM -------------------------------- 30 | @echo Building RocksDbSharp 31 | cd RocksDbSharp 32 | @echo Restoring... 33 | msbuild /t:Restore 34 | @if %errorlevel% neq 0 goto oops 35 | @echo Building... 36 | msbuild /p:Configuration=Release /t:Rebuild,Pack 37 | @if %errorlevel% neq 0 goto oops 38 | @echo Installing... 39 | move /y bin\Release\*.nupkg ..\nuget\ 40 | @if %errorlevel% neq 0 goto oops 41 | cd .. 42 | 43 | :good 44 | popd 45 | exit /b 0 46 | 47 | :oops 48 | set rval=%errorlevel% 49 | echo "ERROR" 50 | popd 51 | exit /b %rval% -------------------------------------------------------------------------------- /nuget/instructions.txt: -------------------------------------------------------------------------------- 1 | Make sure version number is propagated everywhere 2 | Check .csproj files and edit any release info 3 | Run build-and-pack.cmd 4 | 5 | Verify that both packs worked, if possible, by copying them to the local nuget source and then installing them in a test project 6 | Go to nuget and upload through the interface -------------------------------------------------------------------------------- /tests/RocksDbSharpTest/FunctionalTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using System.IO; 4 | using RocksDbSharp; 5 | using System.Text; 6 | using System.Linq; 7 | using System.Collections.Generic; 8 | using System.Runtime.InteropServices; 9 | 10 | namespace RocksDbSharpTest 11 | { 12 | public class FunctionalTests 13 | { 14 | [Fact] 15 | public void FunctionalTest() 16 | { 17 | string temp = Path.GetTempPath(); 18 | var testdir = Path.Combine(temp, "functional_test"); 19 | var testdb = Path.Combine(testdir, "main"); 20 | var testcp = Path.Combine(testdir, "cp"); 21 | var path = Environment.ExpandEnvironmentVariables(testdb); 22 | var cppath = Environment.ExpandEnvironmentVariables(testcp); 23 | 24 | if (Directory.Exists(testdir)) 25 | Directory.Delete(testdir, true); 26 | Directory.CreateDirectory(testdir); 27 | 28 | var options = new DbOptions() 29 | .SetCreateIfMissing(true) 30 | .EnableStatistics(); 31 | 32 | // Using standard open 33 | using (var db = RocksDb.Open(options, path)) 34 | { 35 | // With strings 36 | string value = db.Get("key"); 37 | db.Put("key", "value"); 38 | Assert.Equal("value", db.Get("key")); 39 | Assert.Null(db.Get("non-existent-key")); 40 | db.Remove("key"); 41 | Assert.Null(db.Get("value")); 42 | 43 | // With bytes 44 | db.Put(Encoding.UTF8.GetBytes("key"), Encoding.UTF8.GetBytes("value")); 45 | Assert.True(BinaryComparer.Default.Equals(Encoding.UTF8.GetBytes("value"), db.Get(Encoding.UTF8.GetBytes("key")))); 46 | // non-existent kiey 47 | Assert.Null(db.Get(new byte[] { 0, 1, 2 })); 48 | db.Remove(Encoding.UTF8.GetBytes("key")); 49 | Assert.Null(db.Get(Encoding.UTF8.GetBytes("key"))); 50 | 51 | db.Put(Encoding.UTF8.GetBytes("key"), new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }); 52 | 53 | // With buffers 54 | var buffer = new byte[100]; 55 | long length = db.Get(Encoding.UTF8.GetBytes("key"), buffer, 0, buffer.Length); 56 | Assert.Equal(8, length); 57 | Assert.Equal(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, buffer.Take((int)length).ToList()); 58 | 59 | buffer = new byte[5]; 60 | length = db.Get(Encoding.UTF8.GetBytes("key"), buffer, 0, buffer.Length); 61 | Assert.Equal(8, length); 62 | Assert.Equal(new byte[] { 0, 1, 2, 3, 4 }, buffer.Take((int)Math.Min(buffer.Length, length))); 63 | 64 | length = db.Get(Encoding.UTF8.GetBytes("bogus"), buffer, 0, buffer.Length); 65 | Assert.Equal(-1, length); 66 | 67 | // Write batches 68 | // With strings 69 | using (WriteBatch batch = new WriteBatch() 70 | .Put("one", "uno") 71 | .Put("two", "deuce") 72 | .Put("two", "dos") 73 | .Put("three", "tres")) 74 | { 75 | db.Write(batch); 76 | } 77 | Assert.Equal("uno", db.Get("one")); 78 | 79 | // With save point 80 | using (WriteBatch batch = new WriteBatch()) 81 | { 82 | batch 83 | .Put("hearts", "red") 84 | .Put("diamonds", "red"); 85 | batch.SetSavePoint(); 86 | batch 87 | .Put("clubs", "black"); 88 | batch.SetSavePoint(); 89 | batch 90 | .Put("spades", "black"); 91 | batch.RollbackToSavePoint(); 92 | db.Write(batch); 93 | } 94 | Assert.Equal("red", db.Get("diamonds")); 95 | Assert.Equal("black", db.Get("clubs")); 96 | Assert.Null(db.Get("spades")); 97 | 98 | // Save a checkpoint 99 | using (var cp = db.Checkpoint()) 100 | { 101 | cp.Save(cppath); 102 | } 103 | 104 | // With bytes 105 | var utf8 = Encoding.UTF8; 106 | using (WriteBatch batch = new WriteBatch() 107 | .Put(utf8.GetBytes("four"), new byte[] { 4, 4, 4 }) 108 | .Put(utf8.GetBytes("five"), new byte[] { 5, 5, 5 })) 109 | { 110 | db.Write(batch); 111 | } 112 | Assert.True(BinaryComparer.Default.Equals(new byte[] { 4, 4, 4 }, db.Get(utf8.GetBytes("four")))); 113 | 114 | // Snapshots 115 | using (var snapshot = db.CreateSnapshot()) 116 | { 117 | var before = db.Get("one"); 118 | db.Put("one", "1"); 119 | 120 | var useSnapshot = new ReadOptions() 121 | .SetSnapshot(snapshot); 122 | 123 | // the database value was written 124 | Assert.Equal("1", db.Get("one")); 125 | // but the snapshot still sees the old version 126 | var after = db.Get("one", readOptions: useSnapshot); 127 | Assert.Equal(before, after); 128 | } 129 | 130 | var two = db.Get("two"); 131 | Assert.Equal("dos", two); 132 | 133 | // Iterators 134 | using (var iterator = db.NewIterator( 135 | readOptions: new ReadOptions() 136 | .SetIterateUpperBound("t") 137 | )) 138 | { 139 | iterator.Seek("k"); 140 | Assert.True(iterator.Valid()); 141 | Assert.Equal("key", iterator.StringKey()); 142 | iterator.Next(); 143 | Assert.True(iterator.Valid()); 144 | Assert.Equal("one", iterator.StringKey()); 145 | Assert.Equal("1", iterator.StringValue()); 146 | iterator.Next(); 147 | Assert.False(iterator.Valid()); 148 | } 149 | 150 | // MultiGet 151 | var multiGetResult = db.MultiGet(new[] { "two", "three", "nine" }); 152 | Assert.Equal( 153 | expected: new[] 154 | { 155 | new KeyValuePair("two", "dos"), 156 | new KeyValuePair("three", "tres"), 157 | new KeyValuePair("nine", null) 158 | }, 159 | actual: multiGetResult 160 | ); 161 | } 162 | 163 | // Test reading checkpointed db 164 | using (var cpdb = RocksDb.Open(options, cppath)) 165 | { 166 | Assert.Equal("red", cpdb.Get("diamonds")); 167 | Assert.Equal("black", cpdb.Get("clubs")); 168 | Assert.Null(cpdb.Get("spades")); 169 | // Checkpoint occurred before these changes: 170 | Assert.Null(cpdb.Get("four")); 171 | } 172 | 173 | // Test various operations 174 | using (var db = RocksDb.Open(options, path)) 175 | { 176 | // Nulls should be allowed here 177 | db.CompactRange((byte[])null, (byte[])null); 178 | db.CompactRange((string)null, (string)null); 179 | } 180 | 181 | // Test with column families 182 | var optionsCf = new DbOptions() 183 | .SetCreateIfMissing(true) 184 | .SetCreateMissingColumnFamilies(true); 185 | 186 | var columnFamilies = new ColumnFamilies 187 | { 188 | { "reverse", new ColumnFamilyOptions() }, 189 | }; 190 | 191 | using (var db = RocksDb.Open(optionsCf, path, columnFamilies)) 192 | { 193 | var reverse = db.GetColumnFamily("reverse"); 194 | 195 | db.Put("one", "uno"); 196 | db.Put("two", "dos"); 197 | db.Put("three", "tres"); 198 | 199 | db.Put("uno", "one", cf: reverse); 200 | db.Put("dos", "two", cf: reverse); 201 | db.Put("tres", "three", cf: reverse); 202 | } 203 | 204 | // Test Cf Delete 205 | using (var db = RocksDb.Open(optionsCf, path, columnFamilies)) 206 | { 207 | var reverse = db.GetColumnFamily("reverse"); 208 | 209 | db.Put("cuatro", "four", cf: reverse); 210 | db.Put("cinco", "five", cf: reverse); 211 | 212 | Assert.Equal("four", db.Get("cuatro", cf: reverse)); 213 | Assert.Equal("five", db.Get("cinco", cf: reverse)); 214 | 215 | byte[] keyBytes = Encoding.UTF8.GetBytes("cuatro"); 216 | db.Remove(keyBytes, reverse); 217 | db.Remove("cinco", reverse); 218 | 219 | Assert.Null(db.Get("cuatro", cf: reverse)); 220 | Assert.Null(db.Get("cinco", cf: reverse)); 221 | } 222 | 223 | // Test list 224 | { 225 | var list = RocksDb.ListColumnFamilies(optionsCf, path); 226 | Assert.Equal(new[] { "default", "reverse" }, list.ToArray()); 227 | } 228 | 229 | // Test reopen with column families 230 | using (var db = RocksDb.Open(optionsCf, path, columnFamilies)) 231 | { 232 | var reverse = db.GetColumnFamily("reverse"); 233 | 234 | Assert.Equal("uno", db.Get("one")); 235 | Assert.Equal("one", db.Get("uno", cf: reverse)); 236 | Assert.Null(db.Get("uno")); 237 | Assert.Null(db.Get("one", cf: reverse)); 238 | } 239 | 240 | // Test dropping and creating column family 241 | using (var db = RocksDb.Open(options, path, columnFamilies)) 242 | { 243 | db.DropColumnFamily("reverse"); 244 | var reverse = db.CreateColumnFamily(new ColumnFamilyOptions(), "reverse"); 245 | Assert.Null(db.Get("uno", cf: reverse)); 246 | db.Put("red", "rouge", cf: reverse); 247 | Assert.Equal("rouge", db.Get("red", cf: reverse)); 248 | } 249 | 250 | // Test reopen after drop and create 251 | using (var db = RocksDb.Open(options, path, columnFamilies)) 252 | { 253 | var reverse = db.GetColumnFamily("reverse"); 254 | Assert.Null(db.Get("uno", cf: reverse)); 255 | Assert.Equal("rouge", db.Get("red", cf: reverse)); 256 | } 257 | 258 | // Test read only 259 | using (var db = RocksDb.OpenReadOnly(options, path, columnFamilies, false)) 260 | { 261 | Assert.Equal("uno", db.Get("one")); 262 | } 263 | 264 | // Test SstFileWriter 265 | { 266 | using (var writer = new SstFileWriter()) 267 | { 268 | } 269 | 270 | var envOpts = new EnvOptions(); 271 | var ioOpts = new ColumnFamilyOptions(); 272 | using (var sst = new SstFileWriter(envOpts, ioOpts)) 273 | { 274 | var filename = Path.Combine(temp, "test.sst"); 275 | if (File.Exists(filename)) 276 | File.Delete(filename); 277 | sst.Open(filename); 278 | sst.Add("four", "quatro"); 279 | sst.Add("one", "uno"); 280 | sst.Add("two", "dos"); 281 | sst.Finish(); 282 | 283 | using (var db = RocksDb.Open(options, path, columnFamilies)) 284 | { 285 | Assert.NotEqual("four", db.Get("four")); 286 | var ingestOptions = new IngestExternalFileOptions() 287 | .SetMoveFiles(true); 288 | db.IngestExternalFiles(new string[] { filename }, ingestOptions); 289 | Assert.Equal("quatro", db.Get("four")); 290 | } 291 | } 292 | } 293 | 294 | // test comparator 295 | unsafe { 296 | var opts = new ColumnFamilyOptions() 297 | .SetComparator(new IntegerStringComparator()); 298 | 299 | var filename = Path.Combine(temp, "test.sst"); 300 | if (File.Exists(filename)) 301 | File.Delete(filename); 302 | using (var sst = new SstFileWriter(ioOptions: opts)) 303 | { 304 | sst.Open(filename); 305 | sst.Add("111", "111"); 306 | sst.Add("1001", "1001"); // this order is only allowed using an integer comparator 307 | sst.Finish(); 308 | } 309 | } 310 | 311 | // test write batch with index 312 | { 313 | var wbwi = new WriteBatchWithIndex(reservedBytes: 1024); 314 | wbwi.Put("one", "un"); 315 | wbwi.Put("two", "deux"); 316 | var oneValueIn = Encoding.UTF8.GetBytes("one"); 317 | var oneValueOut = wbwi.Get("one"); 318 | Assert.Equal("un", oneValueOut); 319 | using (var db = RocksDb.Open(options, path, columnFamilies)) 320 | { 321 | var oneCombinedOut = wbwi.Get(db, "one"); 322 | var threeCombinedOut = wbwi.Get(db, "three"); 323 | Assert.Equal("un", oneCombinedOut); 324 | Assert.Equal("tres", threeCombinedOut); 325 | 326 | using (var wbIterator = wbwi.NewIterator(db.NewIterator())) 327 | { 328 | wbIterator.Seek("o"); 329 | Assert.True(wbIterator.Valid()); 330 | var itkey = wbIterator.StringKey(); 331 | Assert.Equal("one", itkey); 332 | var itval = wbIterator.StringValue(); 333 | Assert.Equal("un", itval); 334 | 335 | wbIterator.Next(); 336 | Assert.True(wbIterator.Valid()); 337 | itkey = wbIterator.StringKey(); 338 | Assert.Equal("three", itkey); 339 | itval = wbIterator.StringValue(); 340 | Assert.Equal("tres", itval); 341 | 342 | wbIterator.Next(); 343 | Assert.True(wbIterator.Valid()); 344 | itkey = wbIterator.StringKey(); 345 | Assert.Equal("two", itkey); 346 | itval = wbIterator.StringValue(); 347 | Assert.Equal("deux", itval); 348 | 349 | wbIterator.Next(); 350 | Assert.False(wbIterator.Valid()); 351 | } 352 | 353 | db.Write(wbwi); 354 | 355 | var oneDbOut = wbwi.Get("one"); 356 | Assert.Equal("un", oneDbOut); 357 | } 358 | } 359 | 360 | // compact range 361 | { 362 | using (var db = RocksDb.Open(options, path, columnFamilies)) 363 | { 364 | db.CompactRange("o", "tw"); 365 | } 366 | } 367 | 368 | // Test that GC does not cause access violation on Comparers 369 | { 370 | if (Directory.Exists("test-av-error")) 371 | Directory.Delete("test-av-error", true); 372 | options = new RocksDbSharp.DbOptions() 373 | .SetCreateIfMissing(true) 374 | .SetCreateMissingColumnFamilies(true); 375 | var sc = new RocksDbSharp.StringComparator(StringComparer.InvariantCultureIgnoreCase); 376 | columnFamilies = new RocksDbSharp.ColumnFamilies 377 | { 378 | { "cf1", new RocksDbSharp.ColumnFamilyOptions() 379 | .SetComparator(sc) 380 | }, 381 | }; 382 | GC.Collect(); 383 | using (var db = RocksDbSharp.RocksDb.Open(options, "test-av-error", columnFamilies)) 384 | { 385 | } 386 | if (Directory.Exists("test-av-error")) 387 | Directory.Delete("test-av-error", true); 388 | } 389 | 390 | // Smoke test various options 391 | { 392 | var dbname = "test-options"; 393 | if (Directory.Exists(dbname)) 394 | Directory.Delete(dbname, true); 395 | var optsTest = (DbOptions)new RocksDbSharp.DbOptions() 396 | .SetCreateIfMissing(true) 397 | .SetCreateMissingColumnFamilies(true) 398 | .SetBlockBasedTableFactory(new BlockBasedTableOptions().SetBlockCache(Cache.CreateLru(1024 * 1024))); 399 | GC.Collect(); 400 | using (var db = RocksDbSharp.RocksDb.Open(optsTest, dbname)) 401 | { 402 | } 403 | if (Directory.Exists(dbname)) 404 | Directory.Delete(dbname, true); 405 | 406 | } 407 | 408 | // Smoke test OpenWithTtl 409 | { 410 | var dbname = "test-with-ttl"; 411 | if (Directory.Exists(dbname)) 412 | Directory.Delete(dbname, true); 413 | var optsTest = (DbOptions)new RocksDbSharp.DbOptions() 414 | .SetCreateIfMissing(true) 415 | .SetCreateMissingColumnFamilies(true); 416 | using (var db = RocksDbSharp.RocksDb.OpenWithTtl(optsTest, dbname, 1)) 417 | { 418 | } 419 | if (Directory.Exists(dbname)) 420 | Directory.Delete(dbname, true); 421 | } 422 | 423 | // Smoke test MergeOperator 424 | { 425 | var dbname = "test-merge-operator"; 426 | if (Directory.Exists(dbname)) 427 | Directory.Delete(dbname, true); 428 | var optsTest = (DbOptions)new RocksDbSharp.DbOptions() 429 | .SetCreateIfMissing(true) 430 | .SetMergeOperator(MergeOperators.Create( 431 | name: "test-merge-operator", 432 | partialMerge: (key, keyLength, operandsList, operandsListLength, numOperands, success, newValueLength) => IntPtr.Zero, 433 | fullMerge: (key, keyLength, existingValue, existingValueLength, operandsList, operandsListLength, numOperands, success, newValueLength) => IntPtr.Zero, 434 | deleteValue: (value, valueLength) => { } 435 | )); 436 | GC.Collect(); 437 | using (var db = RocksDbSharp.RocksDb.Open(optsTest, dbname)) 438 | { 439 | } 440 | if (Directory.Exists(dbname)) 441 | Directory.Delete(dbname, true); 442 | 443 | } 444 | 445 | // Test that GC does not cause access violation on Comparers 446 | { 447 | var dbname = "test-av-error"; 448 | if (Directory.Exists(dbname)) 449 | Directory.Delete(dbname, true); 450 | options = new RocksDbSharp.DbOptions() 451 | .SetCreateIfMissing(true) 452 | .SetCreateMissingColumnFamilies(true); 453 | var sc = new RocksDbSharp.StringComparator(StringComparer.InvariantCultureIgnoreCase); 454 | columnFamilies = new RocksDbSharp.ColumnFamilies 455 | { 456 | { "cf1", new RocksDbSharp.ColumnFamilyOptions() 457 | .SetComparator(sc) 458 | }, 459 | }; 460 | GC.Collect(); 461 | using (var db = RocksDbSharp.RocksDb.Open(options, dbname, columnFamilies)) 462 | { 463 | } 464 | if (Directory.Exists(dbname)) 465 | Directory.Delete(dbname, true); 466 | } 467 | 468 | } 469 | 470 | class IntegerStringComparator : StringComparatorBase 471 | { 472 | Comparison Comparer { get; } = Comparer.Default.Compare; 473 | 474 | public override int Compare(string a, string b) 475 | => Comparer(long.TryParse(a, out long avalue) ? avalue : 0, long.TryParse(b, out long bvalue) ? bvalue : 0); 476 | } 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /tests/RocksDbSharpTest/RocksDbSharpTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp2.0 4 | True 5 | PackageReference 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /tests/RocksDbSharpTest/TestBinaryComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using RocksDbSharp; 4 | using System.Text; 5 | 6 | namespace RocksDbSharpTest 7 | { 8 | public class TestBinaryComparer 9 | { 10 | [Fact] 11 | public void TestCompare() 12 | { 13 | var comparer = BinaryComparer.Default; 14 | 15 | var forward = StringComparer.OrdinalIgnoreCase.Compare("a", "b"); 16 | var backward = -forward; 17 | 18 | AssertCompare(comparer, forward, "B", "b"); 19 | AssertCompare(comparer, backward, "b", "B"); 20 | 21 | AssertCompare(comparer, forward, "aB", "ab"); 22 | AssertCompare(comparer, backward, "ab", "aB"); 23 | 24 | AssertCompare(comparer, forward, "cB", "cb"); 25 | AssertCompare(comparer, backward, "cb", "cB"); 26 | 27 | AssertCompare(comparer, forward, "b", "bb"); 28 | AssertCompare(comparer, backward, "bb", "b"); 29 | } 30 | 31 | [Fact] 32 | public void TestPrefixEquals() 33 | { 34 | var comparer = BinaryComparer.Default; 35 | 36 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaa"), 1)); 37 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaa"), 3)); 38 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaa"), 5)); 39 | 40 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaX"), 1)); 41 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaX"), 2)); 42 | Assert.False(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaX"), 3)); 43 | Assert.False(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaX"), 5)); 44 | 45 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaaX"), 1)); 46 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaaX"), 3)); 47 | Assert.False(comparer.PrefixEquals(AsciiBytes("aaa"), AsciiBytes("aaaX"), 4)); 48 | 49 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaaX"), AsciiBytes("aaa"), 1)); 50 | Assert.True(comparer.PrefixEquals(AsciiBytes("aaaX"), AsciiBytes("aaa"), 3)); 51 | Assert.False(comparer.PrefixEquals(AsciiBytes("aaaX"), AsciiBytes("aaa"), 4)); 52 | } 53 | 54 | private byte[] AsciiBytes(string v) 55 | { 56 | return Encoding.ASCII.GetBytes(v); 57 | } 58 | 59 | private void AssertCompare(BinaryComparer comparer, int expected, string v1, string v2) 60 | { 61 | Assert.Equal(expected, comparer.Compare(Encoding.UTF8.GetBytes(v1), Encoding.UTF8.GetBytes(v2))); 62 | } 63 | } 64 | } 65 | --------------------------------------------------------------------------------