mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 09:28:13 +00:00
fix(core): do not subclass range
micropython on real hw dislikes it for some reason also it's completely unnecessary
This commit is contained in:
parent
be9fcf7525
commit
e6a1bf840f
@ -12,6 +12,7 @@ if False:
|
|||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
Collection,
|
Collection,
|
||||||
|
Container,
|
||||||
Iterable,
|
Iterable,
|
||||||
List,
|
List,
|
||||||
Sequence,
|
Sequence,
|
||||||
@ -33,25 +34,17 @@ if False:
|
|||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
class _FastInclusiveRange(range):
|
class Interval:
|
||||||
"""Inclusive range with a fast membership test, suitable for PathSchema use.
|
"""Helper for testing membership in an interval."""
|
||||||
|
|
||||||
Micropython's `range` does not implement the `__contains__` method. This makes
|
|
||||||
checking whether `x in range(BIG_NUMBER)` slow. This class fixes the problem.
|
|
||||||
|
|
||||||
In addition, convenience modifications have been made:
|
|
||||||
* both `min` and `max` belong to the range (so `stop == max + 1`).
|
|
||||||
* both `min` and `max` must be set, `step` is not allowed (we don't need it and it
|
|
||||||
would make the `__contains__` method more complex)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, min: int, max: int) -> None:
|
def __init__(self, min: int, max: int) -> None:
|
||||||
super().__init__(min, max + 1)
|
self.min = min
|
||||||
|
self.max = max
|
||||||
|
|
||||||
def __contains__(self, x: object) -> bool:
|
def __contains__(self, x: object) -> bool:
|
||||||
if not isinstance(x, int):
|
if not isinstance(x, int):
|
||||||
return False
|
return False
|
||||||
return self.start <= x < self.stop
|
return self.min <= x <= self.max
|
||||||
|
|
||||||
|
|
||||||
class PathSchema:
|
class PathSchema:
|
||||||
@ -107,9 +100,9 @@ class PathSchema:
|
|||||||
}
|
}
|
||||||
|
|
||||||
WILDCARD_RANGES = {
|
WILDCARD_RANGES = {
|
||||||
"*": _FastInclusiveRange(0, HARDENED - 1),
|
"*": Interval(0, HARDENED - 1),
|
||||||
"*'": _FastInclusiveRange(HARDENED, 0xFFFF_FFFF),
|
"*'": Interval(HARDENED, 0xFFFF_FFFF),
|
||||||
"**": _FastInclusiveRange(0, 0xFFFF_FFFF),
|
"**": Interval(0, 0xFFFF_FFFF),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, pattern: str, slip44_id: Union[int, Iterable[int]]) -> None:
|
def __init__(self, pattern: str, slip44_id: Union[int, Iterable[int]]) -> None:
|
||||||
@ -120,8 +113,8 @@ class PathSchema:
|
|||||||
if isinstance(slip44_id, int):
|
if isinstance(slip44_id, int):
|
||||||
slip44_id = (slip44_id,)
|
slip44_id = (slip44_id,)
|
||||||
|
|
||||||
self.schema: List[Collection[int]] = []
|
self.schema: List[Container[int]] = []
|
||||||
self.trailing_components: Collection[int] = ()
|
self.trailing_components: Container[int] = ()
|
||||||
|
|
||||||
for component in components:
|
for component in components:
|
||||||
if component in self.WILDCARD_RANGES:
|
if component in self.WILDCARD_RANGES:
|
||||||
@ -151,7 +144,7 @@ class PathSchema:
|
|||||||
if "-" in component:
|
if "-" in component:
|
||||||
# parse as a range
|
# parse as a range
|
||||||
a, b = [parse(s) for s in component.split("-", 1)]
|
a, b = [parse(s) for s in component.split("-", 1)]
|
||||||
self.schema.append(_FastInclusiveRange(a, b))
|
self.schema.append(Interval(a, b))
|
||||||
|
|
||||||
elif "," in component:
|
elif "," in component:
|
||||||
# parse as a list of values
|
# parse as a list of values
|
||||||
@ -193,18 +186,23 @@ class PathSchema:
|
|||||||
return item ^ (item & HARDENED)
|
return item ^ (item & HARDENED)
|
||||||
|
|
||||||
for component in self.schema:
|
for component in self.schema:
|
||||||
if isinstance(component, range):
|
if isinstance(component, Interval):
|
||||||
a, b = component.start, component.stop - 1
|
a, b = component.min, component.max
|
||||||
components.append(
|
components.append(
|
||||||
"[{}-{}]{}".format(
|
"[{}-{}]{}".format(
|
||||||
unharden(a), unharden(b), "'" if a & HARDENED else ""
|
unharden(a), unharden(b), "'" if a & HARDENED else ""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
component_str = ",".join(str(unharden(i)) for i in component)
|
# mypy thinks component is a Contanier but we're using it as a Collection.
|
||||||
if len(component) > 1:
|
# Which in practice it is, the only non-Collection is Interval.
|
||||||
|
# But we're not going to introduce an additional type requirement
|
||||||
|
# for the sake of __repr__ that doesn't exist in production anyway
|
||||||
|
collection: Collection[int] = component # type: ignore
|
||||||
|
component_str = ",".join(str(unharden(i)) for i in collection)
|
||||||
|
if len(collection) > 1:
|
||||||
component_str = "[" + component_str + "]"
|
component_str = "[" + component_str + "]"
|
||||||
if next(iter(component)) & HARDENED:
|
if next(iter(collection)) & HARDENED:
|
||||||
component_str += "'"
|
component_str += "'"
|
||||||
components.append(component_str)
|
components.append(component_str)
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ class TestPathSchemas(unittest.TestCase):
|
|||||||
|
|
||||||
def assertEqualSchema(self, schema_a, schema_b):
|
def assertEqualSchema(self, schema_a, schema_b):
|
||||||
def is_equal(a, b):
|
def is_equal(a, b):
|
||||||
if isinstance(a, range) and isinstance(b, range):
|
if isinstance(a, Interval) and isinstance(b, Interval):
|
||||||
return a.start == b.start and a.step == b.step and a.stop == b.stop
|
return a.min == b.min and a.max == b.max
|
||||||
return set(a) == set(b)
|
return set(a) == set(b)
|
||||||
|
|
||||||
ensure(
|
ensure(
|
||||||
|
Loading…
Reference in New Issue
Block a user