Skip to content

Commit

Permalink
Padding being moved to a class, adapter removed.
Browse files Browse the repository at this point in the history
  • Loading branch information
arekbulski committed Aug 30, 2016
1 parent bb4a13f commit 6bdd8fa
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 87 deletions.
8 changes: 4 additions & 4 deletions construct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@
Container, FieldError, FormatField, LazyBound, LazyContainer, ListContainer, MetaArray, MetaField, OnDemand,
OverwriteError, Packer, Pass, Peek, Pointer, Range, RangeError, Reconfig, RepeatUntil, Restream, Select,
SelectError, Sequence, SizeofError, StaticField, Struct, Subconstruct, Switch, SwitchError, Terminator,
TerminatorError, ULInt24, Union, Computed)
TerminatorError, ULInt24, Union, Computed, Padding, PaddingError)
from construct.adapters import (BitIntegerAdapter, BitIntegerError, CStringAdapter, ConstAdapter, ConstError,
ExprAdapter, FlagsAdapter, FlagsContainer, HexDumpAdapter, HexString, IndexingAdapter, LengthValueAdapter,
MappingAdapter, MappingError, NoneOf, OneOf, PaddedStringAdapter, PaddingAdapter, PaddingError, SlicingAdapter,
MappingAdapter, MappingError, NoneOf, OneOf, PaddedStringAdapter, SlicingAdapter,
StringAdapter, TunnelAdapter, ValidationError, Validator)
from construct.macros import (Alias, Aligned, AlignedStruct, Array, BFloat32, BFloat64, Bit, BitField,
BitStreamReader, BitStreamWriter, BitStruct, Bitwise, CString, Embedded, EmbeddedBitStruct, Enum, Field,
Flag, FlagsEnum, GreedyRange, If, IfThenElse, LFloat32, LFloat64, Magic, NFloat32, NFloat64, Nibble, Octet,
OnDemandPointer, OpenRange, Optional, OptionalGreedyRange, Padding, PascalString, PrefixedArray,
OnDemandPointer, OpenRange, Optional, OptionalGreedyRange, PascalString, PrefixedArray,
Rename, SBInt16, SBInt32, SBInt64, SBInt8, SLInt16, SLInt32, SLInt64, SLInt8, SNInt16, SNInt32, SNInt64,
SNInt8, SeqOfOne, String, SymmetricMapping, UBInt16, UBInt32, UBInt64, UBInt8, ULInt16, ULInt32, ULInt64,
ULInt8, UNInt16, UNInt32, UNInt64, UNInt8, GreedyString)
Expand Down Expand Up @@ -67,7 +67,7 @@
'LFloat32', 'LFloat64', 'LazyBound', 'LazyContainer', 'LengthValueAdapter', 'ListContainer', 'Magic',
'MappingAdapter', 'MappingError', 'MetaArray', 'MetaField', 'NFloat32', 'NFloat64', 'Nibble', 'NoneOf',
'Octet', 'OnDemand', 'OnDemandPointer', 'OneOf', 'OpenRange', 'Optional', 'OptionalGreedyRange',
'OverwriteError', 'Packer', 'PaddedStringAdapter', 'Padding', 'PaddingAdapter', 'PaddingError',
'OverwriteError', 'Packer', 'PaddedStringAdapter', 'Padding', 'PaddingError',
'PascalString', 'Pass', 'Peek', 'Pointer', 'PrefixedArray', 'Probe', 'Range', 'RangeError', 'Reconfig',
'Rename', 'RepeatUntil', 'Restream', 'SBInt16', 'SBInt32', 'SBInt64', 'SBInt8', 'SLInt16', 'SLInt32',
'SLInt64', 'SLInt8', 'SNInt16', 'SNInt32', 'SNInt64', 'SNInt8', 'Select', 'SelectError', 'SeqOfOne',
Expand Down
25 changes: 0 additions & 25 deletions construct/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ class ConstError(AdaptationError):
pass
class ValidationError(AdaptationError):
pass
class PaddingError(AdaptationError):
pass


#===============================================================================
Expand Down Expand Up @@ -373,29 +371,6 @@ def _encode(self, obj, context):
def _decode(self, obj, context):
return obj[self.index]

