{-# LANGUAGE CPP #-}

-- Andreas, Makoto, Francesco 2014-10-15 AIM XX:
-- -O2 does not have any noticable effect on runtime
-- but sabotages cabal repl with -Werror
-- (due to a conflict with --interactive warning)
-- {-# OPTIONS_GHC -O2                      #-}

-- | Structure-sharing serialisation of Agda interface files.

-- -!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-
-- NOTE: Every time the interface format is changed the interface
-- version number should be bumped _in the same patch_.
--
-- See 'currentInterfaceVersion' below.
--
-- -!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-!-

module Agda.TypeChecking.Serialise
  ( encode, encodeFile, encodeInterface
  , decode, decodeFile, decodeInterface, decodeHashes
  , EmbPrj
  )
  where

import Prelude hiding ( null )

import System.Directory ( createDirectoryIfMissing )
import System.FilePath ( takeDirectory )

import Control.Arrow (second)
import Control.DeepSeq
import qualified Control.Exception as E
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class ( MonadIO(..) )
import Control.Monad.Reader
import Control.Monad.State.Strict
import Control.Monad.ST.Trans

import Data.Array.IArray
import Data.Array.IO
import Data.Word
import Data.Int (Int32)
import Data.ByteString.Lazy    ( ByteString )
import Data.ByteString.Builder ( byteString, toLazyByteString )
import qualified Data.ByteString.Lazy as L
import qualified Data.Map as Map
import qualified Data.Binary as B
import qualified Data.Binary.Get as B
import qualified Data.Binary.Put as B
import qualified Data.List as List
import Data.Function (on)

import qualified Codec.Compression.GZip as G
import qualified Codec.Compression.Zlib.Internal as Z

import GHC.Compact as C

import qualified Agda.TypeChecking.Monad.Benchmark as Bench

import Agda.TypeChecking.Serialise.Base
import Agda.TypeChecking.Serialise.Instances () --instance only

import Agda.TypeChecking.Monad

import Agda.Utils.Hash
import qualified Agda.Utils.HashTable as H
import Agda.Utils.IORef
import Agda.Utils.Null
import qualified Agda.Utils.ProfileOptions as Profile

import Agda.Utils.Impossible

-- Note that the Binary instance for Int writes 64 bits, but throws
-- away the 32 high bits when reading (at the time of writing, on
-- 32-bit machines). Word64 does not have these problems.

currentInterfaceVersion :: Word64
currentInterfaceVersion :: Word64
currentInterfaceVersion = Word64
20231019 forall a. Num a => a -> a -> a
* Word64
10 forall a. Num a => a -> a -> a
+ Word64
0

-- | The result of 'encode' and 'encodeInterface'.

data Encoded = Encoded
  { Encoded -> ByteString
uncompressed :: ByteString
    -- ^ The uncompressed bytestring, without hashes and the interface
    -- version.
  , Encoded -> ByteString
compressed :: ByteString
    -- ^ The compressed bytestring.
  }

-- | Encodes something. To ensure relocatability file paths in
-- positions are replaced with module names.

encode :: EmbPrj a => a -> TCM Encoded
encode :: forall a. EmbPrj a => a -> TCM Encoded
encode a
a = do
    Bool
collectStats <- forall (m :: * -> *). MonadDebug m => ProfileOption -> m Bool
hasProfileOption ProfileOption
Profile.Serialize
    newD :: Dict
newD@(Dict HashTable Node Int32
nD HashTable String Int32
ltD HashTable Text Int32
stD HashTable Text Int32
bD HashTable Integer Int32
iD HashTable Double Int32
dD HashTable (Ptr Term) Int32
_tD
      HashTable NameId Int32
_nameD
      HashTable QNameId Int32
_qnameD
      IORef FreshAndReuse
nC IORef FreshAndReuse
ltC IORef FreshAndReuse
stC IORef FreshAndReuse
bC IORef FreshAndReuse
iC IORef FreshAndReuse
dC IORef FreshAndReuse
tC
      IORef FreshAndReuse
nameC
      IORef FreshAndReuse
qnameC
      HashTable String Int
stats Bool
_) <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ Bool -> IO Dict
emptyDict Bool
collectStats
    Int32
root <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ (forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
`runReaderT` Dict
newD) forall a b. (a -> b) -> a -> b
$ forall a. EmbPrj a => a -> ReaderT Dict IO Int32
icode a
a
    [Node]
nL  <- forall {c}. IO c -> TCMT IO c
benchSort forall a b. (a -> b) -> a -> b
$ forall {b} {b}. (Ord b, Hashable b) => HashTable b b -> IO [b]
l HashTable Node Int32
nD
    [Text]
stL <- forall {c}. IO c -> TCMT IO c
benchSort forall a b. (a -> b) -> a -> b
$ forall {b} {b}. (Ord b, Hashable b) => HashTable b b -> IO [b]
l HashTable Text Int32
stD
    [String]
ltL <- forall {c}. IO c -> TCMT IO c
benchSort forall a b. (a -> b) -> a -> b
$ forall {b} {b}. (Ord b, Hashable b) => HashTable b b -> IO [b]
l HashTable String Int32
ltD
    [Text]
bL  <- forall {c}. IO c -> TCMT IO c
benchSort forall a b. (a -> b) -> a -> b
$ forall {b} {b}. (Ord b, Hashable b) => HashTable b b -> IO [b]
l HashTable Text Int32
bD
    [Integer]
iL  <- forall {c}. IO c -> TCMT IO c
benchSort forall a b. (a -> b) -> a -> b
$ forall {b} {b}. (Ord b, Hashable b) => HashTable b b -> IO [b]
l HashTable Integer Int32
iD
    [Double]
dL  <- forall {c}. IO c -> TCMT IO c
benchSort forall a b. (a -> b) -> a -> b
$ forall {b} {b}. (Ord b, Hashable b) => HashTable b b -> IO [b]
l HashTable Double Int32
dD
    -- Record reuse statistics.
    forall (m :: * -> *). MonadDebug m => ProfileOption -> m () -> m ()
whenProfile ProfileOption
Profile.Sharing forall a b. (a -> b) -> a -> b
$ do
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"pointers" IORef FreshAndReuse
tC
    forall (m :: * -> *). MonadDebug m => ProfileOption -> m () -> m ()
whenProfile ProfileOption
Profile.Serialize forall a b. (a -> b) -> a -> b
$ do
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Integer"     IORef FreshAndReuse
iC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Lazy Text"   IORef FreshAndReuse
ltC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Strict Text" IORef FreshAndReuse
stC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Text"        IORef FreshAndReuse
bC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Double"      IORef FreshAndReuse
dC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Node"        IORef FreshAndReuse
nC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"Shared Term" IORef FreshAndReuse
tC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"A.QName"     IORef FreshAndReuse
qnameC
      String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
"A.Name"      IORef FreshAndReuse
nameC
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
collectStats forall a b. (a -> b) -> a -> b
$ do
      Map String Integer
stats <- forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith forall a. HasCallStack => a
__IMPOSSIBLE__ forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second forall a. Integral a => a -> Integer
toInteger) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> do
        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Ord a => [a] -> [a]
List.sort forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall k v. (Eq k, Hashable k) => HashTable k v -> IO [(k, v)]
H.toList HashTable String Int
stats
      (Map String Integer -> Map String Integer) -> TCMT IO ()
modifyStatistics forall a b. (a -> b) -> a -> b
$ forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith forall a. Num a => a -> a -> a
(+) Map String Integer
stats
    -- Encode hashmaps and root, and compress.
    ByteString
bits1 <- forall (m :: * -> *) c.
MonadBench m =>
Account (BenchPhase m) -> m c -> m c
Bench.billTo [ Phase
Bench.Serialization, Phase
Bench.BinaryEncode ] forall a b. (a -> b) -> a -> b
$
      forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. NFData a => (a -> b) -> a -> b
$!! forall a. Binary a => a -> ByteString
B.encode (Int32
root, [Node]
nL, [String]
ltL, [Text]
stL, [Text]
bL, [Integer]
iL, [Double]
dL)
    let compressParams :: CompressParams
compressParams = CompressParams
G.defaultCompressParams
          { compressLevel :: CompressionLevel
G.compressLevel    = CompressionLevel
G.bestSpeed
          , compressStrategy :: CompressionStrategy
G.compressStrategy = CompressionStrategy
G.huffmanOnlyStrategy
          }
    ByteString
cbits <- forall (m :: * -> *) c.
MonadBench m =>
Account (BenchPhase m) -> m c -> m c
Bench.billTo [ Phase
Bench.Serialization, Phase
Bench.Compress ] forall a b. (a -> b) -> a -> b
$
      forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. NFData a => (a -> b) -> a -> b
$!! CompressParams -> ByteString -> ByteString
G.compressWith CompressParams
compressParams ByteString
bits1
    let x :: ByteString
x = forall a. Binary a => a -> ByteString
B.encode Word64
currentInterfaceVersion forall a. Semigroup a => a -> a -> a
<> ByteString
cbits
    forall (m :: * -> *) a. Monad m => a -> m a
return (Encoded { uncompressed :: ByteString
uncompressed = ByteString
bits1, compressed :: ByteString
compressed = ByteString
x })
  where
    l :: HashTable b b -> IO [b]
l HashTable b b
h = forall a b. (a -> b) -> [a] -> [b]
List.map forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> a -> Ordering) -> [a] -> [a]
List.sortBy (forall a. Ord a => a -> a -> Ordering
compare forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall a b. (a, b) -> b
snd) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall k v. (Eq k, Hashable k) => HashTable k v -> IO [(k, v)]
H.toList HashTable b b
h
    benchSort :: IO c -> TCMT IO c
