Hexadecimal constant expressions hosed (and other probs)

From: Date: Wed, 26 Apr 2000 16:35:57 +0000
Subject: Hexadecimal constant expressions hosed (and other probs)
Groups: php.version4 
Request: Send a blank email to php-version4+get-14106@lists.php.net to get a copy of this message
Try something like this: $var1 = 0x7FFFFFFF; $var2 = 0xFFFFFFFF; $var3 = 0x8FFFFFFF; $var4 = 0x9FFFFFFF; $var5 = 0x8FFFFFF1; $var6 = 0x80000001; $var7 = $var1 + 0x7FFFFFFF; $var8 = (0x08FFFFFF << 4); $var9 = intval("AFFFFFFF", 16); echo $var1; echo "\n"; echo $var2; echo "\n"; echo $var3; echo "\n"; echo $var4; echo "\n"; echo $var5; echo "\n"; echo $var6; echo "\n"; echo $var7; echo "\n"; echo $var8; echo "\n"; echo $var9; echo "\n"; Results: 2147483647 0 0 0 0 0 4294967294 -1879048208 2147483647 ("\n" is separated so we don't accidentally cause the interpreter to convert the vars to strings before printing them) var1 prints out (correctly) 2147483647 var2 prints out 0, leading me to believe that PHP deals with integers as signed 32-bit, by default (even though FFFFFFFF should be -1, not 0). However, if that's the case, var4 through var6 should print out some version of a negative number. Instead they print out 0 as well. Then, looking at var7, we see that you *can* assign and print a value greater than 7FFFFFFF in unsigned mode, *if* you add to it to increment the value to that size, as opposed to directly assigned it using the '0x' operator. However, var8 shows that shifting left doesn't afford the same 'convenience'. Using intval doesn't help either. As you can see from var9, not only does intval not translate AFFFFFFF to the proper unsigned 32 bit integer, it doesn't even translate it to the proper signed 32 bit integer! In fact, it's as if the 32nd bit is forcibly turned off - *any* hex number beyond 7FFFFFFF is returned by intval as 2147483647. Lastly, I haven't figured out exactly under what circumstances this occurs, but frequently when I'm working with large 32 bit integers, the Apache process running my PHP script core dumps. These ambiguities, oddities, and downright bugs, make it extremely hard to work with 32 bit integers in any kind of reliable way. This is very disappointing. IP addresses are 32 bit integers. If one wishes to work with IP addresses at the bit level, one needs to be able to convert a dotted-quad notated IP address into a straight 32 bit integer. For those that may not understand this, here's why: Suppose you have a 25 bit network subnet beginning at 192.91.199.128, the subnet mask for a 25 bit subnet being 255.255.255.128. 192.91.199.128 as an integer is 3227240320, which in binary is 11000000010110111100011110000000 255.255.255.128 as in integer is 4294967168, which in binary is 11111111111111111111111110000000 Now, applying the bitwise AND operation to the subnet mask and a connecting IP address, we can determine whether or not that IP address is in this network subnet. If the result is equal to the network subnet, the connecting IP address is within it. If it is not equal to the network subnet, the IP address is not within it. Suppose we get a connection from 192.91.199.129: 11000000010110111100011110000001 & 11111111111111111111111110000000 ----------------------------------- 11000000010110111100011110000000 Which is the same as the network subnet, so this IP address is within the subnet. Suppose we get a connection from 192.91.199.127: 11000000010110111100011101111111 & 11111111111111111111111110000000 ----------------------------------- 11000000010110111100011100000000 Which is not the same as the network subnet (but 25 is off), so this IP address is not within the subnet. These kind of operations are extremely common in systems that deal with IP addresses regularly (routers for example). In my case, I have a set of subnets with their corresponding subnet masks. I want to be able to do something like this: $localnets = array ( "192.91.199.128/25", "192.91.55.64/26" ); foreach ($localnets as $net) { list ($addr, $bits) = explode("/", $net); // convert the dotted quad network address into a 32 bit integer list ($a, $b, $c, $d) = explode(".", $addr); $numaddr = ($a << 24) + ($b << 16) + ($c << 8) + $d; // convert the dotted quad remote address into a 32 bit integer list ($a, $b, $c, $d) = explode(".", $REMOTE_ADDR); $rnumaddr = ($a << 24) + ($b << 16) + ($c << 8) + $d; $mask = bits2mask($bits); echo ((($rnumaddr & $mask) == $numaddr) ? "Local" : "Remote") . "\n"; } function bits2mask ($bits) { $mask = 0xFFFFFFFF; $mask <<= $bits; // This should put 32 - $bits zeros at the end of // $mask, thus giving me the proper subnet mask. // Instead, since the assignment of $mask to // 0xFFFFFFFF yields 0, the algorithm is hosed. // To work around this, I can do: $mask = 0x7FFFFFFF + 0x7FFFFFFF + 1; // achieves 0xFFFFFFFF $mask <<= $bits; // but this causes a core dump. return $mask; } I'm contemplating looking at the PHP code that causes all these bugs - to fix it, and for my own amusement ;-) - Matthew H. North Programmer/Engineering CTSnet Internet Services Tel: (858) 637-3600 Fax: (858) 637-3630 mailto:ctsmhn@cts.com

« previous php.version4 (#14106) next »