ManInTheMiddleAmongUs/proto/fieldtype.py
2020-10-20 02:46:01 +02:00

95 lines
2.3 KiB
Python

from dataclasses import dataclass
import binascii
from typing import *
__all__ = [
"Fixed",
"Unknown",
"Bytes",
"Ascii",
"Int",
]
T = TypeVar("T")
Extractor = Callable[[bytes], T]
Formatter = Callable[[T], str]
default_bytes_formatter: Formatter[bytes] = lambda data: binascii.hexlify(data).decode()
def assert_extractor(pattern: bytes) -> Extractor[bytes]:
def inner(data: bytes) -> bytes:
assert data == pattern
return pattern
return inner
Backref = str
@dataclass
class FieldType(Generic[T]):
"""
Dataclass to store all the information necessary to parse a packet field.
Fields:
-------
name : Optional[str]
Field Name.
length : Union[int, Backref]
How many bytes the field has.
Can be either an integer, or the name of a previous field containing one.
extractor : Extractor[T]
Function to parse the bytes of the field.
May throw Exceptions to signal parsing errors.
formatter : Formatter[T]
Function to get a string representation of the field.
"""
name: Optional[str]
length: Union[int, Backref]
extractor: Extractor[T]
formatter: Formatter[T] = str
def Fixed(pattern: bytes, **kwargs) -> FieldType[bytes]:
return FieldType(None, len(pattern), assert_extractor(pattern), **kwargs)
def Unknown(
length: Union[int, Backref],
name: Optional[str] = None,
formatter: Formatter[bytes] = default_bytes_formatter,
) -> FieldType[bytes]:
return FieldType(name, length, lambda data: data, formatter=formatter)
def Bytes(
name: str,
length: Union[int, Backref],
extractor: Extractor[bytes] = lambda data: data,
formatter: Formatter[bytes] = default_bytes_formatter,
) -> FieldType[bytes]:
return FieldType(name, length, extractor, formatter=formatter)
def Ascii(
name: str,
length: Union[int, Backref],
extractor: Extractor[str] = bytes.decode,
**kwargs
) -> FieldType[str]:
return FieldType(name, length, extractor, **kwargs)
def Int(
name: str,
length: Union[int, Backref],
extractor: Extractor[int] = lambda data: int.from_bytes(data, "big"),
**kwargs
) -> FieldType[int]:
return FieldType(name, length, extractor, **kwargs)