e3c129b105
Enhance driver implementation to optionally use Erlang's asynchronous driver thread pool for all LevelDB operations with the intention to avoid blocking of Erlang's scheduler threads.
465 lines
14 KiB
C++
465 lines
14 KiB
C++
// The MIT License
|
|
//
|
|
// Copyright (C) 2011 by Joseph Wayne Norton <norton@alum.mit.edu>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "lets_drv_lib.h"
|
|
|
|
#include <ei.h>
|
|
|
|
|
|
#if 0
|
|
static bool
|
|
return_false(unsigned line) {
|
|
fprintf(stderr, "FALSE %s:%d\n", __FILE__, line);
|
|
return false;
|
|
}
|
|
#define FALSE return_false(__LINE__)
|
|
#else
|
|
#define FALSE false
|
|
#endif
|
|
|
|
|
|
bool
|
|
lets_drv_lib_init()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lets_init(lets_impl& impl,
|
|
const char type, const char privacy, const char* name, const size_t namelen)
|
|
{
|
|
impl.type = type;
|
|
impl.privacy = privacy;
|
|
impl.name = new std::string(name, namelen);
|
|
if (!impl.name) {
|
|
return FALSE;
|
|
}
|
|
|
|
impl.db_options = leveldb::Options();
|
|
impl.db_read_options = leveldb::ReadOptions();
|
|
impl.db_write_options = leveldb::WriteOptions();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lets_create(lets_impl& impl,
|
|
const char op)
|
|
{
|
|
leveldb::Status status;
|
|
|
|
// db
|
|
switch (op) {
|
|
case OPEN:
|
|
status = leveldb::DB::Open(impl.db_options, impl.name->c_str(), &(impl.db));
|
|
if (!status.ok()) {
|
|
return FALSE;
|
|
} else {
|
|
// alive
|
|
impl.alive = 1;
|
|
}
|
|
break;
|
|
case DESTROY:
|
|
status = DestroyDB(impl.name->c_str(), impl.db_options);
|
|
if (!status.ok()) {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case REPAIR:
|
|
status = RepairDB(impl.name->c_str(), impl.db_options);
|
|
if (!status.ok()) {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lets_parse_options(lets_impl& impl,
|
|
const char* buf, int len)
|
|
{
|
|
int index, ng, items, arity, term_type, size_needed;
|
|
char atom[MAXATOMLEN];
|
|
|
|
index = 0;
|
|
ng = ei_decode_list_header(buf, &index, &items);
|
|
if (ng) return FALSE;
|
|
ng = (items < 0);
|
|
if (ng) return FALSE;
|
|
|
|
if (!items) return true;
|
|
|
|
while (items) {
|
|
ng = ei_get_type(buf, &index, &term_type, &size_needed);
|
|
if (ng) return FALSE;
|
|
|
|
switch (term_type) {
|
|
case ERL_ATOM_EXT:
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "create_if_missing") == 0) {
|
|
impl.db_options.create_if_missing = true;
|
|
} else if (strcmp(atom, "error_if_exists") == 0) {
|
|
impl.db_options.error_if_exists = true;
|
|
} else if (strcmp(atom, "paranoid_checks") == 0) {
|
|
impl.db_options.paranoid_checks = true;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case ERL_SMALL_TUPLE_EXT:
|
|
ng = ei_decode_tuple_header(buf, &index, &arity);
|
|
if (ng) return FALSE;
|
|
ng = (arity != 2);
|
|
if (ng) return FALSE;
|
|
unsigned long val;
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "create_if_missing") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.db_options.create_if_missing = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.db_options.create_if_missing = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp(atom, "error_if_exists") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.db_options.error_if_exists = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.db_options.error_if_exists = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp(atom, "paranoid_checks") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.db_options.paranoid_checks = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.db_options.paranoid_checks = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp(atom, "write_buffer_size") == 0) {
|
|
ng = ei_decode_ulong(buf, &index, &val);
|
|
if (ng) return FALSE;
|
|
impl.db_options.write_buffer_size = val;
|
|
} else if (strcmp(atom, "max_open_files") == 0) {
|
|
ng = ei_decode_ulong(buf, &index, &val);
|
|
if (ng) return FALSE;
|
|
impl.db_options.max_open_files = val;
|
|
} else if (strcmp(atom, "block_cache_size") == 0) {
|
|
ng = ei_decode_ulong(buf, &index, &val);
|
|
if (ng) return FALSE;
|
|
impl.db_block_cache_size = val;
|
|
impl.db_block_cache = leveldb::NewLRUCache(impl.db_block_cache_size);
|
|
impl.db_options.block_cache = impl.db_block_cache;
|
|
if (!impl.db_options.block_cache) {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp(atom, "block_size") == 0) {
|
|
ng = ei_decode_ulong(buf, &index, &val);
|
|
if (ng) return FALSE;
|
|
impl.db_options.block_size = val;
|
|
} else if (strcmp(atom, "block_restart_interval") == 0) {
|
|
ng = ei_decode_ulong(buf, &index, &val);
|
|
if (ng) return FALSE;
|
|
impl.db_options.block_restart_interval = val;
|
|
} else if (strcmp(atom, "compression") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "no") == 0) {
|
|
impl.db_options.compression = leveldb::kNoCompression;
|
|
} else if (strcmp(atom, "snappy") == 0) {
|
|
impl.db_options.compression = leveldb::kSnappyCompression;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp(atom, "async") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.async = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.async = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
items--;
|
|
}
|
|
|
|
ng = ei_decode_list_header(buf, &index, &items);
|
|
if (ng) return FALSE;
|
|
ng = (items != 0);
|
|
if (ng) return FALSE;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lets_parse_read_options(lets_impl& impl,
|
|
const char* buf, int len)
|
|
{
|
|
int index, ng, items, arity, term_type, size_needed;
|
|
char atom[MAXATOMLEN];
|
|
|
|
index = 0;
|
|
ng = ei_decode_list_header(buf, &index, &items);
|
|
if (ng) return FALSE;
|
|
ng = (items < 0);
|
|
if (ng) return FALSE;
|
|
|
|
if (!items) return true;
|
|
|
|
while (items) {
|
|
ng = ei_get_type(buf, &index, &term_type, &size_needed);
|
|
if (ng) return FALSE;
|
|
|
|
switch (term_type) {
|
|
case ERL_ATOM_EXT:
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "verify_checksums") == 0) {
|
|
impl.db_read_options.verify_checksums = true;
|
|
} else if (strcmp(atom, "fill_cache") == 0) {
|
|
impl.db_read_options.fill_cache = true;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case ERL_SMALL_TUPLE_EXT:
|
|
ng = ei_decode_tuple_header(buf, &index, &arity);
|
|
if (ng) return FALSE;
|
|
ng = (arity != 2);
|
|
if (ng) return FALSE;
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "verify_checksums") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.db_read_options.verify_checksums = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.db_read_options.verify_checksums = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else if (strcmp(atom, "fill_cache") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.db_read_options.fill_cache = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.db_read_options.fill_cache = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
items--;
|
|
}
|
|
|
|
ng = ei_decode_list_header(buf, &index, &items);
|
|
if (ng) return FALSE;
|
|
ng = (items != 0);
|
|
if (ng) return FALSE;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
lets_parse_write_options(lets_impl& impl,
|
|
const char* buf, int len)
|
|
{
|
|
int index, ng, items, arity, term_type, size_needed;
|
|
char atom[MAXATOMLEN];
|
|
|
|
index = 0;
|
|
ng = ei_decode_list_header(buf, &index, &items);
|
|
if (ng) return FALSE;
|
|
ng = (items < 0);
|
|
if (ng) return FALSE;
|
|
|
|
if (!items) return true;
|
|
|
|
while (items) {
|
|
ng = ei_get_type(buf, &index, &term_type, &size_needed);
|
|
if (ng) return FALSE;
|
|
|
|
switch (term_type) {
|
|
case ERL_ATOM_EXT:
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "sync") == 0) {
|
|
impl.db_write_options.sync = true;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
case ERL_SMALL_TUPLE_EXT:
|
|
ng = ei_decode_tuple_header(buf, &index, &arity);
|
|
if (ng) return FALSE;
|
|
ng = (arity != 2);
|
|
if (ng) return FALSE;
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "sync") == 0) {
|
|
ng = ei_decode_atom(buf, &index, atom);
|
|
if (ng) return FALSE;
|
|
if (strcmp(atom, "true") == 0) {
|
|
impl.db_write_options.sync = true;
|
|
} else if (strcmp(atom, "false") == 0) {
|
|
impl.db_write_options.sync = false;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
items--;
|
|
}
|
|
|
|
ng = ei_decode_list_header(buf, &index, &items);
|
|
if (ng) return FALSE;
|
|
ng = (items != 0);
|
|
if (ng) return FALSE;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// KEEP AS PLACEHOLDER UNTIL PATCHES ARE ACCEPTED BY Erlang/OTP TEAM
|
|
//
|
|
|
|
/*
|
|
* %CopyrightBegin%
|
|
*
|
|
* Copyright Ericsson AB 1998-2010. All Rights Reserved.
|
|
*
|
|
* The contents of this file are subject to the Erlang Public License,
|
|
* Version 1.1, (the "License"); you may not use this file except in
|
|
* compliance with the License. You should have received a copy of the
|
|
* Erlang Public License along with this software. If not, it can be
|
|
* retrieved online at http://www.erlang.org/.
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS"
|
|
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
* the License for the specific language governing rights and limitations
|
|
* under the License.
|
|
*
|
|
* %CopyrightEnd%
|
|
*
|
|
|
|
*/
|
|
|
|
#define get8(s) \
|
|
((s) += 1, \
|
|
((unsigned char *)(s))[-1] & 0xff)
|
|
|
|
#define get16be(s) \
|
|
((s) += 2, \
|
|
(((((unsigned char *)(s))[-2] << 8) | \
|
|
((unsigned char *)(s))[-1])) & 0xffff)
|
|
|
|
#define get32be(s) \
|
|
((s) += 4, \
|
|
((((unsigned char *)(s))[-4] << 24) | \
|
|
(((unsigned char *)(s))[-3] << 16) | \
|
|
(((unsigned char *)(s))[-2] << 8) | \
|
|
((unsigned char *)(s))[-1]))
|
|
|
|
// This function inspects an atom from the binary format. The p
|
|
// parameter is the name of the atom and the name should be
|
|
// zero-terminated. If the name is equal to the atom in binary
|
|
// format, returns 0. Otherwise, return -1. If name is NULL, no
|
|
// comparison is done and returns 0.
|
|
|
|
int
|
|
ei_inspect_atom(const char *buf, int *index, char *p)
|
|
{
|
|
const char *s = buf + *index;
|
|
const char *s0 = s;
|
|
int len;
|
|
|
|
if (get8(s) != ERL_ATOM_EXT) return -1;
|
|
|
|
len = get16be(s);
|
|
|
|
if (len > MAXATOMLEN) return -1;
|
|
|
|
if (p) {
|
|
if (len != (int) strlen(p)) return -1;
|
|
if (memcmp(p, s, len)) return -1;
|
|
}
|
|
s += len;
|
|
*index += s-s0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// This function inspects a binary from the binary format. The p
|
|
// parameter is set to the address of the binary. The len parameter
|
|
// is set to the actual size of the binary.
|
|
|
|
int
|
|
ei_inspect_binary(const char *buf, int *index, void **p, long *lenp)
|
|
{
|
|
const char *s = buf + *index;
|
|
const char *s0 = s;
|
|
long len;
|
|
|
|
if (get8(s) != ERL_BINARY_EXT) return -1;
|
|
|
|
len = get32be(s);
|
|
if (p) *p = (void*) s;
|
|
s += len;
|
|
|
|
if (lenp) *lenp = len;
|
|
*index += s-s0;
|
|
|
|
return 0;
|
|
}
|