1# Copyright (c) 2005 The Regents of The University of Michigan
2# Copyright (c) 2010 Advanced Micro Devices, Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met: redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer;
9# redistributions in binary form must reproduce the above copyright
10# notice, this list of conditions and the following disclaimer in the
11# documentation and/or other materials provided with the distribution;
12# neither the name of the copyright holders nor the names of its
13# contributors may be used to endorse or promote products derived from
14# this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27#
28# Authors: Nathan Binkert
29#          Gabe Black
30
31import six
32if six.PY3:
33    long = int
34
35# metric prefixes
36atto  = 1.0e-18
37femto = 1.0e-15
38pico  = 1.0e-12
39nano  = 1.0e-9
40micro = 1.0e-6
41milli = 1.0e-3
42
43kilo = 1.0e3
44mega = 1.0e6
45giga = 1.0e9
46tera = 1.0e12
47peta = 1.0e15
48exa  = 1.0e18
49
50# power of 2 prefixes
51kibi = 1024
52mebi = kibi * 1024
53gibi = mebi * 1024
54tebi = gibi * 1024
55pebi = tebi * 1024
56exbi = pebi * 1024
57
58metric_prefixes = {
59    'Ei': exbi,
60    'E': exa,
61    'Pi': pebi,
62    'P': peta,
63    'Ti': tebi,
64    'T': tera,
65    'Gi': gibi,
66    'G': giga,
67    'M': mega,
68    'ki': kibi,
69    'k': kilo,
70    'Mi': mebi,
71    'm': milli,
72    'u': micro,
73    'n': nano,
74    'p': pico,
75    'f': femto,
76    'a': atto,
77}
78
79binary_prefixes = {
80    'Ei': exbi,
81    'E' : exbi,
82    'Pi': pebi,
83    'P' : pebi,
84    'Ti': tebi,
85    'T' : tebi,
86    'Gi': gibi,
87    'G' : gibi,
88    'Mi': mebi,
89    'M' : mebi,
90    'ki': kibi,
91    'k' : kibi,
92}
93
94def assertStr(value):
95    if not isinstance(value, str):
96        raise TypeError("wrong type '%s' should be str" % type(value))
97
98
99# memory size configuration stuff
100def toNum(value, target_type, units, prefixes, converter):
101    assertStr(value)
102
103    def convert(val):
104        try:
105            return converter(val)
106        except ValueError:
107            raise ValueError(
108                "cannot convert '%s' to %s" % (value, target_type))
109
110    if units and not value.endswith(units):
111        units = None
112    if not units:
113        return convert(value)
114
115    value = value[:-len(units)]
116
117    prefix = next((p for p in prefixes.keys() if value.endswith(p)), None)
118    if not prefix:
119        return convert(value)
120    value = value[:-len(prefix)]
121
122    return convert(value) * prefixes[prefix]
123
124def toFloat(value, target_type='float', units=None, prefixes=[]):
125    return toNum(value, target_type, units, prefixes, float)
126
127def toMetricFloat(value, target_type='float', units=None):
128    return toFloat(value, target_type, units, metric_prefixes)
129
130def toBinaryFloat(value, target_type='float', units=None):
131    return toFloat(value, target_type, units, binary_prefixes)
132
133def toInteger(value, target_type='integer', units=None, prefixes=[]):
134    intifier = lambda x: int(x, 0)
135    return toNum(value, target_type, units, prefixes, intifier)
136
137def toMetricInteger(value, target_type='integer', units=None):
138    return toInteger(value, target_type, units, metric_prefixes)
139
140def toBinaryInteger(value, target_type='integer', units=None):
141    return toInteger(value, target_type, units, binary_prefixes)
142
143def toBool(value):
144    assertStr(value)
145
146    value = value.lower()
147    if value in ('true', 't', 'yes', 'y', '1'):
148        return True
149    if value in ('false', 'f', 'no', 'n', '0'):
150        return False
151    return result
152
153def toFrequency(value):
154    return toMetricFloat(value, 'frequency', 'Hz')
155
156def toLatency(value):
157    return toMetricFloat(value, 'latency', 's')
158
159def anyToLatency(value):
160    """result is a clock period"""
161    try:
162        return 1 / toFrequency(value)
163    except (ValueError, ZeroDivisionError):
164        pass
165
166    try:
167        return toLatency(value)
168    except ValueError:
169        pass
170
171    raise ValueError("cannot convert '%s' to clock period" % value)
172
173def anyToFrequency(value):
174    """result is a clock period"""
175    try:
176        return toFrequency(value)
177    except ValueError:
178        pass
179
180    try:
181        return 1 / toLatency(value)
182    except ValueError as ZeroDivisionError:
183        pass
184
185    raise ValueError("cannot convert '%s' to clock period" % value)
186
187def toNetworkBandwidth(value):
188    return toMetricFloat(value, 'network bandwidth', 'bps')
189
190def toMemoryBandwidth(value):
191    return toBinaryFloat(value, 'memory bandwidth', 'B/s')
192
193def toMemorySize(value):
194    return toBinaryInteger(value, 'memory size', 'B')
195
196def toIpAddress(value):
197    if not isinstance(value, str):
198        raise TypeError("wrong type '%s' should be str" % type(value))
199
200    bytes = value.split('.')
201    if len(bytes) != 4:
202        raise ValueError('invalid ip address %s' % value)
203
204    for byte in bytes:
205        if not 0 <= int(byte) <= 0xff:
206            raise ValueError('invalid ip address %s' % value)
207
208    return (int(bytes[0]) << 24) | (int(bytes[1]) << 16) | \
209           (int(bytes[2]) << 8)  | (int(bytes[3]) << 0)
210
211def toIpNetmask(value):
212    if not isinstance(value, str):
213        raise TypeError("wrong type '%s' should be str" % type(value))
214
215    (ip, netmask) = value.split('/')
216    ip = toIpAddress(ip)
217    netmaskParts = netmask.split('.')
218    if len(netmaskParts) == 1:
219        if not 0 <= int(netmask) <= 32:
220            raise ValueError('invalid netmask %s' % netmask)
221        return (ip, int(netmask))
222    elif len(netmaskParts) == 4:
223        netmaskNum = toIpAddress(netmask)
224        if netmaskNum == 0:
225            return (ip, 0)
226        testVal = 0
227        for i in range(32):
228            testVal |= (1 << (31 - i))
229            if testVal == netmaskNum:
230                return (ip, i + 1)
231        raise ValueError('invalid netmask %s' % netmask)
232    else:
233        raise ValueError('invalid netmask %s' % netmask)
234
235def toIpWithPort(value):
236    if not isinstance(value, str):
237        raise TypeError("wrong type '%s' should be str" % type(value))
238
239    (ip, port) = value.split(':')
240    ip = toIpAddress(ip)
241    if not 0 <= int(port) <= 0xffff:
242        raise ValueError('invalid port %s' % port)
243    return (ip, int(port))
244
245def toVoltage(value):
246    return toMetricFloat(value, 'voltage', 'V')
247
248def toCurrent(value):
249    return toMetricFloat(value, 'current', 'A')
250
251def toEnergy(value):
252    return toMetricFloat(value, 'energy', 'J')
253