-----------------------------------------------------------------------------
--
-- Fast write-buffered Handles
--
-- (c) The University of Glasgow 2005
--
-- 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 FastMutInt
import Panic		( panic )

import Monad		( when )
import Char		( ord )
import Foreign
import IO

import GHC.IOBase	( IO(..) )
import System.IO	( hPutBuf )
import GHC.Ptr		( Ptr(..) )

import GLAEXTS		( Int(..), Int#, Addr# )

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

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 = 8192 :: Int

#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 b@(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 -> Addr# -> Int# -> IO ()
bPutLitString b@(BufHandle buf r hdl) a# len# = do
  let len = I# 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 (Ptr a#) len
		    else bPutLitString b a# len#
	else do
		copyBytes (buf `plusPtr` i) (Ptr a#) len
		writeFastMutInt r (i+len)

bFlush :: BufHandle -> IO ()
bFlush b@(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