class PaddingAdapter(Adapter):
r"""
Adapter for padding.
:param subcon: the subcon to pad
:param pattern: the padding pattern (character). default is "\x00"
:param strict: whether or not to verify, during parsing, that the given
padding matches the padding pattern. default is False (unstrict)
"""
__slots__ = ["pattern", "strict"]
def __init__(self, subcon, pattern = b"\x00", strict = False):
super(PaddingAdapter, self).__init__(subcon)
self.pattern = pattern
self.strict = strict
def _encode(self, obj, context):
return self._sizeof(context) * self.pattern
def _decode(self, obj, context):
if self.strict:
expected = self._sizeof(context) * self.pattern
if obj != expected:
raise PaddingError("expected %r, found %r" % (expected, obj))
return obj


#===============================================================================
# validators
Expand Down
37 changes: 37 additions & 0 deletions construct/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class TerminatorError(ConstructError):
pass
class OverwriteError(ValueError):
pass
class PaddingError(ConstructError):
pass

#===============================================================================
# abstract constructs
Expand Down Expand Up @@ -1418,3 +1420,38 @@ def _build(self, obj, stream, context):
ex = sys.exc_info()[1]
raise FieldError(ex)


class Padding(Construct):
r"""
A padding field (adds bytes when building, discards bytes when parsing).
:param length: the length of the field. the length can be either an integer,
or a function that takes the context as an argument and returns the length
:param pattern: the padding pattern (b-string character), default is b"\x00"
:param strict: whether to verify during parsing that the stream contains the pattern,
raises an exception if actual padding differs from the pattern.
default is False.
"""
__slots__ = ["length", "pattern", "strict"]
def __init__(self, length, pattern=b"\x00", strict=False):
if len(pattern) != 1:
raise PaddingError("expected a pattern of single byte, given %r" % pattern)
super(Padding, self).__init__(None)
self.length = length
self.pattern = pattern
self.strict = strict
def _parse(self, stream, context):
length = self.length(context) if callable(self.length) else self.length
read = _read_stream(stream, length)
if self.strict:
expected = length * self.pattern
if read != expected:
raise PaddingError("expected %r, found %r" % (expected, read))
return None
def _build(self, obj, stream, context):
length = self.length(context) if callable(self.length) else self.length
padding = length * self.pattern
_write_stream(stream, length, padding)
def _sizeof(self, context):
length = self.length(context) if callable(self.length) else self.length
return length
22 changes: 4 additions & 18 deletions construct/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

from construct.lib.py3compat import int2byte
from construct.lib import BitStreamReader, BitStreamWriter, encode_bin, decode_bin
from construct.core import Struct, MetaField, StaticField, FormatField, OnDemand, Pointer, Switch, Computed, RepeatUntil, MetaArray, Sequence, Range, Select, Pass, SizeofError, Buffered, Restream, Reconfig
from construct.adapters import BitIntegerAdapter, PaddingAdapter, ConstAdapter, CStringAdapter, LengthValueAdapter, IndexingAdapter, PaddedStringAdapter, FlagsAdapter, StringAdapter, MappingAdapter
from construct.core import Struct, MetaField, StaticField, FormatField, OnDemand, Pointer, Switch, Computed, RepeatUntil, MetaArray, Sequence, Range, Select, Pass, SizeofError, Buffered, Restream, Reconfig, Padding
from construct.adapters import BitIntegerAdapter, ConstAdapter, CStringAdapter, LengthValueAdapter, IndexingAdapter, PaddedStringAdapter, FlagsAdapter, StringAdapter, MappingAdapter


#===============================================================================
Expand Down Expand Up @@ -67,22 +67,8 @@ def BitField(name, length, swapped=False, signed=False, bytesize=8):
bytesize=bytesize
)

def Padding(length, pattern=b"\x00", strict=False):
r"""A padding field (adds bytes when building, discards bytes when parsing)
:param length: the length of the field. the length can be either an integer,
or a function that takes the context as an argument and returns the length
:param pattern: the padding pattern (character) to use. default is "\x00"
:param strict: whether or not to raise an exception is the actual padding
pattern mismatches the desired pattern. default is False.
"""
return PaddingAdapter(Field(None, length),
pattern = pattern,
strict = strict,
)

