<commit_before>#include "erl_nif.h"
#include "erl_nif_compat.h"

#include "leveldb/cache.h"
#include "leveldb/db.h"
#include "leveldb/options.h"
#include "leveldb/write_batch.h"

#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C }
#else
#define BEGIN_C
#define END_C
#endif

#define ENIF_IS(a, b) (enif_compare(a, b) == 0)

typedef struct {
    ErlNifResourceType*         db_res;
    ErlNifResourceType*         it_res;
    ErlNifResourceType*         ss_res;
    ErlNifResourceType*         wb_res;
} State;

typedef struct {
    ErlNifMutex*                lock;
    leveldb::DB*                db;
} DBRes;

typedef struct {
    ErlNifMutex*                lock;
    DBRes*                      dbres;
    leveldb::Iterator*          iter;
} IterRes;

typedef struct {
    ErlNifMutex*                lock;
    DBRes*                      dbres;
    const leveldb::Snapshot*    snap;
} SSRes;

typedef struct {
    ErlNifEnv*                  env;
    ErlNifMutex*                lock;
    DBRes*                      dbres;
    leveldb::WriteBatch*        batch;
} WBRes;

void
free_dbres(ErlNifEnv* env, void* obj)
{
    DBRes* res = (DBRes*) obj;
    if(res->db != NULL) {
        delete res->db;
    }
}

void
free_itres(ErlNifEnv* env, void* obj)
{
    IterRes* res = (IterRes*) obj;
    if(res->iter != NULL) {
        delete res->iter;
    }
    enif_release_resource(res->dbres);
}

void
free_ssres(ErlNifEnv* env, void* obj)
{
    SSRes* res = (SSRes*) obj;
    if(res->snap != NULL) {
        res->dbres->db->ReleaseSnapshot(res->snap);
    }
    enif_release_resource(res->dbres);
}

void
free_wbres(ErlNifEnv* env, void* obj)
{
    WBRes* res = (WBRes*) obj;
    if(res->batch != NULL) {
        delete res->batch;
    }
    enif_release_resource(res->dbres);
    enif_free_env(res->env);
}

static inline ERL_NIF_TERM
make_atom(ErlNifEnv* env, const char* name)
{
    ERL_NIF_TERM ret;
    if(enif_make_existing_atom_compat(env, name, &ret, ERL_NIF_LATIN1)) {
        return ret;
    }
    return enif_make_atom(env, name);
}

static inline ERL_NIF_TERM
make_ok(ErlNifEnv* env, ERL_NIF_TERM mesg)
{
    ERL_NIF_TERM ok = make_atom(env, "ok");
    return enif_make_tuple2(env, ok, mesg);   
}

static inline ERL_NIF_TERM
make_error(ErlNifEnv* env, const char* mesg)
{
    ERL_NIF_TERM error = make_atom(env, "error");
    return enif_make_tuple2(env, error, make_atom(env, mesg));
}

static int
set_db_opts(ErlNifEnv* env, ERL_NIF_TERM list, leveldb::Options& opts)
{
    ERL_NIF_TERM head;
    const ERL_NIF_TERM* tuple;
    int arity;
    
    if(!enif_is_list(env, list)) {
        return 0;
    }
    
    while(enif_get_list_cell(env, list, &head, &list)) {
        if(ENIF_IS(head, make_atom(env, "create_if_missing"))) {
            if(opts.error_if_exists) return 0;
            opts.create_if_missing = true;
            continue;
        }
        
        if(ENIF_IS(head, make_atom(env, "error_if_exists"))) {
            if(opts.create_if_missing) return 0;
            opts.error_if_exists = true;
            continue;
        }
        
        if(ENIF_IS(head, make_atom(env, "paranoid_checks"))) {
            opts.paranoid_checks = true;
            continue;
        }
        
        if(!enif_get_tuple(env, head, &arity, &tuple)) {
            return 0;
        }
        
        if(arity != 2) return 0;
        
        if(ENIF_IS(tuple[0], make_atom(env, "write_buffer_size"))) {
            unsigned int sz;
            if(!enif_get_uint(env, tuple[1], (unsigned int*) &sz)) {
                return 0;
            }
            opts.write_buffer_size = (size_t) sz;
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "max_open_files"))) {
            int num;
            if(!enif_get_int(env, tuple[1], &num)) {
                return 0;
            }
            opts.max_open_files = (size_t) num;
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "cache_size"))) {
            unsigned int sz;
            if(!enif_get_uint(env, tuple[1], &sz)) {
                return 0;
            }
            opts.block_cache = leveldb::NewLRUCache((size_t) sz);
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "block_size"))) {
            unsigned int sz;
            if(!enif_get_uint(env, tuple[1], &sz)) {
                return 0;
            }
            opts.block_size = (size_t) sz;
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "block_restart_interval"))) {
            int bri;
            if(!enif_get_int(env, tuple[1], &bri)) {
                return 0;
            }
            opts.block_restart_interval = bri;
            continue;
        }
        
        return 0;
    }
    
    return 1;
}

