Есть ли хороший порт leveldb для С#?

Я хочу использовать leveldb в моем чистом проекте С#.

У меня есть googled для версии С# leveldb, но не повезло.

Кто-нибудь может сказать мне, где я могу найти версию leveldb на С#?

Спасибо

Ответ 1

Не то, что я знаю, но я использовал его в своем проекте С#. Если вы знакомы с С++, тогда вы можете создать свою собственную оболочку CLI (не должно быть столько проблем), создать ее как DLL, а затем вы можете загрузить эту DLL в свой проект С#, как и любую другую ссылку на сборку.

Существует оконный порт для leveldb, и немного сложно получить его в Visual Studio, но если у вас возникли проблемы, я могу загрузить свою Visual Studio 2010 решение (что составляет 75% от битвы) со всей настройкой и готовностью к построению (кроме оболочки CLI). Я могу поставить его на github или что-то еще, что я на самом деле планирую делать в любом случае, но я ускорю его для вас.

Как я уже сказал, я использовал этот подход для своего проекта С#, и он отлично работает. Однако, если у вас действительно высокие требования к производительности, я бы рекомендовал доработать "работу", чтобы уменьшить P/Invokes.

Пример

Обратите внимание, что я не скомпилировал этот код, но я просто разместил его в качестве примера. Ваш заголовочный файл может выглядеть так:

#pragma once
#include <exception>
#include "leveldb\db.h"

using namespace System::Runtime::InteropServices;

// Create the namespace
namespace LevelDBWrapperNS 
{
    // Note that size_t changes depending on the target platform of your build:
    // for 32-bit builds, size_t is a 32-bit unsigned integer.
    // for 64-bit builds, size_t is a 64-bit unsigned integer.
    // There is no size_t equivalent in C#, but there are ways to
    // mimic the same behavior. Alternately, you can change the
    // size_t to unsigned long for 32-bit builds or unsigned long long (64-bit)

    // Declare the leveldb wrapper
    public ref class LevelDBWrapper
    {
    private:
        leveldb::DB*        _db;
    public:
        LevelDBWrapper(const std::string dataDirectory);
        ~LevelDBWrapper();

        // A get method which given a key, puts data in the value array
        // and sets the valueSize according to the size of the data it
        // allocated. Note: you will have to deallocate the data in C#
        void Get(const char* key, const size_t keySize, char* value, size_t &valueSize);

        // A put method which takes in strings instead of char*
        bool Put(const std::string key, const std::string value);

        // A put method which takes in char* pointers
        bool Put(const char* key, const size_t keySize, const char* value, const size_t valueSize);

        // A delete method
        bool Delete(const char* key, const size_t keySize);

    private:
        void Open(const char* dataDirectory);
    };
}

Ваш файл cpp будет находиться по строкам:

#include "LevelDBWrapper.h"

// Use the same namespace as the header
namespace LevelDBWrapperNS
{
    LevelDBWrapper::LevelDBWrapper(const std::string dataDirectory)
    {
        Open(dataDirectory.c_str());
    }

    LevelDBWrapper::~LevelDBWrapper()
    {
        if(_db!=NULL)
        {
            delete _db;
            _db= NULL;
        }

        // NOTE: don't forget to delete the block cache too!!!
        /*if(options.block_cache != NULL)
        {
            delete options.block_cache;
            options.block_cache = NULL;
        }*/
    }

    bool LevelDBWrapper::Put(const char* key, const size_t keySize, const char* value, const size_t valueSize)
    {
        leveldb::Slice sKey(key, keySize);
        leveldb::Slice sValue(value, valueSize);

        return _db->Put(leveldb::WriteOptions(), sKey, sValue).ok();
    }

