repa-array-4.2.2.1: Data/Repa/Array/Auto/XSV.hs
module Data.Repa.Array.Auto.XSV
( getArrayFromXSV, hGetArrayFromXSV
, putArrayAsXSV, hPutArrayAsXSV)
where
import Data.Repa.Array.Auto.IO
import Data.Repa.Array.Auto.Base
import Data.Repa.Array.Generic.Convert
import Data.Char
import qualified Data.Repa.Array.Material.Auto as A
import qualified Data.Repa.Array.Material.Nested as A
import qualified Data.Repa.Array.Meta as A
import qualified Data.Repa.Array.Generic as A
import qualified System.IO as S
import qualified System.FileLock as S
---------------------------------------------------------------------------------------------------
-- | Read a XSV file as a nested array.
-- We get an array of rows:fields:characters.
getArrayFromXSV
:: Char -- ^ Field separator character, eg '|', ',' or '\t'.
-> FilePath -- ^ Source file handle.
-> IO (Array (Array (Array Char)))
getArrayFromXSV !cSep !path
= S.withFileLock path S.Shared
$ \_lock -> do
h <- S.openFile path S.ReadMode
arr <- hGetArrayFromXSV cSep h
S.hClose h
return arr
-- | Read an XSV file as a nested array.
-- We get an array of rows:fields:characters.
hGetArrayFromXSV
:: Char -- ^ Field separator character, eg '|', ',' or '\t'.
-> S.Handle -- ^ Source file handle.
-> IO (Array (Array (Array Char)))
hGetArrayFromXSV !cSep !hIn
= do
-- Find out how much data there is remaining in the file.
start <- S.hTell hIn
S.hSeek hIn S.SeekFromEnd 0
end <- S.hTell hIn
let !len = end - start
S.hSeek hIn S.AbsoluteSeek start
-- Read array as Word8s.
!arr8 <- hGetArray hIn (fromIntegral len)
-- Rows are separated by new lines,
-- fields are separated by the given separator character.
let !nl = fromIntegral $ ord '\n'
let !nc = fromIntegral $ ord cSep
-- Split XSV file into rows and fields.
-- Convert element data from Word8 to Char.
-- Chars take 4 bytes each, but are standard Haskell and pretty
-- print properly. We've done the dicing on the smaller Word8
-- version, and now map across the elements vector in the array
-- to do the conversion.
let !arrChar
= A.mapElems
(A.mapElems (A.computeS A.F . A.map (chr . fromIntegral)))
(A.diceSep nc nl arr8)
return $ convert arrChar
--------------------------------------------------------------------------------------------------
-- | Write a nested array as an XSV file.
--
-- The array contains rows:fields:characters.
putArrayAsXSV
:: Char -- ^ Separator character, eg '|', ',' or '\t'
-> FilePath -- ^ Source file handle.
-> Array (Array (Array Char)) -- ^ Array of row, field, character.
-> IO ()
putArrayAsXSV !cSep !path !arrChar
= S.withFileLock path S.Exclusive
$ \_lock -> do
h <- S.openFile path S.WriteMode
hPutArrayAsXSV cSep h arrChar
S.hClose h
-- | Write a nested array as an XSV file.
--
-- The array contains rows:fields:characters.
hPutArrayAsXSV
:: Char -- ^ Separator character, eg '|', ',' or '\t'
-> S.Handle -- ^ Source file handle.
-> Array (Array (Array Char)) -- ^ Array of row, field, character.
-> IO ()
hPutArrayAsXSV !cSep !hOut !arrChar
= do
-- Concat result back into Word8s
let !arrC = A.fromList A.U [cSep]
let !arrNL = A.fromList A.U ['\n']
let !arrOut
= A.mapS A.A (fromIntegral . ord)
$ A.concat A.U
$ A.mapS A.B (\ arrFields
-> A.concat A.U $ A.fromList A.B
[ A.intercalate A.U arrC arrFields, arrNL])
$ arrChar
hPutArray hOut arrOut
{-# INLINE hPutArrayAsXSV #-}