xref: /aosp_15_r20/external/openthread/tests/scripts/thread-cert/pktverify/bytes.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1*cfb92d14SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*cfb92d14SAndroid Build Coastguard Worker#
3*cfb92d14SAndroid Build Coastguard Worker#  Copyright (c) 2019, The OpenThread Authors.
4*cfb92d14SAndroid Build Coastguard Worker#  All rights reserved.
5*cfb92d14SAndroid Build Coastguard Worker#
6*cfb92d14SAndroid Build Coastguard Worker#  Redistribution and use in source and binary forms, with or without
7*cfb92d14SAndroid Build Coastguard Worker#  modification, are permitted provided that the following conditions are met:
8*cfb92d14SAndroid Build Coastguard Worker#  1. Redistributions of source code must retain the above copyright
9*cfb92d14SAndroid Build Coastguard Worker#     notice, this list of conditions and the following disclaimer.
10*cfb92d14SAndroid Build Coastguard Worker#  2. Redistributions in binary form must reproduce the above copyright
11*cfb92d14SAndroid Build Coastguard Worker#     notice, this list of conditions and the following disclaimer in the
12*cfb92d14SAndroid Build Coastguard Worker#     documentation and/or other materials provided with the distribution.
13*cfb92d14SAndroid Build Coastguard Worker#  3. Neither the name of the copyright holder nor the
14*cfb92d14SAndroid Build Coastguard Worker#     names of its contributors may be used to endorse or promote products
15*cfb92d14SAndroid Build Coastguard Worker#     derived from this software without specific prior written permission.
16*cfb92d14SAndroid Build Coastguard Worker#
17*cfb92d14SAndroid Build Coastguard Worker#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*cfb92d14SAndroid Build Coastguard Worker#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*cfb92d14SAndroid Build Coastguard Worker#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*cfb92d14SAndroid Build Coastguard Worker#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21*cfb92d14SAndroid Build Coastguard Worker#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*cfb92d14SAndroid Build Coastguard Worker#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*cfb92d14SAndroid Build Coastguard Worker#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*cfb92d14SAndroid Build Coastguard Worker#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*cfb92d14SAndroid Build Coastguard Worker#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*cfb92d14SAndroid Build Coastguard Worker#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*cfb92d14SAndroid Build Coastguard Worker#  POSSIBILITY OF SUCH DAMAGE.
28*cfb92d14SAndroid Build Coastguard Worker#
29*cfb92d14SAndroid Build Coastguard Workerimport sys
30*cfb92d14SAndroid Build Coastguard Workerfrom typing import Union, Any
31*cfb92d14SAndroid Build Coastguard Worker
32*cfb92d14SAndroid Build Coastguard Worker
33*cfb92d14SAndroid Build Coastguard Workerclass Bytes(bytearray):
34*cfb92d14SAndroid Build Coastguard Worker    """Bytes represents a byte array which is able to handle strings of flexible formats"""
35*cfb92d14SAndroid Build Coastguard Worker
36*cfb92d14SAndroid Build Coastguard Worker    def __init__(self, s: Union[str, bytearray, 'Bytes', Any]):
37*cfb92d14SAndroid Build Coastguard Worker        if isinstance(s, str):
38*cfb92d14SAndroid Build Coastguard Worker            try:
39*cfb92d14SAndroid Build Coastguard Worker                s = Bytes._parse_compact(s)
40*cfb92d14SAndroid Build Coastguard Worker            except ValueError:
41*cfb92d14SAndroid Build Coastguard Worker                try:
42*cfb92d14SAndroid Build Coastguard Worker                    s = Bytes._parse_octets(s)
43*cfb92d14SAndroid Build Coastguard Worker                except ValueError:
44*cfb92d14SAndroid Build Coastguard Worker                    s = Bytes._parse_hextets(s)
45*cfb92d14SAndroid Build Coastguard Worker
46*cfb92d14SAndroid Build Coastguard Worker        super().__init__(s)
47*cfb92d14SAndroid Build Coastguard Worker
48*cfb92d14SAndroid Build Coastguard Worker    def __hash__(self):
49*cfb92d14SAndroid Build Coastguard Worker        return hash(bytes(self))
50*cfb92d14SAndroid Build Coastguard Worker
51*cfb92d14SAndroid Build Coastguard Worker    def __repr__(self):
52*cfb92d14SAndroid Build Coastguard Worker        return '%s(%r)' % (self.__class__.__name__, self.format_compact())
53*cfb92d14SAndroid Build Coastguard Worker
54*cfb92d14SAndroid Build Coastguard Worker    def format_compact(self) -> str:
55*cfb92d14SAndroid Build Coastguard Worker        """
56*cfb92d14SAndroid Build Coastguard Worker        Converts the Bytes to a compact string (without ":").
57*cfb92d14SAndroid Build Coastguard Worker        """
58*cfb92d14SAndroid Build Coastguard Worker        return ''.join('%02x' % b for b in self)
59*cfb92d14SAndroid Build Coastguard Worker
60*cfb92d14SAndroid Build Coastguard Worker    def format_octets(self) -> str:
61*cfb92d14SAndroid Build Coastguard Worker        """
62*cfb92d14SAndroid Build Coastguard Worker        Converts the Bytes to a string of octets separated by ":".
63*cfb92d14SAndroid Build Coastguard Worker        """
64*cfb92d14SAndroid Build Coastguard Worker        return ':'.join('%02x' % b for b in self)
65*cfb92d14SAndroid Build Coastguard Worker
66*cfb92d14SAndroid Build Coastguard Worker    def format_hextets(self) -> str:
67*cfb92d14SAndroid Build Coastguard Worker        """
68*cfb92d14SAndroid Build Coastguard Worker        Converts the Bytes to a string of hextets separated by ":"
69*cfb92d14SAndroid Build Coastguard Worker        """
70*cfb92d14SAndroid Build Coastguard Worker        assert len(self) % 2 == 0, self.format_octets()
71*cfb92d14SAndroid Build Coastguard Worker        return ':'.join('%04x' % (self[i] * 256 + self[i + 1]) for i in range(0, len(self), 2))
72*cfb92d14SAndroid Build Coastguard Worker
73*cfb92d14SAndroid Build Coastguard Worker    __str__ = format_octets
74*cfb92d14SAndroid Build Coastguard Worker
75*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
76*cfb92d14SAndroid Build Coastguard Worker    def _parse_compact(s: str) -> bytearray:
77*cfb92d14SAndroid Build Coastguard Worker        try:
78*cfb92d14SAndroid Build Coastguard Worker            assert len(s) % 2 == 0
79*cfb92d14SAndroid Build Coastguard Worker            return bytearray(int(s[i:i + 2], 16) for i in range(0, len(s), 2))
80*cfb92d14SAndroid Build Coastguard Worker        except Exception:
81*cfb92d14SAndroid Build Coastguard Worker            raise ValueError(s)
82*cfb92d14SAndroid Build Coastguard Worker
83*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
84*cfb92d14SAndroid Build Coastguard Worker    def _parse_octets(s: str) -> bytearray:
85*cfb92d14SAndroid Build Coastguard Worker        try:
86*cfb92d14SAndroid Build Coastguard Worker            assert len(s) % 3 == 2 or not s
87*cfb92d14SAndroid Build Coastguard Worker            if not s:
88*cfb92d14SAndroid Build Coastguard Worker                return bytearray(b"")
89*cfb92d14SAndroid Build Coastguard Worker
90*cfb92d14SAndroid Build Coastguard Worker            return bytearray(int(x, 16) for x in s.split(':'))
91*cfb92d14SAndroid Build Coastguard Worker        except Exception:
92*cfb92d14SAndroid Build Coastguard Worker            raise ValueError(s)
93*cfb92d14SAndroid Build Coastguard Worker
94*cfb92d14SAndroid Build Coastguard Worker    @staticmethod
95*cfb92d14SAndroid Build Coastguard Worker    def _parse_hextets(s) -> bytearray:
96*cfb92d14SAndroid Build Coastguard Worker        try:
97*cfb92d14SAndroid Build Coastguard Worker            assert len(s) % 5 == 4 or not s
98*cfb92d14SAndroid Build Coastguard Worker            if not s:
99*cfb92d14SAndroid Build Coastguard Worker                return bytearray(b"")
100*cfb92d14SAndroid Build Coastguard Worker
101*cfb92d14SAndroid Build Coastguard Worker            return bytearray(int(x[i:i + 2], 16) for x in s.split(':') for i in (0, 2))
102*cfb92d14SAndroid Build Coastguard Worker        except Exception:
103*cfb92d14SAndroid Build Coastguard Worker            raise ValueError(s)
104*cfb92d14SAndroid Build Coastguard Worker
105*cfb92d14SAndroid Build Coastguard Worker    def __getitem__(self, item) -> Union['Bytes', int]:
106*cfb92d14SAndroid Build Coastguard Worker        """
107*cfb92d14SAndroid Build Coastguard Worker        Get self[item].
108*cfb92d14SAndroid Build Coastguard Worker
109*cfb92d14SAndroid Build Coastguard Worker        :param item: index or slice to retrieve
110*cfb92d14SAndroid Build Coastguard Worker        :return: the byte value at specified index or sub `Bytes` if item is slice
111*cfb92d14SAndroid Build Coastguard Worker        """
112*cfb92d14SAndroid Build Coastguard Worker        x = super().__getitem__(item)
113*cfb92d14SAndroid Build Coastguard Worker        if isinstance(x, bytearray):
114*cfb92d14SAndroid Build Coastguard Worker            return Bytes(x)
115*cfb92d14SAndroid Build Coastguard Worker        else:
116*cfb92d14SAndroid Build Coastguard Worker            return x
117*cfb92d14SAndroid Build Coastguard Worker
118*cfb92d14SAndroid Build Coastguard Worker    def __eq__(self, other: Union[str, 'Bytes']):
119*cfb92d14SAndroid Build Coastguard Worker        """
120*cfb92d14SAndroid Build Coastguard Worker        Check if bytes is equal to other.
121*cfb92d14SAndroid Build Coastguard Worker        """
122*cfb92d14SAndroid Build Coastguard Worker        if other is None:
123*cfb92d14SAndroid Build Coastguard Worker            return False
124*cfb92d14SAndroid Build Coastguard Worker        elif not isinstance(other, Bytes):
125*cfb92d14SAndroid Build Coastguard Worker            other = self.__class__(other)
126*cfb92d14SAndroid Build Coastguard Worker
127*cfb92d14SAndroid Build Coastguard Worker        eq = super().__eq__(other)
128*cfb92d14SAndroid Build Coastguard Worker        print("[%r %s %r]" % (self, "==" if eq else "!=", other), file=sys.stderr)
129*cfb92d14SAndroid Build Coastguard Worker        return eq
130*cfb92d14SAndroid Build Coastguard Worker
131*cfb92d14SAndroid Build Coastguard Worker
132*cfb92d14SAndroid Build Coastguard Workerif __name__ == '__main__':
133*cfb92d14SAndroid Build Coastguard Worker    # some simple tests
134*cfb92d14SAndroid Build Coastguard Worker    x = Bytes(b"\x01\x02\x03\x04")
135*cfb92d14SAndroid Build Coastguard Worker    assert eval(repr(x)) == x, repr(x)  # representation of Bytes should be able to be evaluated back
136*cfb92d14SAndroid Build Coastguard Worker    assert x == str(x), (x, str(x))
137*cfb92d14SAndroid Build Coastguard Worker
138*cfb92d14SAndroid Build Coastguard Worker    assert x.format_compact() == "01020304", x.format_compact()
139*cfb92d14SAndroid Build Coastguard Worker    assert x.format_octets() == "01:02:03:04", x.format_octets()
140*cfb92d14SAndroid Build Coastguard Worker    assert x.format_hextets() == "0102:0304", x.format_hextets()
141*cfb92d14SAndroid Build Coastguard Worker
142*cfb92d14SAndroid Build Coastguard Worker    assert Bytes._parse_compact("") == Bytes(b"")
143*cfb92d14SAndroid Build Coastguard Worker    assert Bytes._parse_compact('01020304') == x
144*cfb92d14SAndroid Build Coastguard Worker
145*cfb92d14SAndroid Build Coastguard Worker    assert Bytes._parse_octets("") == Bytes(b"")
146*cfb92d14SAndroid Build Coastguard Worker    assert Bytes._parse_octets('01:02:03:04') == x
147*cfb92d14SAndroid Build Coastguard Worker
148*cfb92d14SAndroid Build Coastguard Worker    assert Bytes._parse_hextets("") == Bytes(b"")
149*cfb92d14SAndroid Build Coastguard Worker    assert Bytes._parse_hextets('0102:0304') == x
150*cfb92d14SAndroid Build Coastguard Worker
151*cfb92d14SAndroid Build Coastguard Worker    assert isinstance(x[:2], Bytes)
152*cfb92d14SAndroid Build Coastguard Worker    assert isinstance(x[-2:], Bytes)
153*cfb92d14SAndroid Build Coastguard Worker    assert x[:2] == Bytes(b'\x01\x02')
154*cfb92d14SAndroid Build Coastguard Worker    assert x[-2:] == Bytes(b'\x03\x04')
155*cfb92d14SAndroid Build Coastguard Worker
156*cfb92d14SAndroid Build Coastguard Worker    # should also parse string formats
157*cfb92d14SAndroid Build Coastguard Worker    assert Bytes("01020304") == Bytes(b"\x01\x02\x03\x04")
158*cfb92d14SAndroid Build Coastguard Worker    assert Bytes("01:02:03:04") == Bytes(b"\x01\x02\x03\x04")
159*cfb92d14SAndroid Build Coastguard Worker    assert Bytes("0102:0304") == Bytes(b"\x01\x02\x03\x04")
160