Current section
Files
Jump to
Current section
Files
c_src/nif_utils.cpp
//
// Created by Cocoa on 14/06/2022.
//
#include "nif_utils.hpp"
namespace erlang {
namespace nif {
// Atoms
int get_atom(ErlNifEnv *env, ERL_NIF_TERM term, std::string &var) {
unsigned atom_length;
if (!enif_get_atom_length(env, term, &atom_length, ERL_NIF_LATIN1)) {
return 0;
}
var.resize(atom_length + 1);
if (!enif_get_atom(env, term, &(*(var.begin())), var.size(), ERL_NIF_LATIN1)) {
return 0;
}
var.resize(atom_length);
return 1;
}
ERL_NIF_TERM atom(ErlNifEnv *env, const char *msg) {
ERL_NIF_TERM a;
if (enif_make_existing_atom(env, msg, &a, ERL_NIF_LATIN1)) {
return a;
} else {
return enif_make_atom(env, msg);
}
}
// Helper for returning `{:error, msg}` from NIF.
ERL_NIF_TERM error(ErlNifEnv *env, const char *msg) {
ERL_NIF_TERM error_atom = atom(env, "error");
ERL_NIF_TERM reason;
unsigned char *ptr;
size_t len = strlen(msg);
if ((ptr = enif_make_new_binary(env, len, &reason)) != nullptr) {
strcpy((char *) ptr, msg);
return enif_make_tuple2(env, error_atom, reason);
} else {
ERL_NIF_TERM msg_term = enif_make_string(env, msg, ERL_NIF_LATIN1);
return enif_make_tuple2(env, error_atom, msg_term);
}
}
// Helper for returning `{:ok, term}` from NIF.
ERL_NIF_TERM ok(ErlNifEnv *env) {
return atom(env, "ok");
}
// Helper for returning `:ok` from NIF.
ERL_NIF_TERM ok(ErlNifEnv *env, ERL_NIF_TERM term) {
return enif_make_tuple2(env, ok(env), term);
}
// Boolean type
int get(ErlNifEnv *env, ERL_NIF_TERM term, bool *var) {
std::string b;
if (get_atom(env, term, b)) {
if (b == "true") {
*var = true;
return 1;
} else if (b == "false") {
*var = false;
return 1;
} else {
return 0;
}
} else {
return 0;
}
}
// Numeric types
int get(ErlNifEnv *env, ERL_NIF_TERM term, int *var) {
return enif_get_int(env, term, var);
}
int get(ErlNifEnv *env, ERL_NIF_TERM term, unsigned int *var) {
return enif_get_uint(env, term, var);
}
int get(ErlNifEnv *env, ERL_NIF_TERM term, long long *var) {
return enif_get_int64(env, term, reinterpret_cast<ErlNifSInt64 *>(var));
}
int get(ErlNifEnv *env, ERL_NIF_TERM term, unsigned long long *var) {
return enif_get_uint64(env, term, reinterpret_cast<ErlNifUInt64 *>(var));
}
int get(ErlNifEnv *env, ERL_NIF_TERM term, long *var) {
return enif_get_int64(env, term, reinterpret_cast<ErlNifSInt64 *>(var));
}
int get(ErlNifEnv *env, ERL_NIF_TERM term, unsigned long *var) {
return enif_get_uint64(env, term, reinterpret_cast<ErlNifUInt64 *>(var));
}
int get(ErlNifEnv *env, ERL_NIF_TERM term, double *var) {
return enif_get_double(env, term, var);
}
// Standard types
int get(ErlNifEnv *env, ERL_NIF_TERM term, std::string &var) {
unsigned len;
int ret = enif_get_list_length(env, term, &len);
if (!ret) {
ErlNifBinary bin;
ret = enif_inspect_binary(env, term, &bin);
if (!ret) {
return 0;
}
var = std::string((const char *) bin.data, bin.size);
return ret;
}
var.resize(len + 1);
ret = enif_get_string(env, term, &*(var.begin()), var.size(), ERL_NIF_LATIN1);
if (ret > 0) {
var.resize(ret - 1);
} else if (ret == 0) {
var.resize(0);
} else {
}
return ret;
}
ERL_NIF_TERM make(ErlNifEnv *env, bool var) {
if (var) {
return atom(env, "true");
} else {
return atom(env, "false");
}
}
ERL_NIF_TERM make(ErlNifEnv *env, long var) {
return enif_make_int64(env, var);
}
ERL_NIF_TERM make(ErlNifEnv *env, int32_t var) {
return enif_make_int(env, var);
}
ERL_NIF_TERM make(ErlNifEnv *env, long long var) {
return enif_make_int64(env, var);
}
ERL_NIF_TERM make(ErlNifEnv *env, uint32_t var) {
return enif_make_uint(env, var);
}
ERL_NIF_TERM make(ErlNifEnv *env, unsigned long long var) {
return enif_make_uint64(env, var);
}
ERL_NIF_TERM make(ErlNifEnv *env, double var) {
return enif_make_double(env, var);
}
ERL_NIF_TERM make(ErlNifEnv *env, ErlNifBinary var) {
return enif_make_binary(env, &var);
}
ERL_NIF_TERM make(ErlNifEnv *env, const std::string& var) {
return enif_make_string(env, var.c_str(), ERL_NIF_LATIN1);
}
ERL_NIF_TERM make(ErlNifEnv *env, const char *string) {
return enif_make_string(env, string, ERL_NIF_LATIN1);
}
int make(ErlNifEnv *env, bool var, ERL_NIF_TERM &out) {
out = make(env, var);
return 0;
}
int make(ErlNifEnv *env, long var, ERL_NIF_TERM &out) {
out = make(env, var);
return 0;
}
int make(ErlNifEnv *env, int var, ERL_NIF_TERM &out) {
out = make(env, var);
return 0;
}
int make(ErlNifEnv *env, double var, ERL_NIF_TERM &out) {
out = make(env, var);
return 0;
}
int make(ErlNifEnv *env, ErlNifBinary var, ERL_NIF_TERM &out) {
out = make(env, var);
return 0;
}
int make(ErlNifEnv *env, const std::string& var, ERL_NIF_TERM &out) {
out = make_binary(env, var);
return 0;
}
int make(ErlNifEnv *env, const char *var, ERL_NIF_TERM &out) {
out = make_binary(env, var);
return 0;
}
int make(ErlNifEnv *env, const std::vector<uint8_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
uint8_t * data = (uint8_t *)array.data();
return make_u32_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<uint16_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
uint16_t * data = (uint16_t *)array.data();
return make_u32_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<uint32_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
uint32_t * data = (uint32_t *)array.data();
return make_u32_list_from_c_array(env, count, data, out);
}
#ifndef OS_WIN
int make(ErlNifEnv *env, const std::vector<unsigned long long>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
uint64_t * data = (uint64_t *)array.data();
return make_u64_list_from_c_array(env, count, data, out);
}
#endif
int make(ErlNifEnv *env, const std::vector<int8_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
int8_t * data = (int8_t *)array.data();
return make_i32_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<int16_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
int16_t * data = (int16_t *)array.data();
return make_i32_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<int32_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
int32_t * data = (int32_t *)array.data();
return make_i32_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<long long>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
int64_t * data = (int64_t *)array.data();
return make_i64_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<size_t>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
if (sizeof(size_t) == 8) {
uint64_t * data = (uint64_t *)array.data();
return make_u64_list_from_c_array(env, count, data, out);
} else if (sizeof(size_t) == 4) {
uint32_t * data = (uint32_t *)array.data();
return make_u32_list_from_c_array(env, count, data, out);
} else {
// error
return 1;
}
}
int make(ErlNifEnv *env, const std::vector<float>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
float * data = (float *)array.data();
return make_f64_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<double>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
double * data = (double *)array.data();
return make_f64_list_from_c_array(env, count, data, out);
}
int make(ErlNifEnv *env, const std::vector<std::string>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
if (count == 0) {
out = enif_make_list_from_array(env, nullptr, 0);
return 0;
}
ERL_NIF_TERM *terms = (ERL_NIF_TERM *)enif_alloc(sizeof(ERL_NIF_TERM) * count);
if (terms == nullptr) {
return 1;
}
for (size_t i = 0; i < count; ++i) {
terms[i] = make_binary(env, array[i]);
}
out = enif_make_list_from_array(env, terms, (unsigned)count);
enif_free(terms);
return 0;
}
int make(ErlNifEnv *env, const std::vector<const std::string*>& array, ERL_NIF_TERM &out) {
size_t count = array.size();
if (count == 0) {
out = enif_make_list_from_array(env, nullptr, 0);
return 0;
}
ERL_NIF_TERM *terms = (ERL_NIF_TERM *)enif_alloc(sizeof(ERL_NIF_TERM) * count);
if (terms == nullptr) {
return 1;
}
for (size_t i = 0; i < count; ++i) {
terms[i] = make_binary(env, *array[i]);
}
out = enif_make_list_from_array(env, terms, (unsigned)count);
enif_free(terms);
return 0;
}
ERL_NIF_TERM make_binary(ErlNifEnv *env, const char *c_string) {
ERL_NIF_TERM binary_str;
unsigned char *ptr;
size_t len = strlen(c_string);
if ((ptr = enif_make_new_binary(env, len, &binary_str)) != nullptr) {
memcpy((char *)ptr, c_string, len);
return binary_str;
} else {
fprintf(stderr, "internal error: cannot allocate memory for binary string\r\n");
return atom(env, "error");
}
}
ERL_NIF_TERM make_binary(ErlNifEnv *env, const std::string& string) {
ERL_NIF_TERM binary_str;
unsigned char *ptr;
size_t len = string.size();
if ((ptr = enif_make_new_binary(env, len, &binary_str)) != nullptr) {
memcpy((char *)ptr, string.c_str(), len);
return binary_str;
} else {
fprintf(stderr, "internal error: cannot allocate memory for binary string\r\n");
return atom(env, "error");
}
}
// Check if :nil
int check_nil(ErlNifEnv *env, ERL_NIF_TERM term) {
std::string atom_str;
if (get_atom(env, term, atom_str) && atom_str == "nil") {
return true;
}
return false;
}
// Containers
int get_tuple(ErlNifEnv *env, ERL_NIF_TERM tuple, std::vector<int64_t> &var) {
const ERL_NIF_TERM *terms;
int length;
if (!enif_get_tuple(env, tuple, &length, &terms)) {
return 0;
}
var.reserve(length);
for (int i = 0; i < length; i++) {
int data;
if (!get(env, terms[i], &data)) {
return 0;
}
var.push_back(data);
}
return 1;
}
int get_list(ErlNifEnv *env, ERL_NIF_TERM list, std::vector<ErlNifBinary> &var) {
unsigned int length;
if (!enif_get_list_length(env, list, &length)) {
return 0;
}
var.reserve(length);
ERL_NIF_TERM head, tail;
while (enif_get_list_cell(env, list, &head, &tail)) {
ErlNifBinary elem;
if (!enif_inspect_binary(env, head, &elem)) {
return 0;
}
var.push_back(elem);
list = tail;
}
return 1;
}
int get_list(ErlNifEnv *env, ERL_NIF_TERM list, std::vector<std::string> &var) {
unsigned int length;
if (!enif_get_list_length(env, list, &length)) {
return 0;
}
var.reserve(length);
ERL_NIF_TERM head, tail;
while (enif_get_list_cell(env, list, &head, &tail)) {
std::string elem;
if (!get_atom(env, head, elem)) {
return 0;
}
var.push_back(elem);
list = tail;
}
return 1;
}
int get_list(ErlNifEnv *env, ERL_NIF_TERM list, std::vector<int> &var) {
unsigned int length;
if (!enif_get_list_length(env, list, &length)) {
return 0;
}
var.reserve(length);
ERL_NIF_TERM head, tail;
while (enif_get_list_cell(env, list, &head, &tail)) {
int elem;
if (!get(env, head, &elem)) {
return 0;
}
var.push_back(elem);
list = tail;
}
return 1;
}
int get_list(ErlNifEnv *env, ERL_NIF_TERM list, std::vector<int64_t> &var) {
unsigned int length;
if (!enif_get_list_length(env, list, &length)) {
return 0;
}
var.reserve(length);
ERL_NIF_TERM head, tail;
while (enif_get_list_cell(env, list, &head, &tail)) {
int64_t elem;
if (!get(env, head, &elem)) {
return 0;
}
var.push_back(elem);
list = tail;
}
return 1;
}
int get_list(ErlNifEnv *env, ERL_NIF_TERM list, std::vector<uint64_t> &var) {
unsigned int length;
if (!enif_get_list_length(env, list, &length)) {
return 0;
}
var.reserve(length);
ERL_NIF_TERM head, tail;
while (enif_get_list_cell(env, list, &head, &tail)) {
uint64_t elem;
if (!get(env, head, &elem)) {
return 0;
}
var.push_back(elem);
list = tail;
}
return 1;
}
}
}