forked from haskell/cabal
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add fingerprint of Generic representation when serializing.
The idea is we can use Rep to get a full, structural representation of a type, and the fingerprint it using Typeable. This gives us a very concise way of fingerprinting our Binary representation. This patch is not completely correct; the fingerprint needs to be overridable when someone writes a custom Binary instance. But this should be "good enough" in practice; we're not using these fingerprints to check anything security critical. TODO: Not sure if I have tagged all the call-sites which could profit from this. Fixes haskell#4059. Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>
- Loading branch information
Showing
7 changed files
with
122 additions
and
19 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
{-# LANGUAGE CPP #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
-- | Support for binary serialization with a fingerprint. | ||
module Distribution.Utils.BinaryWithFingerprint ( | ||
encodeWithFingerprint, | ||
decodeWithFingerprint, | ||
decodeWithFingerprintOrFailIO, | ||
) where | ||
|
||
#if MIN_VERSION_base(4,8,0) | ||
|
||
import Distribution.Compat.Binary | ||
import Data.ByteString.Lazy (ByteString) | ||
import Control.Exception | ||
|
||
import Data.Binary.Get | ||
import Data.Binary.Put | ||
|
||
import GHC.Generics | ||
import GHC.Fingerprint | ||
import Data.Typeable | ||
import Control.Monad | ||
|
||
-- | Private wrapper type so we can give 'Binary' instance for | ||
-- 'Fingerprint' | ||
newtype FP = FP Fingerprint | ||
|
||
instance Binary FP where | ||
put (FP (Fingerprint a b)) = put a >> put b | ||
get = do | ||
a <- get | ||
b <- get | ||
return (FP (Fingerprint a b)) | ||
|
||
fingerprintRep :: forall a. Typeable (Rep a) => Proxy a -> Fingerprint | ||
fingerprintRep _ = typeRepFingerprint (typeRep (Proxy :: Proxy (Rep a))) | ||
|
||
-- | Encode a value, recording a fingerprint in the header. | ||
-- | ||
-- The fingerprint is GHC's Typeable fingerprint associated with | ||
-- the Generic Rep of a type: this fingerprint is better than | ||
-- the fingerprint of the type itself, as it changes when the | ||
-- representation changes (and thus the binary serialization format | ||
-- changes.) | ||
-- | ||
encodeWithFingerprint :: forall a. (Binary a, Typeable (Rep a)) => a -> ByteString | ||
encodeWithFingerprint x = runPut $ do | ||
put (FP (fingerprintRep (Proxy :: Proxy a))) | ||
put x | ||
|
||
-- | Decode a value, verifying the fingerprint in the header. | ||
-- | ||
decodeWithFingerprint :: forall a. (Binary a, Typeable (Rep a)) => ByteString -> a | ||
decodeWithFingerprint = runGet $ do | ||
FP fp <- get | ||
let expect_fp = fingerprintRep (Proxy :: Proxy a) | ||
when (expect_fp /= fp) $ | ||
fail $ "Expected fingerprint " ++ show expect_fp ++ | ||
" but got " ++ show fp | ||
get | ||
|
||
-- | Decode a value, forcing the decoded value to discover decoding errors | ||
-- and report them. | ||
-- | ||
decodeWithFingerprintOrFailIO :: (Binary a, Typeable (Rep a)) => ByteString -> IO (Either String a) | ||
decodeWithFingerprintOrFailIO bs = | ||
catch (evaluate (decodeWithFingerprint bs) >>= return . Right) | ||
$ \(ErrorCall str) -> return $ Left str | ||
|
||
#else | ||
|
||
import Distribution.Compat.Binary | ||
import Data.ByteString.Lazy (ByteString) | ||
|
||
-- Dummy implementations that don't actually save fingerprints | ||
|
||
encodeWithFingerprint :: Binary a => a -> ByteString | ||
encodeWithFingerprint = encode | ||
|
||
decodeWithFingerprint :: Binary a => ByteString -> a | ||
decodeWithFingerprint = decode | ||
|
||
decodeWithFingerprintOrFailIO :: Binary a => ByteString -> IO (Either String a) | ||
decodeWithFingerprintOrFailIO = decodeOrFailIO | ||
|
||
#endif |
This file contains 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
This file contains 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
This file contains 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
This file contains 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