X-Git-Url: http://gitweb.michael.orlitzky.com/?p=hath.git;a=blobdiff_plain;f=src%2FIPv4Address.hs;h=7c4436781099b5be4ae98434b0455772145e9ab8;hp=dac7ddaf0596e9fba46b077d4321e88149d23b88;hb=942b8ef3bc5830ca0defa62342d55550aea59934;hpb=81f6f0ca67347de6b3e3f57b5b50bb543cc7c146 diff --git a/src/IPv4Address.hs b/src/IPv4Address.hs index dac7dda..7c44367 100644 --- a/src/IPv4Address.hs +++ b/src/IPv4Address.hs @@ -1,8 +1,7 @@ -module IPv4Address -( ipv4address_tests, +module IPv4Address( + ipv4address_properties, + ipv4address_tests, IPv4Address(..), - max_address, - min_address, most_sig_bit_different, ) where @@ -10,7 +9,8 @@ import Data.Maybe (fromJust) import Test.HUnit (assertEqual) import Test.Framework (Test, testGroup) import Test.Framework.Providers.HUnit (testCase) -import Test.QuickCheck (Arbitrary(..), Gen) +import Test.Framework.Providers.QuickCheck2 (testProperty) +import Test.QuickCheck (Arbitrary(..), Gen, Property, (==>)) import Maskable import Maskbits @@ -165,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 @@ -292,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 @@ -303,6 +372,31 @@ 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 = @@ -329,8 +423,3 @@ test_most_sig_bit_different2 = bit = most_sig_bit_different addr1 addr2 -ipv4address_tests :: Test -ipv4address_tests = - testGroup "IPv4 Address Tests" [ - test_most_sig_bit_different1, - test_most_sig_bit_different2 ]