Skip to content

Add option/parameters to strip padding from the end of binary data. #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 66 additions & 27 deletions intelhex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def _decode_record(self, s, line=0):
if not self._buf.get(addr, None) is None:
raise AddressOverlapError(address=addr, line=line)
self._buf[addr] = bin[i]
addr += 1 # FIXME: addr should be wrapped
addr += 1 # FIXME: addr should be wrapped
# BUT after 02 record (at 64K boundary)
# and after 04 record (at 4G boundary)

Expand Down Expand Up @@ -286,10 +286,10 @@ def frombytes(self, bytes, offset=0):
self._buf[offset] = b
offset += 1

def _get_start_end(self, start=None, end=None, size=None):
def _get_start_end(self, start=None, end=None, size=None, filter=False):
"""Return default values for start and end if they are None.
If this IntelHex object is empty then it's error to
invoke this method with both start and end as None.
invoke this method with both start and end as None.
"""
if (start,end) == (None,None) and self._buf == {}:
raise EmptyIntelHexError
Expand All @@ -311,22 +311,25 @@ def _get_start_end(self, start=None, end=None, size=None):
start = self.minaddr()
if end is None:
end = self.maxaddr()
elif filter:
end = self.maxaddr_in(start, end)
if start > end:
start, end = end, start
return start, end

def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None):
''' Convert this object to binary form as array. If start and end
def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None, filter=False):
''' Convert this object to binary form as array. If start and end
unspecified, they will be inferred from the data.
@param start start address of output bytes.
@param end end address of output bytes (inclusive).
@param pad [DEPRECATED PARAMETER, please use self.padding instead]
fill empty spaces with this value
(if pad is None then this method uses self.padding).
@param size size of the block, used with start or end parameter.
@param filter whether to filter the input range to avoid padding
@return array of unsigned char data.
'''
if not isinstance(pad, _DeprecatedParam):
if not (isinstance(pad, _DeprecatedParam) or pad is None):
print ("IntelHex.tobinarray: 'pad' parameter is deprecated.")
if pad is not None:
print ("Please, use IntelHex.padding attribute instead.")
Expand All @@ -335,9 +338,9 @@ def tobinarray(self, start=None, end=None, pad=_DEPRECATED, size=None):
print ("Use syntax like this: ih.tobinarray(start=xxx, end=yyy, size=zzz)")
else:
pad = None
return self._tobinarray_really(start, end, pad, size)
return self._tobinarray_really(start, end, pad, size, filter)

def _tobinarray_really(self, start, end, pad, size):
def _tobinarray_really(self, start, end, pad, size, filter):
"""Return binary array."""
if pad is None:
pad = self.padding
Expand All @@ -346,22 +349,23 @@ def _tobinarray_really(self, start, end, pad, size):
return bin
if size is not None and size <= 0:
raise ValueError("tobinarray: wrong value for size")
start, end = self._get_start_end(start, end, size)
start, end = self._get_start_end(start, end, size, filter)
for i in range_g(start, end+1):
bin.append(self._buf.get(i, pad))
return bin

def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None):
def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None, filter=False):
''' Convert to binary form and return as binary string.
@param start start address of output bytes.
@param end end address of output bytes (inclusive).
@param pad [DEPRECATED PARAMETER, please use self.padding instead]
fill empty spaces with this value
(if pad is None then this method uses self.padding).
@param size size of the block, used with start or end parameter.
@param filter whether to filter the input range to avoid padding
@return bytes string of binary data.
'''
if not isinstance(pad, _DeprecatedParam):
if not (isinstance(pad, _DeprecatedParam) or pad is None):
print ("IntelHex.tobinstr: 'pad' parameter is deprecated.")
if pad is not None:
print ("Please, use IntelHex.padding attribute instead.")
Expand All @@ -370,12 +374,12 @@ def tobinstr(self, start=None, end=None, pad=_DEPRECATED, size=None):
print ("Use syntax like this: ih.tobinstr(start=xxx, end=yyy, size=zzz)")
else:
pad = None
return self._tobinstr_really(start, end, pad, size)
return self._tobinstr_really(start, end, pad, size, filter)

def _tobinstr_really(self, start, end, pad, size):
return array_tobytes(self._tobinarray_really(start, end, pad, size))
def _tobinstr_really(self, start, end, pad, size, filter):
return array_tobytes(self._tobinarray_really(start, end, pad, size, filter))

def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None):
def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None, filter=False):
'''Convert to binary and write to file.

