blob: 41a1aefb3a78d5f80c8b801b811763938d5e249f [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "LevelDBDatabase.h"
#if USE(LEVELDB)
#include "LevelDBComparator.h"
#include "LevelDBIterator.h"
#include "LevelDBSlice.h"
#include "LevelDBWriteBatch.h"
#include "Logging.h"
#include <helpers/memenv/memenv.h>
#include <leveldb/comparator.h>
#include <leveldb/db.h>
#include <leveldb/env.h>
#include <leveldb/slice.h>
#include <string>
#include <wtf/PassOwnPtr.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
namespace WebCore {
static leveldb::Slice makeSlice(const Vector<char>& value)
{
return leveldb::Slice(value.data(), value.size());
}
static leveldb::Slice makeSlice(const LevelDBSlice& s)
{
return leveldb::Slice(s.begin(), s.end() - s.begin());
}
static LevelDBSlice makeLevelDBSlice(const leveldb::Slice& s)
{
return LevelDBSlice(s.data(), s.data() + s.size());
}
static Vector<char> makeVector(const std::string& s)
{
Vector<char> res;
res.append(s.c_str(), s.length());
return res;
}
class ComparatorAdapter : public leveldb::Comparator {
public:
ComparatorAdapter(const LevelDBComparator* comparator)
: m_comparator(comparator)
{
}
virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const
{
return m_comparator->compare(makeLevelDBSlice(a), makeLevelDBSlice(b));
}
virtual const char* Name() const { return m_comparator->name(); }
// FIXME: Support the methods below in the future.
virtual void FindShortestSeparator(std::string* start, const leveldb::Slice& limit) const { }
virtual void FindShortSuccessor(std::string* key) const { }
private:
const LevelDBComparator* m_comparator;
};
LevelDBDatabase::LevelDBDatabase()
{
}
LevelDBDatabase::~LevelDBDatabase()
{
// m_db's destructor uses m_comparatorAdapter; order of deletion is important.
m_db.clear();
m_comparatorAdapter.clear();
m_env.clear();
}
static leveldb::Status openDB(leveldb::Comparator* comparator, leveldb::Env* env, const String& path, leveldb::DB** db)
{
leveldb::Options options;
options.comparator = comparator;
options.create_if_missing = true;
options.paranoid_checks = true;
options.env = env;
return leveldb::DB::Open(options, path.utf8().data(), db);
}
PassOwnPtr<LevelDBDatabase> LevelDBDatabase::open(const String& fileName, const LevelDBComparator* comparator)
{
OwnPtr<ComparatorAdapter> comparatorAdapter = adoptPtr(new ComparatorAdapter(comparator));
leveldb::DB* db;
const leveldb::Status s = openDB(comparatorAdapter.get(), leveldb::Env::Default(), fileName, &db);
if (!s.ok()) {
LOG_ERROR("Failed to open LevelDB database from %s: %s", fileName.ascii().data(), s.ToString().c_str());
return nullptr;
}
OwnPtr<LevelDBDatabase> result = adoptPtr(new LevelDBDatabase);
result->m_db = adoptPtr(db);
result->m_comparatorAdapter = comparatorAdapter.release();
result->m_comparator = comparator;
return result.release();
}
PassOwnPtr<LevelDBDatabase> LevelDBDatabase::openInMemory(const LevelDBComparator* comparator)
{
OwnPtr<ComparatorAdapter> comparatorAdapter = adoptPtr(new ComparatorAdapter(comparator));
OwnPtr<leveldb::Env> inMemoryEnv = adoptPtr(leveldb::NewMemEnv(leveldb::Env::Default()));
leveldb::DB* db;
const leveldb::Status s = openDB(comparatorAdapter.get(), inMemoryEnv.get(), String(), &db);
if (!s.ok()) {
LOG_ERROR("Failed to open in-memory LevelDB database: %s", s.ToString().c_str());
return nullptr;
}
OwnPtr<LevelDBDatabase> result = adoptPtr(new LevelDBDatabase);
result->m_env = inMemoryEnv.release();
result->m_db = adoptPtr(db);
result->m_comparatorAdapter = comparatorAdapter.release();
result->m_comparator = comparator;
return result.release();
}
bool LevelDBDatabase::put(const LevelDBSlice& key, const Vector<char>& value)
{
leveldb::WriteOptions writeOptions;
writeOptions.sync = true;
const leveldb::Status s = m_db->Put(writeOptions, makeSlice(key), makeSlice(value));
if (s.ok())
return true;
LOG_ERROR("LevelDB put failed: %s", s.ToString().c_str());
return false;
}
bool LevelDBDatabase::remove(const LevelDBSlice& key)
{
leveldb::WriteOptions writeOptions;
writeOptions.sync = true;
const leveldb::Status s = m_db->Delete(writeOptions, makeSlice(key));
if (s.ok())
return true;
if (s.IsNotFound())
return false;
LOG_ERROR("LevelDB remove failed: %s", s.ToString().c_str());
return false;
}
bool LevelDBDatabase::get(const LevelDBSlice& key, Vector<char>& value)
{
std::string result;
leveldb::ReadOptions readOptions;
readOptions.verify_checksums = true; // FIXME: Disable this if the performance impact is too great.
const leveldb::Status s = m_db->Get(readOptions, makeSlice(key), &result);
if (s.ok()) {
value = makeVector(result);
return true;
}
if (s.IsNotFound())
return false;
LOG_ERROR("LevelDB get failed: %s", s.ToString().c_str());
return false;
}
bool LevelDBDatabase::write(LevelDBWriteBatch& writeBatch)
{
leveldb::WriteOptions writeOptions;
writeOptions.sync = true;
const leveldb::Status s = m_db->Write(writeOptions, writeBatch.m_writeBatch.get());
if (s.ok())
return true;
LOG_ERROR("LevelDB write failed: %s", s.ToString().c_str());
return false;
}
namespace {
class IteratorImpl : public LevelDBIterator {
public:
~IteratorImpl() { };
virtual bool isValid() const;
virtual void seekToLast();
virtual void seek(const LevelDBSlice& target);
virtual void next();
virtual void prev();
virtual LevelDBSlice key() const;
virtual LevelDBSlice value() const;
private:
friend class WebCore::LevelDBDatabase;
IteratorImpl(PassOwnPtr<leveldb::Iterator>);
void checkStatus();
OwnPtr<leveldb::Iterator> m_iterator;
};
}
IteratorImpl::IteratorImpl(PassOwnPtr<leveldb::Iterator> it)
: m_iterator(it)
{
}
void IteratorImpl::checkStatus()
{
const leveldb::Status s = m_iterator->status();
if (!s.ok())
LOG_ERROR("LevelDB iterator error: %s", s.ToString().c_str());
}
bool IteratorImpl::isValid() const
{
return m_iterator->Valid();
}
void IteratorImpl::seekToLast()
{
m_iterator->SeekToLast();
checkStatus();
}
void IteratorImpl::seek(const LevelDBSlice& target)
{
m_iterator->Seek(makeSlice(target));
checkStatus();
}
void IteratorImpl::next()
{
ASSERT(isValid());
m_iterator->Next();
checkStatus();
}
void IteratorImpl::prev()
{
ASSERT(isValid());
m_iterator->Prev();
checkStatus();
}
LevelDBSlice IteratorImpl::key() const
{
ASSERT(isValid());
return makeLevelDBSlice(m_iterator->key());
}
LevelDBSlice IteratorImpl::value() const
{
ASSERT(isValid());
return makeLevelDBSlice(m_iterator->value());
}
PassOwnPtr<LevelDBIterator> LevelDBDatabase::createIterator()
{
leveldb::ReadOptions readOptions;
readOptions.verify_checksums = true; // FIXME: Disable this if the performance impact is too great.
OwnPtr<leveldb::Iterator> i = adoptPtr(m_db->NewIterator(readOptions));
if (!i) // FIXME: Double check if we actually need to check this.
return nullptr;
return adoptPtr(new IteratorImpl(i.release()));
}
const LevelDBComparator* LevelDBDatabase::comparator() const
{
return m_comparator;
}
}
#endif