Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(19)

Side by Side Diff: ipaddr.py

Issue 67107: collapse_address_list is too slow SVN Base: http://ipaddr-py.googlecode.com/svn/trunk/
Patch Set: Fixes for comments on Patch Set 6 Created 3 months, 2 weeks ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright 2007 Google Inc. 3 # Copyright 2007 Google Inc.
4 # Licensed to PSF under a Contributor Agreement. 4 # Licensed to PSF under a Contributor Agreement.
5 # 5 #
6 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License. 7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at 8 # You may obtain a copy of the License at
9 # 9 #
10 # http://www.apache.org/licenses/LICENSE-2.0 10 # http://www.apache.org/licenses/LICENSE-2.0
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 162
163 def IPv6(address, host=False): 163 def IPv6(address, host=False):
164 """Generic IPv6 Factory function.. 164 """Generic IPv6 Factory function..
165 165
166 Return an IPv6 address or network. 166 Return an IPv6 address or network.
167 """ 167 """
168 if host: 168 if host:
169 return IPv6Address(address) 169 return IPv6Address(address)
170 return IPv6Network(address) 170 return IPv6Network(address)
171 171
172
173 def _find_address_range(addresses):
174 """Find a sequence of addresses.
175
176 Args:
177 addresses: a list of IPv4 or IPv6 addresses.
178
179 Returns:
180 A tuple containing the first and last IP addresses in the sequence.
181
182 """
183 first = last = addresses[0]
184 for ip in addresses[1:]:
185 if ip._ip == last._ip + 1:
186 last = ip
187 else:
188 break
189 return (first, last)
190
191 def _get_prefix_length(number1, number2, bits):
192 """Get the number of leading bits that are same for two numbers.
193
194 Args:
195 number1: an integer.
196 number2: another integer.
197 bits: the maximum number of bits to compare.
198
199 Returns:
200 The number of leading bits that are the same for two numbers.
201
202 """
203 for i in range(bits):
204 if number1 >> i == number2 >> i:
205 return bits - i
206 return 0
207
208 def _count_righthand_zero_bits(number, bits):
209 """Count the number of zero bits on the right hand side.
210
211 Args:
212 number: an integer.
213 bits: maximum number of bits to count.
214
215 Returns:
216 The number of zero bits on the right hand side of the number.
217
218 """
219 if number == 0:
220 return bits
221 for i in range(bits):
222 if (number >> i) % 2:
223 return i
224
225 def summarize_address_range(first, last):
226 """Summarize a network range given the first and last IP addresses.
227
228 Example:
229 >>> summarize_address_range(IPv4Address('1.1.1.0'),
230 IPv4Address('1.1.1.130'))
231 [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'),
232 IPv4Network('1.1.1.130/32')]
233
234 Args:
235 first: the first IPv4 or IPv6 address in the range.
236 last: the last IPv4 or IPv6 address in the range.
237
238 Returns:
239 The address range collapsed to a list of IPv4 or IPv6 networks.
240
241 Raise:
242 IPTypeError:
243 If the first and last objects are not IP addresses.
244 If the first and last objects are not the same version.
245 ValueError:
246 If the last object is not greater than the first.
247 If the version is not 4 or 6.
248
249 """
250
251 if not (isinstance(first, BaseIP) and isinstance(last, BaseIP)):
252 raise IPTypeError("first and last must be IP addresses, not networks")
253 if first.version != last.version:
254 raise IPTypeError("IP addresses must be same version")
255 if first > last:
256 raise ValueError("last IP address must be greater than first")
257
258 networks = []
259
260 if first.version == 4:
261 ip = IPv4Network
262 elif first.version == 6:
263 ip = IPv6Network
264 else:
265 raise ValueError("unknown IP version")
266
267 ip_bits = first._max_prefixlen
268 first = first._ip
269 last = last._ip
270 while first <= last:
271 nbits = _count_righthand_zero_bits(first, ip_bits)
272 current = None
273 while nbits >= 0:
274 addend = 2**nbits - 1
275 current = first + addend
276 nbits -= 1
277 if current <= last:
278 break
279 prefix = _get_prefix_length(first, current, ip_bits)
280 net = ip(first)
281 net.prefixlen = prefix
282 networks.append(net)
283 if current == ip._ALL_ONES:
284 break
285 first = current + 1
286 return networks
172 287
173 def _collapse_address_list_recursive(addresses): 288 def _collapse_address_list_recursive(addresses):
174 """Loops through the addresses, collapsing concurrent netblocks. 289 """Loops through the addresses, collapsing concurrent netblocks.
175 290
176 Example: 291 Example:
177 292
178 ip1 = IPv4('1.1.0.0/24') 293 ip1 = IPv4('1.1.0.0/24')
179 ip2 = IPv4('1.1.1.0/24') 294 ip2 = IPv4('1.1.1.0/24')
180 ip3 = IPv4('1.1.2.0/24') 295 ip3 = IPv4('1.1.2.0/24')
181 ip4 = IPv4('1.1.3.0/24') 296 ip4 = IPv4('1.1.3.0/24')
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) -> 338 collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) ->
224 [IPv4('1.1.0.0/23')] 339 [IPv4('1.1.0.0/23')]
225 340
226 Args: 341 Args:
227 addresses: A list of IPv4 or IPv6 objects. 342 addresses: A list of IPv4 or IPv6 objects.
228 343
229 Returns: 344 Returns:
230 A list of IPv4 or IPv6 objects depending on what we were passed. 345 A list of IPv4 or IPv6 objects depending on what we were passed.
231 346
232 """ 347 """
348 i = 0
349 addrs = []
350 ips = []
351 nets = []
352 # split IP addresses and networks
353 for ip in addresses:
354 if isinstance(ip, BaseIP):
355 ips.append(ip)
356 elif ip._prefixlen == ip._max_prefixlen:
357 ips.append(ip.ip)
358 else:
359 nets.append(ip)
360 # sort and dedup
361 ips = sorted(set(ips))
362 nets = sorted(set(nets))
363
364 while i < len(ips):
365 (first, last) = _find_address_range(ips[i:])
366 i = ips.index(last) + 1
367 addrs.extend(summarize_address_range(first, last))
368
233 return _collapse_address_list_recursive( 369 return _collapse_address_list_recursive(
234 sorted(addresses, key=BaseNet._get_networks_key)) 370 sorted(addrs + nets, key=BaseNet._get_networks_key))
235 371
236 # backwards compatibility 372 # backwards compatibility
237 CollapseAddrList = collapse_address_list 373 CollapseAddrList = collapse_address_list
238 374
239 # Test whether this Python implementation supports byte objects that 375 # Test whether this Python implementation supports byte objects that
240 # are not identical to str ones. 376 # are not identical to str ones.
241 # We need to exclude platforms where bytes == str so that we can 377 # We need to exclude platforms where bytes == str so that we can
242 # distinguish between packed representations and strings, for example 378 # distinguish between packed representations and strings, for example
243 # b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address). 379 # b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address).
244 try: 380 try:
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 return self._version > other._version 455 return self._version > other._version
320 if self._ip != other._ip: 456 if self._ip != other._ip:
321 return self._ip > other._ip 457 return self._ip > other._ip
322 return False 458 return False
323 459
324 def __repr__(self): 460 def __repr__(self):
325 return '%s(%r)' % (self.__class__.__name__, str(self)) 461 return '%s(%r)' % (self.__class__.__name__, str(self))
326 462
327 def __str__(self): 463 def __str__(self):
328 return '%s' % self._string_from_ip_int(self._ip) 464 return '%s' % self._string_from_ip_int(self._ip)
465
466 def __hash__(self):
467 return hash(self._ip)
329 468
330 @property 469 @property
331 def version(self): 470 def version(self):
332 raise NotImplementedError('BaseIP has no version') 471 raise NotImplementedError('BaseIP has no version')
333 472
334 473
335 class BaseNet(IPAddrBase): 474 class BaseNet(IPAddrBase):
336 475
337 """A generic IP object. 476 """A generic IP object.
338 477
(...skipping 348 matching lines...) Expand 10 before | Expand all | Expand 10 after
687 The following methods are used by IPv4 objects in both single IP 826 The following methods are used by IPv4 objects in both single IP
688 addresses and networks. 827 addresses and networks.
689 828
690 """ 829 """
691 830
692 # Equivalent to 255.255.255.255 or 32 bits of 1's. 831 # Equivalent to 255.255.255.255 or 32 bits of 1's.
693 _ALL_ONES = (2**32) - 1 832 _ALL_ONES = (2**32) - 1
694 833
695 def __init__(self, address): 834 def __init__(self, address):
696 self._version = 4 835 self._version = 4
836 self._max_prefixlen = 32
697 837
698 def _explode_shorthand_ip_string(self, ip_str=None): 838 def _explode_shorthand_ip_string(self, ip_str=None):
699 if not ip_str: 839 if not ip_str:
700 ip_str = str(self) 840 ip_str = str(self)
701 return ip_str 841 return ip_str
702 842
703 def _ip_int_from_string(self, ip_str): 843 def _ip_int_from_string(self, ip_str):
704 """Turn the given IP string into an integer for comparison. 844 """Turn the given IP string into an integer for comparison.
705 845
706 Args: 846 Args:
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
753 if len(octets) != 4: 893 if len(octets) != 4:
754 return False 894 return False
755 895
756 for octet in octets: 896 for octet in octets:
757 try: 897 try:
758 if not 0 <= int(octet) <= 255: 898 if not 0 <= int(octet) <= 255:
759 return False 899 return False
760 except ValueError: 900 except ValueError:
761 return False 901 return False
762 return True 902 return True
903
904 @property
905 def max_prefixlen(self):
906 return self._max_prefixlen
763 907
764 @property 908 @property
765 def packed(self): 909 def packed(self):
766 """The binary representation of this address.""" 910 """The binary representation of this address."""
767 return struct.pack('!I', self._ip) 911 return struct.pack('!I', self._ip)
768 912
769 @property 913 @property
770 def version(self): 914 def version(self):
771 return self._version 915 return self._version
772 916
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after
1107 1251
1108 The following methods are used by IPv6 objects in both single IP 1252 The following methods are used by IPv6 objects in both single IP
1109 addresses and networks. 1253 addresses and networks.
1110 1254
1111 """ 1255 """
1112 1256
1113 _ALL_ONES = (2**128) - 1 1257 _ALL_ONES = (2**128) - 1
1114 1258
1115 def __init__(self, address): 1259 def __init__(self, address):
1116 self._version = 6 1260 self._version = 6
1261 self._max_prefixlen = 128
1117 1262
1118 def _ip_int_from_string(self, ip_str=None): 1263 def _ip_int_from_string(self, ip_str=None):
1119 """Turn an IPv6 ip_str into an integer. 1264 """Turn an IPv6 ip_str into an integer.
1120 1265
1121 Args: 1266 Args:
1122 ip_str: A string, the IPv6 ip_str. 1267 ip_str: A string, the IPv6 ip_str.
1123 1268
1124 Returns: 1269 Returns:
1125 A long, the IPv6 ip_str. 1270 A long, the IPv6 ip_str.
1126 1271
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
1313 Args: 1458 Args:
1314 ip_str: A string, the IPv6 address. 1459 ip_str: A string, the IPv6 address.
1315 1460
1316 Returns: 1461 Returns:
1317 A boolean, True if the address is shortened. 1462 A boolean, True if the address is shortened.
1318 1463
1319 """ 1464 """
1320 if ip_str.count('::') == 1: 1465 if ip_str.count('::') == 1:
1321 return True 1466 return True
1322 return False 1467 return False
1468
1469 @property
1470 def max_prefixlen(self):
1471 return self._max_prefixlen
1323 1472
1324 @property 1473 @property
1325 def packed(self): 1474 def packed(self):
1326 """The binary representation of this address.""" 1475 """The binary representation of this address."""
1327 return struct.pack('!QQ', self._ip >> 64, self._ip & (2**64 - 1)) 1476 return struct.pack('!QQ', self._ip >> 64, self._ip & (2**64 - 1))
1328 1477
1329 @property 1478 @property
1330 def version(self): 1479 def version(self):
1331 return self._version 1480 return self._version
1332 1481
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
1643 """ 1792 """
1644 try: 1793 try:
1645 prefixlen = int(prefixlen) 1794 prefixlen = int(prefixlen)
1646 except ValueError: 1795 except ValueError:
1647 return False 1796 return False
1648 return 0 <= prefixlen <= 128 1797 return 0 <= prefixlen <= 128
1649 1798
1650 # backwards compatibility 1799 # backwards compatibility
1651 Subnet = subnet 1800 Subnet = subnet
1652 Supernet = supernet 1801 Supernet = supernet
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld r497