You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
4.9 KiB
189 lines
4.9 KiB
"use strict"; |
|
|
|
var conversions = {}; |
|
module.exports = conversions; |
|
|
|
function sign(x) { |
|
return x < 0 ? -1 : 1; |
|
} |
|
|
|
function evenRound(x) { |
|
// Round x to the nearest integer, choosing the even integer if it lies halfway between two. |
|
if ((x % 1) === 0.5 && (x & 1) === 0) { // [even number].5; round down (i.e. floor) |
|
return Math.floor(x); |
|
} else { |
|
return Math.round(x); |
|
} |
|
} |
|
|
|
function createNumberConversion(bitLength, typeOpts) { |
|
if (!typeOpts.unsigned) { |
|
--bitLength; |
|
} |
|
const lowerBound = typeOpts.unsigned ? 0 : -Math.pow(2, bitLength); |
|
const upperBound = Math.pow(2, bitLength) - 1; |
|
|
|
const moduloVal = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength) : Math.pow(2, bitLength); |
|
const moduloBound = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength - 1) : Math.pow(2, bitLength - 1); |
|
|
|
return function(V, opts) { |
|
if (!opts) opts = {}; |
|
|
|
let x = +V; |
|
|
|
if (opts.enforceRange) { |
|
if (!Number.isFinite(x)) { |
|
throw new TypeError("Argument is not a finite number"); |
|
} |
|
|
|
x = sign(x) * Math.floor(Math.abs(x)); |
|
if (x < lowerBound || x > upperBound) { |
|
throw new TypeError("Argument is not in byte range"); |
|
} |
|
|
|
return x; |
|
} |
|
|
|
if (!isNaN(x) && opts.clamp) { |
|
x = evenRound(x); |
|
|
|
if (x < lowerBound) x = lowerBound; |
|
if (x > upperBound) x = upperBound; |
|
return x; |
|
} |
|
|
|
if (!Number.isFinite(x) || x === 0) { |
|
return 0; |
|
} |
|
|
|
x = sign(x) * Math.floor(Math.abs(x)); |
|
x = x % moduloVal; |
|
|
|
if (!typeOpts.unsigned && x >= moduloBound) { |
|
return x - moduloVal; |
|
} else if (typeOpts.unsigned) { |
|
if (x < 0) { |
|
x += moduloVal; |
|
} else if (x === -0) { // don't return negative zero |
|
return 0; |
|
} |
|
} |
|
|
|
return x; |
|
} |
|
} |
|
|
|
conversions["void"] = function () { |
|
return undefined; |
|
}; |
|
|
|
conversions["boolean"] = function (val) { |
|
return !!val; |
|
}; |
|
|
|
conversions["byte"] = createNumberConversion(8, { unsigned: false }); |
|
conversions["octet"] = createNumberConversion(8, { unsigned: true }); |
|
|
|
conversions["short"] = createNumberConversion(16, { unsigned: false }); |
|
conversions["unsigned short"] = createNumberConversion(16, { unsigned: true }); |
|
|
|
conversions["long"] = createNumberConversion(32, { unsigned: false }); |
|
conversions["unsigned long"] = createNumberConversion(32, { unsigned: true }); |
|
|
|
conversions["long long"] = createNumberConversion(32, { unsigned: false, moduloBitLength: 64 }); |
|
conversions["unsigned long long"] = createNumberConversion(32, { unsigned: true, moduloBitLength: 64 }); |
|
|
|
conversions["double"] = function (V) { |
|
const x = +V; |
|
|
|
if (!Number.isFinite(x)) { |
|
throw new TypeError("Argument is not a finite floating-point value"); |
|
} |
|
|
|
return x; |
|
}; |
|
|
|
conversions["unrestricted double"] = function (V) { |
|
const x = +V; |
|
|
|
if (isNaN(x)) { |
|
throw new TypeError("Argument is NaN"); |
|
} |
|
|
|
return x; |
|
}; |
|
|
|
// not quite valid, but good enough for JS |
|
conversions["float"] = conversions["double"]; |
|
conversions["unrestricted float"] = conversions["unrestricted double"]; |
|
|
|
conversions["DOMString"] = function (V, opts) { |
|
if (!opts) opts = {}; |
|
|
|
if (opts.treatNullAsEmptyString && V === null) { |
|
return ""; |
|
} |
|
|
|
return String(V); |
|
}; |
|
|
|
conversions["ByteString"] = function (V, opts) { |
|
const x = String(V); |
|
let c = undefined; |
|
for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) { |
|
if (c > 255) { |
|
throw new TypeError("Argument is not a valid bytestring"); |
|
} |
|
} |
|
|
|
return x; |
|
}; |
|
|
|
conversions["USVString"] = function (V) { |
|
const S = String(V); |
|
const n = S.length; |
|
const U = []; |
|
for (let i = 0; i < n; ++i) { |
|
const c = S.charCodeAt(i); |
|
if (c < 0xD800 || c > 0xDFFF) { |
|
U.push(String.fromCodePoint(c)); |
|
} else if (0xDC00 <= c && c <= 0xDFFF) { |
|
U.push(String.fromCodePoint(0xFFFD)); |
|
} else { |
|
if (i === n - 1) { |
|
U.push(String.fromCodePoint(0xFFFD)); |
|
} else { |
|
const d = S.charCodeAt(i + 1); |
|
if (0xDC00 <= d && d <= 0xDFFF) { |
|
const a = c & 0x3FF; |
|
const b = d & 0x3FF; |
|
U.push(String.fromCodePoint((2 << 15) + (2 << 9) * a + b)); |
|
++i; |
|
} else { |
|
U.push(String.fromCodePoint(0xFFFD)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return U.join(''); |
|
}; |
|
|
|
conversions["Date"] = function (V, opts) { |
|
if (!(V instanceof Date)) { |
|
throw new TypeError("Argument is not a Date object"); |
|
} |
|
if (isNaN(V)) { |
|
return undefined; |
|
} |
|
|
|
return V; |
|
}; |
|
|
|
conversions["RegExp"] = function (V, opts) { |
|
if (!(V instanceof RegExp)) { |
|
V = new RegExp(V); |
|
} |
|
|
|
return V; |
|
};
|
|
|