packages feed

hw-ip 1.0.0.0 → 2.0.0.0

raw patch · 18 files changed

+1010/−306 lines, 18 filesdep +containersdep −attoparsecdep ~apparPVP ok

version bump matches the API change (PVP)

Dependencies added: containers

Dependencies removed: attoparsec

Dependency ranges changed: appar

API changes (from Hackage documentation)

- HaskellWorks.Data.Network.Ip.Internal: d :: Int -> Parser Word8
- HaskellWorks.Data.Network.Ip.Internal: ds :: Int -> Int -> Parser Word8
- HaskellWorks.Data.Network.Ip.Ip: firstAddress :: IpBlock -> (Word32, Word32, Word32, Word32)
- HaskellWorks.Data.Network.Ip.Ip: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ip.IpBlock
- HaskellWorks.Data.Network.Ip.Ip: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ip.IpBlock
- HaskellWorks.Data.Network.Ip.Ip: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ip.IpBlock
- HaskellWorks.Data.Network.Ip.Ip: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ip.IpBlock
- HaskellWorks.Data.Network.Ip.Ip: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ip.IpBlock
- HaskellWorks.Data.Network.Ip.Ip: isValidIpBlock :: IpBlock -> Bool
- HaskellWorks.Data.Network.Ip.Ip: lastAddress :: IpBlock -> (Word32, Word32, Word32, Word32)
- HaskellWorks.Data.Network.Ip.Ipv4: Ipv4Address :: Word32 -> Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: Ipv4Block :: !Ipv4Address -> !Ipv4NetMask -> Ipv4Block
- HaskellWorks.Data.Network.Ip.Ipv4: Ipv4NetMask :: Word8 -> Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: [$sel:base:Ipv4Block] :: Ipv4Block -> !Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: [$sel:mask:Ipv4Block] :: Ipv4Block -> !Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: [$sel:word8:Ipv4NetMask] :: Ipv4NetMask -> Word8
- HaskellWorks.Data.Network.Ip.Ipv4: [$sel:word:Ipv4Address] :: Ipv4Address -> Word32
- HaskellWorks.Data.Network.Ip.Ipv4: blockSize :: Ipv4Block -> Int
- HaskellWorks.Data.Network.Ip.Ipv4: data Ipv4Block
- HaskellWorks.Data.Network.Ip.Ipv4: firstIpv4Address :: Ipv4Block -> Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Block
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv4.Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Block
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv4.Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Ipv4.Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv4.Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Block
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv4.Ipv4Block
- HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv4.Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: ipv4AddressToString :: Ipv4Address -> String
- HaskellWorks.Data.Network.Ip.Ipv4: ipv4AddressToText :: Ipv4Address -> Text
- HaskellWorks.Data.Network.Ip.Ipv4: ipv4AddressToWords :: Ipv4Address -> (Word8, Word8, Word8, Word8)
- HaskellWorks.Data.Network.Ip.Ipv4: isValidIpv4Block :: Ipv4Block -> Bool
- HaskellWorks.Data.Network.Ip.Ipv4: lastIpv4Address :: Ipv4Block -> Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: newtype Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: newtype Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Ipv4: parseIpv4Address :: Parser Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv4: textToMaybeIpv4Address :: Text -> Maybe Ipv4Address
- HaskellWorks.Data.Network.Ip.Ipv6: Ipv6Address :: (Word32, Word32, Word32, Word32) -> Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: Ipv6Block :: !Ipv6Address -> !Ipv6NetMask -> Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: Ipv6NetMask :: Word8 -> Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: [$sel:base:Ipv6Block] :: Ipv6Block -> !Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: [$sel:mask:Ipv6Block] :: Ipv6Block -> !Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: [$sel:word:Ipv6NetMask] :: Ipv6NetMask -> Word8
- HaskellWorks.Data.Network.Ip.Ipv6: [$sel:words:Ipv6Address] :: Ipv6Address -> (Word32, Word32, Word32, Word32)
- HaskellWorks.Data.Network.Ip.Ipv6: data Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv6.Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv6.Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Ipv6.Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv6.Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv6.Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv6.Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv6.Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: ipv4BlockToMappedIpv6Block :: Ipv4Block -> Ipv6Block
- HaskellWorks.Data.Network.Ip.Ipv6: isValidIpv6Block :: Ipv6Block -> Bool
- HaskellWorks.Data.Network.Ip.Ipv6: masksIpv6 :: Word8 -> [Word32]
- HaskellWorks.Data.Network.Ip.Ipv6: newtype Ipv6Address
- HaskellWorks.Data.Network.Ip.Ipv6: newtype Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Ipv6: parseIpv6Block :: Text -> Either Text Ipv6Block
- HaskellWorks.Data.Network.Ip.Type: Ipv4Address :: Word32 -> Ipv4Address
- HaskellWorks.Data.Network.Ip.Type: Ipv4Block :: !Ipv4Address -> !Ipv4NetMask -> Ipv4Block
- HaskellWorks.Data.Network.Ip.Type: Ipv4NetMask :: Word8 -> Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Type: Ipv6Address :: (Word32, Word32, Word32, Word32) -> Ipv6Address
- HaskellWorks.Data.Network.Ip.Type: Ipv6Block :: !Ipv6Address -> !Ipv6NetMask -> Ipv6Block
- HaskellWorks.Data.Network.Ip.Type: Ipv6NetMask :: Word8 -> Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Type: [$sel:base:Ipv4Block] :: Ipv4Block -> !Ipv4Address
- HaskellWorks.Data.Network.Ip.Type: [$sel:base:Ipv6Block] :: Ipv6Block -> !Ipv6Address
- HaskellWorks.Data.Network.Ip.Type: [$sel:mask:Ipv4Block] :: Ipv4Block -> !Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Type: [$sel:mask:Ipv6Block] :: Ipv6Block -> !Ipv6NetMask
- HaskellWorks.Data.Network.Ip.Type: [$sel:word8:Ipv4NetMask] :: Ipv4NetMask -> Word8
- HaskellWorks.Data.Network.Ip.Type: [$sel:word:Ipv4Address] :: Ipv4Address -> Word32
- HaskellWorks.Data.Network.Ip.Type: [$sel:word:Ipv6NetMask] :: Ipv6NetMask -> Word8
- HaskellWorks.Data.Network.Ip.Type: [$sel:words:Ipv6Address] :: Ipv6Address -> (Word32, Word32, Word32, Word32)
- HaskellWorks.Data.Network.Ip.Type: data Ipv4Block
- HaskellWorks.Data.Network.Ip.Type: data Ipv6Block
- HaskellWorks.Data.Network.Ip.Type: newtype Ipv4Address
- HaskellWorks.Data.Network.Ip.Type: newtype Ipv4NetMask
- HaskellWorks.Data.Network.Ip.Type: newtype Ipv6Address
- HaskellWorks.Data.Network.Ip.Type: newtype Ipv6NetMask
+ HaskellWorks.Data.Network.Ip.Internal: bitPower :: Word8 -> Word64
+ HaskellWorks.Data.Network.Ip.Internal: bitPower128 :: Word8 -> Integer
+ HaskellWorks.Data.Network.Ip.Internal: blockSize :: Word8 -> Int
+ HaskellWorks.Data.Network.Ip.Internal: blockSize128 :: Word8 -> Integer
+ HaskellWorks.Data.Network.Ip.Internal: digit :: Int -> Parser Word8
+ HaskellWorks.Data.Network.Ip.Internal: digits :: Int -> Int -> Parser Word8
+ HaskellWorks.Data.Network.Ip.Internal: readsPrecOnParser :: Parser a -> Int -> String -> [(a, String)]
+ HaskellWorks.Data.Network.Ip.Ip: canonicalise :: IpBlock Unaligned -> Maybe (IpBlock Canonical)
+ HaskellWorks.Data.Network.Ip.Ip: firstIpAddress :: IpBlock Canonical -> (Word32, Word32, Word32, Word32)
+ HaskellWorks.Data.Network.Ip.Ip: instance GHC.Classes.Eq (HaskellWorks.Data.Network.Ip.Ip.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ip: instance GHC.Classes.Ord (HaskellWorks.Data.Network.Ip.Ip.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ip: instance GHC.Generics.Generic (HaskellWorks.Data.Network.Ip.Ip.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ip: instance GHC.Read.Read (HaskellWorks.Data.Network.Ip.Ip.IpBlock HaskellWorks.Data.Network.Ip.Validity.Unaligned)
+ HaskellWorks.Data.Network.Ip.Ip: instance GHC.Show.Show (HaskellWorks.Data.Network.Ip.Ip.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ip: isCanonical :: IpBlock v -> Bool
+ HaskellWorks.Data.Network.Ip.Ip: lastIpAddress :: IpBlock Canonical -> (Word32, Word32, Word32, Word32)
+ HaskellWorks.Data.Network.Ip.Ipv4: IpAddress :: Word32 -> IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: IpBlock :: !IpAddress -> !IpNetMask -> IpBlock v
+ HaskellWorks.Data.Network.Ip.Ipv4: IpNetMask :: Word8 -> IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: [$sel:base:IpBlock] :: IpBlock v -> !IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: [$sel:mask:IpBlock] :: IpBlock v -> !IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: [$sel:word8:IpNetMask] :: IpNetMask -> Word8
+ HaskellWorks.Data.Network.Ip.Ipv4: [$sel:word:IpAddress] :: IpAddress -> Word32
+ HaskellWorks.Data.Network.Ip.Ipv4: blockToRange :: IpBlock Canonical -> Range IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: canonicaliseIpBlock :: IpBlock v -> IpBlock Canonical
+ HaskellWorks.Data.Network.Ip.Ipv4: collapseIpBlocks :: [IpBlock Canonical] -> [IpBlock Canonical]
+ HaskellWorks.Data.Network.Ip.Ipv4: data IpBlock v
+ HaskellWorks.Data.Network.Ip.Ipv4: firstIpAddress :: IpBlock v -> IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Eq (HaskellWorks.Data.Network.Ip.Ipv4.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv4.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Ord (HaskellWorks.Data.Network.Ip.Ipv4.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv4.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Enum.Bounded HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Enum.Bounded HaskellWorks.Data.Network.Ip.Ipv4.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Ipv4.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Generics.Generic (HaskellWorks.Data.Network.Ip.Ipv4.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv4.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Read.Read (HaskellWorks.Data.Network.Ip.Ipv4.IpBlock HaskellWorks.Data.Network.Ip.Validity.Canonical)
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Read.Read (HaskellWorks.Data.Network.Ip.Ipv4.IpBlock HaskellWorks.Data.Network.Ip.Validity.Unaligned)
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Show.Show (HaskellWorks.Data.Network.Ip.Ipv4.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv4.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv4.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: ipAddressToWords :: IpAddress -> (Word8, Word8, Word8, Word8)
+ HaskellWorks.Data.Network.Ip.Ipv4: lastIpAddress :: IpBlock v -> IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: newtype IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: newtype IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv4: parseIpAddress :: Parser IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv4: rangeToBlocks :: Range IpAddress -> [IpBlock Canonical]
+ HaskellWorks.Data.Network.Ip.Ipv4: rangeToBlocksDL :: Range IpAddress -> [IpBlock Canonical] -> [IpBlock Canonical]
+ HaskellWorks.Data.Network.Ip.Ipv4: showIpAddress :: IpAddress -> String
+ HaskellWorks.Data.Network.Ip.Ipv4: showsIpAddress :: IpAddress -> String -> String
+ HaskellWorks.Data.Network.Ip.Ipv4: splitIpRange :: Range IpAddress -> (IpBlock Canonical, Maybe (Range IpAddress))
+ HaskellWorks.Data.Network.Ip.Ipv4: tshowIpAddress :: IpAddress -> Text
+ HaskellWorks.Data.Network.Ip.Ipv4: tshowIpBlock :: IpBlock v -> Text
+ HaskellWorks.Data.Network.Ip.Ipv6: IpAddress :: Word128 -> IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: IpBlock :: !IpAddress -> !IpNetMask -> IpBlock v
+ HaskellWorks.Data.Network.Ip.Ipv6: IpNetMask :: Word8 -> IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: [$sel:base:IpBlock] :: IpBlock v -> !IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: [$sel:mask:IpBlock] :: IpBlock v -> !IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: [$sel:word:IpNetMask] :: IpNetMask -> Word8
+ HaskellWorks.Data.Network.Ip.Ipv6: blockToRange :: IpBlock Canonical -> Range IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: data IpBlock v
+ HaskellWorks.Data.Network.Ip.Ipv6: firstIpAddress :: IpBlock Canonical -> IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: fromV4 :: IpBlock Canonical -> IpBlock v
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Eq (HaskellWorks.Data.Network.Ip.Ipv6.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv6.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Eq HaskellWorks.Data.Network.Ip.Ipv6.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Ord (HaskellWorks.Data.Network.Ip.Ipv6.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv6.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Classes.Ord HaskellWorks.Data.Network.Ip.Ipv6.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Ipv6.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Generics.Generic (HaskellWorks.Data.Network.Ip.Ipv6.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv6.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Generics.Generic HaskellWorks.Data.Network.Ip.Ipv6.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Read.Read (HaskellWorks.Data.Network.Ip.Ipv6.IpBlock HaskellWorks.Data.Network.Ip.Validity.Unaligned)
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv6.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Read.Read HaskellWorks.Data.Network.Ip.Ipv6.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Show.Show (HaskellWorks.Data.Network.Ip.Ipv6.IpBlock v)
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv6.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: instance GHC.Show.Show HaskellWorks.Data.Network.Ip.Ipv6.IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum HaskellWorks.Data.Network.Ip.Ipv6.IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: isCanonical :: IpBlock v -> Bool
+ HaskellWorks.Data.Network.Ip.Ipv6: lastIpAddress :: IpBlock Canonical -> IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: masksIp :: Word8 -> [Word32]
+ HaskellWorks.Data.Network.Ip.Ipv6: newtype IpAddress
+ HaskellWorks.Data.Network.Ip.Ipv6: newtype IpNetMask
+ HaskellWorks.Data.Network.Ip.Ipv6: parseIpBlock :: Text -> Either Text (IpBlock Unaligned)
+ HaskellWorks.Data.Network.Ip.Ipv6: rangeToBlocks :: Range IpAddress -> [IpBlock Canonical]
+ HaskellWorks.Data.Network.Ip.Ipv6: rangeToBlocksDL :: Range IpAddress -> [IpBlock Canonical] -> [IpBlock Canonical]
+ HaskellWorks.Data.Network.Ip.Ipv6: showIpAddress :: IpAddress -> String
+ HaskellWorks.Data.Network.Ip.Ipv6: showsIpAddress :: IpAddress -> String -> String
+ HaskellWorks.Data.Network.Ip.Ipv6: splitIpRange :: Range IpAddress -> (IpBlock Canonical, Maybe (Range IpAddress))
+ HaskellWorks.Data.Network.Ip.Ipv6: tshowIpAddress :: IpAddress -> Text
+ HaskellWorks.Data.Network.Ip.Ipv6: tshowIpBlock :: IpBlock v -> Text
+ HaskellWorks.Data.Network.Ip.Range: Range :: a -> a -> Range a
+ HaskellWorks.Data.Network.Ip.Range: [first] :: Range a -> a
+ HaskellWorks.Data.Network.Ip.Range: [last] :: Range a -> a
+ HaskellWorks.Data.Network.Ip.Range: data Range a
+ HaskellWorks.Data.Network.Ip.Range: instance GHC.Classes.Eq a => GHC.Classes.Eq (HaskellWorks.Data.Network.Ip.Range.Range a)
+ HaskellWorks.Data.Network.Ip.Range: instance GHC.Classes.Ord a => GHC.Classes.Ord (HaskellWorks.Data.Network.Ip.Range.Range a)
+ HaskellWorks.Data.Network.Ip.Range: instance GHC.Generics.Generic (HaskellWorks.Data.Network.Ip.Range.Range a)
+ HaskellWorks.Data.Network.Ip.Range: instance GHC.Show.Show a => GHC.Show.Show (HaskellWorks.Data.Network.Ip.Range.Range a)
+ HaskellWorks.Data.Network.Ip.Range: mergeRanges :: (SafeEnum a, Ord a) => [Range a] -> [Range a]
+ HaskellWorks.Data.Network.Ip.Range: parseRange :: Parser a -> Parser (Range a)
+ HaskellWorks.Data.Network.Ip.SafeEnum: boundedPred :: SafeEnum a => a -> a
+ HaskellWorks.Data.Network.Ip.SafeEnum: boundedSucc :: SafeEnum a => a -> a
+ HaskellWorks.Data.Network.Ip.SafeEnum: class SafeEnum a
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum ()
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Int.Int16
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Int.Int32
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Int.Int64
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Int.Int8
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Integer.Type.Integer
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Types.Bool
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Types.Char
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Types.Int
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Types.Word
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Word.Word16
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Word.Word32
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Word.Word64
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum GHC.Word.Word8
+ HaskellWorks.Data.Network.Ip.SafeEnum: instance HaskellWorks.Data.Network.Ip.SafeEnum.SafeEnum HaskellWorks.Data.Network.Ip.Word128.Word128
+ HaskellWorks.Data.Network.Ip.SafeEnum: safePred :: SafeEnum a => a -> Maybe a
+ HaskellWorks.Data.Network.Ip.SafeEnum: safeSucc :: SafeEnum a => a -> Maybe a
+ HaskellWorks.Data.Network.Ip.Validity: Canonical :: Canonical
+ HaskellWorks.Data.Network.Ip.Validity: Unaligned :: Unaligned
+ HaskellWorks.Data.Network.Ip.Validity: data Canonical
+ HaskellWorks.Data.Network.Ip.Validity: data Unaligned
+ HaskellWorks.Data.Network.Ip.Word128: instance Data.Bits.Bits HaskellWorks.Data.Network.Ip.Word128.Word128
+ HaskellWorks.Data.Network.Ip.Word128: instance Data.Bits.FiniteBits HaskellWorks.Data.Network.Ip.Word128.Word128
+ HaskellWorks.Data.Network.Ip.Word128: instance GHC.Enum.Enum HaskellWorks.Data.Network.Ip.Word128.Word128
+ HaskellWorks.Data.Network.Ip.Word128: instance GHC.Num.Num HaskellWorks.Data.Network.Ip.Word128.Word128
+ HaskellWorks.Data.Network.Ip.Word128: integerToWord128 :: Integer -> Word128
+ HaskellWorks.Data.Network.Ip.Word128: type Word128 = (Word32, Word32, Word32, Word32)
+ HaskellWorks.Data.Network.Ip.Word128: word128ToInteger :: Word128 -> Integer
- HaskellWorks.Data.Network.Ip.Ip: IpBlockV4 :: Ipv4Block -> IpBlock
+ HaskellWorks.Data.Network.Ip.Ip: IpBlockV4 :: IpBlock v -> IpBlock v
- HaskellWorks.Data.Network.Ip.Ip: IpBlockV6 :: Ipv6Block -> IpBlock
+ HaskellWorks.Data.Network.Ip.Ip: IpBlockV6 :: IpBlock v -> IpBlock v
- HaskellWorks.Data.Network.Ip.Ip: data IpBlock
+ HaskellWorks.Data.Network.Ip.Ip: data IpBlock v
- HaskellWorks.Data.Network.Ip.Ipv4: bitPower :: Ipv4NetMask -> Word64
+ HaskellWorks.Data.Network.Ip.Ipv4: bitPower :: IpNetMask -> Word64
- HaskellWorks.Data.Network.Ip.Ipv4: isCanonical :: Ipv4Block -> Bool
+ HaskellWorks.Data.Network.Ip.Ipv4: isCanonical :: IpBlock v -> Bool
- HaskellWorks.Data.Network.Ip.Ipv4: splitBlock :: Ipv4Block -> Maybe (Ipv4Block, Ipv4Block)
+ HaskellWorks.Data.Network.Ip.Ipv4: splitBlock :: IpBlock Canonical -> Maybe (IpBlock Canonical, IpBlock Canonical)

Files

hw-ip.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: hw-ip-version: 1.0.0.0+version: 2.0.0.0 license: BSD3 license-file: LICENSE copyright: 2018 John Ky, David Turnbull, Jian Wan@@ -25,19 +25,30 @@ library     exposed-modules:       HaskellWorks.Data.Network.Ip-      HaskellWorks.Data.Network.Ip.Ip-      HaskellWorks.Data.Network.Ip.Ipv6       HaskellWorks.Data.Network.Ip.Internal+      HaskellWorks.Data.Network.Ip.Ip       HaskellWorks.Data.Network.Ip.Ipv4-      HaskellWorks.Data.Network.Ip.Type+      HaskellWorks.Data.Network.Ip.Ipv6+      HaskellWorks.Data.Network.Ip.Range+      HaskellWorks.Data.Network.Ip.SafeEnum+      HaskellWorks.Data.Network.Ip.Validity+      HaskellWorks.Data.Network.Ip.Word128     hs-source-dirs: src     other-modules:         Paths_hw_ip     default-language: Haskell2010+    ghc-options:+      -Wall+      -Wcompat+      -Wincomplete-record-updates+      -Wincomplete-uni-patterns+      -Wredundant-constraints+      -O2+      -msse4.2     build-depends:         base            >= 4            && < 4.13       , appar           >= 0.1.7        && < 0.2-      , attoparsec      >= 0.13.2.2     && < 0.14+      , containers       , generic-lens    >= 0.5.1.0      && < 1.2       , hw-bits         >= 0.7.0.2      && < 0.8       , iproute         >= 1.7.3        && < 1.8@@ -48,13 +59,28 @@     main-is: Spec.hs     hs-source-dirs: test     other-modules:-        HaskellWorks.Data.Network.IpSpec+        HaskellWorks.Data.Network.Gen+        HaskellWorks.Data.Network.IpBlockSpec+        HaskellWorks.Data.Network.Ipv4Spec+        HaskellWorks.Data.Network.Ipv6Spec+        HaskellWorks.Data.Network.RangeSpec+        HaskellWorks.Data.Network.Word128Spec         Paths_hw_ip     default-language: Haskell2010-    ghc-options: -threaded -rtsopts -with-rtsopts=-N+    ghc-options:+      -Wall+      -Wcompat+      -Wincomplete-record-updates+      -Wincomplete-uni-patterns+      -Wredundant-constraints+      -O2+      -msse4.2+      -threaded+      -rtsopts+      -with-rtsopts=-N     build-depends:         base              >= 4          && < 4.13-      , attoparsec+      , appar       , generic-lens       , hedgehog          >= 0.5.3      && < 0.7       , hspec             >= 2.4.4      && < 2.6
src/HaskellWorks/Data/Network/Ip.hs view
@@ -1,9 +1,1 @@-module HaskellWorks.Data.Network.Ip-  ( module X-  ) where--import HaskellWorks.Data.Network.Ip.Internal as X-import HaskellWorks.Data.Network.Ip.Ip       as X-import HaskellWorks.Data.Network.Ip.Ipv4     as X-import HaskellWorks.Data.Network.Ip.Ipv6     as X-import HaskellWorks.Data.Network.Ip.Type     as X+module HaskellWorks.Data.Network.Ip where
src/HaskellWorks/Data/Network/Ip/Internal.hs view
@@ -6,7 +6,7 @@ import Data.Word import HaskellWorks.Data.Bits.BitWise -import qualified Data.Attoparsec.Text as AP+import qualified Text.Appar.String as AP  fourOctetsToWord32 :: Word8 -> Word8 -> Word8 -> Word8 -> Word32 fourOctetsToWord32 a b c d =@@ -23,11 +23,11 @@   where paste a b = a * 10 + b  octet :: AP.Parser Word8-octet = (ds 1 2 #<*>#  d 5  ) #<*># ds 0 5-  <|>   (ds 1 2 #<*># ds 0 4) #<*># ds 0 9-  <|>   ( d 1   #<*># ds 0 9) #<*># ds 0 9-  <|>    ds 1 9 #<*># ds 0 9-  <|>    ds 0 9+octet = AP.try ((digits 1 2 #<*>#  digit 5  ) #<*># digits 0 5)+  <|>   AP.try ((digits 1 2 #<*># digits 0 4) #<*># digits 0 9)+  <|>   AP.try (( digit 1   #<*># digits 0 9) #<*># digits 0 9)+  <|>   AP.try ( digits 1 9 #<*># digits 0 9)+  <|>            digits 0 9  whitespace :: AP.Parser () whitespace = void $ many (AP.satisfy isSpace)@@ -40,16 +40,16 @@   <*>  octet  ipv4NetMask :: AP.Parser Word8-ipv4NetMask =  d 3   #<*># ds 0 2-  <|>          d 2   #<*># ds 0 9-  <|>          d 1   #<*># ds 0 9-  <|>         ds 0 9+ipv4NetMask =  AP.try (digit 3   #<*># digits 0 2)+  <|>          AP.try (digit 2   #<*># digits 0 9)+  <|>          AP.try (digit 1   #<*># digits 0 9)+  <|>           digits 0 9 -d :: Int -> AP.Parser Word8-d c      = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (== chr (c + 48))+digit :: Int -> AP.Parser Word8+digit c      = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (== chr (c + 48)) -ds :: Int -> Int -> AP.Parser Word8-ds c1 c2 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= chr (c1 + 48) && c <= chr (c2 + 48))+digits :: Int -> Int -> AP.Parser Word8+digits c1 c2 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= chr (c1 + 48) && c <= chr (c2 + 48))  ipv4Block :: AP.Parser (Word32, Word8) ipv4Block = do@@ -60,3 +60,20 @@  word32x4ToWords :: (Word32, Word32, Word32, Word32) -> [Word32] word32x4ToWords (a, b, c, d) = [a, b, c, d]++bitPower :: Word8 -> Word64+bitPower m = fromIntegral (32 - m)++blockSize :: Word8 -> Int+blockSize m = 2 ^ bitPower m++bitPower128 :: Word8 -> Integer+bitPower128 m = fromIntegral (128 - m)++blockSize128 :: Word8 -> Integer+blockSize128 m = 2 ^ bitPower128 m++readsPrecOnParser :: AP.Parser a -> Int -> String -> [(a, String)]+readsPrecOnParser p _ s = case AP.runParser (whitespace *> p) s of+    (Just a, r) -> [(a, r)]+    _           -> []
src/HaskellWorks/Data/Network/Ip/Ip.hs view
@@ -1,62 +1,62 @@-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE DuplicateRecordFields      #-}-{-# LANGUAGE InstanceSigs               #-}-{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE DeriveGeneric         #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE InstanceSigs          #-}+{-# LANGUAGE OverloadedStrings     #-}  module HaskellWorks.Data.Network.Ip.Ip   ( IpBlock(..)-  , isValidIpBlock-  , firstAddress-  , lastAddress+  , isCanonical+  , canonicalise+  , firstIpAddress+  , lastIpAddress   ) where -import Control.Applicative import Control.Monad-import Data.Char-import Data.Maybe import Data.Word import GHC.Generics import HaskellWorks.Data.Bits.BitWise+import HaskellWorks.Data.Network.Ip.Validity import Text.Read -import qualified Data.Attoparsec.Text                  as AP-import qualified Data.Bits                             as B-import qualified Data.Text                             as T-import qualified HaskellWorks.Data.Network.Ip.Internal as I-import qualified HaskellWorks.Data.Network.Ip.Ipv4     as I4-import qualified HaskellWorks.Data.Network.Ip.Ipv6     as I6-import qualified Text.ParserCombinators.ReadPrec       as RP+import qualified HaskellWorks.Data.Network.Ip.Ipv4 as V4+import qualified HaskellWorks.Data.Network.Ip.Ipv6 as V6 -data IpBlock = IpBlockV4 I4.Ipv4Block | IpBlockV6 I6.Ipv6Block+data IpBlock v = IpBlockV4 (V4.IpBlock v) | IpBlockV6 (V6.IpBlock v)   deriving (Eq, Ord, Generic) -instance Show IpBlock where+instance Show (IpBlock v) where   showsPrec _ (IpBlockV4 a) = shows a   showsPrec _ (IpBlockV6 a) = shows a -instance Read IpBlock where+instance Read (IpBlock Unaligned) where   readsPrec _ s =-    case readMaybe s :: Maybe I4.Ipv4Block of-      Just ipv4 -> [(IpBlockV4 ipv4, "")]+    case readMaybe s :: Maybe (V4.IpBlock Unaligned) of+      Just ip -> [(IpBlockV4 ip, "")]        Nothing ->-        case readMaybe s :: Maybe I6.Ipv6Block of+        case readMaybe s :: Maybe (V6.IpBlock Unaligned) of           Just ipv6 -> [(IpBlockV6 ipv6, "")]           Nothing   -> [] -isValidIpBlock :: IpBlock -> Bool-isValidIpBlock (IpBlockV4 b) = I4.isValidIpv4Block b-isValidIpBlock (IpBlockV6 b) = I6.isValidIpv6Block b+isCanonical :: IpBlock v -> Bool+isCanonical (IpBlockV4 b) = V4.isCanonical b+isCanonical (IpBlockV6 b) = V6.isCanonical b -firstAddress :: IpBlock -> (Word32, Word32, Word32, Word32)-firstAddress (IpBlockV4 i4b)                 = firstAddress (IpBlockV6 (I6.ipv4BlockToMappedIpv6Block i4b))-firstAddress (IpBlockV6 (I6.Ipv6Block ip _)) = I6.words ip+canonicalise :: IpBlock Unaligned -> Maybe (IpBlock Canonical)+canonicalise (IpBlockV4 (V4.IpBlock a m)) = mfilter isCanonical (Just $ IpBlockV4 (V4.IpBlock a m))+canonicalise (IpBlockV6 (V6.IpBlock a m)) = mfilter isCanonical (Just $ IpBlockV6 (V6.IpBlock a m)) -lastAddress :: IpBlock -> (Word32, Word32, Word32, Word32)-lastAddress (IpBlockV4 ib) = (0, 0, 0xFFFF, I4.word (I4.lastIpv4Address ib))-lastAddress (IpBlockV6 (I6.Ipv6Block ip (I6.Ipv6NetMask msk))) =-    let (w1, w2, w3, w4) = I6.words ip-        lt = I6.masksIpv6 $ fromIntegral msk+firstIpAddress :: IpBlock Canonical -> (Word32, Word32, Word32, Word32)+firstIpAddress (IpBlockV4 v4Block)                          = firstIpAddress (IpBlockV6 (V6.fromV4 v4Block))+-- firstIpAddress (IpBlockV6 (V6.IpBlock ip _)) = V6.words ip+firstIpAddress (IpBlockV6 (V6.IpBlock (V6.IpAddress ip) _)) = ip++lastIpAddress :: IpBlock Canonical -> (Word32, Word32, Word32, Word32)+lastIpAddress (IpBlockV4 ib) = (0, 0, 0xFFFF, V4.word (V4.lastIpAddress ib))+lastIpAddress (IpBlockV6 (V6.IpBlock (V6.IpAddress ip) (V6.IpNetMask msk))) =+    let (w1, w2, w3, w4) = ip+        lt = V6.masksIp $ fromIntegral msk         w1' = w1 .|. (lt !! 0)         w2' = w2 .|. (lt !! 1)         w3' = w3 .|. (lt !! 2)
src/HaskellWorks/Data/Network/Ip/Ipv4.hs view
@@ -1,140 +1,203 @@ {-# LANGUAGE BangPatterns               #-} {-# LANGUAGE DeriveGeneric              #-} {-# LANGUAGE DuplicateRecordFields      #-}+{-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs               #-}  module HaskellWorks.Data.Network.Ip.Ipv4-  ( Ipv4Address(..)-  , Ipv4NetMask(..)-  , Ipv4Block(..)-  , isValidIpv4Block+  ( IpAddress(..)+  , IpNetMask(..)+  , IpBlock(..)   , bitPower-  , blockSize   , isCanonical   , splitBlock-  , textToMaybeIpv4Address-  , parseIpv4Address-  , ipv4AddressToString-  , ipv4AddressToText-  , ipv4AddressToWords-  , firstIpv4Address-  , lastIpv4Address+  , parseIpAddress+  , showIpAddress+  , showsIpAddress+  , tshowIpAddress+  , tshowIpBlock+  , ipAddressToWords+  , firstIpAddress+  , lastIpAddress+  , canonicaliseIpBlock+  , collapseIpBlocks+  , splitIpRange+  , rangeToBlocksDL+  , rangeToBlocks+  , blockToRange   ) where -import Control.Applicative-import Control.Monad-import Data.Char-import Data.Maybe+import Data.Bifunctor+import Data.Foldable+import Data.Semigroup                        ((<>)) import Data.Word import GHC.Generics import HaskellWorks.Data.Bits.BitWise-import Text.Read+import HaskellWorks.Data.Network.Ip.Range+import HaskellWorks.Data.Network.Ip.Validity -import qualified Data.Attoparsec.Text                  as AP import qualified Data.Bits                             as B+import qualified Data.Sequence                         as S import qualified Data.Text                             as T import qualified HaskellWorks.Data.Network.Ip.Internal as I-import qualified Text.ParserCombinators.ReadPrec       as RP+import qualified Text.Appar.String                     as AP -newtype Ipv4Address = Ipv4Address+newtype IpAddress = IpAddress   { word :: Word32-  } deriving (Enum, Eq, Ord, Generic)+  } deriving (Enum, Bounded, Eq, Ord, Generic) -instance Show Ipv4Address where-  showsPrec _ (Ipv4Address w) =-    shows ((w .>. 24) .&. 0xff) . ('.':) .-    shows ((w .>. 16) .&. 0xff) . ('.':) .-    shows ((w .>.  8) .&. 0xff) . ('.':) .-    shows ( w         .&. 0xff)+instance Show IpAddress where+  showsPrec _ = showsIpAddress -instance Read Ipv4Address where-  readsPrec :: Int -> String -> [(Ipv4Address, String)]-  readsPrec _ s = case AP.parseWith (return mempty) (I.whitespace *> I.ipv4Address) (T.pack s) of-    Just result -> case result of-      AP.Done i r   -> [(Ipv4Address r, T.unpack i)]-      AP.Partial _  -> []-      AP.Fail a b c -> []-    Nothing -> []+instance Read IpAddress where+  readsPrec :: Int -> String -> [(IpAddress, String)]+  readsPrec = I.readsPrecOnParser (IpAddress <$> I.ipv4Address) -newtype Ipv4NetMask = Ipv4NetMask+newtype IpNetMask = IpNetMask   { word8 :: Word8-  } deriving (Enum, Eq, Ord, Show, Generic)+  } deriving (Enum, Bounded, Eq, Ord, Show, Generic) -data Ipv4Block = Ipv4Block-  { base :: !Ipv4Address-  , mask :: !Ipv4NetMask-  } deriving (Eq, Ord)+-- | An IP block.  The type parameter determines whether or not the value of the type is+-- canonical.+data IpBlock v = IpBlock+  { base :: !IpAddress+  , mask :: !IpNetMask+  } deriving (Eq, Ord, Generic) -instance Show Ipv4Block where-  showsPrec _ (Ipv4Block b (Ipv4NetMask m)) = shows b . ('/':) . shows m+instance Show (IpBlock v) where+  showsPrec _ = showsIpBlock -instance Read Ipv4Block where-  readsPrec :: Int -> String -> [(Ipv4Block, String)]-  readsPrec _ s = case AP.parseWith (return mempty) (I.whitespace *> I.ipv4Block) (T.pack s) of-    Just result -> case result of-      AP.Done i (a, m) ->-        case validIpv4Block $ Ipv4Block (Ipv4Address a) (Ipv4NetMask m) of-          Just b  -> [(b, T.unpack i)]-          Nothing -> []-      AP.Partial _    -> []-      AP.Fail a b c   -> []-    Nothing -> []+instance Read (IpBlock Unaligned) where+  readsPrec = I.readsPrecOnParser parseUnalignedIpBlock --- shift the address left by the amount of mask bits to reveal only host bits--- if any bits left are non-zero, then the mask is not big enough-validIpv4Block :: Ipv4Block -> Maybe Ipv4Block-validIpv4Block b@(Ipv4Block (Ipv4Address word) (Ipv4NetMask mask)) =-  if word `B.shiftL` fromIntegral mask `B.xor` 0 == 0-    then pure b-    else Nothing+instance Read (IpBlock Canonical) where+  readsPrec = I.readsPrecOnParser parseCanonicalIpBlock -isValidIpv4Block :: Ipv4Block -> Bool-isValidIpv4Block = isJust . validIpv4Block+-- | Canonicalise the block by zero-ing out the host bits+canonicaliseIpBlock :: IpBlock v -> IpBlock Canonical+canonicaliseIpBlock (IpBlock (IpAddress w) (IpNetMask m)) = IpBlock (IpAddress newWord) (IpNetMask m)+  where bp = fromIntegral (32 - m)+        newWord = (w .>. bp) .<. bp -firstIpv4Address :: Ipv4Block -> Ipv4Address-firstIpv4Address (Ipv4Block base _) = base+firstIpAddress :: IpBlock v -> IpAddress+firstIpAddress (IpBlock b _) = b -lastIpv4Address :: Ipv4Block -> Ipv4Address-lastIpv4Address b@(Ipv4Block (Ipv4Address base) _) = Ipv4Address (base + fromIntegral (blockSize b) - 1)+lastIpAddress :: IpBlock v -> IpAddress+lastIpAddress (IpBlock (IpAddress b) (IpNetMask m)) = IpAddress (b + fromIntegral (I.blockSize m) - 1) -bitPower :: Ipv4NetMask -> Word64-bitPower (Ipv4NetMask m) = fromIntegral (32 - m)+bitPower :: IpNetMask -> Word64+bitPower (IpNetMask m) = fromIntegral (32 - m) -isCanonical :: Ipv4Block -> Bool-isCanonical (Ipv4Block (Ipv4Address b) m) = ((b .>. bitPower m) .<. bitPower m) == b+-- | A valid block must have all host-bits set to zero after the mask is applied+isCanonical :: IpBlock v -> Bool+isCanonical (IpBlock (IpAddress b) (IpNetMask m)) = ((b .>. I.bitPower m) .<. I.bitPower m) == b -splitBlock :: Ipv4Block -> Maybe (Ipv4Block, Ipv4Block)-splitBlock (Ipv4Block (Ipv4Address b) (Ipv4NetMask m)) =+splitBlock :: IpBlock Canonical -> Maybe (IpBlock Canonical, IpBlock Canonical)+splitBlock (IpBlock (IpAddress b) (IpNetMask m)) =   if m >= 0 && m < 32     then  let !hm       = m + 1-              !halfMask = Ipv4NetMask hm+              !halfMask = IpNetMask hm               !c        = fromIntegral ((0x100000000 :: Word64) .>. fromIntegral (m + 1))           in  Just-              ( Ipv4Block (Ipv4Address  b     ) halfMask-              , Ipv4Block (Ipv4Address (b + c)) halfMask+              ( IpBlock (IpAddress  b     ) halfMask+              , IpBlock (IpAddress (b + c)) halfMask               )     else  Nothing -blockSize :: Ipv4Block -> Int-blockSize (Ipv4Block _ m) = 2 ^ bitPower m+showsIpAddress :: IpAddress -> String -> String+showsIpAddress (IpAddress w) =+  shows ((w .>. 24) .&. 0xff) . ('.':) .+  shows ((w .>. 16) .&. 0xff) . ('.':) .+  shows ((w .>.  8) .&. 0xff) . ('.':) .+  shows ( w         .&. 0xff) -textToMaybeIpv4Address :: T.Text -> Maybe Ipv4Address-textToMaybeIpv4Address t = AP.maybeResult =<< AP.parseWith (return mempty) parseIpv4Address t+showIpAddress :: IpAddress -> String+showIpAddress ipAddress = showsIpAddress ipAddress "" -ipv4AddressToString :: Ipv4Address -> String-ipv4AddressToString = show+tshowIpAddress :: IpAddress -> T.Text+tshowIpAddress = T.pack . showIpAddress -ipv4AddressToText :: Ipv4Address -> T.Text-ipv4AddressToText = T.pack . ipv4AddressToString+showsIpBlock :: IpBlock v -> String -> String+showsIpBlock (IpBlock b (IpNetMask m)) = shows b . ('/':) . shows m -ipv4AddressToWords :: Ipv4Address -> (Word8, Word8, Word8, Word8)-ipv4AddressToWords (Ipv4Address w) =+showIpBlock :: IpBlock v -> String+showIpBlock ipBlock = showsIpBlock ipBlock ""++tshowIpBlock :: IpBlock v -> T.Text+tshowIpBlock = T.pack . showIpBlock++ipAddressToWords :: IpAddress -> (Word8, Word8, Word8, Word8)+ipAddressToWords (IpAddress w) =   ( fromIntegral (w .>. 24) .&. 0xff   , fromIntegral (w .>. 16) .&. 0xff   , fromIntegral (w .>.  8) .&. 0xff   , fromIntegral (w         .&. 0xff)   ) -parseIpv4Address :: AP.Parser Ipv4Address-parseIpv4Address = Ipv4Address <$> I.ipv4Address+parseIpAddress :: AP.Parser IpAddress+parseIpAddress = IpAddress <$> I.ipv4Address++parseUnalignedIpBlock :: AP.Parser (IpBlock Unaligned)+parseUnalignedIpBlock = do+  (a, m) <- I.ipv4Block+  return (IpBlock (IpAddress a) (IpNetMask m))++parseCanonicalIpBlock :: AP.Parser (IpBlock Canonical)+parseCanonicalIpBlock = do+  b <- parseUnalignedIpBlock+  if isCanonical b+    then let IpBlock ip m = b in return (IpBlock ip m)+    else fail $ showIpBlock b <> " is not a canonical block"++splitIpRange :: Range IpAddress -> (IpBlock Canonical, Maybe (Range IpAddress))+splitIpRange (Range (IpAddress a) (IpAddress z)) = (block, remainder)+  where bpOuter   = width - B.countLeadingZeros (z + 1 - a) - 1+        bpInner   = B.countTrailingZeros ((maxBound .<. fromIntegral bpOuter) .|. a)+        block     = IpBlock (IpAddress a) (IpNetMask (fromIntegral (width - bpInner)))+        hostMask  = comp (maxBound .<. fromIntegral bpInner)+        remainder = if a + hostMask >= z+          then Nothing+          else Just (Range (IpAddress (a + hostMask + 1)) (IpAddress z))+        width = B.finiteBitSize a++-- assume distinct & sorted input+collapseIpBlocks :: [IpBlock Canonical] -> [IpBlock Canonical]+collapseIpBlocks tomerge =+  skipOverlapped $ concat $ toList <$> go S.empty tomerge+  where+    go :: S.Seq (IpBlock Canonical) -> [IpBlock Canonical] -> [S.Seq (IpBlock Canonical)]+    go m [] = [m]+    go m (b:bs) =+      case S.viewr m of+        S.EmptyR -> go (m S.|> b) bs+        m' S.:> bp -> do+          let sp@(IpBlock _ (IpNetMask msk)) = superBlock bp+          let sp'@(IpBlock _ (IpNetMask msk')) = superBlock b+          if sp == sp' then go m' (sp : bs)+          else if msk > msk' then+                 m : go (S.empty S.|> b) bs+               else+                 go (m S.|> b) bs+    superBlock (IpBlock (IpAddress w32) (IpNetMask m)) =+      IpBlock (IpAddress (w32 B..&. (0xFFFFFFFF `B.shiftL` fromIntegral (32 - (m - 1))))) (IpNetMask (m - 1))+    skipOverlapped [] = []+    skipOverlapped [b] = [b]+    skipOverlapped (b1:b2:bs) =+      if lastIpAddress b1 >= lastIpAddress b2 then+        skipOverlapped (b1:bs)+      else+        b1 : skipOverlapped (b2:bs)++rangeToBlocksDL :: Range IpAddress -> [IpBlock Canonical] -> [IpBlock Canonical]+rangeToBlocksDL r = do+  let (b, remainder) = splitIpRange r+  case remainder of+    Just rmd -> (b:) . rangeToBlocksDL rmd+    Nothing  -> (b:)++rangeToBlocks :: Range IpAddress -> [IpBlock Canonical]+rangeToBlocks r = rangeToBlocksDL r []++blockToRange :: IpBlock Canonical -> Range IpAddress+blockToRange b = uncurry Range $ bimap firstIpAddress lastIpAddress (b, b)
src/HaskellWorks/Data/Network/Ip/Ipv6.hs view
@@ -1,106 +1,131 @@ {-# LANGUAGE DataKinds                  #-} {-# LANGUAGE DeriveGeneric              #-} {-# LANGUAGE DuplicateRecordFields      #-}+{-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs               #-} {-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE TypeApplications           #-}  module HaskellWorks.Data.Network.Ip.Ipv6-  ( Ipv6Address(..)-  , Ipv6NetMask(..)-  , Ipv6Block(..)-  , ipv4BlockToMappedIpv6Block-  , parseIpv6Block-  , masksIpv6-  , isValidIpv6Block+  ( IpAddress(..)+  , IpNetMask(..)+  , IpBlock(..)+  , fromV4+  , parseIpBlock+  , masksIp+  , showIpAddress+  , showsIpAddress+  , tshowIpAddress+  , tshowIpBlock+  , firstIpAddress+  , lastIpAddress+  , rangeToBlocks+  , rangeToBlocksDL+  , blockToRange+  , isCanonical+  , splitIpRange   ) where  import Control.Applicative import Control.Monad-import Data.Bits-import Data.Char-import Data.Generics.Product.Any+import Data.Bifunctor import Data.Maybe import Data.Word import GHC.Generics-import Prelude                   hiding (words)+import HaskellWorks.Data.Network.Ip.Range+import HaskellWorks.Data.Network.Ip.SafeEnum+import HaskellWorks.Data.Network.Ip.Validity+import Prelude                               hiding (words) import Text.Read -import qualified Data.Attoparsec.Text                  as AP import qualified Data.Bits                             as B import qualified Data.IP                               as D-import qualified Data.String                           as S import qualified Data.Text                             as T import qualified HaskellWorks.Data.Network.Ip.Internal as I-import qualified HaskellWorks.Data.Network.Ip.Ipv4     as I4-import qualified Text.ParserCombinators.ReadPrec       as RP+import qualified HaskellWorks.Data.Network.Ip.Ipv4     as V4+import qualified HaskellWorks.Data.Network.Ip.Word128  as W -newtype Ipv6Address = Ipv6Address-  { words :: (Word32, Word32, Word32, Word32)-  } deriving (Eq, Ord, Generic)+newtype IpAddress = IpAddress W.Word128 deriving (Eq, Ord, Generic, SafeEnum) -instance Show Ipv6Address where-  showsPrec _ (Ipv6Address w) = shows (D.fromHostAddress6 w)+instance Show IpAddress where+  showsPrec _ (IpAddress w) = shows (D.fromHostAddress6 w) -instance Read Ipv6Address where-  readsPrec :: Int -> String -> [(Ipv6Address, String)]+instance Read IpAddress where+  readsPrec :: Int -> String -> [(IpAddress, String)]   readsPrec _ s =     case readMaybe s :: Maybe D.IPv6 of-      Just ip -> [(Ipv6Address (D.toHostAddress6 ip), "")]+      Just ip -> [(IpAddress (D.toHostAddress6 ip), "")]       Nothing -> [] -newtype Ipv6NetMask = Ipv6NetMask+newtype IpNetMask = IpNetMask   { word :: Word8   } deriving (Enum, Eq, Ord, Show, Generic) -instance Read Ipv6NetMask where+instance Read IpNetMask where   readsPrec _ s =-    case Ipv6NetMask <$> m of+    case IpNetMask <$> m of       Just maskv6 -> [(maskv6, "")]       Nothing     -> []     where       m = mfilter (\a -> a >= 0 && a <= 128) (readMaybe s) -data Ipv6Block = Ipv6Block-  { base :: !Ipv6Address-  , mask :: !Ipv6NetMask+data IpBlock v = IpBlock+  { base :: !IpAddress+  , mask :: !IpNetMask   } deriving (Eq, Ord, Generic) -instance Read Ipv6Block where+instance Read (IpBlock Unaligned) where   readsPrec _ s =     case T.unpack <$> T.split (== '/') (T.pack s) of-      [addr, mask] ->-        case readMaybe addr :: Maybe Ipv6Address of+      [addr, msk] ->+        case readMaybe addr :: Maybe IpAddress of           Just ipv6 ->-            case readMaybe mask of-              Just maskv6 ->-                let i6b = Ipv6Block ipv6 maskv6 in-                  [(i6b, "") | isValidIpv6Block i6b]+            case readMaybe msk of+              Just mskv6 ->+                let i6b = IpBlock ipv6 mskv6 in+                  [(i6b, "") | isCanonical i6b]               Nothing     -> []           Nothing -> []       _ -> [] -instance Show Ipv6Block where-  showsPrec _ (Ipv6Block b (Ipv6NetMask m))  = shows b . ('/':) . shows m+instance Show (IpBlock v) where+  showsPrec _ (IpBlock b (IpNetMask m))  = shows b . ('/':) . shows m -parseIpv6Block :: T.Text -> Either T.Text Ipv6Block-parseIpv6Block t =+parseIpBlock :: T.Text -> Either T.Text (IpBlock Unaligned)+parseIpBlock t =   case T.unpack <$> T.split (== '/') t of-    [addr, mask] ->-      case readMaybe addr :: Maybe Ipv6Address of+    [addr, msk] ->+      case readMaybe addr :: Maybe IpAddress of         Just ipv6 ->-          case readMaybe mask of-            Just maskv6 -> Right $ Ipv6Block ipv6 maskv6-            Nothing     -> Left "cannot read mask"+          case readMaybe msk of+            Just mskv6 -> Right $ IpBlock ipv6 mskv6+            Nothing    -> Left "cannot read mask"         Nothing -> Left "cannot read addr"     _ -> Left "invalid input string" -masksIpv6 :: Word8 -> [Word32]-masksIpv6 m =+showsIpAddress :: IpAddress -> String -> String+showsIpAddress (IpAddress w) = shows (D.fromHostAddress6 w)++showIpAddress :: IpAddress -> String+showIpAddress ipAddress = showsIpAddress ipAddress ""++tshowIpAddress :: IpAddress -> T.Text+tshowIpAddress = T.pack . showIpAddress++showsIpBlock :: IpBlock v -> String -> String+showsIpBlock (IpBlock b (IpNetMask m)) = shows b . ('/':) . shows m++showIpBlock :: IpBlock v -> String+showIpBlock ipBlock = showsIpBlock ipBlock ""++tshowIpBlock :: IpBlock v -> T.Text+tshowIpBlock = T.pack . showIpBlock++masksIp :: Word8 -> [Word32]+masksIp m =   let e = 0xFFFFFFFF :: Word32       -- bits: number of bits which should be 1-      maskValue bits = e `shiftR` (32 - bits) in+      maskValue bits = e `B.shiftR` (32 - bits) in     if m < 32 then       [maskValue (32 - fromIntegral m), e, e, e]     else if m < 64 then@@ -112,13 +137,43 @@     else       [0, 0, 0, 0] -isValidIpv6Block :: Ipv6Block -> Bool-isValidIpv6Block (Ipv6Block b (Ipv6NetMask m)) =-  let lt = masksIpv6 m-      ipv6 = I.word32x4ToWords (words b) in-    ipv6 == zipWith (.&.) ipv6 (zipWith xor ipv6 lt)+isCanonical :: IpBlock v -> Bool+isCanonical (IpBlock (IpAddress w) (IpNetMask m)) =+  let lt = masksIp m+      ipv6 = I.word32x4ToWords w in+    ipv6 == zipWith (B..&.) ipv6 (zipWith B.xor ipv6 lt) -ipv4BlockToMappedIpv6Block :: I4.Ipv4Block -> Ipv6Block-ipv4BlockToMappedIpv6Block (I4.Ipv4Block b m) =+fromV4 :: V4.IpBlock Canonical -> IpBlock v+fromV4 (V4.IpBlock b m) =   -- RFC-4291, "IPv4-Mapped IPv6 Address"-  Ipv6Block (Ipv6Address (0, 0, 0xFFFF, I4.word b)) (Ipv6NetMask (96 + I4.word8 m))+  IpBlock (IpAddress (0, 0, 0xFFFF, V4.word b)) (IpNetMask (96 + V4.word8 m))++firstIpAddress :: IpBlock Canonical -> IpAddress+firstIpAddress (IpBlock b _) = b++lastIpAddress :: IpBlock Canonical -> IpAddress+lastIpAddress (IpBlock (IpAddress b) (IpNetMask m)) = IpAddress (b + fromIntegral (I.blockSize128 m) - 1)++splitIpRange :: Range IpAddress -> (IpBlock Canonical, Maybe (Range IpAddress))+splitIpRange (Range (IpAddress a) (IpAddress z)) = (block, remainder)+  where bpOuter   = width - B.countLeadingZeros (z + 1 - a) - 1+        bpInner   = B.countTrailingZeros ((maxBound `B.shiftL` fromIntegral bpOuter) B..|. a)+        block     = IpBlock (IpAddress a) (IpNetMask (fromIntegral (width - bpInner)))+        hostMask  = B.complement (maxBound `B.shiftL` fromIntegral bpInner)+        remainder = if a + hostMask >= z+          then Nothing+          else Just (Range (IpAddress (a + hostMask + 1)) (IpAddress z))+        width = B.finiteBitSize a++rangeToBlocksDL :: Range IpAddress -> [IpBlock Canonical] -> [IpBlock Canonical]+rangeToBlocksDL r = do+  let (b, remainder) = splitIpRange r+  case remainder of+    Just rmd -> (b:) . rangeToBlocksDL rmd+    Nothing  -> (b:)++rangeToBlocks :: Range IpAddress -> [IpBlock Canonical]+rangeToBlocks r = rangeToBlocksDL r []++blockToRange :: IpBlock Canonical -> Range IpAddress+blockToRange b = uncurry Range $ bimap firstIpAddress lastIpAddress (b, b)
+ src/HaskellWorks/Data/Network/Ip/Range.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE MonoLocalBinds    #-}+{-# LANGUAGE OverloadedStrings #-}++module HaskellWorks.Data.Network.Ip.Range where++import GHC.Generics+import HaskellWorks.Data.Network.Ip.SafeEnum++import qualified Text.Appar.String as AP++data Range a = Range+  { first :: a+  , last  :: a+  } deriving (Eq, Ord, Show, Generic)++parseRange :: AP.Parser a -> AP.Parser (Range a)+parseRange pa = Range <$> pa <* AP.string " - " <*> pa++-- | Merge adjacent ranges if they overlap or are adjacent+mergeRanges :: (SafeEnum a, Ord a) => [Range a] -> [Range a]+mergeRanges (r1@(Range f1 l1):r2@(Range f2 l2):rs)+  | boundedSucc l1 >= f2  = mergeRanges (nr:rs)+  | otherwise = r1 : mergeRanges (r2:rs)+  where nr = Range f1 (max l1 l2)+mergeRanges [r] = [r]+mergeRanges [] = []
+ src/HaskellWorks/Data/Network/Ip/SafeEnum.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TypeSynonymInstances #-}++module HaskellWorks.Data.Network.Ip.SafeEnum+  ( SafeEnum(..)+  , boundedPred+  , boundedSucc+  ) where++import Data.Int+import Data.Maybe+import Data.Word+import HaskellWorks.Data.Network.Ip.Word128++class SafeEnum a where+  safePred :: a -> Maybe a+  safeSucc :: a -> Maybe a++instance SafeEnum Word where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Word8 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Word16 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Word32 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Word64 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Word128 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Int where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Int8 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Int16 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Int32 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Int64 where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Char where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Bool where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum () where+  safePred = defaultSafePred+  safeSucc = defaultSafeSucc++instance SafeEnum Integer where+  safePred = Just . pred+  safeSucc = Just . succ++boundedPred :: SafeEnum a => a -> a+boundedPred a = fromMaybe a (safePred a)++boundedSucc :: SafeEnum a => a -> a+boundedSucc a = fromMaybe a (safeSucc a)++defaultSafePred :: (Bounded a, Enum a, Eq a) => a -> Maybe a+defaultSafePred v = if v /= minBound then Just (pred v) else Nothing++defaultSafeSucc :: (Bounded a, Enum a, Eq a) => a -> Maybe a+defaultSafeSucc v = if v /= maxBound then Just (succ v) else Nothing
− src/HaskellWorks/Data/Network/Ip/Type.hs
@@ -1,14 +0,0 @@-{-# LANGUAGE DuplicateRecordFields      #-}-{-# LANGUAGE InstanceSigs               #-}--module HaskellWorks.Data.Network.Ip.Type-  ( I4.Ipv4Address(..)-  , I4.Ipv4NetMask(..)-  , I4.Ipv4Block(..)-  , I6.Ipv6Address(..)-  , I6.Ipv6NetMask(..)-  , I6.Ipv6Block(..)-  ) where--import qualified HaskellWorks.Data.Network.Ip.Ipv4 as I4-import qualified HaskellWorks.Data.Network.Ip.Ipv6 as I6
+ src/HaskellWorks/Data/Network/Ip/Validity.hs view
@@ -0,0 +1,5 @@+module HaskellWorks.Data.Network.Ip.Validity where++data Canonical = Canonical++data Unaligned = Unaligned
+ src/HaskellWorks/Data/Network/Ip/Word128.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DataKinds             #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE InstanceSigs          #-}+{-# LANGUAGE TypeSynonymInstances  #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module HaskellWorks.Data.Network.Ip.Word128 where++import Data.Maybe+import Data.Word+import Prelude    hiding (words)++import qualified Data.Bits as B++type Word128 = (Word32, Word32, Word32, Word32)++instance Enum Word128 where+  fromEnum  = fromIntegral . word128ToInteger+  toEnum i  = integerToWord128 $ fromIntegral i+  succ (0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff) = (0, 0, 0, 0)+  succ (a,          0xffffffff, 0xffffffff, 0xffffffff) = (succ a, 0, 0, 0)+  succ (a,                   b, 0xffffffff, 0xffffffff) = (a, succ b, 0, 0)+  succ (a,                   b,          c, 0xffffffff) = (a, b, succ c, 0)+  succ (a,                   b,          c,          d) = (a, b, c, succ d)+  pred (0, 0, 0, 0) = (0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff)+  pred (a, 0, 0, 0) = (    pred a, 0xffffffff, 0xffffffff, 0xffffffff)+  pred (a, b, 0, 0) = (         a,     pred b, 0xffffffff, 0xffffffff)+  pred (a, b, c, 0) = (         a,          b,     pred c, 0xffffffff)+  pred (a, b, c, d) = (         a,          b,          c,     pred d)++integerToWord128 :: Integer -> Word128+integerToWord128 i = let a  = fromIntegral (i `B.shiftR` 96 B..&. 0xffffffff)+                         b  = fromIntegral (i `B.shiftR` 64 B..&. 0xffffffff)+                         c  = fromIntegral (i `B.shiftR` 32 B..&. 0xffffffff)+                         d  = fromIntegral (i `B.shiftR` 00 B..&. 0xffffffff)+                     in (a, b, c, d)++word128ToInteger :: Word128 -> Integer+word128ToInteger (a, b, c, d) = let a' = fromIntegral a `B.shift` 96+                                    b' = fromIntegral b `B.shift` 64+                                    c' = fromIntegral c `B.shift` 32+                                    d' = fromIntegral d `B.shift` 0+                                in a' B..|. b' B..|. c' B..|. d' :: Integer++instance Num Word128 where+  (+) l r     = integerToWord128 $ (word128ToInteger l) + (word128ToInteger r)+  (-) l r     = integerToWord128 $ (word128ToInteger l) - (word128ToInteger r)+  (*) l r     = integerToWord128 $ (word128ToInteger l) * (word128ToInteger r)+  abs a       = a+  signum (0, 0, 0, 0) = minBound+  signum _            = 1+  fromInteger = integerToWord128++instance B.Bits Word128 where+  (.&.) (a, b, c, d) (e, f, g, h) = (a B..&. e, b B..&. f, c B..&. g, d B..&. h)+  (.|.) (a, b, c, d) (e, f, g, h) = (a B..|. e, b B..|. f, c B..|. g, d B..|. h)+  xor (a, b, c, d) (e, f, g, h)   = (a `B.xor` e, b `B.xor` f, c `B.xor` g, d `B.xor` h)+  complement (a, b, c, d)         = (B.complement a, B.complement b, B.complement c, B.complement d)+  shift w n                       = integerToWord128 $ word128ToInteger w `B.shift` n+  shiftL w n+    | n < 0 = minBound  -- This is the special case to make it behaviour as the same as Word32+    | otherwise = integerToWord128 $ word128ToInteger w `B.shiftL` n+  shiftR w n                      = integerToWord128 $ word128ToInteger w `B.shiftR` n+  rotate w n                      = integerToWord128 $ word128ToInteger w `B.rotate` n+  rotateL w n                     = integerToWord128 $ word128ToInteger w `B.rotateL` n+  rotateR w n                     = integerToWord128 $ word128ToInteger w `B.rotateR` n+  bitSize _                       = 128+  bitSizeMaybe _                  = Just 128+  isSigned _                      = False+  testBit w                       = B.testBit (word128ToInteger w)+  bit n                           = integerToWord128 $ B.bit n+  popCount w                      = B.popCount $ word128ToInteger w++instance B.FiniteBits Word128 where+  finiteBitSize _ = 128
+ test/HaskellWorks/Data/Network/Gen.hs view
@@ -0,0 +1,31 @@+module HaskellWorks.Data.Network.Gen+  ( canonicalIpv4Block+  , canonicalIpv6Block+  ) where++import HaskellWorks.Data.Bits.BitWise+import HaskellWorks.Data.Network.Ip.Validity+import Hedgehog++import qualified Data.Bits                         as B+import qualified HaskellWorks.Data.Network.Ip.Ipv4 as V4+import qualified HaskellWorks.Data.Network.Ip.Ipv6 as V6+import qualified Hedgehog.Gen                      as G+import qualified Hedgehog.Range                    as R++canonicalIpv4Block :: MonadGen m => m (V4.IpBlock Canonical)+canonicalIpv4Block = do+  m <- G.word8   (R.linear 0 32)+  let p = fromIntegral (32 - m)+  w <- G.word32  (R.linear 0 ((0xffffffff .>. p) .<. p))+  return (V4.IpBlock (V4.IpAddress (w .<. p)) (V4.IpNetMask m))++canonicalIpv6Block :: MonadGen m => m (V6.IpBlock Canonical)+canonicalIpv6Block = do+  m <- G.word8   (R.linear 0 128)+  let p = fromIntegral (128 - m)+  w128 <- (,,,) <$> G.word32 (R.linear 0 0xffffffff)+                <*> G.word32 (R.linear 0 0xffffffff)+                <*> G.word32 (R.linear 0 0xffffffff)+                <*> G.word32 (R.linear 0 0xffffffff)+  return (V6.IpBlock (V6.IpAddress (w128 `B.shift` p)) (V6.IpNetMask m))
+ test/HaskellWorks/Data/Network/IpBlockSpec.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications  #-}++module HaskellWorks.Data.Network.IpBlockSpec (spec) where++import HaskellWorks.Data.Network.Ip.Validity+import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified HaskellWorks.Data.Network.Ip.Ip   as V+import qualified HaskellWorks.Data.Network.Ip.Ipv6 as V6++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.Network.IpSpec" $ do+  describe "IpBlock" $ do+    it "should implement show" $ requireTest $ do+      show (V.IpBlockV6 @Unaligned (V6.IpBlock (V6.IpAddress (3, 3, 3, 0)) (V6.IpNetMask 96))) === "0:3:0:3:0:3::/96"++    it "should implement read" $ requireTest $ do+      read "1:2:3:4::/127"  === V.IpBlockV6 @Unaligned (V6.IpBlock (V6.IpAddress (0x10002    , 0x30004 , 0, 0)) (V6.IpNetMask 127))+      read "1234::/16"      === V.IpBlockV6 @Unaligned (V6.IpBlock (V6.IpAddress (0x12340000 , 0       , 0, 0)) (V6.IpNetMask  16))+      read "12:34::/32"     === V.IpBlockV6 @Unaligned (V6.IpBlock (V6.IpAddress (0x120034   , 0       , 0, 0)) (V6.IpNetMask  32))
− test/HaskellWorks/Data/Network/IpSpec.hs
@@ -1,82 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module HaskellWorks.Data.Network.IpSpec (spec) where--import HaskellWorks.Data.Network.Ip-import HaskellWorks.Data.Network.Ip.Ipv4-import HaskellWorks.Data.Network.Ip.Internal-import HaskellWorks.Hspec.Hedgehog-import Hedgehog-import Test.Hspec--import qualified Data.Attoparsec.Text as AP-import qualified Text.Read as TR-import qualified Data.Text as T-import qualified Hedgehog.Gen as G-import qualified Hedgehog.Range as R--{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}--spec :: Spec-spec = describe "HaskellWorks.HUnit.IpSpec" $ do-  describe "octet" $ do-    it "should go from 0-255" $ require $ property $ do-      b <- forAll $ G.word8 R.constantBounded-      AP.parseOnly octet (T.pack . show $ b) === Right b--  describe "Ipv4Address" $ do-    it "should implement show" $ requireTest $ do-      show (Ipv4Address 0x000000ff) === "0.0.0.255"-      show (Ipv4Address 0x0000ff00) === "0.0.255.0"-      show (Ipv4Address 0x00ff0000) === "0.255.0.0"-      show (Ipv4Address 0xff000000) === "255.0.0.0"--    it "should implement read" $ requireTest $ do-      read "1.2.3.4"      === Ipv4Address 0x01020304-      read "10.20.30.40"  === Ipv4Address 0x0a141e28-      read "1.2.3.12"     === Ipv4Address 0x0102030c-      read "1.2.3.160"    === Ipv4Address 0x010203a0--    it "should be possible to extract the octets" $ requireTest $ do-      ipv4AddressToWords (Ipv4Address 0x01020304) === (1, 2, 3, 4)--  describe "Ipv4Block" $ do-    it "should implement show" $ requireTest $ do-      show (Ipv4Block (Ipv4Address 0x000000ff) (Ipv4NetMask 32)) === "0.0.0.255/32"-      show (Ipv4Block (Ipv4Address 0x0000ff00) (Ipv4NetMask 32)) === "0.0.255.0/32"-      show (Ipv4Block (Ipv4Address 0x00ff0000) (Ipv4NetMask 32)) === "0.255.0.0/32"-      show (Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 32)) === "255.0.0.0/32"-      show (Ipv4Block (Ipv4Address 0x000000ff) (Ipv4NetMask 16)) === "0.0.0.255/16"-      show (Ipv4Block (Ipv4Address 0x0000ff00) (Ipv4NetMask 16)) === "0.0.255.0/16"-      show (Ipv4Block (Ipv4Address 0x00ff0000) (Ipv4NetMask 16)) === "0.255.0.0/16"-      show (Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 16)) === "255.0.0.0/16"-      show (firstIpv4Address $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 8))  === "255.0.0.0"-      show (lastIpv4Address  $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 8))  === "255.255.255.255"-      show (firstIpv4Address $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 16)) === "255.0.0.0"-      show (lastIpv4Address  $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 16)) === "255.0.255.255"-      show (firstIpv4Address $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 24)) === "255.0.0.0"-      show (lastIpv4Address  $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 24)) === "255.0.0.255"-      show (firstIpv4Address $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 32)) === "255.0.0.0"-      show (lastIpv4Address  $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 32)) === "255.0.0.0"-      show (firstIpv4Address $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 21)) === "255.0.0.0"-      show (lastIpv4Address  $ Ipv4Block (Ipv4Address 0xff000000) (Ipv4NetMask 21)) === "255.0.7.255"--    it "should implement read" $ requireTest $ do-      read "1.0.0.0/8"  === Ipv4Block (Ipv4Address 0x01000000) (Ipv4NetMask 8)-      read "1.2.0.0/16" === Ipv4Block (Ipv4Address 0x01020000) (Ipv4NetMask 16)-      read "1.2.3.4/32" === Ipv4Block (Ipv4Address 0x01020304) (Ipv4NetMask 32)--    it "should implement splitBlock" $ requireTest $ do-      splitBlock (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask 32)) === Nothing-      splitBlock (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask 31)) === Just (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask 32), Ipv4Block (Ipv4Address 0x00000001) (Ipv4NetMask 32))-      splitBlock (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask 30)) === Just (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask 31), Ipv4Block (Ipv4Address 0x00000002) (Ipv4NetMask 31))-      splitBlock (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask  0)) === Just (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask  1), Ipv4Block (Ipv4Address 0x80000000) (Ipv4NetMask  1))--    it "should implement blockSize" $ requireTest $ do-      blockSize (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask 32)) === 1-      blockSize (Ipv4Block (Ipv4Address 0x00000000) (Ipv4NetMask  0)) === 0x100000000--    it "should validate masks" $ requireTest $ do-      (TR.readMaybe "1.2.3.4/8"  :: Maybe Ipv4Block) === Nothing-      (TR.readMaybe "1.2.3.4/0"  :: Maybe Ipv4Block) === Nothing-      (TR.readMaybe "1.2.3.4/32" :: Maybe Ipv4Block) === (Just $ Ipv4Block (Ipv4Address 0x01020304) (Ipv4NetMask 32))
+ test/HaskellWorks/Data/Network/Ipv4Spec.hs view
@@ -0,0 +1,210 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications  #-}++module HaskellWorks.Data.Network.Ipv4Spec (spec) where++import HaskellWorks.Data.Network.Ip.Internal+import HaskellWorks.Data.Network.Ip.Ipv4+import HaskellWorks.Data.Network.Ip.Range+import HaskellWorks.Data.Network.Ip.Validity+import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified Data.List                             as DL+import qualified HaskellWorks.Data.Network.Gen         as G+import qualified HaskellWorks.Data.Network.Ip.Internal as I+import qualified HaskellWorks.Data.Network.Ip.Ipv4     as I+import qualified HaskellWorks.Data.Network.Ip.Range    as I+import qualified Hedgehog.Gen                          as G+import qualified Hedgehog.Range                        as R+import qualified Text.Appar.String                     as AP++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.Network.Ipv4Spec" $ do+  describe "octet" $ do+    it "should go from 0-255" $ require $ property $ do+      b <- forAll $ G.word8 R.constantBounded+      AP.runParser octet (show b) === (Just b, "")++  describe "I.IpAddress" $ do+    it "should implement show" $ requireTest $ do+      show (I.IpAddress 0x000000ff) === "0.0.0.255"+      show (I.IpAddress 0x0000ff00) === "0.0.255.0"+      show (I.IpAddress 0x00ff0000) === "0.255.0.0"+      show (I.IpAddress 0xff000000) === "255.0.0.0"++    it "should implement read" $ requireTest $ do+      read "1.2.3.4"      === I.IpAddress 0x01020304+      read "10.20.30.40"  === I.IpAddress 0x0a141e28+      read "1.2.3.12"     === I.IpAddress 0x0102030c+      read "1.2.3.160"    === I.IpAddress 0x010203a0++    it "should be possible to extract the octets" $ requireTest $ do+      I.ipAddressToWords (I.IpAddress 0x01020304) === (1, 2, 3, 4)++  describe "I.IpBlock" $ do+    it "should implement show" $ requireTest $ do+      show (I.IpBlock (I.IpAddress 0x000000ff) (I.IpNetMask 32)) === "0.0.0.255/32"+      show (I.IpBlock (I.IpAddress 0x0000ff00) (I.IpNetMask 32)) === "0.0.255.0/32"+      show (I.IpBlock (I.IpAddress 0x00ff0000) (I.IpNetMask 32)) === "0.255.0.0/32"+      show (I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 32)) === "255.0.0.0/32"+      show (I.IpBlock (I.IpAddress 0x000000ff) (I.IpNetMask 16)) === "0.0.0.255/16"+      show (I.IpBlock (I.IpAddress 0x0000ff00) (I.IpNetMask 16)) === "0.0.255.0/16"+      show (I.IpBlock (I.IpAddress 0x00ff0000) (I.IpNetMask 16)) === "0.255.0.0/16"+      show (I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 16)) === "255.0.0.0/16"+      show (I.firstIpAddress $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 8))  === "255.0.0.0"+      show (I.lastIpAddress  $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 8))  === "255.255.255.255"+      show (I.firstIpAddress $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 16)) === "255.0.0.0"+      show (I.lastIpAddress  $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 16)) === "255.0.255.255"+      show (I.firstIpAddress $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 24)) === "255.0.0.0"+      show (I.lastIpAddress  $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 24)) === "255.0.0.255"+      show (I.firstIpAddress $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 32)) === "255.0.0.0"+      show (I.lastIpAddress  $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 32)) === "255.0.0.0"+      show (I.firstIpAddress $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 21)) === "255.0.0.0"+      show (I.lastIpAddress  $ I.IpBlock (I.IpAddress 0xff000000) (I.IpNetMask 21)) === "255.0.7.255"++    it "should implement read" $ requireTest $ do+      read "1.0.0.0/8"  === I.IpBlock @Unaligned (I.IpAddress 0x01000000) (I.IpNetMask 8)+      read "1.2.0.0/16" === I.IpBlock @Unaligned (I.IpAddress 0x01020000) (I.IpNetMask 16)+      read "1.2.3.4/32" === I.IpBlock @Unaligned (I.IpAddress 0x01020304) (I.IpNetMask 32)++    it "should implement splitBlock" $ requireTest $ do+      I.splitBlock (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask 32)) === Nothing+      I.splitBlock (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask 31)) === Just (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask 32), I.IpBlock (I.IpAddress 0x00000001) (I.IpNetMask 32))+      I.splitBlock (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask 30)) === Just (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask 31), I.IpBlock (I.IpAddress 0x00000002) (I.IpNetMask 31))+      I.splitBlock (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask  0)) === Just (I.IpBlock (I.IpAddress 0x00000000) (I.IpNetMask  1), I.IpBlock (I.IpAddress 0x80000000) (I.IpNetMask  1))++    it "should implement blockSize" $ requireTest $ do+      I.blockSize 32 === 0x000000001+      I.blockSize  0 === 0x100000000++    it "should validate masks" $ requireTest $ do+      (read "1.2.3.4/8"  :: I.IpBlock Unaligned) === I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask  8)+      (read "1.2.3.4/0"  :: I.IpBlock Unaligned) === I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask  0)+      (read "1.2.3.4/32" :: I.IpBlock Unaligned) === I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask 32)++    it "should canonicalise block" $ requireTest $ do+      I.canonicaliseIpBlock (I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask 32)) === I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask 32)+      I.canonicaliseIpBlock (I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask 24)) === I.IpBlock (I.IpAddress 0x01020300) (I.IpNetMask 24)+      I.canonicaliseIpBlock (I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask 16)) === I.IpBlock (I.IpAddress 0x01020000) (I.IpNetMask 16)+      I.canonicaliseIpBlock (I.IpBlock (I.IpAddress 0x01020304) (I.IpNetMask  8)) === I.IpBlock (I.IpAddress 0x01000000) (I.IpNetMask  8)++    it "should collapse blocks" $ requireTest $ do+      let ipblocks1 = read @(IpBlock Canonical) <$> ["1.2.3.4/32", "4.3.2.1/32"]+      collapseIpBlocks ipblocks1 === ipblocks1+      let ipblocks2 = read @(IpBlock Canonical) <$> ["1.2.3.3/32", "1.2.3.0/32", "1.2.3.1/32", "1.2.3.2/32"]+      collapseIpBlocks (DL.sort ipblocks2) === (read @(IpBlock Canonical) <$> ["1.2.3.0/30"])+      let ipblocks3 = read @(IpBlock Canonical) <$> ["1.2.3.3/32", "1.2.3.0/32", "1.2.3.1/32", "1.2.3.2/32", "5.5.5.5/32"]+      collapseIpBlocks (DL.sort ipblocks3) === (read @(IpBlock Canonical) <$> ["1.2.3.0/30", "5.5.5.5/32"])++  describe "should split ranges" $ do+    it "0.0.0.0 - 0.0.0.0" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.0" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 32), Nothing)+    it "0.0.0.1 - 0.0.0.1" $ requireTest $ do+      let ip1 = read "0.0.0.1" :: I.IpAddress+      let ip2 = read "0.0.0.1" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 32), Nothing)+    it "0.0.0.0 - 0.0.0.1" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.1" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 31), Nothing)+    it "0.0.0.0 - 0.0.0.2" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.2" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 31), Just (Range ip2 ip2))+    it "0.0.0.0 - 0.0.0.3" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.3" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 30), Nothing)+    it "0.0.0.0 - 0.0.0.4" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.4" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 30), Just (Range ip2 ip2))+    it "0.0.0.0 - 0.0.0.5" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.4" :: I.IpAddress+      let ip3 = read "0.0.0.5" :: I.IpAddress+      I.splitIpRange (Range ip1 ip3) === (I.IpBlock ip1 (I.IpNetMask 30), Just (Range ip2 ip3))+    it "0.0.0.0 - 0.0.0.5" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.4" :: I.IpAddress+      let ip3 = read "0.0.0.6" :: I.IpAddress+      I.splitIpRange (Range ip1 ip3) === (I.IpBlock ip1 (I.IpNetMask 30), Just (Range ip2 ip3))+    it "0.0.0.0 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.0" :: I.IpAddress+      let ip2 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 29), Nothing)+    it "0.0.0.1 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.1" :: I.IpAddress+      let ip2 = read "0.0.0.2" :: I.IpAddress+      let ip3 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip3) === (I.IpBlock ip1 (I.IpNetMask 32), Just (Range ip2 ip3))+    it "0.0.0.2 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.2" :: I.IpAddress+      let ip2 = read "0.0.0.4" :: I.IpAddress+      let ip3 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip3) === (I.IpBlock ip1 (I.IpNetMask 31), Just (Range ip2 ip3))+    it "0.0.0.3 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.3" :: I.IpAddress+      let ip2 = read "0.0.0.4" :: I.IpAddress+      let ip3 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip3) === (I.IpBlock ip1 (I.IpNetMask 32), Just (Range ip2 ip3))+    it "0.0.0.4 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.4" :: I.IpAddress+      let ip2 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 30), Nothing)+    it "0.0.0.5 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.5" :: I.IpAddress+      let ip2 = read "0.0.0.6" :: I.IpAddress+      let ip3 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip3) === (I.IpBlock ip1 (I.IpNetMask 32), Just (Range ip2 ip3))+    it "0.0.0.6 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.6" :: I.IpAddress+      let ip2 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 31), Nothing)+    it "0.0.0.7 - 0.0.0.7" $ requireTest $ do+      let ip1 = read "0.0.0.7" :: I.IpAddress+      I.splitIpRange (Range ip1 ip1) === (I.IpBlock ip1 (I.IpNetMask 32), Nothing)+    it "0.0.0.6 - 0.0.0.6" $ requireTest $ do+      let ip1 = read "0.0.0.6" :: I.IpAddress+      I.splitIpRange (Range ip1 ip1) === (I.IpBlock ip1 (I.IpNetMask 32), Nothing)+    it "255.255.255.255 - 255.255.255.255" $ requireTest $ do+      let ip1 = read "255.255.255.255" :: I.IpAddress+      I.splitIpRange (Range ip1 ip1) === (I.IpBlock ip1 (I.IpNetMask 32), Nothing)+    it "128.0.0.0 - 255.255.255.255" $ requireTest $ do+      let ip1 = read "128.0.0.0"        :: I.IpAddress+      let ip2 = read "255.255.255.255"  :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 1), Nothing)+    it "0.0.0.0 - 255.255.255.255" $ requireTest $ do+      let ip1 = read "0.0.0.0"          :: I.IpAddress+      let ip2 = read "255.255.255.255"  :: I.IpAddress+      I.splitIpRange (Range ip1 ip2) === (I.IpBlock ip1 (I.IpNetMask 0), Nothing)++  describe "should get blocks from ranges" $ do+    it "0.0.0.1 - 0.0.0.2" $ requireTest $ do+      I.rangeToBlocks (I.Range (I.IpAddress 0x000001) (I.IpAddress 0x000002)) === [ I.IpBlock (I.IpAddress 0x000001) (I.IpNetMask 32)+                                                                                  , I.IpBlock (I.IpAddress 0x000002) (I.IpNetMask 32)]+    it "102.36.48.28 - 102.36.48.255" $ requireTest $ do+      I.rangeToBlocks (I.Range (I.IpAddress 0x6624301c) (I.IpAddress 0x662430ff)) === [ I.IpBlock (I.IpAddress 0x6624301c) (I.IpNetMask 30)+                                                                                      , I.IpBlock (I.IpAddress 0x66243020) (I.IpNetMask 27)+                                                                                      , I.IpBlock (I.IpAddress 0x66243040) (I.IpNetMask 26)+                                                                                      , I.IpBlock (I.IpAddress 0x66243080) (I.IpNetMask 25)]++    it "102.36.48.2 - 102.36.48.8" $ requireTest $ do+      I.rangeToBlocks (I.Range (I.IpAddress 0x66243002) (I.IpAddress 0x66243008)) === [ I.IpBlock (I.IpAddress 0x66243002) (I.IpNetMask 31)+                                                                                      , I.IpBlock (I.IpAddress 0x66243004) (I.IpNetMask 30)+                                                                                      , I.IpBlock (I.IpAddress 0x66243008) (I.IpNetMask 32)]++  describe "should get blocks from ranges with difference lists" $ do+    it "0.0.0.1 - 0.0.0.2" $ requireTest $ do+      I.rangeToBlocksDL (I.Range (I.IpAddress 0x000001) (I.IpAddress 0x000002)) [] === [ I.IpBlock (I.IpAddress 0x000001) (I.IpNetMask 32)+                                                                                        , I.IpBlock (I.IpAddress 0x000002) (I.IpNetMask 32)]++  it "block can be converted to range and back" $ require $ property $ do+    b <- forAll G.canonicalIpv4Block+    I.splitIpRange (I.blockToRange b) === (b, Nothing)
+ test/HaskellWorks/Data/Network/Ipv6Spec.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications  #-}++module HaskellWorks.Data.Network.Ipv6Spec (spec) where++import HaskellWorks.Data.Network.Ip.SafeEnum+import HaskellWorks.Data.Network.Ip.Validity+import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec+++import qualified Data.Text                          as T+import qualified HaskellWorks.Data.Network.Gen      as G+import qualified HaskellWorks.Data.Network.Ip.Ip    as V+import qualified HaskellWorks.Data.Network.Ip.Ipv4  as V4+import qualified HaskellWorks.Data.Network.Ip.Ipv6  as V6+import qualified HaskellWorks.Data.Network.Ip.Range as R+import qualified HaskellWorks.Data.Network.Ip.Range as IR++import qualified Hedgehog.Gen   as G+import qualified Hedgehog.Range as R++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.Network.Ipv6Spec" $ do+  describe "V6.IpBlock" $ do+    it "should implement show" $ requireTest $ do+      show (V6.IpBlock (V6.IpAddress (3, 3, 3, 0)) (V6.IpNetMask 96)) === "0:3:0:3:0:3::/96"++    it "should implement firstAddress/lastAddress" $ requireTest $ do+      V.firstIpAddress (V.IpBlockV4 (V4.IpBlock (V4.IpAddress   0xff000000) (V4.IpNetMask  8)))  === (0, 0, 0xFFFF, 0xFF000000)+      V.firstIpAddress (V.IpBlockV6 (V6.IpBlock (V6.IpAddress (4, 4, 0, 0)) (V6.IpNetMask 33)))  === (4, 4, 0, 0)+      V.lastIpAddress  (V.IpBlockV4 (V4.IpBlock (V4.IpAddress   0xff000000) (V4.IpNetMask  8)))  === (0 , 0 , 0xFFFF , 0xFFFFFFFF)+      V.lastIpAddress  (V.IpBlockV6 (V6.IpBlock (V6.IpAddress (4, 4, 0, 0)) (V6.IpNetMask 33)))  === (4 , 0x7FFFFFFF , 0xFFFFFFFF , 0xFFFFFFFF)++    it "should implement read" $ requireTest $ do+      read "1:2:3:4::"      === V6.IpAddress (0x10002,0x30004,0,0)+      read "1:2:3:4::/127"  === V6.IpBlock @Unaligned (V6.IpAddress (0x10002   , 0x30004 , 0, 0)) (V6.IpNetMask 127)+      read "1234::/16"      === V6.IpBlock @Unaligned (V6.IpAddress (0x12340000, 0       , 0, 0)) (V6.IpNetMask  16)+      read "12:34::/32"     === V6.IpBlock @Unaligned (V6.IpAddress (0x120034  , 0       , 0, 0)) (V6.IpNetMask  32)++    it "should parse what it has shown" $ require $ property $ do+      a <- forAll $ G.word32 R.constantBounded+      m <- forAll $ G.word8 $ R.linear 0 128+      let addr = V6.IpBlock (V6.IpAddress (a, 0, 0, 0)) (V6.IpNetMask m)+      V6.parseIpBlock (T.pack (show addr)) === Right addr++    it "should support enum" $ require $ property $ do+      boundedPred (V6.IpAddress (32, 32, 32, 32)) === V6.IpAddress (32, 32, 32, 31)+      boundedSucc (V6.IpAddress (32, 32, 32, 32)) === V6.IpAddress (32, 32, 32, 33)+      boundedPred (V6.IpAddress (0, 0, 0, 0xffffffff)) === V6.IpAddress (0, 0, 0, 0xfffffffe)+      boundedSucc (V6.IpAddress (0, 0, 0, 0xffffffff)) === V6.IpAddress (0, 0, 1, 0)+      boundedSucc (V6.IpAddress maxBound ) === V6.IpAddress maxBound+      boundedPred (V6.IpAddress (0, 0, 0, 0)) === V6.IpAddress (0, 0, 0, 0)++    it "should convert ::/128 to ranges" $ requireTest $ do+      V6.blockToRange (V6.IpBlock (V6.IpAddress (0, 0, 0, 0)) (V6.IpNetMask 128)) === R.Range (V6.IpAddress (0, 0, 0, 0)) (V6.IpAddress (0, 0, 0, 0))++    it "should convert 1234::/64 to ranges" $ requireTest $ do+      V6.blockToRange (V6.IpBlock (V6.IpAddress (0x12340000, 0, 0, 0)) (V6.IpNetMask 64)) === R.Range (V6.IpAddress (0x12340000, 0, 0, 0)) (V6.IpAddress (0x12340000, 0, 0xffffffff, 0xffffffff))++    it "block can be converted to range" $ require $ property $ do+      let b = V6.IpBlock (V6.IpAddress (0x12340000, 0, 0, 0)) (V6.IpNetMask 16)+      V6.blockToRange b === IR.Range (read "1234::") (read "1234:ffff:ffff:ffff:ffff:ffff:ffff:ffff")++    it "block can be converted to range and back" $ require $ property $ do+      b <- forAll G.canonicalIpv6Block+      V6.splitIpRange (V6.blockToRange b) === (b, Nothing)++    it "ranges can be split" $ require $ property $ do+      V6.splitIpRange (IR.Range (read "::") (read "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")) === (V6.IpBlock (V6.IpAddress (0, 0, 0, 0)) (V6.IpNetMask 0), Nothing)+      V6.splitIpRange (IR.Range (read "::") (read "::88")) === (V6.IpBlock (V6.IpAddress (0, 0, 0, 0)) (V6.IpNetMask 121), Just (IR.Range (read "::80") (read "::88")))+      V6.splitIpRange (IR.Range (read "::3") (read "::88")) === (V6.IpBlock (V6.IpAddress (0, 0, 0, 3)) (V6.IpNetMask 128), Just (IR.Range (read "::4") (read "::88")))+      V6.splitIpRange (IR.Range (read "::127") (read "::129")) === (V6.IpBlock (V6.IpAddress (0, 0, 0, 0x127)) (V6.IpNetMask 128), Just (IR.Range (read "::128") (read "::129")))++  describe "should get blocks from ranges" $ do+    it ":: - ::ff" $ requireTest $ do+      V6.rangeToBlocks (R.Range (V6.IpAddress 0) (V6.IpAddress 0xff)) === [ V6.IpBlock (V6.IpAddress 0) (V6.IpNetMask 120)]+    it "::fe - ::18e" $ requireTest $ do+      V6.rangeToBlocks (R.Range (V6.IpAddress 0xfe) (V6.IpAddress 0x18e)) === [ V6.IpBlock (V6.IpAddress 0xfe) (V6.IpNetMask 127)+                                                                              , V6.IpBlock (V6.IpAddress 0x100) (V6.IpNetMask 121)+                                                                              , V6.IpBlock (V6.IpAddress 0x180) (V6.IpNetMask 125)+                                                                              , V6.IpBlock (V6.IpAddress 0x188) (V6.IpNetMask 126)+                                                                              , V6.IpBlock (V6.IpAddress 0x18c) (V6.IpNetMask 127)+                                                                              , V6.IpBlock (V6.IpAddress 0x18e) (V6.IpNetMask 128)+                                                                              ]+    it ":: - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" $ requireTest $ do+      V6.rangeToBlocks (R.Range (V6.IpAddress 0) (V6.IpAddress maxBound)) === [ V6.IpBlock (V6.IpAddress 0) (V6.IpNetMask 0)]++  describe "should get blocks from ranges with difference lists" $ do+    it "::100 - ::200" $ requireTest $ do+      V6.rangeToBlocksDL (R.Range (V6.IpAddress 0x100) (V6.IpAddress 0x200)) [] === [ V6.IpBlock (V6.IpAddress 0x100) (V6.IpNetMask 120)+                                                                                     , V6.IpBlock (V6.IpAddress 0x200) (V6.IpNetMask 128)]
+ test/HaskellWorks/Data/Network/RangeSpec.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE MonoLocalBinds    #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications  #-}++module HaskellWorks.Data.Network.RangeSpec (spec) where++import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified HaskellWorks.Data.Network.Ip.Ipv6  as V6+import qualified HaskellWorks.Data.Network.Ip.Range as R+import qualified Text.Appar.String                  as AP++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.Network.RangeSpec" $ do+  describe "Range" $ do+    it "should be mergeable" $ requireTest $ do+      R.mergeRanges [] === ([] :: [R.Range Int])+      R.mergeRanges @Int [R.Range 0 5] === [R.Range 0 5]+      R.mergeRanges @Int [R.Range 3 5, R.Range 6 7] === [R.Range 3 7]+      R.mergeRanges @Int [R.Range 0 5, R.Range 7 7] === [R.Range 0 5, R.Range 7 7]+      R.mergeRanges [R.Range 'a' 'b', R.Range 'c' 'f'] === [R.Range 'a' 'f']+      R.mergeRanges [R.Range 'a' 'a', R.Range 'a' 'f'] === [R.Range 'a' 'f']+      R.mergeRanges [R.Range 'a' 'b', R.Range 'b' 'b'] === [R.Range 'a' 'b']+      R.mergeRanges [R.Range 'a' 'f', R.Range 'c' 'e'] === [R.Range 'a' 'f']+      R.mergeRanges [R.Range 'a' 'f', R.Range 'c' 'e', R.Range 'x' 'z'] === [R.Range 'a' 'f', R.Range 'x' 'z']++      let v6Ranges1 = [ R.Range (V6.IpAddress (0, 0, 0, 0)) (V6.IpAddress (0, 0, 0, 20))+                      , R.Range (V6.IpAddress (0, 0, 0, 21)) (V6.IpAddress (0, 0, 0, 0xffffffff))+                      , R.Range (V6.IpAddress (0, 0, 1, 0)) (V6.IpAddress (0, 0, 1, 200))+                      ]+      let v6Ranges2 = [ R.Range (V6.IpAddress (0, 0, 0, 0)) (V6.IpAddress (0, 0, 1, 200))+                      ]+      R.mergeRanges v6Ranges1 === v6Ranges2++    it "should parse dash-delimited ranges" $ requireTest $ do+      AP.runParser (AP.try (R.parseRange AP.alphaNum)) "a b"   === (Nothing               , "a b")+      AP.runParser (AP.try (R.parseRange AP.alphaNum)) "a - b" === (Just (R.Range 'a' 'b'), ""   )
+ test/HaskellWorks/Data/Network/Word128Spec.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE OverloadedStrings #-}++module HaskellWorks.Data.Network.Word128Spec (spec) where++import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified Data.Bits                            as B+import qualified HaskellWorks.Data.Network.Ip.Word128 as W++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "HaskellWorks.Data.Network.Ipv6Spec" $ do+  describe "Word128" $ do+    it "should implement +" $ requireTest $ do+      ((1, 1, 1, 1) :: W.Word128) + ((1, 1, 1, 1) :: W.Word128) === ((2, 2, 2, 2) :: W.Word128)+      ((1, 1, 1, 0xffffffff) :: W.Word128) + ((1, 1, 1, 1) :: W.Word128) === ((2, 2, 3, 0) :: W.Word128)+      (maxBound :: W.Word128) + ((0, 0, 0, 1) :: W.Word128) === ((0, 0, 0, 0) :: W.Word128)++    it "should implement -" $ requireTest $ do+      ((1, 1, 1, 1) :: W.Word128) - ((1, 1, 1, 1) :: W.Word128) === ((0, 0, 0, 0) :: W.Word128)+      ((1, 1, 1, 0xffffffff) :: W.Word128) - ((1, 1, 1, 1) :: W.Word128) === ((0, 0, 0, 0xfffffffe) :: W.Word128)+      ((0, 0, 0, 1) :: W.Word128) - (maxBound :: W.Word128) === ((0, 0, 0, 2) :: W.Word128)++    it "should implement *" $ requireTest $ do+      ((0, 0, 0, 0) :: W.Word128) * ((1, 1, 1, 1) :: W.Word128) === ((0, 0, 0, 0) :: W.Word128)+      ((1, 1, 1, 1) :: W.Word128) * ((1, 1, 1, 1) :: W.Word128) === ((4, 3, 2, 1) :: W.Word128)+      ((1, 1, 1, 0xffffffff) :: W.Word128) * ((0, 0, 0, 1) :: W.Word128) === ((1, 1, 1, 0xffffffff) :: W.Word128)+      ((0, 0, 0, 1) :: W.Word128) * (maxBound :: W.Word128) === (maxBound :: W.Word128)++    it "should implement shift" $ requireTest $ do+      ((1, 1, 1, 1) :: W.Word128) `B.shift` (16 :: Int) === ((0x10000, 0x10000, 0x10000, 0x10000) :: W.Word128)+      ((1, 1, 1, 1) :: W.Word128) `B.shift` (32 :: Int) === ((1, 1, 1, 0) :: W.Word128)+      ((1, 1, 1, 0xffffffff) :: W.Word128) `B.shift` (32 :: Int) === ((1, 1, 0xffffffff, 0) :: W.Word128)+      ((1, 1, 1, 1) :: W.Word128) `B.shiftR` (16 :: Int) === ((0, 0x10000, 0x10000, 0x10000) :: W.Word128)+      ((1, 1, 1, 1) :: W.Word128) `B.shiftR` (32 :: Int) === ((0, 1, 1, 1) :: W.Word128)+      ((1, 1, 0xffffffff, 1) :: W.Word128) `B.shiftR` (32 :: Int) === ((0, 1, 1, 0xffffffff) :: W.Word128)+      (maxBound :: W.Word128) `B.shift` (1 :: Int) === ((0xffffffff, 0xffffffff, 0xffffffff, 0xfffffffe) :: W.Word128)++    it "should implement Bits" $ requireTest $ do+      B.countLeadingZeros ((0, 0, 0, 0) :: W.Word128) === 128+      B.countLeadingZeros ((0, 0, 0, 1) :: W.Word128) === 127+      B.countLeadingZeros ((1, 1, 1, 1) :: W.Word128) === 31+      B.countTrailingZeros ((0, 0, 0, 1) :: W.Word128) === 0+      B.countTrailingZeros ((0, 0, 0, 0) :: W.Word128) === 128+      B.countTrailingZeros ((0, 0, 0x10, 0) :: W.Word128) === 36