lets/c_src/lets_nif.cc
Joseph Wayne Norton 4a2b2d6ac3 Initial commit
2011-10-26 23:09:08 +09:00

578 lines
15 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_nif.h"
#include "lets_nif_lib.h"
#include <stdio.h>
static inline ERL_NIF_TERM
make_badarg(ErlNifEnv* env, const leveldb::Status& status, unsigned line=0) {
if (line) {
fprintf(stderr, "MAKEBADARG %s:%d %s\n", __FILE__, line, status.ToString().c_str());
}
return enif_make_badarg(env);
}
#if 0
#define MAKEBADARG(env, status) make_badarg(env, status, __LINE__)
#else
#define MAKEBADARG(env, status) make_badarg(env, status)
#endif
// NifHandle
typedef struct
{
lets_impl impl;
} lets_nif_handle;
static ErlNifResourceType* lets_nif_RESOURCE = NULL;
static unsigned lets_nif_RESOURCE_SIZE = sizeof(lets_nif_handle);
static ErlNifFunc nif_funcs[] =
{
{"impl_open", 6, lets_nif_open6},
{"impl_destroy", 6, lets_nif_destroy6},
{"impl_repair", 6, lets_nif_repair6},
{"impl_insert", 2, lets_nif_insert2},
{"impl_insert", 3, lets_nif_insert3},
{"impl_insert_new", 2, lets_nif_insert_new2},
{"impl_insert_new", 3, lets_nif_insert_new3},
{"impl_delete", 1, lets_nif_delete1},
{"impl_delete", 2, lets_nif_delete2},
{"impl_delete_all_objects", 1, lets_nif_delete_all_objects1},
{"impl_lookup", 2, lets_nif_lookup2},
{"impl_first", 1, lets_nif_first1},
{"impl_next", 2, lets_nif_next2},
{"impl_info_memory", 1, lets_nif_info_memory1},
{"impl_info_size", 1, lets_nif_info_size1},
};
static void
lets_nif_resource_dtor(ErlNifEnv* env, void* arg)
{
lets_nif_handle* h = (lets_nif_handle*) arg;
// alive
h->impl.alive = 0;
// db
delete h->impl.db;
// db_block_cache
delete h->impl.db_block_cache;
// name
delete h->impl.name;
}
static int
on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
if (!lets_nif_lib_init(env)) {
return -1;
}
ErlNifResourceFlags flags = (ErlNifResourceFlags) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER);
lets_nif_RESOURCE = enif_open_resource_type(env, NULL,
"lets_nif_resource",
&lets_nif_resource_dtor,
flags, NULL);
if (lets_nif_RESOURCE == NULL) {
return -1;
}
return 0;
}
ERL_NIF_INIT(lets_nif, nif_funcs, &on_load, NULL, NULL, NULL);
static ERL_NIF_TERM
db_create6(const char op, ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[], lets_nif_handle** reth)
{
char type;
char privacy;
ErlNifBinary path;
ERL_NIF_TERM options;
unsigned options_len;
ERL_NIF_TERM read_options;
unsigned read_options_len;
ERL_NIF_TERM write_options;
unsigned write_options_len;
leveldb::Status status;
if (enif_is_identical(argv[0], lets_atom_set)) {
type = SET;
} else if (enif_is_identical(argv[0], lets_atom_ordered_set)) {
type = ORDERED_SET;
} else {
return MAKEBADARG(env, status);
}
if (enif_is_identical(argv[1], lets_atom_private)) {
privacy = PRIVATE;
} else if (enif_is_identical(argv[1], lets_atom_protected)) {
privacy = PROTECTED;
} else if (enif_is_identical(argv[1], lets_atom_public)) {
privacy = PUBLIC;
} else {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, argv[2], &path) || !path.size) {
return MAKEBADARG(env, status);
}
if (!enif_get_list_length(env, argv[3], &options_len)) {
return MAKEBADARG(env, status);
}
options = argv[3];
if (!enif_get_list_length(env, argv[4], &read_options_len)) {
return MAKEBADARG(env, status);
}
read_options = argv[4];
if (!enif_get_list_length(env, argv[5], &write_options_len)) {
return MAKEBADARG(env, status);
}
write_options = argv[5];
lets_nif_handle* h = (lets_nif_handle*) enif_alloc_resource(lets_nif_RESOURCE, lets_nif_RESOURCE_SIZE);
if (!h) {
return MAKEBADARG(env, status);
}
memset(h, 0, lets_nif_RESOURCE_SIZE);
if (!lets_init(h->impl, type, privacy, (const char*) path.data, path.size)) {
enif_release_resource(h);
return MAKEBADARG(env, status);
}
if (!lets_parse_options(env, h->impl, options, options_len)) {
enif_release_resource(h);
return MAKEBADARG(env, status);
}
if (!lets_parse_read_options(env, h->impl, read_options, read_options_len)) {
enif_release_resource(h);
return MAKEBADARG(env, status);
}
if (!lets_parse_write_options(env, h->impl, write_options, write_options_len)) {
enif_release_resource(h);
return MAKEBADARG(env, status);
}
if (!lets_create(h->impl, op)) {
enif_release_resource(h);
return MAKEBADARG(env, status);
}
*reth = h;
return 0;
}
ERL_NIF_TERM
lets_nif_open6(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h = NULL;
ERL_NIF_TERM badarg = db_create6(OPEN, env, argc, argv, &h);
if (!h) {
return badarg;
}
ERL_NIF_TERM result = enif_make_resource(env, h);
enif_release_resource(h);
return result;
}
ERL_NIF_TERM
lets_nif_destroy6(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h = NULL;
ERL_NIF_TERM badarg = db_create6(DESTROY, env, argc, argv, &h);
if (!h) {
return badarg;
}
enif_release_resource(h);
return lets_atom_true;
}
ERL_NIF_TERM
lets_nif_repair6(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h = NULL;
ERL_NIF_TERM badarg = db_create6(REPAIR, env, argc, argv, &h);
if (!h) {
return badarg;
}
enif_release_resource(h);
return lets_atom_true;
}
ERL_NIF_TERM
lets_nif_insert2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
ERL_NIF_TERM list;
unsigned list_len;
ERL_NIF_TERM head, tail;
int arity;
const ERL_NIF_TERM* tuple;
ErlNifBinary key;
ErlNifBinary blob;
leveldb::WriteBatch batch;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!enif_get_list_length(env, argv[1], &list_len)) {
return MAKEBADARG(env, status);
}
list = argv[1];
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
while (enif_get_list_cell(env, list, &head, &tail)) {
if (enif_get_tuple(env, head, &arity, &tuple) && arity == 2) {
if (!enif_inspect_binary(env, tuple[0], &key)) {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, tuple[1], &blob)) {
return MAKEBADARG(env, status);
}
leveldb::Slice skey((const char*) key.data, key.size);
leveldb::Slice sblob((const char*) blob.data, blob.size);
batch.Put(skey, sblob);
} else {
return MAKEBADARG(env, status);
}
list = tail;
}
status = h->impl.db->Write(h->impl.db_write_options, &batch);
if (!status.ok()) {
return MAKEBADARG(env, status);
}
return lets_atom_true;
}
ERL_NIF_TERM
lets_nif_insert3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
ErlNifBinary key;
ErlNifBinary blob;
leveldb::WriteBatch batch;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, argv[1], &key)) {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, argv[2], &blob)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
leveldb::Slice skey((const char*) key.data, key.size);
leveldb::Slice sblob((const char*) blob.data, blob.size);
batch.Put(skey, sblob);
status = h->impl.db->Write(h->impl.db_write_options, &batch);
if (!status.ok()) {
return MAKEBADARG(env, status);
}
return lets_atom_true;
}
ERL_NIF_TERM
lets_nif_insert_new2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
leveldb::Status status;
// @TODO not supported by leveldb
return MAKEBADARG(env, status);
}
ERL_NIF_TERM
lets_nif_insert_new3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
leveldb::Status status;
// @TODO not supported by leveldb
return MAKEBADARG(env, status);
}
ERL_NIF_TERM
lets_nif_delete1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
leveldb::WriteOptions db_write_options;
leveldb::WriteBatch batch;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
// alive
h->impl.alive = 0;
db_write_options.sync = true;
status = h->impl.db->Write(db_write_options, &batch);
if (!status.ok()) {
return MAKEBADARG(env, status);
}
// @TBD This is quite risky ... need to re-consider.
// delete h->impl.db;
// h->impl.db = NULL;
return lets_atom_true;
}
ERL_NIF_TERM
lets_nif_delete2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
ErlNifBinary key;
leveldb::WriteBatch batch;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, argv[1], &key)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
leveldb::Slice skey((const char*) key.data, key.size);
batch.Delete(skey);
status = h->impl.db->Write(h->impl.db_write_options, &batch);
if (!status.ok()) {
return MAKEBADARG(env, status);
}
return lets_atom_true;
}
ERL_NIF_TERM
lets_nif_delete_all_objects1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
leveldb::Status status;
// @TODO not supported by leveldb
return MAKEBADARG(env, status);
}
ERL_NIF_TERM
lets_nif_lookup2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
ErlNifBinary key;
ERL_NIF_TERM blob = 0;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, argv[1], &key)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
if (!it) {
return MAKEBADARG(env, status);
}
leveldb::Slice skey((const char*) key.data, key.size);
it->Seek(skey);
if (!it->Valid() || it->key().compare(skey) != 0) {
delete it;
return lets_atom_true;
}
size_t size = it->value().size();
unsigned char* b = enif_make_new_binary(env, size, &blob);
if (!b) {
delete it;
return MAKEBADARG(env, status);
}
memcpy(b, it->value().data(), size);
delete it;
return blob;
}
ERL_NIF_TERM
lets_nif_first1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
ERL_NIF_TERM first_key = 0;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
if (!it) {
return MAKEBADARG(env, status);
}
it->SeekToFirst();
if (!it->Valid()) {
delete it;
return lets_atom_end_of_table;
}
size_t size = it->key().size();
unsigned char* k = enif_make_new_binary(env, size, &first_key);
if (!k) {
delete it;
return MAKEBADARG(env, status);
}
memcpy(k, it->key().data(), size);
delete it;
return first_key;
}
ERL_NIF_TERM
lets_nif_next2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
ErlNifBinary key;
ERL_NIF_TERM next_key = 0;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!enif_inspect_binary(env, argv[1], &key)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
leveldb::Iterator* it = h->impl.db->NewIterator(h->impl.db_read_options);
if (!it) {
return MAKEBADARG(env, status);
}
leveldb::Slice skey((const char*) key.data, key.size);
it->Seek(skey);
if (!it->Valid()) {
delete it;
return lets_atom_end_of_table;
}
if (it->key().compare(skey) == 0) {
it->Next();
if (!it->Valid()) {
delete it;
return lets_atom_end_of_table;
}
}
size_t size = it->key().size();
unsigned char* k = enif_make_new_binary(env, size, &next_key);
if (!k) {
delete it;
return MAKEBADARG(env, status);
}
memcpy(k, it->key().data(), size);
delete it;
return next_key;
}
ERL_NIF_TERM
lets_nif_info_memory1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
// ERL_NIF_TERM info;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
// @TODO implementation
// info = enif_make_uint64(env, h->impl.db_memory);
// return info;
return MAKEBADARG(env, status);
}
ERL_NIF_TERM
lets_nif_info_size1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
lets_nif_handle* h;
// ERL_NIF_TERM info;
leveldb::Status status;
if (!enif_get_resource(env, argv[0], lets_nif_RESOURCE, (void**)&h)) {
return MAKEBADARG(env, status);
}
if (!h->impl.alive) {
return MAKEBADARG(env, status);
}
// @TODO implementation
// info = enif_make_uint64(env, h->impl.db_size);
// return info;
return MAKEBADARG(env, status);
}