Page 2 of 3

Re: [Challenge] IPv4 subnet mask validator

Posted: Fri Apr 02, 2010 3:36 am
by VladSun
My solution is:

Code: Select all

/**
 * @param String $mask - decimal, dot separated representation of an IPv4 subnet mask - e.g. "255.255.255.0"
 * @return bool
 */
function isValidIPv4Mask($mask)
{
 	$mask = ip2long($mask);

	if ($mask == -1 || !$mask) return false;

	while (!($mask & 1))
		$mask >>= 1;

	return $mask == -1;
}
Probably it won't behave properly on 64 bit PHP versions:
on 64 bits system ip2long ONLY RETURNS POSITIVE VALUES

so

<?php
echo ip2long('200.200.200.200');
?>

will output -926365496 on a 32 bits system and 3368601800 on a 64 bits system
so it will need 0xFFFFFFFF instead of -1 comparison.

Re: [Challenge] IPv4 subnet mask validator

Posted: Fri Apr 02, 2010 5:44 am
by Weirdan
Probably it won't behave properly on 64 bit PHP versions:
I think this is easy to fix - just invert the mask and then invert the checks. Here's intsize-independent version:

Code: Select all

$tests = array(
    '255.255.255.0' => true,
    '255.255.0.255' => false,
    '0.0.0.0' => false,
    '255.255.255.255' => false,
    '0.255.255.255' => false,
);
function isValidIPv4Mask($mask) {
    $mask = ~ip2long($mask) & (int) 0xffffffff;

    if ($mask == (int) 0xffffffff || !$mask) return false;

    while (($mask & 1))
        $mask >>= 1;

    return $mask == 0;
}

var_dump('Int size: ' . PHP_INT_SIZE);
foreach ($tests as $mask => $expected) {
    assert(var_export($expected, true) . ' === isValidIPv4Mask(' . var_export($mask, true) . ')');
}
var_dump('done');
string(11) "Int size: 4"
string(4) "done"
string(11) "Int size: 8"
string(4) "done"

Re: [Challenge] IPv4 subnet mask validator

Posted: Fri Apr 02, 2010 8:57 am
by VladSun
Nice fix, Weirdan :)

Re: [Challenge] IPv4 subnet mask validator

Posted: Wed Apr 07, 2010 10:46 am
by timWebUK
Weirdan wrote:
timWebUK wrote:Am I missing something but...
255.255.255. (0, 128, 192, 224, 240, 248, 252, 254)
Are all valid subnet masks...
They are. What made you think they are not considered as such?
I guess I misunderstood "A valid subnet mask value is a one that has leftmost bits set to '1' and the rest rightmost bits set to '0'."

Re: [Challenge] IPv4 subnet mask validator

Posted: Wed Apr 07, 2010 3:31 pm
by AbraCadaver
VladSun wrote:Very creative, AbraCadaver :)

The first one fails on 0.255.255.0 mask.

The second one is OK and its time is ~0.8 sec :) It also shares the same idea as the one used in my solution :)

The third one fails on 255.255.255.254 mask :(
First one used decbin() that removes the preceding zeros. Replaced with sprintf():

Code: Select all

function isValidIPv4Mask($mask)
{
    return ($bin=sprintf('%032b',ip2long($mask))) && strpos($bin,'0') && !strpos($bin,'01');
}
Third one used a + instead of *, easy fix:

Code: Select all

function isValidIPv4Mask($mask)
{
    return (bool)preg_match('/^[1]+0[^1]*$/',sprintf('%032b',ip2long($mask)));
}

Re: [Challenge] IPv4 subnet mask validator

Posted: Wed Apr 07, 2010 6:43 pm
by minorDemocritus
timWebUK wrote:
Weirdan wrote:
timWebUK wrote:Am I missing something but...
255.255.255. (0, 128, 192, 224, 240, 248, 252, 254)
Are all valid subnet masks...
They are. What made you think they are not considered as such?
I guess I misunderstood "A valid subnet mask value is a one that has leftmost bits set to '1' and the rest rightmost bits set to '0'."
(dots added for clarity)
11111111.11111111.11111111.00000000 = 255.255.255.0
11111111.11111111.00000000.00000000 = 255.255.0.0
11111111.11111111.11110000.00000000 = 255.255.240.0
11111111.11111111.11111111.11111110 = 255.255.255.254

These are all valid subnet masks. Notice the leftmost bits are 1, and rightmost bits are 0.

11111111.11111111.01111111.00000000 = 255.255.127.0 is NOT a valid mask.

Re: [Challenge] IPv4 subnet mask validator

Posted: Thu Apr 08, 2010 4:27 am
by VladSun
@AbraCadaver:

All tests passed :)

