@stdlib/strided
Version:
Strided.
200 lines (185 loc) • 7.06 kB
JavaScript
/**
* @license Apache-2.0
*
* Copyright (c) 2024 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
// MODULES //
var isFunction = require( '@stdlib/assert/is-function' );
var isTypedArrayLike = require( '@stdlib/assert/is-typed-array-like' );
var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive;
var resolve = require( './../../../base/dtype-resolve-enum' );
var reinterpretComplex64 = require( './../../../base/reinterpret-complex64' );
var reinterpretComplex128 = require( './../../../base/reinterpret-complex128' );
var reinterpretBoolean = require( './../../../base/reinterpret-boolean' );
var offsetView = require( './../../../base/offset-view' );
var minViewBufferIndex = require( './../../../base/min-view-buffer-index' );
var format = require( '@stdlib/string/format' );
// VARIABLES //
var COMPLEX64 = resolve( 'complex64' );
var COMPLEX128 = resolve( 'complex128' );
var BOOLEAN = resolve( 'bool' );
// MAIN //
/**
* Returns a function which dispatches to a native add-on applying a unary function to an input strided array using alternative indexing semantics.
*
* ## Notes
*
* - The returned function has the following signature:
*
* ```text
* f( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY )
* ```
*
* where
*
* - **N**: number of indexed elements.
* - **dtypeX**: `x` data type.
* - **x**: input array.
* - **strideX**: `x` stride length.
* - **offsetX**: starting `x` index.
* - **dtypeY**: `y` data type.
* - **y**: output array.
* - **strideY**: `y` stride length.
* - **offsetY**: starting `y` index.
*
* - The add-on function should have the following signature:
*
* ```text
* f( N, dtypeX, x, strideX, dtypeY, y, strideY )
* ```
*
* where
*
* - **N**: number of indexed elements.
* - **dtypeX**: `x` data type (enumeration constant).
* - **x**: input array.
* - **strideX**: `x` stride length.
* - **dtypeY**: `y` data type (enumeration constant).
* - **y**: output array.
* - **strideY**: `y` stride length.
*
* - The fallback function should have the following signature:
*
* ```text
* f( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY )
* ```
*
* where
*
* - **N**: number of indexed elements.
* - **dtypeX**: `x` data type.
* - **x**: input array.
* - **strideX**: `x` stride length.
* - **offsetX**: starting `x` index.
* - **dtypeY**: `y` data type.
* - **y**: output array.
* - **strideY**: `y` stride length.
* - **offsetY**: starting `y` index.
*
* @param {Function} addon - add-on interface
* @param {Function} fallback - fallback function
* @throws {TypeError} first argument must be a function
* @throws {TypeError} second argument must be a function
* @returns {Function} dispatch function
*
* @example
* function addon( N, dtypeX, x, strideX, dtypeY, y, strideY ) {
* // Call into native add-on...
* }
*
* function fallback( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY ) {
* // Fallback JavaScript implementation...
* }
*
* // Create a dispatch function:
* var f = dispatch( addon, fallback );
*
* // ...
*
* // Invoke the dispatch function with strided array arguments:
* f( 2, 'generic', [ 1, 2 ], 1, 0, 'generic', [ 0, 0 ], 1, 0 );
*/
function dispatch( addon, fallback ) {
if ( !isFunction( addon ) ) {
throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', addon ) );
}
if ( !isFunction( fallback ) ) {
throw new TypeError( format( 'invalid argument. Second argument must be a function. Value: `%s`.', fallback ) );
}
return dispatcher;
/**
* Dispatches to a native add-on.
*
* @private
* @param {integer} N - number of indexed elements
* @param {*} dtypeX - `x` data type
* @param {Collection} x - input array
* @param {integer} strideX - `x` stride length
* @param {NonNegativeInteger} offsetX - starting `x` index
* @param {*} dtypeY - `y` data type
* @param {Collection} y - destination array
* @param {integer} strideY - `y` stride length
* @param {NonNegativeInteger} offsetY - starting `y` index
* @throws {TypeError} fifth argument must be a nonnegative integer
* @throws {TypeError} ninth argument must be a nonnegative integer
* @throws {TypeError} unable to resolve a strided array function supporting the provided array argument data types
* @returns {Collection} `y`
*/
function dispatcher( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY ) { // eslint-disable-line max-len
var viewX;
var viewY;
// WARNING: we assume that, if we're provided something resembling a typed array, we're provided a typed array; however, this can lead to potential unintended errors as the native add-on may not work with non-typed array objects (e.g., generic arrays)...
if ( !isTypedArrayLike( x ) || !isTypedArrayLike( y ) ) {
fallback( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY ); // eslint-disable-line max-len
return y;
}
dtypeX = resolve( dtypeX );
dtypeY = resolve( dtypeY );
if ( dtypeX === null || dtypeY === null ) {
throw new TypeError( 'invalid arguments. Unable to resolve a strided array function supporting the provided array argument data types.' );
}
if ( !isNonNegativeInteger( offsetX ) ) {
throw new TypeError( format( 'invalid argument. Input array offset must be a nonnegative integer. Value: `%s`.', offsetX ) );
}
if ( !isNonNegativeInteger( offsetY ) ) {
throw new TypeError( format( 'invalid argument. Output array offset must be a nonnegative integer. Value: `%s`.', offsetY ) );
}
offsetX = minViewBufferIndex( N, strideX, offsetX );
offsetY = minViewBufferIndex( N, strideY, offsetY );
if ( dtypeX === COMPLEX64 ) {
viewX = reinterpretComplex64( x, offsetX );
} else if ( dtypeX === COMPLEX128 ) {
viewX = reinterpretComplex128( x, offsetX );
} else if ( dtypeX === BOOLEAN ) {
viewX = reinterpretBoolean( x, offsetX );
} else {
viewX = offsetView( x, offsetX );
}
if ( dtypeY === COMPLEX64 ) {
viewY = reinterpretComplex64( y, offsetY );
} else if ( dtypeY === COMPLEX128 ) {
viewY = reinterpretComplex128( y, offsetY );
} else if ( dtypeY === BOOLEAN ) {
viewY = reinterpretBoolean( y, offsetY );
} else {
viewY = offsetView( y, offsetY );
}
addon( N, dtypeX, viewX, strideX, dtypeY, viewY, strideY );
return y;
}
}
// EXPORTS //
module.exports = dispatch;