packages feed

asif 3.2.0 → 3.3.4

raw patch · 4 files changed

+470/−4 lines, 4 filesdep +foldldep +profunctorsdep +transformersPVP ok

version bump matches the API change (PVP)

Dependencies added: foldl, profunctors, transformers

API changes (from Hackage documentation)

+ Arbor.File.Format.Asif.Write: asciiSegment :: MonadResource m => (a -> Char) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: buildAsifBytestring :: (Foldable f, MonadResource m) => String -> Maybe POSIXTime -> FoldM m a [Segment Handle] -> f a -> m ByteString
+ Arbor.File.Format.Asif.Write: genericExtract :: MonadResource m => Text -> Whatever Format -> Handle -> m [Segment Handle]
+ Arbor.File.Format.Asif.Write: genericFold :: MonadResource m => (a -> Builder) -> Whatever Format -> (b -> a) -> Text -> FoldM m b [Segment Handle]
+ Arbor.File.Format.Asif.Write: genericInitial :: MonadResource m => Text -> m Handle
+ Arbor.File.Format.Asif.Write: genericStep :: MonadResource m => (a -> Builder) -> Handle -> a -> m Handle
+ Arbor.File.Format.Asif.Write: int16Segment :: MonadResource m => (a -> Int16) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: int32Segment :: MonadResource m => (a -> Int32) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: int64Segment :: MonadResource m => (a -> Int64) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: int8Segment :: MonadResource m => (a -> Int8) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: ipv4Segment :: MonadResource m => (a -> IPv4) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: ipv6Segment :: MonadResource m => (a -> IPv6) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: lazyByteStringSegment :: MonadResource m => Whatever Format -> (a -> ByteString) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: nullTerminatedStringSegment :: MonadResource m => (a -> Text) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: textSegment :: MonadResource m => (a -> Text) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: utcTimeMicrosSegment :: MonadResource m => (a -> UTCTime) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: word16Segment :: MonadResource m => (a -> Word16) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: word32Segment :: MonadResource m => (a -> Word32) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: word64Segment :: MonadResource m => (a -> Word64) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: word8Segment :: MonadResource m => (a -> Word8) -> Text -> FoldM m a [Segment Handle]
+ Arbor.File.Format.Asif.Write: writeAsif :: (Foldable f, MonadResource m) => Handle -> String -> Maybe POSIXTime -> FoldM m a [Segment Handle] -> f a -> m ()

Files

