struct-layout.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. const {
  2. normalizeTypeText,
  3. parseDeclarator,
  4. parseFirstDeclarator,
  5. resolveType,
  6. splitDeclarations,
  7. splitDeclarators
  8. } = require('./struct-c-syntax.js')
  9. const FIELD_TYPE_QUALIFIERS = {
  10. _I: true,
  11. _IO: true,
  12. _O: true,
  13. code: true,
  14. const: true,
  15. data: true,
  16. extern: true,
  17. idata: true,
  18. pdata: true,
  19. register: true,
  20. static: true,
  21. volatile: true,
  22. xdata: true
  23. }
  24. function isAsciiArray(typeText, dataType, name, arrayLength) {
  25. if (!arrayLength || arrayLength < 2 || arrayLength > 32) return false
  26. const normalizedType = normalizeTypeText(typeText).toLowerCase()
  27. if (normalizedType === 'char' || normalizedType === 'signed char') return true
  28. return dataType === 'uint8_t' && /(^|_)(model|name|text|str|string|chip|version|ver|serial|sn)($|_)/i.test(name)
  29. }
  30. function getDataTypeByteLength(dataType) {
  31. if (dataType === 'int8_t' || dataType === 'uint8_t') return 1
  32. if (dataType === 'int16_t' || dataType === 'uint16_t') return 2
  33. if (dataType === 'float' || dataType === 'int32_t' || dataType === 'uint32_t') return 4
  34. if (dataType === 'double' || dataType === 'int64_t' || dataType === 'uint64_t') return 8
  35. if (dataType === 'int128_t' || dataType === 'uint128_t') return 16
  36. if (dataType === 'int256_t' || dataType === 'uint256_t') return 32
  37. if (dataType === 'int8_t' || dataType === 'uint8_t') return 1
  38. return 2
  39. }
  40. function getBitFieldDataType(bitWidth) {
  41. const width = Math.max(1, Math.round(Number(bitWidth) || 1))
  42. if (width <= 8) return 'uint8_t'
  43. if (width <= 16) return 'uint16_t'
  44. return 'uint32_t'
  45. }
  46. function cloneEnumOptions(enumInfo) {
  47. return (Array.isArray(enumInfo && enumInfo.options) ? enumInfo.options : []).map((option) => ({
  48. label: option.label || option.name,
  49. name: option.name || option.label,
  50. value: Number(option.value) || 0
  51. }))
  52. }
  53. function createEnumMeta(enumInfo) {
  54. if (!enumInfo || !Array.isArray(enumInfo.options) || !enumInfo.options.length) return {}
  55. return {
  56. enumName: enumInfo.name,
  57. enumOptions: cloneEnumOptions(enumInfo),
  58. sourceSymbolType: enumInfo.name
  59. }
  60. }
  61. function normalizeEnumLookupKey(typeText) {
  62. return normalizeTypeText(typeText)
  63. .split(/\s+/)
  64. .filter((token) => !FIELD_TYPE_QUALIFIERS[token])
  65. .join(' ')
  66. .trim()
  67. .toLowerCase()
  68. }
  69. function resolveEnumInfo(typeText, enumTypes = {}) {
  70. const key = normalizeEnumLookupKey(typeText)
  71. if (!key) return null
  72. return enumTypes[key]
  73. || enumTypes[key.replace(/^enum\s+/, '')]
  74. || null
  75. }
  76. function isBitType(typeText) {
  77. return normalizeTypeText(typeText).toLowerCase() === 'bit'
  78. }
  79. function alignLayoutToByte(layoutState) {
  80. if (layoutState.bitOffset % 8 !== 0) {
  81. layoutState.bitOffset += 8 - (layoutState.bitOffset % 8)
  82. }
  83. }
  84. function getLayoutByteStart(layoutState) {
  85. return Math.floor(layoutState.bitOffset / 8)
  86. }
  87. function advanceLayoutBytes(layoutState, byteLength) {
  88. layoutState.bitOffset += Math.max(1, Number(byteLength) || 1) * 8
  89. }
  90. function createBitFieldRegister(field, bitWidth, layoutState, name, enumInfo = null) {
  91. const width = Math.max(0, Math.round(Number(bitWidth) || 0))
  92. if (width === 0) {
  93. alignLayoutToByte(layoutState)
  94. return []
  95. }
  96. const byteStart = getLayoutByteStart(layoutState)
  97. const bitOffset = layoutState.bitOffset % 8
  98. layoutState.bitOffset += width
  99. if (!name) return []
  100. return [{
  101. bitOffset,
  102. bitWidth: width,
  103. byteStart,
  104. dataType: getBitFieldDataType(width),
  105. ...createEnumMeta(enumInfo),
  106. isBitField: true,
  107. name,
  108. unit: 'bit'
  109. }]
  110. }
  111. function createRegisterFromField(field, dataType, originalTypeText, layoutState, enumInfo = null) {
  112. const arrayLength = field.arrayDimensions.reduce((total, value) => total * value, 1)
  113. const hasArray = field.arrayDimensions.length > 0
  114. const bitFieldWidth = field.bitWidth !== null && field.bitWidth !== undefined
  115. ? field.bitWidth
  116. : (isBitType(originalTypeText) ? 1 : null)
  117. if (bitFieldWidth !== null && bitFieldWidth !== undefined) {
  118. if (hasArray) {
  119. const registers = []
  120. for (let index = 0; index < arrayLength; index += 1) {
  121. registers.push(...createBitFieldRegister(
  122. field,
  123. bitFieldWidth,
  124. layoutState,
  125. field.name ? `${field.name}[${index}]` : '',
  126. enumInfo
  127. ))
  128. }
  129. return registers
  130. }
  131. return createBitFieldRegister(field, bitFieldWidth, layoutState, field.name, enumInfo)
  132. }
  133. alignLayoutToByte(layoutState)
  134. if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) {
  135. const byteStart = getLayoutByteStart(layoutState)
  136. advanceLayoutBytes(layoutState, arrayLength)
  137. return [{
  138. byteStart,
  139. dataType: 'text',
  140. ...createEnumMeta(enumInfo),
  141. name: field.name,
  142. textByteLength: String(arrayLength)
  143. }]
  144. }
  145. if (!hasArray) {
  146. const byteStart = getLayoutByteStart(layoutState)
  147. advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType))
  148. return [{
  149. byteStart,
  150. dataType,
  151. ...createEnumMeta(enumInfo),
  152. name: field.name
  153. }]
  154. }
  155. const registers = []
  156. for (let index = 0; index < arrayLength; index += 1) {
  157. const byteStart = getLayoutByteStart(layoutState)
  158. advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType))
  159. registers.push({
  160. byteStart,
  161. dataType,
  162. ...createEnumMeta(enumInfo),
  163. name: `${field.name}[${index}]`
  164. })
  165. }
  166. return registers
  167. }
  168. function parseStructFields(body, aliases, enumTypes = {}) {
  169. const registers = []
  170. const layoutState = {
  171. bitOffset: 0
  172. }
  173. const declarations = splitDeclarations(body)
  174. declarations.forEach((statement) => {
  175. if (!statement || statement.indexOf('(') >= 0) return
  176. const parts = splitDeclarators(statement)
  177. if (!parts.length) return
  178. const first = parseFirstDeclarator(parts[0])
  179. if (!first) return
  180. const dataType = resolveType(first.typeText, aliases)
  181. if (!dataType) return
  182. const enumInfo = resolveEnumInfo(first.typeText, enumTypes)
  183. const declarators = [first.declarator].concat(parts.slice(1))
  184. declarators.forEach((declaratorText) => {
  185. const field = parseDeclarator(declaratorText)
  186. if (!field) return
  187. registers.push(...createRegisterFromField(field, dataType, first.typeText, layoutState, enumInfo))
  188. })
  189. })
  190. return registers.map((register) => ({
  191. ...register,
  192. structByteLength: Math.ceil(layoutState.bitOffset / 8)
  193. }))
  194. }
  195. module.exports = {
  196. parseStructFields
  197. }