content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE MagicHash #-}
  {-# LANGUAGE BangPatterns #-}
  
  -- Copyright     : Erik de Castro Lopo <erikd@mega-nerd.com>
  -- License       : BSD3
  
  module Network.Wai.Handler.Warp.ReadInt (
      readInt
    , readInt64
    ) where
  
  -- This function lives in its own file because the MagicHash pragma interacts
  -- poorly with the CPP pragma.
  
  import Data.ByteString (ByteString)
  import qualified Data.ByteString as S
  import Data.Int (Int64)
  import GHC.Prim
  import GHC.Types
  import GHC.Word
  
  {-# INLINE readInt #-}
  readInt :: Integral a => ByteString -> a
  readInt bs = fromIntegral $ readInt64 bs
  
  -- This function is used to parse the Content-Length field of HTTP headers and
  -- is a performance hot spot. It should only be replaced with something
  -- significantly and provably faster.
  --
  -- It needs to be able work correctly on 32 bit CPUs for file sizes > 2G so we
  -- use Int64 here and then make a generic 'readInt' that allows conversion to
  -- Int and Integer.
  
  {-# NOINLINE readInt64 #-}
  readInt64 :: ByteString -> Int64
  readInt64 bs = S.foldl' (\ !i !c -> i * 10 + fromIntegral (mhDigitToInt c)) 0
               $ S.takeWhile isDigit bs
  
  data Table = Table !Addr#
  
  {-# NOINLINE mhDigitToInt #-}
  mhDigitToInt :: Word8 -> Int
  mhDigitToInt (W8# i) = I# (word2Int# (indexWord8OffAddr# addr (word2Int# i)))
    where
      !(Table addr) = table
      table :: Table
      table = Table
          "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
          \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
  
  isDigit :: Word8 -> Bool
  isDigit w = w >= 48 && w <= 57
  