| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122 |
- const {
- bytesToAutoText,
- bytesToHex,
- formatHexNumber,
- readUint16BE,
- readUint16LE,
- readUint32BE,
- readUint32LE,
- trimTrailingNullBytes
- } = require('../../utils/binary-utils.js')
- const CODE_INFO_TLV_HEADER_BYTE_LENGTH = 2
- const CODE_INFO_ENTRY_NAME_LENGTH_BYTE_LENGTH = 1
- const CODE_INFO_ENTRY_MAX_NAME_BYTE_LENGTH = 0xFF
- const CODE_INFO_TLV = {
- RAW_VARIABLE: 0x01,
- INT8_VARIABLE: 0x02,
- UINT8_VARIABLE: 0x03,
- INT16_VARIABLE: 0x04,
- UINT16_VARIABLE: 0x05,
- INT32_VARIABLE: 0x06,
- UINT32_VARIABLE: 0x07,
- FLOAT32_VARIABLE: 0x08,
- DOUBLE_VARIABLE: 0x09,
- INT64_VARIABLE: 0x0A,
- UINT64_VARIABLE: 0x0B,
- INT128_VARIABLE: 0x0C,
- UINT128_VARIABLE: 0x0D,
- INT256_VARIABLE: 0x0E,
- UINT256_VARIABLE: 0x0F,
- RAW_ARRAY: 0x10,
- INT8_ARRAY: 0x11,
- UINT8_ARRAY: 0x12,
- INT16_ARRAY: 0x13,
- UINT16_ARRAY: 0x14,
- INT32_ARRAY: 0x15,
- UINT32_ARRAY: 0x16,
- FLOAT32_ARRAY: 0x17,
- DOUBLE_ARRAY: 0x18,
- INT64_ARRAY: 0x19,
- UINT64_ARRAY: 0x1A,
- INT128_ARRAY: 0x1B,
- UINT128_ARRAY: 0x1C,
- INT256_ARRAY: 0x1D,
- UINT256_ARRAY: 0x1E,
- TEXT_ARRAY: 0x1F,
- ENUM_VARIABLE: 0x20,
- STRUCT_INSTANCE: 0x21,
- ENUM_ARRAY: 0x22,
- STRUCT_ARRAY: 0x23,
- CAVE_FREQ: 0x40,
- REF_VOLT: 0x41,
- AMP_GAIN: 0x42,
- RS_SHUNT: 0x43,
- BUS_DIV: 0x44,
- ALONG_DIV: 0x45,
- CHIP_MODEL: 0x46,
- MODEL: 0x47
- }
- const CODE_INFO_BOARD_TLV_NAMES = {
- [CODE_INFO_TLV.CAVE_FREQ]: 'cave_freq',
- [CODE_INFO_TLV.REF_VOLT]: 'ref_volt',
- [CODE_INFO_TLV.AMP_GAIN]: 'amp_gain',
- [CODE_INFO_TLV.RS_SHUNT]: 'rs_shunt',
- [CODE_INFO_TLV.BUS_DIV]: 'bus_div',
- [CODE_INFO_TLV.ALONG_DIV]: 'along_div',
- [CODE_INFO_TLV.CHIP_MODEL]: 'chip_model',
- [CODE_INFO_TLV.MODEL]: 'model'
- }
- const CODE_INFO_ENTRY_KIND = {
- STRUCT: 0x00,
- VARIABLE: 0x01,
- ENUM: 0x02,
- ARRAY: 0x03
- }
- const CODE_INFO_ENTRY_KIND_TEXT = {
- [CODE_INFO_ENTRY_KIND.STRUCT]: 'struct',
- [CODE_INFO_ENTRY_KIND.VARIABLE]: 'variable',
- [CODE_INFO_ENTRY_KIND.ENUM]: 'enum',
- [CODE_INFO_ENTRY_KIND.ARRAY]: 'array'
- }
- const CODE_INFO_ENTRY_NAME_ROLE = {
- DEFINITION: 'definition',
- INSTANCE: 'instance',
- VARIABLE: 'variable'
- }
- const CODE_INFO_ENTRY_TYPE_DEFINITIONS = {
- [CODE_INFO_TLV.RAW_VARIABLE]: {
- dataType: 'raw',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- name: 'raw_variable',
- valueTypeName: 'raw'
- },
- [CODE_INFO_TLV.INT8_VARIABLE]: {
- dataType: 'int8_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 1,
- name: 'int8_variable',
- valueTypeName: 'int8_t'
- },
- [CODE_INFO_TLV.UINT8_VARIABLE]: {
- dataType: 'uint8_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 1,
- name: 'uint8_variable',
- valueTypeName: 'uint8_t'
- },
- [CODE_INFO_TLV.INT16_VARIABLE]: {
- dataType: 'int16_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 2,
- name: 'int16_variable',
- valueTypeName: 'int16_t'
- },
- [CODE_INFO_TLV.UINT16_VARIABLE]: {
- dataType: 'uint16_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 2,
- name: 'uint16_variable',
- valueTypeName: 'uint16_t'
- },
- [CODE_INFO_TLV.INT32_VARIABLE]: {
- dataType: 'int32_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 4,
- name: 'int32_variable',
- valueTypeName: 'int32_t'
- },
- [CODE_INFO_TLV.UINT32_VARIABLE]: {
- dataType: 'uint32_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 4,
- name: 'uint32_variable',
- valueTypeName: 'uint32_t'
- },
- [CODE_INFO_TLV.FLOAT32_VARIABLE]: {
- dataType: 'float',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 4,
- name: 'float32_variable',
- valueTypeName: 'float32'
- },
- [CODE_INFO_TLV.DOUBLE_VARIABLE]: {
- dataType: 'double',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 8,
- name: 'double_variable',
- valueTypeName: 'double'
- },
- [CODE_INFO_TLV.INT64_VARIABLE]: {
- dataType: 'int64_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 8,
- name: 'int64_variable',
- valueTypeName: 'int64_t'
- },
- [CODE_INFO_TLV.UINT64_VARIABLE]: {
- dataType: 'uint64_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 8,
- name: 'uint64_variable',
- valueTypeName: 'uint64_t'
- },
- [CODE_INFO_TLV.INT128_VARIABLE]: {
- dataType: 'int128_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 16,
- name: 'int128_variable',
- valueTypeName: 'int128_t'
- },
- [CODE_INFO_TLV.UINT128_VARIABLE]: {
- dataType: 'uint128_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 16,
- name: 'uint128_variable',
- valueTypeName: 'uint128_t'
- },
- [CODE_INFO_TLV.INT256_VARIABLE]: {
- dataType: 'int256_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 32,
- name: 'int256_variable',
- valueTypeName: 'int256_t'
- },
- [CODE_INFO_TLV.UINT256_VARIABLE]: {
- dataType: 'uint256_t',
- entryKind: CODE_INFO_ENTRY_KIND.VARIABLE,
- expectedByteLength: 32,
- name: 'uint256_variable',
- valueTypeName: 'uint256_t'
- },
- [CODE_INFO_TLV.ENUM_VARIABLE]: {
- dataType: '',
- entryKind: CODE_INFO_ENTRY_KIND.ENUM,
- name: 'enum_variable',
- valueTypeName: 'enum'
- },
- [CODE_INFO_TLV.STRUCT_INSTANCE]: {
- dataType: 'raw',
- entryKind: CODE_INFO_ENTRY_KIND.STRUCT,
- name: 'struct_instance',
- valueTypeName: 'struct'
- },
- [CODE_INFO_TLV.RAW_ARRAY]: {
- dataType: 'raw',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- name: 'raw_array',
- valueTypeName: 'raw'
- },
- [CODE_INFO_TLV.INT8_ARRAY]: {
- dataType: 'int8_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 1,
- name: 'int8_array',
- valueTypeName: 'int8_t'
- },
- [CODE_INFO_TLV.UINT8_ARRAY]: {
- dataType: 'uint8_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 1,
- name: 'uint8_array',
- valueTypeName: 'uint8_t'
- },
- [CODE_INFO_TLV.INT16_ARRAY]: {
- dataType: 'int16_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 2,
- name: 'int16_array',
- valueTypeName: 'int16_t'
- },
- [CODE_INFO_TLV.UINT16_ARRAY]: {
- dataType: 'uint16_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 2,
- name: 'uint16_array',
- valueTypeName: 'uint16_t'
- },
- [CODE_INFO_TLV.INT32_ARRAY]: {
- dataType: 'int32_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 4,
- name: 'int32_array',
- valueTypeName: 'int32_t'
- },
- [CODE_INFO_TLV.UINT32_ARRAY]: {
- dataType: 'uint32_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 4,
- name: 'uint32_array',
- valueTypeName: 'uint32_t'
- },
- [CODE_INFO_TLV.FLOAT32_ARRAY]: {
- dataType: 'float',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 4,
- name: 'float32_array',
- valueTypeName: 'float32'
- },
- [CODE_INFO_TLV.DOUBLE_ARRAY]: {
- dataType: 'double',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 8,
- name: 'double_array',
- valueTypeName: 'double'
- },
- [CODE_INFO_TLV.INT64_ARRAY]: {
- dataType: 'int64_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 8,
- name: 'int64_array',
- valueTypeName: 'int64_t'
- },
- [CODE_INFO_TLV.UINT64_ARRAY]: {
- dataType: 'uint64_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 8,
- name: 'uint64_array',
- valueTypeName: 'uint64_t'
- },
- [CODE_INFO_TLV.INT128_ARRAY]: {
- dataType: 'int128_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 16,
- name: 'int128_array',
- valueTypeName: 'int128_t'
- },
- [CODE_INFO_TLV.UINT128_ARRAY]: {
- dataType: 'uint128_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 16,
- name: 'uint128_array',
- valueTypeName: 'uint128_t'
- },
- [CODE_INFO_TLV.INT256_ARRAY]: {
- dataType: 'int256_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 32,
- name: 'int256_array',
- valueTypeName: 'int256_t'
- },
- [CODE_INFO_TLV.UINT256_ARRAY]: {
- dataType: 'uint256_t',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 32,
- name: 'uint256_array',
- valueTypeName: 'uint256_t'
- },
- [CODE_INFO_TLV.TEXT_ARRAY]: {
- dataType: 'text',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- expectedByteLength: 1,
- name: 'text_array',
- valueTypeName: 'text'
- },
- [CODE_INFO_TLV.ENUM_ARRAY]: {
- dataType: '',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- name: 'enum_array',
- valueTypeName: 'enum'
- },
- [CODE_INFO_TLV.STRUCT_ARRAY]: {
- dataType: 'raw',
- entryKind: CODE_INFO_ENTRY_KIND.ARRAY,
- name: 'struct_array',
- valueTypeName: 'struct'
- }
- }
- const CODE_INFO_MEMORY_AREA = {
- DATA: 0x01,
- IDATA: 0x02,
- XDATA: 0x03,
- CODE: 0x04,
- ADDR32: 0x07
- }
- const CODE_INFO_MEMORY_AREA_NAMES = {
- [CODE_INFO_MEMORY_AREA.DATA]: 'DATA',
- [CODE_INFO_MEMORY_AREA.IDATA]: 'IDATA',
- [CODE_INFO_MEMORY_AREA.XDATA]: 'XDATA',
- [CODE_INFO_MEMORY_AREA.CODE]: 'CODE',
- [CODE_INFO_MEMORY_AREA.ADDR32]: 'ADDR32'
- }
- const MEMORY_ENDIAN = {
- BIG: 'big',
- LITTLE: 'little'
- }
- function toBytes(bytes) {
- return Array.prototype.slice.call(bytes || []).map((byte) => Number(byte) & 0xFF)
- }
- function normalizeMemoryEndian(value, fallback = MEMORY_ENDIAN.LITTLE) {
- const text = String(value || '').trim().toLowerCase()
- if (text === 'little' || text === 'le' || text === '1') return MEMORY_ENDIAN.LITTLE
- if (text === 'big' || text === 'be' || text === '0') return MEMORY_ENDIAN.BIG
- return fallback
- }
- function applyMemoryEndian(bytes = [], memoryEndian = MEMORY_ENDIAN.LITTLE) {
- const source = Array.isArray(bytes) ? bytes.slice() : []
- return normalizeMemoryEndian(memoryEndian) === MEMORY_ENDIAN.LITTLE
- ? source.reverse()
- : source
- }
- function readFloat(bytes, offset, memoryEndian = MEMORY_ENDIAN.LITTLE) {
- if (offset + 3 >= bytes.length) return 0
- const buffer = new ArrayBuffer(4)
- const view = new DataView(buffer)
- const source = applyMemoryEndian(bytes.slice(offset, offset + 4), memoryEndian)
- for (let index = 0; index < 4; index += 1) {
- view.setUint8(index, source[index] || 0)
- }
- return view.getFloat32(0, false)
- }
- function readTlvText(bytes) {
- return bytesToAutoText(trimTrailingNullBytes(bytes)).trim()
- }
- function normalizeCodeInfoAddressWidth(value) {
- const numberValue = Number(value)
- if (numberValue === 16) return 16
- if (numberValue === 32) return 32
- throw new Error('CodeInfo 描述符地址长度必须为 16 或 32')
- }
- function formatAddress(address, addressWidth = 32) {
- const numberValue = Math.max(0, Math.floor(Number(address) || 0))
- const hexWidth = normalizeCodeInfoAddressWidth(addressWidth) === 16 ? 4 : 8
- return `0x${formatHexNumber(numberValue, hexWidth)}`
- }
- function normalizeTypeName(value, fallback) {
- const text = String(value || '').trim()
- return text || fallback
- }
- function getEntryKindText(entryKind) {
- return CODE_INFO_ENTRY_KIND_TEXT[entryKind] || 'unknown'
- }
- function getEntryTypeDefinition(type) {
- return CODE_INFO_ENTRY_TYPE_DEFINITIONS[Number(type) & 0xFF] || null
- }
- function getTlvName(type) {
- const tlvType = Number(type) & 0xFF
- const entryDefinition = getEntryTypeDefinition(tlvType)
- if (entryDefinition && entryDefinition.name) return entryDefinition.name
- if (CODE_INFO_BOARD_TLV_NAMES[tlvType]) return CODE_INFO_BOARD_TLV_NAMES[tlvType]
- return `tlv_0x${formatHexNumber(tlvType, 2)}`
- }
- function readMemoryEndianUint16(bytes, memoryEndian) {
- return normalizeMemoryEndian(memoryEndian) === MEMORY_ENDIAN.LITTLE
- ? readUint16LE(bytes, 0)
- : readUint16BE(bytes, 0)
- }
- function readMemoryEndianUint32(bytes, memoryEndian) {
- return normalizeMemoryEndian(memoryEndian) === MEMORY_ENDIAN.LITTLE
- ? readUint32LE(bytes, 0)
- : readUint32BE(bytes, 0)
- }
- function readNameField(valueBytes, offset, role) {
- if (offset >= valueBytes.length) {
- throw new Error('CodeInfo 内存入口 TLV 名称字段缺失')
- }
- const byteLength = valueBytes[offset] & 0xFF
- const nameOffset = offset + CODE_INFO_ENTRY_NAME_LENGTH_BYTE_LENGTH
- const nextOffset = nameOffset + byteLength
- if (byteLength > CODE_INFO_ENTRY_MAX_NAME_BYTE_LENGTH || nextOffset > valueBytes.length) {
- throw new Error(`CodeInfo 内存入口 TLV 名称长度无效,声明 ${byteLength} 字节,实际 ${Math.max(0, valueBytes.length - nameOffset)} 字节`)
- }
- return {
- field: {
- byteLength,
- role,
- text: readTlvText(valueBytes.slice(nameOffset, nextOffset))
- },
- nextOffset
- }
- }
- function ensureNoTrailingBytes(valueBytes, offset) {
- if (offset !== valueBytes.length) {
- throw new Error('CodeInfo 内存入口 TLV 长度与字段定义不一致')
- }
- }
- function resolveCodeInfoByteLength(sourceLength, options = {}) {
- const expectedLength = Number(
- options.codeInfoByteLength === undefined ? options.byteLength : options.codeInfoByteLength
- )
- if (!Number.isFinite(expectedLength) || expectedLength <= 0) return sourceLength
- if (sourceLength < expectedLength) {
- throw new Error('CodeInfo 数据长度小于描述符声明长度')
- }
- return Math.floor(expectedLength)
- }
- function parseEntryPrefix(valueBytes, options = {}) {
- const addressWidth = normalizeCodeInfoAddressWidth(options.addressWidth)
- const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
- const addressByteLength = addressWidth === 16 ? 2 : 4
- const minPrefixByteLength = (addressWidth === 16 ? 1 : 0) + addressByteLength + 2
- if (valueBytes.length < minPrefixByteLength) {
- throw new Error(`CodeInfo 内存入口 TLV 长度无效,至少需要 ${minPrefixByteLength} 字节`)
- }
- let cursor = 0
- let memoryArea = 'ADDR32'
- let memoryAreaCode = CODE_INFO_MEMORY_AREA.ADDR32
- if (addressWidth === 16) {
- memoryAreaCode = valueBytes[cursor] & 0xFF
- memoryArea = CODE_INFO_MEMORY_AREA_NAMES[memoryAreaCode] || ''
- if (!memoryArea || memoryAreaCode === CODE_INFO_MEMORY_AREA.ADDR32) {
- throw new Error('CodeInfo 16 位地址入口存储区域无效')
- }
- cursor += 1
- }
- const byteAddr = addressByteLength === 2
- ? readMemoryEndianUint16(valueBytes.slice(cursor, cursor + addressByteLength), memoryEndian)
- : readMemoryEndianUint32(valueBytes.slice(cursor, cursor + addressByteLength), memoryEndian)
- cursor += addressByteLength
- const byteLength = readMemoryEndianUint16(valueBytes.slice(cursor, cursor + 2), memoryEndian)
- cursor += 2
- return {
- addressByteLength,
- addressWidth,
- byteAddr,
- byteLength,
- cursor,
- memoryArea,
- memoryAreaCode
- }
- }
- function getEntryFallbackName(entryKindText, index) {
- if (entryKindText === 'enum') return `enum_${index + 1}`
- if (entryKindText === 'array') return `array_${index + 1}`
- if (entryKindText === 'variable') return `var_${index + 1}`
- return `struct_${index + 1}`
- }
- function createEntryBase(prefix, index, type, entryKind, names, extra = {}) {
- const entryKindText = getEntryKindText(entryKind)
- const fallbackName = getEntryFallbackName(entryKindText, index)
- const definitionField = names.find((name) => name.role === CODE_INFO_ENTRY_NAME_ROLE.DEFINITION)
- const instanceField = names.find((name) => name.role === CODE_INFO_ENTRY_NAME_ROLE.INSTANCE)
- || names.find((name) => name.role === CODE_INFO_ENTRY_NAME_ROLE.VARIABLE)
- const definitionName = normalizeTypeName(definitionField && definitionField.text, '')
- const instanceName = normalizeTypeName(instanceField && instanceField.text, definitionName || fallbackName)
- const symbolName = entryKind === CODE_INFO_ENTRY_KIND.VARIABLE
- ? normalizeTypeName(instanceField && instanceField.text, fallbackName)
- : instanceName
- const typeName = definitionName || extra.valueTypeName || symbolName
- return {
- addressByteLength: prefix.addressByteLength,
- addressWidth: prefix.addressWidth,
- byteAddr: prefix.byteAddr,
- byteLength: prefix.byteLength,
- definitionName,
- entryKind,
- entryKindText,
- index,
- instanceName,
- isArrayEntry: entryKind === CODE_INFO_ENTRY_KIND.ARRAY,
- isEnumEntry: entryKind === CODE_INFO_ENTRY_KIND.ENUM,
- isStructEntry: entryKind === CODE_INFO_ENTRY_KIND.STRUCT,
- isVariableEntry: entryKind === CODE_INFO_ENTRY_KIND.VARIABLE,
- memType: prefix.memoryAreaCode,
- memoryArea: prefix.memoryArea || 'UNKNOWN',
- nameByteLength: names.reduce((total, name) => total + Number(name.byteLength || 0), 0),
- nameFields: names.map((name) => ({
- byteLength: name.byteLength,
- role: name.role,
- text: name.text
- })),
- rawByteLength: 0,
- sourceAddressText: formatAddress(prefix.byteAddr, prefix.addressWidth),
- symbolName,
- tlvType: Number(type) & 0xFF,
- typeName,
- ...extra
- }
- }
- function validateEntryByteLength(prefix, entryDefinition, label) {
- const expectedByteLength = Number(entryDefinition && entryDefinition.expectedByteLength)
- if (!Number.isFinite(expectedByteLength) || expectedByteLength <= 0) return
- if (Number(prefix.byteLength) !== expectedByteLength) {
- throw new Error(`CodeInfo ${label} 字节长度与 TYPE 定义不一致`)
- }
- }
- function parseVariableEntry(valueBytes, index, type, entryDefinition, options = {}) {
- const prefix = parseEntryPrefix(valueBytes, options)
- let cursor = prefix.cursor
- const nameResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.VARIABLE)
- cursor = nameResult.nextOffset
- ensureNoTrailingBytes(valueBytes, cursor)
- validateEntryByteLength(prefix, entryDefinition, '变量入口')
- const entry = createEntryBase(
- prefix,
- index,
- type,
- CODE_INFO_ENTRY_KIND.VARIABLE,
- [nameResult.field],
- {
- dataType: entryDefinition.dataType || 'raw',
- valueTypeName: entryDefinition.valueTypeName || 'raw'
- }
- )
- return {
- ...entry,
- rawByteLength: valueBytes.length
- }
- }
- function parseEnumEntry(valueBytes, index, type, entryDefinition, options = {}) {
- const prefix = parseEntryPrefix(valueBytes, options)
- let cursor = prefix.cursor
- const definitionResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.DEFINITION)
- cursor = definitionResult.nextOffset
- const instanceResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.INSTANCE)
- cursor = instanceResult.nextOffset
- ensureNoTrailingBytes(valueBytes, cursor)
- const entry = createEntryBase(
- prefix,
- index,
- type,
- CODE_INFO_ENTRY_KIND.ENUM,
- [definitionResult.field, instanceResult.field],
- {
- dataType: entryDefinition.dataType || getIntegerDataTypeForByteLength(prefix.byteLength),
- valueTypeName: entryDefinition.valueTypeName || 'enum'
- }
- )
- return {
- ...entry,
- rawByteLength: valueBytes.length
- }
- }
- function parseStructEntry(valueBytes, index, type, entryDefinition, options = {}) {
- const prefix = parseEntryPrefix(valueBytes, options)
- let cursor = prefix.cursor
- const definitionResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.DEFINITION)
- cursor = definitionResult.nextOffset
- const instanceResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.INSTANCE)
- cursor = instanceResult.nextOffset
- ensureNoTrailingBytes(valueBytes, cursor)
- const entry = createEntryBase(
- prefix,
- index,
- type,
- CODE_INFO_ENTRY_KIND.STRUCT,
- [definitionResult.field, instanceResult.field],
- {
- dataType: entryDefinition.dataType || 'raw',
- valueTypeName: entryDefinition.valueTypeName || 'struct'
- }
- )
- return {
- ...entry,
- rawByteLength: valueBytes.length
- }
- }
- function readArrayDimensions(valueBytes, cursor, dimCount, memoryEndian) {
- const dimensions = []
- for (let index = 0; index < dimCount; index += 1) {
- if (cursor + 2 > valueBytes.length) {
- throw new Error('CodeInfo 数组维度字段长度无效')
- }
- const length = readMemoryEndianUint16(valueBytes.slice(cursor, cursor + 2), memoryEndian)
- if (!length) throw new Error('CodeInfo 数组维度长度必须大于 0')
- dimensions.push(length)
- cursor += 2
- }
- return {
- dimensions,
- nextOffset: cursor
- }
- }
- function parseArrayEntry(valueBytes, index, type, entryDefinition, options = {}) {
- const prefix = parseEntryPrefix(valueBytes, options)
- const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
- let cursor = prefix.cursor
- if (cursor + 5 > valueBytes.length) {
- throw new Error('CodeInfo 数组入口长度无效')
- }
- const elementByteLength = readMemoryEndianUint16(valueBytes.slice(cursor, cursor + 2), memoryEndian)
- cursor += 2
- const elementCount = readMemoryEndianUint16(valueBytes.slice(cursor, cursor + 2), memoryEndian)
- cursor += 2
- const dimensionCount = valueBytes[cursor] & 0xFF
- cursor += 1
- if (!elementByteLength) throw new Error('CodeInfo 数组元素字节长度必须大于 0')
- if (!elementCount) throw new Error('CodeInfo 数组元素数量必须大于 0')
- if (!dimensionCount) throw new Error('CodeInfo 数组维度数量必须大于 0')
- const dimensionsResult = readArrayDimensions(valueBytes, cursor, dimensionCount, memoryEndian)
- cursor = dimensionsResult.nextOffset
- const needsDefinitionName = type === CODE_INFO_TLV.ENUM_ARRAY || type === CODE_INFO_TLV.STRUCT_ARRAY
- let definitionResult = null
- if (needsDefinitionName) {
- definitionResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.DEFINITION)
- cursor = definitionResult.nextOffset
- }
- const instanceResult = readNameField(valueBytes, cursor, CODE_INFO_ENTRY_NAME_ROLE.INSTANCE)
- cursor = instanceResult.nextOffset
- ensureNoTrailingBytes(valueBytes, cursor)
- validateEntryByteLength({ byteLength: elementByteLength }, entryDefinition, '数组元素')
- const dimensionProduct = dimensionsResult.dimensions.reduce((total, value) => total * value, 1)
- if (dimensionProduct !== elementCount) {
- throw new Error('CodeInfo 数组维度乘积与元素数量不一致')
- }
- if (elementByteLength * elementCount !== prefix.byteLength) {
- throw new Error('CodeInfo 数组总字节长度与元素数量不一致')
- }
- const entry = createEntryBase(
- prefix,
- index,
- type,
- CODE_INFO_ENTRY_KIND.ARRAY,
- (definitionResult ? [definitionResult.field] : []).concat(instanceResult.field),
- {
- arrayDimensions: dimensionsResult.dimensions,
- arrayElementBaseIndex: 0,
- dataType: entryDefinition.dataType || 'raw',
- elementByteLength,
- elementCount,
- isEnumArrayEntry: type === CODE_INFO_TLV.ENUM_ARRAY,
- isStructArrayEntry: type === CODE_INFO_TLV.STRUCT_ARRAY,
- isTextArrayEntry: type === CODE_INFO_TLV.TEXT_ARRAY,
- valueTypeName: entryDefinition.valueTypeName || 'raw'
- }
- )
- return {
- ...entry,
- rawByteLength: valueBytes.length
- }
- }
- function parseMemoryEntry(valueBytes, index, type, options = {}) {
- const entryDefinition = getEntryTypeDefinition(type)
- if (!entryDefinition) return null
- switch (entryDefinition.entryKind) {
- case CODE_INFO_ENTRY_KIND.VARIABLE:
- return parseVariableEntry(valueBytes, index, type, entryDefinition, options)
- case CODE_INFO_ENTRY_KIND.ENUM:
- return parseEnumEntry(valueBytes, index, type, entryDefinition, options)
- case CODE_INFO_ENTRY_KIND.STRUCT:
- return parseStructEntry(valueBytes, index, type, entryDefinition, options)
- case CODE_INFO_ENTRY_KIND.ARRAY:
- return parseArrayEntry(valueBytes, index, type, entryDefinition, options)
- default:
- return null
- }
- }
- function applyCodeInfoTlvValue(target, type, valueBytes, options = {}) {
- const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
- switch (type) {
- case CODE_INFO_TLV.CAVE_FREQ:
- if (valueBytes.length < 1) throw new Error('CodeInfo cave_freq TLV 长度无效')
- target.caveFreq = valueBytes[0] & 0xFF
- break
- case CODE_INFO_TLV.REF_VOLT:
- if (valueBytes.length < 1) throw new Error('CodeInfo ref_volt TLV 长度无效')
- target.refVoltRaw = valueBytes[0] & 0xFF
- target.refVolt = target.refVoltRaw / 10
- break
- case CODE_INFO_TLV.AMP_GAIN:
- if (valueBytes.length < 1) throw new Error('CodeInfo amp_gain TLV 长度无效')
- target.ampGain = valueBytes[0] & 0xFF
- break
- case CODE_INFO_TLV.RS_SHUNT:
- if (valueBytes.length < 2) throw new Error('CodeInfo rs_shunt TLV 长度无效')
- target.rsShunt = readMemoryEndianUint16(valueBytes, memoryEndian)
- break
- case CODE_INFO_TLV.BUS_DIV:
- if (valueBytes.length < 4) throw new Error('CodeInfo bus_div TLV 长度无效')
- target.busDiv = readFloat(valueBytes, 0, memoryEndian)
- break
- case CODE_INFO_TLV.ALONG_DIV:
- if (valueBytes.length < 4) throw new Error('CodeInfo along_div TLV 长度无效')
- target.alongDiv = readFloat(valueBytes, 0, memoryEndian)
- break
- case CODE_INFO_TLV.CHIP_MODEL:
- target.chipModel = readTlvText(valueBytes)
- break
- case CODE_INFO_TLV.MODEL:
- target.model = readTlvText(valueBytes)
- break
- default:
- break
- }
- }
- function parseCodeInfoTlvs(bytes, options = {}) {
- const info = {
- entries: [],
- tlvItems: []
- }
- let offset = 0
- while (offset < bytes.length) {
- if (offset + CODE_INFO_TLV_HEADER_BYTE_LENGTH > bytes.length) {
- throw new Error('CodeInfo TLV 项头长度无效')
- }
- const type = bytes[offset] & 0xFF
- const byteLength = bytes[offset + 1] & 0xFF
- const valueOffset = offset + CODE_INFO_TLV_HEADER_BYTE_LENGTH
- const nextOffset = valueOffset + byteLength
- if (nextOffset > bytes.length) {
- throw new Error('CodeInfo TLV 项长度超出声明范围')
- }
- const valueBytes = bytes.slice(valueOffset, nextOffset)
- const item = {
- byteLength,
- name: getTlvName(type),
- rawHex: bytesToHex(valueBytes, ' '),
- type
- }
- info.tlvItems.push(item)
- const entry = parseMemoryEntry(valueBytes, info.entries.length, type, options)
- if (entry) {
- info.entries.push(entry)
- } else {
- applyCodeInfoTlvValue(info, type, valueBytes, options)
- }
- offset = nextOffset
- }
- return info
- }
- function parseCodeInfo(bytes, options = {}) {
- const inputBytes = toBytes(bytes)
- const codeInfoByteLength = resolveCodeInfoByteLength(inputBytes.length, options)
- const source = inputBytes.slice(0, codeInfoByteLength)
- const addressWidth = normalizeCodeInfoAddressWidth(options.addressWidth)
- const maxPacketLength = Number(options.maxPacketLength || 0) & 0xFFFF
- const memoryEndian = normalizeMemoryEndian(options.memoryEndian)
- const tlvInfo = parseCodeInfoTlvs(source, { addressWidth, memoryEndian })
- const entries = tlvInfo.entries
- const entryCount = entries.length
- const structCount = entries.filter((entry) => entry.isStructEntry || entry.isStructArrayEntry).length
- const enumCount = entries.filter((entry) => entry.isEnumEntry || entry.isEnumArrayEntry).length
- const variableCount = entries.filter((entry) => entry.isVariableEntry).length
- const arrayCount = entries.filter((entry) => entry.isArrayEntry).length
- const entryAddressByteLengths = entries
- .map((entry) => Number(entry.addressByteLength) || 0)
- .filter((value, index, list) => value > 0 && list.indexOf(value) === index)
- return {
- ...tlvInfo,
- addressWidth,
- addressWidthRaw: addressWidth,
- arrayCount,
- byteLength: codeInfoByteLength,
- entryCount,
- enumCount,
- maxPacketLength,
- memoryEndian,
- memoryEndianRaw: Number(options.memoryEndianRaw || options.memoryEndianMark || 0) & 0xFFFF,
- rawHex: bytesToHex(source, ' '),
- structEntryAddressByteLength: entryAddressByteLengths.length === 1 ? entryAddressByteLengths[0] : 0,
- structCount,
- structTable: entries,
- tableByteLength: entries.reduce((total, entry) => total + Number(entry.rawByteLength || 0), 0),
- tableCapacity: entryCount,
- tableRemainderByteLength: 0,
- tlvByteLength: source.length,
- truncated: false,
- variableCount
- }
- }
- function getIntegerDataTypeForByteLength(byteLength) {
- const length = Number(byteLength)
- if (length === 1) return 'uint8_t'
- if (length === 2) return 'uint16_t'
- if (length === 4) return 'uint32_t'
- return 'raw'
- }
- function getArrayIndexPath(linearIndex, dimensions = []) {
- const safeDimensions = (Array.isArray(dimensions) ? dimensions : [])
- .map((value) => Math.max(1, Math.floor(Number(value) || 1)))
- .filter((value) => value > 0)
- if (!safeDimensions.length) return [Math.max(0, Math.floor(Number(linearIndex) || 0))]
- const indexes = Array.from({ length: safeDimensions.length }, () => 0)
- let remaining = Math.max(0, Math.floor(Number(linearIndex) || 0))
- for (let index = safeDimensions.length - 1; index >= 0; index -= 1) {
- indexes[index] = remaining % safeDimensions[index]
- remaining = Math.floor(remaining / safeDimensions[index])
- }
- return indexes
- }
- function formatArrayIndexPath(indexPath = []) {
- return (Array.isArray(indexPath) ? indexPath : [indexPath])
- .map((index) => `[${Math.max(0, Math.floor(Number(index) || 0))}]`)
- .join('')
- }
- function createRegisterSourceMeta(entry, byteStart, byteLength, extra = {}) {
- const sourceAddress = entry.byteAddr + Math.max(0, Math.floor(Number(byteStart) || 0))
- return {
- sourceAddress,
- sourceAddressByteLength: entry.addressByteLength,
- sourceAddressText: formatAddress(sourceAddress, entry.addressWidth),
- sourceAddressWidth: entry.addressWidth,
- sourceArrayDimensions: entry.arrayDimensions,
- sourceByteLength: byteLength,
- sourceDefinitionName: entry.definitionName,
- sourceElementByteLength: entry.elementByteLength,
- sourceElementCount: entry.elementCount,
- sourceElementType: entry.valueTypeName,
- sourceEntryKind: entry.entryKindText,
- sourceInstanceName: entry.instanceName,
- sourceMemoryArea: entry.memoryArea,
- sourceMemoryClass: entry.memoryArea,
- sourceSymbolName: entry.symbolName,
- sourceSymbolType: entry.typeName,
- sourceValueType: entry.valueTypeName,
- ...extra
- }
- }
- function createRegistersForByteSpan(entry) {
- const count = Math.max(1, Number(entry.byteLength) || 1)
- const registers = []
- for (let offset = 0; offset < count; offset += 1) {
- registers.push({
- byteStart: offset,
- dataType: 'uint8_t',
- isPlaceholderByteField: true,
- name: offset.toString(16).toUpperCase().padStart(2, '0'),
- ...createRegisterSourceMeta(entry, offset, 1)
- })
- }
- return registers
- }
- function createScalarRegister(entry) {
- const dataType = entry.dataType || (entry.isEnumEntry ? getIntegerDataTypeForByteLength(entry.byteLength) : 'raw')
- const register = {
- byteStart: 0,
- dataType,
- name: entry.symbolName,
- ...createRegisterSourceMeta(entry, 0, entry.byteLength)
- }
- if (dataType === 'text') {
- register.textByteLength = String(entry.byteLength)
- }
- return [register]
- }
- function createArrayRegisters(entry) {
- if (entry.isStructArrayEntry) return createRegistersForByteSpan(entry)
- const dataType = entry.dataType || 'raw'
- if (dataType === 'text') {
- return [{
- byteStart: 0,
- dataType,
- name: entry.symbolName,
- textByteLength: String(entry.byteLength),
- ...createRegisterSourceMeta(entry, 0, entry.byteLength)
- }]
- }
- const count = Math.max(1, Number(entry.elementCount) || 1)
- const elementByteLength = Math.max(1, Number(entry.elementByteLength) || 1)
- const baseIndex = Math.max(0, Number(entry.arrayElementBaseIndex) || 0)
- const registers = []
- for (let index = 0; index < count; index += 1) {
- const linearIndex = baseIndex + index
- const indexPath = getArrayIndexPath(linearIndex, entry.arrayDimensions)
- const byteStart = index * elementByteLength
- registers.push({
- byteStart,
- dataType,
- name: `${entry.symbolName}${formatArrayIndexPath(indexPath)}`,
- ...createRegisterSourceMeta(entry, byteStart, elementByteLength, {
- sourceArrayIndex: linearIndex,
- sourceArrayIndexPath: indexPath,
- sourceSymbolName: `${entry.symbolName}${formatArrayIndexPath(indexPath)}`
- })
- })
- }
- return registers
- }
- function createCodeInfoContext(codeInfo = {}) {
- return {
- addressWidth: codeInfo.addressWidth,
- codeInfoAddressWidth: codeInfo.addressWidth,
- maxPacketLength: codeInfo.maxPacketLength,
- memoryEndian: codeInfo.memoryEndian,
- memoryEndianRaw: Number(codeInfo.memoryEndianRaw || 0) & 0xFFFF,
- storageAddressWidth: codeInfo.addressWidth
- }
- }
- function createSourceMeta(entry, byteLength) {
- return {
- addressUnit: 'byte',
- sourceAddress: entry.byteAddr,
- sourceAddressByteLength: entry.addressByteLength,
- sourceAddressText: formatAddress(entry.byteAddr, entry.addressWidth),
- sourceAddressWidth: entry.addressWidth,
- sourceArrayDimensions: entry.arrayDimensions,
- sourceByteLength: byteLength,
- sourceDefinitionName: entry.definitionName,
- sourceElementByteLength: entry.elementByteLength,
- sourceElementCount: entry.elementCount,
- sourceElementType: entry.valueTypeName,
- sourceEntryKind: entry.entryKindText,
- sourceInstanceName: entry.instanceName,
- sourceMemoryArea: entry.memoryArea,
- sourceMemoryClass: entry.memoryArea,
- sourceSymbolName: entry.symbolName,
- sourceSymbolType: entry.typeName,
- sourceValueType: entry.valueTypeName
- }
- }
- function createGroupFromEntry(entry, chunkLength, suffix = '', codeInfo = {}) {
- const isStructLike = entry.isStructEntry || entry.isStructArrayEntry
- const registers = isStructLike
- ? createRegistersForByteSpan(entry)
- : (entry.isArrayEntry ? createArrayRegisters(entry) : createScalarRegister(entry))
- return {
- codeInfoContext: createCodeInfoContext(codeInfo),
- layout: isStructLike ? 'struct' : 'register',
- name: `${entry.symbolName || entry.typeName}${suffix}`,
- quantity: registers.length,
- registerType: entry.memoryArea === 'CODE' ? 'input' : 'holding',
- registers,
- ...createSourceMeta(entry, chunkLength),
- sourceSegment: 'CodeInfo TLV',
- sourceSegmentModule: '',
- startAddress: formatAddress(entry.byteAddr, entry.addressWidth)
- }
- }
- function createEntryChunk(entry, offset, chunkLength) {
- const chunkEntry = {
- ...entry,
- byteAddr: entry.byteAddr + offset,
- byteLength: chunkLength
- }
- if (entry.isArrayEntry) {
- const elementByteLength = Math.max(1, Number(entry.elementByteLength) || 1)
- const elementOffset = Math.floor(offset / elementByteLength)
- const elementCount = Math.max(1, Math.floor(chunkLength / elementByteLength))
- chunkEntry.arrayElementBaseIndex = (Number(entry.arrayElementBaseIndex) || 0) + elementOffset
- chunkEntry.elementCount = elementCount
- }
- return chunkEntry
- }
- function getChunkStep(entry, maxRegisters) {
- if (!entry.isArrayEntry) return maxRegisters
- const elementByteLength = Math.max(1, Number(entry.elementByteLength) || 1)
- const elementsPerChunk = Math.max(1, Math.floor(maxRegisters / elementByteLength))
- return elementsPerChunk * elementByteLength
- }
- function createGroupsFromCodeInfo(codeInfo, options = {}) {
- const maxRegisters = Math.max(1, Number(options.maxRegistersPerGroup) || 256)
- const groups = []
- const entries = Array.isArray(codeInfo.entries) ? codeInfo.entries : codeInfo.structTable
- ;(Array.isArray(entries) ? entries : []).forEach((entry) => {
- if (!entry.byteLength || entry.memoryArea === 'UNKNOWN') return
- if (!entry.isStructEntry && !entry.isEnumEntry && !entry.isVariableEntry && !entry.isArrayEntry) return
- const chunkStep = getChunkStep(entry, maxRegisters)
- for (let offset = 0; offset < entry.byteLength; offset += chunkStep) {
- const chunkLength = Math.min(chunkStep, entry.byteLength - offset)
- const chunkEntry = createEntryChunk(entry, offset, chunkLength)
- const suffix = entry.byteLength > chunkStep ? ` #${Math.floor(offset / chunkStep) + 1}` : ''
- groups.push(createGroupFromEntry(chunkEntry, chunkLength, suffix, codeInfo))
- }
- })
- return groups
- }
- module.exports = {
- CODE_INFO_ENTRY_KIND,
- CODE_INFO_ENTRY_MAX_NAME_BYTE_LENGTH,
- CODE_INFO_ENTRY_NAME_LENGTH_BYTE_LENGTH,
- CODE_INFO_MEMORY_AREA,
- CODE_INFO_TLV_HEADER_BYTE_LENGTH,
- CODE_INFO_TLV,
- MEMORY_ENDIAN,
- createGroupsFromCodeInfo,
- normalizeCodeInfoAddressWidth,
- parseCodeInfo
- }
|