content: {-# LANGUAGE Trustworthy #-}
  {-# LANGUAGE NoImplicitPrelude #-}
  {-# OPTIONS_GHC -funbox-strict-fields #-}
  
  -----------------------------------------------------------------------------
  -- |
  -- Module      :  GHC.IO.BufferedIO
  -- Copyright   :  (c) The University of Glasgow 2008
  -- License     :  see libraries/base/LICENSE
  --
  -- Maintainer  :  cvs-ghc@haskell.org
  -- Stability   :  internal
  -- Portability :  non-portable (GHC Extensions)
  --
  -- Class of buffered IO devices
  --
  -----------------------------------------------------------------------------
  
  module GHC.IO.BufferedIO (
          BufferedIO(..),
          readBuf, readBufNonBlocking, writeBuf, writeBufNonBlocking
      ) where
  
  import GHC.Base
  import GHC.Ptr
  import Data.Word
  import GHC.Num
  import GHC.IO.Device as IODevice
  import GHC.IO.Device as RawIO
  import GHC.IO.Buffer
  
  -- | The purpose of 'BufferedIO' is to provide a common interface for I/O
  -- devices that can read and write data through a buffer.  Devices that
  -- implement 'BufferedIO' include ordinary files, memory-mapped files,
  -- and bytestrings.  The underlying device implementing a 'Handle' must
  -- provide 'BufferedIO'.
  --
  class BufferedIO dev where
    -- | allocate a new buffer.  The size of the buffer is at the
    -- discretion of the device; e.g. for a memory-mapped file the
    -- buffer will probably cover the entire file.
    newBuffer         :: dev -> BufferState -> IO (Buffer Word8)
  
    -- | reads bytes into the buffer, blocking if there are no bytes
    -- available.  Returns the number of bytes read (zero indicates
    -- end-of-file), and the new buffer.
    fillReadBuffer    :: dev -> Buffer Word8 -> IO (Int, Buffer Word8)
  
    -- | reads bytes into the buffer without blocking.  Returns the
    -- number of bytes read (Nothing indicates end-of-file), and the new
    -- buffer.
    fillReadBuffer0   :: dev -> Buffer Word8 -> IO (Maybe Int, Buffer Word8)
  
    -- | Prepares an empty write buffer.  This lets the device decide
    -- how to set up a write buffer: the buffer may need to point to a
    -- specific location in memory, for example.  This is typically used
    -- by the client when switching from reading to writing on a
    -- buffered read/write device.
    --
    -- There is no corresponding operation for read buffers, because before
    -- reading the client will always call 'fillReadBuffer'.
    emptyWriteBuffer  :: dev -> Buffer Word8 -> IO (Buffer Word8)
    emptyWriteBuffer _dev buf
      = return buf{ bufL=0, bufR=0, bufState = WriteBuffer }
  
    -- | Flush all the data from the supplied write buffer out to the device.
    -- The returned buffer should be empty, and ready for writing.
    flushWriteBuffer  :: dev -> Buffer Word8 -> IO (Buffer Word8)
  
    -- | Flush data from the supplied write buffer out to the device
    -- without blocking.  Returns the number of bytes written and the
    -- remaining buffer.
    flushWriteBuffer0 :: dev -> Buffer Word8 -> IO (Int, Buffer Word8)
  
  -- for an I/O device, these operations will perform reading/writing
  -- to/from the device.
  
  -- for a memory-mapped file, the buffer will be the whole file in
  -- memory.  fillReadBuffer sets the pointers to encompass the whole
  -- file, and flushWriteBuffer needs to do no I/O.  A memory-mapped
  -- file has to maintain its own file pointer.
  
  -- for a bytestring, again the buffer should match the bytestring in
  -- memory.
  
  -- ---------------------------------------------------------------------------
  -- Low-level read/write to/from buffers
  
  -- These operations make it easy to implement an instance of 'BufferedIO'
  -- for an object that supports 'RawIO'.
  
  readBuf :: RawIO dev => dev -> Buffer Word8 -> IO (Int, Buffer Word8)
  readBuf dev bbuf = do
    let bytes = bufferAvailable bbuf
    res <- withBuffer bbuf $ \ptr ->
               RawIO.read dev (ptr `plusPtr` bufR bbuf) bytes
    return (res, bbuf{ bufR = bufR bbuf + res })
           -- zero indicates end of file
  
  readBufNonBlocking :: RawIO dev => dev -> Buffer Word8
                       -> IO (Maybe Int,   -- Nothing ==> end of file
                                           -- Just n  ==> n bytes were read (n>=0)
                              Buffer Word8)
  readBufNonBlocking dev bbuf = do
    let bytes = bufferAvailable bbuf
    res <- withBuffer bbuf $ \ptr ->
             IODevice.readNonBlocking dev (ptr `plusPtr` bufR bbuf) bytes
    case res of
       Nothing -> return (Nothing, bbuf)
       Just n  -> return (Just n, bbuf{ bufR = bufR bbuf + n })
  
  writeBuf :: RawIO dev => dev -> Buffer Word8 -> IO (Buffer Word8)
  writeBuf dev bbuf = do
    let bytes = bufferElems bbuf
    withBuffer bbuf $ \ptr ->
        IODevice.write dev (ptr `plusPtr` bufL bbuf) bytes
    return bbuf{ bufL=0, bufR=0 }
  
  -- XXX ToDo
  writeBufNonBlocking :: RawIO dev => dev -> Buffer Word8 -> IO (Int, Buffer Word8)
  writeBufNonBlocking dev bbuf = do
    let bytes = bufferElems bbuf
    res <- withBuffer bbuf $ \ptr ->
              IODevice.writeNonBlocking dev (ptr `plusPtr` bufL bbuf) bytes
    return (res, bufferAdjustL (bufL bbuf + res) bbuf)
  