    void LevelDBWrapper::Open(const char* dataDirectory)
    {
        leveldb::Options    options;

        // Create a database environment. This will enable caching between 
        // separate calls (and improve performance). This also enables 
        // the db_stat.exe command which allows cache tuning. Open 
        // transactional environment leveldb::Options options;
        options.create_if_missing = true;

        // Open the database if it exists
        options.error_if_exists = false;

        // 64 Mb read cache
        options.block_cache = leveldb::NewLRUCache(64 * 1024 * 1024);   

        // Writes will be flushed every 32 Mb
        options.write_buffer_size = 32 * 1024 * 1024;   

        // If you do a lot of bulk operations it may be good to increase the 
        // block size to a 64k block size. A power of 2 block size also 
        // also improves the compression rate when using Snappy.
        options.block_size = 64 * 1024; 
        options.max_open_files = 500;
        options.compression = leveldb::kNoCompression;

        _db = NULL;

        // Open the database
        leveldb::Status status = leveldb::DB::Open(options, dataDirectory, &_db);

        // Check if there was a failure
        if(!status.ok())
        {
            // The database failed to open!
            if(status.ToString().find("partial record without end")!=std::string::npos)
            {
                // Attempting to recover the database...

                status = leveldb::RepairDB(dataDirectory, options);

                if(status.ok())
                {
                    // Successfully recovered the database! Attempting to reopen... 
                    status = leveldb::DB::Open( options, dataDirectory, &_db);
                }
                else
                {
                    // Failed to recover the database!
                }
            }

            // Throw an exception if the failure was unrecoverable!
            if(!status.ok())
            {
                throw std::runtime_error(std::string("Unable to open: ") + std::string(dataDirectory) + 
                    std::string(" ") + status.ToString());
            }
        }
    }
}

Это должно помочь вам в правильном направлении.

Получить пример

ОК, Get будет выглядеть так:

// Returns a buffer containing the data and sets the bufferLen.
// The user must specify the key and the length of the key so a slice
// can be constructed and sent to leveldb.
const unsigned char* Get(const char* key, const size_t keyLength, [Out]size_t %bufferLen);

Источник находится по строкам:

const unsigned char* LevelDBWrapper::Get(const char* key, const size_t keyLength, [Out]size_t %bufferLen)
{
    unsigned char* buffer = NULL;
    std::string value;
    leveldb::Status s = db->Get(leveldb::ReadOptions(), Slice(key, keyLength), &value);
    if(s.ok())
    {
        // we found the key, so set the buffer length 
        bufferLen = value.size();

        // initialize the buffer
        buffer = new unsigned char[bufferLen];

        // set the buffer
        memset(buffer, 0, bufferLen);

        // copy the data
        memcpy(memcpy((void*)(buffer), value.c_str(), bufferLen);
    }
    else
    {
        // The buffer length is 0 because a key was not found
        bufferLen = 0;
    }
    return buffer;
}

Обратите внимание, что разные данные могут иметь различную кодировку, поэтому я считаю, что самый безопасный способ передачи данных между вашим неуправляемым и управляемым кодом - использовать указатели и UnmanagedMemoryStream. Вот как вы получите данные, связанные с ключом на С#:

UInt32 bufferLen = 0;
byte* buffer = dbInstance.Get(key, keyLength, out bufferLen);
UnmanagedMemoryStream ums = new UnmanagedMemoryStream(buffer, (Int32)bufferLen, (Int32)bufferLen, FileAccess.Read);

// Create a byte array to hold data from unmanaged memory.
byte[] data = new byte [bufferLen];

// Read from unmanaged memory to the byte array.
readStream.Read(data , 0, bufferLen);

// Don't forget to free the block of unmanaged memory!!!
Marshal.FreeHGlobal(buffer);

Опять же, я не скомпилировал или не запустил код, но он должен получить вас на правильном пути.

Ответ 2

Насколько я вижу, вы также можете использовать LMDB (база данных с привязкой к карте молнии, http://symas.com/mdb/) который кажется весьма похожим на LevelDB, а также поставляется с .Net-обложкой (https://github.com/ilyalukyanov/Lightning.NET) Не знаю, как хорошо он работает, хотя еще не использовал его...

Ответ 3

Я не использовал его, но я вижу leveldb-sharp.

Ответ 4

Я не знаю эту историю здесь, но там этот проект на официальной странице официального Rx-Js здесь.