content: {-# language BangPatterns #-}
  {-# language OverloadedStrings #-}
  
  import Criterion
  import Criterion.Main
  import qualified Data.Binary.Get as Get
  
  import Network.WebSockets.Hybi13.Mask
  
  import Data.Bits (shiftR, xor)
  import qualified Data.ByteString as B
  import qualified Data.ByteString.Lazy as BL
  
  setupEnv = do
      let kilo = BL.replicate 1024 37
          mega = BL.replicate (1024 * 1024) 37
          megaU = BL.fromChunks [B.drop 1 (B.replicate (1024 * 1024) 37)]
          megaS = BL.fromChunks [B.replicate (1024 * 1024) 37]
      return (kilo, mega, megaU, megaS)
  
  maskPayload' :: Maybe B.ByteString -> BL.ByteString -> BL.ByteString
  maskPayload' Nothing     = id
  maskPayload' (Just mask) = snd . BL.mapAccumL f (cycle $ B.unpack mask)
    where
      f []     !c = ([], c)
      f (m:ms) !c = (ms, m `xor` c)
  
  main = defaultMain [
      env setupEnv $ \ ~(kilo, mega, megaU, megaS) -> bgroup "main"
          [ bgroup "kilobyte payload"
              [ bgroup "zero_mask"
                  [ bench "current" $ nf (maskPayload (mkMask $ "\x00\x00\x00\x00")) kilo
                  , bench "old" $ nf (maskPayload' (Just "\x00\x00\x00\x00")) kilo
                  ]
              ,  bgroup "full_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\xFF\xFF\xFF\xFF")) kilo
                  , bench "current-unaligned" $ nf (maskPayload (mkMask "\xFF\xFF\xFF\xFF")) (BL.drop 1 kilo)
                  , bench "old" $ nf (maskPayload' (Just "\xFF\xFF\xFF\xFF")) kilo
                  ]
              ,  bgroup "one_byte_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\xCC\xCC\xCC\xCC")) kilo
                  , bench "old" $ nf (maskPayload' (Just "\xCC\xCC\xCC\xCC")) kilo
                  ]
              ,  bgroup "other_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\xB0\xA2\xB0\xA2")) kilo
                  , bench "old" $ nf (maskPayload' (Just "\xB0\xA2\xB0\xA2")) kilo
                  ]
              ]
          , bgroup "megabyte payload"
              [ bgroup "zero_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\x00\x00\x00\x00")) mega
                  , bench "old" $ nf (maskPayload' (Just "\x00\x00\x00\x00")) mega
                  ]
              ,  bgroup "full_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\xFF\xFF\xFF\xFF")) mega
                  , bench "current-unaligned" $ nf (maskPayload (mkMask "\xFF\xFF\xFF\xFF")) megaU
                  , bench "current-aligned" $ nf (maskPayload (mkMask "\xFF\xFF\xFF\xFF")) megaS
                  , bench "old" $ nf (maskPayload' (Just "\xFF\xFF\xFF\xFF")) mega
                  ]
              ,  bgroup "one_byte_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\xCC\xCC\xCC\xCC")) mega
                  , bench "old" $ nf (maskPayload' (Just "\xCC\xCC\xCC\xCC")) mega
                  ]
              ,  bgroup "other_mask"
                  [ bench "current" $ nf (maskPayload (mkMask "\xB0\xA2\xB0\xA2")) mega
                  , bench "old" $ nf (maskPayload' (Just "\xB0\xA2\xB0\xA2")) mega
                  ]
              ]
          ]
      ]
    where
      mkMask b = Just $ Get.runGet parseMask b
  