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