static inline int
set_read_opts(ErlNifEnv* env, ERL_NIF_TERM list, leveldb::ReadOptions& opts)
{
    State* st = (State*) enif_priv_data(env);
    SSRes* res;
    ERL_NIF_TERM head;
    const ERL_NIF_TERM* tuple;
    int arity;
    
    if(!enif_is_list(env, list)) {
        return 0;
    }
    
    while(enif_get_list_cell(env, list, &head, &list)) {
        if(ENIF_IS(head, make_atom(env, "verify_checksums"))) {
            opts.verify_checksums = true;
            continue;
        }
        
        if(ENIF_IS(head, make_atom(env, "skip_cache"))) {
            opts.fill_cache = false;
            continue;
        }
        
        if(!enif_get_tuple(env, head, &arity, &tuple)) {
            return 0;
        }
        
        if(arity != 2) return 0;
        
        if(ENIF_IS(tuple[0], make_atom(env, "snapshot"))) {
            if(!enif_get_resource(env, tuple[1], st->ss_res, (void**) &res)) {
                return 0;
            }
            opts.snapshot = res->snap;
            continue;
        }

        return 0;
    }

    return 1;
}

static inline int
set_write_opts(ErlNifEnv* env, ERL_NIF_TERM list,
                leveldb::WriteOptions& opts, const leveldb::Snapshot** snap)
{
    ERL_NIF_TERM head;
    
    if(!enif_is_list(env, list)) {
        return 0;
    }
    
    while(enif_get_list_cell(env, list, &head, &list)) {
        if(ENIF_IS(head, make_atom(env, "sync"))) {
            opts.sync = true;
            continue;
        }

        if(ENIF_IS(head, make_atom(env, "snapshot"))) {
            opts.post_write_snapshot = snap;
            continue;
        }

        return 0;
    }

    return 1;
}

static inline ERL_NIF_TERM
mk_snapshot(ErlNifEnv* env, DBRes* dbres, const leveldb::Snapshot* snap)
{
    State* st = (State*) enif_priv_data(env);
    SSRes* ssres = (SSRes*) enif_alloc_resource(st->ss_res, sizeof(SSRes));
    ssres->lock = NULL;
    ssres->dbres = dbres;
    enif_keep_resource(ssres->dbres);
    ssres->snap = snap;

    ERL_NIF_TERM ret = enif_make_resource(env, ssres);
    enif_release_resource(ssres);

    return make_ok(env, ret);    
}

BEGIN_C

static int
load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    State* st = (State*) enif_alloc(sizeof(State));
    const char* mod = "erleveldb";
    const char* db_name = "DBResource";
    const char* it_name = "IteratorResource";
    const char* ss_name = "SnapshotResource";
    const char* wb_name = "WBResource";
    int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;

    if(st == NULL) {
        return -1;
    }
    
    st->db_res = enif_open_resource_type(env, mod, db_name, free_dbres, 
                                        (ErlNifResourceFlags) flags, NULL);
    if(st->db_res == NULL) {
        enif_free(st);
        return -1;
    }
    
    st->it_res = enif_open_resource_type(env, mod, it_name, free_itres,
                                        (ErlNifResourceFlags) flags, NULL);
    if(st->it_res == NULL) {
        enif_free(st);
        return -1;
    }
    
    st->ss_res = enif_open_resource_type(env, mod, ss_name, free_ssres,
                                        (ErlNifResourceFlags) flags, NULL);
    if(st->ss_res == NULL) {
        enif_free(st);
        return -1;
    }
    
    st->wb_res = enif_open_resource_type(env, mod, wb_name, free_wbres,
                                        (ErlNifResourceFlags) flags, NULL);
    if(st->wb_res == NULL) {
        enif_free(st);
        return -1;
    }

    *priv = (void*) st;
    return 0;
}

static int
reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    return 0;
}

static int
upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
{
    *priv = *old_priv;
    return 0;
}

