module Data.Columbia.Internal.Headers where
import Data.Bits
import Data.Word
import Data.Int
import Data.Array
import Data.Maybe
import Data.Generics.SYB.WithClass.Basics
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Trans
import Control.Monad.Identity
import Control.Monad
import Control.Parallel.Strategies
import Data.Columbia.Internal.SeekableStream
import Data.Columbia.Internal.SeekableWriter
import Data.Columbia.Internal.RWInstances
import Data.Columbia.Internal.IntegralTypes
import Data.Columbia.Types
import Data.Columbia.FRecord
evalTriple = evalTuple3 rseq rseq rseq
parity x1 fields = (popCount x1 + popCount fields) .&. 1
readHeader :: (Monad m) => ReaderT(SeekableStream m Word8) m (Word8, ConIndex, Int)
readHeader = do
x1 <- consumeToken
if testBit x1 7 then
let x = (case x1 of
160 -> 3
192 -> 2
128 -> 1
_ -> error$"readHeader: header code "++show x1++" is invalid") in
return$!using(x, 0, 0) evalTriple
else do
x2 <- consumeToken
let fields = fromIntegral x2 .&. 127
when(testBit x2 7 /= (parity x1 fields == 1))$
fail"readHeader: parity check error"
return$!using(0, fromIntegral x1, fields) evalTriple
enhancedFromConstr :: forall ctx a. (Data ctx a) => Proxy ctx -> DataType -> (Word8, ConIndex, Int) -> Int -> a
enhancedFromConstr proxy0 ty hdr@(_, ix, _) l = maybe
(runIdentity$fromConstrM proxy0(return$error"enhancedFromConstr: constr is too strict")$indexConstr ty ix)
runIdentity
(dataCast1 proxy0(return$listArray(1,l)$repeat$error"enhancedFromConstr: uninitialized array"))
nConstructorParameters :: forall ctx d. (Data ctx d) => Proxy ctx -> d -> Int
nConstructorParameters proxy d =
execState(gmapM
proxy
(\_ -> do { modify succ; return$error"nConstructorParameters: unused" })
d)
0
headerFromConstr :: forall ctx d. (Data ctx d) => Proxy ctx -> d -> Constr -> (Word8, ConIndex, Int)
headerFromConstr proxy d constr = hdr where
ty = dataTypeOf proxy d
specimen :: d = fromConstr proxy constr
hdr = if dataTypeName ty == "Data.Array.Unboxed.UArray" then
(3, 0, 0)
else if dataTypeName ty == "Data.Array.Array" then
(2, 0, 0)
else if not(isAlgType ty) then
(1, 0, 0)
else
(0, constrIndex constr, nConstructorParameters proxy specimen) where
writeHeader :: (Monad m, Data cxt d) => Proxy cxt -> d -> ReaderT(SeekableWriter m Word8) m ()
writeHeader proxy d = do
let (n, conIndex, nFields) = headerFromConstr proxy d(toConstr proxy d)
case n of
3 -> putToken 160
2 -> putToken 192
1 -> putToken 128
0 -> do
when(conIndex>127)$fail"writeCompoundData: constructor index not in range 1-127"
putToken(fromIntegral conIndex)
when(nFields>127)$fail"writeCompoundData: number of fields not in range 0-127"
putToken(fromIntegral$nFields .|. shiftL(parity conIndex nFields) 7)
nFieldsBytes hdr@(_, _, nFields) = if isHeaderPrimtype hdr then
return(0, 5)
else if isHeaderUArraytype hdr then do
len <- readIntegral
return(0, 5+len)
else if isHeaderArraytype hdr then do
len <- readIntegral
return(len, 5+4*len)
else
return(nFields, 2+4*nFields)
isHeaderUArraytype :: (Word8, ConIndex, Int) -> Bool
isHeaderUArraytype (n, _, _) = n == 3
isHeaderArraytype :: (Word8, ConIndex, Int) -> Bool
isHeaderArraytype (n, _, _) = n == 2
isHeaderPrimtype :: (Word8, ConIndex, Int) -> Bool
isHeaderPrimtype (n, _, _) = n == 1
isHeaderAlgtype :: (Word8, ConIndex, Int) -> Bool
isHeaderAlgtype (n, _, _) = n == 0
readHeader' :: (Monad m) => ReaderT(SeekableStream m Word8) m Header
readHeader' = do
n <- getPosition
(x,x2,x3) <- readHeader
seek n
return$!Header x x2 x3