packages feed

outsort-0.1.0: Algorithms/SortMain.hs

{-# LANGUAGE LambdaCase, OverloadedStrings, FlexibleContexts, BangPatterns, PackageImports #-}

module Algorithms.SortMain
    ( sortMain
    , sortMain'
    , sortMainStorable
    , mergeMain
    , mergeMain'
    ) where

import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
import qualified Data.Conduit as C
import qualified Data.Conduit.List as CL
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.Algorithms as CAlg
import           Control.Concurrent (setNumCapabilities)
import           Control.Monad.IO.Class (liftIO)
import           Data.Conduit ((.|))
import           System.Console.GetOpt
import           System.Environment (getArgs)
import           Data.List (foldl')
import           Safe (atDef)
import           System.Exit (exitFailure)
import           Control.Monad.Trans.Resource (MonadResource)
import           Control.Monad.IO.Class (MonadIO)
import           Foreign

import Algorithms.OutSort


readAllFiles :: (MonadIO m, MonadResource m) =>
                        Bool -- ^ verbose
                        -> [FilePath] -- ^ file list
                        -> C.Source m B.ByteString
readAllFiles False ifiles = sequence_ (map CB.sourceFile ifiles)
readAllFiles True  ifiles = readAllFiles' (zip [(1::Int)..] ifiles)
    where
        n = length ifiles
        readAllFiles' [] = return ()
        readAllFiles' ((i,f):rest) = do
            liftIO $ putStrLn ("Reading file "++f++" ("++show i++"/"++show n++")")
            CB.sourceFile f
            readAllFiles' rest

data CmdArgs = CmdArgs
                { optIFile :: FilePath
                , optOFile :: FilePath
                , optIFileList :: FilePath
                , optVerbose :: Bool
                , nJobs :: Int
                } deriving (Eq, Show)

data CmdFlags = OutputFile FilePath
                | InputFile FilePath
                | ListFile FilePath
                | NJobs Int
                | Verbose
                deriving (Eq, Show)

options :: [OptDescr CmdFlags]
options =
    [ Option ['v'] ["verbose"] (NoArg Verbose)  "verbose mode"
    , Option ['i'] ["input"] (ReqArg InputFile "FILE") "Input file"
    , Option ['F'] ["file-list"] (ReqArg ListFile "FILE") "Input is a list of files"
    , Option ['o'] ["output"] (ReqArg OutputFile "FILE") "Output file"
    , Option ['j'] ["threads", "jobs"] (ReqArg (NJobs . read) "INT") "Nr threads"
    ]


parseArgs :: [String] -> CmdArgs
parseArgs argv = foldl' p (CmdArgs ifile ofile "" False 1) flags
    where
        (flags, args, _extraOpts) = getOpt Permute options argv
        ifile = atDef "" args 0
        ofile = atDef "" args 1

        p c (OutputFile o) = c { optOFile = o }
        p c (InputFile i) = c { optIFile = i }
        p c (ListFile f) = c { optIFileList = f }
        p c (NJobs n) = c { nJobs = n }
        p c Verbose = c { optVerbose = True }

extractIFiles :: CmdArgs -> IO [FilePath]
extractIFiles opts = case (optIFile opts, optIFileList opts) of
    (ifile, "") -> return [ifile]
    ("", ffile) -> C.runConduitRes $
                        CB.sourceFile ffile
                            .| CB.lines
                            .| CL.map B8.unpack
                            .| CL.consume
    _ -> do
        putStrLn "Cannot pass both input file and -F argument"
        exitFailure


sortMain' :: Ord a =>
            [String] -- ^ command line arguments
            -> C.Conduit B.ByteString RIO a -- ^ decoder
            -> C.Conduit a RIO B.ByteString -- ^ encoder
            -> C.Conduit a RIO a -- ^ isolate a block
            -> IO ()
sortMain' args decoder encoder isolator = do
    let opts = parseArgs args
        nthreads = nJobs opts
    setNumCapabilities nthreads
    ifiles <- extractIFiles opts
    outsort
        decoder
        encoder
        isolator
        (readAllFiles (optVerbose opts) ifiles)
        (CB.sinkFileCautious $ optOFile opts)


-- | Simple main function
sortMain :: Ord a =>
                C.Conduit B8.ByteString RIO a
                -> C.Conduit a RIO B8.ByteString
                -> C.Conduit a RIO a
                -> IO ()
sortMain decoder encoder isolator = do
    args <- getArgs
    sortMain' args decoder encoder isolator

-- | Simple main function
sortMainStorable :: (Storable a, Ord a, Show a) =>
                 a
                 -> Int
                -> IO ()
sortMainStorable dummy chunkSize = do
    args <- getArgs
    let opts = parseArgs args
        nthreads = nJobs opts
    setNumCapabilities nthreads
    ifiles <- extractIFiles opts
    outsortStorable
        dummy
        chunkSize
        (readAllFiles (optVerbose opts) ifiles)
        (CB.sinkFileCautious $ optOFile opts)

mergeMain' :: Ord a =>
            [String] -- ^ command line arguments
            -> C.Conduit B.ByteString RIO a -- ^ decoder
            -> C.Conduit a RIO B.ByteString -- ^ encoder
            -> IO ()
mergeMain' args decoder encoder = do
    let opts = parseArgs args
        nthreads = nJobs opts
    setNumCapabilities nthreads
    ifiles <- extractIFiles opts
    C.runConduitRes $
        CAlg.mergeC [(CB.sourceFile f .| decoder) | f <- ifiles]
        .| encoder
        .| CB.sinkFileCautious (optOFile opts)

mergeMain :: Ord a =>
                C.Conduit B8.ByteString RIO a -- ^ decoder
                -> C.Conduit a RIO B8.ByteString -- ^ encoder
                -> IO ()
mergeMain decoder encoder = do
    args <- getArgs
    mergeMain' args decoder encoder