benchSort = forall (m :: * -> *) c.
MonadBench m =>
Account (BenchPhase m) -> m c -> m c
Bench.billTo [Phase
Bench.Serialization, Phase
Bench.Sort] forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
    statistics :: String -> IORef FreshAndReuse -> TCM ()
    statistics :: String -> IORef FreshAndReuse -> TCMT IO ()
statistics String
kind IORef FreshAndReuse
ioref = do
      FreshAndReuse Int32
fresh
#ifdef DEBUG_SERIALISATION
                          reused
#endif
                                 <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. IORef a -> IO a
readIORef IORef FreshAndReuse
ioref
      forall (m :: * -> *).
MonadStatistics m =>
String -> Integer -> m ()
tickN (String
kind forall a. [a] -> [a] -> [a]
++ String
"  (fresh)") forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
fresh
#ifdef DEBUG_SERIALISATION
      tickN (kind ++ " (reused)") $ fromIntegral reused
#endif

-- encode :: EmbPrj a => a -> TCM ByteString
-- encode a = do
--     fileMod <- sourceToModule
--     (x, shared, total) <- liftIO $ do
--       newD@(Dict nD sD iD dD _ _ _ _ _ stats _) <- emptyDict fileMod
--       root <- runReaderT (icode a) newD
--       nL <- l nD; sL <- l sD; iL <- l iD; dL <- l dD
--       (shared, total) <- readIORef stats
--       return (B.encode currentInterfaceVersion <>
--               G.compress (B.encode (root, nL, sL, iL, dL)), shared, total)
--     whenProfile Profile.Sharing $ do
--       tickN "pointers (reused)" $ fromIntegral shared
--       tickN "pointers" $ fromIntegral total
--     return x
--   where
--   l h = List.map fst . List.sortBy (compare `on` snd) <$> H.toList h