static void
unload(ErlNifEnv* env, void* priv)
{<commit_msg>Minor tweaks to clean up resource creation.<commit_after>#include "erl_nif.h"
#include "erl_nif_compat.h"

#include "leveldb/cache.h"
#include "leveldb/db.h"
#include "leveldb/options.h"
#include "leveldb/write_batch.h"

#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C }
#else
#define BEGIN_C
#define END_C
#endif

#define ENIF_RF ErlNifResourceFlags
#define ENIF_ORT enif_open_resource_type
#define ENIF_IS(a, b) (enif_compare(a, b) == 0)

typedef struct {
    ErlNifResourceType*         db_res;
    ErlNifResourceType*         it_res;
    ErlNifResourceType*         ss_res;
    ErlNifResourceType*         wb_res;
} State;

typedef struct {
    ErlNifMutex*                lock;
    leveldb::DB*                db;
} DBRes;

typedef struct {
    ErlNifMutex*                lock;
    DBRes*                      dbres;
    leveldb::Iterator*          iter;
} IterRes;

typedef struct {
    ErlNifMutex*                lock;
    DBRes*                      dbres;
    const leveldb::Snapshot*    snap;
} SSRes;

typedef struct {
    ErlNifEnv*                  env;
    ErlNifMutex*                lock;
    DBRes*                      dbres;
    leveldb::WriteBatch*        batch;
} WBRes;

void
free_dbres(ErlNifEnv* env, void* obj)
{
    DBRes* res = (DBRes*) obj;
    if(res->db != NULL) {
        delete res->db;
    }
}

void
free_itres(ErlNifEnv* env, void* obj)
{
    IterRes* res = (IterRes*) obj;
    if(res->iter != NULL) {
        delete res->iter;
    }
    enif_release_resource(res->dbres);
}

void
free_ssres(ErlNifEnv* env, void* obj)
{
    SSRes* res = (SSRes*) obj;
    if(res->snap != NULL) {
        res->dbres->db->ReleaseSnapshot(res->snap);
    }
    enif_release_resource(res->dbres);
}

void
free_wbres(ErlNifEnv* env, void* obj)
{
    WBRes* res = (WBRes*) obj;
    if(res->batch != NULL) {
        delete res->batch;
    }
    enif_release_resource(res->dbres);
    enif_free_env(res->env);
}

static inline ERL_NIF_TERM
make_atom(ErlNifEnv* env, const char* name)
{
    ERL_NIF_TERM ret;
    if(enif_make_existing_atom_compat(env, name, &ret, ERL_NIF_LATIN1)) {
        return ret;
    }
    return enif_make_atom(env, name);
}

static inline ERL_NIF_TERM
make_ok(ErlNifEnv* env, ERL_NIF_TERM mesg)
{
    ERL_NIF_TERM ok = make_atom(env, "ok");
    return enif_make_tuple2(env, ok, mesg);   
}

static inline ERL_NIF_TERM
make_error(ErlNifEnv* env, const char* mesg)
{
    ERL_NIF_TERM error = make_atom(env, "error");
    return enif_make_tuple2(env, error, make_atom(env, mesg));
}

static int
set_db_opts(ErlNifEnv* env, ERL_NIF_TERM list, leveldb::Options& opts)
{
    ERL_NIF_TERM head;
    const ERL_NIF_TERM* tuple;
    int arity;
    
    if(!enif_is_list(env, list)) {
        return 0;
    }
    
    while(enif_get_list_cell(env, list, &head, &list)) {
        if(ENIF_IS(head, make_atom(env, "create_if_missing"))) {
            if(opts.error_if_exists) return 0;
            opts.create_if_missing = true;
            continue;
        }
        
        if(ENIF_IS(head, make_atom(env, "error_if_exists"))) {
            if(opts.create_if_missing) return 0;
            opts.error_if_exists = true;
            continue;
        }
        
        if(ENIF_IS(head, make_atom(env, "paranoid_checks"))) {
            opts.paranoid_checks = true;
            continue;
        }
        
        if(!enif_get_tuple(env, head, &arity, &tuple)) {
            return 0;
        }
        
        if(arity != 2) return 0;
        
        if(ENIF_IS(tuple[0], make_atom(env, "write_buffer_size"))) {
            unsigned int sz;
            if(!enif_get_uint(env, tuple[1], (unsigned int*) &sz)) {
                return 0;
            }
            opts.write_buffer_size = (size_t) sz;
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "max_open_files"))) {
            int num;
            if(!enif_get_int(env, tuple[1], &num)) {
                return 0;
            }
            opts.max_open_files = (size_t) num;
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "cache_size"))) {
            unsigned int sz;
            if(!enif_get_uint(env, tuple[1], &sz)) {
                return 0;
            }
            opts.block_cache = leveldb::NewLRUCache((size_t) sz);
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "block_size"))) {
            unsigned int sz;
            if(!enif_get_uint(env, tuple[1], &sz)) {
                return 0;
            }
            opts.block_size = (size_t) sz;
            continue;
        }
        
        if(ENIF_IS(tuple[0], make_atom(env, "block_restart_interval"))) {
            int bri;
            if(!enif_get_int(env, tuple[1], &bri)) {
                return 0;
            }
            opts.block_restart_interval = bri;
            continue;
        }
        
        return 0;
    }
    
    return 1;
}

