struct-layout.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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 === 'float' || dataType === 'int32_t' || dataType === 'uint32_t') return 4
  32. if (dataType === 'int8_t' || dataType === 'uint8_t') return 1
  33. return 2
  34. }
  35. function getBitFieldDataType(bitWidth) {
  36. const width = Math.max(1, Math.round(Number(bitWidth) || 1))
  37. if (width <= 8) return 'uint8_t'
  38. if (width <= 16) return 'uint16_t'
  39. return 'uint32_t'
  40. }
  41. function cloneEnumOptions(enumInfo) {
  42. return (Array.isArray(enumInfo && enumInfo.options) ? enumInfo.options : []).map((option) => ({
  43. label: option.label || option.name,
  44. name: option.name || option.label,
  45. value: Number(option.value) || 0
  46. }))
  47. }
  48. function createEnumMeta(enumInfo) {
  49. if (!enumInfo || !Array.isArray(enumInfo.options) || !enumInfo.options.length) return {}
  50. return {
  51. enumName: enumInfo.name,
  52. enumOptions: cloneEnumOptions(enumInfo),
  53. sourceSymbolType: enumInfo.name
  54. }
  55. }
  56. function normalizeEnumLookupKey(typeText) {
  57. return normalizeTypeText(typeText)
  58. .split(/\s+/)
  59. .filter((token) => !FIELD_TYPE_QUALIFIERS[token])
  60. .join(' ')
  61. .trim()
  62. .toLowerCase()
  63. }
  64. function resolveEnumInfo(typeText, enumTypes = {}) {
  65. const key = normalizeEnumLookupKey(typeText)
  66. if (!key) return null
  67. return enumTypes[key]
  68. || enumTypes[key.replace(/^enum\s+/, '')]
  69. || null
  70. }
  71. function isBitType(typeText) {
  72. return normalizeTypeText(typeText).toLowerCase() === 'bit'
  73. }
  74. function alignLayoutToByte(layoutState) {
  75. if (layoutState.bitOffset % 8 !== 0) {
  76. layoutState.bitOffset += 8 - (layoutState.bitOffset % 8)
  77. }
  78. }
  79. function getLayoutByteStart(layoutState) {
  80. return Math.floor(layoutState.bitOffset / 8)
  81. }
  82. function advanceLayoutBytes(layoutState, byteLength) {
  83. layoutState.bitOffset += Math.max(1, Number(byteLength) || 1) * 8
  84. }
  85. function createBitFieldRegister(field, bitWidth, layoutState, name, enumInfo = null) {
  86. const width = Math.max(0, Math.round(Number(bitWidth) || 0))
  87. if (width === 0) {
  88. alignLayoutToByte(layoutState)
  89. return []
  90. }
  91. const byteStart = getLayoutByteStart(layoutState)
  92. const bitOffset = layoutState.bitOffset % 8
  93. layoutState.bitOffset += width
  94. if (!name) return []
  95. return [{
  96. bitOffset,
  97. bitWidth: width,
  98. byteStart,
  99. dataType: getBitFieldDataType(width),
  100. ...createEnumMeta(enumInfo),
  101. isBitField: true,
  102. name,
  103. unit: 'bit'
  104. }]
  105. }
  106. function createRegisterFromField(field, dataType, originalTypeText, layoutState, enumInfo = null) {
  107. const arrayLength = field.arrayDimensions.reduce((total, value) => total * value, 1)
  108. const hasArray = field.arrayDimensions.length > 0
  109. const bitFieldWidth = field.bitWidth !== null && field.bitWidth !== undefined
  110. ? field.bitWidth
  111. : (isBitType(originalTypeText) ? 1 : null)
  112. if (bitFieldWidth !== null && bitFieldWidth !== undefined) {
  113. if (hasArray) {
  114. const registers = []
  115. for (let index = 0; index < arrayLength; index += 1) {
  116. registers.push(...createBitFieldRegister(
  117. field,
  118. bitFieldWidth,
  119. layoutState,
  120. field.name ? `${field.name}[${index}]` : '',
  121. enumInfo
  122. ))
  123. }
  124. return registers
  125. }
  126. return createBitFieldRegister(field, bitFieldWidth, layoutState, field.name, enumInfo)
  127. }
  128. alignLayoutToByte(layoutState)
  129. if (hasArray && isAsciiArray(originalTypeText, dataType, field.name, arrayLength)) {
  130. const byteStart = getLayoutByteStart(layoutState)
  131. advanceLayoutBytes(layoutState, arrayLength)
  132. return [{
  133. byteStart,
  134. dataType: 'ascii',
  135. ...createEnumMeta(enumInfo),
  136. name: field.name,
  137. textByteLength: String(arrayLength)
  138. }]
  139. }
  140. if (!hasArray) {
  141. const byteStart = getLayoutByteStart(layoutState)
  142. advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType))
  143. return [{
  144. byteStart,
  145. dataType,
  146. ...createEnumMeta(enumInfo),
  147. name: field.name
  148. }]
  149. }
  150. const registers = []
  151. for (let index = 0; index < arrayLength; index += 1) {
  152. const byteStart = getLayoutByteStart(layoutState)
  153. advanceLayoutBytes(layoutState, getDataTypeByteLength(dataType))
  154. registers.push({
  155. byteStart,
  156. dataType,
  157. ...createEnumMeta(enumInfo),
  158. name: `${field.name}[${index}]`
  159. })
  160. }
  161. return registers
  162. }
  163. function parseStructFields(body, aliases, enumTypes = {}) {
  164. const registers = []
  165. const layoutState = {
  166. bitOffset: 0
  167. }
  168. const declarations = splitDeclarations(body)
  169. declarations.forEach((statement) => {
  170. if (!statement || statement.indexOf('(') >= 0) return
  171. const parts = splitDeclarators(statement)
  172. if (!parts.length) return
  173. const first = parseFirstDeclarator(parts[0])
  174. if (!first) return
  175. const dataType = resolveType(first.typeText, aliases)
  176. if (!dataType) return
  177. const enumInfo = resolveEnumInfo(first.typeText, enumTypes)
  178. const declarators = [first.declarator].concat(parts.slice(1))
  179. declarators.forEach((declaratorText) => {
  180. const field = parseDeclarator(declaratorText)
  181. if (!field) return
  182. registers.push(...createRegisterFromField(field, dataType, first.typeText, layoutState, enumInfo))
  183. })
  184. })
  185. return registers.map((register) => ({
  186. ...register,
  187. structByteLength: Math.ceil(layoutState.bitOffset / 8)
  188. }))
  189. }
  190. module.exports = {
  191. parseStructFields
  192. }