#!/usr/bin/env tclsh # Martin Heinrich package require ip namespace eval ::ip { # XXX fix toInteger to return ipv4 in range 0 through 4294967295 in any architecture # two possible fixes: # * scan wide (as implemented below) # * format wide (original code, but %lu instead of %u) proc toInteger {ip} { binary scan \0\0\0\0[Normalize4 $ip] W out return [format %u $out] } # calculate the integer distance from ip1 to ip2, i.e. $ip2-$ip1 proc distance1 {ip1 ip2} { # use package ip for normalization # XXX does not support ipv6 --> could use faster distance2 instead expr {[toInteger $ip2]-[toInteger $ip1]} } proc distance2 {ip1 ip2} { # calculate with small numbers, but undefined for invalid IPv4 lassign [split $ip1 .] ip11 ip12 ip13 ip14 lassign [split $ip2 .] ip21 ip22 ip23 ip24 expr {((( $ip21-$ip11)*256+ $ip22-$ip12)*256+ $ip23-$ip13)*256+ $ip24-$ip14 } } # calculate the ip address at an offset from a given address, i.e. $ip2 == [nextIp $ip1 [distance $ip1 $ip2]] proc nextIp {ip {offset 1}} { set int [toInteger $ip] incr int $offset set prot {} # TODO if ipv4 then set prot -ipv4, but # XXX intToString has -ipv4, but never returns ipv6 intToString $int {*}$prot } proc nextIp2 {ipVar {offset 1}} { upvar 1 $ipVar ip lassign [split $ip .] ip1 ip2 ip3 ip4 incr $ip4 $offset if {0 > $ip4} { } set ip "$ip1.$ip2.$ip3.$ip4" } } puts ===nextIp=== foreach {ip1 ip2} { 0.0.0.0 255.255.255.255 255.255.255.255 0.0.0.0 10.11.12.13 10.11.42.42 10.11.42.42 10.11.12.13 54.229.115.42 173.194.116.195 173.194.116.195 54.229.115.42 } { puts "$ip2 - $ip1 == [::ip::distance1 $ip1 $ip2]; test: [expr {$ip2 == [::ip::nextIp $ip1 [::ip::distance1 $ip1 $ip2]]}]" } puts ===distance=== set t1 [time {::ip::distance1 10.11.12.13 10.11.42.42} 10000] set t2 [time {::ip::distance2 10.11.12.13 10.11.42.42} 10000] puts "robust + code reuse: $t1" puts "no arg checks: $t2" lassign $t1 t1 lassign $t2 t2 puts "no arg checks is [format %0.0f [expr {100-100.0*$t2/$t1}]]% faster,\ robustness costs [format %0.0f [expr {100.0*$t1/$t2-100}]]%" ************ in my VM: ===nextIp=== 255.255.255.255 - 0.0.0.0 == 4294967295; test: 1 0.0.0.0 - 255.255.255.255 == -4294967295; test: 1 10.11.42.42 - 10.11.12.13 == 7709; test: 1 10.11.12.13 - 10.11.42.42 == -7709; test: 1 173.194.116.195 - 54.229.115.42 == 1994195353; test: 1 54.229.115.42 - 173.194.116.195 == -1994195353; test: 1 ===distance=== robust + code reuse: 11.574 microseconds per iteration no arg checks: 3.0491 microseconds per iteration no arg checks is 74% faster, robustness costs 280%