Created
May 13, 2011 01:28
-
-
Save zaphar/969805 to your computer and use it in GitHub Desktop.
A toy implementation of a readable writable window onto a filesyste file in haskell.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- Copyright (c) 2011, Jeremy Wall | |
| -- | A moving readable/writable window on an IO Handle. | |
| module FileWindow ( | |
| FileWindow | |
| , WindowReadResult(..) | |
| , newFileWindow | |
| , emptyWindow | |
| -- Accessors | |
| , getHandle | |
| , getPath | |
| , getStart | |
| , getEnd | |
| , getSize | |
| , getMax | |
| -- Mutations | |
| , setMaxSize | |
| -- IO | |
| , openWindow | |
| , closeWindow | |
| , withWindow | |
| , readWindow | |
| , readWindowLines | |
| , seekWindow | |
| , writeWindow | |
| ) where | |
| import System.FilePath | |
| import System.IO | |
| import Data.Maybe (Maybe) | |
| -- | The Datatype of a Window onto a IO Handle. | |
| data FileWindow = FileWindow { | |
| path :: FilePath | |
| , handle ::Maybe Handle | |
| , wStart :: Integer | |
| , wSize :: Integer | |
| , wMax :: Integer | |
| } | |
| deriving (Show) | |
| -- | The Datatype of the result of reading from a FileWindow. | |
| data WindowReadResult = WindowReadResult FileWindow String | |
| deriving (Show) | |
| -- | The empty FileWindow. | |
| emptyWindow = FileWindow { wStart = 0, wSize = 0, wMax = 0, path = "", handle = Nothing } | |
| -- | Constructor for a FileWindow. | |
| -- Constructs a FileWindow for the FilePath and max size. | |
| newFileWindow :: FilePath -> Integer -> FileWindow | |
| newFileWindow p max = emptyWindow { path = p, wMax = max } | |
| -- | Accessor for the FileWindow handle. | |
| getHandle :: FileWindow -> Maybe Handle | |
| getHandle = handle | |
| -- | Accessor for the FileWindow path. | |
| getPath :: FileWindow -> FilePath | |
| getPath = path | |
| -- | Accessor for the start index of the FileWindow window. | |
| getStart ::FileWindow -> Integer | |
| getStart = wStart | |
| -- | Accessor for the actual size of the FileWindow window. | |
| getSize :: FileWindow -> Integer | |
| getSize = wSize | |
| -- | Accessor for the max size of the FileWindow window. | |
| getMax :: FileWindow -> Integer | |
| getMax = wMax | |
| -- | Accessor for the end index of the FileWindow window. | |
| getEnd :: FileWindow -> Integer | |
| getEnd fw = let s = getStart fw | |
| sz = getSize fw | |
| in s + sz | |
| -- | Sets the max size on a FileWindow to the provided size. | |
| setMaxSize :: FileWindow -> Integer -> FileWindow | |
| setMaxSize fw sz = fw { wMax = sz } | |
| -- | Opens a FileWindow with the IOMode. | |
| -- Seeks immediately to the start index for the window. | |
| openWindow :: FileWindow -> IOMode -> IO FileWindow | |
| openWindow fw mode = do | |
| let path = getPath fw | |
| s = getStart fw | |
| sz = getMax fw | |
| h <- openFile path mode | |
| seekWindow (fw { handle = Just h, wSize = sz }) AbsoluteSeek s | |
| -- | Closes a FileWindow. | |
| closeWindow :: FileWindow -> IO () | |
| closeWindow fw = do | |
| case getHandle fw of | |
| Just h -> hClose h | |
| Nothing -> return () | |
| -- | Safely run an action with a FileWindow. | |
| -- Opens the FileWindow for the action and then | |
| -- closes it after the action has run. | |
| withWindow :: FileWindow -> IOMode -> (FileWindow -> IO FileWindow) -> IO FileWindow | |
| withWindow fw mode f = do | |
| opened <- openWindow fw mode | |
| result <- f opened | |
| closeWindow result | |
| return result | |
| -- TODO(jwall): handle the case of an unopened window? | |
| -- | Read the current window from a FileWindow. | |
| -- Reads getMax Bytes from the FileWindow's IO Handle | |
| -- And returns the result. | |
| readWindow :: FileWindow -> IO WindowReadResult | |
| readWindow fw = do | |
| let Just h = getHandle fw | |
| sz = getMax fw | |
| str <- readTo "" h $ getEnd fw | |
| return $ WindowReadResult (fw { wSize = sz }) str | |
| -- | Reads from the FileWindow constraining to full lines. | |
| -- Reads as close to getMax for the FileWindow as it can while | |
| -- still only returning full lines. Sets the size of the FileWindow | |
| -- to the actual size of the window. | |
| readWindowLines :: FileWindow -> IO WindowReadResult | |
| readWindowLines fw = do | |
| WindowReadResult _ str <- readWindow fw | |
| let (s, n) = chopUntilNewLine (reverse str) 0 | |
| return $ WindowReadResult (fw { wSize = n }) s | |
| where chopUntilNewLine s n = | |
| if (head s) == '\n' | |
| then (reverse s, n) | |
| else chopUntilNewLine (drop 1 s) $ n + 1 | |
| -- | Seeks in the FileWindow's IO Handle to the index with the SeekModeMode. | |
| -- Sets the start of the window in the return to the current | |
| -- position of the IO Handle | |
| seekWindow :: FileWindow -> SeekMode -> Integer -> IO FileWindow | |
| seekWindow fw mode i = do | |
| let Just h = getHandle fw | |
| hSeek h mode i | |
| curr <- hTell h | |
| return fw { wStart = curr } | |
| -- | Writes the string to the FileWindow. | |
| -- Always overwrites the contents of the current window range | |
| -- in the IO Handle with the string. | |
| writeWindow :: FileWindow -> String -> IO () | |
| writeWindow fw str = do | |
| let Just h = getHandle fw | |
| let s = getStart fw | |
| let e = getEnd fw | |
| size <- hFileSize h | |
| fst <- readRange h 0 (s - 1) | |
| lst <- readRange h (e + 1) size | |
| fw' <- seekWindow fw AbsoluteSeek 0 | |
| hSetFileSize h 0 | |
| let Just h' = getHandle fw' | |
| hPutStr h' fst | |
| hPutStr h' str | |
| hPutStr h' lst | |
| readRange h s e = do | |
| hSeek h AbsoluteSeek s | |
| readTo "" h e | |
| readTo acc h e = do | |
| eof <- hIsEOF h | |
| curr <- hTell h | |
| if eof || (curr >= e) | |
| then return $ acc ++ "" | |
| else do c <- hGetChar h | |
| readTo (acc ++ [c]) h e |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment