ip 1.6.0 → 1.7.0
raw patch · 8 files changed
+55/−158 lines, 8 filesdep ~basedep ~byteslicedep ~vectorPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, byteslice, vector
API changes (from Hackage documentation)
- Net.IPv4: instance Data.Bits.Bits Net.IPv4.IPv4Range
- Net.IPv4: instance Data.Bits.FiniteBits Net.IPv4.IPv4Range
- Net.IPv6: instance GHC.Num.Num Net.IPv6.IPv6
- Net.IPv6: instance GHC.Real.Integral Net.IPv6.IPv6
- Net.IPv6: instance GHC.Real.Real Net.IPv6.IPv6
+ Net.IP: instance Data.Data.Data Net.IP.IP
+ Net.IP: instance GHC.Arr.Ix Net.IP.IP
+ Net.IPv4: instance Data.Data.Data Net.IPv4.IPv4
+ Net.IPv4: instance Data.Data.Data Net.IPv4.IPv4Range
+ Net.IPv4: instance GHC.Arr.Ix Net.IPv4.IPv4
+ Net.IPv6: instance Data.Aeson.Types.FromJSON.FromJSON Net.IPv6.IPv6Range
+ Net.IPv6: instance Data.Aeson.Types.ToJSON.ToJSON Net.IPv6.IPv6Range
+ Net.IPv6: instance Data.Data.Data Net.IPv6.IPv6
+ Net.IPv6: instance Data.Data.Data Net.IPv6.IPv6Range
+ Net.IPv6: instance GHC.Arr.Ix Net.IPv6.IPv6
+ Net.Mac: instance Data.Data.Data Net.Mac.Mac
+ Net.Mac: instance Data.Data.Data Net.Mac.MacCodec
+ Net.Mac: instance Data.Data.Data Net.Mac.MacGrouping
+ Net.Mac: instance GHC.Arr.Ix Net.Mac.Mac
Files
- ip.cabal +4/−4
- src/Net/IP.hs +4/−1
- src/Net/IPv4.hs +11/−69
- src/Net/IPv6.hs +15/−2
- src/Net/Mac.hs +6/−3
- test/Net/IPv4/RangeSpec.hs +0/−67
- test/Net/IPv4Spec.hs +7/−7
- test/Test.hs +8/−5
ip.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: ip-version: 1.6.0+version: 1.7.0 synopsis: Library for IP and MAC addresses homepage: https://github.com/andrewthad/haskell-ip#readme license: BSD-3-Clause@@ -48,7 +48,7 @@ build-depends: , aeson >= 1.0 && < 1.5 , attoparsec >= 0.13 && < 0.14- , base >= 4.9.1.0 && < 5+ , base >= 4.9 && < 5 , byteslice >= 0.1.2 && < 0.2 , bytesmith >= 0.3 && < 0.4 , bytestring >= 0.10.8 && < 0.11@@ -56,7 +56,7 @@ , hashable >= 1.2 && < 1.4 , natural-arithmetic >= 0.1 && <0.2 , primitive >= 0.6.4 && < 0.8- , small-bytearray-builder >= 0.2.1 && <0.3+ , small-bytearray-builder >= 0.2.1 && <0.4 , text >= 1.2 && < 1.3 , text-short >= 0.1.3 && < 0.2 , vector >= 0.11 && < 0.13@@ -101,7 +101,6 @@ , ip , wide-word other-modules:- Net.IPv4.RangeSpec Net.IPv4Spec ghc-options: -Wall -O2 default-language: Haskell2010@@ -117,6 +116,7 @@ , doctest >= 0.10 , ip , wide-word+ , vector default-language: Haskell2010 benchmark criterion
src/Net/IP.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} @@ -56,6 +57,8 @@ import Data.Aeson (FromJSON(..),ToJSON(..)) import Data.Bits import Data.Coerce (coerce)+import Data.Data (Data)+import Data.Ix (Ix) import Data.Text (Text) import Data.WideWord (Word128(..)) import Data.Word (Word8,Word16)@@ -222,7 +225,7 @@ -- of this type. All functions and typeclass methods that convert -- 'IP' values to text will display it as an 'IPv4' address if possible. newtype IP = IP { getIP :: IPv6 }- deriving (Eq,Ord,Generic)+ deriving (Eq,Ord,Generic,Ix,Data) instance NFData IP
src/Net/IPv4.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MagicHash #-}@@ -94,10 +95,12 @@ import Control.Monad.ST (ST,runST) import Data.Aeson (FromJSON(..),ToJSON(..)) import Data.Aeson (ToJSONKey(..),FromJSONKey(..),ToJSONKeyFunction(..),FromJSONKeyFunction(..))-import Data.Bits ((.&.),(.|.),shiftR,shiftL,unsafeShiftR,complement,shift)+import Data.Bits (Bits(..)) import Data.ByteString (ByteString) import Data.Coerce (coerce)+import Data.Data (Data) import Data.Hashable+import Data.Ix (Ix) import Data.Primitive.Types (Prim) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8')@@ -156,7 +159,9 @@ -- >>> import qualified Data.Text.IO as T -- >>> instance Arbitrary IPv4 where { arbitrary = fmap IPv4 arbitrary } -- >>> instance Arbitrary IPv4Range where { arbitrary = IPv4Range <$> arbitrary <*> arbitrary }+-- >>> import qualified Data.Bytes.Chunks as Chunks + -- | Create an 'IPv4' address from four octets. The first argument -- is the most significant octet. The last argument is the least -- significant. Since IP addresses are commonly written using dot-decimal@@ -452,7 +457,7 @@ -- | Encode an 'IPv4' address as a unbounded byte array builder. ----- >>> UB.run 1 (byteArrayBuilderUtf8 (fromOctets 192 168 2 13))+-- >>> Chunks.concat (UB.run 1 (byteArrayBuilderUtf8 (fromOctets 192 168 2 13))) -- [0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x32, 0x2e, 0x31, 0x33] -- -- Note that period is encoded by UTF-8 as @0x2e@.@@ -533,11 +538,11 @@ -- convert between this and the lifted 'IPv4'. type IPv4# = Word# --- | Convert an unboxed IPv4 address to a boxed one. +-- | Convert an unboxed IPv4 address to a boxed one. box :: IPv4# -> IPv4 box w = IPv4 (W32# w) --- | Convert a boxed IPv4 address to an unboxed one. +-- | Convert a boxed IPv4 address to an unboxed one. unbox :: IPv4 -> IPv4# unbox (IPv4 (W32# w)) = w @@ -546,7 +551,7 @@ -- convert the underlying 'Word32' from host byte order to network byte -- order. newtype IPv4 = IPv4 { getIPv4 :: Word32 }- deriving (Eq,Ord,Enum,Bounded,Hashable,Generic,Prim,Storable)+ deriving (Bits.Bits,Bounded,Data,Enum,Eq,Bits.FiniteBits,Generic,Hashable,Ix,Ord,Prim,Storable) instance NFData IPv4 @@ -642,28 +647,6 @@ Nothing -> fail "Could not parse IPv4 address" Just addr -> return addr -ipv4Bitwise :: (Word32 -> Word32 -> Word32) -> IPv4 -> IPv4 -> IPv4-ipv4Bitwise fun l r = IPv4 $ (getIPv4 l) `fun` (getIPv4 r)---- | Note: we use network order (big endian) as opposed to host order (little--- endian) which differs from the underlying IPv4 type representation.-instance Bits.Bits IPv4 where- (.&.) = ipv4Bitwise (.&.)- (.|.) = ipv4Bitwise (.|.)- xor = ipv4Bitwise Bits.xor- complement = IPv4 . Bits.complement . getIPv4- shift ip i = IPv4 $ Bits.shift (getIPv4 ip) i- rotate ip i = IPv4 $ Bits.rotate (getIPv4 ip) i- bitSize = Bits.finiteBitSize- bitSizeMaybe = Bits.bitSizeMaybe . getIPv4- isSigned = Bits.isSigned . getIPv4- testBit ip i = Bits.testBit (getIPv4 ip) $ Bits.finiteBitSize ip - 1 - i- bit i = IPv4 $ Bits.bit $ Bits.finiteBitSize any - 1 - i- popCount = Bits.popCount . getIPv4--instance Bits.FiniteBits IPv4 where- finiteBitSize = Bits.finiteBitSize . getIPv4- ------------------------------------ -- Internal functions, not exported ------------------------------------@@ -1074,7 +1057,7 @@ data IPv4Range = IPv4Range { ipv4RangeBase :: {-# UNPACK #-} !IPv4 , ipv4RangeLength :: {-# UNPACK #-} !Word8- } deriving (Eq,Ord,Show,Read,Generic)+ } deriving (Eq,Ord,Show,Read,Generic,Data) instance NFData IPv4Range instance Hashable IPv4Range@@ -1196,47 +1179,6 @@ elemseq _ (IPv4Range a b) = GVector.elemseq (undefined :: UVector.Vector a) a . GVector.elemseq (undefined :: UVector.Vector b) b--rangeBitwise :: (IPv4 -> IPv4 -> IPv4) -> IPv4Range -> IPv4Range -> IPv4Range-rangeBitwise fun l r = range ip len- where- -- Normalise first- l' = normalize l- r' = normalize r- ip = (ipv4RangeBase l') `fun` (ipv4RangeBase r')- len = maximum [ipv4RangeLength l, ipv4RangeLength r]--rangeRebase :: (IPv4 -> IPv4) -> IPv4Range -> IPv4Range-rangeRebase fun r = range (fun $ ipv4RangeBase r) (ipv4RangeLength r)---- | Notes:------ * bit operations use network order (big endian),------ * do not operate on host bits,------ * return a normalized range dropping host bits,------ * and "promote operands" by extending the length to the larger of two--- ranges.----instance Bits.Bits IPv4Range where- (.&.) = rangeBitwise (.&.)- (.|.) = rangeBitwise (.|.)- xor = rangeBitwise Bits.xor- complement = rangeRebase Bits.complement- shift r i = rangeRebase (flip Bits.shift i) r- rotate r i = rangeRebase (flip Bits.rotate i) r- bitSize = Bits.finiteBitSize- bitSizeMaybe = Just . Bits.finiteBitSize- isSigned = Bits.isSigned . ipv4RangeBase- testBit ip i = Bits.testBit (ipv4RangeBase ip) i- bit i = IPv4Range (Bits.bit i) $ fromIntegral $ i + 1- popCount = Bits.popCount . ipv4RangeBase . normalize---- | Note: the size is determined by the range length-instance Bits.FiniteBits IPv4Range where- finiteBitSize = fromIntegral . ipv4RangeLength ----------------- -- Internal Stuff
src/Net/IPv6.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-}@@ -66,9 +67,12 @@ import Control.Applicative import Control.DeepSeq (NFData)+import Control.Monad (mzero) import Control.Monad.ST (ST) import Data.Bits import Data.Char (chr)+import Data.Data (Data)+import Data.Ix (Ix) import Data.List (intercalate, group) import Data.Primitive (MutablePrimArray) import Data.Primitive.Types (Prim)@@ -115,7 +119,7 @@ -- | A 128-bit Internet Protocol version 6 address. newtype IPv6 = IPv6 { getIPv6 :: Word128 }- deriving (Bounded,Enum,Eq,Integral,Num,Ord,Real,Storable,Bits,FiniteBits,NFData,Prim)+ deriving (Bounded,Enum,Eq,Ord,Storable,Bits,FiniteBits,NFData,Prim,Ix,Data) instance Show IPv6 where showsPrec p addr = showParen (p > 10)@@ -749,9 +753,18 @@ data IPv6Range = IPv6Range { ipv6RangeBase :: {-# UNPACK #-} !IPv6 , ipv6RangeLength :: {-# UNPACK #-} !Word8- } deriving (Eq,Ord,Show,Read,Generic)+ } deriving (Eq,Ord,Show,Read,Generic,Data) instance NFData IPv6Range++instance Aeson.ToJSON IPv6Range where+ toJSON = Aeson.String . encodeRange++instance Aeson.FromJSON IPv6Range where+ parseJSON (Aeson.String t) = case decodeRange t of+ Nothing -> fail "Could not decodeRange IPv6 range"+ Just res -> return res+ parseJSON _ = mzero mask128 :: IPv6 mask128 = maxBound
src/Net/Mac.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-}@@ -53,7 +54,9 @@ import Data.Bits ((.|.),unsafeShiftL,unsafeShiftR,(.&.)) import Data.ByteString (ByteString) import Data.Char (ord,chr)+import Data.Data (Data) import Data.Hashable (Hashable)+import Data.Ix (Ix) import Data.Primitive.Types (Prim(..)) #if !MIN_VERSION_base(4,11,0) import Data.Semigroup ((<>))@@ -607,7 +610,7 @@ -- type. It is not considered part of the stable API, and it -- allows you to construct invalid MAC addresses. newtype Mac = Mac Word64- deriving (Eq,Ord,Generic)+ deriving (Eq,Ord,Generic,Ix,Data) instance NFData Mac @@ -737,7 +740,7 @@ data MacCodec = MacCodec { macCodecGrouping :: !MacGrouping , macCodecUpperCase :: !Bool- } deriving (Eq,Ord,Show,Read,Generic)+ } deriving (Eq,Ord,Show,Read,Generic,Data) -- | The format expected by the mac address parser. The 'Word8' taken -- by some of these constructors is the ascii value of the character@@ -748,7 +751,7 @@ | MacGroupingTriples !Char -- ^ Three-character groups, @24B-F0A-025-829@ | MacGroupingQuadruples !Char -- ^ Four-character groups, @A220.0745.CAC7@ | MacGroupingNoSeparator -- ^ No separator, @24AF4B5B0780@- deriving (Eq,Ord,Show,Read,Generic)+ deriving (Eq,Ord,Show,Read,Generic,Data) instance Hashable Mac
− test/Net/IPv4/RangeSpec.hs
@@ -1,67 +0,0 @@-{-# OPTIONS_GHC -Wno-deprecations #-}-module Net.IPv4.RangeSpec (spec) where-import Prelude hiding (any)-import Data.Bits-import Net.IPv4-import Test.Hspec--spec :: Spec-spec = parallel $ do- describe "Bits" $ do- context "underlying IPv4 imlementation used correctly" $ do- let host = range (ipv4 255 255 0 0) 32- broadH = range broadcast 32- negBroadH = range (ipv4 0 0 255 255) 32- it ".&." $ do- host .&. broadH `shouldBe` host- it ".|." $ do- host .|. broadH `shouldBe` broadH- it "xor" $ do- host `xor` broadH `shouldBe` negBroadH- it "complement" $ do- complement host `shouldBe` negBroadH- it "shift" $ do- shift host 8 `shouldBe` range (ipv4 255 0 0 0) 32- it "rotate" $ do- rotate host 8 `shouldBe` range (ipv4 255 0 0 255) 32- it "isSigned" $ do- isSigned host `shouldBe` False- context "size operations use length correctly" $ do- it "bitSize" $ do- bitSize (range any 8) `shouldBe` 8- bitSize (range any 15) `shouldBe` 15- bitSize (range any 32) `shouldBe` 32- it "bitSizeMaybe" $ do- bitSizeMaybe (range broadcast 0) `shouldBe` Just 0- bitSizeMaybe (range broadcast 24) `shouldBe` Just 24- bitSizeMaybe (range broadcast 31) `shouldBe` Just 31- it "testBit" $ do- let prefix = range loopback 8- testBit prefix <$> [0..31] `shouldBe`- -- Note: final bit is False not True- [ False, True, True, True, True, True, True, True- , False, False, False, False, False, False, False, False- , False, False, False, False, False, False, False, False- , False, False, False, False, False, False, False, False]- it "bit" $ do- bit 0 `shouldBe` range (ipv4 128 0 0 0) 1- bit 1 `shouldBe` range (ipv4 64 0 0 0) 2- bit 31 `shouldBe` range (ipv4 0 0 0 1) 32- it "popCount" $ do- popCount (range any 0) `shouldBe` 0- popCount (range broadcast 0) `shouldBe` 0- popCount (range loopback 8) `shouldBe` 7- popCount (range loopback 32) `shouldBe` 8- context "operates on network bits only" $ do- it "bitwise: same length" $ do- (IPv4Range broadcast 16) .&. (IPv4Range broadcast 16)- `shouldBe` (IPv4Range (ipv4 255 255 0 0) 16)- it "bitwise: differing lengths ignoring host bits" $ do- (IPv4Range broadcast 8) .&. (IPv4Range broadcast 16)- `shouldBe` (IPv4Range (ipv4 255 0 0 0) 16)- it "rebase: ignores host bits" $ do- complement (IPv4Range loopback 16)- `shouldBe` (IPv4Range (ipv4 128 255 0 0) 16)- describe "FiniteBits" $ do- it "finiteBitSize" $ do- finiteBitSize . (range loopback) <$> [0..31] `shouldBe` [0..31]
test/Net/IPv4Spec.hs view
@@ -49,14 +49,14 @@ isSigned broadcast `shouldBe` False it "testBit" $ do testBit loopback <$> [0..31] `shouldBe`- [ False, True, True, True, True, True, True, True- , False, False, False, False, False, False, False, False- , False, False, False, False, False, False, False, False- , False, False, False, False, False, False, False, True]+ [ True, False, False, False, False, False, False, False+ , False, False, False, False, False, False, False, False+ , False, False, False, False, False, False, False, False+ , True, True, True, True, True, True, True, False ] it "bit" $ do- bit 0 `shouldBe` ipv4 128 0 0 0- bit 1 `shouldBe` ipv4 64 0 0 0- bit 31 `shouldBe` ipv4 0 0 0 1+ bit 0 `shouldBe` ipv4 0 0 0 1+ bit 1 `shouldBe` ipv4 0 0 0 2+ bit 31 `shouldBe` ipv4 128 0 0 0 it "popCount" $ do popCount any `shouldBe` 0 popCount loopback `shouldBe` 8
test/Test.hs view
@@ -16,7 +16,7 @@ import Numeric (showHex) import Test.QuickCheck.Property (failed,succeeded,Result(..)) import Data.Bifunctor-import Test.QuickCheck.Classes (Laws(..),jsonLaws,showReadLaws,primLaws,boundedEnumLaws)+import Test.QuickCheck.Classes (Laws(..),jsonLaws,showReadLaws,primLaws,boundedEnumLaws,bitsLaws) import qualified Test.Tasty.HUnit as PH import Net.Types (IP,IPv4(..),IPv4Range(..),Mac(..),IPv6(..),MacGrouping(..),MacCodec(..),IPv6Range(..))@@ -96,7 +96,7 @@ [ PH.testCase "Parser Test Cases" testIPv4Parser ] , testGroup "IPv6 encode/decode"- [ PH.testCase "Parser Test Cases" $ testIPv6Parser $ \str -> + [ PH.testCase "Parser Test Cases" $ testIPv6Parser $ \str -> either (\_ -> Nothing) (Just . HexIPv6) (AT.parseOnly (IPv6.parser <* AT.endOfInput)@@ -146,19 +146,23 @@ [ testGroup "IPv4" [ lawsToTest (jsonLaws (Proxy :: Proxy IPv4)) , lawsToTest (showReadLaws (Proxy :: Proxy IPv4))- -- , lawsToTest (bitsLaws (Proxy :: Proxy IPv4))+ , lawsToTest (bitsLaws (Proxy :: Proxy IPv4)) ] , testGroup "IPv4Range" [ lawsToTest (jsonLaws (Proxy :: Proxy IPv4Range)) , lawsToTest (showReadLaws (Proxy :: Proxy IPv4Range))- -- , lawsToTest (bitsLaws (Proxy :: Proxy IPv4Range)) ] , testGroup "IPv6" [ lawsToTest (jsonLaws (Proxy :: Proxy IPv6)) , lawsToTest (showReadLaws (Proxy :: Proxy IPv6)) , lawsToTest (primLaws (Proxy :: Proxy IPv6)) , lawsToTest (boundedEnumLaws (Proxy :: Proxy IPv6))+ , lawsToTest (bitsLaws (Proxy :: Proxy IPv6)) ]+ , testGroup "IPv6Range"+ [ lawsToTest (jsonLaws (Proxy :: Proxy IPv6Range))+ , lawsToTest (showReadLaws (Proxy :: Proxy IPv6Range))+ ] , testGroup "IP" [ lawsToTest (jsonLaws (Proxy :: Proxy IP)) , lawsToTest (showReadLaws (Proxy :: Proxy IP))@@ -472,7 +476,6 @@ shrink (IPv6Range addr mask) = liftA2 IPv6.range (shrink addr) (filter (/= mask) [0,div mask 2,if mask > 0 then mask - 1 else 0])- instance Arbitrary MacCodec where arbitrary = MacCodec <$> arbitrary <*> arbitrary