meta2dataview.polyfill.js (5878B)
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 Uint8Array = require( '@stdlib/array/uint8' ); 27 var dtypes = require( './../../../dtypes' ).enum; 28 var orders = require( './../../../orders' ).enum; 29 var modes = require( './../../../index-modes' ).enum; 30 var float64ToInt64Bytes = require( '@stdlib/number/float64/base/to-int64-bytes' ).assign; 31 32 33 // VARIABLES // 34 35 var DTYPES = dtypes(); 36 var ORDERS = orders(); 37 var MODES = modes(); 38 39 40 // FUNCTIONS // 41 42 /** 43 * Serializes ndarray meta data to a `DataView`. 44 * 45 * ## Notes 46 * 47 * - This function takes into account ndarray-like objects which may support index modes. 48 * 49 * - This function defaults to returning cached serialized meta data. To force serialization, set the private `__meta_dataview__` property to `null`. 50 * 51 * - Serialization is performed according to host byte order (endianness). 52 * 53 * - Meta data format: 54 * 55 * ```text 56 * | 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) | 57 * ``` 58 * 59 * which translates to the following `ArrayBuffer` layout: 60 * 61 * ```text 62 * ArrayBuffer[ 63 * <endianness>[int8], 64 * <dtype>[int16], 65 * <ndims>[int64], 66 * <shape>[ndims*int64], 67 * <strides>[ndims*int64], 68 * <offset>[int64], 69 * <order>[int8], 70 * <mode>[int8], 71 * <nsubmodes>[int64], 72 * <submodes>[nsubmodes*int8] 73 * ] 74 * ``` 75 * 76 * where `strides` and `offset` are in units of bytes. 77 * 78 * - If the endianness is `1`, the byte order is little endian. If the endianness is `0`, the byte order is big endian. 79 * 80 * - Buffer length: 81 * 82 * ```text 83 * 1 + 2 + 8 + (ndims*8) + (ndims*8) + 8 + 1 + 1 + 8 + (nsubmodes*1) = 29 + (ndims*16) + nsubmodes 84 * ``` 85 * 86 * For example, consider a three-dimensional ndarray with one subscript index mode (submode): 87 * 88 * ```text 89 * 29 + (3*16) + 1 = 78 bytes 90 * ``` 91 * 92 * - Views: 93 * 94 * - endianness: `Int8Array( buf, 0, 1 )` 95 * - dtype: `Int16Array( buf, 1, 1 )` 96 * - ndims: `Int64Array( buf, 3, 1 )` 97 * - shape: `Int64Array( buf, 11, ndims )` 98 * - strides: `Int64Array( buf, 11+(ndims*8), ndims )` 99 * - offset: `Int64Array( buf, 11+(ndims*16), 1 )` 100 * - order: `Int8Array( buf, 19+(ndims*16), 1 )` 101 * - mode: `Int8Array( buf, 20+(ndims*16), 1 )` 102 * - nsubmodes: `Int64Array( buf, 21+(ndims*16), 1 )` 103 * - submodes: `Int8Array( buf, 29+(ndims*16), nsubmodes )` 104 * 105 * @private 106 * @returns {DataView} serialized meta data 107 */ 108 function meta2dataview() { 109 /* eslint-disable no-invalid-this */ 110 var nbytes; 111 var bytes; 112 var len; 113 var dt; 114 var sh; 115 var st; 116 var sm; 117 var v; 118 var m; 119 var o; 120 var s; 121 var N; 122 var M; 123 var i; 124 125 m = this._mode || 'throw'; 126 sm = this._submode || [ m ]; 127 N = this._ndims; 128 M = sm.length; 129 130 // Compute the amount of memory we need to allocate for storing meta data: 131 len = 29 + (N*16) + M; 132 133 // Check if we've already serialized ndarray meta data and can reuse an already allocated array buffer... 134 v = this.__meta_dataview__; 135 if ( v && v.byteLength === len ) { // Note: the byte length check is only a bare minimum sanity check, as cached contents may still be "stale" (e.g., shape and/or strides may have changed) 136 return v; 137 } 138 // Allocate raw memory and create views for interfacing with the allocated memory: 139 v = new DataView( new ArrayBuffer( len ) ); 140 bytes = new Uint8Array( v.buffer ); 141 142 // Retrieve ndarray meta data: 143 sh = this._shape; 144 st = this._strides; 145 dt = this._dtype; 146 nbytes = this._bytesPerElement; 147 148 // Endianness: (byteoffset: 0; bytelength: 1) 149 o = 0; 150 v.setInt8( o, ( IS_LITTLE_ENDIAN ) ? 1 : 0 ); 151 152 // Data type: (byteoffset: 1; bytelength: 2) 153 o += 1; 154 v.setInt16( o, DTYPES[ dt ], IS_LITTLE_ENDIAN ); 155 156 // Number of dimensions: (byteoffset: 3; bytelength: 8) 157 o += 2; 158 float64ToInt64Bytes( N, bytes, 1, o ); 159 160 // Shape and strides: (byteoffset: 11 and 11+(ndims*8), respectively; bytelength: ndims*8 for both shape and strides, and, thus, ndims*16 total) 161 s = N * 8; // stride length between a dimension (shape[i]) and its associated stride 162 o += 8; 163 for ( i = 0; i < N; i++ ) { 164 float64ToInt64Bytes( sh[i], bytes, 1, o ); 165 float64ToInt64Bytes( st[i]*nbytes, bytes, 1, o+s ); 166 o += 8; 167 } 168 // Offset: (byteoffset: 11+(ndims*16); bytelength: 8) 169 o += s; 170 float64ToInt64Bytes( this._offset*nbytes, bytes, 1, o ); 171 172 // Order: (byteoffset: 19+(ndims*16); bytelength: 1) 173 o += 8; 174 v.setInt8( o, ORDERS[ this._order ] ); 175 176 // Mode: (byteoffset: 20+(ndims*16); bytelength: 1) 177 o += 1; 178 v.setInt8( o, MODES[ m ] ); 179 180 // Number of submodes: (byteoffset: 21+(ndims*16); bytelength: 8) 181 o += 1; 182 float64ToInt64Bytes( M, bytes, 1, o ); 183 184 // Submodes: (byteoffset: 29+(ndims*16); bytelength: nsubmodes*1) 185 o += 8; 186 for ( i = 0; i < M; i++ ) { 187 v.setInt8( o, MODES[ sm[i] ] ); 188 o += 1; 189 } 190 // Cache the serialized meta data: 191 this.__meta_dataview__ = v; 192 193 return v; 194 195 /* eslint-enable no-invalid-this */ 196 } 197 198 199 // EXPORTS // 200 201 module.exports = meta2dataview;