X-Git-Url: http://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2FIPv4Address.hs;h=7c4436781099b5be4ae98434b0455772145e9ab8;hb=942b8ef3bc5830ca0defa62342d55550aea59934;hp=b46f18c453ea967d19291810cd824a546975f5c9;hpb=a6569af040d40db73dfd32677893916638c0d56a;p=hath.git diff --git a/src/IPv4Address.hs b/src/IPv4Address.hs index b46f18c..7c44367 100644 --- a/src/IPv4Address.hs +++ b/src/IPv4Address.hs @@ -1,14 +1,16 @@ -module IPv4Address -( ipv4address_tests, +module IPv4Address( + ipv4address_properties, + ipv4address_tests, IPv4Address(..), - max_address, - min_address, most_sig_bit_different, ) where import Data.Maybe (fromJust) -import Test.HUnit -import Test.QuickCheck +import Test.HUnit (assertEqual) +import Test.Framework (Test, testGroup) +import Test.Framework.Providers.HUnit (testCase) +import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.QuickCheck (Arbitrary(..), Gen, Property, (==>)) import Maskable import Maskbits @@ -163,18 +165,64 @@ instance Maskable IPv4Address where new_addr3 { octet1 = (apply_mask oct1 Zero bit) } +instance Bounded IPv4Address where + -- | The minimum possible IPv4 address, 0.0.0.0. + minBound = IPv4Address minBound minBound minBound minBound --- | The minimum possible IPv4 address, 0.0.0.0. -min_address :: IPv4Address -min_address = - IPv4Address min_octet min_octet min_octet min_octet + -- | The maximum possible IPv4 address, 255.255.255.255. + maxBound = IPv4Address maxBound maxBound maxBound maxBound --- | The maximum possible IPv4 address, 255.255.255.255. -max_address :: IPv4Address -max_address = - IPv4Address max_octet max_octet max_octet max_octet +-- | Convert @addr@ to an 'Int' by converting each octet to an 'Int' +-- and shifting the result to the left by 0,8.16, or 24 bits. +ipv4address_to_int :: IPv4Address -> Int +ipv4address_to_int addr = + (shifted_oct1) + (shifted_oct2) + (shifted_oct3) + oct4 + where + oct1 = octet_to_int (octet1 addr) + oct2 = octet_to_int (octet2 addr) + oct3 = octet_to_int (octet3 addr) + oct4 = octet_to_int (octet4 addr) + + shifted_oct1 = oct1 * 2^(24 :: Integer) + shifted_oct2 = oct2 * 2^(16 :: Integer) + shifted_oct3 = oct3 * 2^(8 :: Integer) + + + +-- | Convert an 'Int' @x@ to an 'IPv4Address'. Each octet of @x@ is +-- right-shifted by the appropriate number of bits, and the fractional +-- part is dropped. +ipv4address_from_int :: Int -> Maybe IPv4Address +ipv4address_from_int x + | (x < 0) || (x > 2^(32 :: Integer) - 1) = Nothing + | otherwise = do + -- If the algebra is right, none of these octet_from_int calls + -- below can fail since 0 <= x <= 2^32 - 1. + oct1 <- octet_from_int shifted_x1 + oct2 <- octet_from_int shifted_x2 + oct3 <- octet_from_int shifted_x3 + oct4 <- octet_from_int x4 + return $ IPv4Address oct1 oct2 oct3 oct4 + where + -- Chop off the higher octets. x1 = x `mod` 2^32, would be + -- redundant. + x2 = x `mod` 2^(24 :: Integer) + x3 = x `mod` 2^(16 :: Integer) + x4 = x `mod` 2^(8 :: Integer) + -- Perform right-shifts. x4 doesn't need a shift. + shifted_x1 = x `quot` 2^(24 :: Integer) + shifted_x2 = x2 `quot` 2^(16 :: Integer) + shifted_x3 = x3 `quot` 2^(8 :: Integer) + + +instance Enum IPv4Address where + -- We're supposed to throw a runtime error if you call (succ + -- maxBound), so the fromJust here doesn't introduce any additional + -- badness. + toEnum = fromJust . ipv4address_from_int + fromEnum = ipv4address_to_int -- | Given two addresses, find the number of the most significant bit -- where they differ. If the addresses are the same, return @@ -290,6 +338,29 @@ most_sig_bit_different addr1 addr2 oct4b = (octet4 addr2) +-- Test lists. +ipv4address_tests :: Test +ipv4address_tests = + testGroup "IPv4 Address Tests" [ + test_enum, + test_maxBound, + test_minBound, + test_most_sig_bit_different1, + test_most_sig_bit_different2 ] + +ipv4address_properties :: Test +ipv4address_properties = + testGroup + "IPv4 Address Properties " + [ testProperty + "fromEnum/toEnum are inverses" + prop_from_enum_to_enum_inverses ] + +-- QuickCheck properties +prop_from_enum_to_enum_inverses :: Int -> Property +prop_from_enum_to_enum_inverses x = + (0 <= x) && (x <= 2^(32 :: Integer) - 1) ==> + fromEnum (toEnum x :: IPv4Address) == x -- HUnit Tests mk_testaddr :: Int -> Int -> Int -> Int -> IPv4Address @@ -301,13 +372,39 @@ mk_testaddr a b c d = oct3 = fromJust $ octet_from_int c oct4 = fromJust $ octet_from_int d +test_minBound :: Test +test_minBound = + testCase desc $ assertEqual desc expected actual + where + desc = "minBound should be 0.0.0.0" + expected = mk_testaddr 0 0 0 0 + actual = minBound :: IPv4Address + +test_maxBound :: Test +test_maxBound = + testCase desc $ assertEqual desc expected actual + where + desc = "maxBound should be 255.255.255.255" + expected = mk_testaddr 255 255 255 255 + actual = maxBound :: IPv4Address + +test_enum :: Test +test_enum = + testCase desc $ assertEqual desc expected actual + where + desc = "enumerating a /24 gives the correct addresses" + expected = ["192.168.0." ++ (show x) | x <- [0..255::Int] ] + lb = mk_testaddr 192 168 0 0 + ub = mk_testaddr 192 168 0 255 + actual = map show [lb..ub] test_most_sig_bit_different1 :: Test test_most_sig_bit_different1 = - TestCase $ assertEqual "10.1.1.0 and 10.1.0.0 differ in bit 24" + testCase desc $ assertEqual desc TwentyFour bit where + desc = "10.1.1.0 and 10.1.0.0 differ in bit 24" addr1 = mk_testaddr 10 1 1 0 addr2 = (mk_testaddr 10 1 0 0) bit = most_sig_bit_different addr1 addr2 @@ -316,16 +413,13 @@ test_most_sig_bit_different1 = test_most_sig_bit_different2 :: Test test_most_sig_bit_different2 = - TestCase $ assertEqual "10.1.2.0 and 10.1.1.0 differ in bit 23" + testCase desc $ assertEqual desc TwentyThree bit where + desc = "10.1.2.0 and 10.1.1.0 differ in bit 23" addr1 = mk_testaddr 10 1 2 0 addr2 = mk_testaddr 10 1 1 0 bit = most_sig_bit_different addr1 addr2 -ipv4address_tests :: [Test] -ipv4address_tests = - [ test_most_sig_bit_different1, - test_most_sig_bit_different2 ]