1
0

index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. const BYTE_MIN = 0
  2. const BYTE_MAX = 0xFF
  3. const CONTROL_LABELS = {
  4. 0x00: 'NUL',
  5. 0x01: 'SOH',
  6. 0x02: 'STX',
  7. 0x03: 'ETX',
  8. 0x04: 'EOT',
  9. 0x05: 'ENQ',
  10. 0x06: 'ACK',
  11. 0x07: 'BEL',
  12. 0x08: 'BS',
  13. 0x09: '\\t',
  14. 0x0A: '\\n',
  15. 0x0B: 'VT',
  16. 0x0C: '\\f',
  17. 0x0D: '\\r',
  18. 0x0E: 'SO',
  19. 0x0F: 'SI',
  20. 0x10: 'DLE',
  21. 0x11: 'DC1',
  22. 0x12: 'DC2',
  23. 0x13: 'DC3',
  24. 0x14: 'DC4',
  25. 0x15: 'NAK',
  26. 0x16: 'SYN',
  27. 0x17: 'ETB',
  28. 0x18: 'CAN',
  29. 0x19: 'EM',
  30. 0x1A: 'SUB',
  31. 0x1B: 'ESC',
  32. 0x1C: 'FS',
  33. 0x1D: 'GS',
  34. 0x1E: 'RS',
  35. 0x1F: 'US',
  36. 0x20: 'SP',
  37. 0x7F: 'DEL'
  38. }
  39. function formatHexByte(value) {
  40. return `0x${(Number(value) & BYTE_MAX).toString(16).toUpperCase().padStart(2, '0')}`
  41. }
  42. function getDefaultRows() {
  43. return [
  44. { label: '字符', meta: '', value: '--', copyValue: '' },
  45. { label: 'HEX', meta: '', value: '--', copyValue: '' },
  46. { label: 'DEC', meta: '', value: '--', copyValue: '' }
  47. ]
  48. }
  49. function createEmptyResultState() {
  50. return {
  51. asciiCodeErrorText: '',
  52. asciiCodeResultRows: getDefaultRows()
  53. }
  54. }
  55. function splitValueTokens(text) {
  56. return String(text || '')
  57. .trim()
  58. .split(/[\s,;,;]+/)
  59. .map((token) => token.trim())
  60. .filter(Boolean)
  61. }
  62. function tokenLooksNumeric(token) {
  63. return /^0x/i.test(token) || /^\d+$/.test(token)
  64. }
  65. function tokenIsValidNumeric(token) {
  66. return /^0x[0-9a-f]+$/i.test(token) || /^\d+$/.test(token)
  67. }
  68. function shouldParseAsNumeric(text) {
  69. const tokens = splitValueTokens(text)
  70. if (!tokens.length) return false
  71. return tokens.every(tokenLooksNumeric)
  72. }
  73. function parseNumericToken(token) {
  74. if (!tokenIsValidNumeric(token)) {
  75. throw new Error('数值格式无效')
  76. }
  77. const value = /^0x/i.test(token)
  78. ? parseInt(token.slice(2), 16)
  79. : Number(token)
  80. if (!Number.isInteger(value) || value < BYTE_MIN || value > BYTE_MAX) {
  81. throw new Error('数值范围需为 0 - 255')
  82. }
  83. return value
  84. }
  85. function parseNumericBytes(text) {
  86. return splitValueTokens(text).map(parseNumericToken)
  87. }
  88. function parseTextBytes(text) {
  89. const bytes = []
  90. Array.from(String(text || '')).forEach((char) => {
  91. const value = char.codePointAt(0)
  92. if (!Number.isInteger(value) || value < BYTE_MIN || value > BYTE_MAX) {
  93. throw new Error('字符需在 0 - 255 范围内')
  94. }
  95. bytes.push(value)
  96. })
  97. return bytes
  98. }
  99. function formatChar(value) {
  100. const byte = Number(value) & BYTE_MAX
  101. if (Object.prototype.hasOwnProperty.call(CONTROL_LABELS, byte)) return CONTROL_LABELS[byte]
  102. if (byte >= 0x21 && byte <= 0x7E) return String.fromCharCode(byte)
  103. return `\\x${byte.toString(16).toUpperCase().padStart(2, '0')}`
  104. }
  105. function formatDisplayText(bytes = []) {
  106. if (!bytes.length) return '--'
  107. if (bytes.every((byte) => {
  108. const value = Number(byte) & BYTE_MAX
  109. return value >= 0x21 && value <= 0x7E
  110. })) {
  111. return bytes.map((byte) => String.fromCharCode(Number(byte) & BYTE_MAX)).join('')
  112. }
  113. return bytes.map(formatChar).join(' ')
  114. }
  115. function bytesToText(bytes = []) {
  116. return bytes.map((byte) => String.fromCharCode(Number(byte) & BYTE_MAX)).join('')
  117. }
  118. function createResultRows(bytes = []) {
  119. if (!bytes.length) return getDefaultRows()
  120. const hexText = bytes.map(formatHexByte).join(' ')
  121. const decText = bytes.map((byte) => String(Number(byte) & BYTE_MAX)).join(' ')
  122. const charText = formatDisplayText(bytes)
  123. return [
  124. {
  125. copyValue: bytesToText(bytes),
  126. label: '字符',
  127. value: charText
  128. },
  129. {
  130. copyValue: hexText,
  131. label: 'HEX',
  132. value: hexText
  133. },
  134. {
  135. copyValue: decText,
  136. label: 'DEC',
  137. value: decText
  138. }
  139. ]
  140. }
  141. function buildState(source = {}) {
  142. const inputText = String(source.asciiCodeInputText || '')
  143. const trimmedText = inputText.trim()
  144. if (!trimmedText) {
  145. return {
  146. asciiCodeInputText: inputText,
  147. ...createEmptyResultState()
  148. }
  149. }
  150. try {
  151. const numericMode = shouldParseAsNumeric(trimmedText)
  152. const bytes = numericMode ? parseNumericBytes(trimmedText) : parseTextBytes(inputText)
  153. return {
  154. asciiCodeErrorText: '',
  155. asciiCodeInputText: inputText,
  156. asciiCodeResultRows: createResultRows(bytes)
  157. }
  158. } catch (error) {
  159. return {
  160. asciiCodeErrorText: error && error.message ? error.message : '转换失败',
  161. asciiCodeInputText: inputText,
  162. asciiCodeResultRows: getDefaultRows()
  163. }
  164. }
  165. }
  166. function createInitialState() {
  167. return buildState({
  168. asciiCodeInputText: ''
  169. })
  170. }
  171. function updateState(state, changedData = {}) {
  172. return buildState({
  173. ...state,
  174. ...changedData
  175. })
  176. }
  177. function clearInput(state = {}) {
  178. return updateState(state, {
  179. asciiCodeInputText: ''
  180. })
  181. }
  182. module.exports = {
  183. clearInput,
  184. createInitialState,
  185. formatHexByte,
  186. updateState
  187. }