@param fobj file name or file object for writing output bytes.
Expand All @@ -385,8 +389,9 @@ def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None):
fill empty spaces with this value
(if pad is None then this method uses self.padding).
@param size size of the block, used with start or end parameter.
@param filter whether to filter the input range to avoid padding
'''
if not isinstance(pad, _DeprecatedParam):
if not (isinstance(pad, _DeprecatedParam) or pad is None):
print ("IntelHex.tobinfile: 'pad' parameter is deprecated.")
if pad is not None:
print ("Please, use IntelHex.padding attribute instead.")
Expand All @@ -401,7 +406,7 @@ def tobinfile(self, fobj, start=None, end=None, pad=_DEPRECATED, size=None):
else:
close_fd = False

fobj.write(self._tobinstr_really(start, end, pad, size))
fobj.write(self._tobinstr_really(start, end, pad, size, filter))

if close_fd:
fobj.close()
Expand All @@ -419,12 +424,21 @@ def todict(self):

def addresses(self):
'''Returns all used addresses in sorted order.
@return list of occupied data addresses in sorted order.
@return list of occupied data addresses in sorted order.
'''
aa = dict_keys(self._buf)
aa.sort()
return aa

def addresses_in(self, start, end):
'''Returns all used addresses in a given range in sorted order.
@param start the start address of the range
@param end the end address of the range
@return list of occupied data addresses in sorted order.
'''
# TODO: end is inclusive, correct?
return [a for a in self.addresses() if a >= start and a <= end]

def minaddr(self):
'''Get minimal address of HEX content.
@return minimal address or None if no data
Expand All @@ -435,6 +449,18 @@ def minaddr(self):
else:
return min(aa)

def minaddr_in(self, start, end):
'''Get minimal address of HEX content within the specified range.
@param start the start address of the range
@param end the end address of the range
@return minimal address or None if no data
'''
aa = self.addresses_in(start, end)
if aa == []:
return None
else:
return min(aa)

def maxaddr(self):
'''Get maximal address of HEX content.
@return maximal address or None if no data
Expand All @@ -445,6 +471,18 @@ def maxaddr(self):
else:
return max(aa)

def maxaddr_in(self, start, end):
'''Get maximal address of HEX content within the specified range.
@param start the start address of the range
@param end the end address of the range
@return maximal address or None if no data
'''
aa = self.addresses_in(start, end)
if aa == []:
return None
else:
return max(aa)

def __getitem__(self, addr):
''' Get requested byte from address.
@param addr address of byte.
Expand Down Expand Up @@ -749,7 +787,7 @@ def puts(self, addr, s):
self._buf[addr+i] = a[i]

def getsz(self, addr):
"""Get zero-terminated bytes string from given address. Will raise
"""Get zero-terminated bytes string from given address. Will raise
NotEnoughDataError exception if a hole is encountered before a 0.
"""
i = 0
Expand All @@ -771,7 +809,7 @@ def putsz(self, addr, s):
def find(self, sub, start=None, end=None):
"""Return the lowest index in self[start:end] where subsection sub is found.
Optional arguments start and end are interpreted as in slice notation.

@param sub bytes-like subsection to find
@param start start of section to search within (optional)
@param end end of section to search within (optional)
Expand Down Expand Up @@ -801,7 +839,7 @@ def dump(self, tofile=None, width=16, withpadding=False):
width = int(width)
if tofile is None:
tofile = sys.stdout

# start addr possibly
if self.start_addr is not None:
cs = self.start_addr.get('CS')
Expand Down Expand Up @@ -856,7 +894,7 @@ def merge(self, other, overlap='error'):
in overlapping region.

