main.c (10058B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2020 The Stdlib Authors. 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 19 #include "stdlib/strided/napi/addon_arguments.h" 20 #include "stdlib/ndarray/base/napi/typedarray_type_to_dtype.h" 21 #include "stdlib/ndarray/base/bytes_per_element.h" 22 #include "stdlib/math/base/assert/is_finite.h" 23 #include "stdlib/math/base/special/floor.h" 24 #include <node_api.h> 25 #include <assert.h> 26 #include <stdbool.h> 27 #include <stdint.h> 28 #include <stdlib.h> 29 30 /** 31 * Validates, extracts, and transforms (to native C types) function arguments provided to a strided array N-API add-on interface. 32 * 33 * ## Notes 34 * 35 * - The function assumes the following argument order: 36 * 37 * ```text 38 * [ N, ia1, is1, ia2, is2, ..., oa1, os1, oa2, os2, ... ] 39 * ``` 40 * 41 * where 42 * 43 * - `N` is the number of elements over which to iterate 44 * - `ia#` is an input strided array 45 * - `is#` is a corresponding input strided array stride (in units of elements) 46 * - `oa#` is an output strided array 47 * - `os#` is a corresponding output strided array stride (in units of elements) 48 * 49 * - The function may return one of the following JavaScript errors: 50 * 51 * - `TypeError`: first argument must be an integer 52 * - `TypeError`: input array stride argument must be an integer 53 * - `TypeError`: output array stride argument must be an integer 54 * - `TypeError`: input array argument must be a typed array 55 * - `TypeError`: output array argument must be a typed array 56 * - `RangeError`: input array argument must have sufficient elements based on the associated stride and the number of indexed elements 57 * - `RangeError`: output array argument must have sufficient elements based on the associated stride and the number of indexed elements 58 * 59 * @param env environment under which the function is invoked 60 * @param argv strided function arguments 61 * @param nargs total number of expected arguments 62 * @param nin number of input strided array arguments 63 * @param arrays destination array for storing pointers to both input and output strided byte arrays 64 * @param shape destination array for storing the array shape (dimensions) 65 * @param strides destination array for storing array strides (in bytes) for each strided array 66 * @param types destination array for storing strided array argument data types 67 * @param err pointer for storing a JavaScript error 68 * @return status code indicating success or failure (returns `napi_ok` if success) 69 * 70 * @example 71 * #include "stdlib/strided/napi/addon_arguments.h" 72 * #include <node_api.h> 73 * #include <stdint.h> 74 * #include <assert.h> 75 * 76 * // Add-on function... 77 * napi_value addon( napi_env env, napi_callback_info info ) { 78 * napi_status status; 79 * 80 * // ... 81 * 82 * int64_t nargs = 7; 83 * int64_t nin = 2; 84 * 85 * // Get callback arguments: 86 * size_t argc = 7; 87 * napi_value argv[ 7 ]; 88 * status = napi_get_cb_info( env, info, &argc, argv, nullptr, nullptr ); 89 * assert( status == napi_ok ); 90 * 91 * // ... 92 * 93 * // Process the provided arguments: 94 * uint8_t *arrays[ 3 ]; 95 * int64_t strides[ 3 ]; 96 * int64_t shape[ 1 ]; 97 * int32_t types[ 3 ]; 98 * 99 * napi_value err; 100 * status = stdlib_strided_napi_addon_arguments( env, argv, nargs, nin, arrays, shape, strides, types, &err ); 101 * assert( status == napi_ok ); 102 * 103 * // ... 104 * 105 * } 106 */ 107 napi_status stdlib_strided_napi_addon_arguments( const napi_env env, const napi_value *argv, const int64_t nargs, const int64_t nin, uint8_t *arrays[], int64_t *shape, int64_t *strides, int32_t *types, napi_value *err ) { 108 napi_status status; 109 int64_t i; 110 int64_t j; 111 112 // Reset the output error: 113 *err = NULL; 114 115 // Compute the index of the first output strided array argument: 116 int64_t iout = ( nin*2 ) + 1; 117 118 // The first argument is always the number of elements over which to iterate... 119 napi_valuetype vtype0; 120 status = napi_typeof( env, argv[ 0 ], &vtype0 ); 121 assert( status == napi_ok ); 122 if ( vtype0 != napi_number ) { 123 napi_value msg; 124 status = napi_create_string_utf8( env, "invalid argument. First argument must be an integer.", NAPI_AUTO_LENGTH, &msg ); 125 assert( status == napi_ok ); 126 127 napi_value code; 128 status = napi_create_string_utf8( env, "ERR_STRIDED_INVALID_ARGUMENT", NAPI_AUTO_LENGTH, &code ); 129 assert( status == napi_ok ); 130 131 napi_value error; 132 status = napi_create_type_error( env, code, msg, &error ); 133 assert( status == napi_ok ); 134 135 *err = error; 136 return napi_ok; 137 } 138 // Retrieve the number of indexed elements... 139 double N; 140 status = napi_get_value_double( env, argv[ 0 ], &N ); 141 assert( status == napi_ok ); 142 if ( !stdlib_base_is_finite( N ) || stdlib_base_floor( N ) != N ) { 143 napi_value msg; 144 status = napi_create_string_utf8( env, "invalid argument. First argument must be an integer.", NAPI_AUTO_LENGTH, &msg ); 145 assert( status == napi_ok ); 146 147 napi_value code; 148 status = napi_create_string_utf8( env, "ERR_STRIDED_INVALID_ARGUMENT", NAPI_AUTO_LENGTH, &code ); 149 assert( status == napi_ok ); 150 151 napi_value error; 152 status = napi_create_type_error( env, code, msg, &error ); 153 assert( status == napi_ok ); 154 155 *err = error; 156 return napi_ok; 157 } 158 shape[ 0 ] = (int64_t)N; 159 160 // Strides for both input and output strided arrays are every other argument beginning from the third argument... 161 for ( i = 2; i < nargs; i += 2 ) { 162 // Check that we were provided a number... 163 napi_valuetype vtype; 164 status = napi_typeof( env, argv[ i ], &vtype ); 165 assert( status == napi_ok ); 166 if ( vtype != napi_number ) { 167 napi_value msg; 168 if ( i < iout ) { 169 status = napi_create_string_utf8( env, "invalid argument. Input array stride argument must be an integer.", NAPI_AUTO_LENGTH, &msg ); 170 } else { 171 status = napi_create_string_utf8( env, "invalid argument. Output array stride argument must be an integer.", NAPI_AUTO_LENGTH, &msg ); 172 } 173 assert( status == napi_ok ); 174 175 napi_value code; 176 status = napi_create_string_utf8( env, "ERR_STRIDED_INVALID_ARGUMENT", NAPI_AUTO_LENGTH, &code ); 177 assert( status == napi_ok ); 178 179 napi_value error; 180 status = napi_create_type_error( env, code, msg, &error ); 181 assert( status == napi_ok ); 182 183 *err = error; 184 return napi_ok; 185 } 186 // Get the stride... 187 double stride; 188 status = napi_get_value_double( env, argv[ i ], &stride ); 189 assert( status == napi_ok ); 190 if ( !stdlib_base_is_finite( stride ) || stdlib_base_floor( stride ) != stride ) { 191 napi_value msg; 192 if ( i < iout ) { 193 status = napi_create_string_utf8( env, "invalid argument. Input array stride argument must be an integer.", NAPI_AUTO_LENGTH, &msg ); 194 } else { 195 status = napi_create_string_utf8( env, "invalid argument. Output array stride argument must be an integer.", NAPI_AUTO_LENGTH, &msg ); 196 } 197 assert( status == napi_ok ); 198 199 napi_value code; 200 status = napi_create_string_utf8( env, "ERR_STRIDED_INVALID_ARGUMENT", NAPI_AUTO_LENGTH, &code ); 201 assert( status == napi_ok ); 202 203 napi_value error; 204 status = napi_create_type_error( env, code, msg, &error ); 205 assert( status == napi_ok ); 206 207 *err = error; 208 return napi_ok; 209 } 210 // Set output data... 211 j = ( i-2 ) / 2; 212 strides[ j ] = (int64_t)stride; 213 } 214 // Input and output strided arrays are every other argument beginning from the second argument... 215 for ( i = 1; i < nargs; i += 2 ) { 216 // Check that we were provided a typed array... 217 bool res; 218 status = napi_is_typedarray( env, argv[ i ], &res ); 219 assert( status == napi_ok ); 220 if ( res == false ) { 221 napi_value msg; 222 if ( i < iout ) { 223 status = napi_create_string_utf8( env, "invalid argument. Input array argument must be a typed array.", NAPI_AUTO_LENGTH, &msg ); 224 } else { 225 status = napi_create_string_utf8( env, "invalid argument. Output array argument must be a typed array.", NAPI_AUTO_LENGTH, &msg ); 226 } 227 assert( status == napi_ok ); 228 229 napi_value code; 230 status = napi_create_string_utf8( env, "ERR_STRIDED_INVALID_ARGUMENT", NAPI_AUTO_LENGTH, &code ); 231 assert( status == napi_ok ); 232 233 napi_value error; 234 status = napi_create_type_error( env, code, msg, &error ); 235 assert( status == napi_ok ); 236 237 *err = error; 238 return napi_ok; 239 } 240 // Get the typed array data... 241 napi_typedarray_type vtype; 242 size_t len; 243 void *TypedArray; 244 status = napi_get_typedarray_info( env, argv[ i ], &vtype, &len, &TypedArray, NULL, NULL ); 245 assert( status == napi_ok ); 246 247 // Check that the provided array has sufficient elements... 248 j = ( i-1 ) / 2; 249 if ( (shape[0]-1)*llabs(strides[j]) >= (int64_t)len ) { 250 napi_value msg; 251 if ( i < iout ) { 252 status = napi_create_string_utf8( env, "invalid argument. Input array argument has insufficient elements based on the associated stride and the number of indexed elements.", NAPI_AUTO_LENGTH, &msg ); 253 } else { 254 status = napi_create_string_utf8( env, "invalid argument. Output array argument has insufficient elements based on the associated stride and the number of indexed elements.", NAPI_AUTO_LENGTH, &msg ); 255 } 256 assert( status == napi_ok ); 257 258 napi_value code; 259 status = napi_create_string_utf8( env, "ERR_STRIDED_INVALID_ARGUMENT", NAPI_AUTO_LENGTH, &code ); 260 assert( status == napi_ok ); 261 262 napi_value error; 263 status = napi_create_range_error( env, code, msg, &error ); 264 assert( status == napi_ok ); 265 266 *err = error; 267 return napi_ok; 268 } 269 // Set output data... 270 arrays[ j ] = (uint8_t *)TypedArray; 271 types[ j ] = stdlib_ndarray_napi_typedarray_type_to_dtype( vtype ); 272 strides[ j ] *= stdlib_ndarray_bytes_per_element( types[ j ] ); 273 } 274 return napi_ok; 275 }