packages feed

asif 3.1.0 → 3.2.0

raw patch · 23 files changed

+1072/−204 lines, 23 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Arbor.File.Format.Asif.Format: instance GHC.Classes.Eq Arbor.File.Format.Asif.Format.Format
- Arbor.File.Format.Asif.Format: instance GHC.Generics.Generic Arbor.File.Format.Asif.Format.Format
- Arbor.File.Format.Asif.Format: instance GHC.Read.Read Arbor.File.Format.Asif.Format.Format
- Arbor.File.Format.Asif.Format: instance GHC.Show.Show Arbor.File.Format.Asif.Format.Format
+ Arbor.File.Format.Asif.Format: Bitmap :: Format
+ Arbor.File.Format.Asif.Format: SBinary :: ByteString -> SegmentValue
+ Arbor.File.Format.Asif.Format: SBitString :: ByteString -> SegmentValue
+ Arbor.File.Format.Asif.Format: SBitmap :: ByteString -> SegmentValue
+ Arbor.File.Format.Asif.Format: SChar :: Char -> SegmentValue
+ Arbor.File.Format.Asif.Format: SInt16 :: Int16 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SInt32 :: Int32 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SInt64 :: Int64 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SInt8 :: Int8 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SIpv4 :: IPv4 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SIpv6 :: IPv6 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SList :: [SegmentValue] -> SegmentValue
+ Arbor.File.Format.Asif.Format: SString :: ByteString -> SegmentValue
+ Arbor.File.Format.Asif.Format: SText :: ByteString -> SegmentValue
+ Arbor.File.Format.Asif.Format: STime :: UTCTime -> SegmentValue
+ Arbor.File.Format.Asif.Format: SUnknown :: Text -> ByteString -> SegmentValue
+ Arbor.File.Format.Asif.Format: SWord16 :: Word16 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SWord32 :: Word32 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SWord64 :: Word64 -> SegmentValue
+ Arbor.File.Format.Asif.Format: SWord8 :: Word8 -> SegmentValue
+ Arbor.File.Format.Asif.Format: data SegmentValue
+ Arbor.File.Format.Asif.Format: extractValues :: (SegmentValue -> a) -> Segment ByteString -> [a]
+ Arbor.File.Format.Asif.Format: segmentValues :: Segment ByteString -> [SegmentValue]
+ Arbor.File.Format.Asif.Format.Text: segmentValueToText :: SegmentValue -> Text
+ Arbor.File.Format.Asif.Segment: extractHeader :: Parser ByteString -> ByteString -> Either String [(Int, Int)]
+ Arbor.File.Format.Asif.Segment: extractSegmentByteStringsWithHeader :: ByteString -> [(Int, Int)] -> Either String [ByteString]
- Arbor.File.Format.Asif.Extract: map :: (Ord a) => ByteString -> Get a -> ByteString -> Get b -> Map a b
+ Arbor.File.Format.Asif.Extract: map :: Ord a => ByteString -> Get a -> ByteString -> Get b -> Map a b

Files