newtype ListLike a = ListLike { forall a. ListLike a -> Array Int32 a
unListLike :: Array Int32 a }

instance B.Binary a => B.Binary (ListLike a) where
  put :: ListLike a -> Put
put = forall a. HasCallStack => a
__IMPOSSIBLE__ -- Will never serialise this
  get :: Get (ListLike a)
get = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. Array Int32 a -> ListLike a
ListLike forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) i e.
Monad m =>
(forall s. STT s m (STArray s i e)) -> m (Array i e)
runSTArray forall a b. (a -> b) -> a -> b
$ do
    Int
n <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (forall t. Binary t => Get t
B.get :: B.Get Int)
    STArray s Int32 a
arr <- forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> m (a i e)
newArray_ (Int32
0, forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n forall a. Num a => a -> a -> a
- Int32
1) :: STT s B.Get (STArray s Int32 a)

    -- We'd like to use 'for_ [0..n-1]' here, but unfortunately GHC doesn't unfold
    -- the list construction and so performs worse than the hand-written version.
    let
      getMany :: Int -> STT s Get ()
getMany Int
i = if Int
i forall a. Eq a => a -> a -> Bool
== Int
n then forall (m :: * -> *) a. Monad m => a -> m a
return () else do
        a
x <- forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift forall t. Binary t => Get t
B.get
        forall (m :: * -> *) s i e.