Speed test results:
- first function : ~ 1.08 sec;
- second one : ~ 1.08 sec :)

I expected you would write a binary logic solution ;)
"There are only 10 types of people in the world: Those who understand binary, and those who don't."
:twisted:

Re: [Challenge] IPv4 subnet mask validator

Posted: Thu Apr 08, 2010 4:43 am
by VladSun
Weirdan wrote:
Probably it won't behave properly on 64 bit PHP versions:
I think this is easy to fix - just invert the mask and then invert the checks.
I've just tested your function and it appears it has a minor problem - if ip2long() returns false (i.e. improper IPv4 format) a fatal error is raised - "Unsupported operand types".

I don't have a 64 bit PHP version to test right now, but it seems to me that one doesn't need to invert the logic - I think the (int)0xFFFFFFFF typecast is just enough (it will return -1 on 32 and 0xFFFFFFFF on 64 bit platforms):

Code: Select all

function isValidIPv4Mask($mask)
{
        $mask = ip2long($mask);

        if ($mask == (int) 0xffffffff || !$mask) return false;

        while (!($mask & 1))
                $mask >>= 1;

        return $mask == (int) 0xffffffff;
}

Re: [Challenge] IPv4 subnet mask validator

Posted: Thu Apr 08, 2010 4:45 am
by timWebUK
minorDemocritus wrote:
timWebUK wrote:
Weirdan wrote: They are. What made you think they are not considered as such?
I guess I misunderstood "A valid subnet mask value is a one that has leftmost bits set to '1' and the rest rightmost bits set to '0'."
(dots added for clarity)
11111111.11111111.11111111.00000000 = 255.255.255.0
11111111.11111111.00000000.00000000 = 255.255.0.0
11111111.11111111.11110000.00000000 = 255.255.240.0
11111111.11111111.11111111.11111110 = 255.255.255.254

These are all valid subnet masks. Notice the leftmost bits are 1, and rightmost bits are 0.

11111111.11111111.01111111.00000000 = 255.255.127.0 is NOT a valid mask.
Thanks for the clarification!

Re: [Challenge] IPv4 subnet mask validator

Posted: Thu Apr 08, 2010 7:46 am
by AbraCadaver
VladSun wrote:@AbraCadaver:

All tests passed :)

Speed test results:
- first function : ~ 1.08 sec;
- second one : ~ 1.08 sec :)

I expected you would write a binary logic solution ;)
"There are only 10 types of people in the world: Those who understand binary, and those who don't."
:twisted:
Not until I can think of one without a loop, or maybe just a loop with no other code outside the loop.

Re: [Challenge] IPv4 subnet mask validator

Posted: Thu Apr 08, 2010 8:17 am
by Weirdan
VladSun wrote: I don't have a 64 bit PHP version to test right now, but it seems to me that one doesn't need to invert the logic - I think the (int)0xFFFFFFFF typecast is just enough (it will return -1 on 32 and 0xFFFFFFFF on 64 bit platforms):
No, that wouldn't work, because the algorithm you use relies on right shift operator extending the sign bit, and ip2long on 64bit platform returns positive numbers only. On 32bit platform for valid ip mask it always return negative number. See for yourself:

