gohex.go raw
1 package gohex
2
3 import (
4 "bufio"
5 "encoding/hex"
6 "io"
7 "sort"
8 )
9
10 // Constants definitions of IntelHex record types
11 const (
12 _DATA_RECORD byte = 0 // Record with data bytes
13 _EOF_RECORD byte = 1 // Record with end of file indicator
14 _ADR_20_RECORD byte = 2 // Record with extended 20-bit linear address
15 _ADR_32_RECORD byte = 4 // Record with extended 32-bit linear address
16 _START_RECORD byte = 5 // Record with start linear address
17 )
18
19 // Structure with binary data segment fields
20 type DataSegment struct {
21 Address uint32 // Starting address of data segment
22 Data []byte // Data segment bytes
23 }
24
25 // Helper type for data segments sorting operations
26 type sortByAddress []*DataSegment
27
28 func (segs sortByAddress) Len() int { return len(segs) }
29 func (segs sortByAddress) Swap(i, j int) { segs[i], segs[j] = segs[j], segs[i] }
30 func (segs sortByAddress) Less(i, j int) bool { return segs[i].Address < segs[j].Address }
31
32 // Main structure with private fields of IntelHex parser
33 type Memory struct {
34 dataSegments []*DataSegment // Slice with pointers to DataSegments
35 startAddress uint32 // Start linear address
36 extendedAddress uint32 // Extended linear address
37 eofFlag bool // End of file record exist flag
38 startFlag bool // Start address record exist flag
39 lineNum uint // Parser input line number
40 firstAddressFlag bool // Dump first address line
41 }
42
43 // Constructor of Memory structure
44 func NewMemory() *Memory {
45 m := new(Memory)
46 m.Clear()
47 return m
48 }
49
50 // Method to getting start address from IntelHex data
51 func (m *Memory) GetStartAddress() (adr uint32, ok bool) {
52 if m.startFlag {
53 return m.startAddress, true
54 }
55 return 0, false
56 }
57
58 // Method to setting start address to IntelHex data
59 func (m *Memory) SetStartAddress(adr uint32) {
60 m.startAddress = adr
61 m.startFlag = true
62 }
63
64 // Method to getting data segments address from IntelHex data
65 func (m *Memory) GetDataSegments() []DataSegment {
66 segs := []DataSegment{}
67 for _, s := range m.dataSegments {
68 segs = append(segs, *s)
69 }
70 return segs
71 }
72
73 // Method to clear memory structure
74 func (m *Memory) Clear() {
75 m.startAddress = 0
76 m.extendedAddress = 0
77 m.lineNum = 0
78 m.dataSegments = []*DataSegment{}
79 m.startFlag = false
80 m.eofFlag = false
81 m.firstAddressFlag = false
82 }
83
84 func (seg *DataSegment) isOverlap(adr uint32, size uint32) bool {
85 if ((adr >= seg.Address) && (adr < seg.Address+uint32(len(seg.Data)))) ||
86 ((adr < seg.Address) && (adr+size) > seg.Address) {
87 return true
88 }
89 return false
90 }
91
92 func (m *Memory) removeSegment(index int) {
93 size := len(m.dataSegments)
94
95 if size == 0 {
96 return
97 } else if size == 1 {
98 m.dataSegments = []*DataSegment{}
99 } else {
100 if index == 0 {
101 m.dataSegments = m.dataSegments[1:]
102 } else if index == size-1 {
103 m.dataSegments = m.dataSegments[:index]
104 } else {
105 m.dataSegments = append(m.dataSegments[:index], m.dataSegments[index+1:]...)
106 }
107 }
108 }
109
110 func (m *Memory) findDataSegment(adr uint32) (seg *DataSegment, offset uint32, index int) {
111 for i, s := range m.dataSegments {
112 if s.isOverlap(adr, 1) == true {
113 return s, adr - s.Address, i
114 }
115 }
116 return nil, 0, 0
117 }
118
119 // Method to add binary data to memory (auto segmented and sorted)
120 func (m *Memory) AddBinary(adr uint32, bytes []byte) error {
121 var segBefore *DataSegment = nil
122 var segAfter *DataSegment = nil
123 var segAfterIndex int
124 for i, s := range m.dataSegments {
125 if s.isOverlap(adr, uint32(len(bytes))) == true {
126 return newParseError(_DATA_ERROR, "data segments overlap", m.lineNum)
127 }
128
129 if adr == s.Address+uint32(len(s.Data)) {
130 segBefore = s
131 }
132 if adr+uint32(len(bytes)) == s.Address {
133 segAfter, segAfterIndex = s, i
134 }
135 }
136
137 if segBefore != nil && segAfter != nil {
138 segBefore.Data = append(segBefore.Data, bytes...)
139 segBefore.Data = append(segBefore.Data, segAfter.Data...)
140 m.dataSegments = append(m.dataSegments[:segAfterIndex], m.dataSegments[segAfterIndex+1:]...)
141
142 } else if segBefore != nil && segAfter == nil {
143 segBefore.Data = append(segBefore.Data, bytes...)
144 } else if segBefore == nil && segAfter != nil {
145 segAfter.Address = adr
146 segAfter.Data = append(bytes, segAfter.Data...)
147 } else {
148 data := make([]byte, len(bytes))
149 copy(data, bytes)
150 m.dataSegments = append(m.dataSegments, &DataSegment{Address: adr, Data: data})
151 }
152 sort.Sort(sortByAddress(m.dataSegments))
153 return nil
154 }
155
156 // Method to set binary data to memory (data overlapped will change, auto segmented and sorted)
157 func (m *Memory) SetBinary(adr uint32, bytes []byte) {
158 for a, b := range bytes {
159 currentAdr := adr + uint32(a)
160 seg, offset, _ := m.findDataSegment(currentAdr)
161
162 if seg != nil {
163 seg.Data[offset] = b
164 } else {
165 m.AddBinary(currentAdr, []byte{b})
166 }
167 }
168 }
169
170 // Method to remove binary data from memory (auto segmented and sorted)
171 func (m *Memory) RemoveBinary(adr uint32, size uint32) {
172 adrEnd := adr + size
173 for currentAdr := adr; currentAdr < adrEnd; currentAdr++ {
174 seg, offset, index := m.findDataSegment(currentAdr)
175
176 if seg == nil {
177 continue
178 }
179
180 if offset == 0 {
181 seg.Address += 1
182 if len(seg.Data) > 1 {
183 seg.Data = seg.Data[1:]
184 } else {
185 m.removeSegment(index)
186 }
187 } else if offset == uint32(len(seg.Data)-1) {
188 if len(seg.Data) > 1 {
189 seg.Data = seg.Data[:offset]
190 } else {
191 m.removeSegment(index)
192 }
193 } else {
194 newSeg := DataSegment{Address: seg.Address + offset + 1, Data: seg.Data[offset+1:]}
195 seg.Data = seg.Data[:offset]
196 m.dataSegments = append(m.dataSegments, &newSeg)
197 }
198 }
199 sort.Sort(sortByAddress(m.dataSegments))
200 }
201
202 func (m *Memory) parseIntelHexRecord(bytes []byte) error {
203 if len(bytes) < 5 {
204 return newParseError(_DATA_ERROR, "not enought data bytes", m.lineNum)
205 }
206 err := checkSum(bytes)
207 if err != nil {
208 return newParseError(_CHECKSUM_ERROR, err.Error(), m.lineNum)
209 }
210 err = checkRecordSize(bytes)
211 if err != nil {
212 return newParseError(_DATA_ERROR, err.Error(), m.lineNum)
213 }
214 switch record_type := bytes[3]; record_type {
215 case _DATA_RECORD:
216 a, data := getDataLine(bytes)
217 adr := uint32(a) + m.extendedAddress
218 err = m.AddBinary(adr, data)
219 if err != nil {
220 return err
221 }
222 case _EOF_RECORD:
223 err = checkEOF(bytes)
224 if err != nil {
225 return newParseError(_RECORD_ERROR, err.Error(), m.lineNum)
226 }
227 m.eofFlag = true
228 case _ADR_20_RECORD:
229 fallthrough
230 case _ADR_32_RECORD:
231 m.extendedAddress, err = getExtendedAddress(bytes)
232 if err != nil {
233 return newParseError(_RECORD_ERROR, err.Error(), m.lineNum)
234 }
235 case _START_RECORD:
236 if m.startFlag == true {
237 return newParseError(_DATA_ERROR, "multiple start address lines", m.lineNum)
238 }
239 m.startAddress, err = getStartAddress(bytes)
240 if err != nil {
241 return newParseError(_RECORD_ERROR, err.Error(), m.lineNum)
242 }
243 m.startFlag = true
244 }
245 return nil
246 }
247
248 func (m *Memory) parseIntelHexLine(line string) error {
249 if len(line) == 0 {
250 return nil
251 }
252 if line[0] != ':' {
253 return newParseError(_SYNTAX_ERROR, "no colon char on the first line character", m.lineNum)
254 }
255 bytes, err := hex.DecodeString(line[1:])
256 if err != nil {
257 return newParseError(_SYNTAX_ERROR, err.Error(), m.lineNum)
258 }
259 return m.parseIntelHexRecord(bytes)
260 }
261
262 // Method to parsing IntelHex data and add into memory
263 func (m *Memory) ParseIntelHex(reader io.Reader) error {
264 scanner := bufio.NewScanner(reader)
265 m.Clear()
266 for scanner.Scan() {
267 m.lineNum++
268 line := scanner.Text()
269 err := m.parseIntelHexLine(line)
270 if err != nil {
271 return err
272 }
273 }
274 if err := scanner.Err(); err != nil {
275 return newParseError(_SYNTAX_ERROR, err.Error(), m.lineNum)
276 }
277 if m.eofFlag == false {
278 return newParseError(_DATA_ERROR, "no end of file line", m.lineNum)
279 }
280
281 return nil
282 }
283
284 func (m *Memory) dumpDataSegment(writer io.Writer, s *DataSegment, lineLength byte) error {
285 lineAdr := s.Address
286 lineData := []byte{}
287 for byteAdr := s.Address; byteAdr < s.Address+uint32(len(s.Data)); byteAdr++ {
288 if ((byteAdr & 0xFFFF0000) != m.extendedAddress) || (m.firstAddressFlag == false) {
289 m.firstAddressFlag = true
290 if len(lineData) != 0 {
291 err := writeDataLine(writer, &lineAdr, byteAdr, &lineData)
292 if err != nil {
293 return err
294 }
295 }
296 m.extendedAddress = (byteAdr & 0xFFFF0000)
297 writeExtendedAddressLine(writer, m.extendedAddress)
298 }
299 if len(lineData) >= int(lineLength) {
300 err := writeDataLine(writer, &lineAdr, byteAdr, &lineData)
301 if err != nil {
302 return err
303 }
304 }
305 lineData = append(lineData, s.Data[byteAdr-s.Address])
306 }
307
308 if len(lineData) != 0 {
309 return writeDataLine(writer, &lineAdr, 0, &lineData)
310 }
311 return nil
312 }
313
314 // Method to dumping IntelHex data previously loaded into memory
315 func (m *Memory) DumpIntelHex(writer io.Writer, lineLength byte) error {
316 if m.startFlag {
317 err := writeStartAddressLine(writer, m.startAddress)
318 if err != nil {
319 return err
320 }
321 }
322
323 m.firstAddressFlag = false
324 m.extendedAddress = 0
325 for _, s := range m.dataSegments {
326 err := m.dumpDataSegment(writer, s, lineLength)
327 if err != nil {
328 return err
329 }
330 }
331
332 return writeEofLine(writer)
333 }
334
335 // Method to load binary data previously loaded into memory
336 func (m *Memory) ToBinary(address uint32, size uint32, padding byte) []byte {
337 data := make([]byte, size)
338
339 i := uint32(0)
340 for i < size {
341 ok := false
342 for _, s := range m.dataSegments {
343 if (address >= s.Address) && (address < s.Address+uint32(len(s.Data))) {
344 data[i] = s.Data[address-s.Address]
345 i++
346 address++
347 ok = true
348 break
349 }
350 }
351 if ok == false {
352 data[i] = padding
353 i++
354 address++
355 }
356 }
357
358 return data
359 }
360