1
0

binary-utils.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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 decodeBytesWithTextDecoder(bytes = [], encoding) {
  103. if (typeof TextDecoder === 'undefined') return null
  104. try {
  105. const decoder = new TextDecoder(encoding, { fatal: true })
  106. return decoder.decode(new Uint8Array(bytes))
  107. } catch (error) {
  108. return null
  109. }
  110. }
  111. function decodeUtf8Strict(bytes = []) {
  112. if (!bytes.length) return ''
  113. let encoded = ''
  114. bytes.forEach((byte) => {
  115. encoded += `%${(byte & 0xFF).toString(16).padStart(2, '0').toUpperCase()}`
  116. })
  117. try {
  118. return decodeURIComponent(encoded)
  119. } catch (error) {
  120. return null
  121. }
  122. }
  123. function bytesToAutoText(bytes = []) {
  124. const trimmed = trimTrailingNullBytes(bytes)
  125. if (!trimmed.length) return ''
  126. const utf8Text = decodeBytesWithTextDecoder(trimmed, 'utf-8')
  127. || decodeUtf8Strict(trimmed)
  128. if (utf8Text !== null) return utf8Text
  129. const gbkText = decodeBytesWithTextDecoder(trimmed, 'gbk')
  130. || decodeBytesWithTextDecoder(trimmed, 'gb18030')
  131. if (gbkText !== null) return gbkText
  132. return bytesToAsciiText(trimmed)
  133. }
  134. function formatBytes(byteLength) {
  135. const length = Number(byteLength) || 0
  136. if (length >= 1024 && length % 1024 === 0) return `${length / 1024} KB`
  137. if (length >= 1024) return `${(length / 1024).toFixed(2)} KB`
  138. return `${length} bytes`
  139. }
  140. function bytesToWords(bytes = []) {
  141. const words = []
  142. for (let index = 0; index + 1 < bytes.length; index += 2) {
  143. const highByte = bytes[index] || 0
  144. const lowByte = bytes[index + 1] || 0
  145. words.push(((highByte << 8) | lowByte) & 0xFFFF)
  146. }
  147. return words
  148. }
  149. function bytesToWordsLE(bytes = []) {
  150. const words = []
  151. for (let index = 0; index + 1 < bytes.length; index += 2) {
  152. const lowByte = bytes[index] || 0
  153. const highByte = bytes[index + 1] || 0
  154. words.push(((highByte << 8) | lowByte) & 0xFFFF)
  155. }
  156. return words
  157. }
  158. function getByteFromWord(word, byteOffset = 0) {
  159. const value = Number(word) & 0xFFFF
  160. return byteOffset === 0 ? ((value >> 8) & 0xFF) : (value & 0xFF)
  161. }
  162. function stringToUtf8Bytes(text) {
  163. const bytes = []
  164. const encoded = encodeURIComponent(String(text || ''))
  165. for (let index = 0; index < encoded.length; index += 1) {
  166. const char = encoded[index]
  167. if (char === '%') {
  168. bytes.push(parseInt(encoded.slice(index + 1, index + 3), 16) & 0xFF)
  169. index += 2
  170. } else {
  171. bytes.push(char.charCodeAt(0) & 0xFF)
  172. }
  173. }
  174. return bytes
  175. }
  176. function trimTrailingNullBytes(bytes = []) {
  177. let end = bytes.length
  178. while (end > 0 && bytes[end - 1] === 0x00) {
  179. end -= 1
  180. }
  181. return bytes.slice(0, end)
  182. }
  183. function wordsToBytes(words = [], byteLength = words.length * 2) {
  184. const bytes = []
  185. for (let index = 0; index < words.length; index += 1) {
  186. const word = Number(words[index]) & 0xFFFF
  187. bytes.push((word >> 8) & 0xFF, word & 0xFF)
  188. }
  189. return bytes.slice(0, Math.max(0, byteLength))
  190. }
  191. function wordsToBytesLE(words = [], byteLength = words.length * 2) {
  192. const bytes = []
  193. for (let index = 0; index < words.length; index += 1) {
  194. const word = Number(words[index]) & 0xFFFF
  195. bytes.push(word & 0xFF, (word >> 8) & 0xFF)
  196. }
  197. return bytes.slice(0, Math.max(0, byteLength))
  198. }
  199. module.exports = {
  200. bytesToAutoText,
  201. bytesToBase64,
  202. bytesToBin,
  203. bytesToHex,
  204. bytesToAsciiText,
  205. bytesToUtf8Text,
  206. bytesToWords,
  207. bytesToWordsLE,
  208. formatBytes,
  209. formatHexNumber,
  210. getByteFromWord,
  211. readByte,
  212. readUint16BE,
  213. readUint16LE,
  214. readUint32BE,
  215. readUint32LE,
  216. splitUint16BE,
  217. splitUint16LE,
  218. splitUint32BE,
  219. splitUint32LE,
  220. stringToUtf8Bytes,
  221. toByteArray,
  222. trimTrailingNullBytes,
  223. wordsToBytes,
  224. wordsToBytesLE
  225. }