title: CIDR bitwise operations - could I be a bit wiser?
body: <p>I am building a class to represent an IPv4 subnet. I am storing the network address and the subnet mask as 4 byte binary strings, which are built during the constructor based on the arguments. One of the representations I would like the constructor to accept is <a href="http://en.wikipedia.org/wiki/CIDR_notation" rel="noreferrer">CIDR notation</a>.</p>
  
  <p>My bitwise operations are a little rusty, and where I have got stuck is in converting a decimal integer CIDR representation of the subnet mask into a 4 byte binary string, and vice versa. I have also found I am unable to perform left/right shifts on string - which I'm sure I have successfully done before?</p>
  
  <hr>
  
  <p>I have managed to get the conversion to the binary string to work with the following code:</p>
  
  <pre><code>// An example input value.
  $mask = 24; // 255.255.255.0
  
  if ($mask &lt; 0 || $mask &gt; 32) {
    // Invalid prefix size
    throw new RangeException('Invalid CIDR prefix size');
  } else if ($mask === 0) {
    // Handle 0
    $mask = "\x00\x00\x00\x00";
  } else {
    // Left-pad a 4-byte string with $mask set bits
    $mask = pack('N', (0x01 &lt;&lt; 31) &gt;&gt; ($mask - 1));
  }
  </code></pre>
  
  <p>I don't like this logic for two reasons:</p>
  
  <ul>
  <li>I don't like treating <code>0</code> as a special case</li>
  <li>I don't like the right-shift followed by a left-shift</li>
  </ul>
  
  <p>I feel sure there is a way to do this more efficiently in a way that will handle <code>0</code> correctly without treating it as a special case.</p>
  
  <hr>
  
  <p>When converting a binary string back to the decimal representation of a CIDR prefix size, I am currently using the code below. I have another very similar block of code when validating a subnet mask provided in other formats to ensure the set bits are contiguous.</p>
  
  <pre><code>// An example input value.
  $mask = "\xff\xff\xff\x00"; // /24
  
  // Convert the binary string to an int so bit shifts will work
  $mask = current(unpack('N', $mask));
  
  // A counter to represent the CIDR
  $cidr = 0;
  
  // Loop and check each bit
  for ($i = 31; $i &gt; 0; $i--) {
    if (($mask &gt;&gt; $i) &amp; 0x01) {
      $cidr++;
    } else {
      break;
    }
  }
  
  // Return the result
  return $cidr;
  </code></pre>
  
  <p>I don't like this because of the loop - I feel sure there is a more intelligent bitwise way to do it.</p>
  
  <hr>
  
  <p>Is there a more intelligent way to do either of these tasks?</p>
  
  <p>Thoughts/suggestions/general abuse please...</p>
  
  <hr>
  
  <p>EDIT:</p>
  
  <p>Any solutions need to work on PHP 4.3.10 and upwards, and must work on both 32- and 64-bit platforms. Please keep in mind that all integers in PHP are signed, and on a 32-bit platform anything <code>&gt;= 0x80000000</code> will be stored as a double (and will therefore not play nice with bitwise operations).</p>