app/App/Commands.hs view
@@ -2,17 +2,23 @@   ( globalOptions   ) where -import App.Commands.Dump-import App.Commands.EncodeFiles-import App.Commands.ExtractFiles-import App.Commands.ExtractSegments-import Data.Monoid                  ((<>))-import Options.Applicative+import           App.Commands.Dump+import           App.Commands.DumpBitmap+import           App.Commands.DumpOnly+import           App.Commands.EncodeFiles+import           App.Commands.ExtractFiles+import           App.Commands.ExtractSegments+import           App.Commands.Ls+import           Data.Monoid                  ((<>))+import           Options.Applicative  globalOptions :: Parser (IO ())-globalOptions = subparser+globalOptions = hsubparser   (   command "dump"                (info commandDump               idm)+  <>  command "dump-only"           (info commandDumpOnly           idm)+  <>  command "dump-bitmap"         (info commandDumpBitmap         idm)   <>  command "encode-files"        (info commandEncodeFiles        idm)   <>  command "extract-files"       (info commandExtractFiles       idm)   <>  command "extract-segments"    (info commandExtractSegments    idm)+  <>  command "ls"                  (info commandLs                 idm)   )
app/App/Commands/Dump.hs view
@@ -5,41 +5,26 @@  module App.Commands.Dump where -import App.Commands.Options.Type-import Arbor.File.Format.Asif.Data.Ip+import App.Commands.Options.Type      (DumpOptions (DumpOptions))+import App.Dump import Arbor.File.Format.Asif.IO import Arbor.File.Format.Asif.Segment-import Arbor.File.Format.Asif.Whatever import Control.Lens import Control.Monad-import Control.Monad.IO.Class          (liftIO)-import Control.Monad.Trans.Resource    (MonadResource, runResourceT)-import Data.Char                       (isPrint)+import Control.Monad.IO.Class         (liftIO)+import Control.Monad.Trans.Resource   (MonadResource, runResourceT) import Data.Generics.Product.Any-import Data.List import Data.Maybe-import Data.Monoid                     ((<>))-import Data.Thyme.Clock-import Data.Thyme.Clock.POSIX          (POSIXTime)-import Data.Thyme.Format               (formatTime)-import Data.Thyme.Time.Core-import Data.Word-import HaskellWorks.Data.Bits.BitShow-import Numeric                         (showHex)+import Data.Monoid                    ((<>))+import Data.Text                      (Text) import Options.Applicative-import System.Locale                   (defaultTimeLocale, iso8601DateFormat) -import qualified Arbor.File.Format.Asif.ByteString.Lazy as LBS-import qualified Arbor.File.Format.Asif.Format          as F-import qualified Data.Attoparsec.ByteString             as AP-import qualified Data.Binary                            as G-import qualified Data.Binary.Get                        as G-import qualified Data.ByteString                        as BS-import qualified Data.ByteString.Lazy                   as LBS-import qualified Data.ByteString.Lazy.Char8             as LBSC-import qualified Data.Text                              as T-import qualified Data.Text.Encoding                     as T-import qualified System.IO                              as IO+import qualified Data.Attoparsec.ByteString as AP+import qualified Data.ByteString            as BS+import qualified Data.ByteString.Lazy       as LBS+import qualified Data.Set                   as S+import qualified Data.Text                  as T+import qualified System.IO                  as IO  {-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-} {-# ANN module ("HLint: ignore Redundant do"        :: String) #-}@@ -58,25 +43,30 @@       <>  value "-"       <>  help "Output file"       )+  <*> many+      ( option auto+        (   long "without-segment"+        <>  metavar "SEGMENT_ID"+        <>  help "Output file"+        )+      )+  <*> many+      ( strOption+        (   long "without-filename"+        <>  metavar "FILE"+        <>  help "Output file"+        )+      )  commandDump :: Parser (IO ()) commandDump = runResourceT . runDump <$> parseDumpOptions -showTime :: FormatTime t => t -> String-showTime = formatTime defaultTimeLocale (iso8601DateFormat (Just "%H:%M:%S %Z"))--getWord32x4 :: G.Get (Word32, Word32, Word32, Word32)-getWord32x4 = do-  a <- G.getWord32be-  b <- G.getWord32be-  c <- G.getWord32be-  d <- G.getWord32be-  return (a, b, c, d)- runDump :: MonadResource m => DumpOptions -> m () runDump opt = do   (_, hIn)  <- openFileOrStd (opt ^. the @"source") IO.ReadMode   (_, hOut) <- openFileOrStd (opt ^. the @"target") IO.WriteMode+  let dumpWithoutSegments  = opt ^. the @"withoutSegments"             & S.fromList :: S.Set Int+  let dumpWithoutFilenames = opt ^. the @"withoutFilenames" <&> T.pack & S.fromList :: S.Set Text    contents <- liftIO $ LBS.hGetContents hIn @@ -89,87 +79,7 @@       let namedSegments = zip filenames segments        forM_ (zip [0..] namedSegments) $ \(i :: Int, (filename, segment)) -> do-        if T.null filename-          then liftIO $ IO.hPutStrLn hOut $ "==== [" <> show i <> "] ===="-          else liftIO $ IO.hPutStrLn hOut $ "==== [" <> show i <> "] " <> T.unpack filename <> " ===="--        case segment ^. the @"meta" . the @"format" of-          Just (Known F.StringZ) -> do-            when (LBS.length (segment ^. the @"payload") > 0) $-              forM_ (init (LBS.split 0 (segment ^. the @"payload"))) $ \bs ->-                liftIO $ IO.hPutStrLn hOut $ T.unpack (T.decodeUtf8 (LBS.toStrict bs))-          Just (Known (F.Repeat n F.Char)) ->-            forM_ (LBS.chunkBy (fromIntegral n) (segment ^. the @"payload")) $ \bs ->-              liftIO $ IO.hPutStrLn hOut $ T.unpack (T.decodeUtf8 (LBS.toStrict bs))-          Just (Known F.TimeMillis64LE) ->-            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getInt64le (LBS.take 8 (bs <> LBS.replicate 8 0))-              let t :: POSIXTime = (w * 1000) ^. from microseconds-              liftIO $ IO.hPutStrLn hOut $ showTime (posixSecondsToUTCTime t) <> " (" <> show w <> " ms)"-          Just (Known F.TimeMicros64LE) ->-            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getInt64le (LBS.take 8 (bs <> LBS.replicate 8 0))-              let t :: POSIXTime = w ^. from microseconds-              liftIO $ IO.hPutStrLn hOut $ showTime (posixSecondsToUTCTime t) <> " (" <> show w <> " µs)"-          Just (Known F.Ipv4) ->-            forM_ (LBS.chunkBy 4 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getWord32le (LBS.take 8 (bs <> LBS.replicate 4 0))-              let ipString = w & word32ToIpv4 & ipv4ToString-              liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (16 - length ipString) ' ' <> "(" <> show w <> ")"-          Just (Known F.Ipv6) ->-            forM_ (LBS.chunkBy 16 (segment ^. the @"payload")) $ \bs -> do-              let (a, b, c, d) = G.runGet getWord32x4 (LBS.take 16 (bs <> LBS.replicate 16 0))-              if a == 0 && b == 0 && c == 0xFFFF-                then do-                  let ipString = d & word32ToIpv4 & ipv4ToString-                  liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (64 - length ipString) ' ' <> "(" <> show d <> ")"-                else do-                  let ipString = word32x4ToIpv6 (a, b, c, d) & ipv6ToString-                  liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (64 - length ipString) ' ' <> "(" <> show (a, b, c, d) <> ")"-          Just (Known F.Int64LE) ->-            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getInt64le (LBS.take 8 (bs <> LBS.replicate 8 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Int32LE) ->-            forM_ (LBS.chunkBy 4 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getInt32le (LBS.take 4 (bs <> LBS.replicate 4 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Int16LE) ->-            forM_ (LBS.chunkBy 2 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getInt16le (LBS.take 2 (bs <> LBS.replicate 2 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Int8) ->-            forM_ (LBS.chunkBy 1 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getInt8 (LBS.take 1 (bs <> LBS.replicate 1 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Word64LE) ->-            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getWord64le (LBS.take 8 (bs <> LBS.replicate 8 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Word32LE) ->-            forM_ (LBS.chunkBy 4 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getWord32le (LBS.take 4 (bs <> LBS.replicate 4 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Word16LE) ->-            forM_ (LBS.chunkBy 2 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getWord16le (LBS.take 2 (bs <> LBS.replicate 2 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Word8) ->-            forM_ (LBS.chunkBy 1 (segment ^. the @"payload")) $ \bs -> do-              let w = G.runGet G.getWord8 (LBS.take 1 (bs <> LBS.replicate 1 0))-              liftIO $ IO.hPrint hOut w-          Just (Known F.Text) ->-            liftIO $ LBSC.hPutStrLn hOut (segment ^. the @"payload")-          Just (Known F.BitString) ->-            liftIO $ IO.hPutStrLn hOut (bitShow (segment ^. the @"payload"))-          _ ->-            forM_ (zip (LBS.chunkBy 16 (segment ^. the @"payload")) [0 :: Int, 16..]) $ \(bs, j) -> do-              let bytes = mconcat (intersperse " " (reverse . take 2 . reverse . ('0':) . flip showHex "" <$> LBS.unpack bs))-              liftIO $ IO.hPutStr hOut $ reverse $ take 8 $ reverse $ ("0000000" ++) $ showHex j ""-              liftIO $ IO.hPutStr hOut "  "-              liftIO $ IO.hPutStr hOut $ bytes <> replicate (47 - length bytes) ' '-              liftIO $ IO.hPutStr hOut "  "-              liftIO $ IO.hPutStr hOut $ (\c -> if isPrint c then c else '.') <$> LBSC.unpack bs-              liftIO $ IO.hPutStrLn hOut ""+        unless (S.member filename dumpWithoutFilenames || S.member i dumpWithoutSegments) $ do+          dumpSegment hOut i filename segment    where magic = AP.string "seg:" *> (BS.pack <$> many AP.anyWord8) AP.<?> "\"seg:????\""
+ app/App/Commands/DumpBitmap.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module App.Commands.DumpBitmap+  ( commandDumpBitmap+  ) where++import App.Commands.Options.Type+import App.Dump+import Arbor.File.Format.Asif.Data.Ip+import Arbor.File.Format.Asif.IO+import Control.Lens+import Control.Monad+import Control.Monad.IO.Class         (liftIO)+import Control.Monad.Trans.Resource   (MonadResource, runResourceT)+import Data.Generics.Product.Any+import Data.Monoid                    ((<>))+import Options.Applicative++import qualified Arbor.File.Format.Asif.ByteString.Lazy as LBS+import qualified Data.Binary.Get                        as G+import qualified Data.ByteString.Lazy                   as LBS+import qualified System.IO                              as IO++{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}++parseDumpBitmapOptions :: Parser DumpBitmapOptions+parseDumpBitmapOptions = DumpBitmapOptions+  <$> strOption+      (   long "source"+      <>  metavar "FILE"+      <>  value "-"+      <>  help "Input file"+      )+  <*> strOption+      (   long "target"+      <>  metavar "FILE"+      <>  value "-"+      <>  help "Output file"+      )++commandDumpBitmap :: Parser (IO ())+commandDumpBitmap = runResourceT . runDump <$> parseDumpBitmapOptions++runDump :: MonadResource m => DumpBitmapOptions -> m ()+runDump opt = do+  (_, hIn)  <- openFileOrStd (opt ^. the @"source") IO.ReadMode+  (_, hOut) <- openFileOrStd (opt ^. the @"target") IO.WriteMode++  contents <- liftIO $ LBS.hGetContents hIn++  forM_ (zip [0..] (G.runGet G.getWord64le <$> LBS.chunkBy 8 contents)) $ \(idx, w64) ->+    forM_ (word64ToList idx w64 []) $ \w32 -> do+      let ipString = w32 & word32ToIpv4 & ipv4ToString+      liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (16 - length ipString) ' ' <> "(" <> show w32 <> ")"
+ app/App/Commands/DumpOnly.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module App.Commands.DumpOnly+  ( commandDumpOnly+  ) where++import App.Commands.Options.Type      (DumpOnlyOptions (DumpOnlyOptions))+import App.Dump+import Arbor.File.Format.Asif.IO+import Arbor.File.Format.Asif.Segment+import Control.Lens+import Control.Monad+import Control.Monad.IO.Class         (liftIO)+import Control.Monad.Trans.Resource   (MonadResource, runResourceT)+import Data.Generics.Product.Any+import Data.Maybe+import Data.Monoid                    ((<>))+import Data.Text                      (Text)+import Options.Applicative++import qualified Data.Attoparsec.ByteString as AP+import qualified Data.ByteString            as BS+import qualified Data.ByteString.Lazy       as LBS+import qualified Data.Set                   as S+import qualified Data.Text                  as T+import qualified System.IO                  as IO++{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}++parseDumpOnlyOptions :: Parser DumpOnlyOptions+parseDumpOnlyOptions = DumpOnlyOptions+  <$> strOption+      (   long "source"+      <>  metavar "FILE"+      <>  value "-"+      <>  help "Input file"+      )+  <*> strOption+      (   long "target"+      <>  metavar "FILE"+      <>  value "-"+      <>  help "Output file"+      )+  <*> many+      ( option auto+        (   long "segment"+        <>  metavar "SEGMENT_ID"+        <>  help "Output file"+        )+      )+  <*> many+      ( strOption+        (   long "filename"+        <>  metavar "FILE"+        <>  help "Output file"+        )+      )++commandDumpOnly :: Parser (IO ())+commandDumpOnly = runResourceT . runDump <$> parseDumpOnlyOptions++runDump :: MonadResource m => DumpOnlyOptions -> m ()+runDump opt = do+  (_, hIn)  <- openFileOrStd (opt ^. the @"source") IO.ReadMode+  (_, hOut) <- openFileOrStd (opt ^. the @"target") IO.WriteMode+  let dumpSegments  = opt ^. the @"segments"             & S.fromList :: S.Set Int+  let dumpFilenames = opt ^. the @"filenames" <&> T.pack & S.fromList :: S.Set Text++  contents <- liftIO $ LBS.hGetContents hIn++  case extractSegments magic contents of+    Left errorMessage -> do+      liftIO $ IO.hPutStrLn IO.stderr $ "Error occured: " <> errorMessage+      return ()+    Right segments -> do+      let filenames = fromMaybe "" . (^. the @"meta" . the @"filename") <$> segments+      let namedSegments = zip filenames segments++      forM_ (zip [0..] namedSegments) $ \(i :: Int, (filename, segment)) -> do+        when (S.member filename dumpFilenames || S.member i dumpSegments) $ do+          dumpSegment hOut i filename segment++  where magic = AP.string "seg:" *> (BS.pack <$> many AP.anyWord8) AP.<?> "\"seg:????\""
app/App/Commands/EncodeFiles.hs view
@@ -4,7 +4,7 @@  module App.Commands.EncodeFiles where -import App.Commands.Options.Type+import App.Commands.Options.Type                 (EncodeFilesOptions (EncodeFilesOptions)) import Arbor.File.Format.Asif.ByteString.Builder import Arbor.File.Format.Asif.IO import Conduit
app/App/Commands/ExtractFiles.hs view
@@ -4,7 +4,7 @@  module App.Commands.ExtractFiles where -import App.Commands.Options.Type+import App.Commands.Options.Type      (ExtractFilesOptions (ExtractFilesOptions)) import Arbor.File.Format.Asif.IO import Arbor.File.Format.Asif.Segment import Control.Lens
app/App/Commands/ExtractSegments.hs view
@@ -4,7 +4,7 @@  module App.Commands.ExtractSegments where -import App.Commands.Options.Type+import App.Commands.Options.Type      (ExtractSegmentsOptions (ExtractSegmentsOptions)) import Arbor.File.Format.Asif.IO import Arbor.File.Format.Asif.Segment import Control.Lens
+ app/App/Commands/Ls.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module App.Commands.Ls+  ( commandLs+  ) where++import App.Commands.Options.Type      (LsOptions (LsOptions))+import Arbor.File.Format.Asif.IO+import Arbor.File.Format.Asif.Segment hiding (meta)+import Control.Lens+import Control.Monad+import Control.Monad.IO.Class         (liftIO)+import Control.Monad.Trans.Resource   (MonadResource, runResourceT)+import Data.Generics.Product.Any+import Data.Maybe+import Data.Monoid                    ((<>))+import Data.Thyme.Clock.POSIX         (POSIXTime)+import Data.Thyme.Time.Core+import Options.Applicative++import qualified Data.Attoparsec.ByteString as AP+import qualified Data.ByteString            as BS+import qualified Data.ByteString.Lazy       as LBS+import qualified Data.Text                  as T+import qualified System.IO                  as IO++{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}++parseLsOptions :: Parser LsOptions+parseLsOptions = LsOptions+  <$> strOption+      (   long "source"+      <>  metavar "FILE"+      <>  value "-"+      <>  help "Input file"+      )+  <*> strOption+      (   long "target"+      <>  metavar "FILE"+      <>  value "-"+      <>  help "Output file"+      )++commandLs :: Parser (IO ())+commandLs = runResourceT . runDump <$> parseLsOptions++runDump :: MonadResource m => LsOptions -> m ()+runDump opt = do+  (_, hIn)  <- openFileOrStd (opt ^. the @"source") IO.ReadMode+  (_, hOut) <- openFileOrStd (opt ^. the @"target") IO.WriteMode++  contents <- liftIO $ LBS.hGetContents hIn++  case extractSegments magic contents of+    Left errorMessage -> do+      liftIO $ IO.hPutStrLn IO.stderr $ "Error occured: " <> errorMessage+      return ()+    Right segments -> do+      let filenames = fromMaybe "" . (^. the @"meta" . the @"filename") <$> segments+      let namedSegments = zip filenames segments++      forM_ (zip [0..] namedSegments) $ \(i :: Int, (filename, segment)) -> do+        let meta = segment ^. the @"meta"++        liftIO $ IO.hPutStrLn hOut $ mempty+          <> (show i & trimPadLeft 4)+          <> " " <> (meta     ^. the @"createTime" <&> showPosixSeconds & fromMaybe ""  & trimPadLeft   19)+          <> " " <> (meta     ^. the @"format"     <&> show             & fromMaybe ""  & trimPadRight  20)+          <> " " <> (segment  ^. the @"payload"    &   showLbsLength                    & trimPadLeft   12)+          <> " " <> T.unpack filename++  where magic = AP.string "seg:" *> (BS.pack <$> many AP.anyWord8) AP.<?> "\"seg:????\""++showLbsLength :: LBS.ByteString -> String+showLbsLength = show . LBS.length++showPosixSeconds :: POSIXTime -> String+showPosixSeconds t = take 19 (show (posixSecondsToUTCTime t))++trimPadLeft :: Int -> String -> String+trimPadLeft n s+  | len < n   = replicate (n - len) ' ' <> s+  | len == n  = s+  | otherwise = '<':reverse (take (n - 1) (reverse s))+  where len = length s++trimPadRight :: Int -> String -> String+trimPadRight n s+  | len < n   = s <> replicate (n - len) ' '+  | len == n  = s+  | otherwise = take (n - 1) s <> ">"+  where len = length s
app/App/Commands/Options/Type.hs view
@@ -22,6 +22,25 @@   } deriving (Eq, Show, Generic)  data DumpOptions = DumpOptions+  { source           :: FilePath+  , target           :: FilePath+  , withoutSegments  :: [Int]+  , withoutFilenames :: [FilePath]+  } deriving (Eq, Show, Generic)++data DumpOnlyOptions = DumpOnlyOptions+  { source    :: FilePath+  , target    :: FilePath+  , segments  :: [Int]+  , filenames :: [FilePath]+  } deriving (Eq, Show, Generic)++data DumpBitmapOptions = DumpBitmapOptions+  { source :: FilePath+  , target :: FilePath+  } deriving (Eq, Show, Generic)++data LsOptions = LsOptions   { source :: FilePath   , target :: FilePath   } deriving (Eq, Show, Generic)
+ app/App/Dump.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module App.Dump+  ( dumpSegment+  , word64ToList+  ) where++import Arbor.File.Format.Asif.Data.Ip+import Arbor.File.Format.Asif.Segment+import Arbor.File.Format.Asif.Whatever+import Control.Lens+import Control.Monad+import Control.Monad.IO.Class          (liftIO)+import Control.Monad.Trans.Resource    (MonadResource)+import Data.Char                       (isPrint)+import Data.Generics.Product.Any+import Data.List+import Data.Monoid                     ((<>))+import Data.Text                       (Text)+import Data.Thyme.Clock+import Data.Thyme.Clock.POSIX          (POSIXTime)+import Data.Thyme.Format               (formatTime)+import Data.Thyme.Time.Core+import Data.Word+import HaskellWorks.Data.Bits.BitShow+import HaskellWorks.Data.Bits.BitWise+import Numeric                         (showHex)+import System.IO                       (Handle)+import System.Locale                   (defaultTimeLocale, iso8601DateFormat)++import qualified Arbor.File.Format.Asif.ByteString.Lazy as LBS+import qualified Arbor.File.Format.Asif.Format          as F+import qualified Data.Binary                            as G+import qualified Data.Binary.Get                        as G+import qualified Data.Bits                              as B+import qualified Data.ByteString.Lazy                   as LBS+import qualified Data.ByteString.Lazy.Char8             as LBSC+import qualified Data.Text                              as T+import qualified Data.Text.Encoding                     as T+import qualified System.IO                              as IO++{-# ANN module ("HLint: ignore Reduce duplication"  :: String) #-}+{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}++showTime :: FormatTime t => t -> String+showTime = formatTime defaultTimeLocale (iso8601DateFormat (Just "%H:%M:%S %Z"))++getWord32x4 :: G.Get (Word32, Word32, Word32, Word32)+getWord32x4 = do+  a <- G.getWord32be+  b <- G.getWord32be+  c <- G.getWord32be+  d <- G.getWord32be+  return (a, b, c, d)++word64ToList :: Int -> Word64 -> [Word32] -> [Word32]+word64ToList _ 0 = id+word64ToList o w = (ip:) . word64ToList o (w .&. comp b)+  where p  = B.countTrailingZeros w+        hi = o .<. 6+        ip = fromIntegral (p .|. hi)+        b  = 1 .<. fromIntegral p++dumpSegment :: MonadResource m => Handle -> Int -> Text -> Segment LBS.ByteString -> m ()+dumpSegment hOut i filename segment = do+        if T.null filename+          then liftIO $ IO.hPutStrLn hOut $ "==== [" <> show i <> "] ===="+          else liftIO $ IO.hPutStrLn hOut $ "==== [" <> show i <> "] " <> T.unpack filename <> " ===="++        case segment ^. the @"meta" . the @"format" of+          Just (Known F.StringZ) -> do+            when (LBS.length (segment ^. the @"payload") > 0) $+              forM_ (init (LBS.split 0 (segment ^. the @"payload"))) $ \bs ->+                liftIO $ IO.hPutStrLn hOut $ T.unpack (T.decodeUtf8 (LBS.toStrict bs))+          Just (Known (F.Repeat n F.Char)) ->+            forM_ (LBS.chunkBy (fromIntegral n) (segment ^. the @"payload")) $ \bs ->+              liftIO $ IO.hPutStrLn hOut $ T.unpack (T.decodeUtf8 (LBS.toStrict bs))+          Just (Known F.TimeMillis64LE) ->+            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getInt64le (LBS.take 8 (bs <> LBS.replicate 8 0))+              let t :: POSIXTime = (w * 1000) ^. from microseconds+              liftIO $ IO.hPutStrLn hOut $ showTime (posixSecondsToUTCTime t) <> " (" <> show w <> " ms)"+          Just (Known F.TimeMicros64LE) ->+            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getInt64le (LBS.take 8 (bs <> LBS.replicate 8 0))+              let t :: POSIXTime = w ^. from microseconds+              liftIO $ IO.hPutStrLn hOut $ showTime (posixSecondsToUTCTime t) <> " (" <> show w <> " µs)"+          Just (Known F.Ipv4) ->+            forM_ (LBS.chunkBy 4 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getWord32le (LBS.take 8 (bs <> LBS.replicate 4 0))+              let ipString = w & word32ToIpv4 & ipv4ToString+              liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (16 - length ipString) ' ' <> "(" <> show w <> ")"+          Just (Known F.Ipv6) ->+            forM_ (LBS.chunkBy 16 (segment ^. the @"payload")) $ \bs -> do+              let (a, b, c, d) = G.runGet getWord32x4 (LBS.take 16 (bs <> LBS.replicate 16 0))+              if a == 0 && b == 0 && c == 0xFFFF+                then do+                  let ipString = d & word32ToIpv4 & ipv4ToString+                  liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (64 - length ipString) ' ' <> "(" <> show d <> ")"+                else do+                  let ipString = word32x4ToIpv6 (a, b, c, d) & ipv6ToString+                  liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (64 - length ipString) ' ' <> "(" <> show (a, b, c, d) <> ")"+          Just (Known F.Int64LE) ->+            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getInt64le (LBS.take 8 (bs <> LBS.replicate 8 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Int32LE) ->+            forM_ (LBS.chunkBy 4 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getInt32le (LBS.take 4 (bs <> LBS.replicate 4 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Int16LE) ->+            forM_ (LBS.chunkBy 2 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getInt16le (LBS.take 2 (bs <> LBS.replicate 2 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Int8) ->+            forM_ (LBS.chunkBy 1 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getInt8 (LBS.take 1 (bs <> LBS.replicate 1 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Word64LE) ->+            forM_ (LBS.chunkBy 8 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getWord64le (LBS.take 8 (bs <> LBS.replicate 8 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Word32LE) ->+            forM_ (LBS.chunkBy 4 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getWord32le (LBS.take 4 (bs <> LBS.replicate 4 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Word16LE) ->+            forM_ (LBS.chunkBy 2 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getWord16le (LBS.take 2 (bs <> LBS.replicate 2 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Word8) ->+            forM_ (LBS.chunkBy 1 (segment ^. the @"payload")) $ \bs -> do+              let w = G.runGet G.getWord8 (LBS.take 1 (bs <> LBS.replicate 1 0))+              liftIO $ IO.hPrint hOut w+          Just (Known F.Text) ->+            liftIO $ LBSC.hPutStrLn hOut (segment ^. the @"payload")+          Just (Known F.BitString) ->+            liftIO $ IO.hPutStrLn hOut (bitShow (segment ^. the @"payload"))+          Just (Known F.Bitmap) ->+            forM_ (zip [0..] (G.runGet G.getWord64le <$> LBS.chunkBy 8 (segment ^. the @"payload"))) $ \(idx, w64) ->+              forM_ (word64ToList idx w64 []) $ \w32 -> do+                let ipString = w32 & word32ToIpv4 & ipv4ToString+                liftIO $ IO.hPutStrLn hOut $ ipString <> replicate (16 - length ipString) ' ' <> "(" <> show w32 <> ")"+          _ ->+            forM_ (zip (LBS.chunkBy 16 (segment ^. the @"payload")) [0 :: Int, 16..]) $ \(bs, j) -> do+              let bytes = mconcat (intersperse " " (reverse . take 2 . reverse . ('0':) . flip showHex "" <$> LBS.unpack bs))+              liftIO $ IO.hPutStr hOut $ reverse $ take 8 $ reverse $ ("0000000" ++) $ showHex j ""+              liftIO $ IO.hPutStr hOut "  "+              liftIO $ IO.hPutStr hOut $ bytes <> replicate (47 - length bytes) ' '+              liftIO $ IO.hPutStr hOut "  "+              liftIO $ IO.hPutStr hOut $ (\c -> if isPrint c then c else '.') <$> LBSC.unpack bs+              liftIO $ IO.hPutStrLn hOut ""
asif.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack  name:           asif-version:        3.1.0+version:        3.2.0 description:    Please see the README on Github at <https://github.com/packetloop/asif#readme> category:       Services homepage:       https://github.com/packetloop/asif#readme@@ -63,6 +63,7 @@       Arbor.File.Format.Asif.Data.Read       Arbor.File.Format.Asif.Extract       Arbor.File.Format.Asif.Format+      Arbor.File.Format.Asif.Format.Text       Arbor.File.Format.Asif.Get       Arbor.File.Format.Asif.IO       Arbor.File.Format.Asif.Lookup@@ -70,6 +71,9 @@       Arbor.File.Format.Asif.Type       Arbor.File.Format.Asif.Whatever   other-modules:+      Arbor.File.Format.Asif.Format.SegmentValue+      Arbor.File.Format.Asif.Format.Type+      Arbor.File.Format.Asif.List       Arbor.File.Format.Asif.Maybe       Arbor.File.Format.Asif.Text       Paths_asif@@ -111,10 +115,14 @@   other-modules:       App.Commands       App.Commands.Dump+      App.Commands.DumpBitmap+      App.Commands.DumpOnly       App.Commands.EncodeFiles       App.Commands.ExtractFiles       App.Commands.ExtractSegments+      App.Commands.Ls       App.Commands.Options.Type+      App.Dump       App.IO   default-language: Haskell2010 @@ -156,7 +164,10 @@   other-modules:       Arbor.File.Format.Asif.ByteString.BuilderSpec       Arbor.File.Format.Asif.Data.IpSpec+      Arbor.File.Format.Asif.Format.SegmentValueSpec+      Arbor.File.Format.Asif.Format.TextSpec       Arbor.File.Format.AsifSpec+      Arbor.TestUtils       Gen.Feed       TestApp   default-language: Haskell2010
src/Arbor/File/Format/Asif/ByteString/Builder.hs view
@@ -10,32 +10,32 @@   , magicLength   ) where -import Arbor.File.Format.Asif.Whatever-import Conduit-import Control.Lens-import Control.Monad-import Data.Bits-import Data.ByteString.Builder-import Data.Generics.Product.Any-import Data.Int-import Data.Maybe-import Data.Monoid                     ((<>))-import Data.String-import Data.Thyme.Clock-import Data.Thyme.Clock.POSIX          (POSIXTime, getPOSIXTime)-import Data.Word+import           Arbor.File.Format.Asif.Whatever+import           Conduit+import           Control.Lens+import           Control.Monad+import           Data.Bits+import           Data.ByteString.Builder+import           Data.Generics.Product.Any+import           Data.Int+import           Data.Maybe+import           Data.Monoid                        ((<>))+import           Data.String+import           Data.Thyme.Clock+import           Data.Thyme.Clock.POSIX             (POSIXTime, getPOSIXTime)+import           Data.Word -import qualified Arbor.File.Format.Asif.Format as F-import qualified Arbor.File.Format.Asif.IO     as IO-import qualified Arbor.File.Format.Asif.Type   as Z-import qualified Data.ByteString               as BS-import qualified Data.ByteString.Builder       as B-import qualified Data.ByteString.Lazy          as LBS-import qualified Data.ByteString.Lazy.Char8    as LC8-import qualified Data.Conduit.List             as CL-import qualified Data.Text.Encoding            as T-import qualified GHC.IO.Handle                 as IO-import qualified System.IO.Temp                as IO+import qualified Arbor.File.Format.Asif.Format.Type as F+import qualified Arbor.File.Format.Asif.IO          as IO+import qualified Arbor.File.Format.Asif.Type        as Z+import qualified Data.ByteString                    as BS+import qualified Data.ByteString.Builder            as B+import qualified Data.ByteString.Lazy               as LBS+import qualified Data.ByteString.Lazy.Char8         as LC8+import qualified Data.Conduit.List                  as CL+import qualified Data.Text.Encoding                 as T+import qualified GHC.IO.Handle                      as IO+import qualified System.IO.Temp                     as IO  makeMagic :: String -> Builder makeMagic c = B.lazyByteString (magicString c)
src/Arbor/File/Format/Asif/Extract.hs view
@@ -13,21 +13,21 @@   , vectorUnboxed   ) where -import Arbor.File.Format.Asif.Format   (Format)-import Arbor.File.Format.Asif.Whatever-import Control.Lens-import Data.Binary.Get-import Data.List                       hiding (map)-import Data.Text                       (Text)-import Data.Text.Encoding              (decodeUtf8')-import Data.Text.Encoding.Error-import Prelude                         hiding (map)+import           Arbor.File.Format.Asif.Format.Type (Format)+import           Arbor.File.Format.Asif.Whatever+import           Control.Lens+import           Data.Binary.Get+import           Data.List                          hiding (map)+import           Data.Text                          (Text)+import           Data.Text.Encoding                 (decodeUtf8')+import           Data.Text.Encoding.Error+import           Prelude                            hiding (map) -import qualified Data.Binary.Get      as G-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Map.Strict      as M-import qualified Data.Vector          as V-import qualified Data.Vector.Unboxed  as VU+import qualified Data.Binary.Get                    as G+import qualified Data.ByteString.Lazy               as LBS+import qualified Data.Map.Strict                    as M+import qualified Data.Vector                        as V+import qualified Data.Vector.Unboxed                as VU  vectorBoxed :: Get a -> LBS.ByteString -> V.Vector a vectorBoxed g = V.unfoldr step
src/Arbor/File/Format/Asif/Format.hs view
@@ -1,26 +1,16 @@-{-# LANGUAGE DeriveGeneric #-}+module Arbor.File.Format.Asif.Format+  ( module X+  , SegmentValue (..)+  , segmentValues+  , extractValues+  )+where -module Arbor.File.Format.Asif.Format where+import           Arbor.File.Format.Asif.Format.Type         as X -import GHC.Generics+import           Arbor.File.Format.Asif.Format.SegmentValue+import           Arbor.File.Format.Asif.Segment+import           Data.ByteString.Lazy                       as LBS -data Format-  = Binary-  | BitString-  | Char-  | Int8-  | Int16LE-  | Int32LE-  | Int64LE-  | Ipv4-  | Ipv6-  | Repeat Word Format-  | StringZ-  | Text-  | TimeMicros64LE-  | TimeMillis64LE-  | Word8-  | Word16LE-  | Word32LE-  | Word64LE-  deriving (Eq, Read, Show, Generic)+extractValues :: (SegmentValue -> a) -> Segment LBS.ByteString -> [a]+extractValues f s = f <$> segmentValues s
+ src/Arbor/File/Format/Asif/Format/SegmentValue.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE DeriveGeneric       #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-}++module Arbor.File.Format.Asif.Format.SegmentValue+where++import qualified Arbor.File.Format.Asif.ByteString.Lazy as LBS+import           Arbor.File.Format.Asif.Data.Ip+import qualified Arbor.File.Format.Asif.Format.Type     as F+import           Arbor.File.Format.Asif.List            as L+import           Arbor.File.Format.Asif.Segment+import           Arbor.File.Format.Asif.Whatever+import           Control.Lens+import qualified Data.Binary                            as G+import qualified Data.Binary.Get                        as G+import qualified Data.ByteString.Lazy                   as LBS+import qualified Data.ByteString.Lazy.Char8             as LBSC+import           Data.Generics.Product.Any+import           Data.Int+import           Data.IP+import           Data.Semigroup                         ((<>))+import qualified Data.Text                              as T+import           Data.Thyme.Time.Core+import           Data.Word+import           GHC.Generics                           (Generic)++data SegmentValue+  = SString LBS.ByteString+  | SChar Char+  | STime UTCTime+  | SIpv4 IPv4+  | SIpv6 IPv6+  | SInt64 Int64+  | SInt32 Int32+  | SInt16 Int16+  | SInt8 Int8+  | SWord64 Word64+  | SWord32 Word32+  | SWord16 Word16+  | SWord8 Word8+  | SText LBS.ByteString+  | SBitString LBS.ByteString+  | SBitmap LBS.ByteString+  | SBinary LBS.ByteString+  | SList [SegmentValue]+  | SUnknown T.Text LBS.ByteString+  deriving (Show, Eq, Generic)++getWord32x4 :: G.Get (Word32, Word32, Word32, Word32)+getWord32x4 = do+  a <- G.getWord32be+  b <- G.getWord32be+  c <- G.getWord32be+  d <- G.getWord32be+  return (a, b, c, d)++getValues :: Int64 -> G.Get a -> LBSC.ByteString -> [a]+getValues n f bs =+  let getValue bs' = G.runGet f (LBS.take n (bs' <> LBS.replicate n 0))+  in LBS.chunkBy n bs <&> getValue++getRawValue :: F.Format -> LBS.ByteString -> [SegmentValue]+getRawValue format bs =+  case format of+    F.StringZ ->+      if LBS.null bs+        then []+        else init (LBS.split 0 bs) <&> SString++    F.Char -> whenNonEmpty bs $+      LBSC.unpack bs <&> SChar++    F.TimeMillis64LE -> whenNonEmpty bs $+      let toTime ms = (ms * 1000) ^. from microseconds & posixSecondsToUTCTime+      in bs & getValues 8 G.getInt64le <&> (STime . toTime)++    F.TimeMicros64LE -> whenNonEmpty bs $+      let toTime ms = ms ^. from microseconds & posixSecondsToUTCTime+      in bs & getValues 8 G.getInt64le <&> (STime . toTime)++    F.Ipv4 -> whenNonEmpty bs $+      bs & getValues 4 G.getWord32le <&> (SIpv4 . word32ToIpv4)++    F.Ipv6 -> whenNonEmpty bs $+      bs & getValues 16 getWord32x4 <&> (SIpv6 . word32x4ToIpv6)++    F.Int64LE -> whenNonEmpty bs $+      bs & getValues 8 G.getInt64le <&> SInt64++    F.Int32LE -> whenNonEmpty bs $+      bs & getValues 4 G.getInt32le <&> SInt32++    F.Int16LE -> whenNonEmpty bs $+      bs & getValues 2 G.getInt16le <&> SInt16++    F.Int8 -> whenNonEmpty bs $+      bs & getValues 1 G.getInt8 <&> SInt8++    F.Word64LE -> whenNonEmpty bs $+      bs & getValues 8 G.getWord64le <&> SWord64++    F.Word32LE -> whenNonEmpty bs $+      bs & getValues 4 G.getWord32le <&> SWord32++    F.Word16LE -> whenNonEmpty bs $+      bs & getValues 2 G.getWord16le <&> SWord16++    F.Word8 -> whenNonEmpty bs $+      bs & getValues 1 G.getWord8 <&> SWord8++    F.Text ->+      [SText bs]++    F.Repeat n fmt@F.Text -> whenNonEmpty bs $+      LBS.chunkBy (fromIntegral n) bs >>= getRawValue fmt++    F.BitString ->+      [SBitString bs]++    F.Repeat n fmt@F.BitString -> whenNonEmpty bs $+      LBS.chunkBy (fromIntegral n) bs >>= getRawValue fmt++    F.Binary ->+      [SBinary bs]++    F.Repeat n fmt@F.Binary -> whenNonEmpty bs $+      LBS.chunkBy (fromIntegral n) bs >>= getRawValue fmt++    F.Bitmap ->+      [SBitmap bs]++    F.Repeat n fmt@F.Bitmap -> whenNonEmpty bs $+      LBS.chunkBy (fromIntegral n) bs >>= getRawValue fmt++    F.Repeat n fmt -> whenNonEmpty bs $+      getRawValue fmt bs & L.chunksOf (fromIntegral n) <&> SList++whenNonEmpty :: LBSC.ByteString -> [a] -> [a]+whenNonEmpty bs f =+  if LBS.null bs then [] else f++segmentValues :: Segment LBS.ByteString -> [SegmentValue]+segmentValues segment =+  case segment ^. the @"meta" . the @"format" of+    Just (Known format) -> getRawValue format (segment ^. the @"payload")+    Just (Unknown txt)  -> [SUnknown txt (segment ^. the @"payload")]+    Nothing             -> []+
+ src/Arbor/File/Format/Asif/Format/Text.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE LambdaCase #-}+module Arbor.File.Format.Asif.Format.Text+( segmentValueToText+)+where++import qualified Arbor.File.Format.Asif.ByteString.Lazy     as LBS+import           Arbor.File.Format.Asif.Data.Ip+import           Arbor.File.Format.Asif.Format.SegmentValue+import           Control.Lens+import qualified Data.ByteString.Lazy                       as LBS+import qualified Data.List                                  as L+import           Data.Text                                  (Text)+import qualified Data.Text                                  as T+import qualified Data.Text.Encoding                         as T+import           HaskellWorks.Data.Bits.BitShow             (bitShow)+import           Numeric                                    (showHex)++segmentValueToText :: SegmentValue -> Text+segmentValueToText = \case+  SString v -> T.decodeUtf8 (LBS.toStrict v)+  SChar v -> T.singleton v+  STime v -> T.pack $ show v++  SIpv4 v -> T.pack $ ipv4ToString v+  SIpv6 v ->+    case ipv6ToWord32x4 v of+      (a, b, c, d) | a == 0 && b == 0 && c == 0xFFFF -> d & word32ToIpv4 & ipv4ToString & T.pack+      _ -> T.pack $ ipv6ToString v++  SInt64 v -> T.pack $ show v+  SInt32 v -> T.pack $ show v+  SInt16 v -> T.pack $ show v+  SInt8  v -> T.pack $ show v++  SWord64 v -> T.pack $ show v+  SWord32 v -> T.pack $ show v+  SWord16 v -> T.pack $ show v+  SWord8  v -> T.pack $ show v++  SText v -> T.decodeUtf8 (LBS.toStrict v)++  SBitString v  -> T.pack $ bitShow v+  SBitmap v     -> T.pack $ bitShow v+  SBinary v     -> LBS.chunkBy 4 v <&> toHex & L.intersperse " " & mconcat++  SList vs -> vs <&> segmentValueToText & T.intercalate ","++  SUnknown _ v -> LBS.chunkBy 4 v <&> toHex & L.intersperse " " & mconcat+++toHex :: LBS.ByteString -> Text+toHex bs =+  T.pack $ mconcat (L.reverse . L.take 2 . L.reverse . ('0':) . flip showHex "" <$> LBS.unpack bs)
+ src/Arbor/File/Format/Asif/Format/Type.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE DeriveGeneric #-}++module Arbor.File.Format.Asif.Format.Type+where++import           GHC.Generics++data Format+  = Binary+  | Bitmap+  | BitString+  | Char+  | Int8+  | Int16LE+  | Int32LE+  | Int64LE+  | Ipv4+  | Ipv6+  | Repeat Word Format+  | StringZ+  | Text+  | TimeMicros64LE+  | TimeMillis64LE+  | Word8+  | Word16LE+  | Word32LE+  | Word64LE+  deriving (Eq, Read, Show, Generic)
+ src/Arbor/File/Format/Asif/List.hs view
@@ -0,0 +1,6 @@+module Arbor.File.Format.Asif.List where++import           Data.List++chunksOf :: Int -> [a] -> [[a]]+chunksOf n = takeWhile (not . null) . unfoldr (Just . splitAt n)
src/Arbor/File/Format/Asif/Segment.hs view
@@ -6,6 +6,8 @@   , mkDefaultSegment   , extractSegments   , extractNamedSegments+  , extractHeader+  , extractSegmentByteStringsWithHeader   , segmentNamed   ) where @@ -34,7 +36,8 @@  extractSegments :: AP.Parser BS.ByteString -> LBS.ByteString -> Either String [Z.Segment LBS.ByteString] extractSegments magicParser bs = do-  bss <- extractSegmentByteStrings magicParser bs+  header <- extractHeader magicParser bs+  bss <- extractSegmentByteStringsWithHeader bs header   case bss of     (as:_) -> if ".asif/filenames\0" `LBS.isPrefixOf` as       then do@@ -57,10 +60,16 @@   let filenames = fromMaybe "" . (^. the @"meta" . the @"filename") <$> segments   return $ M.fromList $ zip filenames segments +extractHeader :: AP.Parser BS.ByteString -> LBS.ByteString -> Either String [(Int, Int)]+extractHeader magicParser bs = case runGetOrFail (getHeader magicParser) bs of+  Left (_, _, err)     -> Left err+  Right (_, _, header) -> Right header+ extractSegmentByteStrings :: AP.Parser BS.ByteString -> LBS.ByteString -> Either String [LBS.ByteString]-extractSegmentByteStrings magicParser bs = case runGetOrFail (getHeader magicParser) bs of-  Left (_, _, err) -> Left err-  Right (_, _, header) -> do+extractSegmentByteStrings magicParser bs = extractHeader magicParser bs >>= extractSegmentByteStringsWithHeader bs++extractSegmentByteStringsWithHeader :: LBS.ByteString -> [(Int, Int)] -> Either String [LBS.ByteString]+extractSegmentByteStringsWithHeader bs header = do     let segs = fmap (\(o, l) -> LBS.take (fromIntegral l) $ LBS.drop (fromIntegral o) bs) header     forM_ (zip segs header) $ \(seg, (_, len)) ->       when (LC8.length seg /= fromIntegral len) $
src/Arbor/File/Format/Asif/Type.hs view
@@ -2,13 +2,13 @@  module Arbor.File.Format.Asif.Type where -import Arbor.File.Format.Asif.Format   (Format)-import Arbor.File.Format.Asif.Maybe-import Arbor.File.Format.Asif.Whatever-import Data.Semigroup                  (Semigroup, (<>))-import Data.Text                       (Text)-import Data.Thyme.Clock.POSIX          (POSIXTime)-import GHC.Generics+import           Arbor.File.Format.Asif.Format.Type (Format)+import           Arbor.File.Format.Asif.Maybe+import           Arbor.File.Format.Asif.Whatever+import           Data.Semigroup                     (Semigroup, (<>))+import           Data.Text                          (Text)+import           Data.Thyme.Clock.POSIX             (POSIXTime)+import           GHC.Generics  data SegmentMeta = SegmentMeta   { createTime :: Maybe POSIXTime
+ test/Arbor/File/Format/Asif/Format/SegmentValueSpec.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Arbor.File.Format.Asif.Format.SegmentValueSpec+  ( spec+  ) where++import           Arbor.File.Format.Asif.Data.Ip+import           Arbor.File.Format.Asif.Format   as F+import           Arbor.File.Format.Asif.Type+import           Arbor.File.Format.Asif.Whatever+import           Control.Lens+import qualified Data.List                       as L+import           Data.Semigroup                  ((<>))+import           Data.Thyme.Time.Core++import qualified Data.ByteString.Builder         as BB+import qualified Data.ByteString.Lazy            as LBS+import qualified Data.ByteString.Lazy.Char8      as LC8++import           Arbor.TestUtils+import           HaskellWorks.Hspec.Hedgehog+import           Hedgehog+import           Test.Hspec++import qualified Hedgehog.Gen                    as Gen+import qualified Hedgehog.Range                  as Range++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "Arbor.File.Format.Asif.Format.RawValueSpec" $ do+  it "should parse strings" $ require $ property $ do+    bs <- forAll $ Gen.list (Range.linear 0 20) (genLazyBS (Range.linear 0 512) Gen.alphaNum)+    let body = mconcat $ (<> "\0") <$> bs+    let seg = segment body $ metaFormat (Known F.StringZ)+    segmentValues seg === fmap SString bs++  it "should parse chars" $ require $ property $ do+    bs <- forAll $ genLazyBS (Range.linear 0 512) Gen.latin1+    let expected = LC8.unpack bs <&> SChar+    let seg = segment bs $ metaFormat (Known F.Char)+    segmentValues seg === expected++  it "should parse time (micros)" $ require $ property $ do+    ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64 (Range.linear 0 maxBound))+    let body = ts <&> BB.int64LE & mconcat & BB.toLazyByteString+    let times = ts ^.. each . from microseconds . to posixSecondsToUTCTime+    let seg = segment body $ metaFormat (Known F.TimeMicros64LE)+    segmentValues seg === fmap STime times++  it "should parse time (millis)" $ require $ property $ do+    ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64 (Range.linear 0 maxBound) <&> (`div` 1000))+    let body = ts <&> BB.int64LE & mconcat & BB.toLazyByteString+    let times = ts ^.. each . to (*1000) . from microseconds . to posixSecondsToUTCTime+    let seg = segment body $ metaFormat (Known F.TimeMillis64LE)+    segmentValues seg === fmap STime times++  it "should parse int64" $ require $ property $ do+    testMany F.Int64LE SInt64 BB.int64LE (Gen.int64 (Range.linear 0 maxBound))++  it "should parse int32" $ require $ property $ do+    testMany F.Int32LE SInt32 BB.int32LE (Gen.int32 (Range.linear 0 maxBound))++  it "should parse int16" $ require $ property $ do+    testMany F.Int16LE SInt16 BB.int16LE (Gen.int16 (Range.linear 0 maxBound))++  it "should parse int8" $ require $ property $ do+    testMany F.Int8 SInt8 BB.int8 (Gen.int8 (Range.linear 0 maxBound))++  it "should parse word64" $ require $ property $ do+    testMany F.Word64LE SWord64 BB.word64LE (Gen.word64 (Range.linear 0 maxBound))++  it "should parse word32" $ require $ property $ do+    testMany F.Word32LE SWord32 BB.word32LE (Gen.word32 (Range.linear 0 maxBound))++  it "should parse word16" $ require $ property $ do+    testMany F.Word16LE SWord16 BB.word16LE (Gen.word16 (Range.linear 0 maxBound))++  it "should parse word8" $ require $ property $ do+    testMany F.Word8 SWord8 BB.word8 (Gen.word8 (Range.linear 0 maxBound))++  it "should parse BitString" $ require $ property $+    let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict+    in testOne F.BitString SBitString BB.lazyByteString gen++  it "should parse Binary" $ require $ property $+    let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict+    in testOne F.Binary SBinary BB.lazyByteString gen++  it "should parse Bitmap" $ require $ property $+    let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict+    in testOne F.Bitmap SBitmap BB.lazyByteString gen++  it "should parse repeated BitString" $ require $ property $ do+    n <- forAll $ Gen.int (Range.linear 1 32)+    vs <- forAll $ Gen.list (Range.linear 0 20) (Gen.bytes (Range.singleton n) <&> LBS.fromStrict)+    let seg = segment (mconcat vs) $ metaFormat (Known (Repeat (fromIntegral n) F.BitString))+    segmentValues seg === fmap SBitString vs++  it  "should parse repeated chars" $ require $ property $ do+    n <- forAll $ Gen.int (Range.linear 1 32)+    vs <- forAll $ Gen.list (Range.linear 0 20) (Gen.string (Range.singleton n) Gen.alphaNum)+    let seg = segment (LC8.pack $ mconcat vs) $ metaFormat (Known (Repeat (fromIntegral n) F.Char))+    segmentValues seg === fmap (SList . fmap SChar) vs++  it "should parse repeated ips" $ require $ property $ do+    n <- forAll $ Gen.int (Range.linear 1 32)+    k <- forAll $ Gen.int (Range.linear 0 20)+    vs <- forAll $ Gen.list (Range.singleton (n*k)) (Gen.word32 Range.linearBounded)+    let body = vs <&> BB.word32LE & mconcat & BB.toLazyByteString+    let seg = segment body $ metaFormat (Known (Repeat (fromIntegral n) F.Ipv4))+    let expected = vs <&> (SIpv4 . word32ToIpv4) & chunksOf n <&> SList+    segmentValues seg === expected++genLazyBS :: MonadGen m => Range Int -> m Char -> m LBS.ByteString+genLazyBS rng gen = Gen.utf8 rng gen <&> LBS.fromStrict++testOne :: (Monad m, Show a)+        => Format+        -> (a -> SegmentValue)+        -> (a -> BB.Builder)+        -> Gen a+        -> PropertyT m ()+testOne fmt wrap put gen = do+  v <- forAll gen+  let body = put v & BB.toLazyByteString+  let seg = segment body $ metaFormat (Known fmt)+  segmentValues seg === [wrap v]++testMany :: (Monad m, Show a)+          => Format+          -> (a -> SegmentValue)+          -> (a -> BB.Builder)+          -> Gen a+          -> PropertyT m ()+testMany fmt wrap put gen = do+  vs <- forAll $ Gen.list (Range.linear 0 20) gen+  let body = vs <&> put & mconcat & BB.toLazyByteString+  let seg = segment body $ metaFormat (Known fmt)+  segmentValues seg === fmap wrap vs
+ test/Arbor/File/Format/Asif/Format/TextSpec.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE DataKinds           #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Arbor.File.Format.Asif.Format.TextSpec+  ( spec+  ) where++import           Arbor.File.Format.Asif.Data.Ip+import           Arbor.File.Format.Asif.Format      as F+import           Arbor.File.Format.Asif.Format.Text as F+import           Arbor.File.Format.Asif.Type+import           Arbor.File.Format.Asif.Whatever+import           Control.Lens+import qualified Data.List                          as L+import           Data.Semigroup                     ((<>))+import           Data.Thyme.Time.Core+import           HaskellWorks.Data.Bits.BitShow     (bitShow)++import qualified Data.ByteString.Builder            as BB+import qualified Data.ByteString.Lazy               as LBS+import qualified Data.ByteString.Lazy.Char8         as LC8+import qualified Data.Text                          as T+import qualified Data.Text.Encoding                 as T++import           Arbor.TestUtils+import           HaskellWorks.Hspec.Hedgehog+import           Hedgehog+import           Test.Hspec+++import qualified Hedgehog.Gen                       as Gen+import qualified Hedgehog.Range                     as Range++{-# ANN module ("HLint: ignore Redundant do"  :: String) #-}++spec :: Spec+spec = describe "Arbor.File.Format.Asif.Format.TextSpec" $ do+  it "should parse strings" $ require $ property $ do+    ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.text (Range.linear 0 512) Gen.alphaNum)+    let body = ts <&> T.encodeUtf8 <&> (<> "\0") & mconcat & LBS.fromStrict+    let seg = segment body $ metaFormat (Known F.StringZ)+    F.extractValues F.segmentValueToText seg === ts++  it "should parse chars" $ require $ property $ do+    txt <- forAll $ Gen.text (Range.linear 0 512) Gen.alphaNum+    let expected = T.unpack txt <&> T.singleton+    let body = txt & T.encodeUtf8 & LBS.fromStrict+    let seg = segment body $ metaFormat (Known F.Char)+    F.extractValues F.segmentValueToText seg === expected++  it "should parse time (micros)" $ require $ property $ do+    ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64 (Range.linear 0 maxBound))+    let body = ts <&> BB.int64LE & mconcat & BB.toLazyByteString+    let times = ts ^.. each . from microseconds . to posixSecondsToUTCTime+    let seg = segment body $ metaFormat (Known F.TimeMicros64LE)+    F.extractValues F.segmentValueToText seg === (T.pack . show <$> times)++  it "should parse time (millis)" $ require $ property $ do+    ts <- forAll $ Gen.list (Range.linear 0 20) (Gen.int64 (Range.linear 0 (maxBound `div` 1000)))+    let body = ts <&> BB.int64LE & mconcat & BB.toLazyByteString+    let times = ts ^.. each . to (*1000) . from microseconds . to posixSecondsToUTCTime+    let seg = segment body $ metaFormat (Known F.TimeMillis64LE)+    F.extractValues F.segmentValueToText seg === (T.pack . show <$> times)++  it "should parse int64" $ require $ property $ do+    testMany F.Int64LE (T.pack . show) BB.int64LE (Gen.int64 (Range.linear 0 maxBound))++  it "should parse int32" $ require $ property $ do+    testMany F.Int32LE (T.pack . show) BB.int32LE (Gen.int32 (Range.linear 0 maxBound))++  it "should parse int16" $ require $ property $ do+    testMany F.Int16LE (T.pack . show) BB.int16LE (Gen.int16 (Range.linear 0 maxBound))++  it "should parse int8" $ require $ property $ do+    testMany F.Int8 (T.pack . show) BB.int8 (Gen.int8 (Range.linear 0 maxBound))++  it "should parse Word64" $ require $ property $ do+    testMany F.Word64LE (T.pack . show) BB.word64LE (Gen.word64 (Range.linear 0 maxBound))++  it "should parse Word32" $ require $ property $ do+    testMany F.Word32LE (T.pack . show) BB.word32LE (Gen.word32 (Range.linear 0 maxBound))++  it "should parse Word16" $ require $ property $ do+    testMany F.Word16LE (T.pack . show) BB.word16LE (Gen.word16 (Range.linear 0 maxBound))++  it "should parse Word8" $ require $ property $ do+    testMany F.Word8 (T.pack . show) BB.word8 (Gen.word8 (Range.linear 0 maxBound))++  it "should parse BitString" $ require $ property $+    let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict+    in testOne F.BitString (T.pack . bitShow) BB.lazyByteString gen++  it "should parse Bitmap" $ require $ property $+    let gen = Gen.bytes (Range.linear 0 256) <&> LBS.fromStrict+    in testOne F.Bitmap (T.pack . bitShow) BB.lazyByteString gen++  it "should parse repeated BitString" $ require $ property $ do+    n <- forAll $ Gen.int (Range.linear 1 32)+    vs <- forAll $ Gen.list (Range.linear 0 20) (Gen.bytes (Range.singleton n) <&> LBS.fromStrict)+    let seg = segment (mconcat vs) $ metaFormat (Known (Repeat (fromIntegral n) F.BitString))+    F.extractValues F.segmentValueToText seg === fmap (T.pack . bitShow) vs++  it  "should parse repeated chars" $ require $ property $ do+    n <- forAll $ Gen.int (Range.linear 1 32)+    vs <- forAll $ Gen.list (Range.linear 0 20) (Gen.string (Range.singleton n) Gen.alphaNum)+    let seg = segment (LC8.pack $ mconcat vs) $ metaFormat (Known (Repeat (fromIntegral n) F.Char))+    F.extractValues F.segmentValueToText seg === fmap (T.pack . L.intersperse ',') vs++  it "should parse repeated ips" $ require $ property $ do+    n <- forAll $ Gen.int (Range.linear 1 32)+    k <- forAll $ Gen.int (Range.linear 0 20)+    vs <- forAll $ Gen.list (Range.singleton (n*k)) (Gen.word32 Range.linearBounded)+    let body = vs <&> BB.word32LE & mconcat & BB.toLazyByteString+    let seg = segment body $ metaFormat (Known (Repeat (fromIntegral n) F.Ipv4))+    let expected = vs <&> (ipv4ToString . word32ToIpv4) & chunksOf n <&> (T.pack . L.intercalate ",")+    F.extractValues F.segmentValueToText seg === expected++testMany :: (Monad m, Show a)+          => Format+          -> (a -> T.Text)+          -> (a -> BB.Builder)+          -> Gen a+          -> PropertyT m ()+testMany fmt wrap put gen = do+  vs <- forAll $ Gen.list (Range.linear 0 20) gen+  let body = vs <&> put & mconcat & BB.toLazyByteString+  let seg = segment body $ metaFormat (Known fmt)+  F.extractValues F.segmentValueToText seg === fmap wrap vs++testOne :: (Monad m, Show a)+        => Format+        -> (a -> T.Text)+        -> (a -> BB.Builder)+        -> Gen a+        -> PropertyT m ()+testOne fmt wrap put gen = do+  v <- forAll gen+  let body = put v & BB.toLazyByteString+  let seg = segment body $ metaFormat (Known fmt)+  F.extractValues F.segmentValueToText seg === [wrap v]
+ test/Arbor/TestUtils.hs view
@@ -0,0 +1,8 @@+module Arbor.TestUtils+where++import qualified Data.List as L++chunksOf :: Int -> [a] -> [[a]]+chunksOf n = L.takeWhile (not . L.null) . L.unfoldr (Just . L.splitAt n)+