    -----------------------------------------------------------------------------
    --
    -- Fast write-buffered Handles
    --
    -- (c) The University of Glasgow 2005-2006
    --
    -- This is a simple abstraction over Handles that offers very fast write
    -- buffering, but without the thread safety that Handles provide.  It's used
    -- to save time in Pretty.printDoc.
    --
    -----------------------------------------------------------------------------

    module BufWrite (
    	BufHandle(..),
    	newBufHandle,
    	bPutChar,
    	bPutStr,
    	bPutFS,
    	bPutLitString,
    	bFlush,
      ) where

    #include "HsVersions.h"

    import FastString
    import FastTypes
    import FastMutInt

    import Control.Monad	( when )
    import Data.Char	( ord )
    import Foreign
    import System.IO

    -- -----------------------------------------------------------------------------

    data BufHandle = BufHandle {-#UNPACK#-}!(Ptr Word8)
    			   {-#UNPACK#-}!FastMutInt
    			   Handle

    newBufHandle :: Handle -> IO BufHandle
    newBufHandle hdl = do
      ptr <- mallocBytes buf_size
      r <- newFastMutInt
      writeFastMutInt r 0
      return (BufHandle ptr r hdl)

    buf_size :: Int
    buf_size = 8192

    #define STRICT2(f) f a b | a `seq` b `seq` False = undefined
    #define STRICT3(f) f a b c | a `seq` b `seq` c `seq` False = undefined

    bPutChar :: BufHandle -> Char -> IO ()
    STRICT2(bPutChar)
    bPutChar b@(BufHandle buf r hdl) c = do
      i <- readFastMutInt r
      if (i >= buf_size)
    	then do hPutBuf hdl buf buf_size
    		writeFastMutInt r 0
    		bPutChar b c
    	else do pokeElemOff buf i (fromIntegral (ord c) :: Word8)
    		writeFastMutInt r (i+1)

    bPutStr :: BufHandle -> String -> IO ()
    STRICT2(bPutStr)
    bPutStr (BufHandle buf r hdl) str = do
      i <- readFastMutInt r
      loop str i
      where loop _ i | i `seq` False = undefined
    	loop "" i = do writeFastMutInt r i; return ()
    	loop (c:cs) i
    	   | i >= buf_size = do
    		hPutBuf hdl buf buf_size
    		loop (c:cs) 0
    	   | otherwise = do
    		pokeElemOff buf i (fromIntegral (ord c))
    		loop cs (i+1)

    bPutFS :: BufHandle -> FastString -> IO ()
    bPutFS b@(BufHandle buf r hdl) fs@(FastString _ len _ fp _) =
     withForeignPtr fp $ \ptr -> do
      i <- readFastMutInt r
      if (i + len) >= buf_size
    	then do hPutBuf hdl buf i
    		writeFastMutInt r 0
    		if (len >= buf_size)
    		    then hPutBuf hdl ptr len
    		    else bPutFS b fs
    	else do
    		copyBytes (buf `plusPtr` i) ptr len
    		writeFastMutInt r (i+len)

    bPutLitString :: BufHandle -> LitString -> FastInt -> IO ()
    bPutLitString b@(BufHandle buf r hdl) a len_ = a `seq` do
      let len = iBox len_
      i <- readFastMutInt r
      if (i+len) >= buf_size
    	then do hPutBuf hdl buf i
    		writeFastMutInt r 0
    		if (len >= buf_size)
    		    then hPutBuf hdl a len
    		    else bPutLitString b a len_
    	else do
    		copyBytes (buf `plusPtr` i) a len
    		writeFastMutInt r (i+len)

    bFlush :: BufHandle -> IO ()
    bFlush (BufHandle buf r hdl) = do
      i <- readFastMutInt r
      when (i > 0) $ hPutBuf hdl buf i
      free buf
      return ()

    #if 0
    myPutBuf s hdl buf i =
      modifyIOError (\e -> ioeSetErrorString e (ioeGetErrorString e ++ ':':s ++ " (" ++ show buf ++ "," ++ show i ++ ")")) $

      hPutBuf hdl buf i
    #endif