Applicative m =>
STArray s i e -> Int -> e -> STT s m ()
unsafeWriteSTArray STArray s Int32 a
arr Int
i a
x
        Int -> STT s Get ()
getMany (Int
i forall a. Num a => a -> a -> a
+ Int
1)
    () <- Int -> STT s Get ()
getMany Int
0

    forall (m :: * -> *) a. Monad m => a -> m a
return STArray s Int32 a
arr

-- | Decodes an uncompressed bytestring (without extra hashes or magic
-- numbers). The result depends on the include path.
--
-- Returns 'Nothing' if a decoding error is encountered.

decode :: EmbPrj a => ByteString -> TCM (Maybe a)
decode :: forall a. EmbPrj a => ByteString -> TCM (Maybe a)
decode ByteString
s = do
  ModuleToSource
mf   <- forall (m :: * -> *) a. ReadTCState m => Lens' TCState a -> m a
useTC Lens' TCState ModuleToSource
stModuleToSource
  [AbsolutePath]
incs <- forall (m :: * -> *). HasOptions m => m [AbsolutePath]
getIncludeDirs

  -- Note that runGetState can raise errors if the input is malformed.
  -- The decoder is (intended to be) strict enough to ensure that all
  -- such errors can be caught by the handler here.

  Either String (ModuleToSource, a)
res <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => (e -> IO a) -> IO a -> IO a
E.handle (\(E.ErrorCall String
s) -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left String
s) forall a b. (a -> b) -> a -> b
$ do
    ((Int32
r, ListLike [Int32]
nL, ListLike String
ltL, ListLike Text
stL, ListLike Text
bL, ListLike Integer
iL, ListLike Double
dL), ByteString
s, Int64
_) <- forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. Get a -> ByteString -> Int64 -> (a, ByteString, Int64)
runGetState forall t. Binary t => Get t
B.get ByteString
s Int64
0
    let ar :: ListLike a -> Array Int32 a
ar = forall a. ListLike a -> Array Int32 a
unListLike
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not (forall a. Null a => a -> Bool
null ByteString
s)) forall a b. (a -> b) -> a -> b
$ forall e a. Exception e => e -> IO a
E.throwIO forall a b. (a -> b) -> a -> b
$ String -> ErrorCall
E.ErrorCall String
"Garbage at end."
    let nL' :: Array Int32 [Int32]
nL' = forall a. ListLike a -> Array Int32 a
ar ListLike [Int32]
nL
    St
