{-# LANGUAGE DoAndIfThenElse, BangPatterns #-} {-# LANGUAGE NoImplicitPrelude, RecordWildCards #-} module Network.Wai.Logger.IO ( LogStr(..) , hPutLogStr, hPutBuilder, initHandle ) where import Blaze.ByteString.Builder import qualified Data.ByteString as BS import Data.ByteString.Internal (ByteString(..), c2w) import Data.List import Data.Maybe import Foreign import GHC.Base import GHC.IO.Buffer import qualified GHC.IO.BufferedIO as Buffered import GHC.IO.Handle.Internals import GHC.IO.Handle.Text import GHC.IO.Handle.Types import GHC.IORef import GHC.Num import GHC.Real import System.IO -- | A date type to contain 'String' and 'ByteString'. data LogStr = LS !String | LB !ByteString -- | The 'hPut' function directory to copy a lift of 'LogStr' to the buffer. hPutLogStr :: Handle -> [LogStr] -> IO () hPutLogStr handle bss = wantWritableHandle "hPutLogStr" handle $ \h_ -> bufsWrite h_ bss bufsWrite :: Handle__-> [LogStr] -> IO () bufsWrite h_@Handle__{..} bss = do old_buf@Buffer{ bufRaw = old_raw , bufR = w , bufSize = size } <- readIORef haByteBuffer if size - w > len then do withRawBuffer old_raw $ \ptr -> go (ptr `plusPtr` w) bss writeIORef haByteBuffer old_buf{ bufR = w + len } else do old_buf' <- Buffered.flushWriteBuffer haDevice old_buf writeIORef haByteBuffer old_buf' bufsWrite h_ bss where len = foldl' (\x y -> x + getLength y) 0 bss getLength (LB s) = BS.length s getLength (LS s) = length s go :: Ptr Word8 -> [LogStr] -> IO () go _ [] = return () go dst (LB b:bs) = do dst' <- copy dst b go dst' bs go dst (LS s:ss) = do dst' <- copy' dst s go dst' ss copy :: Ptr Word8 -> ByteString -> IO (Ptr Word8) copy dst (PS ptr off len) = withForeignPtr ptr $ \s -> do let src = s `plusPtr` off memcpy dst src (fromIntegral len) return (dst `plusPtr` len) copy' :: Ptr Word8 -> String -> IO (Ptr Word8) copy' dst [] = return dst copy' dst (x:xs) = do poke dst (c2w x) copy' (dst `plusPtr` 1) xs -- | Setting a proper buffering to 'Handle'. initHandle :: Handle -> IO () initHandle hdl = hSetBuffering hdl (BlockBuffering (Just 4096)) {-| The 'hPut' function directory to copy 'Builder' to the buffer. The current implementation is inefficient at this moment. 'initHandle' must be called once beforehand if this function is used. -} hPutBuilder :: Handle -> Builder -> IO () hPutBuilder hdl = BS.hPut hdl . toByteString