asif.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack  name:           asif-version:        3.2.0+version:        3.3.4 description:    Please see the README on Github at <https://github.com/packetloop/asif#readme> category:       Services homepage:       https://github.com/packetloop/asif#readme@@ -41,6 +41,7 @@     , cpu     , either     , exceptions+    , foldl     , generic-lens     , hw-bits     , hw-ip@@ -48,10 +49,12 @@     , lens     , network     , old-locale+    , profunctors     , resourcet     , temporary-resourcet     , text     , thyme+    , transformers     , vector   exposed-modules:       Arbor.File.Format.Asif@@ -70,6 +73,7 @@       Arbor.File.Format.Asif.Segment       Arbor.File.Format.Asif.Type       Arbor.File.Format.Asif.Whatever+      Arbor.File.Format.Asif.Write   other-modules:       Arbor.File.Format.Asif.Format.SegmentValue       Arbor.File.Format.Asif.Format.Type@@ -97,6 +101,7 @@     , cpu     , either     , exceptions+    , foldl     , generic-lens     , hw-bits     , hw-ip@@ -104,10 +109,12 @@     , lens     , network     , old-locale+    , profunctors     , resourcet     , temporary-resourcet     , text     , thyme+    , transformers     , vector     , asif     , directory@@ -145,6 +152,7 @@     , cpu     , either     , exceptions+    , foldl     , generic-lens     , hw-bits     , hw-ip@@ -152,10 +160,12 @@     , lens     , network     , old-locale+    , profunctors     , resourcet     , temporary-resourcet     , text     , thyme+    , transformers     , vector     , asif     , hspec@@ -166,6 +176,7 @@       Arbor.File.Format.Asif.Data.IpSpec       Arbor.File.Format.Asif.Format.SegmentValueSpec       Arbor.File.Format.Asif.Format.TextSpec+      Arbor.File.Format.Asif.WriteSpec       Arbor.File.Format.AsifSpec       Arbor.TestUtils       Gen.Feed
+ src/Arbor/File/Format/Asif/Write.hs view
@@ -0,0 +1,230 @@+{-# LANGUAGE MultiParamTypeClasses #-}+++-- |+-- Module: Arbor.File.Format.Asif.Write+--+-- Functions to make it easier to write ASIF files without needing to deal with+-- raw Handles and ByteStrings too much.++module Arbor.File.Format.Asif.Write+  (+  -- * Encode an entire ASIF bytestring+  -- $usage+    writeAsif+  , buildAsifBytestring++  -- * Folds for Segments+  -- $segments+  , lazyByteStringSegment+  , nullTerminatedStringSegment+  , textSegment+  , asciiSegment+  , word8Segment+  , word16Segment+  , word32Segment+  , word64Segment+  , int8Segment+  , int16Segment+  , int32Segment+  , int64Segment+  , ipv4Segment+  , ipv6Segment+  , utcTimeMicrosSegment++  -- * Utility functions+  -- $helper+  , genericInitial+  , genericStep+  , genericExtract+  , genericFold+  )+where++import Arbor.File.Format.Asif.ByteString.Builder+import Arbor.File.Format.Asif.Data.Ip            (ipv4ToWord32, ipv6ToWord32x4)+import Arbor.File.Format.Asif.Type+import Arbor.File.Format.Asif.Whatever           (Whatever (..))+import Conduit+import Control.Foldl+import Control.Lens+import Control.Monad.IO.Class                    (liftIO)+import Control.Monad.Trans.Resource              (MonadResource)+import Data.Int+import Data.Profunctor                           (lmap)+import Data.Semigroup                            ((<>))+import Data.Word+import System.IO                                 (Handle, SeekMode (AbsoluteSeek), hFlush, hSeek)+import System.IO.Temp                            (openTempFile)++import qualified Arbor.File.Format.Asif.Format as F+import qualified Data.ByteString.Builder       as BB+import qualified Data.ByteString.Lazy          as LBS+import qualified Data.IP                       as IP+import qualified Data.Text                     as T+import qualified Data.Text.Encoding            as T+import qualified Data.Text.Lazy                as TL+import qualified Data.Text.Lazy.Encoding       as TE++import qualified Data.Thyme.Clock.POSIX as TY+import qualified Data.Thyme.Time.Core   as TY++-- $usage+--+-- Facilities for writing an entire ASIF file, either to an actual file or to a+-- ByteString.+--+-- ** Usage:+-- Use the various *segment functions to produce a FoldM. These are designed to+-- allow you to take some large type (e.g. a tuple or a product type) and pull+-- out the constituent pieces to encode into 'Segment's.+--+-- 'FoldM's are composable using '<>'. Once you have a single, provide it to+-- 'writeAsif' or 'buildAsifBytestring' along with an appropriate foldable.+-- Both these functions will stream from the input, assuming the foldable is+-- something that can be streamed from.++-- | Write an ASIF file to the supplied handle.+-- Streams the input foldable if possible.+writeAsif :: (Foldable f, MonadResource m)+  => Handle+  -> String+  -> Maybe TY.POSIXTime+  -> FoldM m a [Segment Handle]+  -> f a+  -> m ()+writeAsif hOutput asifType mTimestamp fld foldable = do+  segments <- foldM fld foldable+  contents  <- segmentsC asifType mTimestamp segments+  runConduit $ contents .| sinkHandle hOutput+  liftIO $ hFlush hOutput++-- | Builds a lazy ASIF bytestring.+-- Streams the input foldable if possible.+buildAsifBytestring :: (Foldable f, MonadResource m)+  => String+  -> Maybe TY.POSIXTime+  -> FoldM m a [Segment Handle]+  -> f a+  -> m LBS.ByteString+buildAsifBytestring asifType mTimestamp fld foldable = do+  (_, _, h) <- openTempFile Nothing "asif"+  writeAsif h asifType mTimestamp fld foldable+  liftIO $ hSeek h AbsoluteSeek 0+  liftIO $ LBS.hGetContents h++-----++-- $segments+--+-- Use these to build 'FoldM's for the types you want to encode.+-- Compose them together using '<>'.++-- | Builds a segment from lazy bytestrings.+-- This can in priciple cover any bytestring-y format,+-- including StringZ, Text, Binary, Bitmap, and Bitstring, as well as unknown encodings.+-- Correctly encoding the value is the responsibility of the caller.+lazyByteStringSegment :: MonadResource m => Whatever F.Format -> (a -> LBS.ByteString) -> T.Text -> FoldM m a [Segment Handle]+lazyByteStringSegment = genericFold BB.lazyByteString++-- | Builds a segment of null-termianted strings.+-- Note that the input itself does *not* need to be null-terminated.+-- The null-termination is added by this function.+nullTerminatedStringSegment :: MonadResource m => (a -> T.Text) -> T.Text -> FoldM m a [Segment Handle]+nullTerminatedStringSegment f t = FoldM step initial extract+  where+    initial = genericInitial t+    step h b = do+      liftIO $ BB.hPutBuilder h $ BB.byteString (T.encodeUtf8 . f $ b) <> BB.word8 0+      pure h+    extract = genericExtract t (Known F.StringZ)++-- | Builds a segment of 'Text's.+textSegment :: MonadResource m => (a -> T.Text) -> T.Text -> FoldM m a [Segment Handle]+textSegment f = genericFold TE.encodeUtf8Builder (Known F.Text) (TL.fromStrict . f)++-- | Builds a segment of 'Char's.+asciiSegment :: MonadResource m => (a -> Char) -> T.Text -> FoldM m a [Segment Handle]+asciiSegment = genericFold BB.char8 (Known F.Char)++-----++-- | Builds a segment of 'Word8's.+word8Segment :: MonadResource m => (a -> Word8) -> T.Text -> FoldM m a [Segment Handle]+word8Segment = genericFold BB.word8 (Known F.Word8)++-- | Builds a segment of 'Word16's.+word16Segment :: MonadResource m => (a -> Word16) -> T.Text -> FoldM m a [Segment Handle]+word16Segment = genericFold BB.word16LE (Known F.Word16LE)++-- | Builds a segment of 'Word32's.+word32Segment :: MonadResource m => (a -> Word32) -> T.Text -> FoldM m a [Segment Handle]+word32Segment = genericFold BB.word32LE (Known F.Word32LE)++-- | Builds a segment of 'Word64's.+word64Segment :: MonadResource m => (a -> Word64) -> T.Text -> FoldM m a [Segment Handle]+word64Segment = genericFold BB.word64LE (Known F.Word64LE)++-----++-- | Builds a segment of 'Int8's.+int8Segment :: MonadResource m => (a -> Int8) -> T.Text -> FoldM m a [Segment Handle]+int8Segment = genericFold BB.int8 (Known F.Int8)++-- | Builds a segment of 'Int16's.+int16Segment :: MonadResource m => (a -> Int16) -> T.Text -> FoldM m a [Segment Handle]+int16Segment = genericFold BB.int16LE (Known F.Int16LE)++-- | Builds a segment of 'Int32's.+int32Segment :: MonadResource m => (a -> Int32) -> T.Text -> FoldM m a [Segment Handle]+int32Segment = genericFold BB.int32LE (Known F.Int32LE)++-- | Builds a segment of 'Int64's.+int64Segment :: MonadResource m => (a -> Int64) -> T.Text -> FoldM m a [Segment Handle]+int64Segment = genericFold BB.int64LE (Known F.Int64LE)++-----++-- | Builds a segment of 'IPv4's.+ipv4Segment :: MonadResource m => (a -> IP.IPv4) -> T.Text -> FoldM m a [Segment Handle]+ipv4Segment f = genericFold BB.word32LE (Known F.Ipv4) (ipv4ToWord32 . f)++-- | Builds a segment of 'IPv6's.+ipv6Segment :: MonadResource m => (a -> IP.IPv6) -> T.Text -> FoldM m a [Segment Handle]+ipv6Segment f = genericFold encoding (Known F.Ipv6) extract+    where+      -- I do not know why this is Big-Endian, when everything else is Little-Endian.+      encoding = Prelude.foldMap BB.word32BE+      extract = tupleToList . ipv6ToWord32x4 . f+      tupleToList (w1,w2,w3,w4) = [w1,w2,w3,w4]++-----++-- | Builds a segment of 'UTCTime's, accurate to microseconds.+utcTimeMicrosSegment :: MonadResource m => (a -> TY.UTCTime) -> T.Text -> FoldM m a [Segment Handle]+utcTimeMicrosSegment f = genericFold BB.int64LE (Known F.TimeMicros64LE) (fromTime . f)+    where+      fromTime :: TY.UTCTime -> Int64+      fromTime = view (TY.posixTime . TY.microseconds)++-------------------------------------------------------------------------------++-- $helper+--+-- Helper functions for creating 'FoldM's from scratch.++genericInitial :: MonadResource m => T.Text -> m Handle+genericInitial name = do+  (_, _, h) <- openTempFile Nothing (T.unpack name)+  pure h++genericStep :: MonadResource m => (a -> BB.Builder) -> Handle -> a -> m Handle+genericStep enc h b = do+  liftIO $ BB.hPutBuilder h $ enc b+  pure h++genericExtract :: MonadResource m => T.Text -> Whatever F.Format -> Handle -> m [Segment Handle]+genericExtract filen typ h = pure [segment h $ metaFilename filen <> metaFormat typ]++genericFold :: MonadResource m =>  (a -> BB.Builder) -> Whatever F.Format -> (b -> a) -> T.Text -> FoldM m b [Segment Handle]+genericFold enc fmt f t = lmap f $ FoldM (genericStep enc) (genericInitial t) (genericExtract t fmt)
+ test/Arbor/File/Format/Asif/WriteSpec.hs view
@@ -0,0 +1,225 @@+module Arbor.File.Format.Asif.WriteSpec where++import Arbor.File.Format.Asif.Data.Ip+import Arbor.File.Format.Asif.Format+import Arbor.File.Format.Asif.Segment+import Arbor.File.Format.Asif.Write+import Control.Monad.Trans.Class+import Control.Monad.Trans.Resource+import Data.Int+import Data.Semigroup                 ((<>))+import Data.Word++import HaskellWorks.Hspec.Hedgehog+import Hedgehog+import Test.Hspec++import qualified Data.Attoparsec.ByteString as AP+import qualified Data.IP                    as IP+import qualified Data.Text.Lazy             as T+import qualified Data.Text.Lazy.Encoding    as T+import qualified Data.Thyme.Clock.POSIX     as TY+import qualified Data.Thyme.Time.Core       as TY+import qualified Hedgehog.Gen               as G+import qualified Hedgehog.Range             as R+++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "Arbor.File.Format.Asif.Write" $ do+  it "should write several segments, then read them back in" $ require $ property $ do+    tplList <- forAll $ G.list (R.linear 0 50) genTriple++    let f1 = int64Segment                (\(a,_,_) -> a) "first"+    let f2 = word16Segment               (\(_,a,_) -> a) "second"+    let f3 = nullTerminatedStringSegment (\(_,_,a) -> T.toStrict a) "third"+    let fld = f1 <> f2 <> f3++    lbs <- buildAsifBytestring "wxyz" Nothing fld tplList++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs++    [names, times, types, l1,l2,l3] <- forAll $ pure (segmentValues <$> segments)++    ((\(a,_,_) -> SInt64 a) <$> tplList) === l1+    ((\(_,a,_) -> SWord16 a) <$> tplList) === l2+    ((\(_,_,a) -> SString . T.encodeUtf8 $ a) <$> tplList) === l3++-----++  it "should write out and read back in a word8 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.word8 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (word8Segment id "word8") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SWord8 <$> lst) === seg++  it "should write out and read back in a word16 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.word16 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (word16Segment id "word16") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SWord16 <$> lst) === seg++  it "should write out and read back in a word32 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.word32 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (word32Segment id "word32") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SWord32 <$> lst) === seg++  it "should write out and read back in a word64 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.word64 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (word64Segment id "word64") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SWord64 <$> lst) === seg++-----++  it "should write out and read back in a int8 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.int8 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (int8Segment id "int8") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SInt8 <$> lst) === seg++  it "should write out and read back in a int16 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.int16 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (int16Segment id "int16") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SInt16 <$> lst) === seg++  it "should write out and read back in a int32 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.int32 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (int32Segment id "int32") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SInt32 <$> lst) === seg++  it "should write out and read back in a int64 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (G.int64 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (int64Segment id "int64") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SInt64 <$> lst) === seg++-----++  it "should write out and read back in a ipv4 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (word32ToIpv4 <$> G.word32 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (ipv4Segment id "ipv4") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SIpv4 <$> lst) === seg++  it "should write out and read back in a ipv6 segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) genIpv6++    lbs <- buildAsifBytestring "wxyz" Nothing (ipv6Segment id "ipv6") lst++    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SIpv6 <$> lst) === seg++-----++  it "should write out and read back in a time segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) (TY.posixSecondsToUTCTime . TY.fromMicroseconds <$> G.int64 R.linearBounded)++    lbs <- buildAsifBytestring "wxyz" Nothing (utcTimeMicrosSegment id "time") lst+    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (STime <$> lst) === seg++-----++  it "should write out and read back in a char segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) G.ascii++    lbs <- buildAsifBytestring "wxyz" Nothing (asciiSegment id "char") lst+    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SChar <$> lst) === seg++  it "should write out and read back in a null-terminated string segment" $ require $ property $ do+    lst <- forAll $ G.list (R.linear 0 50) genNonNullText++    lbs <- buildAsifBytestring "wxyz" Nothing (nullTerminatedStringSegment id "nullterminatedstring") (T.toStrict <$> lst)+    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    (SString . T.encodeUtf8 <$> lst) === seg++  it "should write out and read back in a text segment" $ require $ property $ do+    -- Explaination of this test:+    -- We can't deliniate 'breaks' in Text when we read it back in,+    -- so regardless of how many we write out, when we read it back we only+    -- get a single Text value.+    lst <- forAll $ G.list (R.linear 1 50) (G.text (R.linear 0 50) G.unicode)++    lbs <- buildAsifBytestring "wxyz" Nothing (textSegment id "text") lst+    let Right segments = extractSegments (AP.string "seg:wxyz") lbs+    [names, times, types, seg] <- forAll $ pure (segmentValues <$> segments)++    [SText . T.encodeUtf8 . T.concat $ T.fromStrict <$> lst] === seg+++genTriple :: MonadGen m => m (Int64, Word16, T.Text)+genTriple+  = (,,)+  <$> G.int64 R.linearBounded+  <*> G.word16 R.linearBounded+  <*> genNonNullText++genIpv6 :: MonadGen m => m IP.IPv6+genIpv6 = word32x4ToIpv6 <$> gen4+  where+    gen4 :: MonadGen m => m (Word32, Word32, Word32, Word32)+    gen4 =+      let g = G.word32 R.linearBounded+      in (,,,) <$> g <*> g <*> g <*> g++genNonNullText :: MonadGen m => m T.Text+genNonNullText =+  T.fromStrict <$> G.text (R.linear 0 32) (G.filter (/= toEnum 0) G.unicode)+++instance MonadResource m => MonadResource (PropertyT m) where+  liftResourceT = lift . liftResourceT++-- I have no idea how this doesn't exist, but whatever. ¯\_(ツ)_/¯+instance MonadResource IO where+  liftResourceT = runResourceT
test/Gen/Feed.hs view
@@ -6,15 +6,15 @@   ) where  import Data.Word-import HaskellWorks.Data.Network.Ip import HaskellWorks.Data.Network.Ip.Internal+import HaskellWorks.Data.Network.Ip.Ipv4 import Hedgehog  import qualified Hedgehog.Gen   as G import qualified Hedgehog.Range as R -ipv4 :: MonadGen m => Word8 -> Word8 -> m Ipv4Address-ipv4 start stop = Ipv4Address <$> (fourOctetsToWord32 <$> w8 <*> w8 <*> w8 <*> w8)+ipv4 :: MonadGen m => Word8 -> Word8 -> m IpAddress+ipv4 start stop = IpAddress <$> (fourOctetsToWord32 <$> w8 <*> w8 <*> w8 <*> w8)   where w8 = G.word8 (R.linear start stop)  feedElemIp :: MonadGen m => Range Word32 -> m Word32