49 lines
2.7 KiB
Python
49 lines
2.7 KiB
Python
"""Models for the C99 Intel HEX parser. No Python parser fallback."""
|
||
import ctypes as ct
|
||
from functools import lru_cache
|
||
from protocan.native import _load_library
|
||
|
||
class Cell(ct.Structure):
|
||
_fields_ = [('address', ct.c_uint32), ('value', ct.c_uint8)]
|
||
|
||
@lru_cache(maxsize=1)
|
||
def library():
|
||
lib = _load_library()
|
||
try:
|
||
lib.firmware_hex_parse.argtypes = [ct.c_char_p, ct.c_size_t, ct.POINTER(Cell),
|
||
ct.c_size_t, ct.c_size_t, ct.POINTER(ct.c_size_t), ct.POINTER(ct.c_size_t)]
|
||
lib.firmware_hex_parse.restype = ct.c_int
|
||
lib.firmware_hex_segment_size.argtypes = [ct.POINTER(Cell), ct.c_size_t, ct.c_size_t]
|
||
lib.firmware_hex_segment_size.restype = ct.c_size_t
|
||
except AttributeError as exc:
|
||
raise RuntimeError('Rebuild SETProtocol: native firmware image API is missing') from exc
|
||
return lib
|
||
|
||
def parse_segments(text, max_records, error_type, segment_type):
|
||
try:
|
||
raw = bytes(text) if isinstance(text, (bytes, bytearray)) else str(text or '').encode('ascii')
|
||
except UnicodeError as exc:
|
||
raise error_type('Intel HEX должен состоять из ASCII') from exc
|
||
if max_records < 1 or len(raw) > 64 * 1024 * 1024:
|
||
raise error_type('Файл Intel HEX превышает допустимый размер')
|
||
cells = (Cell * max(1, len(raw) // 2))()
|
||
count, line = ct.c_size_t(), ct.c_size_t()
|
||
lib = library()
|
||
status = lib.firmware_hex_parse(raw, len(raw), cells, len(cells), max_records,
|
||
ct.byref(count), ct.byref(line))
|
||
if status:
|
||
messages = {1: 'неверный формат', 2: 'неверная checksum записи',
|
||
3: 'неизвестный тип или неверная длина записи', 4: 'нет данных/EOF или данные после записи EOF',
|
||
5: 'адрес описан повторно', 6: 'адрес выходит за uint32',
|
||
7: 'превышено допустимое число записей',
|
||
8: "запись должна начинаться с ':'", 9: 'запись содержит не пары HEX-символов',
|
||
10: 'длина записи не совпадает с полем count'}
|
||
raise error_type('Intel HEX, строка %d: %s' % (line.value, messages.get(status, 'ошибка')))
|
||
result, offset = [], 0
|
||
while offset < count.value:
|
||
size = lib.firmware_hex_segment_size(cells, count.value, offset)
|
||
result.append(segment_type(cells[offset].address,
|
||
bytes(cells[i].value for i in range(offset, offset + size))))
|
||
offset += size
|
||
return result
|