NumericToRawBytes.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. 'use strict';
  2. var GetIntrinsic = require('get-intrinsic');
  3. var $floor = GetIntrinsic('%Math.floor%');
  4. var $log = GetIntrinsic('%Math.log%');
  5. var $log2E = GetIntrinsic('%Math.LOG2E%');
  6. var $log2 = GetIntrinsic('%Math.log2%', true) || function log2(x) {
  7. return $log(x) * $log2E;
  8. };
  9. var $parseInt = GetIntrinsic('%parseInt%');
  10. var $pow = GetIntrinsic('%Math.pow%');
  11. var $Number = GetIntrinsic('%Number%');
  12. var $TypeError = GetIntrinsic('%TypeError%');
  13. var $BigInt = GetIntrinsic('%BigInt%', true);
  14. var callBound = require('call-bind/callBound');
  15. var $reverse = callBound('Array.prototype.reverse');
  16. var $numberToString = callBound('Number.prototype.toString');
  17. var $strSlice = callBound('String.prototype.slice');
  18. var abs = require('./abs');
  19. var hasOwnProperty = require('./HasOwnProperty');
  20. var ToBigInt64 = require('./ToBigInt64');
  21. var ToBigUint64 = require('./ToBigUint64');
  22. var ToInt16 = require('./ToInt16');
  23. var ToInt32 = require('./ToInt32');
  24. var ToInt8 = require('./ToInt8');
  25. var ToUint16 = require('./ToUint16');
  26. var ToUint32 = require('./ToUint32');
  27. var ToUint8 = require('./ToUint8');
  28. var ToUint8Clamp = require('./ToUint8Clamp');
  29. var Type = require('./Type');
  30. var isNaN = require('../helpers/isNaN');
  31. var isFinite = require('../helpers/isFinite');
  32. var keys = require('object-keys');
  33. // https://262.ecma-international.org/11.0/#table-the-typedarray-constructors
  34. var TypeToSizes = {
  35. __proto__: null,
  36. Int8: 1,
  37. Uint8: 1,
  38. Uint8C: 1,
  39. Int16: 2,
  40. Uint16: 2,
  41. Int32: 4,
  42. Uint32: 4,
  43. BigInt64: 8,
  44. BigUint64: 8,
  45. Float32: 4,
  46. Float64: 8
  47. };
  48. var TypeToAO = {
  49. __proto__: null,
  50. Int8: ToInt8,
  51. Uint8: ToUint8,
  52. Uint8C: ToUint8Clamp,
  53. Int16: ToInt16,
  54. Uint16: ToUint16,
  55. Int32: ToInt32,
  56. Uint32: ToUint32,
  57. BigInt64: ToBigInt64,
  58. BigUint64: ToBigUint64
  59. };
  60. // https://262.ecma-international.org/11.0/#sec-numerictorawbytes
  61. module.exports = function NumericToRawBytes(type, value, isLittleEndian) {
  62. if (typeof type !== 'string' || !hasOwnProperty(TypeToSizes, type)) {
  63. throw new $TypeError('Assertion failed: `type` must be a TypedArray element type: ' + keys(TypeToSizes));
  64. }
  65. if (Type(value) !== 'Number' && Type(value) !== 'BigInt') {
  66. throw new $TypeError('Assertion failed: `value` must be a Number or a BigInt');
  67. }
  68. if (Type(isLittleEndian) !== 'Boolean') {
  69. throw new $TypeError('Assertion failed: `isLittleEndian` must be a Boolean');
  70. }
  71. var rawBytes = [];
  72. var exponent;
  73. if (type === 'Float32') { // step 1
  74. if (isNaN(value)) {
  75. return isLittleEndian ? [0, 0, 192, 127] : [127, 192, 0, 0]; // hardcoded
  76. }
  77. var leastSig;
  78. if (value === 0) {
  79. leastSig = Object.is(value, -0) ? 0x80 : 0;
  80. return isLittleEndian ? [0, 0, 0, leastSig] : [leastSig, 0, 0, 0];
  81. }
  82. if (!isFinite(value)) {
  83. leastSig = value < 0 ? 255 : 127;
  84. return isLittleEndian ? [0, 0, 128, leastSig] : [leastSig, 128, 0, 0];
  85. }
  86. var sign = value < 0 ? 1 : 0;
  87. value = abs(value); // eslint-disable-line no-param-reassign
  88. exponent = 0;
  89. while (value >= 2) {
  90. exponent += 1;
  91. value /= 2; // eslint-disable-line no-param-reassign
  92. }
  93. while (value < 1) {
  94. exponent -= 1;
  95. value *= 2; // eslint-disable-line no-param-reassign
  96. }
  97. var mantissa = value - 1;
  98. mantissa *= $pow(2, 23);
  99. mantissa = $floor(mantissa);
  100. exponent += 127;
  101. exponent = exponent << 23;
  102. var result = sign << 31;
  103. result |= exponent;
  104. result |= mantissa;
  105. var byte0 = result & 255;
  106. result = result >> 8;
  107. var byte1 = result & 255;
  108. result = result >> 8;
  109. var byte2 = result & 255;
  110. result = result >> 8;
  111. var byte3 = result & 255;
  112. if (isLittleEndian) {
  113. return [byte0, byte1, byte2, byte3];
  114. }
  115. return [byte3, byte2, byte1, byte0];
  116. } else if (type === 'Float64') { // step 2
  117. if (value === 0) {
  118. leastSig = Object.is(value, -0) ? 0x80 : 0;
  119. return isLittleEndian ? [0, 0, 0, 0, 0, 0, 0, leastSig] : [leastSig, 0, 0, 0, 0, 0, 0, 0];
  120. }
  121. if (isNaN(value)) {
  122. return isLittleEndian ? [0, 0, 0, 0, 0, 0, 248, 127] : [127, 248, 0, 0, 0, 0, 0, 0];
  123. }
  124. if (!isFinite(value)) {
  125. var infBytes = value < 0 ? [0, 0, 0, 0, 0, 0, 240, 255] : [0, 0, 0, 0, 0, 0, 240, 127];
  126. return isLittleEndian ? infBytes : $reverse(infBytes);
  127. }
  128. var isNegative = value < 0;
  129. if (isNegative) { value = -value; } // eslint-disable-line no-param-reassign
  130. exponent = $floor($log2(value));
  131. var significand = (value / $pow(2, exponent)) - 1;
  132. var bitString = '';
  133. for (var i = 0; i < 52; i++) {
  134. significand *= 2;
  135. if (significand >= 1) {
  136. bitString += '1';
  137. significand -= 1;
  138. } else {
  139. bitString += '0';
  140. }
  141. }
  142. exponent += 1023;
  143. var exponentBits = $numberToString(exponent, 2);
  144. while (exponentBits.length < 11) { exponentBits = '0' + exponentBits; }
  145. var fullBitString = (isNegative ? '1' : '0') + exponentBits + bitString;
  146. while (fullBitString.length < 64) { fullBitString = '0' + fullBitString; }
  147. for (i = 0; i < 8; i++) {
  148. rawBytes[i] = $parseInt($strSlice(fullBitString, i * 8, (i + 1) * 8), 2);
  149. }
  150. return isLittleEndian ? $reverse(rawBytes) : rawBytes;
  151. } // step 3
  152. var n = TypeToSizes[type]; // step 3.a
  153. var convOp = TypeToAO[type]; // step 3.b
  154. var intValue = convOp(value); // step 3.c
  155. var isBigInt = type === 'BigInt64' || type === 'BigUint64';
  156. /*
  157. if (intValue >= 0) { // step 3.d
  158. // Let rawBytes be a List containing the n-byte binary encoding of intValue. If isLittleEndian is false, the bytes are ordered in big endian order. Otherwise, the bytes are ordered in little endian order.
  159. } else { // step 3.e
  160. // Let rawBytes be a List containing the n-byte binary 2's complement encoding of intValue. If isLittleEndian is false, the bytes are ordered in big endian order. Otherwise, the bytes are ordered in little endian order.
  161. }
  162. */
  163. if (intValue < 0) {
  164. intValue = intValue >>> 0;
  165. }
  166. var OxFF = isBigInt ? $BigInt(0xFF) : 0xFF;
  167. var eight = isBigInt ? $BigInt(8) : 8;
  168. for (i = 0; i < n; i++) {
  169. rawBytes[isLittleEndian ? i : n - 1 - i] = $Number(intValue & OxFF);
  170. intValue = intValue >> eight;
  171. }
  172. return rawBytes; // step 4
  173. };