1# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
2# Copyright (C) 2015 YAMAMOTO Takashi <yamamoto at valinux co jp>
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#    http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import base64
18
19import six
20
21from ryu.lib import addrconv
22
23
24class TypeDescr(object):
25    pass
26
27
28class IntDescr(TypeDescr):
29    def __init__(self, size):
30        self.size = size
31
32    def to_user(self, binary):
33        i = 0
34        for _ in range(self.size):
35            c = binary[:1]
36            i = i * 256 + ord(c)
37            binary = binary[1:]
38        return i
39
40    def from_user(self, i):
41        binary = b''
42        for _ in range(self.size):
43            binary = six.int2byte(i & 255) + binary
44            i //= 256
45        return binary
46
47
48Int1 = IntDescr(1)
49Int2 = IntDescr(2)
50Int3 = IntDescr(3)
51Int4 = IntDescr(4)
52Int8 = IntDescr(8)
53Int9 = IntDescr(9)
54Int16 = IntDescr(16)
55
56
57def _split_str(s, n):
58    """
59    split string into list of strings by specified number.
60    """
61    length = len(s)
62    return [s[i:i + n] for i in range(0, length, n)]
63
64
65class IntDescrMlt(TypeDescr):
66    def __init__(self, length, num):
67        self.length = length
68        self.num = num
69        self.size = length * num
70
71    def to_user(self, binary):
72        assert len(binary) == self.size
73        lb = _split_str(binary, self.length)
74        li = []
75        for b in lb:
76            i = 0
77            for _ in range(self.length):
78                c = b[:1]
79                i = i * 256 + ord(c)
80                b = b[1:]
81            li.append(i)
82        return tuple(li)
83
84    def from_user(self, li):
85        assert len(li) == self.num
86        binary = b''
87        for i in li:
88            b = b''
89            for _ in range(self.length):
90                b = six.int2byte(i & 255) + b
91                i //= 256
92            binary += b
93        return binary
94
95
96Int4Double = IntDescrMlt(4, 2)
97
98
99class MacAddr(TypeDescr):
100    size = 6
101    to_user = addrconv.mac.bin_to_text
102    from_user = addrconv.mac.text_to_bin
103
104
105class IPv4Addr(TypeDescr):
106    size = 4
107    to_user = addrconv.ipv4.bin_to_text
108    from_user = addrconv.ipv4.text_to_bin
109
110
111class IPv6Addr(TypeDescr):
112    size = 16
113    to_user = addrconv.ipv6.bin_to_text
114    from_user = addrconv.ipv6.text_to_bin
115
116
117class UnknownType(TypeDescr):
118
119    @staticmethod
120    def to_user(data):
121        if six.PY3:
122            return base64.b64encode(data).decode('ascii')
123        else:
124            return base64.b64encode(data)
125
126    from_user = staticmethod(base64.b64decode)
127
128
129class TypeDisp(object):
130    _TYPES = {}
131    _REV_TYPES = None
132    _UNKNOWN_TYPE = None
133
134    @classmethod
135    def register_unknown_type(cls):
136        def _register_type(subcls):
137            cls._UNKNOWN_TYPE = subcls
138            return subcls
139        return _register_type
140
141    @classmethod
142    def register_type(cls, type_):
143        cls._TYPES = cls._TYPES.copy()
144
145        def _register_type(subcls):
146            cls._TYPES[type_] = subcls
147            cls._REV_TYPES = None
148            return subcls
149        return _register_type
150
151    @classmethod
152    def _lookup_type(cls, type_):
153        try:
154            return cls._TYPES[type_]
155        except KeyError:
156            return cls._UNKNOWN_TYPE
157
158    @classmethod
159    def _rev_lookup_type(cls, targ_cls):
160        if cls._REV_TYPES is None:
161            rev = dict((v, k) for k, v in cls._TYPES.items())
162            cls._REV_TYPES = rev
163        return cls._REV_TYPES[targ_cls]
164