Code: Select all

string(11) "Int size: 8"

Warning: assert(): Assertion "true === isValidIPv4Mask('255.255.255.0')" failed in /home/weirdan/q.php on line 23
string(4) "done"
using these tests:

Code: Select all

$tests = array(
    '255.255.255.0' => true,
    '255.255.0.255' => false,
    '0.0.0.0' => false,
    '255.255.255.255' => false,
    '0.255.255.255' => false,
);
var_dump('Int size: ' . PHP_INT_SIZE);
foreach ($tests as $mask => $expected) {
    assert(var_export($expected, true) . ' === isValidIPv4Mask(' . var_export($mask, true) . ')');
}
var_dump('done');

Re: [Challenge] IPv4 subnet mask validator

Posted: Fri Apr 23, 2010 8:28 am
by Mordred
Considering this:
In this challenge, these subnet masks are also considered invalid:
255.255.255.255 (11111111 11111111 11111111 11111111)
0.0.0.0 (00000000 00000000 00000000 00000000)
"Trick" solution:

Code: Select all

function isValidIPv4Mask($mask)
{
    $n = ip2long($mask);
    return $n===false ? $n : !(~($n-1) & ~($n));
}
But of course the "boring" solution is faster:

Code: Select all

$s_ValidMasks = Array(
'255.255.255.255' => true,
'255.255.255.254' => true,
'255.255.255.252' => true,
'255.255.255.248' => true,
'255.255.255.240' => true,
'255.255.255.224' => true,
'255.255.255.192' => true,
'255.255.255.128' => true,
'255.255.255.0' => true,
'255.255.254.0' => true,
'255.255.252.0' => true,
'255.255.248.0' => true,
'255.255.240.0' => true,
'255.255.224.0' => true,
'255.255.192.0' => true,
'255.255.128.0' => true,
'255.255.0.0' => true,
'255.254.0.0' => true,
'255.252.0.0' => true,
'255.248.0.0' => true,
'255.240.0.0' => true,
'255.224.0.0' => true,
'255.192.0.0' => true,
'255.128.0.0' => true,
'255.0.0.0' => true,
'254.0.0.0' => true,
'252.0.0.0' => true,
'248.0.0.0' => true,
'240.0.0.0' => true,
'224.0.0.0' => true,
'192.0.0.0' => true,
'128.0.0.0' => true,
'0.0.0.0' => true,
);

function isValidIPv4Mask($mask)
{
    global $s_ValidMasks;
    return isset($s_ValidMasks[$mask]);
}

Re: [Challenge] IPv4 subnet mask validator

Posted: Fri Apr 23, 2010 5:55 pm
by AbraCadaver
Mordred wrote:Considering this:
In this challenge, these subnet masks are also considered invalid:
255.255.255.255 (11111111 11111111 11111111 11111111)
0.0.0.0 (00000000 00000000 00000000 00000000)
"Trick" solution:

Code: Select all

function isValidIPv4Mask($mask)
{
    $n = ip2long($mask);
    return $n===false ? $n : !(~($n-1) & ~($n));
}
Unfortunately the "trick" is failing on several:
[text]
255.255.255.0 true
255.255.0.255 false
0.0.0.0 true
255.255.255.255 true
0.255.255.255 false
255 false
0 true[/text]

Re: [Challenge] IPv4 subnet mask validator

Posted: Fri Apr 23, 2010 6:30 pm
by Apollo
Bit trickery FTW:

Code: Select all

function isValidIPv4Mask($mask)
{
  $m = ~ip2long($mask);	
  return $m && ~$m && !($m&($m+1));
}

Re: [Challenge] IPv4 subnet mask validator

Posted: Wed Apr 28, 2010 7:18 am
by Weirdan
Mordred wrote:But of course the "boring" solution is faster
Are you sure? In my tests table lookup was slower than Vlad's solution.