NumberToRawBytes.js 5.4 KB

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