1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2010-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 /*
21  * Purpose: Simple example of NIFs using resource objects to implement functions
22  *          for matrix calculations.
23  */
24 
25 #include <erl_nif.h>
26 
27 #include <stddef.h>
28 #include <assert.h>
29 
30 typedef struct
31 {
32     unsigned nrows;
33     unsigned ncols;
34     double* data;
35 } Matrix;
36 
37 /*
38  * Use a union for pointer type conversion to avoid compiler warnings
39  * about strict-aliasing violations with gcc-4.1. gcc >= 4.2 does not
40  * emit the warning.
41  * TODO: Reconsider use of union once gcc-4.1 is obsolete?
42  */
43 typedef union
44 {
45     void* vp;
46     Matrix* p;
47 } mx_t;
48 
49 #define POS(MX, ROW, COL) ((MX)->data[(ROW)* (MX)->ncols + (COL)])
50 
51 static int get_number(ErlNifEnv* env, ERL_NIF_TERM term, double* dp);
52 static Matrix* alloc_matrix(ErlNifEnv* env, unsigned nrows, unsigned ncols);
53 static void matrix_dtor(ErlNifEnv* env, void* obj);
54 
55 
56 static ErlNifResourceType* resource_type = NULL;
57 
load(ErlNifEnv * env,void ** priv_data,ERL_NIF_TERM load_info)58 static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
59 {
60     ErlNifResourceType* rt = enif_open_resource_type(env, NULL,
61 						     "matrix_nif_example",
62 						     matrix_dtor,
63 						     ERL_NIF_RT_CREATE, NULL);
64     if (rt == NULL) {
65 	return -1;
66     }
67     assert(resource_type == NULL);
68     resource_type = rt;
69     return 0;
70 }
71 
create(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])72 static ERL_NIF_TERM create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
73 {
74     /* create(Nrows, Ncolumns, [[first row],[second row],...,[last row]]) -> Matrix */
75     unsigned nrows, ncols;
76     unsigned i, j;
77     ERL_NIF_TERM list, row, ret;
78     Matrix* mx = NULL;
79 
80     if (!enif_get_uint(env, argv[0], &nrows) || nrows < 1 ||
81 	!enif_get_uint(env, argv[1], &ncols) || ncols < 1) {
82 
83 	goto badarg;
84     }
85     mx = alloc_matrix(env, nrows, ncols);
86     list = argv[2];
87     for (i = 0; i<nrows; i++) {
88 	if (!enif_get_list_cell(env, list, &row, &list)) {
89 	    goto badarg;
90 	}
91 	for (j = 0; j<ncols; j++) {
92 	    ERL_NIF_TERM v;
93 	    if (!enif_get_list_cell(env, row, &v, &row) ||
94 		!get_number(env, v, &POS(mx,i,j))) {
95 		goto badarg;
96 	    }
97 	}
98 	if (!enif_is_empty_list(env, row)) {
99 	    goto badarg;
100 	}
101     }
102     if (!enif_is_empty_list(env, list)) {
103 	goto badarg;
104     }
105 
106     ret = enif_make_resource(env, mx);
107     enif_release_resource(mx);
108     return ret;
109 
110 badarg:
111     if (mx != NULL) {
112 	enif_release_resource(mx);
113     }
114     return enif_make_badarg(env);
115 }
116 
117 
pos(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])118 static ERL_NIF_TERM pos(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
119 {
120     /* pos(Matrix, Row, Column) -> float() */
121     mx_t mx;
122     unsigned i, j;
123     if (!enif_get_resource(env, argv[0], resource_type, &mx.vp) ||
124 	!enif_get_uint(env, argv[1], &i) || (--i >= mx.p->nrows) ||
125 	!enif_get_uint(env, argv[2], &j) || (--j >= mx.p->ncols)) {
126 	return enif_make_badarg(env);
127     }
128     return enif_make_double(env, POS(mx.p, i,j));
129 }
130 
add(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])131 static ERL_NIF_TERM add(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
132 {
133     /* add(Matrix_A, Matrix_B) -> Matrix_Sum */
134     unsigned i, j;
135     ERL_NIF_TERM ret;
136     mx_t mxA, mxB, mxS;
137     mxA.p = NULL;
138     mxB.p = NULL;
139     mxS.p = NULL;
140 
141     if (!enif_get_resource(env, argv[0], resource_type, &mxA.vp) ||
142 	!enif_get_resource(env, argv[1], resource_type, &mxB.vp) ||
143 	mxA.p->nrows != mxB.p->nrows ||
144 	mxB.p->ncols != mxB.p->ncols) {
145 
146     	return enif_make_badarg(env);
147     }
148     mxS.p = alloc_matrix(env, mxA.p->nrows, mxA.p->ncols);
149     for (i = 0; i < mxA.p->nrows; i++) {
150 	for (j = 0; j < mxA.p->ncols; j++) {
151 	    POS(mxS.p, i, j) = POS(mxA.p, i, j) + POS(mxB.p, i, j);
152 	}
153     }
154     ret = enif_make_resource(env, mxS.p);
155     enif_release_resource(mxS.p);
156     return ret;
157 }
158 
size_of(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])159 static ERL_NIF_TERM size_of(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
160 {
161     /* size(Matrix) -> {Nrows, Ncols} */
162     mx_t mx;
163     if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
164 	return enif_make_badarg(env);
165     }
166     return enif_make_tuple2(env, enif_make_uint(env, mx.p->nrows),
167 			    enif_make_uint(env, mx.p->ncols));
168 }
169 
to_term(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])170 static ERL_NIF_TERM to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
171 {
172     /* to_term(Matrix) -> [[first row], [second row], ...,[last row]] */
173     unsigned i, j;
174     ERL_NIF_TERM res;
175     mx_t mx;
176     mx.p = NULL;
177 
178     if (!enif_get_resource(env, argv[0], resource_type, &mx.vp)) {
179     	return enif_make_badarg(env);
180     }
181     res = enif_make_list(env, 0);
182     for (i = mx.p->nrows; i-- > 0; ) {
183 	ERL_NIF_TERM row = enif_make_list(env, 0);
184 	for (j = mx.p->ncols; j-- > 0; ) {
185 	    row = enif_make_list_cell(env, enif_make_double(env, POS(mx.p,i,j)),
186 				      row);
187 	}
188 	res = enif_make_list_cell(env, row, res);
189     }
190     return res;
191 }
192 
get_number(ErlNifEnv * env,ERL_NIF_TERM term,double * dp)193 static int get_number(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)
194 {
195     long i;
196     return enif_get_double(env, term, dp) ||
197 	(enif_get_long(env, term, &i) && (*dp=(double)i, 1));
198 }
199 
alloc_matrix(ErlNifEnv * env,unsigned nrows,unsigned ncols)200 static Matrix* alloc_matrix(ErlNifEnv* env, unsigned nrows, unsigned ncols)
201 {
202     Matrix* mx = enif_alloc_resource(resource_type, sizeof(Matrix));
203     mx->nrows = nrows;
204     mx->ncols = ncols;
205     mx->data = enif_alloc(nrows*ncols*sizeof(double));
206     return mx;
207 }
208 
matrix_dtor(ErlNifEnv * env,void * obj)209 static void matrix_dtor(ErlNifEnv* env, void* obj)
210 {
211     Matrix* mx = (Matrix*) obj;
212     enif_free(mx->data);
213     mx->data = NULL;
214 }
215 
216 static ErlNifFunc nif_funcs[] =
217 {
218     {"create", 3, create},
219     {"pos", 3, pos},
220     {"add", 2, add},
221     {"size_of", 1, size_of},
222     {"to_term", 1, to_term}
223 };
224 
225 ERL_NIF_INIT(matrix_nif,nif_funcs,load,NULL,NULL,NULL);
226 
227