main.js (6225B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2021 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 'use strict'; 20 21 // MODULES // 22 23 var IS_LITTLE_ENDIAN = require( '@stdlib/assert/is-little-endian' ); 24 var ArrayBuffer = require( '@stdlib/array/buffer' ); 25 var DataView = require( '@stdlib/array/dataview' ); 26 var BigInt = require( '@stdlib/bigint/ctor' ); 27 var bytesPerElement = require( './../../../base/bytes-per-element' ); 28 var dtypes = require( './../../../dtypes' ).enum; 29 var orders = require( './../../../orders' ).enum; 30 var modes = require( './../../../index-modes' ).enum; 31 32 33 // VARIABLES // 34 35 var DTYPES = dtypes(); 36 var ORDERS = orders(); 37 var MODES = modes(); 38 39 40 // MAIN // 41 42 /** 43 * Serializes ndarray meta data. 44 * 45 * ## Notes 46 * 47 * - This function takes into account ndarray-like objects which may support index modes. 48 * 49 * - Serialization is performed according to host byte order (endianness). 50 * 51 * - Meta data format: 52 * 53 * ```text 54 * | 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) | 55 * ``` 56 * 57 * which translates to the following `ArrayBuffer` layout: 58 * 59 * ```text 60 * ArrayBuffer[ 61 * <endianness>[int8], 62 * <dtype>[int16], 63 * <ndims>[int64], 64 * <shape>[ndims*int64], 65 * <strides>[ndims*int64], 66 * <offset>[int64], 67 * <order>[int8], 68 * <mode>[int8], 69 * <nsubmodes>[int64], 70 * <submodes>[nsubmodes*int8] 71 * ] 72 * ``` 73 * 74 * where `strides` and `offset` are in units of bytes. 75 * 76 * - If the endianness is `1`, the byte order is little endian. If the endianness is `0`, the byte order is big endian. 77 * 78 * - Buffer length: 79 * 80 * ```text 81 * 1 + 2 + 8 + (ndims*8) + (ndims*8) + 8 + 1 + 1 + 8 + (nsubmodes*1) = 29 + (ndims*16) + nsubmodes 82 * ``` 83 * 84 * For example, consider a three-dimensional ndarray with one subscript index mode (submode): 85 * 86 * ```text 87 * 29 + (3*16) + 1 = 78 bytes 88 * ``` 89 * 90 * - Views: 91 * 92 * - endianness: `Int8Array( buf, 0, 1 )` 93 * - dtype: `Int16Array( buf, 1, 1 )` 94 * - ndims: `Int64Array( buf, 3, 1 )` 95 * - shape: `Int64Array( buf, 11, ndims )` 96 * - strides: `Int64Array( buf, 11+(ndims*8), ndims )` 97 * - offset: `Int64Array( buf, 11+(ndims*16), 1 )` 98 * - order: `Int8Array( buf, 19+(ndims*16), 1 )` 99 * - mode: `Int8Array( buf, 20+(ndims*16), 1 )` 100 * - nsubmodes: `Int64Array( buf, 21+(ndims*16), 1 )` 101 * - submodes: `Int8Array( buf, 29+(ndims*16), nsubmodes )` 102 * 103 * @param {ndarrayLike} x - input array 104 * @param {string} x.dtype - array data type 105 * @param {NonNegativeIntegerArray} x.shape - array shape 106 * @param {IntegerArray} x.strides - array strides 107 * @param {NonNegativeInteger} x.offset - array index offset 108 * @param {string} x.order - array order 109 * @param {string} [x.mode='throw'] - array index mode 110 * @param {StringArray} [x.submode=[x.mode]] - array subscript index modes 111 * @returns {DataView} serialized meta data 112 * 113 * @example 114 * var array = require( '@stdlib/ndarray/array' ); 115 * 116 * var x = array( [ [ 1, 2 ], [ 3, 4 ] ] ); 117 * 118 * var dv = serialize( x ); 119 * // returns <DataView> 120 */ 121 function serialize( x ) { 122 var nbytes; 123 var len; 124 var dt; 125 var sh; 126 var st; 127 var sm; 128 var v; 129 var m; 130 var o; 131 var s; 132 var N; 133 var M; 134 var i; 135 136 // Check for interface which does the work of serializing to a DataView for us... 137 if ( x.__array_meta_dataview__ ) { // eslint-disable-line no-underscore-dangle 138 return x.__array_meta_dataview__(); // eslint-disable-line no-underscore-dangle 139 } 140 // Extract meta data known to be attached to ndarray-like objects: 141 dt = x.dtype; 142 sh = x.shape; 143 st = x.strides; 144 N = sh.length; // ndims 145 146 // Extract meta data which may be available on ndarray-like objects (e.g., stdlib ndarray instances): 147 m = x.mode || 'throw'; 148 sm = x.submode || [ m ]; 149 M = sm.length; 150 151 // Determine number of bytes per element according to the ndarray dtype: 152 nbytes = bytesPerElement( dt ); 153 154 // Compute the amount of memory we need to allocate for storing meta data: 155 len = 29 + (N*16) + M; 156 157 // Allocate raw memory and create a view for interfacing with the allocated memory: 158 v = new DataView( new ArrayBuffer( len ) ); 159 160 // Endianness: (byteoffset: 0; bytelength: 1) 161 o = 0; 162 v.setInt8( o, ( IS_LITTLE_ENDIAN ) ? 1 : 0 ); 163 164 // Data type: (byteoffset: 1; bytelength: 2) 165 o += 1; 166 v.setInt16( o, DTYPES[ dt ], IS_LITTLE_ENDIAN ); 167 168 // Number of dimensions: (byteoffset: 3; bytelength: 8) 169 o += 2; 170 v.setBigInt64( o, BigInt( N ), IS_LITTLE_ENDIAN ); 171 172 // Shape and strides: (byteoffset: 11 and 11+(ndims*8), respectively; bytelength: ndims*8 for both shape and strides, and, thus, ndims*16 total) 173 s = N * 8; // stride length between a dimension (shape[i]) and its associated stride 174 o += 8; 175 for ( i = 0; i < N; i++ ) { 176 v.setBigInt64( o, BigInt( sh[i] ), IS_LITTLE_ENDIAN ); 177 v.setBigInt64( o+s, BigInt( st[i]*nbytes ), IS_LITTLE_ENDIAN ); 178 o += 8; 179 } 180 // Offset: (byteoffset: 11+(ndims*16); bytelength: 8) 181 o += s; 182 v.setBigInt64( o, BigInt( x.offset*nbytes ), IS_LITTLE_ENDIAN ); 183 184 // Order: (byteoffset: 19+(ndims*16); bytelength: 1) 185 o += 8; 186 v.setInt8( o, ORDERS[ x.order ] ); 187 188 // Index mode: (byteoffset: 20+(ndims*16); bytelength: 1) 189 o += 1; 190 v.setInt8( o, MODES[ m ] ); 191 192 // Number of index submodes: (byteoffset: 21+(ndims*16); bytelength: 8) 193 o += 1; 194 v.setBigInt64( o, BigInt( M ), IS_LITTLE_ENDIAN ); 195 196 // Submodes: (byteoffset: 29+(ndims*16); bytelength: nsubmodes*1) 197 o += 8; 198 for ( i = 0; i < M; i++ ) { 199 v.setInt8( o, MODES[ sm[i] ] ); 200 o += 1; 201 } 202 return v; 203 } 204 205 206 // EXPORTS // 207 208 module.exports = serialize;