function toByteArray(bytes) { if (!bytes) return [] if (bytes instanceof ArrayBuffer) return Array.prototype.slice.call(new Uint8Array(bytes)) if (ArrayBuffer.isView(bytes)) return Array.prototype.slice.call(new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)) return Array.prototype.slice.call(bytes) } function bytesToBase64(bytes) { const source = toByteArray(bytes) const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' let output = '' for (let index = 0; index < source.length; index += 3) { const first = source[index] & 0xFF const second = index + 1 < source.length ? source[index + 1] & 0xFF : 0 const third = index + 2 < source.length ? source[index + 2] & 0xFF : 0 const triple = (first << 16) | (second << 8) | third output += alphabet[(triple >> 18) & 0x3F] output += alphabet[(triple >> 12) & 0x3F] output += index + 1 < source.length ? alphabet[(triple >> 6) & 0x3F] : '=' output += index + 2 < source.length ? alphabet[triple & 0x3F] : '=' } return output } function bytesToBin(bytes) { return toByteArray(bytes).map((byte) => (byte & 0xFF).toString(2).padStart(8, '0')).join('') } function bytesToHex(bytes, separator = '') { return toByteArray(bytes).map((byte) => (byte & 0xFF).toString(16).toUpperCase().padStart(2, '0')).join(separator) } function readByte(bytes, offset) { if (!bytes) return 0 if (bytes instanceof ArrayBuffer) return (new Uint8Array(bytes)[offset] || 0) & 0xFF return (bytes[offset] || 0) & 0xFF } function formatHexNumber(value, length = 2) { const numberValue = Math.max(0, Math.floor(Number(value) || 0)) return numberValue.toString(16).toUpperCase().padStart(length, '0') } function readUint16BE(bytes = [], offset = 0) { return ((readByte(bytes, offset) << 8) | readByte(bytes, offset + 1)) & 0xFFFF } function readUint16LE(bytes = [], offset = 0) { return (readByte(bytes, offset) | (readByte(bytes, offset + 1) << 8)) & 0xFFFF } function readUint32BE(bytes = [], offset = 0) { return ( (readByte(bytes, offset) * 0x1000000) + ((readByte(bytes, offset + 1) << 16) >>> 0) + ((readByte(bytes, offset + 2) << 8) >>> 0) + readByte(bytes, offset + 3) ) >>> 0 } function readUint32LE(bytes = [], offset = 0) { return ( readByte(bytes, offset) + ((readByte(bytes, offset + 1) << 8) >>> 0) + ((readByte(bytes, offset + 2) << 16) >>> 0) + (readByte(bytes, offset + 3) * 0x1000000) ) >>> 0 } function splitUint16BE(value) { const numberValue = Number(value) & 0xFFFF return [(numberValue >> 8) & 0xFF, numberValue & 0xFF] } function splitUint16LE(value) { const numberValue = Number(value) & 0xFFFF return [numberValue & 0xFF, (numberValue >> 8) & 0xFF] } function splitUint32BE(value) { const numberValue = Number(value) >>> 0 return [ (numberValue >>> 24) & 0xFF, (numberValue >>> 16) & 0xFF, (numberValue >>> 8) & 0xFF, numberValue & 0xFF ] } function splitUint32LE(value) { const numberValue = Number(value) >>> 0 return [ numberValue & 0xFF, (numberValue >>> 8) & 0xFF, (numberValue >>> 16) & 0xFF, (numberValue >>> 24) & 0xFF ] } function bytesToAsciiText(bytes = []) { return String.fromCharCode.apply(null, trimTrailingNullBytes(bytes).map((byte) => byte & 0xFF)) } function bytesToUtf8Text(bytes = []) { const trimmed = trimTrailingNullBytes(bytes) if (!trimmed.length) return '' let encoded = '' trimmed.forEach((byte) => { encoded += `%${(byte & 0xFF).toString(16).padStart(2, '0').toUpperCase()}` }) try { return decodeURIComponent(encoded) } catch (error) { return bytesToAsciiText(trimmed) } } function formatBytes(byteLength) { const length = Number(byteLength) || 0 if (length >= 1024 && length % 1024 === 0) return `${length / 1024} KB` if (length >= 1024) return `${(length / 1024).toFixed(2)} KB` return `${length} bytes` } function bytesToWords(bytes = []) { const words = [] for (let index = 0; index + 1 < bytes.length; index += 2) { const highByte = bytes[index] || 0 const lowByte = bytes[index + 1] || 0 words.push(((highByte << 8) | lowByte) & 0xFFFF) } return words } function bytesToWordsLE(bytes = []) { const words = [] for (let index = 0; index + 1 < bytes.length; index += 2) { const lowByte = bytes[index] || 0 const highByte = bytes[index + 1] || 0 words.push(((highByte << 8) | lowByte) & 0xFFFF) } return words } function getByteFromWord(word, byteOffset = 0) { const value = Number(word) & 0xFFFF return byteOffset === 0 ? ((value >> 8) & 0xFF) : (value & 0xFF) } function stringToUtf8Bytes(text) { const bytes = [] const encoded = encodeURIComponent(String(text || '')) for (let index = 0; index < encoded.length; index += 1) { const char = encoded[index] if (char === '%') { bytes.push(parseInt(encoded.slice(index + 1, index + 3), 16) & 0xFF) index += 2 } else { bytes.push(char.charCodeAt(0) & 0xFF) } } return bytes } function trimTrailingNullBytes(bytes = []) { let end = bytes.length while (end > 0 && bytes[end - 1] === 0x00) { end -= 1 } return bytes.slice(0, end) } function wordsToBytes(words = [], byteLength = words.length * 2) { const bytes = [] for (let index = 0; index < words.length; index += 1) { const word = Number(words[index]) & 0xFFFF bytes.push((word >> 8) & 0xFF, word & 0xFF) } return bytes.slice(0, Math.max(0, byteLength)) } function wordsToBytesLE(words = [], byteLength = words.length * 2) { const bytes = [] for (let index = 0; index < words.length; index += 1) { const word = Number(words[index]) & 0xFFFF bytes.push(word & 0xFF, (word >> 8) & 0xFF) } return bytes.slice(0, Math.max(0, byteLength)) } module.exports = { bytesToBase64, bytesToBin, bytesToHex, bytesToAsciiText, bytesToUtf8Text, bytesToWords, bytesToWordsLE, formatBytes, formatHexNumber, getByteFromWord, readByte, readUint16BE, readUint16LE, readUint32BE, readUint32LE, splitUint16BE, splitUint16LE, splitUint32BE, splitUint32LE, stringToUtf8Bytes, toByteArray, trimTrailingNullBytes, wordsToBytes, wordsToBytesLE }