def Flag(name, truth=1, falsehood=0, default=False):
"""
r"""
A flag.
Flags are usually used to signify a Boolean value, and this construct
Expand Down Expand Up @@ -375,7 +361,7 @@ def padlength(ctx):
# ??????
# ??????
# ??????
Padding(padlength, pattern = pattern),
Padding(padlength, pattern),
nested = False,
)

Expand Down
56 changes: 16 additions & 40 deletions tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class ZlibCodec(object):

[Computed("computed", lambda ctx: "moo").parse, b"", "moo", None],
[Computed("computed", lambda ctx: "moo").build, None, b"", None],

[Struct("s",Computed("c", lambda ctx: None)).parse, b"", Container(c=None), None],
[Struct("s",Computed("c", lambda ctx: None)).build, Container(c=None), b"", None],
[Struct("s",Computed("c", lambda ctx: None)).build, Container(), b"", None],
Expand Down Expand Up @@ -106,12 +105,9 @@ class ZlibCodec(object):
b"\x07\x09", Container(a=7,b=LazyContainer(None, None, None, None),c=9), None],

[OnDemand(UBInt8("ondemand")).build, 8, b"\x08", None],
[Struct("ondemand", UBInt8("a"), OnDemand(UBInt8("b")), UBInt8("c")).build,
Container(a=7,b=8,c=9), b"\x07\x08\x09", None],
[Struct("ondemand", UBInt8("a"), OnDemand(UBInt8("b"), force_build = False), UBInt8("c")).build,
Container(a=7,b=LazyContainer(None, None, None, None),c=9), b"\x07\x00\x09", None],
[Struct("ondemand", UBInt8("a"), OnDemand(UBInt8("b"), force_build = False, advance_stream = False), UBInt8("c")).build,
Container(a=7,b=LazyContainer(None, None, None, None),c=9), b"\x07\x09", None],
[Struct("ondemand", UBInt8("a"), OnDemand(UBInt8("b")), UBInt8("c")).build, Container(a=7,b=8,c=9), b"\x07\x08\x09", None],
[Struct("ondemand", UBInt8("a"), OnDemand(UBInt8("b"), force_build = False), UBInt8("c")).build, Container(a=7,b=LazyContainer(None, None, None, None),c=9), b"\x07\x00\x09", None],
[Struct("ondemand", UBInt8("a"), OnDemand(UBInt8("b"), force_build = False, advance_stream = False), UBInt8("c")).build, Container(a=7,b=LazyContainer(None, None, None, None),c=9), b"\x07\x09", None],

[Struct("reconfig", Reconfig("foo", UBInt8("bar"))).parse, b"\x01", Container(foo=1), None],
[Struct("reconfig", Reconfig("foo", UBInt8("bar"))).build, Container(foo=1), b"\x01", None],
Expand Down Expand Up @@ -176,11 +172,13 @@ class ZlibCodec(object):
[SlicingAdapter(Array(3, UBInt8("indexingadapter")), 1, 3).parse, b"\x11\x22\x33", [0x22, 0x33], None],
[SlicingAdapter(Array(3, UBInt8("indexingadapter")), 1, 3)._encode, ([0x22, 0x33], {}), [None, 0x22, 0x33], None],

[PaddingAdapter(Field("paddingadapter", 4)).parse, b"abcd", b"abcd", None],
[PaddingAdapter(Field("paddingadapter", 4), strict = True).parse, b"abcd", None, PaddingError],
[PaddingAdapter(Field("paddingadapter", 4), strict = True).parse, b"\x00\x00\x00\x00", b"\x00\x00\x00\x00", None],
[PaddingAdapter(Field("paddingadapter", 4)).build, b"abcd", b"\x00\x00\x00\x00", None],

[Padding(4).parse, b"\x00\x00\x00\x00", None, None],
[Padding(4, strict=True).parse, b"\x00\x00\x00\x00", None, None],
[Padding(4, strict=True).parse, b"????", None, PaddingError],
[Padding(4).build, None, b"\x00\x00\x00\x00", None],
[Padding(4, strict=True).build, None, b"\x00\x00\x00\x00", None],
[lambda none: Padding(4, pattern=b"??"), None, None, PaddingError],

