Current section
Files
Jump to
Current section
Files
src/libjq.c
#include <assert.h>
#include <erl_nif.h>
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <jq.h>
struct jq_resource {
jq_state * jq;
};
ErlNifResourceType * jq_resource_type;
void free_jq_resource(ErlNifEnv * env, void * obj) {
struct jq_resource * jq_resource = (struct jq_resource *) obj;
free(jq_resource->jq);
jq_resource->jq = NULL;
}
int open_jq_resource_type(ErlNifEnv * env) {
ErlNifResourceFlags tried;
jq_resource_type = enif_open_resource_type(env, NULL, "jq", free_jq_resource, ERL_NIF_RT_CREATE, &tried);
return jq_resource_type != NULL;
}
ERL_NIF_TERM make_jq_resource(ErlNifEnv * env, jq_state * jq) {
struct jq_resource * jq_resource = enif_alloc_resource(jq_resource_type, sizeof(struct jq_resource));
jq_resource->jq = jq;
return enif_make_resource(env, jq_resource);
}
int get_jq_resource(ErlNifEnv * env, ERL_NIF_TERM arg, jq_state ** jq) {
struct jq_resource * jq_resource;
if (!enif_get_resource(env, arg, jq_resource_type, (void **) &jq_resource)) {
return 0;
}
*jq = jq_resource->jq;
return 1;
}
ERL_NIF_TERM ok_atom;
ERL_NIF_TERM error_atom;
ERL_NIF_TERM nil_atom;
ERL_NIF_TERM false_atom;
ERL_NIF_TERM true_atom;
void init_atoms(ErlNifEnv * env) {
ok_atom = enif_make_atom(env, "ok");
error_atom = enif_make_atom(env, "error");
nil_atom = enif_make_atom(env, "nil");
false_atom = enif_make_atom(env, "false");
true_atom = enif_make_atom(env, "true");
}
int term_to_jv(ErlNifEnv * env, ERL_NIF_TERM term, jv * result) {
long int integer;
double number;
ErlNifBinary string;
if (term == nil_atom) {
*result = jv_null();
return 1;
}
if (term == false_atom) {
*result = jv_false();
return 1;
}
if (term == true_atom) {
*result = jv_true();
return 1;
}
if (enif_is_atom(env, term)) {
unsigned int size;
assert(enif_get_atom_length(env, term, &size, ERL_NIF_UTF8));
char * data = malloc(size + 1);
assert(enif_get_atom(env, term, data, size + 1, ERL_NIF_UTF8));
*result = jv_string(data);
free(data);
return 1;
}
if (enif_get_int64(env, term, &integer)) {
*result = jv_number(integer);
return 1;
}
if (enif_get_double(env, term, &number)) {
*result = jv_number(number);
return 1;
}
if (enif_inspect_binary(env, term, &string)) {
char * data = malloc(string.size + 1);
memcpy(data, string.data, string.size);
data[string.size] = 0;
*result = jv_string(data);
free(data);
return 1;
}
if (enif_is_list(env, term)) {
unsigned int length;
if (!enif_get_list_length(env, term, &length)) {
return 0;
}
*result = jv_array_sized(length);
ERL_NIF_TERM head, tail = term;
for (int i = 0; enif_get_list_cell(env, tail, &head, &tail); i++) {
jv element;
if (!term_to_jv(env, head, &element)) {
return 0;
}
*result = jv_array_set(*result, i, element);
}
return 1;
}
if (enif_is_map(env, term)) {
*result = jv_object();
ErlNifMapIterator iter;
assert(enif_map_iterator_create(env, term, &iter, ERL_NIF_MAP_ITERATOR_FIRST));
ERL_NIF_TERM key_term, value_term;
jv key_jv, value_jv;
while (enif_map_iterator_get_pair(env, &iter, &key_term, &value_term)) {
if (!term_to_jv(env, key_term, &key_jv) ||
!term_to_jv(env, value_term, &value_jv)) {
enif_map_iterator_destroy(env, &iter);
return 0;
}
*result = jv_object_set(*result, key_jv, value_jv);
enif_map_iterator_next(env, &iter);
}
enif_map_iterator_destroy(env, &iter);
return 1;
}
return 0;
}
int jv_to_term(ErlNifEnv * env, jv value, ERL_NIF_TERM * term) {
int size;
int length;
switch (jv_get_kind(jv_copy(value))) {
case JV_KIND_NULL:
*term = nil_atom;
return 1;
case JV_KIND_FALSE:
*term = false_atom;
return 1;
case JV_KIND_TRUE:
*term = true_atom;
return 1;
case JV_KIND_NUMBER:
if (jv_is_integer(jv_copy(value))) {
*term = enif_make_int64(env, jv_number_value(value));
} else {
*term = enif_make_double(env, jv_number_value(value));
}
return 1;
case JV_KIND_STRING:
size = jv_string_length_bytes(jv_copy(value));
unsigned char * data = enif_make_new_binary(env, size, term);
memcpy(data, jv_string_value(value), size);
return 1;
case JV_KIND_ARRAY:
length = jv_array_length(jv_copy(value));
*term = enif_make_list(env, 0);
for (int i = length - 1; i >= 0; i--) {
jv element_jv = jv_array_get(jv_copy(value), i);
ERL_NIF_TERM element;
if (!jv_to_term(env, element_jv, &element)) {
return 0;
}
*term = enif_make_list_cell(env, element, *term);
}
return 1;
case JV_KIND_OBJECT:
*term = enif_make_new_map(env);
ERL_NIF_TERM key_term, value_term;
jv_object_foreach(value, key_jv, value_jv) {
if (!jv_to_term(env, key_jv, &key_term) ||
!jv_to_term(env, value_jv, &value_term) ||
!enif_make_map_put(env, *term, key_term, value_term, term)) {
return 0;
}
};
return 1;
case JV_KIND_INVALID:
default:
return 0;
}
}
static ERL_NIF_TERM encode_nif(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) {
jv value;
if (argc != 1 ||
!term_to_jv(env, argv[0], &value)) {
return enif_make_badarg(env);
}
int flags = 0;
jv result = jv_dump_string(value, flags);
ERL_NIF_TERM result_term;
if (!jv_to_term(env, result, &result_term)) {
return enif_make_badarg(env);
}
return result_term;
}
static ERL_NIF_TERM decode_nif(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifBinary string;
if (argc != 1 ||
!enif_inspect_binary(env, argv[0], &string)) {
return enif_make_badarg(env);
}
jv result = jv_parse_sized((char *) string.data, string.size);
ERL_NIF_TERM result_term;
if (!jv_to_term(env, result, &result_term)) {
return enif_make_badarg(env);
}
return result_term;
}
static ERL_NIF_TERM compile_nif(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) {
ErlNifBinary program_string;
if (argc != 1 ||
!enif_inspect_binary(env, argv[0], &program_string)) {
return enif_make_badarg(env);
}
char * program = malloc(program_string.size + 1);
memcpy(program, program_string.data, program_string.size);
program[program_string.size] = 0;
jq_state * jq = jq_init();
if (!jq_compile(jq, program)) {
free(program);
return enif_make_badarg(env);
}
free(program);
return make_jq_resource(env, jq);
}
static ERL_NIF_TERM run_nif(ErlNifEnv * env, int argc, const ERL_NIF_TERM argv[]) {
jq_state * jq;
ErlNifBinary input_string;
if (argc != 2 ||
!get_jq_resource(env, argv[0], &jq) ||
!enif_inspect_binary(env, argv[1], &input_string)) {
return enif_make_badarg(env);
}
jv input = jv_parse_sized((char *) input_string.data, input_string.size);
if (!jv_is_valid(input)) {
return enif_make_badarg(env);
}
int flags = 0;
jq_start(jq, input, flags);
ERL_NIF_TERM result_list = enif_make_list(env, 0);
jv value;
while (jv_is_valid(value = jq_next(jq))) {
ERL_NIF_TERM value_term;
if (!jv_to_term(env, value, &value_term)) {
return enif_make_badarg(env);
}
result_list = enif_make_list_cell(env, value_term, result_list);
}
assert(enif_make_reverse_list(env, result_list, &result_list));
return result_list;
}
static ErlNifFunc nif_funcs[] = {
{"encode!", 1, encode_nif},
{"decode!", 1, decode_nif},
{"compile", 1, compile_nif},
{"run", 2, run_nif},
};
int load(ErlNifEnv * env, void ** priv_data, ERL_NIF_TERM load_info) {
init_atoms(env);
assert(open_jq_resource_type(env));
return 0;
}
ERL_NIF_INIT(Elixir.Libjq, nif_funcs, load, NULL, NULL, NULL)