c_src/hyper_carray.c (203 lines of code) (raw):

// Copyright (c) 2014 Johannes Huning <mail@johanneshuning.com> // Copyright (c) 2015 Christian Lundgren, Chris de Vries, and Jon Elverkilde // // 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. // // Format this file with // indent -kr -i8 -sob c_src/hyper_carray.c // before committing. #include <stdint.h> #include <stdio.h> #include <string.h> #include "erl_nif.h" /* * Erlang NIF resource type used to 'tag' hyper_carrays. */ static ErlNifResourceType *carray_resource; /* * Single resource produced and consumed by these NIFs. * Header placed directly in front of the items it points to. */ struct hyper_carray { /* * Precision = log_2(size). Handy to have it. */ unsigned int precision; /* * Number of items. */ unsigned int size; /* * Array of items each one byte in size. */ uint8_t *items; }; #ifdef _WIN32 typedef struct hyper_carray *carray_ptr; #else typedef struct hyper_carray *restrict carray_ptr; #endif #define HYPER_CARRAY_SIZE sizeof(struct hyper_carray) /* * Attempts to read a hyper_carray from _term into _varname. * Returns badarg on failure to do so. */ #define HYPER_CARRAY_OR_BADARG(_term, _varname) \ void* _varname_res = NULL; \ if (!enif_get_resource(env, _term, carray_resource, &_varname_res)) \ return enif_make_badarg(env); \ _varname = _varname_res; /* * Allocate a new hyper_carray for use as an Erlang NIF resource. */ static void carray_alloc(unsigned int precision, carray_ptr * arr) { unsigned int nitems = 0x01 << precision; size_t header_size = HYPER_CARRAY_SIZE; size_t res_size = header_size + nitems; void *res = enif_alloc_resource(carray_resource, res_size); *arr = res; memset(*arr, 0, header_size); (*arr)->precision = precision; (*arr)->size = nitems; (*arr)->items = (uint8_t *) res + header_size; } /* * Given an hyper_carray and a valid index, set the value at that index to * max(current value, given value). */ #ifdef _WIN32 static void carray_merge_item(carray_ptr arr, unsigned int index, unsigned int value) #else static inline void carray_merge_item(carray_ptr arr, unsigned int index, unsigned int value) #endif { uint8_t *item = arr->items + index; *item = (value > *item) ? value : *item; } /* * Create a new hyper_carray resource with all items set to 0. */ static ERL_NIF_TERM new_hyper_carray(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { unsigned int precision = 0; if (!enif_get_uint(env, argv[0], &precision)) return enif_make_badarg(env); carray_ptr arr = NULL; carray_alloc(precision, &arr); memset(arr->items, 0, arr->size); ERL_NIF_TERM erl_res = enif_make_resource(env, arr); enif_release_resource(arr); return erl_res; } /* * NIF variant of carray_merge_item (see above). */ static ERL_NIF_TERM set(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { carray_ptr arr = NULL; HYPER_CARRAY_OR_BADARG(argv[2], arr); unsigned int index = 0; unsigned int new_value = 0; if (!enif_get_uint(env, argv[0], &index) || !enif_get_uint(env, argv[1], &new_value)) return enif_make_badarg(env); // Validate bounds if (index > arr->size - 1) return enif_make_badarg(env); carray_merge_item(arr, index, new_value); return argv[2]; } void dtor(ErlNifEnv * env, void *obj); /* * Given a list of at least 1 hyper_carrays [A,B,...], merge into a single new * hyper_carray N. Where the i-ths item N[i] is max(A[i], B[i], ...). * A, B, and so on are assumed to be _different_ hyper_carrays. */ static ERL_NIF_TERM max_merge(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { unsigned int narrays = 0; ERL_NIF_TERM head; ERL_NIF_TERM tail; if (!enif_get_list_length(env, argv[0], &narrays) || !enif_get_list_cell(env, argv[0], &head, &tail)) return enif_make_badarg(env); if (narrays < 1) return enif_make_badarg(env); carray_ptr first = NULL; HYPER_CARRAY_OR_BADARG(head, first); const unsigned int nitems = first->size; carray_ptr acc = NULL; carray_alloc(first->precision, &acc); memcpy(acc->items, first->items, acc->size); // Merge arrays for (int i = 1; i < narrays; ++i) { carray_ptr curr = NULL; if (!enif_get_list_cell(env, tail, &head, &tail) || !enif_get_resource(env, head, carray_resource, (void *) &curr)) goto dealloc_and_badarg; // Require uniform precision. if (curr->precision != acc->precision) goto dealloc_and_badarg; for (uint8_t * accitem = acc->items, *item = curr->items, *enditem = curr->items + nitems; item != enditem; ++item, ++accitem) { *accitem = (*item > *accitem) ? *item : *accitem; } continue; dealloc_and_badarg: dtor(env, acc); return enif_make_badarg(env); } ERL_NIF_TERM erl_res = enif_make_resource(env, acc); enif_release_resource(acc); return erl_res; } /* * Return the total number of bytes allocated for the given hyper_carray. * Includes the header's size. */ static ERL_NIF_TERM bytes(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { carray_ptr arr = NULL; HYPER_CARRAY_OR_BADARG(argv[0], arr); return enif_make_int(env, HYPER_CARRAY_SIZE + arr->size); } /* * Sum over 2^-X where X is the value of each item in the given hyper_carray. */ static ERL_NIF_TERM register_sum(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { carray_ptr arr = NULL; HYPER_CARRAY_OR_BADARG(argv[0], arr); int currval = 0; double sum = 0.0; unsigned int size = arr->size; for (int i = 0; i < size; ++i) { currval = arr->items[i]; sum += 1.0 / (double) (0x01 << currval); } return enif_make_double(env, sum); } /* * Number of items with a 0 as value; */ static ERL_NIF_TERM zero_count(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { carray_ptr arr = NULL; HYPER_CARRAY_OR_BADARG(argv[0], arr); unsigned int nzeros = 0; unsigned int size = arr->size; for (int i = 0; i < size; ++i) { if (arr->items[i] == 0) ++nzeros; } return enif_make_int(env, nzeros); } /* * Encode the given hyper_carray as an Erlang binary. */ static ERL_NIF_TERM encode_registers(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { carray_ptr arr = NULL; HYPER_CARRAY_OR_BADARG(argv[0], arr); size_t nbytes = arr->size; ERL_NIF_TERM bin; unsigned char *buf = enif_make_new_binary(env, nbytes, &bin); memcpy(buf, arr->items, nbytes); return bin; } /* * Decode the given serialized hyper_carray into a new resource. */ static ERL_NIF_TERM decode_registers(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) { unsigned int precision = 0; ErlNifBinary bin; if (!enif_get_uint(env, argv[1], &precision) || !enif_inspect_binary(env, argv[0], &bin)) return enif_make_badarg(env); carray_ptr arr = NULL; carray_alloc(precision, &arr); memcpy(arr->items, bin.data, arr->size); ERL_NIF_TERM erl_res = enif_make_resource(env, arr); enif_release_resource(arr); return erl_res; } /* * Map of funs to NIFs. */ static ErlNifFunc niftable[] = { {"new", 1, new_hyper_carray}, {"set", 3, set}, {"max_merge", 1, max_merge}, {"bytes", 1, bytes}, {"register_sum", 1, register_sum}, {"zero_count", 1, zero_count}, {"encode_registers", 1, encode_registers}, {"decode_registers", 2, decode_registers} }; /* * Destructor for hyper_carray resources. */ void dtor(ErlNifEnv * env, void *obj) { enif_release_resource(obj); } /* * Creates or opens the hyper_carray resource _type_. * Registers dtor to be called on garbage collection of hyper_carrays. * Please see http://www.erlang.org/doc/man/erl_nif.html. */ static int load(ErlNifEnv * env, void **priv_data, ERL_NIF_TERM load_info) { carray_resource = enif_open_resource_type(env, NULL, "hyper_carray", &dtor, ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER, 0); return 0; } /* * Called when the NIF library is loaded and there is old code of this module * with a loaded NIF library. */ static int upgrade(ErlNifEnv * env, void **priv, void **old_priv, ERL_NIF_TERM load_info) { *priv = *old_priv; return 0; } /* * Initialize the NIF library. */ ERL_NIF_INIT(hyper_carray, niftable, &load, NULL, &upgrade, NULL);