Skip to content

Commit

Permalink
IOHasBufFS` interface for I/O using user-supplied buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
jorisdral committed Mar 7, 2024
1 parent 13e85bd commit f2bbbee
Show file tree
Hide file tree
Showing 15 changed files with 618 additions and 69 deletions.
11 changes: 11 additions & 0 deletions fs-api/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Revision history for fs-api

## next version -- ????-??-??

### Non-breaking

* Add new `HasBufFS` interface for performing I/O using pointer buffers. Note
that it is likely that this interfaced is unified with the `HasFS` interface
in the future.
* Add `hGetBufExactlyAt` and `hPutBufExactlyAt` functions that ensure all
requested bytes are read or written respectively.
* Provide an instantiation of the `HasBufFS` interface for `IO`.

## 0.2.0.1 -- 2023-10-30

### Patch
Expand Down
22 changes: 21 additions & 1 deletion fs-api/fs-api.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ license-files:

copyright: 2019-2023 Input Output Global Inc (IOG)
author: IOG Engineering Team
maintainer: [email protected], Joris Dral
maintainer: [email protected], Joris Dral ([email protected])
category: System
build-type: Simple
extra-doc-files: CHANGELOG.md
Expand Down Expand Up @@ -66,3 +66,23 @@ library
-Wall -Wcompat -Wincomplete-uni-patterns
-Wincomplete-record-updates -Wpartial-fields -Widentities
-Wredundant-constraints -Wmissing-export-lists -Wunused-packages

test-suite fs-api-test
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
other-modules: Test.System.FS.IO
default-language: Haskell2010
build-depends:
, base
, bytestring
, fs-api
, tasty
, tasty-quickcheck
, temporary

ghc-options:
-Wall -Wcompat -Wincomplete-uni-patterns
-Wincomplete-record-updates -Wpartial-fields -Widentities
-Wredundant-constraints -Wmissing-export-lists -Wunused-packages
-fno-ignore-asserts
37 changes: 34 additions & 3 deletions fs-api/src-unix/System/FS/IO/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,36 @@ module System.FS.IO.Internal (
, getSize
, open
, pread
, preadBuf
, pwriteBuf
, read
, readBuf
, sameError
, seek
, tell
, truncate
, write
, writeBuf
) where

import Prelude hiding (read, truncate)

import Control.Monad (void)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Internal as Internal
import Data.Coerce (coerce)
import Data.Int (Int64)
import Data.Word (Word32, Word64, Word8)
import Foreign (Ptr)
import qualified GHC.IO.Device as Device
import GHC.IO.FD (FD (..))
import System.FS.API.Types (AllowExisting (..), FsError,
OpenMode (..), SeekMode (..), sameFsError)
import System.FS.IO.Internal.Handle
import qualified System.Posix as Posix
import System.Posix (Fd)
import System.Posix.IO.ByteString.Ext (fdPreadBuf)
import System.Posix (ByteCount, Fd (..), FileOffset)
import qualified System.Posix.IO.ByteString.Ext as Posix (fdPreadBuf,
fdPwriteBuf)

type FHandle = HandleOS Fd

Expand Down Expand Up @@ -123,16 +132,38 @@ seek :: FHandle -> SeekMode -> Int64 -> IO ()
seek h seekMode offset = withOpenHandle "seek" h $ \fd ->
void $ Posix.fdSeek fd seekMode (fromIntegral offset)

tell :: FHandle -> IO Word64
tell h = withOpenHandle "tell" h $ \fd ->
fromIntegral <$> Device.tell (FD (coerce fd) 0)

-- | Reads a given number of bytes from the input 'FHandle'.
read :: FHandle -> Word64 -> IO ByteString
read h bytes = withOpenHandle "read" h $ \fd ->
Internal.createUptoN (fromIntegral bytes) $ \ptr ->
fromIntegral <$> Posix.fdReadBuf fd ptr (fromIntegral bytes)

readBuf :: FHandle -> Ptr Word8 -> ByteCount -> IO ByteCount
readBuf f buf c = withOpenHandle "readBuf" f $ \fd -> Posix.fdReadBuf fd buf c

writeBuf :: FHandle -> Ptr Word8 -> ByteCount -> IO ByteCount
writeBuf f buf c = withOpenHandle "writeBuf" f $ \fd -> Posix.fdWriteBuf fd buf c

pread :: FHandle -> Word64 -> Word64 -> IO ByteString
pread h bytes offset = withOpenHandle "pread" h $ \fd ->
Internal.createUptoN (fromIntegral bytes) $ \ptr ->
fromIntegral <$> fdPreadBuf fd ptr (fromIntegral bytes) (fromIntegral offset)
fromIntegral <$> Posix.fdPreadBuf fd ptr (fromIntegral bytes) (fromIntegral offset)

-- | @'preadBuf' fh buf c off@ reads @c@ bytes into the buffer @buf@ from the file
-- handle @fh@ at the file offset @off@. This does not move the position of the
-- file handle.
preadBuf :: FHandle -> Ptr Word8 -> ByteCount -> FileOffset -> IO ByteCount
preadBuf h buf c off = withOpenHandle "preadBuf" h $ \fd -> Posix.fdPreadBuf fd buf c off

-- | @'pwriteBuf' fh buf c off@ writes @c@ bytes from the data in the buffer
-- @buf@ to the file handle @fh@ at the file offset @off@. This does not move
-- the position of the file handle.
pwriteBuf :: FHandle -> Ptr Word8 -> ByteCount -> FileOffset -> IO ByteCount
pwriteBuf h buf c off = withOpenHandle "pwriteBuf" h $ \fd -> Posix.fdPwriteBuf fd buf c off

-- | Truncates the file managed by the input 'FHandle' to the input size.
truncate :: FHandle -> Word64 -> IO ()
Expand Down
33 changes: 33 additions & 0 deletions fs-api/src-win32/System/FS/IO/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ module System.FS.IO.Internal (
, getSize
, open
, pread
, preadBuf
, pwriteBuf
, read
, readBuf
, sameError
, seek
, tell
, truncate
, write
, writeBuf
) where

import Prelude hiding (read, truncate)
Expand All @@ -26,6 +31,7 @@ import Foreign (Int64, Ptr)
import System.FS.API.Types (AllowExisting (..), FsError (..),
FsErrorType (..), OpenMode (..), SeekMode (..))
import System.FS.IO.Internal.Handle
import System.Posix.Types
import System.Win32

type FHandle = HandleOS HANDLE
Expand Down Expand Up @@ -65,6 +71,9 @@ seek :: FHandle -> SeekMode -> Int64 -> IO ()
seek fh seekMode size = void <$> withOpenHandle "seek" fh $ \h ->
setFilePointerEx h size (fromSeekMode seekMode)

tell :: FHandle -> IO Word64
tell h = withOpenHandle "tell" h $ fmap fromIntegral . getCurrentFileOffset

fromSeekMode :: SeekMode -> FilePtrDirection
fromSeekMode AbsoluteSeek = fILE_BEGIN
fromSeekMode RelativeSeek = fILE_CURRENT
Expand All @@ -78,6 +87,14 @@ read fh bytes = withOpenHandle "read" fh $ \h ->
getCurrentFileOffset :: HANDLE -> IO Int64
getCurrentFileOffset h = setFilePointerEx h 0 fILE_CURRENT

readBuf :: FHandle -> Ptr Word8 -> ByteCount -> IO ByteCount
readBuf fh buf c = withOpenHandle "readBuf" fh $ \h ->
fromIntegral <$> win32_ReadFile h buf (fromIntegral c) Nothing

writeBuf :: FHandle -> Ptr Word8 -> ByteCount -> IO ByteCount
writeBuf fh buf c = withOpenHandle "writeBuf" fh $ \h ->
fromIntegral <$> win32_WriteFile h buf (fromIntegral c) Nothing

pread :: FHandle -> Word64 -> Word64 -> IO ByteString
pread fh bytes pos = withOpenHandle "pread" fh $ \h ->
Internal.createUptoN (fromIntegral bytes) $ \ptr -> do
Expand All @@ -87,6 +104,22 @@ pread fh bytes pos = withOpenHandle "pread" fh $ \h ->
_ <- setFilePointerEx h initialOffset fILE_BEGIN
return n

preadBuf :: FHandle -> Ptr Word8 -> ByteCount -> FileOffset -> IO ByteCount
preadBuf fh buf c off = withOpenHandle "preadBuf" fh $ \h -> do
initialOffset <- getCurrentFileOffset h
_ <- setFilePointerEx h (fromIntegral off) fILE_BEGIN
n <- fromIntegral <$> win32_ReadFile h buf (fromIntegral c) Nothing
_ <- setFilePointerEx h initialOffset fILE_BEGIN
return n

pwriteBuf :: FHandle -> Ptr Word8 -> ByteCount -> FileOffset -> IO ByteCount
pwriteBuf fh buf c off = withOpenHandle "pwriteBuf" fh $ \h -> do
initialOffset <- getCurrentFileOffset h
_ <- setFilePointerEx h (fromIntegral off) fILE_BEGIN
n <- fromIntegral <$> win32_WriteFile h buf (fromIntegral c) Nothing
_ <- setFilePointerEx h initialOffset fILE_BEGIN
return n

-- We only allow truncate in AppendMode, but Windows do not support it, so we manually seek to the end.
-- It is important that the logical end of the handle stays alligned to the physical end of the file.
truncate :: FHandle -> Word64 -> IO ()
Expand Down
Loading

0 comments on commit f2bbbee

Please sign in to comment.