packages feed

snack-0.1.0.0: bench/Bench.hs

-- |
-- Module      :  Bench
-- License     :  CC0-1.0
--
-- Maintainer  :  mordae@anilinux.org
-- Stability   :  unstable
-- Portability :  non-portable (ghc)
--

{-# OPTIONS_GHC -ddump-to-file -ddump-stg-from-core -ddump-cmm-opt -dsuppress-all -dppr-cols=200 #-}

module Main
  ( main
  )
where
  import Data.String.Conversions

  import Control.Applicative
  import Data.Either
  import Data.Maybe

  import Data.Text (Text)
  import Data.ByteString (ByteString)

  import Criterion.Main

  import qualified Data.ByteString.Parser.Char8 as SC
  import qualified Data.Attoparsec.ByteString.Char8 as AC
  import qualified Data.Text.Parser as ST
  import qualified Data.Attoparsec.Text as AT


  main :: IO ()
  main = defaultMain
    [ bgroup "media"
        [ bench "Data.ByteString.Parser.Char8" $ nf scMedia (cs $! sampleMedia)
        , bench "Data.Attoparsec.ByteString"   $ nf acMedia (cs $! sampleMedia)
        , bench "Data.Text.Parser"             $ nf stMedia (cs $! sampleMedia)
        , bench "Data.Attoparsec.Text"         $ nf atMedia (cs $! sampleMedia)
        ]
    , bgroup "kv"
        [ bench "Data.ByteString.Parser.Char8" $ nf scKeyValue (cs $! sampleKeyValue)
        , bench "Data.Attoparsec.ByteString"   $ nf acKeyValue (cs $! sampleKeyValue)
        , bench "Data.Text.Parser"             $ nf stKeyValue (cs $! sampleKeyValue)
        , bench "Data.Attoparsec.Text"         $ nf atKeyValue (cs $! sampleKeyValue)
        ]
    ]



  type Media a = (a, a, [(a, a)], Float)

  sampleMedia :: String
  sampleMedia = "text/html, text/plain;q=0.7"


  sampleKeyValue :: String
  sampleKeyValue = "lst = \"\\\"first\\\", \\\"second\\\", \\\"third\\\"\""


  {-# NOINLINE scMedia #-}
  scMedia :: ByteString -> Maybe [Media ByteString]
  scMedia = SC.parseOnly (pMediaList <* SC.endOfInput)
    where
      pMedia :: SC.Parser (Media ByteString)
      pMediaList = pMedia `SC.sepBy` pSeparator
      pMedia = do
        mainType <- pToken
        subType  <- (SC.char '/' *> pToken) <|> pure ""

        (qs, params) <- partitionEithers <$> SC.many (SC.eitherP pQuality pParameter)

        let quality = fromMaybe 1.0 . listToMaybe $ qs

        return (mainType, subType, params, quality)

      pParameter :: SC.Parser (ByteString, ByteString)
      pParameter = do
        _     <- SC.takeWhile SC.isSpace
        _     <- SC.char ';'
        name  <- pToken
        _     <- SC.char '='
        value <- pValue
        return (name, value)

      pQuality :: SC.Parser Float
      pQuality = do
        _ <- SC.takeWhile SC.isSpace
        _ <- SC.char ';'
        _ <- pSpaced $ SC.char 'q'
        _ <- SC.char '='
        SC.fractional

      pToken :: SC.Parser ByteString
      pToken = pSpaced $ SC.takeTill1 isSpecial

      pSeparator :: SC.Parser Char
      pSeparator = pSpaced $ SC.char ','

      pValue :: SC.Parser ByteString
      pValue = pToken <|> pQuotedStr

      pQuotedStr :: SC.Parser ByteString
      pQuotedStr = pSpaced $ pQuoted $ SC.takeWhile isStrChar

      pSpaced :: SC.Parser a -> SC.Parser a
      pSpaced p = p `SC.wrap` SC.takeWhile SC.isSpace

      pQuoted :: SC.Parser a -> SC.Parser a
      pQuoted p = SC.char '"' *> p <* SC.char '"'

      isStrChar :: (Char -> Bool)
      isStrChar c = c /= '\\' && c /= '"'

      isSpecial :: (Char -> Bool)
      isSpecial c = c <= ' '
                 || c == '(' || c == ')'
                 || c == '<' || c == '>'
                 || c == '@' || c == ',' || c == ';'
                 || c == ':' || c == '\\'
                 || c == '"' || c == '/'
                 || c == '[' || c == ']'
                 || c == '?' || c == '='



  {-# NOINLINE acMedia #-}
  acMedia :: ByteString -> Either String [Media ByteString]
  acMedia = AC.parseOnly (pMediaList <* AC.endOfInput)
    where
      pMedia :: AC.Parser (Media ByteString)
      pMediaList = pMedia `AC.sepBy` pSeparator
      pMedia = do
        mainType <- pToken
        subType  <- (AC.char '/' *> pToken) <|> pure ""

        (qs, params) <- partitionEithers <$> AC.many' (AC.eitherP pQuality pParameter)

        let quality = fromMaybe 1.0 . listToMaybe $ qs

        return (mainType, subType, params, quality)

      pParameter :: AC.Parser (ByteString, ByteString)
      pParameter = do
        _     <- AC.skipSpace
        _     <- AC.char ';'
        name  <- pToken
        _     <- AC.char '='
        value <- pValue
        return (name, value)

      pQuality :: AC.Parser Float
      pQuality = do
        _ <- AC.skipSpace
        _ <- AC.char ';'
        _ <- pSpaced $ AC.char 'q'
        _ <- AC.char '='
        AC.rational

      pToken :: AC.Parser ByteString
      pToken = pSpaced $ AC.takeTill isSpecial

      pSeparator :: AC.Parser Char
      pSeparator = pSpaced $ AC.char ','

      pValue :: AC.Parser ByteString
      pValue = pToken <|> pQuotedStr

      pQuotedStr :: AC.Parser ByteString
      pQuotedStr = pSpaced $ pQuoted $ AC.takeWhile isStrChar

      pSpaced :: AC.Parser a -> AC.Parser a
      pSpaced p = AC.skipSpace *> p <* AC.skipSpace

      pQuoted :: AC.Parser a -> AC.Parser a
      pQuoted p = AC.char '"' *> p <* AC.char '"'

      isStrChar :: (Char -> Bool)
      isStrChar c = c /= '\\' && c /= '"'

      isSpecial :: (Char -> Bool)
      isSpecial c = c <= ' '
                 || c == '(' || c == ')'
                 || c == '<' || c == '>'
                 || c == '@' || c == ',' || c == ';'
                 || c == ':' || c == '\\'
                 || c == '"' || c == '/'
                 || c == '[' || c == ']'
                 || c == '?' || c == '='


  {-# NOINLINE stMedia #-}
  stMedia :: Text -> Maybe [Media Text]
  stMedia = ST.parseOnly (pMediaList <* ST.endOfInput)
    where
      pMedia :: ST.Parser (Media Text)
      pMediaList = pMedia `ST.sepBy` pSeparator
      pMedia = do
        mainType <- pToken
        subType  <- (ST.char '/' *> pToken) <|> pure ""

        (qs, params) <- partitionEithers <$> ST.many (ST.eitherP pQuality pParameter)

        let quality = fromMaybe 1.0 . listToMaybe $ qs

        return (mainType, subType, params, quality)

      pParameter :: ST.Parser (Text, Text)
      pParameter = do
        _     <- ST.takeWhile ST.isSpace
        _     <- ST.char ';'
        name  <- pToken
        _     <- ST.char '='
        value <- pValue
        return (name, value)

      pQuality :: ST.Parser Float
      pQuality = do
        _ <- ST.takeWhile ST.isSpace
        _ <- ST.char ';'
        _ <- pSpaced $ ST.char 'q'
        _ <- ST.char '='
        ST.fractional

      pToken :: ST.Parser Text
      pToken = pSpaced $ ST.takeTill1 isSpecial

      pSeparator :: ST.Parser Char
      pSeparator = pSpaced $ ST.char ','

      pValue :: ST.Parser Text
      pValue = pToken <|> pQuotedStr

      pQuotedStr :: ST.Parser Text
      pQuotedStr = pSpaced $ pQuoted $ ST.takeWhile isStrChar

      pSpaced :: ST.Parser a -> ST.Parser a
      pSpaced p = p `ST.wrap` ST.takeWhile ST.isSpace

      pQuoted :: ST.Parser a -> ST.Parser a
      pQuoted p = ST.char '"' *> p <* ST.char '"'

      isStrChar :: (Char -> Bool)
      isStrChar c = c /= '\\' && c /= '"'

      isSpecial :: (Char -> Bool)
      isSpecial c = c <= ' '
                 || c == '(' || c == ')'
                 || c == '<' || c == '>'
                 || c == '@' || c == ',' || c == ';'
                 || c == ':' || c == '\\'
                 || c == '"' || c == '/'
                 || c == '[' || c == ']'
                 || c == '?' || c == '='



  {-# NOINLINE atMedia #-}
  atMedia :: Text -> Either String [Media Text]
  atMedia = AT.parseOnly (pMediaList <* AT.endOfInput)
    where
      pMedia :: AT.Parser (Media Text)
      pMediaList = pMedia `AT.sepBy` pSeparator
      pMedia = do
        mainType <- pToken
        subType  <- (AT.char '/' *> pToken) <|> pure ""

        (qs, params) <- partitionEithers <$> AT.many' (AT.eitherP pQuality pParameter)

        let quality = fromMaybe 1.0 . listToMaybe $ qs

        return (mainType, subType, params, quality)

      pParameter :: AT.Parser (Text, Text)
      pParameter = do
        _     <- AT.skipSpace
        _     <- AT.char ';'
        name  <- pToken
        _     <- AT.char '='
        value <- pValue
        return (name, value)

      pQuality :: AT.Parser Float
      pQuality = do
        _ <- AT.skipSpace
        _ <- AT.char ';'
        _ <- pSpaced $ AT.char 'q'
        _ <- AT.char '='
        AT.rational

      pToken :: AT.Parser Text
      pToken = pSpaced $ AT.takeTill isSpecial

      pSeparator :: AT.Parser Char
      pSeparator = pSpaced $ AT.char ','

      pValue :: AT.Parser Text
      pValue = pToken <|> pQuotedStr

      pQuotedStr :: AT.Parser Text
      pQuotedStr = pSpaced $ pQuoted $ AT.takeWhile isStrChar

      pSpaced :: AT.Parser a -> AT.Parser a
      pSpaced p = AT.skipSpace *> p <* AT.skipSpace

      pQuoted :: AT.Parser a -> AT.Parser a
      pQuoted p = AT.char '"' *> p <* AT.char '"'

      isStrChar :: (Char -> Bool)
      isStrChar c = c /= '\\' && c /= '"'

      isSpecial :: (Char -> Bool)
      isSpecial c = c <= ' '
                 || c == '(' || c == ')'
                 || c == '<' || c == '>'
                 || c == '@' || c == ',' || c == ';'
                 || c == ':' || c == '\\'
                 || c == '"' || c == '/'
                 || c == '[' || c == ']'
                 || c == '?' || c == '='


  scKeyValue :: ByteString -> Maybe (ByteString, ByteString)
  scKeyValue = SC.parseOnly (pKeyValue <* SC.endOfInput)
    where
      pKeyValue = do
        _   <- SC.skipSpace
        key <- SC.takeWhile1 isToken
        _   <- SC.skipSpace
        _   <- SC.skipSpace
        '=' <- SC.char '='
        _   <- SC.skipSpace
        _   <- SC.char '"'
        val <- SC.scan False scanString
        _   <- SC.char '"'
        return (key, val)


  acKeyValue :: ByteString -> Either String (ByteString, ByteString)
  acKeyValue = AC.parseOnly (pKeyValue <* AC.endOfInput)
    where
      pKeyValue = do
        _   <- AC.skipSpace
        key <- AC.takeWhile1 isToken
        _   <- AC.skipSpace
        '=' <- AC.char '='
        _   <- AC.skipSpace
        _   <- AC.char '"'
        val <- AC.scan False scanString
        _   <- AC.char '"'
        return (key, val)


  stKeyValue :: Text -> Maybe (Text, Text)
  stKeyValue = ST.parseOnly (pKeyValue <* ST.endOfInput)
    where
      pKeyValue = do
        _   <- ST.skipSpace
        key <- ST.takeWhile1 isToken
        _   <- ST.skipSpace
        _   <- ST.skipSpace
        '=' <- ST.char '='
        _   <- ST.skipSpace
        _   <- ST.char '"'
        val <- ST.scan False scanString
        _   <- ST.char '"'
        return (key, val)


  atKeyValue :: Text -> Either String (Text, Text)
  atKeyValue = AT.parseOnly (pKeyValue <* AT.endOfInput)
    where
      pKeyValue = do
        _   <- AT.skipSpace
        key <- AT.takeWhile1 isToken
        _   <- AT.skipSpace
        '=' <- AT.char '='
        _   <- AT.skipSpace
        _   <- AT.char '"'
        val <- AT.scan False scanString
        _   <- AT.char '"'
        return (key, val)


  scanString :: Bool -> Char -> Maybe Bool
  scanString True _     = Just False
  scanString False '"'  = Nothing
  scanString False '\\' = Just True
  scanString False _    = Just False


  isToken :: Char -> Bool
  isToken c = ('A' <= c && c <= 'Z')
           || ('a' <= c && c <= 'z')
           || ('0' <= c && c <= '9')
           || ('_' == c)
           || ('-' == c)


-- vim:set ft=haskell sw=2 ts=2 et: