|
|
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not
- // use this file except in compliance with the License. You may obtain a copy of
- // the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations under
- // the License.
-
- #include <assert.h>
- #include <string.h>
- #include <stdio.h>
-
- #include "hqueue.h"
-
-
- typedef struct
- {
- ERL_NIF_TERM atom_ok;
- ERL_NIF_TERM atom_error;
- ERL_NIF_TERM atom_value;
- ERL_NIF_TERM atom_empty;
- ERL_NIF_TERM atom_full;
- ERL_NIF_TERM atom_max_elems;
- ERL_NIF_TERM atom_heap_size;
- ERL_NIF_TERM atom_too_small;
- ErlNifResourceType* res_hqueue;
- } hqueue_priv;
-
-
- typedef struct
- {
- ErlNifEnv* env;
- ERL_NIF_TERM value;
- } hqnode_nif_t;
-
-
- typedef struct
- {
- int version;
- uint64_t gen;
- hqueue_t* hqueue;
- ErlNifPid p;
- } hqueue_nif_t;
-
-
- static const uint32_t default_max_elems = UINT32_MAX-1;
- static const uint32_t default_heap_size = 1024;
-
-
- static inline ERL_NIF_TERM
- make_atom(ErlNifEnv* env, const char* name)
- {
- ERL_NIF_TERM ret;
- if(enif_make_existing_atom(env, name, &ret, ERL_NIF_LATIN1)) {
- return ret;
- }
- return enif_make_atom(env, name);
- }
-
-
- static inline ERL_NIF_TERM
- make_ok(ErlNifEnv* env, hqueue_priv* priv, ERL_NIF_TERM value)
- {
- return enif_make_tuple2(env, priv->atom_ok, value);
- }
-
-
- static inline ERL_NIF_TERM
- make_error(ErlNifEnv* env, hqueue_priv* priv, ERL_NIF_TERM reason)
- {
- return enif_make_tuple2(env, priv->atom_error, reason);
- }
-
-
- static inline int
- check_pid(ErlNifEnv* env, hqueue_nif_t* hqueue_nif)
- {
- ErlNifPid pid;
- enif_self(env, &pid);
-
- if(enif_compare(pid.pid, hqueue_nif->p.pid) == 0) {
- return 1;
- }
-
- return 0;
- }
-
-
- void
- hqueue_nif_node_free(hqnode_nif_t* hqnode_nif)
- {
- enif_free_env(hqnode_nif->env);
- enif_free(hqnode_nif);
-
- return;
- }
-
-
- void
- hqueue_nif_node_free_ext(void* node)
- {
- hqueue_nif_node_free((hqnode_nif_t*) node);
-
- return;
- }
-
-
- hqnode_nif_t*
- hqueue_nif_node_alloc()
- {
- hqnode_nif_t* node = (hqnode_nif_t*) enif_alloc(sizeof(hqnode_nif_t*));
-
- memset(node, 0, sizeof(hqnode_nif_t));
-
- node->env = enif_alloc_env();
-
- return node;
- }
-
-
- static int
- get_uint_param(ErlNifEnv* env, ERL_NIF_TERM value, ERL_NIF_TERM atom, uint32_t* p)
- {
- const ERL_NIF_TERM* tuple;
- int arity;
-
- if(!enif_get_tuple(env, value, &arity, &tuple)) {
- return 0;
- }
-
- if(arity != 2) {
- return 0;
- }
-
- if(enif_compare(tuple[0], atom) != 0) {
- return 0;
- }
-
- if(!enif_get_uint(env, tuple[1], p)) {
- return 0;
- }
-
- return 1;
- }
-
-
- static inline hqueue_nif_t*
- hqueue_nif_create_int(ErlNifEnv* env, hqueue_priv* priv, uint32_t max_elems,
- uint32_t heap_size)
- {
- hqueue_nif_t* hqueue_nif = NULL;
-
- assert(priv != NULL && "missing private data member");
-
- hqueue_nif = (hqueue_nif_t*) enif_alloc_resource(
- priv->res_hqueue, sizeof(hqueue_nif_t));
- memset(hqueue_nif, 0, sizeof(hqueue_nif_t));
- hqueue_nif->version = HQ_VERSION;
-
- hqueue_nif->hqueue = hqueue_new(max_elems, heap_size);
-
- if(hqueue_nif->hqueue == NULL ) {
- enif_release_resource(hqueue_nif);
- return NULL;
- }
-
- enif_self(env, &(hqueue_nif->p));
-
- return hqueue_nif;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_new(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
- ERL_NIF_TERM opts;
- ERL_NIF_TERM value;
- uint32_t max_elems = default_max_elems;
- uint32_t heap_size = default_heap_size;
-
- if(argc != 1) {
- return enif_make_badarg(env);
- }
-
- opts = argv[0];
- if(!enif_is_list(env, opts)) {
- return enif_make_badarg(env);
- }
-
- while(enif_get_list_cell(env, opts, &value, &opts)) {
- if(get_uint_param(env, value, priv->atom_max_elems, &max_elems)) {
- continue;
- } else if(get_uint_param(env, value, priv->atom_heap_size, &heap_size)) {
- continue;
- } else {
- return enif_make_badarg(env);
- }
- }
-
- hqueue_nif = hqueue_nif_create_int(env, priv, max_elems, heap_size);
- if(hqueue_nif == NULL) {
- return enif_make_badarg(env);
- }
-
- ret = enif_make_resource(env, hqueue_nif);
- enif_release_resource(hqueue_nif);
-
- return make_ok(env, priv, ret);
- }
-
-
- static void
- hqueue_nif_free(ErlNifEnv* env, void* obj)
- {
- hqueue_nif_t* hqueue_nif = (hqueue_nif_t*) obj;
-
- hqueue_free2(hqueue_nif->hqueue, hqueue_nif_node_free_ext);
-
- return;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_extract_max(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- hqnode_nif_t* hqnode_nif;
- double tmp_priority;
- ERL_NIF_TERM ret;
- ERL_NIF_TERM priority;
- ERL_NIF_TERM value;
-
- if(argc != 1) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if (!hqueue_extract_max(hqueue_nif->hqueue, &tmp_priority, (void**) &hqnode_nif)) {
- return make_error(env, priv, priv->atom_empty);
- }
-
- priority = enif_make_double(env, tmp_priority);
- value = enif_make_copy(env, hqnode_nif->value);
- ret = enif_make_tuple2(env, priority, value);
-
- hqueue_nif_node_free(hqnode_nif);
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_insert(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- hqnode_nif_t* hqnode_nif;
- ERL_NIF_TERM ret;
- double priority;
-
- if(argc != 3) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_double(env, argv[1], &priority)) {
- return enif_make_badarg(env);
- }
-
- if(priority < 0.0) {
- return enif_make_badarg(env);
- }
-
- hqnode_nif = hqueue_nif_node_alloc();
- hqnode_nif->value = enif_make_copy(hqnode_nif->env, argv[2]);
-
- if (!hqueue_insert(hqueue_nif->hqueue, priority, (void*) hqnode_nif)) {
- return make_error(env, priv, priv->atom_full);
- }
-
- ret = priv->atom_ok;
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
-
- if(argc != 1) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- ret = enif_make_uint64(env, hqueue_size(hqueue_nif->hqueue));
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_heap_size(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
-
- if(argc != 1) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- ret = enif_make_uint64(env, hqueue_heap_size(hqueue_nif->hqueue));
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_max_elems(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
-
- if(argc != 1) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- ret = enif_make_uint64(env, hqueue_max_elems(hqueue_nif->hqueue));
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- hqueue_t* hqueue;
- hqnode_nif_t* hqnode_nif;
- double tmp_priority;
- ERL_NIF_TERM ret = enif_make_list(env, 0);
- ERL_NIF_TERM priority;
- ERL_NIF_TERM value;
- ERL_NIF_TERM tuple;
- uint32_t i;
-
- if(argc != 1) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- hqueue = hqueue_nif->hqueue;
-
- for (i = 1; i <= hqueue_size(hqueue); i++) {
- hqueue_get_elem(hqueue, i, &tmp_priority, (void **) &hqnode_nif);
- priority = enif_make_double(env, tmp_priority);
- value = enif_make_copy(env, hqnode_nif->value);
- tuple = enif_make_tuple2(env, priority, value);
- ret = enif_make_list_cell(env, tuple, ret);
- }
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_scale_by(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
- double factor;
-
- if(argc != 2) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_double(env, argv[1], &factor)) {
- return enif_make_badarg(env);
- }
-
- if(factor < 0.0) {
- return enif_make_badarg(env);
- }
-
- hqueue_scale_by(hqueue_nif->hqueue, factor);
-
- ret = priv->atom_ok;
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_resize_heap(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
- uint32_t new_heap_size;
- uint32_t old_heap_size;
-
- if(argc != 2) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_uint(env, argv[1], &new_heap_size)) {
- return enif_make_badarg(env);
- }
-
- if(hqueue_size(hqueue_nif->hqueue) > new_heap_size) {
- return make_error(env, priv, priv->atom_too_small);
- }
-
- if((old_heap_size = hqueue_resize_heap(hqueue_nif->hqueue, new_heap_size)) == 0) {
- return enif_make_badarg(env);
- }
-
- ret = enif_make_uint64(env, old_heap_size);
-
- return ret;
- }
-
-
- static ERL_NIF_TERM
- hqueue_nif_set_max_elems(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- hqueue_priv* priv = enif_priv_data(env);
- hqueue_nif_t* hqueue_nif;
- ERL_NIF_TERM ret;
- uint32_t new_max_elems;
- uint32_t old_max_elems;
-
- if(argc != 2) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_resource(env, argv[0], priv->res_hqueue, (void**) &hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!check_pid(env, hqueue_nif)) {
- return enif_make_badarg(env);
- }
-
- if(!enif_get_uint(env, argv[1], &new_max_elems)) {
- return enif_make_badarg(env);
- }
-
- if(hqueue_size(hqueue_nif->hqueue) > new_max_elems) {
- return make_error(env, priv, priv->atom_too_small);
- }
-
- if ((old_max_elems = hqueue_set_max_elems(hqueue_nif->hqueue, new_max_elems)) == 0) {
- return enif_make_badarg(env);
- }
-
- ret = enif_make_uint64(env, old_max_elems);
-
- return ret;
- }
-
-
- static int
- load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
- {
- int flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER;
- ErlNifResourceType* res;
-
- hqueue_priv* new_priv = (hqueue_priv*) enif_alloc(sizeof(hqueue_priv));
- if(new_priv == NULL) {
- return 1;
- }
-
- res = enif_open_resource_type(
- env, NULL, "hqueue", hqueue_nif_free, flags, NULL);
- if(res == NULL) {
- enif_free(new_priv);
- return 1;
- }
- new_priv->res_hqueue = res;
-
- new_priv->atom_ok = make_atom(env, "ok");
- new_priv->atom_error = make_atom(env, "error");
- new_priv->atom_value = make_atom(env, "value");
- new_priv->atom_empty = make_atom(env, "empty");
- new_priv->atom_full = make_atom(env, "full");
- new_priv->atom_max_elems = make_atom(env, "max_elems");
- new_priv->atom_heap_size = make_atom(env, "heap_size");
- new_priv->atom_too_small = make_atom(env, "too_small");
-
- *priv = (void*) new_priv;
-
- return 0;
- }
-
-
- static int
- upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
- {
- return load(env, priv, info);
- }
-
-
- static void
- unload(ErlNifEnv* env, void* priv)
- {
- enif_free(priv);
- return;
- }
-
-
- static ErlNifFunc funcs[] = {
- {"new", 1, hqueue_nif_new},
- {"extract_max", 1, hqueue_nif_extract_max},
- {"insert", 3, hqueue_nif_insert},
- {"size", 1, hqueue_nif_size},
- {"heap_size", 1, hqueue_nif_heap_size},
- {"max_elems", 1, hqueue_nif_max_elems},
- {"set_max_elems", 2, hqueue_nif_set_max_elems},
- {"to_list", 1, hqueue_nif_to_list},
- {"scale_by", 2, hqueue_nif_scale_by},
- {"resize_heap", 2, hqueue_nif_resize_heap}
- };
-
-
- ERL_NIF_INIT(hqueue, funcs, &load, NULL, &upgrade, &unload);
|