@raise TypeError if other is not instance of IntelHex
@raise ValueError if other is the same object as self
@raise ValueError if other is the same object as self
(it can't merge itself)
@raise ValueError if overlap argument has incorrect value
@raise AddressOverlapError on overlapped data
Expand Down Expand Up @@ -911,7 +949,7 @@ def segments(self, min_gap=1):
beginnings = [addresses[b+1] for b in breaks]
beginnings.insert(0, addresses[0])
return [(a, b+1) for (a, b) in zip(beginnings, endings)]

def get_memory_size(self):
"""Returns the approximate memory footprint for data."""
n = sys.getsizeof(self)
Expand Down Expand Up @@ -999,7 +1037,7 @@ def minaddr(self):
def maxaddr(self):
'''Get maximal address of HEX content in 16-bit mode.

@return maximal address used in this object
@return maximal address used in this object
'''
aa = dict_keys(self._buf)
if aa == []:
Expand Down Expand Up @@ -1035,7 +1073,7 @@ def tobinarray(self, start=None, end=None, size=None):
#/class IntelHex16bit


def hex2bin(fin, fout, start=None, end=None, size=None, pad=None):
def hex2bin(fin, fout, start=None, end=None, size=None, pad=None, filter=False):
"""Hex-to-Bin convertor engine.
@return 0 if all OK

Expand All @@ -1045,6 +1083,7 @@ def hex2bin(fin, fout, start=None, end=None, size=None, pad=None):
@param end end of address range (inclusive; optional)
@param size size of resulting file (in bytes) (optional)
@param pad padding byte (optional)
@param filter whether to filter the input range to avoid padding (optional)
"""
try:
h = IntelHex(fin)
Expand All @@ -1070,7 +1109,7 @@ def hex2bin(fin, fout, start=None, end=None, size=None, pad=None):
if pad is not None:
# using .padding attribute rather than pad argument to function call
h.padding = pad
h.tobinfile(fout, start, end)
h.tobinfile(fout, start, end, filter=filter)
except IOError:
e = sys.exc_info()[1] # current exception
txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e))
Expand Down Expand Up @@ -1173,7 +1212,7 @@ def data(offset, bytes):

def eof():
"""Return End of File record as a string.
@return String representation of Intel Hex EOF record
@return String representation of Intel Hex EOF record
"""
return ':00000001FF'
eof = staticmethod(eof)
Expand Down
31 changes: 23 additions & 8 deletions intelhex/scripts/hex2bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@

VERSION = '2.3.0'

def split_range(a):
l = a.split(":")
if l[0] != '':
start = int(l[0], 16)
if l[1] != '':
end = int(l[1], 16)
return start, end


def main():
import getopt
import os
Expand All @@ -58,6 +67,9 @@ def main():
-r, --range=START:END specify address range for writing output
(ascii hex value).
Range can be in form 'START:' or ':END'.
-f, --filter=START:END specify address range for filtering input
(ascii hex value).
Filter range can be in form 'START:' or ':END'.
-l, --length=NNNN,
-s, --size=NNNN size of output (decimal value).
'''
Expand All @@ -66,11 +78,12 @@ def main():
start = None
end = None
size = None
filter = False

try:
opts, args = getopt.getopt(sys.argv[1:], "hvp:r:l:s:",
opts, args = getopt.getopt(sys.argv[1:], "hvp:r:f:l:s:",
["help", "version", "pad=", "range=",
"length=", "size="])
"filter", "length=", "size="])

for o, a in opts:
if o in ("-h", "--help"):
Expand All @@ -86,13 +99,15 @@ def main():
raise getopt.GetoptError('Bad pad value')
elif o in ("-r", "--range"):
try:
l = a.split(":")
if l[0] != '':
start = int(l[0], 16)
if l[1] != '':
end = int(l[1], 16)
start, end = split_range(a)
except:
raise getopt.GetoptError('Bad range value(s)')
elif o in ("-f", "--filter"):
filter = True
try:
start, end = split_range(a)
except:
raise getopt.GetoptError('Bad filter range value(s)')
elif o in ("-l", "--lenght", "-s", "--size"):
try:
size = int(a, 10)
Expand Down Expand Up @@ -129,7 +144,7 @@ def main():
fout = compat.get_binary_stdout()

from intelhex import hex2bin
sys.exit(hex2bin(fin, fout, start, end, size, pad))
sys.exit(hex2bin(fin, fout, start, end, size, pad, filter))

if __name__ == '__main__':
main()