[LengthValueAdapter(Sequence("lengthvalueadapter", UBInt8("length"), Field("value", lambda ctx: ctx.length))).parse,
b"\x05abcde", b"abcde", None],
[LengthValueAdapter(Sequence("lengthvalueadapter", UBInt8("length"), Field("value", lambda ctx: ctx.length))).build,
Expand Down Expand Up @@ -211,10 +209,8 @@ class ZlibCodec(object):
#
[Aligned(UBInt8("aligned")).parse, b"\x01\x00\x00\x00", 1, None],
[Aligned(UBInt8("aligned")).build, 1, b"\x01\x00\x00\x00", None],
[Struct("aligned", Aligned(UBInt8("a")), UBInt8("b")).parse,
b"\x01\x00\x00\x00\x02", Container(a=1,b=2), None],
[Struct("aligned", Aligned(UBInt8("a")), UBInt8("b")).build,
Container(a=1,b=2), b"\x01\x00\x00\x00\x02", None],
[Struct("aligned", Aligned(UBInt8("a")), UBInt8("b")).parse, b"\x01\x00\x00\x00\x02", Container(a=1,b=2), None],
[Struct("aligned", Aligned(UBInt8("a")), UBInt8("b")).build, Container(a=1,b=2), b"\x01\x00\x00\x00\x02", None],

[Bitwise(Field("bitwise", 8)).parse, b"\xff", b"\x01" * 8, None],
[Bitwise(Field("bitwise", lambda ctx: 8)).parse, b"\xff", b"\x01" * 8, None],
Expand Down Expand Up @@ -266,14 +262,10 @@ class ZlibCodec(object):
[PrefixedArray(UBInt8("array"), UBInt8("count")).sizeof, [1,1,1], 4, None],
[PrefixedArray(UBInt8("array"), UBInt8("count")).build, [1,1,1], b"\x03\x01\x01\x01", None],

[IfThenElse("ifthenelse", lambda ctx: True, UBInt8("then"), UBInt16("else")).parse,
b"\x01", 1, None],
[IfThenElse("ifthenelse", lambda ctx: False, UBInt8("then"), UBInt16("else")).parse,
b"\x00\x01", 1, None],
[IfThenElse("ifthenelse", lambda ctx: True, UBInt8("then"), UBInt16("else")).build,
1, b"\x01", None],
[IfThenElse("ifthenelse", lambda ctx: False, UBInt8("then"), UBInt16("else")).build,
1, b"\x00\x01", None],
[IfThenElse("ifthenelse", lambda ctx: True, UBInt8("then"), UBInt16("else")).parse, b"\x01", 1, None],
[IfThenElse("ifthenelse", lambda ctx: False, UBInt8("then"), UBInt16("else")).parse, b"\x00\x01", 1, None],
[IfThenElse("ifthenelse", lambda ctx: True, UBInt8("then"), UBInt16("else")).build, 1, b"\x01", None],
[IfThenElse("ifthenelse", lambda ctx: False, UBInt8("then"), UBInt16("else")).build, 1, b"\x00\x01", None],

[Magic(b"MZ").parse, b"MZ", b"MZ", None],
[Magic(b"MZ").parse, b"ELF", None, ConstError],
Expand Down Expand Up @@ -311,19 +303,3 @@ def test_all(self):
if errors:
self.fail("\n=========================\n".join(str(e) for e in errors))


class TestComputed(unittest.TestCase):
def setUp(self):
self.s = Struct("s",
UBInt8("width"),
UBInt8("height"),
Computed("total", lambda ctx: ctx.width * ctx.height),
)

def test_parse(self):
self.assertEquals(self.s.parse(b'\x05\x05'), Container(width=5,height=5,total=25))

def test_build(self):
self.assertEquals(self.s.build(Container(width=5,height=5,total=25)), b'\x05\x05')
self.assertEquals(self.s.build(Container(width=5,height=5)), b'\x05\x05')

0 comments on commit 6bdd8fa

Please sign in to comment.