packages feed

ip 0.9.1 → 0.9.2

raw patch · 3 files changed

+92/−22 lines, 3 filesdep ~aesondep ~vector

Dependency ranges changed: aeson, vector

Files

ip.cabal view
@@ -1,5 +1,5 @@ name:                ip-version:             0.9.1+version:             0.9.2 synopsis:            Library for IP and MAC addresses homepage:            https://github.com/andrewthad/haskell-ip#readme license:             BSD3
src/Net/IPv6/Text.hs view
@@ -1,18 +1,45 @@ {-# LANGUAGE BangPatterns #-}  module Net.IPv6.Text-  ( parser+  ( encode+  , parser   ) where  import Prelude hiding (print) import Net.Types (IPv6(..))+import Net.IPv6 (toWord16s) import Data.Bits+import Data.List (intercalate, group) import Data.Word import Control.Applicative+import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Attoparsec.Text as Atto-import Debug.Trace+import Numeric (showHex) +-- | Encodes the IP, using zero-compression on the leftmost-longest string of+-- zeroes in the address.+encode :: IPv6 -> Text+encode ip = toText [w1, w2, w3, w4, w5, w6, w7, w8]+  where+  (w1, w2, w3, w4, w5, w6, w7, w8) = toWord16s ip+  toText ws = Text.pack $ intercalate ":" $ expand 0 longestZ grouped+    where+    expand _ 8 _ = ["::"]+    expand _ _ [] = []+    expand i longest ((x, len):wsNext)+        -- zero-compressed group:+        | x == 0 && len == longest =+            -- first and last need an extra colon since there's nothing+            -- to concat against+            (if i == 0 || (i+len) == 8 then ":" else "")+            : expand (i+len) 0 wsNext+        -- normal group:+        | otherwise = replicate len (showHex x "") ++ expand (i+len) longest wsNext+    longestZ = maximum . (0:) . map snd . filter ((==0) . fst) $ grouped+    grouped = map (\x -> (head x, length x)) (group ws)++         parser :: Atto.Parser IPv6 parser = do   s <- start@@ -41,27 +68,12 @@             _ <- Atto.anyChar -- should be a colon             if colonIndex == currentIndex               then fmap ResWord Atto.hexadecimal <|> pure ResDone-                -- do-                -- md <- Atto.peekChar-                -- case md of-                --   Nothing -> return ResDone-                --   Just d -> fmap ResWord Atto.hexadecimal <|> pure-								-- 		then return ResColon-								-- 		else fmap ResWord Atto.hexadecimal               else do                 d <- Atto.peekChar'                 if d == ':'                   then return ResColon                   else fmap ResWord Atto.hexadecimal-              -- else if colonIndex == currentIndex-              --   then fmap ResWord Atto.hexadecimal <|> (ResDone <$ Atto.anyChar)-              --   else fmap ResWord Atto.hexadecimal           else return ResDone-    -- fmap ResWord Atto.hexadecimal <|> (ResColon <$ Atto.char ':')-    -- ) <|> pure ResDone-    -- r <- (do _ <- Atto.char ':'-    --          fmap ResWord Atto.hexadecimal <|> (ResColon <$ Atto.char ':')-    --      ) <|> pure ResDone     case r of       ResDone -> pure (S colonIndex currentIndex ws)       ResColon -> if alreadySet colonIndex@@ -70,7 +82,12 @@       ResWord w -> restrictTo16 msg w >> go colonIndex (currentIndex + 1) (w : ws)  toIPv6 :: S -> Maybe IPv6-toIPv6 (S colonIndex total input) = go 0 0 input+toIPv6 (S colonIndex total input) = case compare total 8 of+  EQ -> if colonIndex == (-1)+    then go 0 0 input+    else Nothing+  GT -> Nothing+  LT -> go 0 0 input   where   revColonIndex = total - colonIndex   spacesToSkip = 8 - total@@ -111,9 +128,9 @@ --   The third is a reversed list of the 16s --   that comprise the ipv6 address. data S = S-  { sDoubleColon :: {-# UNPACK #-} !Int-  , sTotal :: {-# UNPACK #-} !Int-  , sRevWords :: ![Word64]+  { _sDoubleColon :: {-# UNPACK #-} !Int+  , _sTotal :: {-# UNPACK #-} !Int+  , _sRevWords :: ![Word64]   } deriving (Show,Read)  data Res
test/Test.hs view
@@ -10,6 +10,7 @@ import Numeric                              (showHex) import Test.QuickCheck.Property             (failed,succeeded,Result(..)) import Data.Word+import Data.Bifunctor  import Net.Types (IPv4(..),IPv4Range(..),Mac(..),IPv6(..)) import qualified Data.Text as Text@@ -84,6 +85,8 @@       ]     , testGroup "IPv6 encode/decode"       [ testCase "Parser Test Cases" testIPv6Parser+      , testCase "Encode test cases" testIPv6Encode+      , testCase "Parser Failure Test Cases" testIPv6ParserFailure       ]     ]   , testGroup "IP Range Operations"@@ -184,6 +187,56 @@         (IPv6Text.parser <* AT.endOfInput)         (Text.pack str)       )++testIPv6ParserFailure :: Assertion+testIPv6ParserFailure = do+  go "1111:2222:3333:4444:5555:6666::7777:8888"+  go "1111:2222:3333:4444:5555:6666:7777:8888:9999"+  go "1111:2222:3333:4444:5555:6666:7777:8888::9999"+  where+  go str =+    Left ()+    @?= bimap (\_ -> ()) HexIPv6+      (AT.parseOnly+        (IPv6Text.parser <* AT.endOfInput)+        (Text.pack str)+      )++testIPv6Encode :: Assertion+testIPv6Encode = do++    -- degenerate cases:+    "::" `roundTripsTo` "::"+    "1234::" `roundTripsTo` "1234::"+    "::1234" `roundTripsTo` "::1234"++    -- zero-compression works:+    "1234:1234:0000:0000:0000:0000:3456:3434" `roundTripsTo` "1234:1234::3456:3434"++    -- picks first case:+    "1234:0000:1234:0000:1234:0000:0123:1234" `roundTripsTo` "1234::1234:0:1234:0:123:1234"++    -- picks longest case:+    "1234:0000:1234:0000:0:0000:0123:1234" `roundTripsTo` "1234:0:1234::123:1234"++    -- can exclude all but first and last:+    "1234::1234" `roundTripsTo` "1234::1234"++    -- prefers leftmost part to zero-compress:+    "1:2:0:0:5::8" `roundTripsTo` "1:2::5:0:0:8"++    -- can work with no zeroes:+    "1:2:3:4:5:6:7:8" `roundTripsTo` "1:2:3:4:5:6:7:8"++    -- works with only first or last:+    "::2:3:4:5:6:7:8" `roundTripsTo` "::2:3:4:5:6:7:8"+    "1:2:3:4:5:6:7::" `roundTripsTo` "1:2:3:4:5:6:7::"++   where+   roundTripsTo s sExpected =+     case AT.parseOnly (IPv6Text.parser <* AT.endOfInput) (Text.pack s) of+        Right result -> IPv6Text.encode result @?= Text.pack sExpected+        Left failMsg -> fail failMsg -- parse shouldn't fail here  textBadIPv4 :: [String] textBadIPv4 =