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

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

Я гуглил на C# версию leveldb, но не повезло.

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

Спасибо

4 ответа

Решение

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

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

Как я уже сказал, я использовал этот подход для своего проекта на C#, и он прекрасно работает. Однако, если у вас действительно высокие требования к производительности, я бы порекомендовал группировать "работу", чтобы уменьшить 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, Вот как вы можете получить данные, связанные с ключом в C#:

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);

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

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

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

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

Другие вопросы по тегам