/src/Data/NetCDF/Serialize.hs

http://github.com/jystic/netcdf-hs · Haskell · 172 lines · 119 code · 40 blank · 13 comment · 6 complexity · 6c3bdd16685af33f2b4dcce89ca36212 MD5 · raw file

  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Data.NetCDF.Serialize (getHeader) where
  3. import Control.Applicative
  4. import Data.Bits ((.&.), complement)
  5. import Data.ByteString (ByteString)
  6. import qualified Data.ByteString.Char8 as B
  7. import Data.Int (Int16, Int32)
  8. import Data.Serialize
  9. import Data.Word (Word8, Word32)
  10. import Prelude hiding (take, drop)
  11. import Data.NetCDF.Types
  12. ------------------------------------------------------------------------
  13. -- | Parse the header of a NetCDF file.
  14. getHeader :: Get Header
  15. getHeader = do
  16. string "CDF"
  17. format <- pure FormatClassic <* word8 1
  18. <|> pure Format64Bit <* word8 2
  19. <?> "file format"
  20. numrecs <- pure Streaming <* word32be 0xFFFFFFFF
  21. <|> NumRecs <$> getWord32be
  22. dims <- getDimensionList
  23. gatts <- getAttributeList
  24. vars <- getVariableList dims (getOffsetFor format)
  25. return (Header format numrecs dims gatts vars)
  26. -- | Create a parser for file offsets depending on the file format.
  27. getOffsetFor :: Format -> Get FileOffset
  28. getOffsetFor FormatClassic = fromIntegral <$> getWord32be
  29. getOffsetFor Format64Bit = fromIntegral <$> getWord64be
  30. getDimensionList :: Get [Dim]
  31. getDimensionList = headerList 0xA getDimension <?> "dimensions"
  32. getAttributeList :: Get [Attr]
  33. getAttributeList = headerList 0xC getAttribute <?> "attributes"
  34. getVariableList :: [Dim] -> Get FileOffset -> Get [Var]
  35. getVariableList dims getOffset = headerList 0xB (getVariable dims getOffset) <?> "variables"
  36. headerList :: Word32 -> Get a -> Get [a]
  37. headerList tag gelem = absent <|> word32be tag *> getList gelem
  38. where
  39. absent = zero *> zero *> pure []
  40. zero = word32be 0
  41. getName :: Get Name
  42. getName = getAlignedBytes <?> "name"
  43. getDimension :: Get Dim
  44. getDimension = Dim <$> getName <*> getDimensionLength
  45. getDimensionLength :: Get DimLength
  46. getDimensionLength = pure Unlimited <* word32be 0
  47. <|> Fixed <$> getWord32be
  48. getAttribute :: Get Attr
  49. getAttribute = Attr <$> getName <*> getAttributeValue <?> "attribute"
  50. getAttributeValue :: Get AttrValue
  51. getAttributeValue = do
  52. typ <- getVariableType
  53. case typ of
  54. VarByte -> AttrByte <$> getAlignedBytes
  55. VarChar -> AttrChar <$> getAlignedBytes
  56. VarShort -> AttrShort <$> getAlignedShorts
  57. VarInt -> AttrInt <$> getList getInt32be
  58. VarFloat -> AttrFloat <$> getList getFloat32be
  59. VarDouble -> AttrDouble <$> getList getFloat64be
  60. getVariable :: [Dim] -> Get FileOffset -> Get Var
  61. getVariable dims getOffset =
  62. Var <$> getName
  63. <*> getList ((dims !!) <$> nonNeg)
  64. <*> getAttributeList
  65. <*> getVariableType
  66. <*> getWord32be
  67. <*> getOffset
  68. <?> "variable"
  69. getVariableType :: Get VarType
  70. getVariableType = do
  71. typ <- getWord32be
  72. case typ of
  73. 1 -> pure VarByte
  74. 2 -> pure VarChar
  75. 3 -> pure VarShort
  76. 4 -> pure VarInt
  77. 5 -> pure VarFloat
  78. 6 -> pure VarDouble
  79. _ -> fail ("unsupported nc_type = " ++ show typ)
  80. <?> "variableType"
  81. ------------------------------------------------------------------------
  82. -- Utils
  83. -- | Label the parser, in case failure occurs.
  84. (<?>) :: Get a -> String -> Get a
  85. g <?> lbl = label lbl g
  86. infix 0 <?>
  87. -- | Apply the given action repeatedly, returning every result.
  88. count :: Monad m => Int -> m a -> m [a]
  89. count n p = sequence (replicate n p)
  90. -- | Expect a specific string.
  91. string :: ByteString -> Get ()
  92. string xs = expectS B.unpack (getByteString $ B.length xs) xs
  93. -- | Expect a specific 8-bit word.
  94. word8 :: Word8 -> Get ()
  95. word8 = expect getWord8
  96. -- | Expect a specific 32-bit big endian word.
  97. word32be :: Word32 -> Get ()
  98. word32be = expect getWord32be
  99. expect :: (Show a, Eq a) => Get a -> a -> Get ()
  100. expect = expectS show
  101. expectS :: Eq a => (a -> String) -> Get a -> a -> Get ()
  102. expectS show' get' x = get' >>= \y ->
  103. if x == y
  104. then return ()
  105. else fail ("expected " ++ show' x ++ " (was " ++ show' y ++ ")")
  106. getAlignedBytes :: Get ByteString
  107. getAlignedBytes = do
  108. n <- nonNeg
  109. getBytes n <* align n
  110. <?> "aligned bytes"
  111. getAlignedShorts :: Get [Int16]
  112. getAlignedShorts = do
  113. n <- nonNeg
  114. count n getInt16be <* align (n * 2)
  115. <?> "aligned shorts"
  116. -- | Re-aligns the current offset to a 4-byte boundary given
  117. -- the number of bytes read since the last alignment.
  118. align :: Int -> Get ()
  119. align bytesRead = getBytes padding *> pure () <?> "align"
  120. where
  121. boundary = (bytesRead + 3) .&. complement 0x3
  122. padding = boundary - bytesRead
  123. getList :: Get a -> Get [a]
  124. getList g = do
  125. n <- nonNeg
  126. count n g
  127. nonNeg :: Get Int
  128. nonNeg = fromIntegral <$> getWord32be
  129. getInt16be :: Get Int16
  130. getInt16be = fromIntegral <$> getWord16be
  131. getInt32be :: Get Int32
  132. getInt32be = fromIntegral <$> getWord32be