static inline int
set_read_opts(ErlNifEnv* env, ERL_NIF_TERM list, leveldb::ReadOptions& opts)
{
    State* st = (State*) enif_priv_data(env);
    SSRes* res;
    ERL_NIF_TERM head;
    const ERL_NIF_TERM* tuple;
    int arity;
    
    if(!enif_is_list(env, list)) {
        return 0;
    }
    
    while(enif_get_list_cell(env, list, &head, &list)) {
        if(ENIF_IS(head, make_atom(env, "verify_checksums"))) {
            opts.verify_checksums = true;
            continue;
        }
        
        if(ENIF_IS(head, make_atom(env, "skip_cache"))) {
            opts.fill_cache = false;
            continue;
        }
        
        if(!enif_get_tuple(env, head, &arity, &tuple)) {
            return 0;
        }
        
        if(arity != 2) return 0;
        
        if(ENIF_IS(tuple[0], make_atom(env, "snapshot"))) {
            if(!enif_get_resource(env, tuple[1], st->ss_res, (void**) &res)) {
                return 0;
            }
            opts.snapshot = res->snap;
            continue;
        }

        return 0;
    }

    return 1;
}

static inline int
set_write_opts(ErlNifEnv* env, ERL_NIF_TERM list,
                leveldb::WriteOptions& opts, const leveldb::Snapshot** snap)
{
    ERL_NIF_TERM head;
    
    if(!enif_is_list(env, list)) {
        return 0;
    }
    
    while(enif_get_list_cell(env, list, &head, &list)) {
        if(ENIF_IS(head, make_atom(env, "sync"))) {
            opts.sync = true;
            continue;
        }

        if(ENIF_IS(head, make_atom(env, "snapshot"))) {
            opts.post_write_snapshot = snap;
            continue;
        }

        return 0;
    }

    return 1;
}

static inline ERL_NIF_TERM
mk_snapshot(ErlNifEnv* env, DBRes* dbres, const leveldb::Snapshot* snap)
{
    State* st = (State*) enif_priv_data(env);
    SSRes* ssres = (SSRes*) enif_alloc_resource(st->ss_res, sizeof(SSRes));
    ssres->lock = NULL;
    ssres->dbres = dbres;
    enif_keep_resource(ssres->dbres);
    ssres->snap = snap;

    ERL_NIF_TERM ret = enif_make_resource(env, ssres);
    enif_release_resource(ssres);

    return make_ok(env, ret);    
}

BEGIN_C

static int
load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    State* st = (State*) enif_alloc(sizeof(State));
    const char* mod = "erleveldb";
    const char* db_name = "DBResource";
    const char* it_name = "IteratorResource";
    const char* ss_name = "SnapshotResource";
    const char* wb_name = "WBResource";
    ENIF_RF flags = (ENIF_RF) (ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER);

    if(st == NULL) {
        return -1;
    }
    
    st->db_res = ENIF_ORT(env, mod, db_name, free_dbres, flags, NULL);
    if(st->db_res == NULL) {
        enif_free(st);
        return -1;
    }

    st->it_res = ENIF_ORT(env, mod, it_name, free_itres, flags, NULL);
    if(st->it_res == NULL) {
        enif_free(st);
        return -1;
    }
    
    st->ss_res = ENIF_ORT(env, mod, ss_name, free_ssres, flags, NULL);
    if(st->ss_res == NULL) {
        enif_free(st);
        return -1;
    }

    st->wb_res = ENIF_ORT(env, mod, wb_name, free_wbres, flags, NULL);
    if(st->wb_res == NULL) {
        enif_free(st);
        return -1;
    }

    *priv = (void*) st;
    return 0;
}

static int
reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    return 0;
}

static int
upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
{
    *priv = *old_priv;
    return 0;
}

static void
unload(ErlNifEnv* env, void* priv)
{
{'fraction_non_alphanumeric': 0.12285546415981198, 'fraction_numerical': 0.005522914218566393, 'mean_word_length': 2.0254177035193743, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6093614', 'n_tokens_mistral': 6364, 'n_tokens_neox': 6220, 'n_words': 1189}