diff -r d3732760e3d6 Lib/ipaddress.py --- a/Lib/ipaddress.py Sun Mar 02 04:17:01 2014 -0500 +++ b/Lib/ipaddress.py Sun Mar 02 15:20:09 2014 +0100 @@ -258,7 +258,7 @@ first = first.__class__(first_int) -def _collapse_addresses_recursive(addresses): +def _collapse_addresses_shiftreduce(addresses): """Loops through the addresses, collapsing concurrent netblocks. Example: @@ -268,7 +268,7 @@ ip3 = IPv4Network('192.0.2.128/26') ip4 = IPv4Network('192.0.2.192/26') - _collapse_addresses_recursive([ip1, ip2, ip3, ip4]) -> + _collapse_addresses_shiftreduce([ip1, ip2, ip3, ip4]) -> [IPv4Network('192.0.2.0/24')] This shouldn't be called directly; it is called via @@ -282,28 +282,49 @@ passed. """ - while True: - last_addr = None - ret_array = [] - optimized = False - - for cur_addr in addresses: - if not ret_array: - last_addr = cur_addr - ret_array.append(cur_addr) - elif (cur_addr.network_address >= last_addr.network_address and - cur_addr.broadcast_address <= last_addr.broadcast_address): - optimized = True - elif cur_addr == list(last_addr.supernet().subnets())[1]: - ret_array[-1] = last_addr = last_addr.supernet() - optimized = True - else: - last_addr = cur_addr - ret_array.append(cur_addr) - - addresses = ret_array - if not optimized: - return addresses + + stack = [] + + for net in addresses: + + # Initialise the output + if not stack: + stack.append(net) + continue + + # Keep a ref to stack[-1] (just to keep the code more readable) + head = stack[-1] + + # If the current net is completely contained inside the last item on + # the stack we can simply ignore it. + if (head.network_address <= net.network_address and + head.broadcast_address >= net.broadcast_address): + continue + + # If the current net does not have the same prefix length, we cannot + # merge them. Shift it onto the stack. + elif net.prefixlen != head.prefixlen: + stack.append(net) + + # If the network which immediately follows last item on the stack is + # the same as the current net, we can merge (i.e. add the head's + # supernet) + elif list(head.supernet().subnets())[1] == net: + stack[-1] = head.supernet() + + # Now, the last item on the stack changed, and possibly this can + # have an effect on previous items on the stack. Reduce the stack + # as much as possible. + while (len(stack) >= 2 and + list(stack[-2].supernet().subnets())[1] == stack[-1]): + stack.pop() + stack[-1] = stack[-1].supernet() + + # Nothing can be done. Shift the current net on the stack. + else: + stack.append(net) + + return stack def collapse_addresses(addresses): @@ -359,7 +380,7 @@ i = ips.index(last) + 1 addrs.extend(summarize_address_range(first, last)) - return iter(_collapse_addresses_recursive(sorted( + return iter(_collapse_addresses_shiftreduce(sorted( addrs + nets, key=_BaseNetwork._get_networks_key)))