borc
Version:
Encode and parse data in the Concise Binary Object Representation (CBOR) data format (RFC7049).
174 lines (138 loc) • 4.08 kB
JavaScript
const { Buffer } = require('buffer')
const Bignumber = require('bignumber.js').BigNumber
const constants = require('./constants')
const SHIFT32 = constants.SHIFT32
const SHIFT16 = constants.SHIFT16
const MAX_SAFE_HIGH = 0x1fffff
exports.parseHalf = function parseHalf (buf) {
const sign = buf[0] & 0x80 ? -1 : 1
const exp = (buf[0] & 0x7C) >> 2
const mant = ((buf[0] & 0x03) << 8) | buf[1]
if (!exp) {
return sign * 5.9604644775390625e-8 * mant
} else if (exp === 0x1f) {
return sign * (mant ? 0 / 0 : 2e308) // eslint-disable-line no-loss-of-precision
} else {
return sign * Math.pow(2, exp - 25) * (1024 + mant)
}
}
function toHex (n) {
if (n < 16) {
return '0' + n.toString(16)
}
return n.toString(16)
}
exports.arrayBufferToBignumber = function (buf) {
const len = buf.byteLength
let res = ''
for (let i = 0; i < len; i++) {
res += toHex(buf[i])
}
return new Bignumber(res, 16)
}
// convert an Object into a Map
exports.buildMap = (obj) => {
const res = new Map()
const keys = Object.keys(obj)
const length = keys.length
for (let i = 0; i < length; i++) {
res.set(keys[i], obj[keys[i]])
}
return res
}
exports.buildInt32 = (f, g) => {
return f * SHIFT16 + g
}
exports.buildInt64 = (f1, f2, g1, g2) => {
const f = exports.buildInt32(f1, f2)
const g = exports.buildInt32(g1, g2)
if (f > MAX_SAFE_HIGH) {
return new Bignumber(f).times(SHIFT32).plus(g)
} else {
return (f * SHIFT32) + g
}
}
exports.writeHalf = function writeHalf (buf, half) {
// assume 0, -0, NaN, Infinity, and -Infinity have already been caught
// HACK: everyone settle in. This isn't going to be pretty.
// Translate cn-cbor's C code (from Carsten Borman):
// uint32_t be32;
// uint16_t be16, u16;
// union {
// float f;
// uint32_t u;
// } u32;
// u32.f = float_val;
const u32 = Buffer.allocUnsafe(4)
u32.writeFloatBE(half, 0)
const u = u32.readUInt32BE(0)
// if ((u32.u & 0x1FFF) == 0) { /* worth trying half */
// hildjj: If the lower 13 bits are 0, we won't lose anything in the conversion
if ((u & 0x1FFF) !== 0) {
return false
}
// int s16 = (u32.u >> 16) & 0x8000;
// int exp = (u32.u >> 23) & 0xff;
// int mant = u32.u & 0x7fffff;
let s16 = (u >> 16) & 0x8000 // top bit is sign
const exp = (u >> 23) & 0xff // then 5 bits of exponent
const mant = u & 0x7fffff
// if (exp == 0 && mant == 0)
// ; /* 0.0, -0.0 */
// hildjj: zeros already handled. Assert if you don't believe me.
// else if (exp >= 113 && exp <= 142) /* normalized */
// s16 += ((exp - 112) << 10) + (mant >> 13);
if ((exp >= 113) && (exp <= 142)) {
s16 += ((exp - 112) << 10) + (mant >> 13)
// else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */
// if (mant & ((1 << (126 - exp)) - 1))
// goto float32; /* loss of precision */
// s16 += ((mant + 0x800000) >> (126 - exp));
} else if ((exp >= 103) && (exp < 113)) {
if (mant & ((1 << (126 - exp)) - 1)) {
return false
}
s16 += ((mant + 0x800000) >> (126 - exp))
// } else if (exp == 255 && mant == 0) { /* Inf */
// s16 += 0x7c00;
// hildjj: Infinity already handled
// } else
// goto float32; /* loss of range */
} else {
return false
}
// ensure_writable(3);
// u16 = s16;
// be16 = hton16p((const uint8_t*)&u16);
buf.writeUInt16BE(s16, 0)
return true
}
exports.keySorter = function (a, b) {
const lenA = a[0].byteLength
const lenB = b[0].byteLength
if (lenA > lenB) {
return 1
}
if (lenB > lenA) {
return -1
}
return a[0].compare(b[0])
}
// Adapted from http://www.2ality.com/2012/03/signedzero.html
exports.isNegativeZero = (x) => {
return x === 0 && (1 / x < 0)
}
exports.nextPowerOf2 = (n) => {
let count = 0
// First n in the below condition is for
// the case where n is 0
if (n && !(n & (n - 1))) {
return n
}
while (n !== 0) {
n >>= 1
count += 1
}
return 1 << count
}