st <- Array Int32 [Int32]
-> Array Int32 String
-> Array Int32 Text
-> Array Int32 Text
-> Array Int32 Integer
-> Array Int32 Double
-> Memo
-> ModuleToSource
-> [AbsolutePath]
-> St
St Array Int32 [Int32]
nL' (forall a. ListLike a -> Array Int32 a
ar ListLike String
ltL) (forall a. ListLike a -> Array Int32 a
ar ListLike Text
stL) (forall a. ListLike a -> Array Int32 a
ar ListLike Text
bL) (forall a. ListLike a -> Array Int32 a
ar ListLike Integer
iL) (forall a. ListLike a -> Array Int32 a
ar ListLike Double
dL)
            forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> e -> m (a i e)
newArray (forall (a :: * -> * -> *) e i.
(IArray a e, Ix i) =>
a i e -> (i, i)
bounds Array Int32 [Int32]
nL') MemoEntry
MEEmpty)
            forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a. Monad m => a -> m a
return ModuleToSource
mf forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a. Monad m => a -> m a
return [AbsolutePath]
incs
    (a
r, St
st) <- forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
runStateT (forall a. EmbPrj a => Int32 -> R a
value Int32
r) St
st
    let !mf :: ModuleToSource
mf = St -> ModuleToSource
modFile St
st
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. b -> Either a b
Right (ModuleToSource
mf, a
r)

  case Either String (ModuleToSource, a)
res of
    Left String
s -> do
      forall (m :: * -> *).
MonadDebug m =>
String -> Int -> String -> m ()
reportSLn String
"import.iface" Int
5 forall a b. (a -> b) -> a -> b
$ String
"Error when decoding interface file: " forall a. [a] -> [a] -> [a]
++ String
s
      forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing

    Right (ModuleToSource
mf, a
x) -> do
      forall (m :: * -> *) a.
MonadTCState m =>
Lens' TCState a -> a -> m ()
setTCLens Lens' TCState ModuleToSource
stModuleToSource ModuleToSource
mf
      -- "Compact" the interfaces (without breaking sharing) to
      -- reduce the amount of memory that is traversed by the
      -- garbage collector.
      forall (m :: * -> *) c.
MonadBench m =>
Account (BenchPhase m) -> m c -> m c
Bench.billTo [Phase
Bench.Deserialization, Phase
Bench.Compaction] forall a b. (a -> b) -> a -> b
$
        forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Compact a -> a
C.getCompact forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. a -> IO (Compact a)
C.compactWithSharing a
x)


encodeInterface :: Interface -> TCM Encoded
encodeInterface :: Interface -> TCM Encoded
encodeInterface Interface
i = do
  Encoded
r <- forall a. EmbPrj a => a -> TCM Encoded
encode Interface
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Encoded
r{ compressed :: ByteString
compressed = ByteString
hashes forall a. Semigroup a => a -> a -> a
<> Encoded -> ByteString
compressed Encoded
r }
  where
    hashes :: ByteString
    hashes :: ByteString
hashes = Put -> ByteString
B.runPut forall a b. (a -> b) -> a -> b
$ forall t. Binary t => t -> Put
B.put (Interface -> Word64
iSourceHash Interface
i) forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall t. Binary t => t -> Put
B.put (Interface -> Word64
iFullHash Interface
i)

-- | Encodes an interface. To ensure relocatability file paths in
-- positions are replaced with module names.
--
-- An uncompressed bytestring corresponding to the encoded interface
-- is returned.

encodeFile :: FilePath -> Interface -> TCM ByteString
encodeFile :: String -> Interface -> TCMT IO ByteString
encodeFile String
f Interface
i = do
  Encoded
r <- Interface -> TCM Encoded
encodeInterface Interface
i
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ Bool -> String -> IO ()
createDirectoryIfMissing Bool
True (String -> String
takeDirectory String
f)
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ String -> ByteString -> IO ()
L.writeFile String
f (Encoded -> ByteString
compressed Encoded
r)
  forall (m :: * -> *) a. Monad m => a -> m a
