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