@stdlib/strided
Version:
Strided.
238 lines (221 loc) • 8.64 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.
*/
/* eslint-disable max-len */
'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 binary function to two input strided arrays using alternative indexing semantics.
*
* ## Notes
*
* - The returned function has the following signature:
*
* ```text
* f( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY, dtypeZ, z, strideZ, offsetZ )
* ```
*
* 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**: input array.
* - **strideY**: `y` stride length.
* - **offsetY**: starting `y` index.
* - **dtypeZ**: `z` data type.
* - **z**: output array.
* - **strideZ**: `z` stride length.
* - **offsetZ**: starting `z` index.
*
* - The add-on function should have the following signature:
*
* ```text
* f( N, dtypeX, x, strideX, dtypeY, y, strideY, dtypeZ, z, strideZ )
* ```
*
* 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**: input array.
* - **strideY**: `y` stride length.
* - **dtypeZ**: `z` data type (enumeration constant).
* - **z**: output array.
* - **strideZ**: `z` stride length.
*
* - The fallback function should have the following signature:
*
* ```text
* f( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY, dtypeZ, z, strideZ, offsetZ )
* ```
*
* 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**: input array.
* - **strideY**: `y` stride length.
* - **offsetY**: starting `y` index.
* - **dtypeZ**: `z` data type.
* - **z**: output array.
* - **strideZ**: `z` stride length.
* - **offsetZ**: starting `z` 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, dtypeZ, z, strideZ ) {
* // Call into native add-on...
* }
*
* function fallback( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY, dtypeZ, z, strideZ, offsetZ ) {
* // 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', [ 3, 4 ], 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 - input array
* @param {integer} strideY - `y` stride length
* @param {NonNegativeInteger} offsetY - starting `y` index
* @param {*} dtypeZ - `z` data type
* @param {Collection} z - destination array
* @param {integer} strideZ - `z` stride length
* @param {NonNegativeInteger} offsetZ - starting `z` index
* @throws {TypeError} fifth argument must be a nonnegative integer
* @throws {TypeError} ninth argument must be a nonnegative integer
* @throws {TypeError} thirteenth argument must be a nonnegative integer
* @throws {TypeError} unable to resolve a strided array function supporting the provided array argument data types
* @returns {Collection} `z`
*/
function dispatcher( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY, dtypeZ, z, strideZ, offsetZ ) { // eslint-disable-line max-params
var viewX;
var viewY;
var viewZ;
// 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 ) ||
!isTypedArrayLike( z )
) {
fallback( N, dtypeX, x, strideX, offsetX, dtypeY, y, strideY, offsetY, dtypeZ, z, strideZ, offsetZ );
return z;
}
dtypeX = resolve( dtypeX );
dtypeY = resolve( dtypeY );
dtypeZ = resolve( dtypeZ );
if ( dtypeX === null || dtypeY === null || dtypeZ === 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. First input array offset must be a nonnegative integer. Value: `%s`.', offsetX ) );
}
if ( !isNonNegativeInteger( offsetY ) ) {
throw new TypeError( format( 'invalid argument. Second input array offset must be a nonnegative integer. Value: `%s`.', offsetY ) );
}
if ( !isNonNegativeInteger( offsetZ ) ) {
throw new TypeError( format( 'invalid argument. Output array offset must be a nonnegative integer. Value: `%s`.', offsetZ ) );
}
offsetX = minViewBufferIndex( N, strideX, offsetX );
offsetY = minViewBufferIndex( N, strideY, offsetY );
offsetZ = minViewBufferIndex( N, strideZ, offsetZ );
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 );
}
if ( dtypeZ === COMPLEX64 ) {
viewZ = reinterpretComplex64( z, offsetZ );
} else if ( dtypeZ === COMPLEX128 ) {
viewZ = reinterpretComplex128( z, offsetZ );
} else if ( dtypeZ === BOOLEAN ) {
viewZ = reinterpretBoolean( z, offsetZ );
} else {
viewZ = offsetView( z, offsetZ );
}
addon( N, dtypeX, viewX, strideX, dtypeY, viewY, strideY, dtypeZ, viewZ, strideZ );
return z;
}
}
// EXPORTS //
module.exports = dispatch;