return (Encoded -> ByteString
uncompressed Encoded
r)

-- | Decodes an interface. The result depends on the include path.
--
-- Returns 'Nothing' if the file does not start with the right magic
-- number or some other decoding error is encountered.

decodeInterface :: ByteString -> TCM (Maybe Interface)
decodeInterface :: ByteString -> TCM (Maybe Interface)
decodeInterface ByteString
s = do

  -- Note that runGetState and the decompression code below can raise
  -- errors if the input is malformed. The decoder is (intended to be)
  -- strict enough to ensure that all such errors can be caught by the
  -- handler here or the one in decode.

  Either String ByteString
s <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$
       forall e a. Exception e => (e -> IO a) -> IO a -> IO a
E.handle (\(E.ErrorCall String
s) -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall a b. a -> Either a b
Left String
s)) forall a b. (a -> b) -> a -> b
$
       forall a. a -> IO a
E.evaluate forall a b. (a -> b) -> a -> b
$
       let (Word64
ver, ByteString
s', Int64
_) = forall a. Get a -> ByteString -> Int64 -> (a, ByteString, Int64)
runGetState forall t. Binary t => Get t
B.get (Int64 -> ByteString -> ByteString
L.drop Int64
16 ByteString
s) Int64
0 in
       if Word64
ver forall a. Eq a => a -> a -> Bool
/= Word64
currentInterfaceVersion
       then forall a b. a -> Either a b
Left String
"Wrong interface version."
       else forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$
            Builder -> ByteString
toLazyByteString forall a b. (a -> b) -> a -> b
$
            forall a.
(ByteString -> a -> a)
-> (ByteString -> a)
-> (DecompressError -> a)
-> (forall s. DecompressStream (ST s))
-> ByteString
-> a
Z.foldDecompressStreamWithInput
              (\ByteString
s -> (ByteString -> Builder
byteString ByteString
s forall a. Semigroup a => a -> a -> a
<>))
              (\ByteString
s -> if forall a. Null a => a -> Bool
null ByteString
s
                     then forall a. Monoid a => a
mempty
                     else forall a. HasCallStack => String -> a
error String
"Garbage at end.")
              (\DecompressError
err -> forall a. HasCallStack => String -> a
error (forall a. Show a => a -> String
show DecompressError
err))
              (forall s. Format -> DecompressParams -> DecompressStream (ST s)
Z.decompressST Format
Z.gzipFormat DecompressParams
Z.defaultDecompressParams)
              ByteString
s'

  case Either String ByteString
s of
    Right ByteString
s  -> forall a. EmbPrj a => ByteString -> TCM (Maybe a)
decode ByteString
s
    Left String
err -> do
      forall (m :: * -> *).
MonadDebug m =>
String -> Int -> String -> m ()
reportSLn String
"import.iface" Int
5 forall a b. (a -> b) -> a -> b
$
        String
"Error when decoding interface file: " forall a. [a] -> [a] -> [a]
++ String
err
      forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing

decodeHashes :: ByteString -> Maybe (Hash, Hash)
decodeHashes :: ByteString -> Maybe (Word64, Word64)
decodeHashes ByteString
s
  | ByteString -> Int64
L.length ByteString
s forall a. Ord a => a -> a -> Bool
< Int64
16 = forall a. Maybe a
Nothing
  | Bool
otherwise       = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. Get a -> ByteString -> a
B.runGet Get (Word64, Word64)
getH forall a b. (a -> b) -> a -> b
$ Int64 -> ByteString -> ByteString
L.take Int64
16 ByteString
s
  where getH :: Get (Word64, Word64)
getH = (,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall t. Binary t => Get t
B.get forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall t. Binary t => Get t
B.get

decodeFile :: FilePath -> TCM (Maybe Interface)
decodeFile :: String -> TCM (Maybe Interface)
decodeFile String
f = ByteString -> TCM (Maybe Interface)
decodeInterface forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO ByteString
L.readFile String
f)