Skip to content

Commit

Permalink
EmbeddedSwitch removed (and docs updated)
Browse files Browse the repository at this point in the history
  • Loading branch information
arekbulski committed Jan 19, 2020
1 parent c2ee9bd commit 5d2e3a8
Show file tree
Hide file tree
Showing 8 changed files with 3 additions and 124 deletions.
1 change: 0 additions & 1 deletion construct/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
'Default',
'Double',
'Embedded',
'EmbeddedSwitch',
'Enum',
'EnumInteger',
'EnumIntegerString',
Expand Down
48 changes: 1 addition & 47 deletions construct/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2434,7 +2434,7 @@ class Embedded(Subconstruct):
.. warning::
Can only be used between Struct Sequence FocusedSeq Union LazyStruct, for example Struct can embed fields from a Sequence or vice versa. There is also :class:`~construct.core.EmbeddedSwitch` macro that pseudo-embeds a Switch, it is NOT a Embedded(Switch(...). Its not possible to embed IfThenElse Switch RawCopy or otherwise.
Can only be used between Struct Sequence FocusedSeq Union LazyStruct, for example Struct can embed fields from a Sequence or vice versa. Its not possible to embed IfThenElse Switch RawCopy or otherwise.
Parsing building and sizeof are deferred to subcon.
Expand Down Expand Up @@ -3710,52 +3710,6 @@ def _emitparse(self, code):
return "%s.get(%s, %s)(io, this)" % (fname, self.keyfunc, defaultfname)


def EmbeddedSwitch(merged, selector, mapping):
r"""
Macro that simulates embedding Switch, which under new embedding semantics is not possible. This macro does NOT produce a Switch. It generates classes that behave the same way as you would expect from embedded Switch, only that. Instance created by this macro CAN be embedded.
Both `merged` and all values in `mapping` must be Struct instances. Macro re-creates a single struct that contains all fields, where each field is wrapped in `If(selector == key, ...)`. Note that resulting dictionary contains None values for fields that would not be chosen by switch. Note also that if selector does not match any cases, it passes successfully (default Switch behavior).
All fields should have unique names. Otherwise fields that were not selected during parsing may return None and override other fields context entries that have same name. This is because `If` field returns None value if condition is not met, but the Struct inserts that None value into the context entry regardless.
:param merged: Struct instance
:param selector: this expression, that references one of `merged` fields
:param mapping: dict with values being Struct instances
Example::
d = EmbeddedSwitch(
Struct(
"type" / Byte,
),
this.type,
{
0: Struct("name" / PascalString(Byte, "utf8")),
1: Struct("value" / Byte),
}
)
# generates essentially following
d = Struct(
"type" / Byte,
"name" / If(this.type == 0, PascalString(Byte, "utf8")),
"value" / If(this.type == 1, Byte),
)
# both parse like following
>>> d.parse(b"\x00\x00")
Container(type=0, name=u'', value=None)
>>> d.parse(b"\x01\x00")
Container(type=1, name=None, value=0)
"""

merged2 = list(merged.subcons)
for key,st in mapping.items():
for sc in st.subcons:
merged2.append(sc.name / If(selector == key, sc))
return Struct(*merged2)


class StopIf(Construct):
r"""
Checks for a condition, and stops certain classes (:class:`~construct.core.Struct` :class:`~construct.core.Sequence` :class:`~construct.core.GreedyRange`) from parsing or building further.
Expand Down
1 change: 0 additions & 1 deletion docs/api/conditional.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ Core API: Conditional
.. autofunction:: construct.If
.. autofunction:: construct.IfThenElse
.. autofunction:: construct.Switch
.. autofunction:: construct.EmbeddedSwitch
.. autofunction:: construct.StopIf
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ User Guide
intro
transition28
transition29
transition210
basics
advanced
bitwise
Expand Down
36 changes: 1 addition & 35 deletions docs/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Special wrapper that allows outer multiple-subcons construct to merge fields fro

.. warning::

Can only be used between Struct Sequence FocusedSeq Union LazyStruct, for example Struct can embed fields from a Sequence or vice versa. There is also :class:`~construct.core.EmbeddedSwitch` macro that pseudo-embeds a Switch, it is NOT a Embedded(Switch(...). Its not possible to embed IfThenElse Switch RawCopy or otherwise.
Can only be used between Struct Sequence FocusedSeq Union LazyStruct, for example Struct can embed fields from a Sequence or vice versa. Its not possible to embed IfThenElse Switch RawCopy or otherwise.

>>> outer = Struct(
... Embedded(Struct(
Expand Down Expand Up @@ -403,40 +403,6 @@ b'\x00\x00\x00\x05'
b"\x01"


EmbeddedSwitch
----------------

Macro that simulates embedding Switch, which under new embedding semantics is not possible. This macro does NOT produce a Switch. It generates classes that behave the same way as you would expect from embedded Switch, only that. Instance created by this macro CAN be embedded.

All fields should have unique names. Otherwise fields that were not selected during parsing may return None and override other fields context entries that have same name. This is because `If` field returns None value if condition is not met, but the Struct inserts that None value into the context entry regardless.

::

d = EmbeddedSwitch(
Struct(
"type" / Byte,
),
this.type,
{
0: Struct("name" / PascalString(Byte, "utf8")),
1: Struct("value" / Byte),
}
)

# generates essentially following
d = Struct(
"type" / Byte,
"name" / If(this.type == 0, PascalString(Byte, "utf8")),
"value" / If(this.type == 1, Byte),
)

# both parse like following
>>> d.parse(b"\x00\x00")
Container(type=0, name=u'', value=None)
>>> d.parse(b"\x01\x00")
Container(type=1, name=None, value=0)


StopIf
------

Expand Down
2 changes: 0 additions & 2 deletions tests/test_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,6 @@ def test_class_switch_build(benchmark):
d = Switch(this.n, { 1:Int8ub, 2:Int16ub, 4:Int32ub })
benchmark(d.build, 0, n=4)

# - combines performance of other fields
# EmbeddedSwitch
# - raises exception
# StopIf

Expand Down
11 changes: 0 additions & 11 deletions tests/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,6 @@
from construct.lib import *


embeddedswitch1 = EmbeddedSwitch(
Struct(
"type" / Byte,
),
this.type,
{
0: Struct("name" / PascalString(Byte, "utf8")),
1: Struct("value" / Byte),
}
)
example = Struct(
"num" / Byte,

Expand Down Expand Up @@ -115,7 +105,6 @@
"switch1" / Switch(this.num, {0 : Byte, 255 : Error}),
"switch2" / Switch(this.num, {}),
"switch3" / Switch(this.num, {}, default=Byte),
"embeddedswitch1" / embeddedswitch1,
"stopif0" / StopIf(this.num == 255),
"stopif1" / Struct(StopIf(this._.num == 0), Error),
"stopif2" / Sequence(StopIf(this._.num == 0), Error),
Expand Down
27 changes: 0 additions & 27 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -830,33 +830,6 @@ def test_switch_issue_357():
)
assert st1.parse(b"") == st2.parse(b"")

def test_embeddedswitch():
d = EmbeddedSwitch(
Struct(
"type" / Byte,
),
this.type,
{
0: Struct("name" / PascalString(Byte, "utf8")),
1: Struct("value" / Byte),
}
)
common(d, b"\x00\x00", Container(type=0, name=u"", value=None))
common(d, b"\x01\x00", Container(type=1, name=None, value=0))

def test_embeddedswitch_issue_684():
d = EmbeddedSwitch(
Struct(
"type" / Byte,
),
this.type,
{
1: Struct("value" / Byte),
2: Struct("value" / Byte),
}
)
d.parse(b"\x01\xff") == Container(type=1, value=None)

def test_stopif():
d = Struct("x"/Byte, StopIf(this.x == 0), "y"/Byte)
common(d, b"\x00", Container(x=0))
Expand Down

0 comments on commit 5d2e3a8

Please sign in to comment.