bitray
Version:
Bitray - Small Utility For Handling Binary Data
334 lines (260 loc) • 8.76 kB
JavaScript
const hexLookupTable = (() => {
const alphabet = '0123456789abcdef'
const table = new Array(256)
for (let i = 0; i < 16; ++i) {
const i16 = i * 16
for (let j = 0; j < 16; ++j) {
table[i16 + j] = alphabet[i] + alphabet[j]
}
}
return table
})()
const decLookupTable = (() => {
const alphabet = '0123456789abcdef'
const table = {}
for (let i = 0; i < 16; ++i) {
const i16 = i * 16
for (let j = 0; j < 16; ++j) {
table[alphabet[i] + alphabet[j]] = i16 + j
}
}
return table
})()
const Base64 = require('base64-js')
class Bitray extends Uint8Array {
constructor(data, encoding) {
let binary
if (Array.isArray(data)) {
binary = Uint8Array.from(array)
} else if (data instanceof Uint8Array) {
binary = data
} else {
if (typeof encoding !== 'string' || encoding === '') {
encoding = 'utf8'
}
if (encoding === 'utf8' || encoding === 'utf-8') {
binary = utf8Decode(data) || new Uint8Array(0)
} else if (['latin1', 'binary'].includes(encoding)) {
binary = latinDecode(data) || new Uint8Array(0)
} else if (encoding === 'hex') {
binary = hexDecode(data) || new Uint8Array(0)
} else if (['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].includes(encoding)) {
binary = utf16Decode(data) || new Uint8Array(0)
} else if (encoding === 'base64') {
binary = Base64.toByteArray(data) || new Uint8Array(0)
} else {
throw new Error('Unknown Encoding Provided. Recieved Encoding "' + encoding + '"')
}
}
super(binary.length)
for (let i = 0; i < binary.length; i++) {
super.fill(binary[i], i, i + 1)
}
this.binary = binary
}
toFormat(encoding) {
if (['utf-8', 'utf8'].includes(encoding)) {
const res = []
let i = 0
while (i < this.binary.length) {
const firstByte = this.binary[i]
let codePoint = null
let bytesPerSequence = (firstByte > 0xEF)
? 4
: (firstByte > 0xDF)
? 3
: (firstByte > 0xBF)
? 2
: 1
if (i + bytesPerSequence <= this.binary.length) {
let secondByte, thirdByte, fourthByte, tempCodePoint
switch (bytesPerSequence) {
case 1:
if (firstByte < 0x80) {
codePoint = firstByte
}
break
case 2:
secondByte = this.binary[i + 1]
if ((secondByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
if (tempCodePoint > 0x7F) {
codePoint = tempCodePoint
}
}
break
case 3:
secondByte = this.binary[i + 1]
thirdByte = this.binary[i + 2]
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
codePoint = tempCodePoint
}
}
break
case 4:
secondByte = this.binary[i + 1]
thirdByte = this.binary[i + 2]
fourthByte = this.binary[i + 3]
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
codePoint = tempCodePoint
}
}
}
}
if (codePoint === null) {
codePoint = 0xFFFD
bytesPerSequence = 1
} else if (codePoint > 0xFFFF) {
codePoint -= 0x10000
res.push(codePoint >>> 10 & 0x3FF | 0xD800)
codePoint = 0xDC00 | codePoint & 0x3FF
}
res.push(codePoint)
i += bytesPerSequence
}
const len = res.length
if (len <= 0x1000) {
let ress = ''
for (let i = 0; i < res.length; i++) {
ress += String.fromCharCode(res[i])
}
return ress
}
let resi = ''
let ii = 0
while (ii < len) {
resi += String.fromCharCode(res.slice(ii, ii += 0x1000))
}
return resi
}
if (['binary', 'latin1'].includes(encoding)) {
let ret = ''
for (let i = 0; i < this.binary.length; ++i) {
ret += String.fromCharCode(this.binary[i])
}
return ret
}
if (['hex'].includes(encoding)) {
let out = ''
for (let i = 0; i < this.binary.byteLength; ++i) {
out += hexLookupTable[this.binary[i]]
}
return out
}
if (['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].includes(encoding)) {
let res = ''
for (let i = 0; i < this.binary.length - 1; i += 2) {
res += String.fromCharCode(this.binary[i] + (this.binary[i + 1] * 256))
}
return res
}
if (encoding === 'base64') {
return Base64.fromByteArray(this.binary)
}
}
}
Bitray.from = (array) => {
const bit = new Bitray('', '')
bit.binary = array
return bit
}
function hexDecode (str) {
const len = str.length >>> 1
const byteArray = new Uint8Array(len)
for (let i = 0; i < len; ++i) {
byteArray[i] = decLookupTable[str.substr(i * 2, 2)]
}
return byteArray
}
function latinDecode (str) {
const byteArray = new Uint8Array(str.length)
for (let i = 0; i < str.length; i++) {
byteArray[i] = str.charCodeAt(i)
}
return byteArray
}
function utf8Decode (string) {
let units = Infinity
let codePoint
const length = string.length
let leadSurrogate = null
const bytes = []
for (let i = 0; i < length; ++i) {
codePoint = string.charCodeAt(i)
if (codePoint > 0xD7FF && codePoint < 0xE000) {
if (!leadSurrogate) {
if (codePoint > 0xDBFF) {
bytes.push(0xEF)
bytes.push(0xBF)
bytes.push(0xBD)
continue
} else if (i + 1 === length) {
bytes.push(0xEF)
bytes.push(0xBF)
bytes.push(0xBD)
continue
}
leadSurrogate = codePoint
continue
}
if (codePoint < 0xDC00) {
bytes.push(0xEF)
bytes.push(0xBF)
bytes.push(0xBD)
leadSurrogate = codePoint
continue
}
codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
} else if (leadSurrogate) {
bytes.push(0xEF)
bytes.push(0xBF)
bytes.push(0xBD)
}
leadSurrogate = null
if (codePoint < 0x80) {
if ((units -= 1) < 0) break
bytes.push(codePoint)
} else if (codePoint < 0x800) {
if ((units -= 2) < 0) break
bytes.push(codePoint >> 0x6 | 0xC0)
bytes.push(codePoint & 0x3F | 0x80)
} else if (codePoint < 0x10000) {
if ((units -= 3) < 0) break
bytes.push(codePoint >> 0xC | 0xE0)
bytes.push(codePoint >> 0x6 & 0x3F | 0x80)
bytes.push(codePoint & 0x3F | 0x80)
} else if (codePoint < 0x110000) {
if ((units -= 4) < 0) break
bytes.push(codePoint >> 0x12 | 0xF0)
bytes.push(codePoint >> 0xC & 0x3F | 0x80)
bytes.push(codePoint >> 0x6 & 0x3F | 0x80)
bytes.push(codePoint & 0x3F | 0x80)
} else {
throw new Error('Invalid code point')
}
}
const uint8 = new Uint8Array(bytes.length)
for (let i = 0; i < bytes.length; i++) {
uint8[i] = bytes[i]
}
return uint8
}
function utf16Decode (str) {
let c, hi, lo
const byteArray = new Uint8Array(str.length * 2)
let pos = 0
for (let i = 0; i < str.length; ++i) {
c = str.charCodeAt(i)
hi = c >> 8
lo = c % 256
byteArray[pos] = lo
byteArray[pos + 1] = hi
pos = pos + 2
}
return byteArray
}
module.exports = Bitray