UNPKG

@stdlib/ndarray-base-ctor

Version:

Base multidimensional array.

613 lines (577 loc) 15.6 kB
/** * @license Apache-2.0 * * Copyright (c) 2018 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 no-restricted-syntax, no-invalid-this */ 'use strict'; // MODULES // var hasBigIntSupport = require( '@stdlib/assert-has-bigint-support' ); var setReadOnly = require( '@stdlib/utils-define-nonenumerable-read-only-property' ); var setReadOnlyAccessor = require( '@stdlib/utils-define-nonenumerable-read-only-accessor' ); var bytesPerElement = require( '@stdlib/ndarray-base-bytes-per-element' ); var iterationOrder = require( '@stdlib/ndarray-base-iteration-order' ); var strides2order = require( '@stdlib/ndarray-base-strides2order' ); var Boolean = require( '@stdlib/boolean-ctor' ); var isColumnMajorContiguous = require( './is_column_major_contiguous.js' ); var isRowMajorContiguous = require( './is_row_major_contiguous.js' ); var isContiguous = require( './is_contiguous.js' ); var copyFlags = require( './copy_flags.js' ); var igetValue = require( './iget.js' ); var isetValue = require( './iset.js' ); var setValue = require( './set.js' ); var getValue = require( './get.js' ); var toJSON = require( './tojson.js' ); var toString = require( './tostring.js' ); // eslint-disable-line stdlib/no-redeclare var meta2dataview = require( './meta2dataview.js' ); var meta2dataviewPolyfill = require( './meta2dataview.polyfill.js' ); // MAIN // /** * ndarray constructor. * * ## Notes * * - To create a zero-dimensional array, * * ```javascript * var buffer = [ 1 ]; * var shape = []; * var strides = [ 0 ]; * var offset = 0; * * var out = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * ``` * * @constructor * @param {string} dtype - data type * @param {(ArrayLikeObject|TypedArray|Buffer)} buffer - data buffer * @param {NonNegativeIntegerArray} shape - array shape * @param {IntegerArray} strides - array strides * @param {NonNegativeInteger} offset - index offset * @param {string} order - specifies whether an array is row-major (C-style) or column-major (Fortran-style) * @returns {ndarray} ndarray instance * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var out = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); */ function ndarray( dtype, buffer, shape, strides, offset, order ) { var contiguous; var nbytes; var ord; var len; var i; if ( !(this instanceof ndarray) ) { return new ndarray( dtype, buffer, shape, strides, offset, order ); } // Compute the number of elements... len = 1; for ( i = 0; i < shape.length; i++ ) { len *= shape[ i ]; } // Compute the number of bytes... if ( buffer.BYTES_PER_ELEMENT ) { nbytes = buffer.BYTES_PER_ELEMENT * len; } else { nbytes = null; } // Set private properties... this._byteLength = nbytes; this._bytesPerElement = bytesPerElement( dtype ); this._buffer = buffer; this._dtype = dtype; this._length = len; this._ndims = shape.length; this._offset = offset; this._order = order; this._shape = shape; this._strides = strides; this._accessors = Boolean( buffer.get && buffer.set ); this._iterationOrder = iterationOrder( strides ); // Determine if the array can be stored contiguously: contiguous = isContiguous( len, shape, strides, offset, this._iterationOrder ); // eslint-disable-line max-len // Infer the array "order" from the stride array (this is supplementary to the `order` parameter): ord = strides2order( strides ); this._flags = { 'ROW_MAJOR_CONTIGUOUS': isRowMajorContiguous( ord, contiguous ), 'COLUMN_MAJOR_CONTIGUOUS': isColumnMajorContiguous( ord, contiguous ), 'READONLY': false }; // Initialize a property for caching serialized meta data: this.__meta_dataview__ = null; return this; } /** * Constructor name. * * @name name * @memberof ndarray * @type {string} * @default 'ndarray' * * @example * var str = ndarray.name; * // returns 'ndarray' */ setReadOnly( ndarray, 'name', 'ndarray' ); /** * Size (in bytes) of the array (if known). * * @name byteLength * @memberof ndarray.prototype * @type {(NonNegativeInteger|null)} * * @example * var Float64Array = require( '@stdlib/array-float64' ); * * var buffer = new Float64Array( [ 1, 2, 3, 4, 5, 6 ] ); * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'float64', buffer, shape, strides, offset, 'row-major' ); * * var byteLength = x.byteLength; * // returns 48 */ setReadOnlyAccessor( ndarray.prototype, 'byteLength', function get() { return this._byteLength; }); /** * Size (in bytes) of each array element (if known). * * @name BYTES_PER_ELEMENT * @memberof ndarray.prototype * @type {(PositiveInteger|null)} * * @example * var Float64Array = require( '@stdlib/array-float64' ); * * var buffer = new Float64Array( [ 1, 2, 3, 4, 5, 6 ] ); * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'float64', buffer, shape, strides, offset, 'row-major' ); * * var nbytes = x.BYTES_PER_ELEMENT; * // returns 8 */ setReadOnlyAccessor( ndarray.prototype, 'BYTES_PER_ELEMENT', function get() { return this._bytesPerElement; }); /** * Pointer to the underlying data buffer. * * @name data * @memberof ndarray.prototype * @type {(Array|TypedArray|Buffer)} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var data = x.data; * // returns [ 1, 2, 3, 4, 5, 6 ] */ setReadOnlyAccessor( ndarray.prototype, 'data', function get() { return this._buffer; }); /** * Underlying data type. * * @name dtype * @memberof ndarray.prototype * @type {string} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var dtype = x.dtype; * // returns 'generic' */ setReadOnlyAccessor( ndarray.prototype, 'dtype', function get() { return this._dtype; }); /** * Meta information, such as information concerning the memory layout of the array. * * @name flags * @memberof ndarray.prototype * @type {Object} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var flgs = x.flags; * // returns <Object> */ setReadOnlyAccessor( ndarray.prototype, 'flags', function get() { return copyFlags( this._flags ); }); /** * Length of the array. * * @name length * @memberof ndarray.prototype * @type {NonNegativeInteger} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var len = x.length; * // returns 6 */ setReadOnlyAccessor( ndarray.prototype, 'length', function get() { return this._length; }); /** * Number of dimensions. * * @name ndims * @memberof ndarray.prototype * @type {PositiveInteger} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var ndims = x.ndims; * // returns 2 */ setReadOnlyAccessor( ndarray.prototype, 'ndims', function get() { return this._ndims; }); /** * Index offset which specifies the buffer index at which to start iterating over array elements. * * @name offset * @memberof ndarray.prototype * @type {NonNegativeInteger} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var o = x.offset; * // returns 0 */ setReadOnlyAccessor( ndarray.prototype, 'offset', function get() { return this._offset; }); /** * Array order. * * ## Notes * * - The array order is either row-major (C-style) or column-major (Fortran-style). * * @name order * @memberof ndarray.prototype * @type {string} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var order = x.order; * // returns 'row-major' */ setReadOnlyAccessor( ndarray.prototype, 'order', function get() { return this._order; }); /** * Shape of the array. * * @name shape * @memberof ndarray.prototype * @type {NonNegativeIntegerArray} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var s = x.shape; * // returns [ 3, 2 ] */ setReadOnlyAccessor( ndarray.prototype, 'shape', function get() { return this._shape.slice(); }); /** * Index strides which specify how to access data along corresponding array dimensions. * * @name strides * @memberof ndarray.prototype * @type {IntegerArray} * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var s = x.strides; * // returns [ 2, 1 ] */ setReadOnlyAccessor( ndarray.prototype, 'strides', function get() { return this._strides.slice(); }); /** * Returns an array element. * * ## Notes * * - The number of indices should **equal** the number of dimensions. Accordingly, for zero-dimensional arrays, no indices should be provided. * * @name get * @memberof ndarray.prototype * @type {Function} * @param {...integer} [idx] - indices * @returns {*} array element * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var v = x.get( 1, 1 ); * // returns 4 */ setReadOnly( ndarray.prototype, 'get', getValue ); /** * Returns an array element located at a specified linear index. * * ## Notes * * - For zero-dimensional arrays, the input argument is ignored and, for clarity, should not be provided. * * @name iget * @memberof ndarray.prototype * @type {Function} * @param {integer} [idx] - linear index * @returns {*} array element * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var v = x.iget( 3 ); * // returns 4 */ setReadOnly( ndarray.prototype, 'iget', igetValue ); /** * Sets an array element. * * ## Notes * * - The number of indices should **equal** the number of dimensions. Accordingly, for zero-dimensional arrays, no indices should be provided. * * @name set * @memberof ndarray.prototype * @type {Function} * @param {...integer} [idx] - indices * @param {*} v - value to set * @returns {ndarray} ndarray instance * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var v = x.get( 1, 1 ); * // returns 4 * * x.set( 1, 1, 10 ); * * var b = x.data; * // returns [ 1, 2, 3, 10, 5, 6 ] * * v = x.get( 1, 1 ); * // returns 10 */ setReadOnly( ndarray.prototype, 'set', setValue ); /** * Sets an array element located at a specified linear index. * * ## Notes * * - For zero-dimensional arrays, the first, and only, argument should be the value to set. * * @name iset * @memberof ndarray.prototype * @type {Function} * @param {integer} [idx] - linear index * @param {*} v - value to set * @returns {ndarray} ndarray instance * * @example * var buffer = [ 1, 2, 3, 4, 5, 6 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 0; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var v = x.iget( 3 ); * // returns 4 * * x.iset( 3, 10 ); * * var b = x.data; * // returns [ 1, 2, 3, 10, 5, 6 ] * * v = x.iget( 3 ); * // returns 10 */ setReadOnly( ndarray.prototype, 'iset', isetValue ); /** * Serializes an ndarray as a string. * * ## Notes * * - The method does **not** serialize data outside of the buffer region defined by the array configuration. * * @name toString * @memberof ndarray.prototype * @type {Function} * @returns {string} serialized ndarray * * @example * var buffer = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 2; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var str = x.toString(); * // returns "ndarray( 'generic', [ 3, 4, 5, 6, 7, 8 ], [ 3, 2 ], [ 2, 1 ], 0, 'row-major' )" */ setReadOnly( ndarray.prototype, 'toString', toString ); /** * Serializes an ndarray as a JSON object. * * ## Notes * * - `JSON.stringify()` implicitly calls this method when stringifying an `ndarray` instance. * - The method does **not** serialize data outside of the buffer region defined by the array configuration. * * @name toJSON * @memberof ndarray.prototype * @type {Function} * @returns {Object} serialized ndarray * * @example * var buffer = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 2; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var o = x.toJSON(); * // e.g., returns { 'type': 'ndarray', 'dtype': 'generic', 'flags': {...}, 'offset': 0, 'order': 'row-major', 'shape': [ 3, 2 ], 'strides': [ 2, 1 ], 'data': [ 3, 4, 5, 6, 7, 8 ] } */ setReadOnly( ndarray.prototype, 'toJSON', toJSON ); /** * Serializes ndarray meta data to a `DataView`. * * ## Notes * * - Meta data format: * * ```text * | <endianness> (1 byte) | <dtype> (2 bytes) | <ndims> (8 bytes) | <shape> (ndims*8 bytes) | <strides> (ndims*8 bytes) | <offset> (8 bytes) | <order> (1 byte) | <mode> (1 byte) | <nsubmodes> (8 bytes) | <submodes> (nsubmodes*1 bytes) | <flags> (4 bytes) | * ``` * * where `strides` and `offset` are in units of bytes. * * - If the endianness is `1`, the byte order is little endian. If the endianness is `0`, the byte order is big endian. * * - Serialization is performed according to host byte order (endianness). * * - Consumers of this method should treat the returned `DataView` as **immutable**. Otherwise, mutation can invalidate meta data and potentially affect other consumers. * * @private * @name __array_meta_dataview__ * @memberof ndarray.prototype * @type {Function} * @returns {DataView} serialized meta data * * @example * var buffer = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; * var shape = [ 3, 2 ]; * var strides = [ 2, 1 ]; * var offset = 2; * * var x = ndarray( 'generic', buffer, shape, strides, offset, 'row-major' ); * * var dv = x.__array_meta_dataview__(); * // returns <DataView> */ setReadOnly( ndarray.prototype, '__array_meta_dataview__', ( hasBigIntSupport() ) ? meta2dataview : meta2dataviewPolyfill ); // EXPORTS // module.exports = ndarray;