├── c_src ├── .gitignore ├── Makefile ├── nifty.c └── nifty_clangparse.c ├── priv ├── .gitignore └── templates │ ├── lib │ ├── void.tpl │ ├── builtin_type.tpl │ ├── float_type.tpl │ ├── array_type.tpl │ ├── pointer_type.tpl │ ├── struct_type.tpl │ ├── union_type.tpl │ ├── int_type.tpl │ ├── function.tpl │ └── structures.tpl │ ├── app.tpl │ ├── config.tpl │ ├── hrlmodule.tpl │ ├── save_emodule.tpl │ ├── cmodule.tpl │ └── emodule.tpl ├── test ├── cfiles │ ├── .gitignore │ ├── answer.c │ ├── dereference_regression.c │ ├── proxy_header.c │ ├── types.h │ ├── proxy_header.h │ ├── array.h │ ├── answer.h │ ├── unions.c │ ├── dereference_regression.h │ ├── arguments.h │ ├── fptr.h │ ├── arguments.c │ ├── array.c │ ├── unions.h │ ├── enums.h │ ├── structs.h │ ├── builtin_types.h │ └── builtin_types.c ├── nifty_lib_test.erl └── nifty_test.erl ├── .gitignore ├── travis ├── after_failure.sh ├── safe-cflags.sh ├── fix_exports.sh └── install_deps.sh ├── src ├── nifty.app.src ├── nifty_tags.erl ├── nifty_types.erl ├── nifty_remotecall.erl ├── nifty_remote.erl ├── nifty_filters.erl ├── nifty_utils.erl ├── nifty_clangparse.erl └── nifty.erl ├── include └── nifty.hrl ├── .travis.yml ├── LICENSE ├── rebar.config ├── Makefile └── README.md /c_src/.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | -------------------------------------------------------------------------------- /priv/.gitignore: -------------------------------------------------------------------------------- 1 | nifty*.so 2 | -------------------------------------------------------------------------------- /test/cfiles/.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | -------------------------------------------------------------------------------- /test/cfiles/answer.c: -------------------------------------------------------------------------------- 1 | #include "answer.h" 2 | 3 | my_t 4 | life_universe_and_everything() { 5 | return 42; 6 | } 7 | -------------------------------------------------------------------------------- /test/cfiles/dereference_regression.c: -------------------------------------------------------------------------------- 1 | #include "dereference_regression.h" 2 | 3 | int f(struct s **pp) {return 0;}; 4 | -------------------------------------------------------------------------------- /test/cfiles/proxy_header.c: -------------------------------------------------------------------------------- 1 | #include "proxy_header.h" 2 | 3 | void* 4 | fproxy(struct s1* arg) { 5 | return (void*)arg; 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | .nifty_plt 3 | .rebar 4 | cache/ 5 | deps 6 | doc/ 7 | *.o 8 | *.beam 9 | *.plt 10 | rebar3 11 | rebar.lock 12 | _build/ 13 | -------------------------------------------------------------------------------- /test/cfiles/types.h: -------------------------------------------------------------------------------- 1 | #ifndef _NIFTY_TUTORIAL_TYPES_H 2 | #define _NIFTY_TUTORIAL_TYPES_H 3 | 4 | typedef short my_t; 5 | 6 | #endif /* _NIFTY_TUTORIAL_ANSWER_H_ */ 7 | -------------------------------------------------------------------------------- /test/cfiles/proxy_header.h: -------------------------------------------------------------------------------- 1 | #ifndef NIFTY_PROXY_TEST 2 | #define NIFTY_PROXY_TEST 3 | 4 | #include "structs.h" 5 | #include "arguments.h" 6 | 7 | extern void* fproxy(struct s1* arg); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /test/cfiles/array.h: -------------------------------------------------------------------------------- 1 | extern int sumarray(int arr[10]); 2 | 3 | struct array_st { 4 | char start[10]; 5 | int val; 6 | char end[10]; 7 | }; 8 | 9 | extern int sumstruct_array(struct array_st* val); 10 | 11 | -------------------------------------------------------------------------------- /test/cfiles/answer.h: -------------------------------------------------------------------------------- 1 | #ifndef _NIFTY_TUTORIAL_ANSWER_H_ 2 | #define _NIFTY_TUTORIAL_ANSWER_H_ 3 | 4 | #include "types.h" 5 | 6 | extern my_t life_universe_and_everything(); 7 | 8 | #endif /* _NIFTY_TUTORIAL_ANSWER_H_ */ 9 | -------------------------------------------------------------------------------- /priv/templates/lib/void.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_return %} 3 | ERL_NIF_TERM retval; 4 | {% endif %} 5 | {% endif %} 6 | 7 | {% if phase=="to_erl"%} 8 | {{erlarg}} = enif_make_atom(env, "ok"); 9 | {% endif %} 10 | -------------------------------------------------------------------------------- /test/cfiles/unions.c: -------------------------------------------------------------------------------- 1 | #include "unions.h" 2 | 3 | int 4 | check_i(union_t u) { 5 | return u.i; 6 | } 7 | 8 | float 9 | check_f(union_t u) { 10 | return u.f; 11 | } 12 | 13 | float 14 | check_sf(complex s) { 15 | return s.u.f; 16 | } 17 | -------------------------------------------------------------------------------- /test/cfiles/dereference_regression.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEREFERENCE_REGRESSION_H_ 2 | #define _DEREFERENCE_REGRESSION_H_ 3 | 4 | 5 | typedef struct s { int i; int j;} s; 6 | 7 | extern int f(struct s **); 8 | 9 | #endif /* _DEREFERENCE_REGRESSION_H_ */ 10 | -------------------------------------------------------------------------------- /test/cfiles/arguments.h: -------------------------------------------------------------------------------- 1 | #ifndef NIFTY_ARGUMENTS_TEST 2 | #define NIFTY_ARGUMENTS_TEST 3 | 4 | extern void f1(void); 5 | extern void f2(); 6 | extern int f3(int,int,int,int); 7 | extern int f4(int arg1, int arg2, int arg3, int arg4); 8 | 9 | #endif /* NIFTY_ARGUMENTS_TEST */ 10 | -------------------------------------------------------------------------------- /priv/templates/app.tpl: -------------------------------------------------------------------------------- 1 | { 2 | application, 3 | {{module}}, 4 | [ 5 | {vsn, 6 | "1.0"}, 7 | {description, 8 | "Nifty interface of {{module}}"}, 9 | {modules, [ 10 | {{module}}, 11 | {{module}}_remote]}, 12 | {applications,[kernel,stdlib,compile]} 13 | ] 14 | }. 15 | -------------------------------------------------------------------------------- /travis/after_failure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo $LLVM_DIR 3 | which llvm-config 4 | echo "LDFLAGS:" 5 | llvm-config --ldflags 6 | echo "CFLAGS:" 7 | travis/safe-cflags.sh llvm-config 8 | echo "GLIBCXX" 9 | strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBCXX 10 | echo "env" 11 | env 12 | -------------------------------------------------------------------------------- /priv/templates/config.tpl: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, []}. 3 | 4 | {plugins, [{ pc, {git, "https://github.com/blt/port_compiler.git", {branch, "master"}}}]}. 5 | 6 | {provider_hooks, 7 | [{pre,[{compile, {pc, compile}}, 8 | {clean, {pc, clean}}]}]}. 9 | 10 | {% for E in config %} 11 | {{E}}. 12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /test/cfiles/fptr.h: -------------------------------------------------------------------------------- 1 | extern void f1(int a, int b); 2 | 3 | extern int (*(*f2(int a, int aa))(int b, int bb))(int c, int cc); 4 | 5 | extern int (*f3(int a, int z))(int (*b)(int c, int d), int g); 6 | 7 | extern void f4(char g, int (*a)(int x, int z)); 8 | 9 | #include 10 | 11 | struct s_fptr { 12 | void (*field1)(va_list mylist); 13 | }; 14 | -------------------------------------------------------------------------------- /test/cfiles/arguments.c: -------------------------------------------------------------------------------- 1 | #include "arguments.h" 2 | 3 | static int i; 4 | 5 | void 6 | f1(void) 7 | { 8 | i = 0; 9 | } 10 | 11 | void f2() 12 | { 13 | i++; 14 | } 15 | 16 | int 17 | f3(int a,int b,int c,int d) 18 | { 19 | return a+b+c+d+i; 20 | } 21 | 22 | int 23 | f4(int arg1, int arg2, int arg3, int arg4) 24 | { 25 | return arg1+arg2+arg3+arg4; 26 | } 27 | -------------------------------------------------------------------------------- /test/cfiles/array.c: -------------------------------------------------------------------------------- 1 | #include "array.h" 2 | 3 | int 4 | sumarray(int arr[10]) { 5 | int i, sum=0; 6 | for (i=0; i<10; i++) { 7 | sum += arr[i]; 8 | } 9 | return sum; 10 | } 11 | 12 | int 13 | sumstruct_array(struct array_st* val) { 14 | int i, sum=0; 15 | for (i=0; i<10; i++) { 16 | sum += (val->start)[i] + (val->end)[i]; 17 | } 18 | return sum; 19 | } 20 | -------------------------------------------------------------------------------- /test/cfiles/unions.h: -------------------------------------------------------------------------------- 1 | #ifndef NIFTY_UNIONS_TEST 2 | #define NIFTY_UNIONS_TEST 3 | 4 | typedef union _u { 5 | int i; 6 | float f; 7 | } union_t; 8 | 9 | typedef struct _s { 10 | union_t u; 11 | int i; 12 | } complex; 13 | 14 | extern int check_i(union_t u); 15 | extern float check_f(union_t s); 16 | extern float check_sf(complex s); 17 | 18 | #endif /* NIFTY_UNIONS_TEST */ 19 | -------------------------------------------------------------------------------- /test/cfiles/enums.h: -------------------------------------------------------------------------------- 1 | #ifndef NIFTY_ENUMS_TEST 2 | #define NIFTY_ENUMS_TEST 3 | 4 | enum enum0 { 5 | THE_VALUE 6 | }; 7 | 8 | typedef enum enum1 { 9 | VALUE1, 10 | VALUE2, 11 | VALUE3 12 | } enum1_t; 13 | 14 | enum enum2 { 15 | VALUE4 = 4, 16 | VALUE5, 17 | VALUE6 = 100 18 | }; 19 | 20 | enum1_t f1(enum enum0 par1, enum enum2 par2) { 21 | return VALUE2; 22 | } 23 | 24 | #endif /* NIFTY_ENUMS_TEST */ 25 | -------------------------------------------------------------------------------- /travis/safe-cflags.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | UNSAFE_CFLAGS=`$1 --cflags` 3 | FLAGS_LIST=$(echo $UNSAFE_CFLAGS | tr " " "\n" ) 4 | SAFE_FLAGS="" 5 | for FLAG in $FLAGS_LIST 6 | do 7 | case $FLAG in 8 | "-Wcovered-switch-default") 9 | ;; 10 | "-Werror=date-time") 11 | ;; 12 | "-Wdelete-non-virtual-dtor") 13 | ;; 14 | "-Wstring-conversion") 15 | ;; 16 | "-Werror=unguarded-availability-new") 17 | ;; 18 | -D*) 19 | ;; 20 | ?*) 21 | SAFE_FLAGS="$SAFE_FLAGS $FLAG" 22 | ;; 23 | esac 24 | done 25 | echo $SAFE_FLAGS 26 | -------------------------------------------------------------------------------- /src/nifty.app.src: -------------------------------------------------------------------------------- 1 | { 2 | application, 3 | nifty, 4 | [ 5 | {description, 6 | "NIF interface generator"}, 7 | {vsn, 8 | "0.9"}, 9 | {modules, [ 10 | nifty, 11 | nifty_clangparse, 12 | nifty_filters, 13 | nifty_rebar, 14 | nifty_remote, 15 | nifty_remotecall, 16 | nifty_tags, 17 | nifty_types, 18 | nifty_utils]}, 19 | {applications,[kernel,stdlib]}, 20 | {maintainers, ["Andreas Loscher"]}, 21 | {licenses, ["BSD-2-Clause"]}, 22 | {links, [{"Github", "https://github.com/parapluu/nifty"}, {"Website", "http://parapluu.github.io/nifty/"}]} 23 | ] 24 | }. 25 | -------------------------------------------------------------------------------- /test/cfiles/structs.h: -------------------------------------------------------------------------------- 1 | #ifndef NIFTY_STRUCTS_TEST 2 | #define NIFTY_STRUCTS_TEST 3 | 4 | struct s1 { 5 | int f1; 6 | char f2; 7 | char* f3; 8 | char f4[10]; 9 | }; 10 | 11 | struct s2 { 12 | struct s1 f1; 13 | struct s1 *f2; 14 | }; 15 | 16 | 17 | struct s3 { 18 | struct s2 f1; 19 | struct s1 f2; 20 | }; 21 | 22 | struct s4 { 23 | float myfloat; 24 | short int myint; 25 | }; 26 | 27 | typedef struct s4 t4; 28 | 29 | struct s5 { 30 | unsigned long long bla; 31 | }; 32 | 33 | struct s6 { 34 | struct s5* alot[10]; 35 | }; 36 | 37 | typedef struct s7 { 38 | int f1; 39 | } s7_t; 40 | 41 | struct s8 { 42 | s7_t f1; 43 | }; 44 | 45 | /* No functions */ 46 | 47 | 48 | 49 | 50 | #endif /* NIFTY_STRUCTS_TEST */ 51 | -------------------------------------------------------------------------------- /travis/fix_exports.sh: -------------------------------------------------------------------------------- 1 | if grep -q "nowarn_export_all" _build/default/lib/erlware_commons/src/ec_semver_parser.erl 2 | then 3 | echo "ec_semver_parser.erl ... ok" 4 | else 5 | echo "Suppress export_all warning in ec_semver_parser.erl" 6 | sed -i "s/-compile(export_all)\./-compile(export_all)\.\n-compile(nowarn_export_all)\./g" _build/default/lib/erlware_commons/src/ec_semver_parser.erl 7 | fi 8 | 9 | if grep -q "nowarn_export_all" _build/default/lib/relx/src/rlx_goal.erl 10 | then 11 | echo "rlx_goal.erl ... ok" 12 | else 13 | echo "Suppress export_all warning in rlx_goal.erl" 14 | sed -i "s/-compile(export_all)\./-compile(export_all)\.\n-compile(nowarn_export_all)\./g" _build/default/lib/relx/src/rlx_goal.erl 15 | fi 16 | -------------------------------------------------------------------------------- /test/cfiles/builtin_types.h: -------------------------------------------------------------------------------- 1 | #ifndef NIFTY_BUILTIN_TYPES_TEST 2 | #define NIFTY_BUILTIN_TYPES_TEST 3 | 4 | /* integer */ 5 | extern short signed int f1(short signed int arg); 6 | extern short unsigned int f2(short unsigned int arg); 7 | extern signed int f3(signed int arg); 8 | extern unsigned int f4(unsigned int arg); 9 | extern long signed int f5(long signed int arg); 10 | extern long unsigned int f6(long unsigned int arg); 11 | extern signed char f7(signed char arg); 12 | extern unsigned char f8(unsigned char arg); 13 | extern long long int f13(long long int); 14 | 15 | /* float */ 16 | extern float f9(float arg); 17 | extern double f10(double arg); 18 | 19 | /* pointer */ 20 | extern void* f11(void* arg); 21 | 22 | /* arrays */ 23 | extern char* f12(char smth[10]); 24 | 25 | #endif /* NIFTY_BUILTIN_TYPES_TEST */ 26 | -------------------------------------------------------------------------------- /include/nifty.hrl: -------------------------------------------------------------------------------- 1 | %%% ------------------------------------------------------------------- 2 | %%% Copyright (c) 2014, Andreas Löscher and 3 | %%% Konstantinos Sagonas 4 | %%% All rights reserved. 5 | %%% 6 | %%% This file is distributed under the Simplified BSD License. 7 | %%% Details can be found in the LICENSE file. 8 | %%% ------------------------------------------------------------------- 9 | 10 | -define(NEW_CONFIG, []). 11 | -define(ADD_SOURCES(S, C), nifty_utils:merge_nif_spec(C, {".*", "$NIF", S, [{env, []}]})). 12 | -define(ADD_CFLAG(F, C), nifty_utils:merge_nif_spec(C, {".*", "$NIF", [], [{env, [{"CFLAGS", "$CFLAGS "++ F}]}]})). 13 | -define(ADD_LDFLAG(F, C), nifty_utils:merge_nif_spec(C, {".*", "$NIF", [], [{env, [{"LDFLAGS", "$LDFLAGS "++ F}]}]})). 14 | -------------------------------------------------------------------------------- /priv/templates/hrlmodule.tpl: -------------------------------------------------------------------------------- 1 | %% {{module}}.hrl generated by Nifty 2 | 3 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 %}{% if kind=="struct" %} 4 | -record('struct {{constr|getNth:2}}', { 5 | {% with fields=constructors|fetch:constr %}{% for _, name, _, __ in fields %}'{{name}}'{% if not forloop.last %},{% endif %}{% endfor %}{% endwith %} 6 | }). 7 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 8 | 9 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 %}{% if kind=="union" %} 10 | -record('union {{constr|getNth:2}}', { 11 | {% with fields=constructors|fetch:constr %}{% for _, name, _, __ in fields %}'{{name}}' = undefined{% if not forloop.last %},{% endif %}{% endfor %}{% endwith %} 12 | }). 13 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 14 | -------------------------------------------------------------------------------- /priv/templates/lib/builtin_type.tpl: -------------------------------------------------------------------------------- 1 | {% with type_tuple=types|fetch:type %} 2 | {% with kind=type_tuple|getNth:1 typedef=type_tuple|getNth:2 %} 3 | {% if "int" == typedef|getNth:1 or "char" == typedef|getNth:1%} 4 | {% include "lib/int_type.tpl" %} 5 | {% else %}{% if "float" == typedef|getNth:1 or "double" == typedef|getNth:1 %} 6 | {% include "lib/float_type.tpl" %} 7 | {% else %}{% if "userdef" == kind and not ("*" in typedef|getNth:1) and not ("[" in typedef|getNth:1) and ("struct" in type) %} 8 | {% include "lib/struct_type.tpl" %} 9 | {% else %}{% if "userdef" == kind and not ("*" in typedef|getNth:1) and not ("[" in typedef|getNth:1) and ("union" in type) %} 10 | {% include "lib/union_type.tpl" %} 11 | {% else %}{% if "void" == type%} 12 | {% include "lib/void.tpl" %} 13 | {% else %}{% if typedef|getNth:1|is_array %} 14 | {% include "lib/array_type.tpl" %} 15 | {% else %} 16 | {% include "lib/pointer_type.tpl" %} 17 | {% endif %}{% endif %}{% endif %}{% endif %}{% endif %}{% endif %} 18 | {% endwith %} 19 | {% endwith %} 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | dist: xenial 3 | language: erlang 4 | script: "make all" 5 | branches: 6 | only: 7 | - master 8 | 9 | otp_release: 10 | - 22.3 11 | - 22.0 12 | - 21.3 13 | - 21.0 14 | - 20.3 15 | - 20.0 16 | env: 17 | - LLVM_VERSION=9.0.0 18 | - LLVM_VERSION=8.0.0 19 | - LLVM_VERSION=7.0.1 20 | - LLVM_VERSION=6.0.1 21 | - LLVM_VERSION=5.0.2 22 | - LLVM_VERSION=4.0.0 23 | - LLVM_VERSION=3.8.1 24 | 25 | addons: 26 | apt: 27 | sources: 28 | - ubuntu-toolchain-r-test 29 | packages: 30 | - g++-4.9 31 | - libncurses5 32 | - libncurses5-dev 33 | - autoconf 34 | - libssl-dev 35 | before_script: 36 | - ./travis/install_deps.sh 37 | - make get-deps 38 | - export LLVM_DIR=$PWD/cache/clang+llvm5-$LLVM_VERSION 39 | - export PATH=$LLVM_DIR/bin:$PATH 40 | - export LD_LIBRARY_PATH=$LLVM_DIR/lib 41 | - export NIFTY_ROOT=$PWD 42 | - clang --version # just for sanity 43 | after_failure: 44 | - ./travis/after_failure.sh 45 | cache: 46 | timeout: 5 47 | directories: 48 | - cache 49 | -------------------------------------------------------------------------------- /priv/templates/lib/float_type.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_argument%} 3 | double {{carg}}; 4 | {% endif %} 5 | 6 | {% if argument|is_return %} 7 | double c_retval; 8 | ERL_NIF_TERM retval; 9 | {% endif %} 10 | 11 | {% if argument|is_field %} 12 | {% if record=="to_record" %} 13 | ERL_NIF_TERM {{erlarg}}; 14 | {% endif %} 15 | {% if record=="to_ptr" %} 16 | double helper{{N}}; 17 | {% endif %} 18 | {% endif %} 19 | {% endif %} 20 | 21 | {% if phase=="to_c" %} 22 | {% if argument|is_field and typedef|getNth:1=="float" %} 23 | err = enif_get_double(env, {{erlarg}}, &helper{{N}}); 24 | {{carg}} = (float)helper{{N}}; 25 | {% else %} 26 | err = enif_get_double(env, {{erlarg}}, &{{carg}}); 27 | {% endif %} 28 | {% endif %} 29 | 30 | {% if phase=="argument" %} 31 | {% if argument|is_argument %} 32 | ({{raw_type|discard_restrict}}){{carg}} 33 | {% else %} 34 | ({{type}}) 35 | {% endif %} 36 | {% endif %} 37 | 38 | {% if phase=="to_erl"%} 39 | {{erlarg}} = enif_make_double(env, {{carg}}); 40 | {% endif %} 41 | 42 | 43 | {# no cleanup phase #} 44 | -------------------------------------------------------------------------------- /test/cfiles/builtin_types.c: -------------------------------------------------------------------------------- 1 | #include "builtin_types.h" 2 | 3 | /* integer */ 4 | short signed int 5 | f1(short signed int arg) 6 | { 7 | return arg; 8 | } 9 | 10 | short unsigned int 11 | f2(short unsigned int arg) 12 | { 13 | return arg; 14 | } 15 | 16 | signed int 17 | f3(signed int arg) 18 | { 19 | return arg; 20 | } 21 | 22 | unsigned int 23 | f4(unsigned int arg) 24 | { 25 | return arg; 26 | } 27 | 28 | long signed int 29 | f5(long signed int arg) 30 | { 31 | return arg; 32 | } 33 | 34 | long long int 35 | f13(long long int arg) 36 | { 37 | return arg; 38 | } 39 | 40 | long unsigned int 41 | f6(long unsigned int arg) 42 | { 43 | return arg; 44 | } 45 | 46 | signed char 47 | f7(signed char arg) 48 | { 49 | return arg; 50 | } 51 | 52 | unsigned char 53 | f8(unsigned char arg) 54 | { 55 | return arg; 56 | } 57 | 58 | /* float */ 59 | float 60 | f9(float arg) 61 | { 62 | return arg; 63 | } 64 | 65 | double 66 | f10(double arg) 67 | { 68 | return arg; 69 | } 70 | 71 | /* pointer */ 72 | void* 73 | f11(void* arg) 74 | { 75 | return arg; 76 | } 77 | 78 | /* arrays */ 79 | char* 80 | f12(char smth[10]) 81 | { 82 | return &smth; 83 | } 84 | -------------------------------------------------------------------------------- /priv/templates/lib/array_type.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_argument %} 3 | ERL_NIF_TERM *tpl{{N}}; 4 | int arity{{N}}; 5 | {{type|array_type}} {{carg}}[{{types|fetch:type|array_size}}]; 6 | ptr_t addr{{N}}; 7 | {% endif %} 8 | {% if argument|is_return %} 9 | ptr_t c_retval; 10 | ERL_NIF_TERM retval; 11 | {% endif %} 12 | {% if argument|is_field %} 13 | {% if record=="to_record" %} 14 | ERL_NIF_TERM {{erlarg}}; 15 | {% endif %} 16 | {% if record=="to_ptr" %} 17 | ERL_NIF_TERM *tpl{{N}}; 18 | int arity{{N}}; 19 | ptr_t addr{{N}}; 20 | {% endif %} 21 | {% endif %} 22 | {% endif %} 23 | 24 | {% if phase=="to_c" %} 25 | if (enif_compare({{erlarg}}, enif_make_atom(env, "null"))) { 26 | err = enif_get_tuple(env, {{erlarg}}, &arity{{N}}, (const ERL_NIF_TERM**)(&tpl{{N}})); 27 | if (err) { 28 | err = nifty_get_ptr(env, tpl{{N}}[0], &addr{{N}}); 29 | memcpy(&{{carg}}, (const void *)addr{{N}}, sizeof({{carg}})); 30 | } 31 | } 32 | {% endif %} 33 | 34 | {% if phase=="argument" %} 35 | {% if argument|is_argument %} 36 | {{carg}} 37 | {% else %} 38 | (ptr_t)& 39 | {% endif %} 40 | {% endif %} 41 | 42 | {% if phase=="to_erl" %} 43 | {{erlarg}} = enif_make_tuple2( 44 | env, 45 | nifty_make_ptr(env, (ptr_t)&{{carg}}), 46 | enif_make_string(env, "{{module}}.{{type}}", ERL_NIF_LATIN1)); 47 | {% endif %} 48 | -------------------------------------------------------------------------------- /priv/templates/lib/pointer_type.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_argument %} 3 | ERL_NIF_TERM *tpl{{N}}; 4 | int arity{{N}}; 5 | ptr_t {{carg}}; 6 | {% endif %} 7 | {% if argument|is_return %} 8 | ptr_t c_retval; 9 | ERL_NIF_TERM retval; 10 | {% endif %} 11 | {% if argument|is_field %} 12 | {% if record=="to_record" %} 13 | ERL_NIF_TERM {{erlarg}}; 14 | {% endif %} 15 | {% if record=="to_ptr" %} 16 | ERL_NIF_TERM *tpl{{N}}; 17 | int arity{{N}}; 18 | {% endif %} 19 | {% endif %} 20 | {% endif %} 21 | 22 | {% if phase=="to_c" %} 23 | if (!enif_compare({{erlarg}}, enif_make_atom(env, "null"))) { 24 | {{carg}} = 0; 25 | } else { 26 | err = enif_get_tuple(env, {{erlarg}}, &arity{{N}}, (const ERL_NIF_TERM**)(&tpl{{N}})); 27 | if (err) { 28 | if (arity{{N}}>2) { 29 | err = 0; 30 | } else { 31 | err = nifty_get_ptr(env, tpl{{N}}[0], (ptr_t*)&{{carg}}); 32 | } 33 | } 34 | } 35 | {% endif %} 36 | 37 | {% if phase=="argument" %} 38 | {% if argument|is_argument %} 39 | ({{raw_type|resolved:types|discard_restrict}}){{carg}} 40 | {% else %} 41 | (ptr_t) 42 | {% endif %} 43 | {% endif %} 44 | 45 | {% if phase=="to_erl" %} 46 | {{erlarg}} = enif_make_tuple2( 47 | env, 48 | nifty_make_ptr(env, (ptr_t){{carg}}), 49 | enif_make_string(env, "{{module}}.{{type}}", ERL_NIF_LATIN1)); 50 | {% endif %} 51 | -------------------------------------------------------------------------------- /travis/install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p cache 3 | 4 | # LLVM 5 | case $LLVM_VERSION in 6 | "3.9.1") 7 | LLVM_URL="http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-16.04.tar.xz" 8 | ;; 9 | ?*) 10 | LLVM_URL="http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz" 11 | ;; 12 | esac 13 | LLVM_DEP="cache/$LLVM_VERSION.tar.xz" 14 | LLVM_DIR="cache/clang+llvm-$LLVM_VERSION" 15 | 16 | echo $LLVM_URL 17 | echo $LLVM_DEP 18 | echo $LLVM_DIR 19 | 20 | # download and install 21 | if [ ! -f $LLVM_DEP ] ; then 22 | echo "Downloading LLVM" 23 | wget $LLVM_URL -O $LLVM_DEP 24 | if [ ! $? -eq 0 ]; then 25 | echo "Error: Download failed" 26 | rm $LLVM_DEP || true 27 | exit 1 28 | fi 29 | else 30 | echo "Found Cached Archive" 31 | fi 32 | 33 | if [ ! -d $LLVM_DIR ] ; then 34 | echo "Extracting LLVM" 35 | tar -xf $LLVM_DEP -C cache/ 36 | if [ ! $? -eq 0 ]; then 37 | echo "Error: Extraction Failed" 38 | rm $LLVM_DEP || true 39 | exit 1 40 | fi 41 | case $LLVM_VERSION in 42 | "3.9.1") 43 | mv cache/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-16.04 cache/clang+llvm-$LLVM_VERSION 44 | ;; 45 | ?*) 46 | mv cache/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04 cache/clang+llvm-$LLVM_VERSION 47 | ;; 48 | esac 49 | else 50 | echo "Found Cached Installation" 51 | fi 52 | 53 | # PLT 54 | cp cache/plt-$TRAVIS_OTP_RELEASE .nifty_plt || true 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016, Andreas Löscher 2 | and Konstantinos Sagonas 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- Erlang -*- 2 | %%============================================================================ 3 | {deps, 4 | [{rebar3, {git, "git://github.com/erlang/rebar3.git", {tag, "3.13.2"}}}, 5 | {proper, {git, "https://github.com/proper-testing/proper", {branch, "master"}}}] 6 | }. 7 | 8 | {plugins, 9 | [rebar3_hex, {rebar3_erlydtl_plugin, ".*", {git, "https://github.com/tsloughter/rebar3_erlydtl_plugin.git", {branch, "master"}}}] 10 | }. 11 | 12 | {erlydtl_opts, [{custom_tags_modules, [nifty_tags]}, 13 | {custom_filters_modules, [nifty_filters]}, 14 | {auto_escape, false}, 15 | {compiler_options, [debug_info]}, 16 | {source_ext, ".tpl"}, 17 | {recursive, false}, 18 | {force_recompile, true}, 19 | {module_ext, "_nifty_template"} 20 | ]}. 21 | 22 | {pre_hooks, 23 | [{"(linux|darwin|solaris)", compile, "make -C c_src"}, 24 | {"(freebsd)", compile, "gmake -C c_src"}]}. 25 | {post_hooks, 26 | [{"(linux|darwin|solaris)", clean, "make -C c_src clean"}, 27 | {"(freebsd)", clean, "gmake -C c_src clean"}]}. 28 | 29 | {dialyzer, [{warnings, [unmatched_returns, underspecs]}, 30 | {base_plt_location, "cache/"}, 31 | {base_plt_apps, [erts, 32 | kernel, 33 | stdlib, 34 | compiler, 35 | crypto, 36 | syntax_tools, 37 | tools]}]}. 38 | 39 | %% don't change the following line 40 | {provider_hooks, [{post, [{compile, {erlydtl, compile}}]}]}. 41 | -------------------------------------------------------------------------------- /priv/templates/lib/struct_type.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_argument %} 3 | {{raw_type}}* {{carg}}; 4 | ERL_NIF_TERM {{carg}}_ptr; 5 | ERL_NIF_TERM *{{carg}}_tpl; 6 | int {{carg}}_ar; 7 | {% endif %} 8 | {% if argument|is_return %} 9 | {{raw_type}} c_retval; 10 | ERL_NIF_TERM retval; 11 | {% endif %} 12 | {% if argument|is_field %} 13 | {% if record=="to_record" %} 14 | ERL_NIF_TERM {{erlarg}}; 15 | {% endif %} 16 | {% if record=="to_ptr" %} 17 | ERL_NIF_TERM *{{N}}_tpl; 18 | ERL_NIF_TERM {{N}}_ptr; 19 | int {{N}}_ar; 20 | {% endif %} 21 | {% endif %} 22 | {% endif %} 23 | 24 | {% if phase=="to_c" %} 25 | {% if argument|is_field %} 26 | {{N}}_ptr = record_to_erlptr_{{types|fetch:type|getNth:2|getNth:1|getNth:2}}(env, {{erlarg}}); 27 | err = enif_get_tuple(env, {{N}}_ptr, &{{N}}_ar, (const ERL_NIF_TERM**)(&{{N}}_tpl)); 28 | if (err) { 29 | err = nifty_get_ptr(env, {{N}}_tpl[0], (ptr_t*)(&{{carg}})); 30 | } 31 | {% else %} 32 | {{carg}}_ptr = record_to_erlptr_{{types|fetch:type|getNth:2|getNth:1|getNth:2}}(env, {{erlarg}}); 33 | err = enif_get_tuple(env, {{carg}}_ptr, &{{carg}}_ar, (const ERL_NIF_TERM**)(&{{carg}}_tpl)); 34 | if (err) { 35 | err = nifty_get_ptr(env, {{carg}}_tpl[0], (ptr_t*)(&{{carg}})); 36 | } 37 | {% endif %} 38 | {% endif %} 39 | 40 | {% if phase=="argument" %} 41 | {% if argument|is_argument %} 42 | ({{raw_type|discard_restrict}})(*{{carg}}) 43 | {% endif %} 44 | {% endif %} 45 | 46 | {% if phase=="to_erl"%} 47 | {{erlarg}} = ptr_to_record_{{types|fetch:type|getNth:2|getNth:1|getNth:2}}(env, (ptr_t)(&{{carg}})); 48 | {% endif %} 49 | 50 | {% if phase=="cleanup"%} 51 | enif_free({{carg}}); 52 | {% endif %} 53 | -------------------------------------------------------------------------------- /priv/templates/lib/union_type.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_argument %} 3 | {{raw_type}}* {{carg}}; 4 | ERL_NIF_TERM {{carg}}_ptr; 5 | ERL_NIF_TERM *{{carg}}_tpl; 6 | int {{carg}}_ar; 7 | {% endif %} 8 | {% if argument|is_return %} 9 | {{raw_type}} c_retval; 10 | ERL_NIF_TERM retval; 11 | {% endif %} 12 | {% if argument|is_field %} 13 | {% if record=="to_record" %} 14 | ERL_NIF_TERM {{erlarg}}; 15 | {% endif %} 16 | {% if record=="to_ptr" %} 17 | ERL_NIF_TERM *{{N}}_tpl; 18 | ERL_NIF_TERM {{N}}_ptr; 19 | int {{N}}_ar; 20 | {{raw_type}}* {{N}}_union; 21 | {% endif %} 22 | {% endif %} 23 | {% endif %} 24 | 25 | {% if phase=="to_c" %} 26 | {% if argument|is_field %} 27 | {{N}}_ptr = urecord_to_erlptr_{{types|fetch:type|getNth:2|getNth:1|getNth:2}}(env, {{erlarg}}); 28 | err = enif_get_tuple(env, {{N}}_ptr, &{{N}}_ar, (const ERL_NIF_TERM**)(&{{N}}_tpl)); 29 | if (err) { 30 | err = nifty_get_ptr(env, {{N}}_tpl[0], (ptr_t*)(&{{N}}_union)); 31 | } 32 | {{carg}} = *{{N}}_union; 33 | {% else %} 34 | {{carg}}_ptr = urecord_to_erlptr_{{types|fetch:type|getNth:2|getNth:1|getNth:2}}(env, {{erlarg}}); 35 | err = enif_get_tuple(env, {{carg}}_ptr, &{{carg}}_ar, (const ERL_NIF_TERM**)(&{{carg}}_tpl)); 36 | if (err) { 37 | err = nifty_get_ptr(env, {{carg}}_tpl[0], (ptr_t*)(&{{carg}})); 38 | } 39 | {% endif %} 40 | {% endif %} 41 | 42 | {% if phase=="argument" %} 43 | {% if argument|is_argument %} 44 | ({{raw_type|discard_restrict}})(*{{carg}}) 45 | {% endif %} 46 | {% endif %} 47 | 48 | {% if phase=="to_erl"%} 49 | {{erlarg}} = ptr_to_urecord_{{types|fetch:type|getNth:2|getNth:1|getNth:2}}(env, (ptr_t)(&{{carg}})); 50 | {% endif %} 51 | 52 | {% if phase=="cleanup"%} 53 | enif_free({{carg}}); 54 | {% endif %} 55 | -------------------------------------------------------------------------------- /src/nifty_tags.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2014-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_tags). 12 | 13 | -export([struct_name/1, 14 | debug/1, 15 | struct_dereference/1, 16 | struct_reference/1]). 17 | 18 | -spec struct_name([{name, string()},...]) -> nonempty_string(). 19 | struct_name([{name, Type}]) -> 20 | Splited = string:tokens(Type, " "), 21 | case Splited of 22 | ["struct", TypeName, _] -> 23 | TypeName; 24 | ["struct", TypeName] -> 25 | TypeName 26 | end. 27 | 28 | -spec struct_dereference([{name, string()},...]) -> nonempty_string(). 29 | struct_dereference([{name, Type}]) -> 30 | Splited = string:tokens(Type, " "), 31 | case Splited of 32 | ["struct", _, P] -> 33 | string:copies("*", length(P)-1); 34 | ["struct", _] -> 35 | "&" 36 | end. 37 | 38 | -spec struct_reference([{name, string()},...]) -> nonempty_string(). 39 | struct_reference([{name, Type}]) -> 40 | Splited = string:tokens(Type, " "), 41 | case Splited of 42 | ["struct", _, P] -> 43 | string:copies("&", length(P)-1); 44 | ["struct", _] -> 45 | "*" 46 | end. 47 | 48 | -spec debug(proplists:proplist()) -> []. 49 | debug(DBM) -> 50 | io:format("DEBUG: ~n"), 51 | print_dbg(DBM). 52 | 53 | print_dbg([]) -> ""; 54 | print_dbg([{Key, Value}|T]) -> 55 | io:format("~p -> ~p~n", [Key, Value]), 56 | print_dbg(T). 57 | -------------------------------------------------------------------------------- /priv/templates/save_emodule.tpl: -------------------------------------------------------------------------------- 1 | -module({{module}}_remote). 2 | 3 | -export([{% with fn=symbols|fetch_keys %}{% for name in fn %} 4 | '{{name}}'/{{ symbols|fetch:name|length|add:-1 }},{% endfor %}{% endwith %} 5 | start/0, 6 | stop/0, 7 | restart/0, 8 | erlptr_to_record/1, 9 | record_to_erlptr/1, 10 | '__nifty__get_types'/0, 11 | '__nifty__new'/1]). 12 | 13 | -type addr() :: integer(). 14 | -type typename() :: string(). 15 | -type ptr() :: {addr(), typename()}. 16 | -type reason() :: atom(). 17 | -type error() :: {error, reason()}. 18 | 19 | %%% Generated 20 | 21 | {% with fn=symbols|fetch_keys %}{% for name in fn %}'{{name}}'({% with arguments=symbols|fetch:name %}{% for argument in arguments %}{% if argument|is_argument %}X{{forloop.counter0}}{% if not forloop.last %},{%endif%}{% endif %}{% endfor %}) -> 22 | nifty_remotecall:call_remote({{module}}, '{{name}}', [{% for argument in arguments %}{% if argument|is_argument %}X{{forloop.counter0}}{% if not forloop.last %},{%endif%}{% endif %}{% endfor %}]). 23 | {% endwith %}{% endfor %}{% endwith %} 24 | 25 | %%% static 26 | 27 | start() -> 28 | nifty_remotecall:start(). 29 | 30 | stop() -> 31 | nifty_remotecall:stop(). 32 | 33 | restart() -> 34 | nifty_remotecall:restart(). 35 | 36 | -spec erlptr_to_record(ptr()) -> tuple() | error(). 37 | erlptr_to_record(Ptr) -> 38 | nifty_remotecall:call_remote({{module}}, erlptr_to_record, [Ptr]). 39 | 40 | -spec record_to_erlptr(tuple()) -> ptr() | error(). 41 | record_to_erlptr(Rec) -> 42 | nifty_remotecall:call_remote({{module}}, record_to_erlptr, [Rec]). 43 | 44 | -spec '__nifty__get_types'() -> nifty_clangparse:type_table() | error(). 45 | '__nifty__get_types'() -> 46 | nifty_remotecall:call_remote({{module}}, '__nifty__get_types', []). 47 | 48 | -spec '__nifty__new'(typename()) -> term(). 49 | '__nifty__new'(Type) -> 50 | nifty_remotecall:call_remote({{module}}, '__nifty__new', [Type]). 51 | -------------------------------------------------------------------------------- /priv/templates/cmodule.tpl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "{{header|absname}}" 8 | 9 | #if _WIN32 || _WIN64 10 | #if _WIN64 11 | typedef unsigned __int64 uint64_t; 12 | #define ENV64BIT 13 | #else 14 | #define ENV32BIT 15 | #endif 16 | #else // clang gcc 17 | #include 18 | #if __x86_64__ 19 | #define ENV64BIT 20 | #else 21 | #define ENV32BIT 22 | #endif 23 | #endif 24 | 25 | #ifdef ENV32BIT 26 | typedef unsigned long ptr_t; 27 | #define nifty_get_ptr(env, term, ip) enif_get_ulong((env), (term), (ip)) 28 | #define nifty_make_ptr(env, i) enif_make_ulong((env), (i)) 29 | #else /* ENV64BIT */ 30 | typedef uint64_t ptr_t; 31 | #define nifty_get_ptr(env, term, ip) enif_get_uint64((env), (term), (ip)) 32 | #define nifty_make_ptr(env, i) enif_make_uint64((env), (i)) 33 | #endif 34 | 35 | {% if config|config_schedule_dirty %} 36 | #ifndef ERL_NIF_DIRTY_SCHEDULER_SUPPORT 37 | #warning "Dirty Schedulers are not supported, compiling without..." 38 | #endif 39 | {% endif %} 40 | 41 | /* 42 | * forward declarations 43 | */ 44 | {% with prototypes=1 %} 45 | {% include "lib/structures.tpl" %} 46 | {% endwith %} 47 | 48 | /* 49 | * Structs 50 | */ 51 | {% include "lib/structures.tpl" %} 52 | 53 | /* 54 | * Build Function Definitions 55 | */ 56 | {% include "lib/function.tpl" %} 57 | 58 | /* 59 | * Function definitions for ErLang 60 | */ 61 | static ErlNifFunc nif_functions[] = { 62 | {% with fn=symbols|fetch_keys %}{% for name in fn %} 63 | {"{{name}}", {{ symbols|fetch:name|length|add:-1 }}, _nifty_{{name}}}, 64 | {% endfor %}{% endwith %} 65 | {"erlptr_to_record", 1, erlptr_to_record}, 66 | {"record_to_erlptr", 1, record_to_erlptr}, 67 | {"erlptr_to_urecord", 1, erlptr_to_urecord}, 68 | {"urecord_to_erlptr", 1, urecord_to_erlptr}, 69 | {"__nifty__new", 1, new_type_object}, 70 | {"__nifty__size_of", 1, size_of} 71 | }; 72 | 73 | int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) 74 | { 75 | return 0; 76 | } 77 | 78 | ERL_NIF_INIT({{module}}, nif_functions, NULL, NULL, upgrade, NULL); 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default fast all get-deps compile dialyzer tests clean mrproper 2 | 3 | ERL_INCLUDE = $(PWD):$(ERL_LIBS) 4 | 5 | ifneq (,$(findstring Windows,$(OS))) 6 | SEP := $(strip \) 7 | else 8 | SEP := $(strip /) 9 | endif 10 | 11 | BEAMS = ebin$(SEP)nifty_clangparse.beam \ 12 | ebin$(SEP)nifty.beam \ 13 | ebin$(SEP)nifty_filters.beam \ 14 | ebin$(SEP)nifty_rebar.beam \ 15 | ebin$(SEP)nifty_tags.beam \ 16 | ebin$(SEP)nifty_types.beam \ 17 | ebin$(SEP)nifty_utils.beam 18 | 19 | REBAR := .$(SEP)rebar3 20 | 21 | # nifty_root 22 | ifndef NIFTY_ROOT_CONFIG 23 | NIFTY_ROOT_CONFIG := NIFTY_ROOT=$(PWD) 24 | else 25 | NIFTY_ROOT_CONFIG := 26 | endif 27 | 28 | # LLVM config 29 | ifdef NIFTY_LLVM_VERSION 30 | LLVM_CONFIG := NIFTY_LLVM_CONFIG=llvm-config-$(NIFTY_LLVM_VERSION) 31 | NIFTY_LLVM_CONFIG := llvm-config-$(NIFTY_LLVM_VERSION) 32 | CLANG := clang-$(NIFTY_LLVM_VERSION) 33 | else 34 | LLVM_CONFIG := NIFTY_LLVM_CONFIG=llvm-config 35 | NIFTY_LLVM_CONFIG := llvm-config 36 | CLANG := clang 37 | endif 38 | 39 | NIFTY_CPATH := `$(NIFTY_LLVM_CONFIG) --libdir`/clang/`$(NIFTY_LLVM_CONFIG) --version`/include/:$(CPATH) 40 | 41 | CONFIG := $(NIFTY_ROOT_CONFIG) $(LLVM_CONFIG) 42 | 43 | DIALYZER_APPS = erts kernel stdlib compiler crypto syntax_tools tools 44 | DIALYZER_FLAGS = -Wunmatched_returns -Wunderspecs 45 | 46 | default: fast 47 | 48 | fast: compile 49 | 50 | all: default tests dialyze doc 51 | 52 | get-deps: rebar3 53 | $(REBAR) get-deps 54 | travis/fix_exports.sh 55 | 56 | compile: get-deps rebar3 57 | $(CONFIG) $(REBAR) compile 58 | 59 | dialyze: rebar3 60 | $(REBAR) dialyzer 61 | 62 | tests: compile rebar3 63 | CC=$(CLANG) \ 64 | CPATH=$(NIFTY_CPATH) \ 65 | $(CONFIG) \ 66 | ERL_LIBS=$(ERL_INCLUDE) \ 67 | $(REBAR) eunit 68 | 69 | doc: rebar3 70 | $(REBAR) edoc 71 | 72 | clean: rebar3 73 | $(REBAR) clean 74 | 75 | mrproper: clean 76 | $(RM) rebar3 rebar.lock 77 | $(RM) -r _build/ cache/ 78 | $(RM) -rf nt_* dereference_regression/ 79 | $(RM) -r doc 80 | 81 | shell: rebar3 82 | $(REBAR) shell 83 | 84 | rebar3: 85 | wget https://github.com/erlang/rebar3/releases/download/3.13.2/rebar3 86 | chmod +x rebar3 87 | -------------------------------------------------------------------------------- /priv/templates/emodule.tpl: -------------------------------------------------------------------------------- 1 | -module({{module}}). 2 | 3 | -export([{% with fn=symbols|fetch_keys %}{% for name in fn %} 4 | '{{name}}'/{{ symbols|fetch:name|length|add:-1 }},{% endfor %}{% endwith %} 5 | erlptr_to_record/1, 6 | record_to_erlptr/1, 7 | erlptr_to_urecord/1, 8 | urecord_to_erlptr/1, 9 | '__nifty__get_types'/0, 10 | '__nifty__get_enum_aliases'/0, 11 | '__nifty__new'/1, 12 | '__nifty__size_of'/1]). 13 | 14 | -define(TYPES, {{types}}). 15 | 16 | -define(ENUM_ALIASES, {{constructors|enum_aliases}}). 17 | 18 | -on_load(init/0). 19 | 20 | -type addr() :: integer(). 21 | -type typename() :: string(). 22 | -type ptr() :: {addr(), typename()}. 23 | 24 | init() -> 25 | PrivDir = case code:priv_dir(?MODULE) of 26 | {error, _} -> 27 | EbinDir = filename:dirname(code:which(?MODULE)), 28 | AppPath = filename:dirname(EbinDir), 29 | filename:join(AppPath, "priv"); 30 | Path -> 31 | Path 32 | end, 33 | erlang:load_nif(filename:join(PrivDir, "{{module}}_nif"), 0). 34 | 35 | {% with fn=symbols|fetch_keys %}{% for name in fn %}'{{name}}'({% with arguments=symbols|fetch:name %}{% for argument in arguments %}{% if argument|is_argument %}_{% if not forloop.last %},{%endif%}{% endif %}{% endfor %}{% endwith %}) -> 36 | erlang:nif_error(nif_library_not_loaded). 37 | {% endfor %}{% endwith %} 38 | 39 | %%% static 40 | 41 | -spec erlptr_to_record(ptr()) -> tuple(). 42 | erlptr_to_record(_) -> 43 | erlang:nif_error(nif_library_not_loaded). 44 | 45 | -spec record_to_erlptr(tuple()) -> ptr(). 46 | record_to_erlptr(_) -> 47 | erlang:nif_error(nif_library_not_loaded). 48 | 49 | -spec erlptr_to_urecord(ptr()) -> tuple(). 50 | erlptr_to_urecord(_) -> 51 | erlang:nif_error(nif_library_not_loaded). 52 | 53 | -spec urecord_to_erlptr(tuple()) -> ptr(). 54 | urecord_to_erlptr(_) -> 55 | erlang:nif_error(nif_library_not_loaded). 56 | 57 | -spec '__nifty__get_types'() -> nifty_clangparse:type_table(). 58 | '__nifty__get_types'() -> 59 | ?TYPES. 60 | 61 | -spec '__nifty__get_enum_aliases'() -> proplist:proplist(). 62 | '__nifty__get_enum_aliases'() -> 63 | ?ENUM_ALIASES. 64 | 65 | -spec '__nifty__new'(typename()) -> term(). 66 | '__nifty__new'(_) -> 67 | erlang:nif_error(nif_library_not_loaded). 68 | 69 | -spec '__nifty__size_of'(typename()) -> integer() | undef. 70 | '__nifty__size_of'(_) -> 71 | erlang:nif_error(nif_library_not_loaded). 72 | -------------------------------------------------------------------------------- /c_src/Makefile: -------------------------------------------------------------------------------- 1 | TARGETS = ../priv/nifty.so ../priv/nifty_clangparse.so 2 | 3 | CURDIR := $(shell pwd) 4 | BASEDIR := $(abspath $(CURDIR)/..) 5 | 6 | ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s/erts-~s/include/\", [code:root_dir(), erlang:system_info(version)]).") 7 | ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, include)]).") 8 | ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, lib)]).") 9 | 10 | C_SRC_DIR = $(CURDIR) 11 | 12 | # System type and C compiler/flags. 13 | 14 | UNAME_SYS := $(shell uname -s) 15 | ifeq ($(UNAME_SYS), Darwin) 16 | CC ?= cc 17 | CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes 18 | LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress 19 | else ifeq ($(UNAME_SYS), FreeBSD) 20 | CC ?= cc 21 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 22 | else ifeq ($(UNAME_SYS), Linux) 23 | CC ?= gcc 24 | CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes 25 | endif 26 | 27 | # LLVM config 28 | ifdef NIFTY_LLVM_VERSION 29 | NIFTY_LLVM_CONFIG := llvm-config-$(NIFTY_LLVM_VERSION) 30 | else 31 | NIFTY_LLVM_CONFIG := llvm-config 32 | endif 33 | 34 | CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) 35 | CFLAGS_CP = $(CFLAGS) `$(BASEDIR)/travis/safe-cflags.sh $(NIFTY_LLVM_CONFIG)` 36 | 37 | LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei 38 | LDLIBS_CP = $(LDLIBS) -lclang `$(NIFTY_LLVM_CONFIG) --ldflags` 39 | LDFLAGS += -shared 40 | 41 | # Verbosity. 42 | 43 | c_verbose_0 = @echo " C " $(?F); 44 | c_verbose = $(c_verbose_$(V)) 45 | 46 | link_verbose_0 = @echo " LD " $(@F); 47 | link_verbose = $(link_verbose_$(V)) 48 | 49 | SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) 50 | OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) 51 | 52 | COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c 53 | COMPILE_C_CP = $(c_verbose) $(CC) $(CFLAGS_CP) $(CPPFLAGS) -c 54 | 55 | all: $(TARGETS) 56 | 57 | ../priv/nifty.so: nifty.o 58 | @mkdir -p $(BASEDIR)/priv/ 59 | $(link_verbose) $(CC) $< $(LDFLAGS) $(LDLIBS) -o $@ 60 | 61 | ../priv/nifty_clangparse.so: nifty_clangparse.o 62 | @mkdir -p $(BASEDIR)/priv/ 63 | $(link_verbose) $(CC) $< $(LDFLAGS) $(LDLIBS_CP) -o $@ 64 | 65 | nifty.o: nifty.c 66 | $(COMPILE_C) $< 67 | 68 | nifty_clangparse.o: nifty_clangparse.c 69 | $(COMPILE_C_CP) $< 70 | 71 | clean: 72 | @rm -f $(OBJECTS) 73 | -------------------------------------------------------------------------------- /test/nifty_lib_test.erl: -------------------------------------------------------------------------------- 1 | -module(nifty_lib_test). 2 | -export([cstr_list_test/0, 3 | read_write_test/0, 4 | short_test/0, 5 | int_deref_test/0, 6 | pointer_of_pointer_test/0, 7 | array_test/0]). 8 | 9 | -include_lib("proper/include/proper.hrl"). 10 | -inlcude_lib("eunit/include/eunit.hrl"). 11 | 12 | -type mystring() :: [1..255]. 13 | 14 | %% auxiliary functions 15 | -spec cstr_list_comp(mystring()) -> boolean(). 16 | cstr_list_comp(Str) -> 17 | CStr = nifty:list_to_cstr(Str), 18 | DStr = nifty:cstr_to_list(CStr), 19 | ok = nifty:free(CStr), 20 | DStr =:= Str. 21 | 22 | short_comp(Sh) -> 23 | P = nifty:pointer_of(Sh, "unsigned short"), 24 | DSh = nifty:dereference(P), 25 | ok = nifty:free(P), 26 | Sh =:= DSh. 27 | 28 | -spec write_read([byte()]) -> boolean(). 29 | write_read(Data) -> 30 | Ptr = nifty:mem_write(Data), 31 | DData = nifty:mem_read(Ptr, length(Data)), 32 | ok = nifty:free(Ptr), 33 | DData =:= Data. 34 | 35 | %% properties 36 | -spec prop_cstr_list() -> proper:outer_test(). 37 | prop_cstr_list() -> 38 | ?FORALL(S, mystring(), cstr_list_comp(S)). 39 | 40 | -spec prop_read_write() -> proper:outer_test(). 41 | prop_read_write() -> 42 | ?FORALL(S, list(byte()), write_read(S)). 43 | 44 | -spec prop_short() -> proper:outer_test(). 45 | prop_short() -> 46 | ?FORALL(S, integer(0,trunc(math:pow(2,16))-1), short_comp(S)). 47 | 48 | %% testcases 49 | -spec cstr_list_test() -> boolean(). 50 | cstr_list_test() -> 51 | proper:quickcheck(prop_cstr_list(), [{to_file, user}, {numtests, 1000}]). 52 | 53 | -spec read_write_test() -> boolean(). 54 | read_write_test() -> 55 | proper:quickcheck(prop_read_write(), [{to_file, user}, {numtests, 1000}]). 56 | 57 | -spec short_test() -> boolean(). 58 | short_test() -> 59 | proper:quickcheck(prop_short(), [{to_file, user}, {numtests, 1000}]). 60 | 61 | -spec int_deref_test() -> boolean(). 62 | int_deref_test() -> 63 | Ptr = nifty:mem_write([16#ff, 16#ff, 16#ff, 16#ff, 16#ff, 16#ff, 16#ff, 16#ff]), 64 | -1 = nifty:dereference(nifty:as_type(Ptr, "signed char *")), 65 | -1 = nifty:dereference(nifty:as_type(Ptr, "short *")), 66 | -1 = nifty:dereference(nifty:as_type(Ptr, "int *")), 67 | -1 = nifty:dereference(nifty:as_type(Ptr, "long *")), 68 | -1 = nifty:dereference(nifty:as_type(Ptr, "nifty.long long *")), 69 | ok = nifty:free(Ptr), 70 | true. 71 | 72 | -spec pointer_of_pointer_test() -> boolean(). 73 | pointer_of_pointer_test() -> 74 | 10 =:= nifty:dereference(nifty:dereference(nifty:pointer_of(nifty:pointer_of(10, "int"), "nifty.int *"))). 75 | 76 | -spec array_test() -> boolean(). 77 | array_test() -> 78 | Array = nifty:list_to_array([1,2,3,4,5], "int"), 79 | ok = nifty:array_set(Array, 1000, 2), 80 | [1,2,1000,4,5] =:= nifty:array_to_list(Array, 5). 81 | -------------------------------------------------------------------------------- /priv/templates/lib/int_type.tpl: -------------------------------------------------------------------------------- 1 | {% if phase=="prepare" %} 2 | {% if argument|is_argument %} 3 | {{type}} {{carg}}; 4 | {% if typedef|getNth:3=="short" or typedef|getNth:1=="char" %} 5 | {% if typedef|getNth:2=="unsigned" %} 6 | unsigned int helper{{N}}; 7 | {% else %} 8 | int helper{{N}}; 9 | {% endif %} 10 | {% endif %} 11 | {% endif %} 12 | {% if argument|is_return %} 13 | {{type}} c_retval; 14 | ERL_NIF_TERM retval; 15 | {% endif %} 16 | {% if argument|is_field %} 17 | {% if record=="to_record" %} 18 | ERL_NIF_TERM {{erlarg}}; 19 | {% endif %} 20 | {% if record=="to_ptr" and (typedef|getNth:1=="char" or typedef|getNth:3=="short") %} 21 | {% if typedef|getNth:2=="unsigned" %} 22 | unsigned int helper{{N}}; 23 | {% else %} 24 | int helper{{N}}; 25 | {% endif %} 26 | {% endif %} 27 | {% endif %} 28 | {% endif %} 29 | 30 | {% if phase=="to_c" %} 31 | err = 32 | {% if typedef|getNth:2=="unsigned" and (typedef|getNth:3=="short" or typedef|getNth:1=="char") %}enif_get_uint{% endif %} 33 | {% if typedef|getNth:2=="signed" and (typedef|getNth:3=="short" or typedef|getNth:1=="char") %}enif_get_int{% endif %} 34 | {% if typedef|getNth:2=="unsigned" and (typedef|getNth:3=="none" and typedef|getNth:1=="int") %}enif_get_uint{% endif %} 35 | {% if typedef|getNth:2=="signed" and (typedef|getNth:3=="none" and typedef|getNth:1=="int") %}enif_get_int{% endif %} 36 | {% if typedef|getNth:2=="unsigned" and typedef|getNth:3=="long" %}enif_get_ulong{% endif %} 37 | {% if typedef|getNth:2=="signed" and typedef|getNth:3=="long" %}enif_get_long{% endif %} 38 | {% if typedef|getNth:2=="unsigned" and typedef|getNth:3=="longlong" %}enif_get_uint64{% endif %} 39 | {% if typedef|getNth:2=="signed" and typedef|getNth:3=="longlong" %}enif_get_int64{% endif %} 40 | (env, {{erlarg}}, 41 | {% if typedef|getNth:3=="short" or typedef|getNth:1=="char" %}&helper{{N}}{% else %} 42 | {% if typedef|getNth:2=="unsigned" and typedef|getNth:3=="longlong" %}(unsigned long int*){%endif%} 43 | {% if typedef|getNth:2=="signed" and typedef|getNth:3=="longlong" %}(long int*){%endif%} 44 | (&{{carg}}) 45 | {% endif %}); 46 | {% if typedef|getNth:3=="short" or typedef|getNth:1=="char" %} 47 | {{carg}}=({{type}})helper{{N}}; 48 | {% endif %} 49 | {% endif %} 50 | 51 | {% if phase=="argument" %} 52 | {% if argument|is_argument %} 53 | ({{raw_type|discard_restrict}}){{carg}} 54 | {% else %} 55 | ({{type}}) 56 | {% endif %} 57 | {% endif %} 58 | 59 | {% if phase=="to_erl"%} 60 | {{erlarg}} = 61 | {% if typedef|getNth:2=="unsigned" and (typedef|getNth:3=="short" or typedef|getNth:1=="char") %}enif_make_uint{% endif %} 62 | {% if typedef|getNth:2=="signed" and (typedef|getNth:3=="short" or typedef|getNth:1=="char") %}enif_make_int{% endif %} 63 | {% if typedef|getNth:2=="unsigned" and (typedef|getNth:3=="none" and typedef|getNth:1=="int") %}enif_make_uint{% endif %} 64 | {% if typedef|getNth:2=="signed" and (typedef|getNth:3=="none" and typedef|getNth:1=="int") %}enif_make_int{% endif %} 65 | {% if typedef|getNth:2=="unsigned" and typedef|getNth:3=="long" %}enif_make_ulong{% endif %} 66 | {% if typedef|getNth:2=="signed" and typedef|getNth:3=="long" %}enif_make_long{% endif %} 67 | {% if typedef|getNth:2=="unsigned" and typedef|getNth:3=="longlong" %}enif_make_uint64{% endif %} 68 | {% if typedef|getNth:2=="signed" and typedef|getNth:3=="longlong" %}enif_make_int64{% endif %}(env, {{carg}}); 69 | {% endif %} 70 | 71 | {# no cleanup phase #} 72 | -------------------------------------------------------------------------------- /src/nifty_types.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2014-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_types). 12 | 13 | -export([check_type/2, 14 | check_type/3, 15 | resolve_type/2]). 16 | 17 | -define(CLANG_BUILTINS, ["__int128_t", "__builtin_va_list", "__uint128_t"]). 18 | -define(CLANG_BLACKLIST, ["__builtin_va_list", 19 | "__va_list_tag", 20 | "__va_list_tag [1]"]). 21 | 22 | %% @doc takes a type and a type table and returns the resolved type (according to the type table) 23 | -spec resolve_type(nifty_clangparse:ctype(), nifty_clangparse:type_table()) -> nifty_clangparse:ctype() | undef. 24 | resolve_type(Type, Types) -> 25 | case resolve_type2(Type, nifty:get_types()) of 26 | undef -> resolve_type2(Type, Types); 27 | T -> T 28 | end. 29 | 30 | resolve_type2(Type, Types) -> 31 | case dict:is_key(Type, Types) of 32 | true -> 33 | {Kind, TypeDef} = dict:fetch(Type, Types), 34 | case Kind of 35 | typedef -> resolve_type(TypeDef, Types); 36 | _ -> Type 37 | end; 38 | false -> 39 | undef 40 | end. 41 | 42 | %% @doc Checks if Type is a valid type 43 | -spec check_type(nifty_clangparse:ctype(), nifty_clangparse:type_table()) -> boolean(). 44 | check_type(Type, Types) -> 45 | (not lists:member(Type, ?CLANG_BLACKLIST)) andalso check_type(Type, Types, undef). 46 | 47 | %% @doc Checks if Type is a valid type 48 | -spec check_type(nifty_clangparse:ctype(), nifty_clangparse:type_table(), nifty_clangparse:constr_table() | undef) -> boolean(). 49 | check_type(Type, Types, Constructors) -> 50 | (not lists:member(Type, ?CLANG_BLACKLIST)) 51 | andalso 52 | (check_type2(Type, nifty:get_types(), dict:new()) orelse 53 | check_type2(Type, Types, Constructors)). 54 | 55 | check_type2(Type, Types, Constructors) -> 56 | case dict:is_key(Type, Types) of 57 | true -> 58 | case resolve_type(Type, Types) of 59 | undef -> 60 | false; 61 | RType -> 62 | case dict:fetch(RType, Types) of 63 | {userdef, [RType]} -> 64 | false; %% loop 65 | {userdef, [T]} -> 66 | %% constructor or dead end 67 | case T of 68 | {struct, _Name} -> 69 | case Constructors of 70 | undef -> true; 71 | C -> dict:is_key(T, C) 72 | end; 73 | {union, _Name} -> 74 | case Constructors of 75 | undef -> true; 76 | C -> dict:is_key(T, C) 77 | end; 78 | _-> 79 | case T =:= Type of 80 | true -> false; %% loop 81 | false -> check_type(T, Types) %% something else 82 | end 83 | end; 84 | {userdef, [T, "const"]} -> 85 | %% discard const and check again 86 | check_type(T,Types); 87 | {userdef, [H|T]} -> 88 | string:right(H, 1) =/= ")" %% function pointer 89 | andalso lists:last(T) =/= "enum"; %% enum 90 | {base, _} -> 91 | true 92 | end 93 | end; 94 | false -> 95 | false 96 | end. 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Travis][travis badge]][travis] 2 | [![Erlang Versions][erlang versions badge]][erlang] 3 | [![Clang Versions][clang versions badge]][clang] 4 | [![Last Commit][commit badge]][commit] 5 | 6 | 7 | # Nifty - Erlang NIF Wrapper Generator 8 | 9 | Nifty is an interface generator that allows the use of C modules from Erlang. 10 | 11 | Web page: [http://parapluu.github.io/nifty/] 12 | 13 | ## A Simple Example 14 | 15 | Let's say we have two C files `mylib.h` and `mylib.c` which we want to use in our Erlang application: 16 | 17 | ```C 18 | /* mylib.h */ 19 | extern int fib(int n); 20 | ``` 21 | 22 | ```C 23 | /* mylib.c */ 24 | int 25 | fib(int n) { 26 | if (n <= 2) { 27 | return 1; 28 | } else { 29 | return fib(n-1) + fib(n-2); 30 | } 31 | } 32 | 33 | ``` 34 | 35 | We can generate a NIF interface and use it from Erlang with the following command: 36 | 37 | ```Erlang 38 | nifty:compile("mylib.h", mylib, nifty_utils:add_sources(["mylib.c"], [])). 39 | 5 = mylib:fib(5). 40 | ``` 41 | 42 | ***nifty:compile/3*** gets as its first argument a header or interface 43 | file and tries to generate an interface for all specified functions. 44 | The second argument specifies the Erlang module the interface is about 45 | to use and the third argument is used for additional options. These 46 | options are compatible with the ones rebar uses to compile NIF 47 | modules. In fact, Nifty uses rebar to compile the generated interface. 48 | 49 | ## Installation 50 | After successfully cloning enter the following commands 51 | 52 | ```shell 53 | make 54 | ``` 55 | 56 | and include Nifty in your ERL_LIBS path. 57 | 58 | ## Unit Tests 59 | Run the following command to check that everything works correct: 60 | 61 | ```shell 62 | make tests 63 | ``` 64 | 65 | ## Dependencies 66 | * The [clang](http://clang.llvm.org/) compiler and **libclang**, including the header files. 67 | * **[Optional]** [PropEr](https://github.com/proper-testing/proper) for the unit tests. 68 | 69 | ## Base Types 70 | 71 | | C Types | Erlang Types | Nifty Type Name 72 | |--------------------------------|-----------------------------|-------------------------- 73 | | ```signed int``` or ```int``` | ```integer()``` | ```nifty.int``` 74 | | ```unsigned int``` | ```integer()``` | ```nifty.unsigned int``` 75 | | ```char``` | ```integer()``` | ```nifty.char``` 76 | | ```short``` | ```integer()``` | ```nifty.short``` 77 | | ```long``` | ```integer()``` | ```nifty.long``` 78 | | ```long long``` | ```integer()``` | ```nifty.long long``` 79 | | ```float``` | ```float()``` | ```nifty.float``` 80 | | ```double``` | ```float()``` | ```nifty.double``` 81 | | ``` *``` | ```{integer(), string()}``` | ```. *``` 82 | 83 | ## Known Limitations 84 | * Function pointers are only partially supported. 85 | * There is no support for anonymous structs and unions. 86 | * Functions using unsupported types are not translated and a warning is issued. 87 | * Functions with a variable number of arguments (`va_list` or `...`) are not supported. If `va_list` as type is used, Nifty will print a warning. If `...` is used, then the function is translated **without** the variable arguments: `int printf(const char *format, ...)` will be translated into `printf/1`. 88 | * The usage of incomplete types is limited. 89 | * Nifty has not been tested under Windows. 90 | 91 | 92 | [clang]: http://clang.llvm.org 93 | [commit]: https://github.com/parapluu/nifty/commit/HEAD 94 | [erlang]: http://www.erlang.org 95 | [travis]: https://travis-ci.org/parapluu/nifty 96 | 97 | 98 | [clang versions badge]: https://img.shields.io/badge/clang-3.8.1%20to%209.0.0-ff69b4.svg?style=flat-square 99 | [commit badge]: https://img.shields.io/github/last-commit/parapluu/nifty.svg?style=flat-square 100 | [erlang versions badge]: https://img.shields.io/badge/erlang-20.0%20to%2022.3-blue.svg?style=flat-square 101 | [travis badge]: https://img.shields.io/travis/parapluu/nifty.svg?branch=master?style=flat-square 102 | -------------------------------------------------------------------------------- /src/nifty_remotecall.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2014-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_remotecall). 12 | 13 | -export([start/0, 14 | stop/0, 15 | restart/0, 16 | call_remote/3]). 17 | 18 | 19 | %% @doc Starts remote node to safely call NIFs. 20 | -spec start() -> ok. 21 | start() -> 22 | %% try to start epmd 23 | [] = os:cmd("epmd -daemon"), 24 | Host = list_to_atom(net_adm:localhost()), 25 | case net_kernel:start([mastername(), shortnames]) of 26 | {ok, Pid} -> 27 | Pid; 28 | {error, {already_started, Pid}} -> 29 | Pid 30 | end, 31 | case slave:start_link(Host, slavename()) of 32 | {ok, Node} -> 33 | SlavePid = spawn(Node, fun slave_server/0), 34 | SlavePid ! {self(), code:get_path()}, 35 | undefined = put(slave_pid, SlavePid), 36 | ok; 37 | {error, {already_running, _}} -> 38 | ok 39 | end. 40 | 41 | %% @doc Stops remote node 42 | -spec stop() -> ok. 43 | stop() -> 44 | case is_slave_alive() of 45 | true -> 46 | Host = list_to_atom(net_adm:localhost()), 47 | erase(slave_pid), 48 | {error, {already_running, Node}} = slave:start_link(Host, slavename()), 49 | slave:stop(Node); 50 | false -> 51 | ok 52 | end, 53 | _ = net_kernel:stop(), %% after this call the kernel is not running 54 | ok. 55 | 56 | %% @doc Restarts remote node, useful for testing (side-effects are reset). 57 | -spec restart() -> ok. 58 | restart() -> 59 | stop(), 60 | start(). 61 | 62 | slave_server() -> 63 | receive 64 | stop -> 65 | ok; 66 | {P, Paths} -> 67 | lists:foreach(fun code:add_patha/1, Paths), 68 | slave_server(P) 69 | end. 70 | 71 | slave_server(P) -> 72 | receive 73 | stop -> 74 | ok; 75 | alive -> 76 | P ! ok, 77 | slave_server(P); 78 | {Module, Function, Args} -> 79 | RetMsg = try erlang:apply(Module, Function, Args) of 80 | RetVal -> 81 | {return, RetVal} 82 | catch 83 | throw:Error -> 84 | {throw, Error}; 85 | error:Error -> 86 | {error, Error}; 87 | exit:Error -> 88 | {exit, Error} 89 | end, 90 | P ! RetMsg, 91 | slave_server(P) 92 | end. 93 | 94 | %% @doc Works like erlang:apply/3, with the exception that the calls 95 | %% are redirected to a remote node. The node must be started in order 96 | %% to be able to call functions remotely. If the node is down, 97 | %% {error, node_down} is thrown. If the node crashes 98 | %% during the call (SIGSEGV or similar) {error, 99 | %% node_crashed} is thrown. 100 | -spec call_remote(atom(), atom(), [term()]) -> term(). 101 | call_remote(Mod, Func, Args) -> 102 | case get(slave_pid) of 103 | undefined -> 104 | {error, node_down}; 105 | Slave -> 106 | Slave ! {Mod, Func, Args}, 107 | receive_msg(100) 108 | end. 109 | 110 | receive_msg(T) -> 111 | receive 112 | {return, RetVal} -> 113 | RetVal; 114 | {throw, Error} -> 115 | throw(Error); 116 | {error, Error} -> 117 | erlang:error(Error); 118 | {exit, Error} -> 119 | erlang:exit(Error) 120 | after T -> 121 | case is_slave_alive() of 122 | false -> 123 | erlang:error(node_crashed); 124 | true -> 125 | receive_msg(1000) 126 | end 127 | end. 128 | 129 | is_slave_alive() -> 130 | Host = list_to_atom(net_adm:localhost()), 131 | case slave:start_link(Host, slavename()) of 132 | {ok, Node} -> 133 | slave:stop(Node), 134 | erase(slave_pid), 135 | false; 136 | {error, {already_running, _}} -> 137 | true 138 | end. 139 | 140 | mastername() -> 141 | list_to_atom(processname()++"_master"). 142 | 143 | slavename() -> 144 | list_to_atom(processname()++"_slave"). 145 | 146 | processname() -> 147 | I = pid_to_list(self()), 148 | [_, PN1, PN2] = string:tokens(I, "<.>"), 149 | "p"++PN1++"_p"++PN2. 150 | -------------------------------------------------------------------------------- /priv/templates/lib/function.tpl: -------------------------------------------------------------------------------- 1 | {% with fn=symbols|fetch_keys %}{% for name in fn %} 2 | 3 | 4 | {% if config|config_schedule_dirty:name %} 5 | #ifdef ERL_NIF_DIRTY_SCHEDULER_SUPPORT 6 | static ERL_NIF_TERM _nifty_impl_{{name}}(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); 7 | 8 | static ERL_NIF_TERM 9 | _nifty_{{name}}(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { 10 | return enif_schedule_nif(env, "{{name}}", {{config|config_schedule_dirty_type:name}}, &_nifty_impl_{{name}}, argc, argv); 11 | } 12 | 13 | static ERL_NIF_TERM 14 | _nifty_impl_{{name}}(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 15 | #else 16 | static ERL_NIF_TERM 17 | _nifty_{{name}}(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 18 | #endif 19 | {% endif %} 20 | {% if not config|config_schedule_dirty:name %} 21 | static ERL_NIF_TERM 22 | _nifty_{{name}}(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 23 | {% endif %} 24 | { 25 | int err=0; 26 | {# 27 | /* 28 | * Variable transition inside of a function: 29 | * prepare -- are additional variable definitions required -> e.a. short int translation 30 | * to_c -- translate the erlang values to c values 31 | * argument-- yield argument 32 | * to_erl -- translate the return value and eventual output arguments to erlang types 33 | * cleanup -- cleanup memory used by the function 34 | */ 35 | #} 36 | 37 | 38 | {% with arguments=symbols|fetch:name %} 39 | {% for argument in arguments %} 40 | {% if argument|is_argument %} 41 | {% with raw_type=argument|getNth:3 phase="prepare" %} 42 | {% with type=raw_type|resolved:types %} 43 | {% with N=argument|getNth:2 %} 44 | {% with carg="carg_"|add:N erlarg="argv["|add:N|add:"]" %} 45 | {% include "lib/builtin_type.tpl" %} 46 | {% endwith %} 47 | {% endwith %} 48 | {% endwith %} 49 | {% endwith %} 50 | {% endif %} 51 | {% if argument|is_return %} 52 | {% with raw_type=argument|getNth:2 phase="prepare" %} 53 | {% with type=raw_type|resolved:types %} 54 | {% with N=argument|getNth:2 %} 55 | {% with carg="carg_"|add:N erlarg="argv["|add:N|add:"]" %} 56 | {% include "lib/builtin_type.tpl" %} 57 | {% endwith %} 58 | {% endwith %} 59 | {% endwith %} 60 | {% endwith %} 61 | {% endif %} 62 | {% endfor %} 63 | {% endwith %} 64 | 65 | {% with arguments=symbols|fetch:name %} 66 | {% for argument in arguments %} 67 | {% if argument|is_argument %} 68 | {% with raw_type=argument|getNth:3 phase="to_c" %} 69 | {% with type=raw_type|resolved:types %} 70 | {% with N=argument|getNth:2 %} 71 | {% with carg="carg_"|add:N erlarg="argv["|add:N|add:"]" %} 72 | {% include "lib/builtin_type.tpl" %} 73 | {% endwith %} 74 | {% endwith %} 75 | {% endwith %} 76 | if (!err) { 77 | goto error; 78 | } 79 | {% endwith %} 80 | {% endif %} 81 | {% endfor %} 82 | {% endwith %} 83 | 84 | {% with arguments=symbols|fetch:name %} 85 | {% for argument in arguments %} 86 | {% if argument|is_return %} 87 | {% with raw_type=argument|getNth:2 phase="argument" %} 88 | {% with type=raw_type|resolved:types %} 89 | {% if not type=="void" %} 90 | c_retval = 91 | {% endif %} 92 | {% endwith %} 93 | {% endwith %} 94 | {% endif %} 95 | {% endfor %} 96 | {% endwith %} 97 | {% with arguments=symbols|fetch:name %} 98 | {% for argument in arguments %} 99 | {% if argument|is_return %} 100 | {% with raw_type=argument|getNth:2 phase="argument" %} 101 | {% with type=raw_type|resolved:types %} 102 | {% with N=argument|getNth:2 %} 103 | {% with carg="carg_"|add:N erlarg="argv["|add:N|add:"]" %} 104 | {% include "lib/builtin_type.tpl" %} 105 | {% endwith %} 106 | {% endwith %} 107 | {% endwith %} 108 | {% endwith %} 109 | {% endif %} 110 | {% endfor %} 111 | {% endwith %} 112 | {{name}}( 113 | {% with arguments=symbols|fetch:name %} 114 | {% for argument in arguments %} 115 | {% if argument|is_argument %} 116 | {% with raw_type=argument|getNth:3 phase="argument" %} 117 | {% with type=raw_type|resolved:types %} 118 | {% with N=argument|getNth:2 %} 119 | {% with carg="carg_"|add:N erlarg="argv["|add:N|add:"]" %} 120 | {% include "lib/builtin_type.tpl" %} 121 | {% endwith %} 122 | {% endwith %} 123 | {% endwith %} 124 | {% endwith %} 125 | {% endif %} 126 | {% if not forloop.last and argument|is_argument %},{%endif%} 127 | {% endfor %} 128 | {% endwith %} 129 | ); 130 | 131 | {% with arguments=symbols|fetch:name %} 132 | {% for argument in arguments %} 133 | {% if argument|is_return %} 134 | {% with raw_type=argument|getNth:2 phase="to_erl" %} 135 | {% with type=raw_type|resolved:types %} 136 | {% with N=argument|getNth:2 %} 137 | {% with carg="c_retval" erlarg="retval" %} 138 | {% include "lib/builtin_type.tpl" %} 139 | {% endwith %} 140 | {% endwith %} 141 | {% endwith %} 142 | {% endwith %} 143 | {% endif %} 144 | {% endfor %} 145 | {% endwith %} 146 | 147 | {% with arguments=symbols|fetch:name %} 148 | {% for argument in arguments %} 149 | {% if argument|is_argument %} 150 | {% with raw_type=argument|getNth:3 phase="cleanup" %} 151 | {% with type=raw_type|resolved:types %} 152 | {% with N=argument|getNth:2 %} 153 | {% with carg="carg_"|add:N erlarg="argv["|add:N|add:"]" %} 154 | {% include "lib/builtin_type.tpl" %} 155 | {% endwith %} 156 | {% endwith %} 157 | {% endwith %} 158 | {% endwith %} 159 | {% endif %} 160 | {% endfor %} 161 | {% endwith %} 162 | 163 | return retval; 164 | error: 165 | return enif_make_badarg(env); 166 | 167 | goto error; 168 | err++; 169 | } 170 | 171 | {% endfor %}{% endwith %} 172 | -------------------------------------------------------------------------------- /src/nifty_remote.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2014-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_remote). 12 | 13 | -export([%% control 14 | start/0, 15 | stop/0, 16 | %% create modules 17 | compile/3, 18 | %% strings 19 | list_to_cstr/1, 20 | cstr_to_list/1, 21 | %% pointers 22 | dereference/1, 23 | pointer/0, 24 | pointer/1, 25 | pointer_of/2, 26 | pointer_of/1, 27 | %% types 28 | as_type/2, 29 | size_of/1, 30 | %% memory allocation 31 | malloc/1, 32 | mem_write/1, 33 | mem_write/2, 34 | mem_read/2, 35 | mem_alloc/1, 36 | mem_copy/3, 37 | free/1, 38 | %% configuration 39 | get_config/0, 40 | get_env/0, 41 | %% builtin types 42 | get_types/0, 43 | %% array 44 | array_new/2, 45 | array_ith/2, 46 | array_element/2, 47 | array_set/3, 48 | array_to_list/2, 49 | list_to_array/2, 50 | %% enums 51 | enum_value/2 52 | ]). 53 | 54 | -spec start() -> ok. 55 | start() -> 56 | nifty_remotecall:start(). 57 | 58 | -spec stop() -> ok. 59 | stop() -> 60 | nifty_remotecall:stop(). 61 | 62 | -spec compile(string(), module(), nifty:options()) -> nifty:comp_ret() | {error, node_down}. 63 | compile(X0,X1,X2) -> 64 | nifty_remotecall:call_remote(nifty, compile, [X0,X1,X2]). 65 | 66 | -spec list_to_cstr(string()) -> nifty:ptr() | {error, node_down}. 67 | list_to_cstr(X0) -> 68 | nifty_remotecall:call_remote(nifty, list_to_cstr, [X0]). 69 | 70 | -spec cstr_to_list(nifty:ptr()) -> string() | {error, node_down}. 71 | cstr_to_list(X0) -> 72 | nifty_remotecall:call_remote(nifty, cstr_to_list, [X0]). 73 | 74 | -spec dereference(nifty:ptr()) -> nifty:cvalue() | {error, node_down}. 75 | dereference(X0) -> 76 | nifty_remotecall:call_remote(nifty, dereference, [X0]). 77 | 78 | -spec pointer() -> nifty:ptr() | {error, node_down}. 79 | pointer() -> 80 | nifty_remotecall:call_remote(nifty, pointer, []). 81 | 82 | -spec pointer(nonempty_string()) -> nifty:ptr() | undef | {error, node_down}. 83 | pointer(X0) -> 84 | nifty_remotecall:call_remote(nifty, pointer, [X0]). 85 | 86 | -spec pointer_of(term(), string()) -> nifty:ptr() | undef | {error, node_down}. 87 | pointer_of(X0,X1) -> 88 | nifty_remotecall:call_remote(nifty, pointer_of, [X0,X1]). 89 | 90 | -spec pointer_of(nifty:ptr()) -> nifty:ptr() | undef | {error, node_down}. 91 | pointer_of(X0) -> 92 | nifty_remotecall:call_remote(nifty, pointer_of, [X0]). 93 | 94 | -spec as_type(nifty:ptr(), nonempty_string()) -> nifty:ptr() | undef | {error, node_down}. 95 | as_type(X0,X1) -> 96 | nifty_remotecall:call_remote(nifty, as_type, [X0,X1]). 97 | 98 | -spec size_of(nonempty_string()) -> integer() | undef | {error, node_down}. 99 | size_of(X0) -> 100 | nifty_remotecall:call_remote(nifty, size_of, [X0]). 101 | 102 | -spec mem_write(binary() | list()) -> nifty:ptr() | {error, node_down}. 103 | mem_write(X0) -> 104 | nifty_remotecall:call_remote(nifty, mem_write, [X0]). 105 | 106 | -spec mem_write(nifty:ptr(), binary() | list()) -> nifty:ptr() | {error, node_down}. 107 | mem_write(X0,X1) -> 108 | nifty_remotecall:call_remote(nifty, mem_write, [X0,X1]). 109 | 110 | -spec mem_read (term(),term()) -> term() | {error, node_down}. 111 | mem_read(X0,X1) -> 112 | nifty_remotecall:call_remote(nifty, mem_read, [X0,X1]). 113 | 114 | -spec mem_alloc(non_neg_integer()) -> nifty:ptr() | {error, node_down}. 115 | mem_alloc(X0) -> 116 | nifty_remotecall:call_remote(nifty, mem_alloc, [X0]). 117 | 118 | -spec free(nifty:ptr()) -> 'ok' | {error, node_down}. 119 | free(X0) -> 120 | nifty_remotecall:call_remote(nifty, free, [X0]). 121 | 122 | -spec get_config() -> proplists:proplist() | {error, node_down}. 123 | get_config() -> 124 | nifty_remotecall:call_remote(nifty, get_config, []). 125 | 126 | -spec get_env() -> {integer(), nonempty_string()} | {error, node_down}. 127 | get_env() -> 128 | nifty_remotecall:call_remote(nifty, get_env, []). 129 | 130 | -spec get_types() -> dict:dict() | {error, node_down}. 131 | get_types() -> 132 | nifty_remotecall:call_remote(nifty, get_types, []). 133 | 134 | -spec enum_value(atom(), nonempty_string() | atom()) -> integer() | undef. 135 | enum_value(X0, X1) -> 136 | nifty_remotecall:call_remote(nifty, enum_value, [X0, X1]). 137 | 138 | -spec mem_copy(nifty:ptr(), nifty:ptr(), non_neg_integer()) -> ok. 139 | mem_copy(X0, X1, X2) -> 140 | nifty_remotecall:call_remote(nifty, mem_copy, [X0, X1, X2]). 141 | 142 | -spec array_new(nonempty_string(), non_neg_integer()) -> nifty:ptr(). 143 | array_new(X0, X1) -> 144 | nifty_remotecall:call_remote(nifty, array_new, [X0, X1]). 145 | 146 | -spec array_ith(nifty:ptr(), integer()) -> nifty:ptr(). 147 | array_ith(X0, X1) -> 148 | nifty_remotecall:call_remote(nifty, array_ith, [X0, X1]). 149 | 150 | -spec array_element(nifty:ptr(), integer()) -> nifty:cvalue(). 151 | array_element(X0, X1) -> 152 | nifty_remotecall:call_remote(nifty, array_element, [X0, X1]). 153 | 154 | -spec array_set(nifty:ptr(), term(), integer()) -> ok. 155 | array_set(X0, X1, X2) -> 156 | nifty_remotecall:call_remote(nifty, array_set, [X0, X1, X2]). 157 | 158 | -spec array_to_list(nifty:ptr(), non_neg_integer()) -> [nifty:cvalue()]. 159 | array_to_list(X0, X1) -> 160 | nifty_remotecall:call_remote(nifty, array_to_list, [X0, X1]). 161 | 162 | -spec list_to_array(list(), nonempty_string()) -> nifty:ptr(). 163 | list_to_array(X0, X1) -> 164 | nifty_remotecall:call_remote(nifty, list_to_array, [X0, X1]). 165 | 166 | -spec malloc(non_neg_integer()) -> nifty:ptr(). 167 | malloc(X0) -> 168 | mem_alloc(X0). 169 | -------------------------------------------------------------------------------- /src/nifty_filters.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2014-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_filters). 12 | 13 | -export([raw_include/1, 14 | raw_path/1, 15 | absname/1, 16 | getNth/2, 17 | resolved/2, 18 | reversed/1, 19 | fetch/2, 20 | fetch_keys/1, 21 | has_key/2, 22 | is_argument/1, 23 | is_return/1, 24 | is_field/1, 25 | is_input/1, 26 | is_output/1, 27 | is_array/1, 28 | 29 | array_size/1, 30 | array_type/1, 31 | 32 | norm_type/1, 33 | dereference_type/1, 34 | discard_const/1, 35 | discard_restrict/1, 36 | loopcounter/2, 37 | 38 | config_schedule_dirty/1, 39 | config_schedule_dirty/2, 40 | config_schedule_dirty_type/2, 41 | enum_aliases/1, 42 | clear_function_pointers/1]). 43 | 44 | -spec norm_type(string()) -> string(). 45 | norm_type(Type) -> 46 | case string:str(Type, "[") of 47 | 0 -> Type; 48 | S -> 49 | case string:str(Type, "]") of 50 | 0 -> Type; % error; 51 | E -> norm_type(string:substr(Type, 1, S-1) ++ "*" ++ string:substr(Type, E+1)) 52 | end 53 | end. 54 | 55 | -spec dereference_type(string()) -> string(). 56 | dereference_type(Type) -> 57 | NType = norm_type(Type), 58 | case string:str(NType, "*") of 59 | 0 -> NType; 60 | S -> string:strip(string:substr(NType, 1, S-1) ++ string:substr(NType, S+1)) 61 | end. 62 | 63 | -spec discard_const(string()) -> string(). 64 | discard_const(Type) -> 65 | string:join([Tok || Tok <- string:tokens(Type, " "), Tok =/= "const"], " "). 66 | 67 | -spec loopcounter(string(), string()) -> string(). 68 | loopcounter(Type, Name) -> 69 | NType = "#"++norm_type(Type)++"#", 70 | Type_ = string:join(string:tokens(NType, " "), "_"), 71 | Enclosed = string:join(string:tokens(Type_, "*"), "P"), 72 | "index_"++Name++"_"++string:substr(Enclosed, 2, length(Enclosed)-2). 73 | 74 | %%% special 75 | -spec raw_include(string()) -> string(). 76 | raw_include(Path) -> 77 | lists:last(filename:split(Path)). 78 | 79 | -spec raw_path(string()) -> string(). 80 | raw_path(Path) -> 81 | filename:dirname(Path). 82 | 83 | -spec absname(string()) -> string(). 84 | absname(Path) -> 85 | filename:absname(Path). 86 | 87 | %%% general 88 | -spec getNth(nonempty_list() | tuple(), pos_integer()) -> term(). 89 | getNth(I, N) when is_list(I) -> 90 | lists:nth(N, I); 91 | getNth(I, N) when is_tuple(I) -> 92 | lists:nth(N, tuple_to_list(I)). 93 | 94 | -spec reversed([X]) -> [X]. 95 | reversed(L) -> 96 | lists:reverse(L). 97 | 98 | -spec discard_restrict(string()) -> string(). 99 | discard_restrict(Type) -> 100 | case string:str(Type, "restrict") of 101 | 0 -> 102 | Type; 103 | P -> 104 | string:strip(string:substr(Type, 1, P-1) ++ string:substr(Type, P+length("restrict"))) 105 | end. 106 | 107 | -spec resolved(string(), dict:dict()) -> string(). 108 | resolved(Type, Types) -> 109 | nifty_types:resolve_type(Type, Types). 110 | 111 | %%% dict 112 | -spec fetch(dict:dict(), string()) -> term(). 113 | fetch(Dict, Key) -> 114 | dict:fetch(Key, Dict). 115 | 116 | -spec fetch_keys(dict:dict()) -> list(). 117 | fetch_keys(Dict) -> 118 | dict:fetch_keys(Dict). 119 | 120 | -spec has_key(dict:dict(), string()) -> boolean(). 121 | has_key(Dict, Key) -> 122 | dict:is_key(Key, Dict). 123 | 124 | %%% symbol table entries 125 | -spec is_argument(string()) -> boolean(). 126 | is_argument(Arg) -> 127 | getNth(Arg, 1) =:= argument. 128 | 129 | -spec is_return(string()) -> boolean(). 130 | is_return(Arg) -> 131 | getNth(Arg, 1) =:= return. 132 | 133 | -spec is_field(string()) -> boolean(). 134 | is_field(Arg) -> 135 | getNth(Arg, 1) =:= field. 136 | 137 | -spec is_input(string()) -> boolean(). 138 | is_input(Arg) -> 139 | (getNth(Arg, 4) =:= input) orelse (getNth(Arg, 4) =:= inoutput). 140 | 141 | -spec is_output(string()) -> boolean(). 142 | is_output(Arg) -> 143 | (getNth(Arg, 4) =:= output) orelse (getNth(Arg, 4) =:= inoutput). 144 | 145 | -spec is_array(string()) -> boolean(). 146 | is_array(Arg) -> 147 | (is_list(Arg) andalso (string:str(Arg, "[") =:= 1 andalso length(Arg) > 2)). 148 | 149 | -spec array_type(string()) -> string(). 150 | array_type(Type) -> 151 | Token = string:tokens(Type, " "), 152 | array_type_build(Token, []). 153 | 154 | array_type_build([H|T], Acc) -> 155 | case string:str(H, "[") =:= 1 andalso lists:last(H) =:= $] of 156 | true -> 157 | string:strip(Acc); 158 | false -> 159 | array_type_build(T, Acc++" "++H) 160 | end. 161 | 162 | -spec array_size({base | userdef, [string()]}) -> integer(). 163 | array_size({_, Typedef}) -> 164 | array_size(Typedef, 1). 165 | 166 | array_size([H|T], Acc) -> 167 | case string:str(H, "[") =:= 1 andalso lists:last(H) =:= $] of 168 | true -> 169 | {Size,[]} = string:to_integer(string:substr(H, 2, length(H)-2)), 170 | array_size(T, Acc*Size); 171 | false -> 172 | Acc 173 | end. 174 | 175 | -spec config_schedule_dirty(nifty:options()) -> boolean(). 176 | config_schedule_dirty(Options) -> 177 | case proplists:get_value(nifty, Options) of 178 | undefined -> 179 | false; 180 | NiftyOptions -> 181 | case proplists:get_value(schedule_dirty, NiftyOptions) of 182 | undefined -> 183 | %% check the all functions 184 | FOpts = proplists:get_value(functions_options, NiftyOptions, []), 185 | lists:foldl(fun ({_, Opts}, Acc) -> 186 | Acc orelse lists:member(schedule_dirty_cpu, Opts) 187 | orelse lists:member(schedule_dirty_io, Opts) 188 | end, false, FOpts); 189 | false -> 190 | false; 191 | _ -> 192 | true 193 | end 194 | end. 195 | 196 | -spec config_schedule_dirty(nifty:options(), string()) -> boolean(). 197 | config_schedule_dirty(Options, FN) -> 198 | case proplists:get_value(nifty, Options) of 199 | undefined -> 200 | false; 201 | NiftyOptions -> 202 | case proplists:get_value(schedule_dirty, NiftyOptions) of 203 | undefined -> 204 | %% check the functions 205 | FOpts = get_function_options(NiftyOptions, FN), 206 | lists:member(schedule_dirty_cpu, FOpts) orelse 207 | lists:member(schedule_dirty_io, FOpts); 208 | false -> 209 | false; 210 | _ -> 211 | true 212 | end 213 | end. 214 | 215 | -spec get_function_options(nifty:options(), string()) -> proplists:proplist(). 216 | get_function_options(NiftyOptions, FN) -> 217 | case proplists:get_value(functions_options, NiftyOptions) of 218 | undefined -> 219 | []; 220 | Options when is_list(Options) -> 221 | proplists:get_value(FN, Options, []) 222 | end. 223 | 224 | -spec config_schedule_dirty_type(nifty:options(), string()) -> string(). 225 | config_schedule_dirty_type(Options, FN) -> 226 | %% should always succeed: 227 | NiftyOptions = proplists:get_value(nifty, Options), 228 | FOpts = get_function_options(NiftyOptions, FN), 229 | case lists:member(schedule_dirty_io, FOpts) of 230 | true -> "ERL_NIF_DIRTY_JOB_IO_BOUND"; 231 | _ -> "ERL_NIF_DIRTY_JOB_CPU_BOUND" 232 | end. 233 | 234 | -spec enum_aliases(dict:dict()) -> nonempty_string(). 235 | enum_aliases(Constructors) -> 236 | enum_aliases(dict:fetch_keys(Constructors), Constructors, []). 237 | 238 | enum_aliases([], _, Acc) -> 239 | io_lib:format("~p", [Acc]); 240 | enum_aliases([H|T], Constructors, Acc) -> 241 | case H of 242 | {enum, _} -> 243 | Aliases = dict:fetch(H, Constructors), 244 | enum_aliases(T, Constructors, Acc ++ Aliases); 245 | _ -> 246 | enum_aliases(T, Constructors, Acc) 247 | end. 248 | 249 | -spec clear_function_pointers(dict:dict()) -> dict:dict(). 250 | clear_function_pointers(Types) -> 251 | Pred = fun (Type, _) -> string:str(Type, "(") =:= 0 end, 252 | dict:filter(Pred, Types). 253 | -------------------------------------------------------------------------------- /src/nifty_utils.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2014-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_utils). 12 | -export([expand/1, 13 | new_config/0, 14 | add_sources/2, 15 | add_cflags/2, 16 | add_ldflags/2, 17 | merge_nif_spec/2]). 18 | 19 | -type env() :: {'env', [{nonempty_string(), string()}]}. 20 | -type target_options() :: [env()]. 21 | -type arch() :: nonempty_string(). 22 | -type target_name() :: nonempty_string(). 23 | -type source() :: nonempty_string(). 24 | -type target() :: {arch(), target_name(), [source()]} 25 | | {arch(), target_name(), [source()], target_options()}. 26 | %%-type port_specs() :: {'port_specs', [target()]}. 27 | -type config() :: proplists:proplist(). % port_specs() is an element here... 28 | 29 | %% @doc Merges two configurations 30 | -spec merge_nif_spec(config(), target()) -> config(). 31 | merge_nif_spec(Config, {".*", "$NIF", Sources, [{env, Env}]} = Spec) -> 32 | case proplists:get_value(port_specs, Config) of 33 | undefined -> 34 | [{port_specs, [Spec]} | Config]; 35 | Specs -> 36 | NewSpecs = case get_nifspec(Specs) of 37 | {".*", "$NIF", OldSources} -> 38 | store_nifspec({".*", "$NIF", OldSources ++ Sources, [{env, Env}]}, Specs); 39 | {".*", "$NIF", OldSources, [{env, OldEnv}]} -> 40 | store_nifspec({".*", "$NIF", OldSources ++ Sources, [{env, OldEnv ++ Env}]}, Specs); 41 | undefined -> 42 | store_nifspec({".*", "$NIF", Sources, [{env, []}] }, Specs) 43 | end, 44 | [{port_specs, NewSpecs} | proplists:delete(port_specs, Config)] 45 | end. 46 | 47 | get_nifspec([]) -> undefined; 48 | get_nifspec([H|T]) -> 49 | case H of 50 | {".*", "$NIF", _} -> H; 51 | {".*", "$NIF", _, _} -> H; 52 | _ -> get_nifspec(T) 53 | end. 54 | 55 | store_nifspec(NifSpec, Specs) -> 56 | store_nifspec(Specs, NifSpec, []). 57 | 58 | store_nifspec([], _, Acc) -> Acc; 59 | store_nifspec([H|T], Spec, Acc) -> 60 | case H of 61 | {".*", "$NIF", _} -> store_nifspec(T, Spec, [Spec|Acc]); 62 | {".*", "$NIF", _, _} -> store_nifspec(T, Spec, [Spec|Acc]); 63 | _ -> store_nifspec(T, Spec, [H|Acc]) 64 | end. 65 | 66 | %% @doc Returns an empty configuration 67 | -spec new_config() -> config(). 68 | new_config() -> []. 69 | 70 | %% @doc Adds the sources S to the NIF module 71 | -spec add_sources([source()], config()) -> config(). 72 | add_sources(S, C) -> 73 | merge_nif_spec(C, {".*", "$NIF", S, [{env, []}]}). 74 | 75 | %% @doc Adds the compile flags F to the NIF module 76 | -spec add_cflags(string(), config()) -> config(). 77 | add_cflags(F, C) -> 78 | merge_nif_spec(C, {".*", "$NIF", [], [{env, [{"CFLAGS", "$CFLAGS "++F}]}]}). 79 | 80 | %% @doc Adds link flags F to the NIF module 81 | -spec add_ldflags(string(), config()) -> config(). 82 | add_ldflags(F, C) -> 83 | merge_nif_spec(C, {".*", "$NIF", [], [{env, [{"LDFLAGS", "$LDFLAGS "++F}]}]}). 84 | 85 | %% @doc Returns Path with all environment variables 86 | %% expanded 87 | -spec expand(string()) -> string(). 88 | expand(Path) -> 89 | S = lists:foldr(fun(A, Acc) -> A ++ " " ++ Acc end, [], tokenize(Path)), 90 | string:strip(S). 91 | 92 | -define(IS_WHITESPACE(Char), ((Char) =:= $\s orelse (Char) =:= $\t orelse (Char) =:= $\n orelse (Char) =:= $\r)). 93 | 94 | -spec tokenize(CmdLine :: string()) -> [nonempty_string()]. 95 | tokenize(CmdLine) -> 96 | tokenize(CmdLine, [], []). 97 | 98 | -spec tokenize(CmdLine :: string(), Acc :: [string()], ArgAcc :: string()) -> [string()]. 99 | tokenize([Sep | Tail], Acc, ArgAcc) when ?IS_WHITESPACE(Sep) -> 100 | NewAcc = case ArgAcc of 101 | [_ | _] -> 102 | %% Found separator: add to the list of arguments. 103 | [lists:reverse(ArgAcc) | Acc]; 104 | [] -> 105 | %% Found separator with no accumulated argument; discard it. 106 | Acc 107 | end, 108 | tokenize(Tail, NewAcc, []); 109 | tokenize([QuotationMark | Tail], Acc, ArgAcc) when QuotationMark =:= $"; QuotationMark =:= $' -> 110 | %% Quoted argument (might contain spaces, tabs, etc.) 111 | tokenize_quoted_arg(QuotationMark, Tail, Acc, ArgAcc); 112 | tokenize([Char | _Tail] = CmdLine, Acc, ArgAcc) when Char =:= $$; Char =:= $% -> 113 | %% Unix and Windows environment variable expansion: ${VAR}; $VAR; %VAR% 114 | {NewCmdLine, Var} = expand_env_var(CmdLine), 115 | tokenize(NewCmdLine, Acc, lists:reverse(Var, ArgAcc)); 116 | tokenize([$\\, Char | Tail], Acc, ArgAcc) -> 117 | %% Escaped char. 118 | tokenize(Tail, Acc, [Char | ArgAcc]); 119 | tokenize([Char | Tail], Acc, ArgAcc) -> 120 | tokenize(Tail, Acc, [Char | ArgAcc]); 121 | tokenize([], Acc, []) -> 122 | lists:reverse(Acc); 123 | tokenize([], Acc, ArgAcc) -> 124 | lists:reverse([lists:reverse(ArgAcc) | Acc]). 125 | 126 | -spec tokenize_quoted_arg(QuotationMark :: char(), CmdLine :: string(), Acc :: [string()], ArgAcc :: string()) -> [string()]. 127 | tokenize_quoted_arg(QuotationMark, [QuotationMark | Tail], Acc, ArgAcc) -> 128 | %% End of quoted argument 129 | tokenize(Tail, Acc, ArgAcc); 130 | tokenize_quoted_arg(QuotationMark, [$\\, Char | Tail], Acc, ArgAcc) -> 131 | %% Escaped char. 132 | tokenize_quoted_arg(QuotationMark, Tail, Acc, [Char | ArgAcc]); 133 | tokenize_quoted_arg($" = QuotationMark, [Char | _Tail] = CmdLine, Acc, ArgAcc) when Char =:= $$; Char =:= $% -> 134 | %% Unix and Windows environment variable expansion (only for double-quoted arguments): ${VAR}; $VAR; %VAR% 135 | {NewCmdLine, Var} = expand_env_var(CmdLine), 136 | tokenize_quoted_arg(QuotationMark, NewCmdLine, Acc, lists:reverse(Var, ArgAcc)); 137 | tokenize_quoted_arg(QuotationMark, [Char | Tail], Acc, ArgAcc) -> 138 | tokenize_quoted_arg(QuotationMark, Tail, Acc, [Char | ArgAcc]); 139 | tokenize_quoted_arg(_QuotationMark, CmdLine, Acc, ArgAcc) -> 140 | tokenize(CmdLine, Acc, ArgAcc). 141 | 142 | -spec expand_env_var(CmdLine :: nonempty_string()) -> {string(), string()}. 143 | expand_env_var(CmdLine) -> 144 | case CmdLine of 145 | "${" ++ Tail -> 146 | expand_env_var("${", $}, Tail, []); 147 | "$" ++ Tail -> 148 | expand_env_var("$", Tail, []); 149 | "%" ++ Tail -> 150 | expand_env_var("%", $%, Tail, []) 151 | end. 152 | 153 | -spec expand_env_var(Prefix :: string(), EndMark :: char(), CmdLine :: string(), Acc :: string()) -> {string(), string()}. 154 | expand_env_var(Prefix, EndMark, [Char | Tail], Acc) 155 | when (Char >= $A andalso Char =< $Z) orelse (Char >= $a andalso Char =< $z) orelse 156 | (Char >= $0 andalso Char =< $9) orelse (Char =:= $_) -> 157 | expand_env_var(Prefix, EndMark, Tail, [Char | Acc]); 158 | expand_env_var(Prefix, EndMark, [EndMark | Tail], Acc) -> 159 | {Tail, get_env_var(Prefix, [EndMark], Acc)}; 160 | expand_env_var(Prefix, _EndMark, CmdLine, Acc) -> 161 | {CmdLine, Prefix ++ lists:reverse(Acc)}. 162 | 163 | -spec expand_env_var(Prefix :: string(), CmdLine :: string(), Acc :: string()) -> {string(), string()}. 164 | expand_env_var(Prefix, [Char | Tail], Acc) 165 | when (Char >= $A andalso Char =< $Z) orelse (Char >= $a andalso Char =< $z) orelse 166 | (Char >= $0 andalso Char =< $9) orelse (Char =:= $_) -> 167 | expand_env_var(Prefix, Tail, [Char | Acc]); 168 | expand_env_var(Prefix, CmdLine, Acc) -> 169 | {CmdLine, get_env_var(Prefix, "", Acc)}. 170 | 171 | -spec get_env_var(Prefix :: string(), Suffix :: string(), Acc :: string()) -> string(). 172 | get_env_var(Prefix, Suffix, [_ | _] = Acc) -> 173 | Name = lists:reverse(Acc), 174 | %% Only expand valid/existing variables. 175 | case os:getenv(Name) of 176 | false -> Prefix ++ Name ++ Suffix; 177 | Value -> Value 178 | end; 179 | get_env_var(Prefix, Suffix, []) -> 180 | Prefix ++ Suffix. 181 | -------------------------------------------------------------------------------- /src/nifty_clangparse.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2015-2016, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty_clangparse). 12 | 13 | -export([parse/1, build_type_table/2]). 14 | 15 | -export_type([parser_output/0, 16 | func_location/0, 17 | symbol_table/0, 18 | ctype/0, 19 | types/0, 20 | type_table/0, 21 | constr_table/0]). 22 | 23 | -type path() :: string(). 24 | -type cname() :: nonempty_string(). 25 | -type ctype() :: nonempty_string(). 26 | -type index() :: integer(). 27 | -type field() :: {'field', cname(), ctype(), index()}. 28 | -type enum_decl() :: {cname(), integer()}. 29 | -type types() :: [ctype()]. 30 | -type constructor_type() :: 'struct'. 31 | -type ctypedef() :: {'base', [string()]} | 32 | {'typedef', ctype()} | 33 | {'userdef' , [string() | {constructor_type(), nonempty_string()}]}. 34 | -type func_location() :: dict:dict(path(), cname()). 35 | -type symbol_table() :: dict:dict(cname(), {'return', ctype()} | {'argument', index(), ctype()}). 36 | -type type_table() :: dict:dict(ctype(), ctypedef()). 37 | -type constr_table() :: dict:dict({'struct' | 'typedef' | 'enum', cname()}, ctype() | [field()] | [enum_decl()]). 38 | -type parser_output() :: {func_location(), symbol_table(), types(), constr_table()}. 39 | 40 | -define(BASE_TYPES, ["char", "int", "float", "double", "void"]). 41 | -define(SPECIFIER, ["signed", "unsigned", "short", "long"]). 42 | 43 | -on_load(init/0). 44 | 45 | init() -> %% loading code from jiffy 46 | PrivDir = case code:priv_dir(?MODULE) of 47 | {error, _} -> 48 | EbinDir = filename:dirname(code:which(?MODULE)), 49 | AppPath = filename:dirname(EbinDir), 50 | filename:join(AppPath, "priv"); 51 | Path -> 52 | Path 53 | end, 54 | erlang:load_nif(filename:join(PrivDir, "nifty_clangparse"), 0). 55 | 56 | cparse(_) -> 57 | erlang:nif_error(nif_library_not_loaded). 58 | 59 | %% @doc Takes clang compiler arguments, and returns functions locations, symbol table, types and constructor table 60 | -spec parse([string()]) -> parser_output() | {error, fail}. 61 | parse(Args) -> 62 | case cparse(Args) of 63 | fail -> 64 | {error, fail}; 65 | {FL, ST, T, CT} -> 66 | {dict:from_list(FL), dict:from_list(ST), T, dict:from_list(CT)} 67 | end. 68 | 69 | %% @doc building the type table from the raw parser output 70 | -spec build_type_table(types(), constr_table()) -> type_table(). 71 | build_type_table(Types, Constr) -> 72 | Constr_Types = fill_constructed(Constr, dict:new()), 73 | Used_Types = fill_types(Types, Constr_Types), 74 | fill_type_table(Used_Types). 75 | 76 | fill_constructed(Constr, Types) -> 77 | fill_constructed(dict:fetch_keys(Constr), Constr, Types). 78 | 79 | fill_constructed([], _, Types) -> 80 | Types; 81 | fill_constructed([H|T], Constr, Types) -> 82 | NewTypes = case H of 83 | {typedef, Alias} -> 84 | Type = dict:fetch(H, Constr), 85 | case is_fptr(Type) of 86 | true -> 87 | dict:store(Alias, {typedef, "void *"}, Types); 88 | false -> 89 | dict:store(Alias, {typedef, Type}, Types) 90 | end; 91 | {struct, Name} -> 92 | D = dict:store("struct " ++ Name, {userdef, [H]}, Types), 93 | dict:store("struct " ++ Name ++ " *", {userdef, ["*", H]}, D); 94 | {union, Name} -> 95 | D = dict:store("union " ++ Name, {userdef, [H]}, Types), 96 | dict:store("union " ++ Name ++ " *", {userdef, ["*", H]}, D); 97 | {enum, Name} -> 98 | dict:store("enum " ++ Name, {typedef, "long long"}, Types) 99 | end, 100 | fill_constructed(T, Constr, NewTypes). 101 | 102 | fill_types([], Types) -> 103 | Types; 104 | fill_types([Type|T], Types) -> 105 | fill_types(T, build_type_entry(Types, Type)). 106 | 107 | fill_type_table(Types) -> 108 | fill_type_table(Types, dict:fetch_keys(Types)). 109 | 110 | fill_type_table(Types, []) -> Types; 111 | fill_type_table(Types, [Type|TypeNames]) -> 112 | {Kind, L} = dict:fetch(Type, Types), 113 | case Kind of 114 | base -> fill_type_table(Types, TypeNames); 115 | typedef -> fill_type_table(Types, TypeNames); 116 | userdef -> 117 | [H|T] = L, 118 | case H of 119 | {_,_} -> 120 | fill_type_table(Types, TypeNames); 121 | _ -> 122 | case (H =:= "*") orelse string:str(H, "[")>0 of 123 | true -> 124 | [N|_] = T, 125 | case N of 126 | {_,_} -> 127 | fill_type_table(Types, TypeNames); 128 | _ -> 129 | [P|Token] = lists:reverse(string:tokens(Type, " ")), 130 | NewP = string:substr(P, 1, length(P) - length(H)), 131 | NType = string:strip(string:join(lists:reverse(Token)++[NewP], " ")), 132 | case dict:is_key(NType, Types) of 133 | true -> fill_type_table(Types, TypeNames); 134 | false -> fill_type_table(dict:store(NType, {Kind, T}, Types), [NType|TypeNames]) 135 | end 136 | end; 137 | false -> fill_type_table(Types, TypeNames) 138 | end 139 | end 140 | end. 141 | 142 | count_in_list(L, E) -> 143 | count_in_list(L,E,0). 144 | 145 | count_in_list([], _, Acc) -> Acc; 146 | count_in_list([H|T], E, Acc) -> 147 | case H =:= E of 148 | true -> count_in_list(T, E, Acc+1); 149 | false -> count_in_list(T, E, Acc) 150 | end. 151 | 152 | 153 | simplify_specifiers(Specifiers) -> 154 | LSpec = case count_in_list(Specifiers, "long") of 155 | 0 -> 156 | case lists:member("short", Specifiers) of 157 | true -> ["short"]; 158 | false -> ["none"] 159 | end; 160 | 1 -> ["long"]; 161 | _ -> ["longlong"] 162 | end, 163 | case count_in_list(Specifiers, "unsigned") of 164 | 0 -> ["signed"|LSpec]; 165 | _ -> ["unsigned"|LSpec] 166 | end. 167 | 168 | parse_type(Token) -> 169 | parse_type(Token, [], none). 170 | 171 | parse_type([], TypeDef, none) -> parse_type(["int"], TypeDef, none); 172 | parse_type([], TypeDef, Kind) -> {Kind, TypeDef}; 173 | parse_type([E|T], TypeDef, Kind) -> 174 | case E of 175 | %% special cases 176 | "struct" -> 177 | [StructName|TT] = T, 178 | parse_type(TT, [{struct, StructName}|TypeDef], userdef); 179 | _ -> 180 | %% simple type 181 | case lists:member(E, ?BASE_TYPES) of 182 | true -> parse_type(T, [E|simplify_specifiers(TypeDef)], base); 183 | false -> 184 | case lists:member(E, ?SPECIFIER) of 185 | true -> parse_type(T, [E|TypeDef], none); 186 | false -> 187 | case ((E =:= "*") orelse lists:member($[, E)) of 188 | true -> 189 | case Kind of 190 | none -> parse_type(["int"|[E|T]], TypeDef, base); 191 | _ -> parse_type(T, [E|TypeDef], Kind) 192 | end; 193 | false -> 194 | %% user defined type 195 | parse_type(T, [E|TypeDef], userdef) 196 | end 197 | end 198 | end 199 | end. 200 | 201 | type_extend(Type) -> 202 | type_extend(Type, []). 203 | 204 | type_extend([], Acc) -> Acc; 205 | type_extend([H|T], Acc) -> 206 | case [H] of 207 | "*" -> type_extend(T, Acc++" * "); 208 | "[" -> type_extend(T, Acc++" ["); 209 | C -> type_extend(T, Acc++C) 210 | end. 211 | 212 | norm_type(Type) -> 213 | case string:str(Type, "[]") of 214 | 0 -> Type; 215 | P -> string:substr(Type, 1, P-1)++"*" 216 | end. 217 | 218 | is_fptr(Type) -> 219 | string:str(Type, "(") =/= 0. 220 | 221 | 222 | build_type_entry(TypeTable, Type) -> 223 | NType = norm_type(Type), 224 | case dict:is_key(NType, TypeTable) of 225 | true -> 226 | TypeTable; 227 | false-> 228 | case is_fptr(NType) of 229 | true -> 230 | TDef_Table = dict:store(NType, {typedef, "void *"}, TypeTable), 231 | build_type_entry(TDef_Table, "void *"); 232 | false -> 233 | Def = parse_type(string:tokens(type_extend(NType), " ")), 234 | dict:store(NType, Def, TypeTable) 235 | end 236 | end. 237 | -------------------------------------------------------------------------------- /test/nifty_test.erl: -------------------------------------------------------------------------------- 1 | %%% ------------------------------------------------------------------- 2 | %%% Copyright (c) 2015-2018, Andreas Löscher 3 | %%% and Konstantinos Sagonas 4 | %%% All rights reserved. 5 | %%% 6 | %%% This file is distributed under the Simplified BSD License. 7 | %%% Details can be found in the LICENSE file. 8 | %%% ------------------------------------------------------------------- 9 | 10 | -module(nifty_test). 11 | 12 | -include_lib("eunit/include/eunit.hrl"). 13 | 14 | %% suppress warning for test/0, a generated function automatically exported 15 | -spec test() -> term(). 16 | 17 | -define(TIMEOUT, 600). 18 | -define(OPTS(F), [{port_specs, [{".*", "$NIF", [F]}]}]). 19 | 20 | -spec compile_builtin() -> ok. 21 | compile_builtin() -> 22 | ?_assertEqual(ok, nifty:compile("test/cfiles/builtin_types.h", 23 | nt_builtin, 24 | ?OPTS("test/cfiles/builtin_types.c"))). 25 | 26 | -spec call_functions_builtin() -> term(). 27 | call_functions_builtin() -> 28 | [?_assertEqual(1, nt_builtin:f1(1)), 29 | ?_assertEqual(1, nt_builtin:f2(1)), 30 | ?_assertEqual(1, nt_builtin:f3(1)), 31 | ?_assertEqual(1, nt_builtin:f4(1)), 32 | ?_assertEqual(1, nt_builtin:f5(1)), 33 | ?_assertEqual(1, nt_builtin:f6(1)), 34 | ?_assertEqual(1, nt_builtin:f7(1)), 35 | ?_assertEqual(1, nt_builtin:f8(1)), 36 | ?_assertEqual(1.0, nt_builtin:f9(1.0)), 37 | ?_assertEqual(1.0, nt_builtin:f10(1.0)), 38 | ?_assertEqual({0, "nt_builtin.void *"}, nt_builtin:f11({0, "void *"})), 39 | ?_assertEqual(ok, fun () -> 40 | P = nifty:mem_alloc(10), 41 | {_, _} = nt_builtin:f12(P), 42 | nifty:free(P) 43 | end())]. 44 | 45 | -spec builtin_test_() -> term(). 46 | builtin_test_() -> 47 | {timeout, ?TIMEOUT, [compile_builtin(), 48 | call_functions_builtin()]}. 49 | 50 | %% -spec call_functions_builtin_remote() -> ok. 51 | %% call_functions_builtin_remote() -> 52 | %% 1 = nt_builtin_remote:f1(1), 53 | %% 1 = nt_builtin_remote:f2(1), 54 | %% 1 = nt_builtin_remote:f3(1), 55 | %% 1 = nt_builtin_remote:f4(1), 56 | %% 1 = nt_builtin_remote:f5(1), 57 | %% 1 = nt_builtin_remote:f6(1), 58 | %% 1 = nt_builtin_remote:f7(1), 59 | %% 1 = nt_builtin_remote:f8(1), 60 | %% 1.0 = nt_builtin_remote:f9(1.0), 61 | %% 1.0 = nt_builtin_remote:f10(1.0), 62 | %% P = nifty:pointer(), 63 | %% nt_builtin_remote:f11(P), 64 | %% nifty:free(P), 65 | %% P2 = nifty:mem_alloc(10), 66 | %% nt_builtin_remote:f11(P2), 67 | %% nifty:free(P2), 68 | %% ok. 69 | 70 | %% -spec builtin_remote_test() -> ok. 71 | %% builtin_remote_test() -> 72 | %% ok = nt_builtin_remote:start(), 73 | %% ok = call_functions_builtin_remote(), 74 | %% ok = nt_builtin_remote:stop(). 75 | 76 | -spec compile_arguments() -> term(). 77 | compile_arguments() -> 78 | ?_assertEqual(ok, nifty:compile("test/cfiles/arguments.h", 79 | nt_arguments, 80 | ?OPTS("test/cfiles/arguments.c"))). 81 | 82 | -spec call_functions_arguments() -> term(). 83 | call_functions_arguments() -> 84 | [?_assertEqual(ok, nt_arguments:f1()), 85 | ?_assertEqual(0, nt_arguments:f3(0,0,0,0)), 86 | ?_assertEqual(ok, nt_arguments:f2()), 87 | ?_assertEqual(1, nt_arguments:f3(0,0,0,0)), 88 | ?_assertEqual(10, nt_arguments:f4(1,2,3,4))]. 89 | 90 | -spec arguments_test_() -> term(). 91 | arguments_test_() -> 92 | {timeout, ?TIMEOUT, [compile_arguments(), 93 | call_functions_arguments()]}. 94 | 95 | -spec compile_structs() -> term(). 96 | compile_structs() -> 97 | ?_assertEqual(ok, nifty:compile("test/cfiles/structs.h", nt_structs, [])). 98 | 99 | -spec call_functions_structs() -> ok. 100 | call_functions_structs() -> 101 | [?_assertMatch({_,_,_,_,_}, nifty:dereference(nt_structs:record_to_erlptr(nt_structs:'__nifty__new'("struct s1")))), 102 | ?_assertMatch({_,_,_}, nifty:dereference(nt_structs:record_to_erlptr(nt_structs:'__nifty__new'("struct s2")))), 103 | ?_assertMatch({_,_,_}, nifty:dereference(nt_structs:record_to_erlptr(nt_structs:'__nifty__new'("struct s3")))), 104 | ?_assertMatch({_,_,_}, nifty:dereference(nt_structs:record_to_erlptr(nt_structs:'__nifty__new'("struct s4")))), 105 | ?_assertEqual({'struct s4', 0.5, 10}, nifty:dereference(nifty:pointer_of({'struct s4', 0.5, 10}, "nt_structs.struct s4")))]. 106 | 107 | -spec structs_test_() -> term(). 108 | structs_test_() -> 109 | {timeout, ?TIMEOUT, [compile_structs(), 110 | call_functions_structs()]}. 111 | 112 | -spec compile_unions() -> term(). 113 | compile_unions() -> 114 | ?_assertEqual(ok, nifty:compile("test/cfiles/unions.h", nt_unions, 115 | nifty_utils:add_sources(["test/cfiles/unions.c"], []))). 116 | 117 | -spec call_functions_unions() -> ok. 118 | call_functions_unions() -> 119 | [?_assertEqual(42, fun () -> 120 | U = {'union _u', 42, undefined}, 121 | nt_unions:check_i(U) 122 | end()), 123 | ?_assertNotEqual(42, fun () -> 124 | U = {'union _u', 42, 0.0}, 125 | nt_unions:check_i(U) 126 | end()), 127 | ?_assert(fun() -> 128 | S = {'struct _s', {'union _u', undefined, 12.42}, 100}, 129 | abs(nt_unions:check_sf(S)-12.42) < 0.0001 130 | end())]. 131 | 132 | -spec unions_test_() -> term(). 133 | unions_test_() -> 134 | {timeout, ?TIMEOUT, [compile_unions(), 135 | call_functions_unions()]}. 136 | 137 | -spec compile_proxy() -> term(). 138 | compile_proxy() -> 139 | ?_assertEqual(ok, nifty:compile("test/cfiles/proxy_header.h", 140 | nt_proxy, 141 | nifty_utils:add_sources( 142 | ["test/cfiles/proxy_header.c"], 143 | nifty_utils:add_cflags("-Itest/cfiles", [])))). 144 | 145 | -spec call_functions_proxy() -> term(). 146 | call_functions_proxy() -> 147 | [?_assertMatch({0, _}, nt_proxy:fproxy({0, "void *"})), 148 | ?_assertEqual('none', proplists:lookup(strcmp, nt_proxy:module_info(exports)))]. 149 | 150 | -spec proxy_test_() -> term(). 151 | proxy_test_() -> 152 | {timeout, ?TIMEOUT, [compile_proxy(), 153 | call_functions_proxy()]}. 154 | 155 | -spec fptr_test_() -> term(). 156 | fptr_test_() -> 157 | {timeout, ?TIMEOUT, 158 | ?_assertEqual(ok, nifty:compile("test/cfiles/fptr.h", nt_fptr, []))}. 159 | 160 | -spec compile_array() -> term(). 161 | compile_array() -> 162 | ?_assertEqual(ok, nifty:compile( 163 | "test/cfiles/array.h", nt_array, 164 | nifty_utils:add_sources(["test/cfiles/array.c"], []))). 165 | 166 | -spec call_functions_array() -> ok. 167 | call_functions_array() -> 168 | [?_assertEqual(10, nt_array:sumarray(nifty:mem_write([1,0,0,0,1,0,0,0,1,0,0,0, 169 | 1,0,0,0,1,0,0,0,1,0,0,0, 170 | 1,0,0,0,1,0,0,0,1,0,0,0, 171 | 1,0,0,0]))), 172 | ?_assertEqual(20, fun () -> 173 | B = [1,1,1,1,1,1,1,1,1,1], 174 | Rec = {'struct array_st', nifty:mem_write(B), 0, nifty:mem_write(B)}, 175 | Ptr = nifty:pointer_of(Rec, "nt_array.struct array_st"), 176 | nt_array:sumstruct_array(Ptr) 177 | end())]. 178 | 179 | -spec array_test_() -> term(). 180 | array_test_() -> 181 | {timeout, ?TIMEOUT, [compile_array(), 182 | call_functions_array()]}. 183 | 184 | -spec compile_tut2() -> term(). 185 | compile_tut2() -> 186 | ?_assertEqual(ok, nifty:compile("test/cfiles/answer.h", 187 | nt_tut2, 188 | nifty_utils:add_sources( 189 | ["test/cfiles/answer.c"], 190 | nifty_utils:add_cflags( 191 | "-Itest/cfiles", [])))). 192 | 193 | -spec call_tut2() -> term(). 194 | call_tut2() -> 195 | ?_assertEqual(42, nt_tut2:life_universe_and_everything()). 196 | 197 | -spec tut2_test_() -> term(). 198 | tut2_test_() -> 199 | {timeout, ?TIMEOUT, [compile_tut2(), 200 | call_tut2()]}. 201 | 202 | -spec compile_dereference_regression() -> term(). 203 | compile_dereference_regression() -> 204 | ?_assertEqual(ok, nifty:compile("test/cfiles/dereference_regression.h", 205 | dereference_regression, 206 | nifty_utils:add_sources( 207 | ["test/cfiles/dereference_regression.c"], 208 | nifty_utils:add_cflags("-Itest/cfiles", [])))). 209 | 210 | -spec call_dereference_regression() -> term(). 211 | call_dereference_regression() -> 212 | ?_assert(begin 213 | P = nifty:pointer("dereference_regression.struct s"), 214 | PP = nifty:pointer_of(P), 215 | P =:= nifty:dereference(PP) 216 | end). 217 | 218 | -spec dereference_regression_test_() -> term(). 219 | dereference_regression_test_() -> 220 | {timeout, ?TIMEOUT, [compile_dereference_regression(), 221 | call_dereference_regression()]}. 222 | 223 | -spec enum_test_() -> term(). 224 | enum_test_() -> 225 | {timeout, ?TIMEOUT, 226 | [compile_enum(), 227 | call_enum(), 228 | check_enum()]}. 229 | 230 | compile_enum() -> 231 | ?_assertEqual(ok, nifty:compile("test/cfiles/enums.h", nt_enums, [])). 232 | 233 | call_enum() -> 234 | ?_assertEqual(1, nt_enums:f1(1,2)). 235 | 236 | check_enum() -> 237 | ?_assertEqual(100, nifty:enum_value(nt_enums, "VALUE6")). 238 | 239 | -spec compile_arguments_dirty() -> term(). 240 | compile_arguments_dirty() -> 241 | Opts = nifty_utils:add_sources(["test/cfiles/arguments.c"], []) 242 | ++ [{nifty, [schedule_dirty, {function_options, [{"f2", [schedule_dirty_io]}]}]}], 243 | ?_assertEqual(ok, nifty:compile("test/cfiles/arguments.h", nt_arguments_dirty, Opts)). 244 | 245 | -spec call_functions_arguments_dirt() -> term(). 246 | call_functions_arguments_dirt() -> 247 | [?_assertEqual(ok, nt_arguments_dirty:f1()), 248 | ?_assertEqual(0, nt_arguments_dirty:f3(0,0,0,0)), 249 | ?_assertEqual(ok, nt_arguments_dirty:f2()), 250 | ?_assertEqual(1, nt_arguments_dirty:f3(0,0,0,0)), 251 | ?_assertEqual(10, nt_arguments_dirty:f4(1,2,3,4))]. 252 | 253 | -spec arguments_dirty_test_() -> term(). 254 | arguments_dirty_test_() -> 255 | {timeout, ?TIMEOUT, [compile_arguments_dirty(), 256 | call_functions_arguments_dirt()]}. 257 | -------------------------------------------------------------------------------- /c_src/nifty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Andreas Löscher and 3 | * Konstantinos Sagonas 4 | * All rights reserved. 5 | * 6 | * This file is distributed under the Simplified BSD License. 7 | * Details can be found in the LICENSE file. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #if _WIN32 || _WIN64 15 | #if _WIN64 16 | typedef unsigned __int64 uint64_t; 17 | #define ENV64BIT 18 | #else 19 | #define ENV32BIT 20 | #endif 21 | #else // clang gcc 22 | #include 23 | #if __x86_64__ 24 | #define ENV64BIT 25 | #else 26 | #define ENV32BIT 27 | #endif 28 | #endif 29 | 30 | #ifdef ENV32BIT 31 | typedef unsigned long ptr_t; 32 | #define nifty_get_ptr(env, term, ip) enif_get_ulong((env), (term), (ip)) 33 | #define nifty_make_ptr(env, i) enif_make_ulong((env), (i)) 34 | #else /* ENV64BIT */ 35 | typedef uint64_t ptr_t; 36 | #define nifty_get_ptr(env, term, ip) enif_get_uint64((env), (term), (ip)) 37 | #define nifty_make_ptr(env, i) enif_make_uint64((env), (i)) 38 | #endif 39 | 40 | 41 | static ERL_NIF_TERM 42 | raw_free(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 43 | { 44 | int err; 45 | ptr_t ptr; 46 | err = nifty_get_ptr(env, argv[0], &ptr); 47 | if (!err) { 48 | goto error; 49 | } 50 | enif_free((void*)ptr); 51 | return enif_make_atom(env, "ok"); 52 | error: 53 | return enif_make_badarg(env); 54 | } 55 | 56 | 57 | static ERL_NIF_TERM 58 | raw_deref(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 59 | { 60 | int err; 61 | ptr_t ptr; 62 | err = nifty_get_ptr(env, argv[0], &ptr); 63 | if (!err) { 64 | goto error; 65 | } 66 | return enif_make_int64(env, (*(ptr_t*)ptr)); 67 | error: 68 | return enif_make_badarg(env); 69 | } 70 | 71 | static ERL_NIF_TERM 72 | float_deref(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 73 | { 74 | int err; 75 | ptr_t ptr; 76 | err = nifty_get_ptr(env, argv[0], &ptr); 77 | if (!err) { 78 | goto error; 79 | } 80 | return enif_make_double(env, ((double)(*((float*)ptr)))); 81 | error: 82 | return enif_make_badarg(env); 83 | } 84 | 85 | static ERL_NIF_TERM 86 | float_ref(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 87 | { 88 | int err; 89 | double helper; 90 | float *value; 91 | 92 | value = enif_alloc(sizeof(float)); 93 | if (!value) { 94 | goto error; 95 | } 96 | 97 | err = enif_get_double(env, argv[0], &helper); 98 | if (!err) { 99 | enif_free(value); 100 | goto error; 101 | } 102 | 103 | *value = (float)helper; 104 | 105 | return enif_make_tuple2(env, 106 | nifty_make_ptr(env, (ptr_t)value), 107 | enif_make_string(env, "nifty.float *", ERL_NIF_LATIN1)); 108 | 109 | error: 110 | return enif_make_badarg(env); 111 | } 112 | 113 | static ERL_NIF_TERM 114 | double_deref(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 115 | { 116 | int err; 117 | ptr_t ptr; 118 | err = nifty_get_ptr(env, argv[0], &ptr); 119 | if (!err) { 120 | goto error; 121 | } 122 | return enif_make_double(env, *((double*)ptr)); 123 | error: 124 | return enif_make_badarg(env); 125 | } 126 | 127 | static ERL_NIF_TERM 128 | double_ref(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 129 | { 130 | int err; 131 | double *value; 132 | 133 | value = enif_alloc(sizeof(double)); 134 | if (!value) { 135 | goto error; 136 | } 137 | 138 | err = enif_get_double(env, argv[0], value); 139 | if (!err) { 140 | enif_free(value); 141 | goto error; 142 | } 143 | 144 | return enif_make_tuple2(env, 145 | nifty_make_ptr(env, (ptr_t)value), 146 | enif_make_string(env, "nifty.double *", ERL_NIF_LATIN1)); 147 | 148 | error: 149 | return enif_make_badarg(env); 150 | } 151 | 152 | static ERL_NIF_TERM 153 | cstr_to_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 154 | { 155 | int err, ar; 156 | ptr_t ptr; 157 | ERL_NIF_TERM *tpl; 158 | 159 | err = enif_get_tuple(env, argv[0], &ar, (const ERL_NIF_TERM**)(&tpl)); 160 | if (err) { 161 | err = nifty_get_ptr(env, tpl[0], &ptr); 162 | } 163 | if (!err) { 164 | goto error; 165 | } 166 | return enif_make_string(env, (char*)ptr, ERL_NIF_LATIN1); 167 | 168 | error: 169 | return enif_make_badarg(env); 170 | } 171 | 172 | static ERL_NIF_TERM 173 | list_to_cstr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 174 | { 175 | int err, written, tmp; 176 | unsigned int l; 177 | char* cstr; 178 | 179 | err = enif_get_list_length(env, argv[0], &l); 180 | if (!err) { 181 | goto error; 182 | } 183 | l+=1; // Null Termination 184 | cstr = enif_alloc(sizeof(char)*l); 185 | if (!cstr) { 186 | goto error; 187 | } 188 | written = 0; 189 | while (written<(l)) { 190 | tmp = enif_get_string(env, argv[0], cstr+written, l-written, ERL_NIF_LATIN1); 191 | if (tmp<=0) { 192 | enif_free(cstr); 193 | goto error; 194 | } 195 | written += tmp; 196 | } 197 | 198 | return enif_make_tuple2(env, 199 | enif_make_int64(env, (ptr_t)cstr), 200 | enif_make_string(env, "nifty.char *", ERL_NIF_LATIN1)); 201 | 202 | error: 203 | return enif_make_badarg(env); 204 | } 205 | 206 | static ERL_NIF_TERM 207 | mem_write_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 208 | { 209 | unsigned int i, l, data, err; 210 | int ar; 211 | char* ptr; 212 | ERL_NIF_TERM head, tail, list, *tpl; 213 | 214 | err = enif_get_list_length(env, argv[0], &l); 215 | if (!err) { 216 | goto error; 217 | } 218 | 219 | 220 | err = enif_get_tuple(env, argv[1], &ar, (const ERL_NIF_TERM**)(&tpl)); 221 | if (err) { 222 | err = nifty_get_ptr(env, tpl[0], (ptr_t*)&ptr); 223 | } 224 | if (!err) { 225 | goto error; 226 | } 227 | 228 | list = argv[0]; 229 | i = 0; 230 | 231 | while (!enif_is_empty_list(env, list)) { 232 | err = enif_get_list_cell(env, list, &head, &tail); 233 | list = tail; 234 | if (!err) { 235 | goto error; 236 | } 237 | err = enif_get_uint(env, head, &data); 238 | if (!err) { 239 | goto error; 240 | } 241 | *(ptr+i++) = (char)data; 242 | } 243 | 244 | return argv[1]; 245 | 246 | error: 247 | return enif_make_badarg(env); 248 | } 249 | 250 | static ERL_NIF_TERM 251 | mem_write_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 252 | { 253 | unsigned int err; 254 | int ar; 255 | unsigned char* ptr; 256 | ERL_NIF_TERM *tpl; 257 | ErlNifBinary bin; 258 | 259 | if (!enif_inspect_binary(env, argv[0], &bin)) { 260 | goto error; 261 | } 262 | 263 | err = enif_get_tuple(env, argv[1], &ar, (const ERL_NIF_TERM**)(&tpl)); 264 | if (err) { 265 | err = nifty_get_ptr(env, tpl[0], (ptr_t*)&ptr); 266 | } 267 | if (!err) { 268 | goto error; 269 | } 270 | 271 | memcpy(ptr, bin.data, bin.size); 272 | 273 | return argv[1]; 274 | 275 | error: 276 | return enif_make_badarg(env); 277 | } 278 | 279 | static ERL_NIF_TERM 280 | mem_read(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 281 | { 282 | unsigned int err, l, i, tmp; 283 | char element; 284 | int ar; 285 | char* ptr; 286 | ERL_NIF_TERM list, head, *tpl; 287 | 288 | 289 | err = enif_get_tuple(env, argv[0], &ar, (const ERL_NIF_TERM**)(&tpl)); 290 | if (err) { 291 | err = nifty_get_ptr(env, tpl[0], (ptr_t*)&ptr); 292 | } 293 | if (!err) { 294 | goto error; 295 | } 296 | 297 | err = enif_get_uint(env, argv[1], &l); 298 | if (!err) { 299 | goto error; 300 | } 301 | 302 | list = enif_make_list(env, 0); 303 | 304 | for (i=0;i 3 | * and Konstantinos Sagonas 4 | * All rights reserved. 5 | * 6 | * This file is distributed under the Simplified BSD License. 7 | * Details can be found in the LICENSE file. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | /* ----------------- Type definitions ----------------- 17 | */ 18 | 19 | typedef struct _clientd { 20 | ErlNifEnv* env; 21 | ERL_NIF_TERM func_file; 22 | ERL_NIF_TERM types; 23 | ERL_NIF_TERM symbol_table; 24 | ERL_NIF_TERM constr_table; 25 | } Data; 26 | 27 | typedef struct _subdata { 28 | ErlNifEnv* env; 29 | ERL_NIF_TERM types; 30 | ERL_NIF_TERM data; 31 | } SubData; 32 | 33 | typedef struct _enumdata { 34 | ErlNifEnv* env; 35 | ERL_NIF_TERM enum_values; 36 | } EnumData; 37 | 38 | 39 | /* ----------------- Static functions ----------------- 40 | */ 41 | 42 | static int 43 | is_argument(const char *str) { 44 | unsigned count = 0; 45 | char* sub = strstr((char *)str, "@"); 46 | if (!sub) { 47 | return 1; 48 | } 49 | while (sub) { 50 | sub++; 51 | count++; 52 | sub = strstr(sub, "@"); 53 | } 54 | return count == 4; 55 | } 56 | 57 | static enum CXChildVisitResult 58 | visitor_struct_cb(CXCursor cursor, CXCursor parent, CXClientData client_data) { 59 | ERL_NIF_TERM name; 60 | ERL_NIF_TERM typename; 61 | 62 | CXString tmp; 63 | CXType type; 64 | ERL_NIF_TERM etmp; 65 | unsigned len; 66 | SubData *data = (SubData*)client_data; 67 | ErlNifEnv *env = data->env; 68 | 69 | switch (clang_getCursorKind(cursor)) { 70 | case CXCursor_FieldDecl: { 71 | enif_get_list_length(env, data->data, &len); 72 | 73 | tmp = clang_getCursorSpelling(cursor); 74 | name = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 75 | clang_disposeString(tmp); 76 | 77 | type = clang_getCursorType(cursor); 78 | tmp = clang_getTypeSpelling(type); 79 | typename = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 80 | data->types = enif_make_list_cell(env, typename, data->types); 81 | clang_disposeString(tmp); 82 | 83 | etmp = enif_make_tuple4(env, 84 | enif_make_atom(env, "field"), 85 | name, 86 | typename, 87 | enif_make_uint(env, len)); 88 | data->data = enif_make_list_cell(env, etmp, data->data); 89 | return CXChildVisit_Continue; 90 | } 91 | default: { 92 | return CXChildVisit_Continue; 93 | } 94 | } 95 | } 96 | 97 | static enum CXChildVisitResult 98 | visitor_enum_cb(CXCursor cursor, CXCursor parent, CXClientData client_data) { 99 | EnumData* data = (EnumData*)client_data; 100 | ErlNifEnv *env = data->env; 101 | ERL_NIF_TERM name; 102 | ERL_NIF_TERM value; 103 | long long cvalue; 104 | 105 | CXString tmp; 106 | char* cstr; 107 | 108 | switch (clang_getCursorKind(cursor)) { 109 | case CXCursor_EnumConstantDecl: { 110 | tmp = clang_getCursorSpelling(cursor); 111 | cstr = (char*)clang_getCString(tmp); 112 | cvalue = clang_getEnumConstantDeclValue(cursor); 113 | name = enif_make_string(env, cstr, ERL_NIF_LATIN1); 114 | value = enif_make_int64(env, cvalue); 115 | data->enum_values = enif_make_list_cell(env, 116 | enif_make_tuple2(env, name, value), 117 | data->enum_values); 118 | } 119 | default: { 120 | return CXChildVisit_Continue; 121 | } 122 | } 123 | } 124 | 125 | static enum CXChildVisitResult 126 | visitor_function_cb(CXCursor cursor, CXCursor parent, CXClientData client_data) { 127 | ERL_NIF_TERM typename; 128 | ERL_NIF_TERM etmp; 129 | 130 | CXString tmp; 131 | CXType type; 132 | unsigned len; 133 | 134 | SubData *data = (SubData*)client_data; 135 | ErlNifEnv *env = data->env; 136 | 137 | const char* ctmp; 138 | 139 | switch (clang_getCursorKind(cursor)) { 140 | case CXCursor_ParmDecl: { 141 | tmp = clang_getCursorUSR(cursor); 142 | ctmp = clang_getCString(tmp); 143 | if (is_argument(ctmp)) { 144 | clang_disposeString(tmp); 145 | 146 | enif_get_list_length(env, data->data, &len); 147 | type = clang_getCursorType(cursor); 148 | tmp = clang_getTypeSpelling(type); 149 | typename = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 150 | data->types = enif_make_list_cell(env, typename, data->types); 151 | clang_disposeString(tmp); 152 | etmp = enif_make_tuple3(env, 153 | enif_make_atom(env, "argument"), 154 | enif_make_uint(env, len), 155 | typename); 156 | data->data = enif_make_list_cell(env, etmp, data->data); 157 | return CXChildVisit_Continue; 158 | } else { 159 | clang_disposeString(tmp); 160 | return CXChildVisit_Continue; 161 | } 162 | } 163 | default: { 164 | return CXChildVisit_Continue; 165 | } 166 | } 167 | } 168 | 169 | static enum CXChildVisitResult 170 | visitor_cb(CXCursor cursor, CXCursor parent, CXClientData client_data) 171 | { 172 | char* ctmp; 173 | CXString tmp; 174 | CXType type; 175 | 176 | CXFile file; 177 | unsigned line; 178 | unsigned column; 179 | unsigned offset; 180 | CXSourceLocation loc; 181 | 182 | Data* data = (Data*)client_data; 183 | 184 | ERL_NIF_TERM ff_l = data->func_file; 185 | ERL_NIF_TERM fn, funcname; 186 | ERL_NIF_TERM etmp, etmp2, etmp3; 187 | ErlNifEnv* env = data->env; 188 | 189 | SubData* subd; 190 | EnumData* enumd; 191 | 192 | switch (clang_getCursorKind(cursor)) { 193 | case CXCursor_FunctionDecl: { 194 | tmp = clang_getCursorSpelling(cursor); 195 | funcname = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 196 | clang_disposeString(tmp); 197 | 198 | loc = clang_getCursorLocation(cursor); 199 | clang_getFileLocation(loc, &file, &line, &column, &offset); 200 | 201 | tmp = clang_getFileName(file); 202 | fn = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 203 | clang_disposeString(tmp); 204 | ff_l = enif_make_list_cell(env, enif_make_tuple2(env, funcname, fn), ff_l); 205 | data->func_file = ff_l; 206 | 207 | subd = enif_alloc(sizeof(SubData)); 208 | subd->types = data->types; 209 | subd->data = enif_make_list(env, 0); 210 | subd->env = env; 211 | 212 | clang_visitChildren(cursor, visitor_function_cb, (CXClientData)subd); 213 | 214 | type = clang_getResultType(clang_getCursorType(cursor)); 215 | tmp = clang_getTypeSpelling(type); 216 | etmp = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 217 | data->types = enif_make_list_cell(env, etmp, subd->types); 218 | etmp = enif_make_tuple2(env, enif_make_atom(env, "return"), etmp); 219 | 220 | enif_make_reverse_list(env, subd->data, &etmp2); 221 | etmp = enif_make_list_cell(env, etmp, etmp2); 222 | etmp = enif_make_tuple2(env, funcname, etmp); 223 | data->symbol_table = enif_make_list_cell(env, etmp, data->symbol_table); 224 | enif_free(subd); 225 | return CXChildVisit_Continue; 226 | } 227 | case CXCursor_StructDecl: { 228 | tmp = clang_getCursorSpelling(cursor); 229 | ctmp = (char*)clang_getCString(tmp); 230 | if ((clang_getCursorKind(parent) == CXCursor_TranslationUnit) && (!strlen(ctmp))) { 231 | clang_disposeString(tmp); 232 | return CXChildVisit_Continue; 233 | } else { 234 | subd = enif_alloc(sizeof(SubData)); 235 | subd->types = data->types; 236 | subd->data = enif_make_list(env, 0); 237 | subd->env = env; 238 | clang_visitChildren(cursor, visitor_struct_cb, (CXClientData)subd); 239 | etmp = enif_make_tuple2(env, enif_make_atom(env, "struct"), 240 | enif_make_string(env, ctmp, ERL_NIF_LATIN1)); 241 | clang_disposeString(tmp); 242 | enif_make_reverse_list(env, subd->data, &etmp2); 243 | etmp = enif_make_tuple2(env, etmp, etmp2); 244 | data->constr_table = enif_make_list_cell(env, etmp, data->constr_table); 245 | data->types = subd->types; 246 | enif_free(subd); 247 | return CXChildVisit_Continue; 248 | } 249 | } 250 | case CXCursor_UnionDecl: { 251 | tmp = clang_getCursorSpelling(cursor); 252 | ctmp = (char*)clang_getCString(tmp); 253 | if ((clang_getCursorKind(parent) == CXCursor_TranslationUnit) && (!strlen(ctmp))) { 254 | clang_disposeString(tmp); 255 | return CXChildVisit_Continue; 256 | } else { 257 | subd = enif_alloc(sizeof(SubData)); 258 | subd->types = data->types; 259 | subd->data = enif_make_list(env, 0); 260 | subd->env = env; 261 | clang_visitChildren(cursor, visitor_struct_cb, (CXClientData)subd); 262 | etmp = enif_make_tuple2(env, enif_make_atom(env, "union"), 263 | enif_make_string(env, ctmp, ERL_NIF_LATIN1)); 264 | clang_disposeString(tmp); 265 | enif_make_reverse_list(env, subd->data, &etmp2); 266 | etmp = enif_make_tuple2(env, etmp, etmp2); 267 | data->constr_table = enif_make_list_cell(env, etmp, data->constr_table); 268 | data->types = subd->types; 269 | enif_free(subd); 270 | return CXChildVisit_Continue; 271 | } 272 | } 273 | case CXCursor_TypedefDecl: { 274 | tmp = clang_getCursorSpelling(cursor); 275 | etmp = enif_make_tuple2(env, enif_make_atom(env, "typedef"), 276 | enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1)); 277 | clang_disposeString(tmp); 278 | 279 | type = clang_getTypedefDeclUnderlyingType(cursor); 280 | tmp = clang_getTypeSpelling(type); 281 | etmp2 = enif_make_string(env, clang_getCString(tmp), ERL_NIF_LATIN1); 282 | etmp = enif_make_tuple2(env, etmp, etmp2); 283 | data->types = enif_make_list_cell(env, etmp2, data->types); 284 | data->constr_table = enif_make_list_cell(env, etmp, data->constr_table); 285 | clang_disposeString(tmp); 286 | 287 | return CXChildVisit_Continue; 288 | } 289 | case CXCursor_EnumDecl: { 290 | tmp = clang_getCursorSpelling(cursor); 291 | ctmp = (char*)clang_getCString(tmp); 292 | if ((clang_getCursorKind(parent) == CXCursor_TranslationUnit) && (!strlen(ctmp))) { 293 | clang_disposeString(tmp); 294 | } else { 295 | enumd = enif_alloc(sizeof(EnumData)); 296 | enumd->env = env; 297 | enumd->enum_values = enif_make_list(env, 0); 298 | clang_visitChildren(cursor, visitor_enum_cb, (CXClientData)enumd); 299 | etmp = enif_make_tuple2(env, enif_make_atom(env, "enum"), 300 | enif_make_string(env, ctmp, ERL_NIF_LATIN1)); 301 | clang_disposeString(tmp); 302 | enif_make_reverse_list(env, enumd->enum_values, &etmp2); 303 | etmp = enif_make_tuple2(env, etmp, etmp2); 304 | data->constr_table = enif_make_list_cell(env, etmp, data->constr_table); 305 | /* defaults to long long */ 306 | etmp3 = enif_make_string(env, "long long", ERL_NIF_LATIN1); 307 | data->types = enif_make_list_cell(env, etmp3, data->types); 308 | enif_free(enumd); 309 | } 310 | return CXChildVisit_Continue; 311 | } 312 | default: { 313 | return CXChildVisit_Continue; 314 | } 315 | } 316 | } 317 | 318 | static ERL_NIF_TERM 319 | walk_cursor(ErlNifEnv* env, CXTranslationUnit t, CXCursor c) { 320 | CXCursorVisitor visitor = visitor_cb; 321 | Data* data = enif_alloc(sizeof(Data)); 322 | 323 | data->env = env; 324 | data->func_file = enif_make_list(env, 0); 325 | data->types = enif_make_list(env, 0); 326 | data->symbol_table = enif_make_list(env, 0); 327 | data->constr_table = enif_make_list(env, 0); 328 | clang_visitChildren(c, visitor, (CXClientData)data); 329 | return enif_make_tuple4(env, 330 | data->func_file, 331 | data->symbol_table, 332 | data->types, 333 | data->constr_table); 334 | } 335 | 336 | static void print_fails(CXTranslationUnit t) 337 | { 338 | unsigned i, n; 339 | 340 | for (i = 0, n = clang_getNumDiagnostics(t); i!=n; i++) { 341 | CXDiagnostic diag = clang_getDiagnostic(t, i); 342 | CXString s = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); 343 | fprintf(stderr, "%s\r\n", clang_getCString(s)); 344 | clang_disposeString(s); 345 | } 346 | } 347 | 348 | static char* 349 | term2string(ErlNifEnv* env, ERL_NIF_TERM s) 350 | { 351 | char* ret; 352 | unsigned int n; 353 | int atom = 0; 354 | 355 | if (!enif_get_list_length(env, s, &n)) 356 | { 357 | if (!enif_get_atom_length(env, s, &n, ERL_NIF_LATIN1)) 358 | return NULL; 359 | else 360 | atom = 1; 361 | } 362 | ret = enif_alloc(sizeof(char)*(n+1)); 363 | if (!atom) 364 | { 365 | if (!enif_get_string(env, s, ret, n+1, ERL_NIF_LATIN1)) { 366 | enif_free(ret); 367 | return NULL; 368 | } 369 | } else { 370 | if (!enif_get_atom(env, s, ret, n+1, ERL_NIF_LATIN1)) { 371 | enif_free(ret); 372 | return NULL; 373 | } 374 | } 375 | return ret; 376 | } 377 | 378 | static ERL_NIF_TERM 379 | cparse(ErlNifEnv* env, int argc, char *argv[]) { 380 | CXIndex Index = clang_createIndex(0,0); 381 | CXTranslationUnit TU = clang_parseTranslationUnit(Index, 0, (const char**)(argv), argc, 0,0, CXTranslationUnit_None); 382 | ERL_NIF_TERM retval; 383 | 384 | if (!clang_getNumDiagnostics(TU)) { 385 | retval = walk_cursor(env, TU, clang_getTranslationUnitCursor(TU)); 386 | } else { 387 | retval = enif_make_atom(env, "fail"); 388 | print_fails(TU); 389 | } 390 | clang_disposeTranslationUnit(TU); 391 | clang_disposeIndex(Index); 392 | return retval; 393 | } 394 | 395 | static ERL_NIF_TERM 396 | parse_impl(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 397 | { 398 | char **cargv; 399 | unsigned arg_count, i; 400 | ERL_NIF_TERM head, tail, list, retval; 401 | 402 | list = argv[0]; 403 | if (!enif_get_list_length(env, list, &arg_count)) { 404 | return enif_make_badarg(env); 405 | } 406 | 407 | cargv = enif_alloc(sizeof(char*)*arg_count); 408 | 409 | for (i = 0; i < arg_count; i++) { 410 | if (!enif_get_list_cell(env, list, &head, &tail)) { 411 | retval = enif_make_badarg(env); 412 | goto free_cells; 413 | } 414 | list = tail; 415 | if (!(cargv[i] = term2string(env, head))) { 416 | retval = enif_make_badarg(env); 417 | goto free_cells; 418 | } 419 | } 420 | retval = cparse(env, arg_count, cargv); 421 | 422 | free_cells: 423 | arg_count = i; 424 | for (i = 0; i < arg_count; i++) { 425 | enif_free(cargv[i]); 426 | } 427 | enif_free(cargv); 428 | 429 | return retval; 430 | } 431 | 432 | static ErlNifFunc nif_funcs[] = { 433 | {"cparse", 1, parse_impl}, 434 | }; 435 | 436 | static int 437 | upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) 438 | { 439 | return 0; 440 | } 441 | 442 | ERL_NIF_INIT(nifty_clangparse, nif_funcs, NULL, NULL, upgrade, NULL) 443 | -------------------------------------------------------------------------------- /priv/templates/lib/structures.tpl: -------------------------------------------------------------------------------- 1 | {% if prototypes==1 %} 2 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="struct"%} 3 | static ERL_NIF_TERM ptr_to_record_{{name}}(ErlNifEnv* env, ptr_t ptr); 4 | static ERL_NIF_TERM record_to_erlptr_{{name}}(ErlNifEnv* env, ERL_NIF_TERM record); 5 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 6 | 7 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="union"%} 8 | static ERL_NIF_TERM ptr_to_urecord_{{name}}(ErlNifEnv* env, ptr_t ptr); 9 | static ERL_NIF_TERM urecord_to_erlptr_{{name}}(ErlNifEnv* env, ERL_NIF_TERM record); 10 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 11 | {% else %} 12 | 13 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="struct" %} 14 | static ERL_NIF_TERM 15 | ptr_to_record_{{name}}(ErlNifEnv* env, ptr_t ptr) 16 | { 17 | {% if constructors|fetch:constr|length > 0 %} 18 | struct {{name}}* cstruct=(struct {{name}}*)ptr; 19 | {% endif %} 20 | ERL_NIF_TERM retval; 21 | {% with fields=constructors|fetch:constr %} 22 | {% for argument in fields %} 23 | {% with raw_type=argument|getNth:3 phase="prepare" %} 24 | {% with type=raw_type|resolved:types %} 25 | {% with N=argument|getNth:2 %} 26 | {% with erlarg="erlarg_"|add:N carg=N record="to_record" %} 27 | {% include "lib/builtin_type.tpl" %} 28 | {% endwith %} 29 | {% endwith %} 30 | {% endwith %} 31 | {% endwith %} 32 | {% endfor %} 33 | {% endwith %} 34 | 35 | 36 | 37 | {% with fields=constructors|fetch:constr %} 38 | {% for argument in fields %} 39 | {% with raw_type=argument|getNth:3 phase="to_erl" %} 40 | {% with type=raw_type|resolved:types %} 41 | {% with N=argument|getNth:2 %} 42 | {% with carg="(cstruct->"|add:N|add:")" erlarg="erlarg_"|add:N %} 43 | {% include "lib/builtin_type.tpl" %} 44 | {% endwith %} 45 | {% endwith %} 46 | {% endwith %} 47 | {% endwith %} 48 | {% endfor %} 49 | {% endwith %} 50 | 51 | {% with fields=constructors|fetch:constr %}{% with tpl_length=fields|length|add:1 %}{% if tpl_length>9 %} 52 | retval = enif_make_tuple(env, {{tpl_length}}, enif_make_atom( 53 | {% else %} 54 | retval = enif_make_tuple{{tpl_length}}(env, enif_make_atom( 55 | {% endif %}{% endwith %}{% endwith %} 56 | env, "struct {{name}}") 57 | {% with fields=constructors|fetch:constr %} 58 | {% for argument in fields %} 59 | {% with N=argument|getNth:2 %} 60 | ,{{"erlarg_"|add:N}} 61 | {% endwith %} 62 | {% endfor %} 63 | {% endwith %}); 64 | 65 | return retval; 66 | } 67 | 68 | static ERL_NIF_TERM 69 | record_to_erlptr_{{name}}(ErlNifEnv* env, ERL_NIF_TERM record) 70 | { 71 | struct {{name}}* cstruct; 72 | int ar, err; 73 | ERL_NIF_TERM *tpl; 74 | 75 | {% with fields=constructors|fetch:constr %} 76 | {% for argument in fields %} 77 | {% with raw_type=argument|getNth:3 phase="prepare" %} 78 | {% with type=raw_type|resolved:types %} 79 | {% with N=argument|getNth:2 %} 80 | {% with record="to_ptr" %} 81 | {% include "lib/builtin_type.tpl" %} 82 | {% endwith %} 83 | {% endwith %} 84 | {% endwith %} 85 | {% endwith %} 86 | {% endfor %} 87 | {% endwith %} 88 | 89 | cstruct = (struct {{name}}*)enif_alloc(sizeof(struct {{name}})); 90 | 91 | err = enif_get_tuple(env, record, &ar, (const ERL_NIF_TERM**)(&tpl)); 92 | if (!err) { 93 | goto error; 94 | } 95 | 96 | {% with fields=constructors|fetch:constr %} 97 | {% for argument in fields %} 98 | {% with raw_type=argument|getNth:3 phase="to_c" %} 99 | {% with type=raw_type|resolved:types %} 100 | {% with N=argument|getNth:2 I=argument|getNth:4|add:1 %} 101 | {% with carg="(cstruct->"|add:N|add:")" erlarg="(tpl["|add:I|add:"])" %} 102 | {% include "lib/builtin_type.tpl" %} 103 | {% endwith %} 104 | {% endwith %} 105 | {% endwith %} 106 | if (!err) { 107 | goto error; 108 | } 109 | {% endwith %} 110 | {% endfor %} 111 | {% endwith %} 112 | 113 | return enif_make_tuple2( 114 | env, 115 | nifty_make_ptr(env, (ptr_t)cstruct), 116 | enif_make_string(env, "{{module}}.struct {{name}} *", ERL_NIF_LATIN1)); 117 | 118 | error: 119 | return enif_make_badarg(env); 120 | } 121 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 122 | 123 | 124 | static ERL_NIF_TERM 125 | record_to_erlptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 126 | { 127 | int err, written, tmp, ar; 128 | unsigned int l; 129 | char* cstr; 130 | ERL_NIF_TERM *tpl; 131 | 132 | err = enif_get_tuple(env, argv[0], &ar, (const ERL_NIF_TERM**)(&tpl)); 133 | if (!err) { 134 | goto error; 135 | } 136 | 137 | err = enif_get_atom_length(env, tpl[0], &l, ERL_NIF_LATIN1); 138 | if (!err) { 139 | goto error; 140 | } 141 | 142 | l+=1; 143 | cstr = enif_alloc(sizeof(char)*l); 144 | written = 0; 145 | while (written<(l)) { 146 | tmp = enif_get_atom(env, tpl[0], cstr+written, l-written, ERL_NIF_LATIN1); 147 | if (tmp==-(l-written)) { 148 | tmp=-tmp; 149 | } 150 | if (tmp<=0) { 151 | enif_free(cstr); 152 | goto error; 153 | } 154 | written += tmp; 155 | } 156 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="struct" %} 157 | if ((!(strcmp((const char*)cstr, "{{name}}"))) 158 | || (!(strcmp((const char*)cstr, "struct {{name}}")))) { 159 | return record_to_erlptr_{{name}}(env, argv[0]); 160 | } 161 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 162 | 163 | 164 | 165 | error: 166 | return enif_make_badarg(env); 167 | } 168 | 169 | static ERL_NIF_TERM 170 | erlptr_to_record(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 171 | { 172 | int err, written, tmp; 173 | unsigned int l; 174 | ptr_t ptr; 175 | char* cstr; 176 | 177 | ERL_NIF_TERM *tpl; 178 | 179 | err = enif_get_tuple(env, argv[0], &tmp, (const ERL_NIF_TERM**)(&tpl)); 180 | if (!err) { 181 | goto error; 182 | } 183 | 184 | err = enif_get_list_length(env, tpl[1], &l); 185 | if (!err) { 186 | goto error; 187 | } 188 | 189 | l+=1; 190 | cstr = enif_alloc(sizeof(char)*l); 191 | written = 0; 192 | while (written<(l)) { 193 | tmp = enif_get_string(env, tpl[1], cstr+written, l-written, ERL_NIF_LATIN1); 194 | if (tmp==-(l-written)) { 195 | tmp=-tmp; 196 | } 197 | if (tmp<=0) { 198 | enif_free(cstr); 199 | goto error; 200 | } 201 | written += tmp; 202 | } 203 | 204 | err = nifty_get_ptr(env, tpl[0], &ptr); 205 | if (!err) { 206 | goto error; 207 | } 208 | 209 | 210 | 211 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="struct" %} 212 | if (!(strcmp((const char*)cstr, "{{name}}"))) { return ptr_to_record_{{name}}(env, ptr); } 213 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 214 | 215 | error: 216 | return enif_make_badarg(env); 217 | } 218 | 219 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="union" %} 220 | static ERL_NIF_TERM 221 | ptr_to_urecord_{{name}}(ErlNifEnv* env, ptr_t ptr) 222 | { 223 | {% if constructors|fetch:constr|length > 0 %} 224 | union {{name}}* cunion=(union {{name}}*)ptr; 225 | {% endif %} 226 | ERL_NIF_TERM retval; 227 | {% with fields=constructors|fetch:constr %} 228 | {% for argument in fields %} 229 | {% with raw_type=argument|getNth:3 phase="prepare" %} 230 | {% with type=raw_type|resolved:types %} 231 | {% with N=argument|getNth:2 %} 232 | {% with erlarg="erlarg_"|add:N carg=N record="to_record" %} 233 | {% include "lib/builtin_type.tpl" %} 234 | {% endwith %} 235 | {% endwith %} 236 | {% endwith %} 237 | {% endwith %} 238 | {% endfor %} 239 | {% endwith %} 240 | 241 | 242 | 243 | {% with fields=constructors|fetch:constr %} 244 | {% for argument in fields %} 245 | {% with raw_type=argument|getNth:3 phase="to_erl" %} 246 | {% with type=raw_type|resolved:types %} 247 | {% with N=argument|getNth:2 %} 248 | {% with carg="(cunion->"|add:N|add:")" erlarg="erlarg_"|add:N %} 249 | {% include "lib/builtin_type.tpl" %} 250 | {% endwith %} 251 | {% endwith %} 252 | {% endwith %} 253 | {% endwith %} 254 | {% endfor %} 255 | {% endwith %} 256 | 257 | {% with fields=constructors|fetch:constr %}{% with tpl_length=fields|length|add:1 %}{% if tpl_length>9 %} 258 | retval = enif_make_tuple(env, {{tpl_length}}, enif_make_atom( 259 | {% else %} 260 | retval = enif_make_tuple{{tpl_length}}(env, enif_make_atom( 261 | {% endif %}{% endwith %}{% endwith %} 262 | env, "union {{name}}") 263 | {% with fields=constructors|fetch:constr %} 264 | {% for argument in fields %} 265 | {% with N=argument|getNth:2 %} 266 | ,{{"erlarg_"|add:N}} 267 | {% endwith %} 268 | {% endfor %} 269 | {% endwith %}); 270 | 271 | return retval; 272 | } 273 | 274 | static ERL_NIF_TERM 275 | urecord_to_erlptr_{{name}}(ErlNifEnv* env, ERL_NIF_TERM record) 276 | { 277 | union {{name}}* cunion; 278 | int ar, err; 279 | ERL_NIF_TERM *tpl; 280 | 281 | {% with fields=constructors|fetch:constr %} 282 | {% for argument in fields %} 283 | {% with raw_type=argument|getNth:3 phase="prepare" %} 284 | {% with type=raw_type|resolved:types %} 285 | {% with N=argument|getNth:2 %} 286 | {% with record="to_ptr" %} 287 | {% include "lib/builtin_type.tpl" %} 288 | {% endwith %} 289 | {% endwith %} 290 | {% endwith %} 291 | {% endwith %} 292 | {% endfor %} 293 | {% endwith %} 294 | 295 | cunion = (union {{name}}*)enif_alloc(sizeof(union {{name}})); 296 | 297 | err = enif_get_tuple(env, record, &ar, (const ERL_NIF_TERM**)(&tpl)); 298 | if (!err) { 299 | goto error; 300 | } 301 | 302 | {% with fields=constructors|fetch:constr %} 303 | {% for argument in fields %} 304 | {% with raw_type=argument|getNth:3 phase="to_c" %} 305 | {% with type=raw_type|resolved:types %} 306 | {% with N=argument|getNth:2 I=argument|getNth:4|add:1 %} 307 | {% with carg="(cunion->"|add:N|add:")" erlarg="(tpl["|add:I|add:"])" %} 308 | if (!enif_is_atom(env, {{erlarg}})) { 309 | {% include "lib/builtin_type.tpl" %} 310 | } 311 | {% endwith %} 312 | {% endwith %} 313 | {% endwith %} 314 | if (!err) { 315 | goto error; 316 | } 317 | {% endwith %} 318 | {% endfor %} 319 | {% endwith %} 320 | 321 | return enif_make_tuple2( 322 | env, 323 | nifty_make_ptr(env, (ptr_t)cunion), 324 | enif_make_string(env, "{{module}}.union {{name}} *", ERL_NIF_LATIN1)); 325 | 326 | error: 327 | return enif_make_badarg(env); 328 | } 329 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 330 | 331 | 332 | static ERL_NIF_TERM 333 | urecord_to_erlptr(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 334 | { 335 | int err, written, tmp, ar; 336 | unsigned int l; 337 | char* cstr; 338 | ERL_NIF_TERM *tpl; 339 | 340 | err = enif_get_tuple(env, argv[0], &ar, (const ERL_NIF_TERM**)(&tpl)); 341 | if (!err) { 342 | goto error; 343 | } 344 | 345 | err = enif_get_atom_length(env, tpl[0], &l, ERL_NIF_LATIN1); 346 | if (!err) { 347 | goto error; 348 | } 349 | 350 | l+=1; 351 | cstr = enif_alloc(sizeof(char)*l); 352 | written = 0; 353 | while (written<(l)) { 354 | tmp = enif_get_atom(env, tpl[0], cstr+written, l-written, ERL_NIF_LATIN1); 355 | if (tmp==-(l-written)) { 356 | tmp=-tmp; 357 | } 358 | if (tmp<=0) { 359 | enif_free(cstr); 360 | goto error; 361 | } 362 | written += tmp; 363 | } 364 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="union" %} 365 | if ((!(strcmp((const char*)cstr, "{{name}}"))) 366 | || (!(strcmp((const char*)cstr, "union {{name}}")))) { 367 | return urecord_to_erlptr_{{name}}(env, argv[0]); 368 | } 369 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 370 | 371 | error: 372 | return enif_make_badarg(env); 373 | } 374 | 375 | static ERL_NIF_TERM 376 | erlptr_to_urecord(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 377 | { 378 | int err, written, tmp; 379 | unsigned int l; 380 | ptr_t ptr; 381 | char* cstr; 382 | 383 | ERL_NIF_TERM *tpl; 384 | 385 | err = enif_get_tuple(env, argv[0], &tmp, (const ERL_NIF_TERM**)(&tpl)); 386 | if (!err) { 387 | goto error; 388 | } 389 | 390 | err = enif_get_list_length(env, tpl[1], &l); 391 | if (!err) { 392 | goto error; 393 | } 394 | 395 | l+=1; 396 | cstr = enif_alloc(sizeof(char)*l); 397 | written = 0; 398 | while (written<(l)) { 399 | tmp = enif_get_string(env, tpl[1], cstr+written, l-written, ERL_NIF_LATIN1); 400 | if (tmp==-(l-written)) { 401 | tmp=-tmp; 402 | } 403 | if (tmp<=0) { 404 | enif_free(cstr); 405 | goto error; 406 | } 407 | written += tmp; 408 | } 409 | 410 | err = nifty_get_ptr(env, tpl[0], &ptr); 411 | if (!err) { 412 | goto error; 413 | } 414 | 415 | 416 | 417 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="union" %} 418 | if (!(strcmp((const char*)cstr, "{{name}}"))) { return ptr_to_urecord_{{name}}(env, ptr); } 419 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 420 | 421 | error: 422 | return enif_make_badarg(env); 423 | } 424 | 425 | 426 | static ERL_NIF_TERM 427 | size_of(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 428 | { 429 | int err, written, tmp; 430 | unsigned int l; 431 | char* cstr; 432 | 433 | err = enif_get_list_length(env, argv[0], &l); 434 | if (!err) { 435 | goto error; 436 | } 437 | 438 | l+=1; 439 | cstr = enif_alloc(sizeof(char)*l); 440 | written = 0; 441 | while (written<(l)) { 442 | tmp = enif_get_string(env, argv[0], cstr+written, l-written, ERL_NIF_LATIN1); 443 | if (tmp==-(l-written)) { 444 | tmp=-tmp; 445 | } 446 | if (tmp<=0) { 447 | enif_free(cstr); 448 | goto error; 449 | } 450 | written += tmp; 451 | } 452 | /* structs and unions */ 453 | {% with type_keys=types|clear_function_pointers|fetch_keys %} 454 | {% for type in type_keys %} 455 | {% with kind=types|fetch:type|getNth:1 %} 456 | {% if kind=="base" or kind=="userdef" or kind=="typedef" %} 457 | if (!(strcmp((const char*)cstr, "{{type}}"))) 458 | { 459 | return enif_make_ulong(env, sizeof({{type|discard_restrict}})); 460 | } 461 | {% endif %} 462 | {% if kind=="userdef" %} 463 | {% endif %} 464 | {% endwith%} 465 | {% endfor %} 466 | {% endwith %} 467 | return enif_make_atom(env, "undef"); 468 | error: 469 | return enif_make_badarg(env); 470 | } 471 | 472 | static ERL_NIF_TERM 473 | new_type_object(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) 474 | { 475 | int err, written, tmp; 476 | unsigned int l; 477 | char* cstr; 478 | ptr_t type_holder; 479 | ERL_NIF_TERM retval; 480 | 481 | err = enif_get_list_length(env, argv[0], &l); 482 | if (!err) { 483 | goto error; 484 | } 485 | 486 | l+=1; 487 | cstr = enif_alloc(sizeof(char)*l); 488 | written = 0; 489 | while (written<(l)) { 490 | tmp = enif_get_string(env, argv[0], cstr+written, l-written, ERL_NIF_LATIN1); 491 | if (tmp==-(l-written)) { 492 | tmp=-tmp; 493 | } 494 | if (tmp<=0) { 495 | enif_free(cstr); 496 | goto error; 497 | } 498 | written += tmp; 499 | } 500 | /* structs */ 501 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="struct" %} 502 | if ((!(strcmp((const char*)cstr, "{{name}}"))) 503 | || (!(strcmp((const char*)cstr, "struct {{name}}")))) 504 | { 505 | type_holder = (ptr_t)enif_alloc(sizeof(struct {{name}})); 506 | retval=ptr_to_record_{{name}}(env, type_holder); 507 | enif_free((void*)type_holder); 508 | return retval; 509 | } 510 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 511 | /* unions */ 512 | {% with keys = constructors|fetch_keys %}{% for constr in keys %}{% with kind=constr|getNth:1 name=constr|getNth:2 %}{% if kind=="union" %} 513 | if ((!(strcmp((const char*)cstr, "{{name}}"))) 514 | || (!(strcmp((const char*)cstr, "union {{name}}")))) 515 | { 516 | type_holder = (ptr_t)enif_alloc(sizeof(union {{name}})); 517 | retval=ptr_to_urecord_{{name}}(env, type_holder); 518 | enif_free((void*)type_holder); 519 | return retval; 520 | } 521 | {% endif %}{% endwith%}{% endfor %}{% endwith %} 522 | return enif_make_atom(env, "undef"); 523 | error: 524 | return enif_make_badarg(env); 525 | /* suppress warnings */ 526 | type_holder++; 527 | retval++; 528 | } 529 | {% endif %} 530 | -------------------------------------------------------------------------------- /src/nifty.erl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level: 2 -*- 2 | %%% ------------------------------------------------------------------- 3 | %%% Copyright (c) 2015-2017, Andreas Löscher 4 | %%% and Konstantinos Sagonas 5 | %%% All rights reserved. 6 | %%% 7 | %%% This file is distributed under the Simplified BSD License. 8 | %%% Details can be found in the LICENSE file. 9 | %%% ------------------------------------------------------------------- 10 | 11 | -module(nifty). 12 | -export([%% create modules 13 | compile/3, 14 | compile/2, 15 | %% strings 16 | list_to_cstr/1, 17 | cstr_to_list/1, 18 | %% pointers 19 | dereference/1, 20 | pointer/0, 21 | pointer/1, 22 | pointer_of/2, 23 | pointer_of/1, 24 | %% enums 25 | enum_value/2, 26 | %% types 27 | as_type/2, 28 | size_of/1, 29 | %% memory allocation 30 | mem_write/1, 31 | mem_write/2, 32 | mem_read/2, 33 | mem_alloc/1, 34 | mem_copy/3, 35 | malloc/1, 36 | free/1, 37 | %% configuration 38 | get_config/0, 39 | get_env/0, 40 | %% builtin types 41 | get_types/0, 42 | %% array utilities 43 | array_new/2, 44 | array_ith/2, 45 | array_element/2, 46 | array_set/3, 47 | list_to_array/2, 48 | array_to_list/2 49 | ]). 50 | 51 | -export_type([error_reason/0, options/0, ptr/0, comp_ret/0, cvalue/0]). 52 | 53 | -type error_reason() :: 'compile' | 'no_file'. 54 | -type options() :: proplists:proplist(). 55 | 56 | -type addr() :: integer(). 57 | -type ptr() :: {addr(), nonempty_string()}. 58 | -type cvalue_error() :: 'undefined' | 'unknown_builtin_type' | 'unknown_type'. 59 | -type cvalue() :: ptr() | integer() | float() | tuple() 60 | | {string(), integer()} | {'error', cvalue_error()}. 61 | 62 | %%--------------------------------------------------------------------------- 63 | %% Initialization of NIFs 64 | %%--------------------------------------------------------------------------- 65 | 66 | -on_load(init/0). 67 | 68 | init() -> %% loading code from jiffy 69 | PrivDir = case code:priv_dir(?MODULE) of 70 | {error, _} -> 71 | EbinDir = filename:dirname(code:which(?MODULE)), 72 | AppPath = filename:dirname(EbinDir), 73 | filename:join(AppPath, "priv"); 74 | Path -> 75 | Path 76 | end, 77 | ok = erlang:load_nif(filename:join(PrivDir, "nifty"), 0), 78 | load_dependencies(). 79 | 80 | load_dependencies() -> 81 | ok = load_dependency(rebar3), 82 | ok = load_dependency(erlydtl). 83 | 84 | load_dependency(Module) -> 85 | case code:ensure_loaded(Module) of 86 | {error, nofile} -> 87 | %% module not found 88 | NiftyPath = code:lib_dir(nifty, deps), 89 | case code:add_patha(filename:join([NiftyPath, atom_to_list(Module), "ebin"])) of 90 | {error, _} -> 91 | {error, dependency_not_found}; 92 | true -> 93 | ok 94 | end; 95 | {module, Module} -> 96 | ok 97 | end. 98 | 99 | %%--------------------------------------------------------------------------- 100 | %% Compile 101 | %%--------------------------------------------------------------------------- 102 | 103 | -type comp_ret() :: 'ok' 104 | | {'error', error_reason()} 105 | | {'warning', {'not_complete', [nonempty_string()]}}. 106 | 107 | %% @doc same as compile(InterfaceFile, Module, []). 108 | -spec compile(string(), module()) -> comp_ret(). 109 | compile(InterfaceFile, Module) -> 110 | compile(InterfaceFile, Module, []). 111 | 112 | %% @doc Generates a NIF module out of a C header file and compiles it, 113 | %% generating wrapper functions for all functions present in the header file. 114 | %% InterfaceFile specifies the header file. 115 | %% Module specifies the module name of the translated NIF. 116 | %% Options specifies the compile options. 117 | %% These options are a superset rebar's config options and include 118 | %% additional Nifty options:
119 | %% {nifty, NiftyOptions}
120 | %% where NiftyOptions is a list of options, which can be:
121 | %% 122 | %% 123 | %%
schedule_dirtyuse dirty schedulers
124 | 125 | -spec compile(string(), module(), options()) -> comp_ret(). 126 | compile(InterfaceFile, Module, Options) -> 127 | ModuleName = atom_to_list(Module), 128 | os:putenv("NIF", libname(ModuleName)), 129 | {ok, NiftyRoot} = file:get_cwd(), 130 | os:putenv("NIFTY_ROOT", NiftyRoot), 131 | UCO = update_compile_options(InterfaceFile, ModuleName, Options), 132 | CFlags = build_cflags(ModuleName, UCO), 133 | case render(InterfaceFile, ModuleName, CFlags, UCO) of 134 | {error, _} = E -> 135 | E; 136 | {Output, Lost} -> 137 | ok = store_files(InterfaceFile, ModuleName, UCO, Output), 138 | case compile_module(ModuleName) of 139 | ok -> 140 | ModulePath = filename:absname(filename:join([ModuleName, "_build", "default", "lib", ModuleName, "ebin"])), 141 | true = code:add_patha(ModulePath), 142 | case Lost of 143 | [] -> ok; 144 | _ -> {warning, {not_complete, Lost}} 145 | end; 146 | fail -> 147 | {error, compile} 148 | end 149 | end. 150 | 151 | -type renderout() :: {iolist(), iolist(), iolist(), iolist(), iolist(), iolist()}. 152 | -type modulename() :: string(). 153 | 154 | %% @doc Renders an InterfaceFile into a Erlang module 155 | %% containing of ModuleName.erl 156 | %% ModuleName.c, ModuleName.app and 157 | %% rebar.config and returns the contents of these files 158 | %% as tuple of iolists (in this order). It uses CFlags to 159 | %% parse the InterfaceFile and Options to 160 | %% compile it. Options are equivalent to rebar options. 161 | -spec render(string(), modulename(), [string()], options()) -> {'error', error_reason()} | {renderout(), [nonempty_string()]}. 162 | render(InterfaceFile, ModuleName, CFlags, Options) -> 163 | io:format("generating... ~n"),%%, [ModuleName, ModuleName++"_remote", InterfaceFile]), 164 | %% c parse stuff 165 | PathToH = InterfaceFile, 166 | case filelib:is_file(PathToH) andalso (not filelib:is_dir(PathToH)) of 167 | false -> 168 | {error, no_file}; 169 | true -> 170 | case nifty_clangparse:parse([PathToH|CFlags]) of 171 | {error, fail} -> 172 | {error, compile}; 173 | {FuncLoc, Raw_Symbols, Raw_Types, Unsave_Constructors} -> 174 | Constructors = check_constructors(Unsave_Constructors), 175 | Unsave_Types = nifty_clangparse:build_type_table(Raw_Types, Constructors), 176 | Types = check_types(Unsave_Types, Constructors), 177 | Unsave_Symbols = filter_symbols(InterfaceFile, Raw_Symbols, FuncLoc, Options), 178 | {Symbols, Lost} = check_symbols(Unsave_Symbols, Types), 179 | RenderVars = [{"module", ModuleName}, 180 | {"header", InterfaceFile}, 181 | {"config", Options}, 182 | {"types", Types}, 183 | {"symbols", Symbols}, 184 | {"constructors", Constructors}, 185 | {"none", none}], 186 | COutput = render_with_errors(cmodule_nifty_template, RenderVars), 187 | ErlOutput = render_with_errors(emodule_nifty_template, RenderVars), 188 | SaveErlOutput = render_with_errors(save_emodule_nifty_template, RenderVars), 189 | HrlOutput = render_with_errors( hrlmodule_nifty_template, RenderVars), 190 | AppOutput = render_with_errors(app_nifty_template, RenderVars), 191 | ConfigOutput = render_with_errors(config_nifty_template, RenderVars), 192 | {{ErlOutput, SaveErlOutput, HrlOutput, COutput, AppOutput, ConfigOutput}, Lost} 193 | end 194 | end. 195 | 196 | render_with_errors(Template, Vars) -> 197 | try Template:render(Vars) of 198 | {ok, Output} -> Output; 199 | {error, Err} -> 200 | io:format("Error during rendering of template ~p:~n~p~nPlease report the error~n", [Template, Err]), 201 | throw(nifty_render_error) 202 | catch 203 | ET:E -> 204 | io:format("~p:~p during rendering of template ~p:~nVars: ~n~p~nPlease report the error~n", [ET, E, Template, Vars]), 205 | throw(nifty_render_error) 206 | end. 207 | 208 | 209 | check_types(Types, Constr) -> 210 | %% somehow we have incomplete types in the type table 211 | Pred = fun (Key, Value) -> 212 | case Value of 213 | {userdef, [{struct, Name}]} -> 214 | dict:is_key({struct, Name}, Constr); 215 | _ -> 216 | nifty_types:check_type(Key, Types, Constr) 217 | end 218 | end, 219 | dict:filter(Pred, Types). 220 | 221 | check_constructors(Constr) -> 222 | Pred = fun (_, Fields) -> length(Fields) > 0 end, 223 | dict:filter(Pred, Constr). 224 | 225 | filter_symbols(InterfaceFile, Symbols, FuncLoc, Options) -> 226 | Pred = case get_filter_option(Options) of 227 | none -> 228 | BaseName = filename:basename(InterfaceFile), 229 | fun (Key, _) -> 230 | filename:basename(dict:fetch(Key, FuncLoc)) =:= BaseName 231 | end; 232 | RegEx -> 233 | fun (Key, _) -> 234 | Location = dict:fetch(Key, FuncLoc), 235 | case re:run(Location, RegEx, [{capture, none}]) of 236 | match -> true; 237 | {error, _} -> error(nifty_bad_regex); 238 | _ -> false 239 | end 240 | end 241 | end, 242 | dict:filter(Pred, Symbols). 243 | 244 | get_filter_option(Options) -> 245 | case proplists:lookup(nifty, Options) of 246 | {nifty, NiftyOptions} -> 247 | case proplists:lookup(filter_headers, NiftyOptions) of 248 | {filter_headers, RegEx} -> RegEx; 249 | _ -> none 250 | end; 251 | _ -> none 252 | end. 253 | 254 | 255 | check_symbols(Symbols, Types) -> 256 | Pred = fun (_, Args) -> check_args(Args, Types) end, 257 | Accml = fun(Name, Args, AccIn) -> 258 | case check_args(Args, Types) of 259 | true -> AccIn; 260 | false -> [Name|AccIn] 261 | end 262 | end, 263 | {dict:filter(Pred, Symbols), dict:fold(Accml, [], Symbols)}. 264 | 265 | check_args([], _) -> true; 266 | check_args([H|T], Types) -> 267 | Type = case H of 268 | {return, Tp} -> Tp; 269 | {argument, _, Tp} -> Tp 270 | end, 271 | case nifty_types:check_type(Type, Types) of 272 | false -> false; 273 | true -> check_args(T, Types) 274 | end. 275 | 276 | store_files(InterfaceFile, ModuleName, Options, RenderOutput) -> 277 | {ok, Path} = file:get_cwd(), 278 | {ok, _} = rebar3:run(["new", "lib", ModuleName]), 279 | ok = file:set_cwd(filename:join([Path, ModuleName])), 280 | store_files(InterfaceFile, ModuleName, Options, RenderOutput, Path), 281 | ok = file:set_cwd(Path). 282 | 283 | store_files(_, ModuleName, _, RenderOutput, Path) -> 284 | ok = case file:make_dir(filename:join([Path, ModuleName, "include"])) of 285 | ok -> ok; 286 | {error,eexist} -> ok; 287 | _ -> fail 288 | end, 289 | ok = case file:make_dir(filename:join([Path, ModuleName, "c_src"])) of 290 | ok -> ok; 291 | {error,eexist} -> ok; 292 | _ -> fail 293 | end, 294 | ok = case file:make_dir(filename:join([Path, ModuleName, "priv"])) of 295 | ok -> ok; 296 | {error,eexist} -> ok; 297 | _ -> fail 298 | end, 299 | {ErlOutput, SaveErlOutput, HrlOutput, COutput, AppOutput, ConfigOutput} = RenderOutput, 300 | ok = fwrite_render(Path, ModuleName, "src", ModuleName++".erl", ErlOutput), 301 | ok = fwrite_render(Path, ModuleName, "src", ModuleName++"_remote"++".erl", SaveErlOutput), 302 | ok = fwrite_render(Path, ModuleName, "src", ModuleName++".app.src", AppOutput), 303 | ok = fwrite_render(Path, ModuleName, "include", ModuleName++".hrl", HrlOutput), 304 | ok = fwrite_render(Path, ModuleName, "c_src", ModuleName++"_nif.c", COutput), 305 | ok = fwrite_render(Path, ModuleName, ".", "rebar.config", ConfigOutput). 306 | 307 | fwrite_render(Path, ModuleName, Dir, FileName, Template) -> 308 | file:write_file(filename:join([Path, ModuleName, Dir, FileName]), [Template]). 309 | 310 | compile_module(ModuleName) -> 311 | {ok, Path} = file:get_cwd(), 312 | ok = file:set_cwd(filename:join([Path, ModuleName])), 313 | try rebar3:run(["compile"]) of 314 | _ -> file:set_cwd(Path) 315 | catch 316 | throw:rebar_abort -> 317 | ok = file:set_cwd(Path), 318 | fail 319 | end. 320 | 321 | build_cflags(ModuleName, Options) -> 322 | Env = case proplists:get_value(port_env, Options) of 323 | undefined -> []; 324 | EnvList -> EnvList 325 | end, 326 | EnvAll = case proplists:get_value(port_specs, Options) of 327 | undefined -> Env; 328 | SpecList -> 329 | lists:concat([Env, get_spec_env(ModuleName, SpecList)]) 330 | end, 331 | OrigCFlags = os:getenv("CFLAGS", ""), 332 | os:putenv("CFLAGS", OrigCFlags), 333 | Retval = merge_cflags(proplists:get_all_values("CFLAGS", EnvAll)), 334 | os:putenv("CFLAGS", OrigCFlags), 335 | Retval. 336 | 337 | merge_cflags([]) -> 338 | string:tokens(os:getenv("CFLAGS"), " "); 339 | merge_cflags([Flag|T]) -> 340 | Expanded = nifty_utils:expand(Flag), 341 | os:putenv("CFLAGS", Expanded), 342 | merge_cflags(T). 343 | 344 | 345 | 346 | get_spec_env(_, []) -> []; 347 | get_spec_env(ModuleName, [S|T]) -> 348 | Lib = libname(ModuleName), 349 | case S of 350 | {_, Lib, _, Options} -> 351 | case proplists:get_value(env, Options) of 352 | undefined -> []; 353 | Env -> expand_env(Env, []) 354 | end; 355 | _ -> 356 | get_spec_env(ModuleName, T) 357 | end. 358 | 359 | norm_opts(Options) -> 360 | case proplists:get_value(env, Options) of 361 | undefined -> Options; 362 | Env -> 363 | [{env, merge_env(expand_env(Env, []), dict:new())}| proplists:delete(env, Options)] 364 | end. 365 | 366 | merge_env([], D) -> dict:to_list(D); 367 | merge_env([{Key, Opt}|T], D) -> 368 | case dict:is_key(Key, D) of 369 | true -> 370 | merge_env(T, dict:store(Key, dict:fetch(Key,D) ++ " " ++ remove_envvar(Key, Opt), D)); 371 | false -> 372 | merge_env(T, dict:store(Key, Opt, D)) 373 | end. 374 | 375 | remove_envvar(Key, Opt) -> 376 | %% remove in the beginning and the end 377 | Striped = string:strip(Opt), 378 | K1 = "${" ++ Key ++ "}", 379 | K2 = "$" ++ Key, 380 | K3 = "%" ++ Key, 381 | E1 = length(Striped) - length(K1) + 1, 382 | E23 = length(Striped) - length(K2) + 1, 383 | case string:str(Striped, K1) of 384 | 1 -> 385 | string:substr(Striped, length(K1)+1); 386 | E1 -> 387 | string:substr(Striped, 1, E1); 388 | _ -> 389 | case string:str(Striped, K2) of 390 | 1 -> 391 | string:substr(Striped, length(K2)+1); 392 | E23 -> 393 | string:substr(Striped, 1, E23 -1); 394 | _ -> 395 | case string:str(Striped, K3) of 396 | 1 -> 397 | string:substr(Striped, length(K3)+1); 398 | E23 -> 399 | string:substr(Striped, 1, E23 - 1); 400 | _ -> 401 | Striped 402 | end 403 | end 404 | end. 405 | 406 | expand_env([], Acc) -> 407 | Acc; 408 | expand_env([{ON, O}|T], Acc) -> 409 | expand_env(T, [{ON, nifty_utils:expand(O)}|Acc]). 410 | 411 | libname(ModuleName) -> 412 | "priv/"++ModuleName++"_nif.so". 413 | 414 | update_compile_options(InterfaceFile, ModuleName, CompileOptions) -> 415 | NewPort_Spec = case proplists:get_value(port_specs, CompileOptions) of 416 | undefined -> 417 | [module_spec(".*", [], [], InterfaceFile, ModuleName)]; 418 | UPortSpec -> 419 | update_port_spec(InterfaceFile, ModuleName, UPortSpec, [], false) 420 | end, 421 | orddict:store(port_specs, NewPort_Spec, orddict:from_list(CompileOptions)). 422 | 423 | module_spec(ARCH, Sources, Options, InterfaceFile, ModuleName) -> 424 | { 425 | ARCH, 426 | libname(ModuleName), 427 | ["c_src/"++ModuleName++"_nif.c"|abspath_sources(Sources)], 428 | norm_opts(join_options([{env, 429 | [{"CFLAGS", 430 | "$CFLAGS -I"++filename:absname(filename:dirname(nifty_utils:expand(InterfaceFile)))}]}], 431 | Options)) 432 | }. 433 | 434 | join_options(Proplist1, Proplist2) -> 435 | orddict:merge(fun(_, X, Y) -> X ++ Y end, 436 | orddict:from_list(Proplist1), 437 | orddict:from_list(Proplist2)). 438 | 439 | abspath_sources(S) -> abspath_sources(S, []). 440 | 441 | abspath_sources([], Acc) -> Acc; 442 | abspath_sources([S|T], Acc) -> 443 | abspath_sources(T, [filename:absname(nifty_utils:expand(S))|Acc]). 444 | 445 | 446 | update_port_spec(_, _, [], Acc, true) -> 447 | Acc; 448 | update_port_spec(InterfaceFile, ModuleName, [], Acc, false) -> %% empty spec 449 | [module_spec(".*", [], [], InterfaceFile, ModuleName), Acc]; 450 | update_port_spec(InterfaceFile, ModuleName, [Spec|T], Acc, Found) -> 451 | Shared = libname(ModuleName), 452 | case expand_spec(Spec) of 453 | {ARCH, Shared, Sources} -> 454 | update_port_spec( 455 | InterfaceFile, 456 | ModuleName, 457 | T, 458 | [module_spec(ARCH, Sources, [], InterfaceFile, ModuleName)|Acc], true); 459 | {ARCH, Shared, Sources, Options} -> 460 | update_port_spec( 461 | InterfaceFile, 462 | ModuleName, 463 | T, 464 | [module_spec(ARCH, Sources, Options, InterfaceFile, ModuleName)|Acc], true); 465 | _ -> 466 | update_port_spec(InterfaceFile, ModuleName, T, [Spec|Acc], Found) 467 | end. 468 | 469 | expand_spec(S) -> 470 | case S of 471 | {ARCH, Shared, Sources} -> 472 | {ARCH, nifty_utils:expand(Shared), norm_sources(Sources)}; 473 | {ARCH, Shared, Sources, Options} -> 474 | {ARCH, nifty_utils:expand(Shared), norm_sources(Sources), Options} 475 | end. 476 | 477 | norm_sources(S) -> 478 | [nifty_utils:expand(X) || X <- S]. 479 | 480 | %%--------------------------------------------------------------------------- 481 | 482 | %% @doc Returns nifty's base types as a dict 483 | -spec get_types() -> nifty_clangparse:type_table(). 484 | get_types() -> 485 | %% builtin types: 486 | %% int types ( [(short|long)] [(long|short)] int; [(signed|unsigned)] char ) 487 | %% float types ( float; double) 488 | %% string (char *) 489 | %% pointer (void *) 490 | dict:from_list( 491 | [{"signed char",{base,["char","signed","none"]}}, 492 | {"char",{base,["char","signed","none"]}}, 493 | {"unsigned char",{base,["char","unsigned","none"]}}, 494 | {"short",{base,["int","signed","short"]}}, 495 | {"unsigned short",{base,["int","unsigned","short"]}}, 496 | {"int",{base,["int","signed","none"]}}, 497 | {"unsigned int",{base,["int","unsigned","none"]}}, 498 | {"long",{base,["int","signed","long"]}}, 499 | {"unsigned long",{base,["int","unsigned","long"]}}, 500 | {"long long",{base,["int","signed","longlong"]}}, 501 | {"unsigned long long",{base,["int","unsigned","longlong"]}}, 502 | {"float",{base,["float","signed","none"]}}, 503 | {"double",{base,["double","signed","none"]}}, 504 | %% pointers 505 | {"signed char *",{base,["*","char","signed","none"]}}, 506 | {"char *",{base,["*","char","signed","none"]}}, 507 | {"unsigned char *",{base,["*","char","unsigned","none"]}}, 508 | {"short *",{base,["*","int","signed","short"]}}, 509 | {"unsigned short *",{base,["*","int","unsigned","short"]}}, 510 | {"int *",{base,["*","int","signed","none"]}}, 511 | {"unsigned int *",{base,["*","int","unsigned","none"]}}, 512 | {"long *",{base,["*","int","signed","long"]}}, 513 | {"unsigned long *",{base,["*","int","unsigned","long"]}}, 514 | {"long long *",{base,["*","int","signed","longlong"]}}, 515 | {"unsigned long long *",{base,["*","int","unsigned","longlong"]}}, 516 | {"float *",{base,["*","float","signed","none"]}}, 517 | {"double *",{base,["*","double","signed","none"]}}, 518 | {"_Bool", {typedef, "int"}}, 519 | %% special types 520 | {"void *",{base,["*","void","signed","none"]}}, 521 | {"char *",{base,["*","char","signed","none"]}} 522 | ]). 523 | 524 | get_types(?MODULE) -> get_types(); 525 | get_types(Module) -> Module:'__nifty__get_types'(). 526 | 527 | get_derefed_type(Type, Module) -> 528 | Types = get_types(Module), 529 | case dict:is_key(Type, Types) of 530 | true -> 531 | ResType = nifty_types:resolve_type(Type, Types), 532 | {_, TypeDef} = dict:fetch(ResType, Types), 533 | [H|_] = TypeDef, 534 | case (H =:= "*") orelse (string:str(H, "[") > 0) of 535 | true -> 536 | [[_|PointerDef]|Token] = lists:reverse(string:tokens(ResType, " ")), 537 | NType = case PointerDef of 538 | [] ->string:join(lists:reverse(Token), " "); 539 | _ -> string:join(lists:reverse([PointerDef|Token]), " ") 540 | end, 541 | ResNType = nifty_types:resolve_type(NType, Types), 542 | case dict:is_key(ResNType, Types) of 543 | true -> 544 | {_, DTypeDef} = dict:fetch(ResNType, Types), 545 | [DH|_] = DTypeDef, 546 | case DH of 547 | {_, _} -> {final, ResNType}; 548 | _ -> case (DH =:= "*") orelse (string:str(DH, "[") > 0) of 549 | true -> {pointer, ResNType}; 550 | false -> {final, ResNType} 551 | end 552 | end; 553 | false -> 554 | undef 555 | end; 556 | false -> 557 | {final, ResType} 558 | end; 559 | false -> 560 | case lists:last(Type) of 561 | $* -> 562 | %% pointer 563 | NName = string:strip(string:left(Type, length(Type)-1)), 564 | case lists:last(NName) of 565 | $* -> 566 | {pointer, NName}; 567 | _ -> 568 | {final, NName} 569 | end; 570 | _ -> 571 | {error, unknown_type} 572 | end 573 | end. 574 | 575 | %% @doc Dereference a nifty pointer 576 | -spec dereference(ptr()) -> cvalue(). 577 | dereference(Pointer) -> 578 | {Address, ModuleType} = Pointer, 579 | [ModuleName, Type] = case string:tokens(ModuleType, ".") of 580 | [NiftyType] -> ["nifty", NiftyType]; 581 | FullType -> FullType 582 | end, 583 | Module = list_to_atom(ModuleName), 584 | %% case Module of 585 | %% nifty -> 586 | %% _builtin_type(Type, Address); 587 | %% _ -> 588 | NType = get_derefed_type(Type, Module), 589 | case NType of 590 | {pointer, PType} -> 591 | {raw_deref(Address), ModuleName++"."++PType}; 592 | {final, DType} -> 593 | build_type(Module, DType, Address); 594 | undef -> 595 | {error, undefined} 596 | end. 597 | %% end. 598 | 599 | %% build_builtin_type(DType, Address) -> 600 | %% case DType of 601 | %% "void *" -> {raw_deref(Address), "undef"}; 602 | %% "char *" -> cstr_to_list({Address, "nifty.char *"}); 603 | %% _ -> build_type(nifty, DType, Address) 604 | %% end. 605 | 606 | build_type(Module, Type, Address) -> 607 | Types = get_types(Module), 608 | case dict:is_key(Type, Types) of 609 | true -> 610 | RType = nifty_types:resolve_type(Type, Types), 611 | {Kind, Def} = dict:fetch(RType, Types), 612 | case Kind of 613 | userdef -> 614 | case Def of 615 | [{struct, Name}] -> 616 | Module:erlptr_to_record({Address, Name}); 617 | [{union, Name}] -> 618 | Module:erlptr_to_urecord({Address, Name}); 619 | _ -> 620 | {error, undefined} 621 | end; 622 | base -> 623 | case Def of 624 | ["char", Sign, _] -> 625 | int_deref(Address, 1, Sign); 626 | ["int", Sign, L] -> 627 | {_, {ShI, I, LI, LLI, _, _}} = proplists:lookup("sizes", get_config()), 628 | Size = case L of 629 | "short" -> 630 | ShI; 631 | "none" -> 632 | I; 633 | "long" -> 634 | LI; 635 | "longlong" -> 636 | LLI 637 | end, 638 | int_deref(Address, Size, Sign); 639 | ["float", _, _] -> 640 | float_deref(Address); 641 | ["double", _, _] -> 642 | double_deref(Address); 643 | _ -> 644 | {error, unknown_builtin_type} 645 | end; 646 | _ -> 647 | {error, unknown_type} 648 | end; 649 | false -> 650 | {error, unknown_type} 651 | end. 652 | 653 | int_deref(Addr, Size, Sign) -> 654 | I = int_deref(lists:reverse(mem_read({Addr, "nifty.void *"}, Size)), 0), 655 | case Sign of 656 | "signed" -> 657 | case I > (trunc(math:pow(2, (Size*8)-1))-1) of 658 | true -> 659 | I - trunc(math:pow(2,(Size*8))); 660 | false -> 661 | I 662 | end; 663 | "unsigned" -> 664 | I 665 | end. 666 | 667 | int_deref([], Acc) -> Acc; 668 | int_deref([E|T], Acc) -> 669 | int_deref(T, (Acc bsl 8) + E). 670 | 671 | %% @doc Free's the memory associated with a nifty pointer 672 | -spec free(ptr() | list(ptr())) -> 'ok'. 673 | free([]) -> ok; 674 | free([H|T]) -> 675 | free(H), 676 | free(T); 677 | free({Addr, _}) -> 678 | raw_free(Addr). 679 | 680 | 681 | %% @doc Allocates the specified amount of bytes and returns a pointer 682 | %% to the allocated memory 683 | -spec malloc(non_neg_integer()) -> ptr(). 684 | malloc(Size) -> 685 | mem_alloc(Size). 686 | 687 | %%% NIF Functions 688 | raw_free(_) -> 689 | erlang:nif_error(nif_library_not_loaded). 690 | 691 | float_deref(_) -> 692 | erlang:nif_error(nif_library_not_loaded). 693 | 694 | float_ref(_) -> 695 | erlang:nif_error(nif_library_not_loaded). 696 | 697 | double_deref(_) -> 698 | erlang:nif_error(nif_library_not_loaded). 699 | 700 | double_ref(_) -> 701 | erlang:nif_error(nif_library_not_loaded). 702 | 703 | %% @doc Converts an erlang string into a 0 terminated C string and 704 | %% returns a nifty pointer to it 705 | -spec list_to_cstr(string()) -> ptr(). 706 | list_to_cstr(_) -> 707 | erlang:nif_error(nif_library_not_loaded). 708 | 709 | %% @doc Converts a nifty pointer to a 0 terminated C string into a 710 | %% erlang string. 711 | -spec cstr_to_list(ptr()) -> string(). 712 | cstr_to_list(_) -> 713 | erlang:nif_error(nif_library_not_loaded). 714 | 715 | %% @doc size of a base type, no error handling 716 | -spec size_of(nonempty_string()) -> integer() | undef. 717 | size_of(Type) -> 718 | Types = get_types(), 719 | case dict:is_key(Type, Types) of 720 | true -> 721 | %% builtin 722 | case dict:fetch(Type, Types) of 723 | {base, ["char", _, _]} -> 724 | 1; 725 | {base, ["int", _, L]} -> 726 | {_, {ShI, I, LI, LLI, _, _}} = proplists:lookup("sizes", get_config()), 727 | case L of 728 | "short" -> 729 | ShI; 730 | "none" -> 731 | I; 732 | "long" -> 733 | LI; 734 | "longlong" -> 735 | LLI 736 | end; 737 | {base, ["float", _, _]}-> 738 | {_, {_, _, _, _, Fl, _}} = proplists:lookup("sizes", get_config()), 739 | Fl; 740 | {base, ["double", _, _]}-> 741 | {_, {_, _, _, _, _, Dbl}} = proplists:lookup("sizes", get_config()), 742 | Dbl; 743 | {base, ["*"|_]} -> 744 | {_, {_, P}} = proplists:lookup("arch", get_config()), 745 | P 746 | end; 747 | false -> 748 | %% full referenced 749 | case string:tokens(Type, ".") of 750 | ["nifty", TypeName] -> 751 | %% builtin 752 | size_of(TypeName); 753 | [ModuleName, TypeName] -> 754 | Mod = list_to_atom(ModuleName), 755 | case {module, Mod} =:= code:ensure_loaded(Mod) andalso 756 | lists:member({'__nifty__size_of',1}, Mod:module_info(exports)) of 757 | true -> 758 | Mod:'__nifty__size_of'(TypeName); 759 | false -> 760 | undef 761 | end; 762 | _ -> 763 | undef 764 | end 765 | end. 766 | 767 | %% @doc Returns the integer value associated with an enum alias 768 | -spec enum_value(atom(), nonempty_string() | atom()) -> integer() | undef. 769 | enum_value(Module, Value) when is_atom(Value) -> 770 | enum_value(Module, atom_to_list(Value)); 771 | enum_value(Module, Value) -> 772 | case {module, Module} =:= code:ensure_loaded(Module) andalso 773 | lists:member({'__nifty__get_enum_aliases',0}, Module:module_info(exports)) of 774 | true -> 775 | case proplists:lookup(Value, Module:'__nifty__get_enum_aliases'()) of 776 | {Value, IntValue} -> IntValue; 777 | _ -> undef 778 | end; 779 | false -> 780 | undef 781 | end. 782 | 783 | 784 | %% @doc Returns a pointer to a memory area that is the size of a pointer 785 | -spec pointer() -> ptr(). 786 | pointer() -> 787 | {_, Size} = proplists:get_value("arch", get_config()), 788 | mem_alloc(Size). 789 | 790 | referred_type(Type) -> 791 | case lists:last(Type) of 792 | $* -> Type++"*"; 793 | _ -> Type++" *" 794 | end. 795 | 796 | %% @doc Returns a pointer to the specified Type. This 797 | %% function allocates memory of sizeof(Type) 798 | -spec pointer(nonempty_string()) -> ptr() | undef. 799 | pointer(Type) -> 800 | case size_of(Type) of 801 | undef -> undef; 802 | S -> as_type(mem_alloc(S), referred_type(Type)) 803 | end. 804 | 805 | %% @doc Returns a pointer to the given pointer 806 | -spec pointer_of(ptr()) -> ptr() | undef. 807 | pointer_of({_, Type} = Ptr) -> 808 | pointer_of(Ptr, Type). 809 | 810 | %% @doc Returns a pointer to the Value with the type 811 | %% Type 812 | -spec pointer_of(term(), string()) -> ptr() | undef. 813 | pointer_of(Value, Type) -> 814 | case string:right(Type, 1) of 815 | "*" -> 816 | %% pointer 817 | {Addr, VType} = Value, 818 | case VType =:= Type of 819 | true -> 820 | {_, Size} = proplists:get_value("arch", get_config()), 821 | {NAddr, _} = int_constr(Addr, Size), 822 | {NAddr, Type++"*"}; 823 | false -> 824 | undef 825 | end; 826 | _ -> 827 | %% something else 828 | case string:tokens(Type, ".") of 829 | [_] -> 830 | %% base types 831 | builtin_pointer_of(Value, Type); 832 | ["nifty", T] -> 833 | %% base type 834 | builtin_pointer_of(Value, T); 835 | [ModuleName, T] -> 836 | case builtin_pointer_of(Value, T) of 837 | undef -> 838 | %% no base type, try the module 839 | %% resolve type and try again 840 | Module = list_to_atom(ModuleName), 841 | Types = Module:'__nifty__get_types'(), 842 | case nifty_types:resolve_type(T, Types) of 843 | undef -> 844 | %% can (right now) only be a struct or a union 845 | structured_pointer_of(Module, Value); 846 | ResT -> 847 | case builtin_pointer_of(Value, ResT) of 848 | undef -> 849 | %% can (right now) only be a struct 850 | structured_pointer_of(Module, Value); 851 | Ptr -> 852 | Ptr 853 | end 854 | end; 855 | Ptr -> 856 | Ptr 857 | end 858 | end 859 | end. 860 | 861 | structured_pointer_of(Module, Value) -> 862 | Tag = atom_to_list(element(1, Value)), 863 | case string:str(Tag, "struct") of 864 | 1 -> Module:record_to_erlptr(Value); 865 | _ -> case string:str(Tag, "union") of 866 | 1 -> Module:urecord_to_erlptr(Value); 867 | _ -> undef 868 | end 869 | end. 870 | 871 | builtin_pointer_of(Value, Type) -> 872 | Types = get_types(), 873 | case dict:is_key(Type, Types) of 874 | true -> 875 | case dict:fetch(Type, Types) of 876 | {base, ["float", _, _]}-> 877 | float_ref(Value); 878 | {base, ["double", _, _]}-> 879 | double_ref(Value); 880 | _ -> 881 | case size_of(Type) of 882 | undef -> 883 | undef; 884 | Size -> 885 | case is_integer(Value) of 886 | true -> 887 | as_type(int_constr(Value, Size), "nifty."++Type++" *"); 888 | false -> 889 | undef 890 | end 891 | end 892 | end; 893 | false -> 894 | undef 895 | end. 896 | 897 | int_constr(Value, Size) -> 898 | mem_write(int_constr(Value, Size, [])). 899 | 900 | int_constr(_, 0, Acc) -> 901 | lists:reverse(Acc); 902 | int_constr(Val, S, Acc) -> 903 | R = Val rem 256, 904 | V = Val div 256, 905 | int_constr(V, S-1, [R|Acc]). 906 | 907 | raw_deref(_) -> 908 | erlang:nif_error(nif_library_not_loaded). 909 | 910 | %% @doc Writes the Data to the memory area pointed to by 911 | %% Ptr and returns a the pointer; the list elements are 912 | %% interpreted as byte values 913 | -spec mem_write(ptr(), binary() | list()) -> ptr(). 914 | mem_write({Addr, _} = Ptr, Data) -> 915 | {Addr, _} = case is_binary(Data) of 916 | true -> 917 | mem_write_binary(Data, Ptr); 918 | false -> 919 | mem_write_list(Data, Ptr) 920 | end, 921 | Ptr. 922 | 923 | %% @doc Writes the Data to memory and returns a nifty 924 | %% pointer to it; the list elements are interpreted as byte values 925 | -spec mem_write(binary() | list()) -> ptr(). 926 | mem_write(Data) -> 927 | case is_binary(Data) of 928 | true -> 929 | mem_write_binary(Data, mem_alloc(byte_size(Data))); 930 | false -> 931 | mem_write_list(Data, mem_alloc(length(Data))) 932 | end. 933 | 934 | -spec mem_write_list(list(), ptr()) -> ptr(). 935 | mem_write_list(_, _) -> 936 | erlang:nif_error(nif_library_not_loaded). 937 | 938 | -spec mem_write_binary(binary(), ptr()) -> ptr(). 939 | mem_write_binary(_, _) -> 940 | erlang:nif_error(nif_library_not_loaded). 941 | 942 | %% @doc Reads X2 bytes from the pointer X1 943 | %% and returns it as list 944 | -spec mem_read(ptr(), integer()) -> list(). 945 | mem_read(_, _) -> 946 | erlang:nif_error(nif_library_not_loaded). 947 | 948 | %% @doc Allocates X1 bytes and returns a pointer to it 949 | -spec mem_alloc(non_neg_integer()) -> ptr(). 950 | mem_alloc(_) -> 951 | erlang:nif_error(nif_library_not_loaded). 952 | 953 | %% @doc Copies Size bytes from Ptr1 to 954 | %% Ptr2 955 | -spec mem_copy(ptr(), ptr(), non_neg_integer()) -> ok. 956 | mem_copy(_, _, _) -> 957 | erlang:nif_error(nif_library_not_loaded). 958 | 959 | %% @doc Returns the platform specific configuration of nifty 960 | -spec get_config() -> proplists:proplist(). 961 | get_config() -> 962 | erlang:nif_error(nif_library_not_loaded). 963 | 964 | %% @doc Returns erlangs NIF environment 965 | -spec get_env() -> {integer(), nonempty_string()}. 966 | get_env() -> 967 | erlang:nif_error(nif_library_not_loaded). 968 | 969 | %% @doc Casts a pointer to Type; returns undef 970 | %% if the specified type is invalid 971 | -spec as_type(ptr(), nonempty_string()) -> ptr() | undef. 972 | as_type({Address, _} = Ptr, Type) -> 973 | BaseType = case string:tokens(Type, "*") of 974 | [T] -> 975 | string:strip(T); 976 | _ -> 977 | [] 978 | end, 979 | case dict:is_key(BaseType, get_types()) of 980 | true -> 981 | {Address, "nifty."++Type}; 982 | false -> 983 | case string:tokens(Type, ".") of 984 | ["nifty", TypeName] -> 985 | %% builtin type 986 | as_type(Ptr, TypeName); 987 | [ModuleName, TypeName] -> 988 | Mod = list_to_atom(ModuleName), 989 | case {module, Mod} =:= code:ensure_loaded(Mod) andalso 990 | lists:member({'__nifty__get_types',0}, Mod:module_info(exports)) of 991 | true -> 992 | %% resolve and build but we are looking for the basetype 993 | %% if the base type is defined or basetype * we are allowing 994 | %% casting 995 | [RBUType] = string:tokens(TypeName, "*"), 996 | RBType = string:strip(RBUType), 997 | ModTypes = Mod:'__nifty__get_types'(), 998 | case nifty_types:resolve_type(RBType, ModTypes) of 999 | undef -> 1000 | case nifty_types:resolve_type(RBType++" *", ModTypes) of 1001 | undef -> 1002 | %% unknown type 1003 | undef; 1004 | _ -> 1005 | %% pointer to incomplete type 1006 | {Address, Type} 1007 | end; 1008 | _ -> 1009 | %% pointer to complete type 1010 | {Address, Type} 1011 | end; 1012 | _ -> 1013 | %% module part of the type is not a nifty module 1014 | undef 1015 | end; 1016 | _ -> 1017 | %% malformed type 1018 | undef 1019 | end 1020 | end. 1021 | 1022 | %% @doc Allocates an array with Size elements of type 1023 | %% Type 1024 | -spec array_new(nonempty_string(), non_neg_integer()) -> ptr(). 1025 | array_new(Type, Size) -> 1026 | {Addr, _} = malloc(size_of(Type)*Size), 1027 | {Addr, referred_type(Type)}. 1028 | 1029 | array_element_type({_, Type}) -> 1030 | string:strip(lists:droplast(Type)). 1031 | 1032 | %% @doc returns a pointer to the element at position 1033 | %% Index of the array 1034 | -spec array_ith(ptr(), integer()) -> ptr(). 1035 | array_ith({Addr, Type} = Array, Index) -> 1036 | %% Type must be a pointer of the stored type 1037 | Offset = size_of(array_element_type(Array)), 1038 | {Addr + (Index * Offset), Type}. 1039 | 1040 | %% @doc returns the element at position Index of the array 1041 | -spec array_element(ptr(), integer()) -> cvalue(). 1042 | array_element(Array, Index) -> 1043 | dereference(array_ith(Array, Index)). 1044 | 1045 | %% @doc updates the element at position Index of the array 1046 | -spec array_set(ptr(), term(), integer()) -> ok. 1047 | array_set(Array, Value, Index) -> 1048 | ElementPtr = array_ith(Array, Index), 1049 | NewElement = pointer_of(Value, array_element_type(Array)), 1050 | Size = size_of(array_element_type(Array)), 1051 | mem_copy(NewElement, ElementPtr, Size), 1052 | free(NewElement). 1053 | 1054 | -spec array_to_list(ptr(), non_neg_integer()) -> [cvalue()]. 1055 | array_to_list(Array, N) -> 1056 | [array_element(Array, I) || I <- lists:seq(0,N)]. 1057 | 1058 | -spec list_to_array(list(), nonempty_string()) -> ptr(). 1059 | list_to_array(List, Type) -> 1060 | N = length(List), 1061 | A = array_new(Type, N), 1062 | _ = [array_set(A, E, I) || { I, E} <- lists:zip(lists:seq(0,N-1), List) ], 1063 | A. 1064 | --------------------------------------------------------------------------------