aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/misc/sstruct.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/misc/sstruct.py')
-rw-r--r--Lib/fontTools/misc/sstruct.py228
1 files changed, 116 insertions, 112 deletions
diff --git a/Lib/fontTools/misc/sstruct.py b/Lib/fontTools/misc/sstruct.py
index 6db8b515..d35bc9a5 100644
--- a/Lib/fontTools/misc/sstruct.py
+++ b/Lib/fontTools/misc/sstruct.py
@@ -56,68 +56,72 @@ __copyright__ = "Copyright 1998, Just van Rossum <just@letterror.com>"
class Error(Exception):
- pass
+ pass
+
def pack(fmt, obj):
- formatstring, names, fixes = getformat(fmt, keep_pad_byte=True)
- elements = []
- if not isinstance(obj, dict):
- obj = obj.__dict__
- for name in names:
- value = obj[name]
- if name in fixes:
- # fixed point conversion
- value = fl2fi(value, fixes[name])
- elif isinstance(value, str):
- value = tobytes(value)
- elements.append(value)
- data = struct.pack(*(formatstring,) + tuple(elements))
- return data
+ formatstring, names, fixes = getformat(fmt, keep_pad_byte=True)
+ elements = []
+ if not isinstance(obj, dict):
+ obj = obj.__dict__
+ for name in names:
+ value = obj[name]
+ if name in fixes:
+ # fixed point conversion
+ value = fl2fi(value, fixes[name])
+ elif isinstance(value, str):
+ value = tobytes(value)
+ elements.append(value)
+ data = struct.pack(*(formatstring,) + tuple(elements))
+ return data
+
def unpack(fmt, data, obj=None):
- if obj is None:
- obj = {}
- data = tobytes(data)
- formatstring, names, fixes = getformat(fmt)
- if isinstance(obj, dict):
- d = obj
- else:
- d = obj.__dict__
- elements = struct.unpack(formatstring, data)
- for i in range(len(names)):
- name = names[i]
- value = elements[i]
- if name in fixes:
- # fixed point conversion
- value = fi2fl(value, fixes[name])
- elif isinstance(value, bytes):
- try:
- value = tostr(value)
- except UnicodeDecodeError:
- pass
- d[name] = value
- return obj
+ if obj is None:
+ obj = {}
+ data = tobytes(data)
+ formatstring, names, fixes = getformat(fmt)
+ if isinstance(obj, dict):
+ d = obj
+ else:
+ d = obj.__dict__
+ elements = struct.unpack(formatstring, data)
+ for i in range(len(names)):
+ name = names[i]
+ value = elements[i]
+ if name in fixes:
+ # fixed point conversion
+ value = fi2fl(value, fixes[name])
+ elif isinstance(value, bytes):
+ try:
+ value = tostr(value)
+ except UnicodeDecodeError:
+ pass
+ d[name] = value
+ return obj
+
def unpack2(fmt, data, obj=None):
- length = calcsize(fmt)
- return unpack(fmt, data[:length], obj), data[length:]
+ length = calcsize(fmt)
+ return unpack(fmt, data[:length], obj), data[length:]
+
def calcsize(fmt):
- formatstring, names, fixes = getformat(fmt)
- return struct.calcsize(formatstring)
+ formatstring, names, fixes = getformat(fmt)
+ return struct.calcsize(formatstring)
# matches "name:formatchar" (whitespace is allowed)
_elementRE = re.compile(
- r"\s*" # whitespace
- r"([A-Za-z_][A-Za-z_0-9]*)" # name (python identifier)
- r"\s*:\s*" # whitespace : whitespace
- r"([xcbB?hHiIlLqQfd]|" # formatchar...
- r"[0-9]+[ps]|" # ...formatchar...
- r"([0-9]+)\.([0-9]+)(F))" # ...formatchar
- r"\s*" # whitespace
- r"(#.*)?$" # [comment] + end of string
- )
+ r"\s*" # whitespace
+ r"([A-Za-z_][A-Za-z_0-9]*)" # name (python identifier)
+ r"\s*:\s*" # whitespace : whitespace
+ r"([xcbB?hHiIlLqQfd]|" # formatchar...
+ r"[0-9]+[ps]|" # ...formatchar...
+ r"([0-9]+)\.([0-9]+)(F))" # ...formatchar
+ r"\s*" # whitespace
+ r"(#.*)?$" # [comment] + end of string
+)
# matches the special struct fmt chars and 'x' (pad byte)
_extraRE = re.compile(r"\s*([x@=<>!])\s*(#.*)?$")
@@ -125,54 +129,53 @@ _extraRE = re.compile(r"\s*([x@=<>!])\s*(#.*)?$")
# matches an "empty" string, possibly containing whitespace and/or a comment
_emptyRE = re.compile(r"\s*(#.*)?$")
-_fixedpointmappings = {
- 8: "b",
- 16: "h",
- 32: "l"}
+_fixedpointmappings = {8: "b", 16: "h", 32: "l"}
_formatcache = {}
+
def getformat(fmt, keep_pad_byte=False):
- fmt = tostr(fmt, encoding="ascii")
- try:
- formatstring, names, fixes = _formatcache[fmt]
- except KeyError:
- lines = re.split("[\n;]", fmt)
- formatstring = ""
- names = []
- fixes = {}
- for line in lines:
- if _emptyRE.match(line):
- continue
- m = _extraRE.match(line)
- if m:
- formatchar = m.group(1)
- if formatchar != 'x' and formatstring:
- raise Error("a special fmt char must be first")
- else:
- m = _elementRE.match(line)
- if not m:
- raise Error("syntax error in fmt: '%s'" % line)
- name = m.group(1)
- formatchar = m.group(2)
- if keep_pad_byte or formatchar != "x":
- names.append(name)
- if m.group(3):
- # fixed point
- before = int(m.group(3))
- after = int(m.group(4))
- bits = before + after
- if bits not in [8, 16, 32]:
- raise Error("fixed point must be 8, 16 or 32 bits long")
- formatchar = _fixedpointmappings[bits]
- assert m.group(5) == "F"
- fixes[name] = after
- formatstring = formatstring + formatchar
- _formatcache[fmt] = formatstring, names, fixes
- return formatstring, names, fixes
+ fmt = tostr(fmt, encoding="ascii")
+ try:
+ formatstring, names, fixes = _formatcache[fmt]
+ except KeyError:
+ lines = re.split("[\n;]", fmt)
+ formatstring = ""
+ names = []
+ fixes = {}
+ for line in lines:
+ if _emptyRE.match(line):
+ continue
+ m = _extraRE.match(line)
+ if m:
+ formatchar = m.group(1)
+ if formatchar != "x" and formatstring:
+ raise Error("a special fmt char must be first")
+ else:
+ m = _elementRE.match(line)
+ if not m:
+ raise Error("syntax error in fmt: '%s'" % line)
+ name = m.group(1)
+ formatchar = m.group(2)
+ if keep_pad_byte or formatchar != "x":
+ names.append(name)
+ if m.group(3):
+ # fixed point
+ before = int(m.group(3))
+ after = int(m.group(4))
+ bits = before + after
+ if bits not in [8, 16, 32]:
+ raise Error("fixed point must be 8, 16 or 32 bits long")
+ formatchar = _fixedpointmappings[bits]
+ assert m.group(5) == "F"
+ fixes[name] = after
+ formatstring = formatstring + formatchar
+ _formatcache[fmt] = formatstring, names, fixes
+ return formatstring, names, fixes
+
def _test():
- fmt = """
+ fmt = """
# comments are allowed
> # big endian (see documentation for struct)
# empty lines are allowed:
@@ -188,29 +191,30 @@ def _test():
apad: x
"""
- print('size:', calcsize(fmt))
+ print("size:", calcsize(fmt))
+
+ class foo(object):
+ pass
- class foo(object):
- pass
+ i = foo()
- i = foo()
+ i.ashort = 0x7FFF
+ i.along = 0x7FFFFFFF
+ i.abyte = 0x7F
+ i.achar = "a"
+ i.astr = "12345"
+ i.afloat = 0.5
+ i.adouble = 0.5
+ i.afixed = 1.5
+ i.abool = True
- i.ashort = 0x7fff
- i.along = 0x7fffffff
- i.abyte = 0x7f
- i.achar = "a"
- i.astr = "12345"
- i.afloat = 0.5
- i.adouble = 0.5
- i.afixed = 1.5
- i.abool = True
+ data = pack(fmt, i)
+ print("data:", repr(data))
+ print(unpack(fmt, data))
+ i2 = foo()
+ unpack(fmt, data, i2)
+ print(vars(i2))
- data = pack(fmt, i)
- print('data:', repr(data))
- print(unpack(fmt, data))
- i2 = foo()
- unpack(fmt, data, i2)
- print(vars(i2))
if __name__ == "__main__":
- _test()
+ _test()