binary-utils.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. function toByteArray(bytes) {
  2. if (!bytes) return []
  3. if (bytes instanceof ArrayBuffer) return Array.prototype.slice.call(new Uint8Array(bytes))
  4. if (ArrayBuffer.isView(bytes)) return Array.prototype.slice.call(new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength))
  5. return Array.prototype.slice.call(bytes)
  6. }
  7. function bytesToBase64(bytes) {
  8. const source = toByteArray(bytes)
  9. const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  10. let output = ''
  11. for (let index = 0; index < source.length; index += 3) {
  12. const first = source[index] & 0xFF
  13. const second = index + 1 < source.length ? source[index + 1] & 0xFF : 0
  14. const third = index + 2 < source.length ? source[index + 2] & 0xFF : 0
  15. const triple = (first << 16) | (second << 8) | third
  16. output += alphabet[(triple >> 18) & 0x3F]
  17. output += alphabet[(triple >> 12) & 0x3F]
  18. output += index + 1 < source.length ? alphabet[(triple >> 6) & 0x3F] : '='
  19. output += index + 2 < source.length ? alphabet[triple & 0x3F] : '='
  20. }
  21. return output
  22. }
  23. function bytesToBin(bytes) {
  24. return toByteArray(bytes).map((byte) => (byte & 0xFF).toString(2).padStart(8, '0')).join('')
  25. }
  26. function bytesToHex(bytes, separator = '') {
  27. return toByteArray(bytes).map((byte) => (byte & 0xFF).toString(16).toUpperCase().padStart(2, '0')).join(separator)
  28. }
  29. function readByte(bytes, offset) {
  30. if (!bytes) return 0
  31. if (bytes instanceof ArrayBuffer) return (new Uint8Array(bytes)[offset] || 0) & 0xFF
  32. return (bytes[offset] || 0) & 0xFF
  33. }
  34. function formatHexNumber(value, length = 2) {
  35. const numberValue = Math.max(0, Math.floor(Number(value) || 0))
  36. return numberValue.toString(16).toUpperCase().padStart(length, '0')
  37. }
  38. function readUint16BE(bytes = [], offset = 0) {
  39. return ((readByte(bytes, offset) << 8) | readByte(bytes, offset + 1)) & 0xFFFF
  40. }
  41. function readUint16LE(bytes = [], offset = 0) {
  42. return (readByte(bytes, offset) | (readByte(bytes, offset + 1) << 8)) & 0xFFFF
  43. }
  44. function readUint32BE(bytes = [], offset = 0) {
  45. return (
  46. (readByte(bytes, offset) * 0x1000000)
  47. + ((readByte(bytes, offset + 1) << 16) >>> 0)
  48. + ((readByte(bytes, offset + 2) << 8) >>> 0)
  49. + readByte(bytes, offset + 3)
  50. ) >>> 0
  51. }
  52. function readUint32LE(bytes = [], offset = 0) {
  53. return (
  54. readByte(bytes, offset)
  55. + ((readByte(bytes, offset + 1) << 8) >>> 0)
  56. + ((readByte(bytes, offset + 2) << 16) >>> 0)
  57. + (readByte(bytes, offset + 3) * 0x1000000)
  58. ) >>> 0
  59. }
  60. function splitUint16BE(value) {
  61. const numberValue = Number(value) & 0xFFFF
  62. return [(numberValue >> 8) & 0xFF, numberValue & 0xFF]
  63. }
  64. function splitUint16LE(value) {
  65. const numberValue = Number(value) & 0xFFFF
  66. return [numberValue & 0xFF, (numberValue >> 8) & 0xFF]
  67. }
  68. function splitUint32BE(value) {
  69. const numberValue = Number(value) >>> 0
  70. return [
  71. (numberValue >>> 24) & 0xFF,
  72. (numberValue >>> 16) & 0xFF,
  73. (numberValue >>> 8) & 0xFF,
  74. numberValue & 0xFF
  75. ]
  76. }
  77. function splitUint32LE(value) {
  78. const numberValue = Number(value) >>> 0
  79. return [
  80. numberValue & 0xFF,
  81. (numberValue >>> 8) & 0xFF,
  82. (numberValue >>> 16) & 0xFF,
  83. (numberValue >>> 24) & 0xFF
  84. ]
  85. }
  86. function bytesToAsciiText(bytes = []) {
  87. return String.fromCharCode.apply(null, trimTrailingNullBytes(bytes).map((byte) => byte & 0xFF))
  88. }
  89. function bytesToUtf8Text(bytes = []) {
  90. const trimmed = trimTrailingNullBytes(bytes)
  91. if (!trimmed.length) return ''
  92. let encoded = ''
  93. trimmed.forEach((byte) => {
  94. encoded += `%${(byte & 0xFF).toString(16).padStart(2, '0').toUpperCase()}`
  95. })
  96. try {
  97. return decodeURIComponent(encoded)
  98. } catch (error) {
  99. return bytesToAsciiText(trimmed)
  100. }
  101. }
  102. function formatBytes(byteLength) {
  103. const length = Number(byteLength) || 0
  104. if (length >= 1024 && length % 1024 === 0) return `${length / 1024} KB`
  105. if (length >= 1024) return `${(length / 1024).toFixed(2)} KB`
  106. return `${length} bytes`
  107. }
  108. function bytesToWords(bytes = []) {
  109. const words = []
  110. for (let index = 0; index + 1 < bytes.length; index += 2) {
  111. const highByte = bytes[index] || 0
  112. const lowByte = bytes[index + 1] || 0
  113. words.push(((highByte << 8) | lowByte) & 0xFFFF)
  114. }
  115. return words
  116. }
  117. function bytesToWordsLE(bytes = []) {
  118. const words = []
  119. for (let index = 0; index + 1 < bytes.length; index += 2) {
  120. const lowByte = bytes[index] || 0
  121. const highByte = bytes[index + 1] || 0
  122. words.push(((highByte << 8) | lowByte) & 0xFFFF)
  123. }
  124. return words
  125. }
  126. function getByteFromWord(word, byteOffset = 0) {
  127. const value = Number(word) & 0xFFFF
  128. return byteOffset === 0 ? ((value >> 8) & 0xFF) : (value & 0xFF)
  129. }
  130. function stringToUtf8Bytes(text) {
  131. const bytes = []
  132. const encoded = encodeURIComponent(String(text || ''))
  133. for (let index = 0; index < encoded.length; index += 1) {
  134. const char = encoded[index]
  135. if (char === '%') {
  136. bytes.push(parseInt(encoded.slice(index + 1, index + 3), 16) & 0xFF)
  137. index += 2
  138. } else {
  139. bytes.push(char.charCodeAt(0) & 0xFF)
  140. }
  141. }
  142. return bytes
  143. }
  144. function trimTrailingNullBytes(bytes = []) {
  145. let end = bytes.length
  146. while (end > 0 && bytes[end - 1] === 0x00) {
  147. end -= 1
  148. }
  149. return bytes.slice(0, end)
  150. }
  151. function wordsToBytes(words = [], byteLength = words.length * 2) {
  152. const bytes = []
  153. for (let index = 0; index < words.length; index += 1) {
  154. const word = Number(words[index]) & 0xFFFF
  155. bytes.push((word >> 8) & 0xFF, word & 0xFF)
  156. }
  157. return bytes.slice(0, Math.max(0, byteLength))
  158. }
  159. function wordsToBytesLE(words = [], byteLength = words.length * 2) {
  160. const bytes = []
  161. for (let index = 0; index < words.length; index += 1) {
  162. const word = Number(words[index]) & 0xFFFF
  163. bytes.push(word & 0xFF, (word >> 8) & 0xFF)
  164. }
  165. return bytes.slice(0, Math.max(0, byteLength))
  166. }
  167. module.exports = {
  168. bytesToBase64,
  169. bytesToBin,
  170. bytesToHex,
  171. bytesToAsciiText,
  172. bytesToUtf8Text,
  173. bytesToWords,
  174. bytesToWordsLE,
  175. formatBytes,
  176. formatHexNumber,
  177. getByteFromWord,
  178. readByte,
  179. readUint16BE,
  180. readUint16LE,
  181. readUint32BE,
  182. readUint32LE,
  183. splitUint16BE,
  184. splitUint16LE,
  185. splitUint32BE,
  186. splitUint32LE,
  187. stringToUtf8Bytes,
  188. toByteArray,
  189. trimTrailingNullBytes,
  190. wordsToBytes,
  191. wordsToBytesLE
  192. }