Merge pull request #32 from haskell-nix/readonly-store-hash

Implement readonly store path hashing
This commit is contained in:
Doug Beardsley 2019-03-10 13:13:01 -04:00 committed by GitHub
commit 8965dc85bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 19 deletions

View File

@ -24,9 +24,11 @@ library
, System.Nix.Internal.Hash , System.Nix.Internal.Hash
, System.Nix.Nar , System.Nix.Nar
, System.Nix.Path , System.Nix.Path
, System.Nix.ReadonlyStore
, System.Nix.Store , System.Nix.Store
, System.Nix.Util , System.Nix.Util
build-depends: base >=4.10 && <4.12 build-depends: base >=4.10 && <4.12
, base16-bytestring
, bytestring , bytestring
, binary , binary
, bytestring , bytestring
@ -61,7 +63,7 @@ test-suite format-tests
NarFormat NarFormat
Hash Hash
hs-source-dirs: hs-source-dirs:
tests tests
build-depends: build-depends:
hnix-store-core hnix-store-core
, base , base

View File

@ -12,25 +12,29 @@ Maintainer : Greg Hale <imalsogreg@gmail.com>
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeInType #-} {-# LANGUAGE TypeInType #-}
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.Internal.Hash where module System.Nix.Internal.Hash where
import qualified Crypto.Hash.MD5 as MD5 import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1 import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.SHA256 as SHA256 import qualified Crypto.Hash.SHA256 as SHA256
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC import qualified Data.ByteString.Base16 as Base16
import Data.Bits (xor) import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString as BS import Data.Bits (xor)
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString as BS
import qualified Data.Hashable as DataHashable import qualified Data.ByteString.Lazy as BSL
import Data.Kind (Type) import qualified Data.Hashable as DataHashable
import Data.List (foldl') import Data.Kind (Type)
import Data.Proxy (Proxy(Proxy)) import Data.List (foldl')
import qualified Data.Text as T import Data.Monoid
import qualified Data.Text.Encoding as T import Data.Proxy (Proxy(Proxy))
import qualified Data.Vector as V import Data.Text (Text)
import Data.Word (Word8) import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Vector as V
import Data.Word (Word8)
import GHC.TypeLits import GHC.TypeLits
-- | A tag for different hashing algorithms -- | A tag for different hashing algorithms
@ -45,6 +49,18 @@ data HashAlgorithm' n
| Truncated n (HashAlgorithm' n) | Truncated n (HashAlgorithm' n)
deriving (Eq, Show) deriving (Eq, Show)
class HashAlgoText a where
algoString :: Proxy a -> Text
instance HashAlgoText 'MD5 where
algoString (Proxy :: Proxy 'MD5) = "md5"
instance HashAlgoText 'SHA1 where
algoString (Proxy :: Proxy 'SHA1) = "sha1"
instance HashAlgoText 'SHA256 where
algoString (Proxy :: Proxy 'SHA256) = "sha256"
type HashAlgorithm = HashAlgorithm' Nat type HashAlgorithm = HashAlgorithm' Nat
-- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance -- | Types with kind @HashAlgorithm@ may be a @HasDigest@ instance
@ -80,8 +96,11 @@ hashLazy :: forall a.HasDigest a => BSL.ByteString -> Digest a
hashLazy bsl = hashLazy bsl =
finalize $ foldl' (update @a) (initialize @a) (BSL.toChunks bsl) finalize $ foldl' (update @a) (initialize @a) (BSL.toChunks bsl)
digestText32 :: forall a. HashAlgoText a => Digest a -> T.Text
digestText32 d = algoString (Proxy :: Proxy a) <> ":" <> printAsBase32 d
digestText16 :: forall a. HashAlgoText a => Digest a -> T.Text
digestText16 (Digest bs) = algoString (Proxy :: Proxy a) <> ":" <> T.decodeUtf8 (Base16.encode bs)
-- | Convert any Digest to a base32-encoded string. -- | Convert any Digest to a base32-encoded string.
-- This is not used in producing store path hashes -- This is not used in producing store path hashes

View File

@ -3,11 +3,13 @@ Description : Types and effects for interacting with the Nix store.
Maintainer : Shea Levy <shea@shealevy.com> Maintainer : Shea Levy <shea@shealevy.com>
-} -}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}
module System.Nix.Path module System.Nix.Path
( FilePathPart(..) ( FilePathPart(..)
, PathHashAlgo , PathHashAlgo
, Path(..) , Path(..)
, pathToText
, PathSet , PathSet
, SubstitutablePathInfo(..) , SubstitutablePathInfo(..)
, ValidPathInfo(..) , ValidPathInfo(..)
@ -19,12 +21,14 @@ module System.Nix.Path
import System.Nix.Hash (Digest(..), import System.Nix.Hash (Digest(..),
HashAlgorithm'(Truncated, SHA256)) HashAlgorithm'(Truncated, SHA256))
import System.Nix.Internal.Hash
import qualified Data.ByteString as BS import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC import qualified Data.ByteString.Char8 as BSC
import Data.Hashable (Hashable (..), hashPtrWithSalt) import Data.Hashable (Hashable (..), hashPtrWithSalt)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.HashSet (HashSet) import Data.HashSet (HashSet)
import Data.Map.Strict (Map) import Data.Map.Strict (Map)
import Data.Monoid
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import System.IO.Unsafe (unsafeDupablePerformIO) import System.IO.Unsafe (unsafeDupablePerformIO)
@ -46,7 +50,7 @@ newtype PathName = PathName
-- | A regular expression for matching a valid 'PathName' -- | A regular expression for matching a valid 'PathName'
nameRegex :: Regex nameRegex :: Regex
nameRegex = nameRegex =
makeRegex "[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*" makeRegex ("[a-zA-Z0-9\\+\\-\\_\\?\\=][a-zA-Z0-9\\+\\-\\.\\_\\?\\=]*" :: String)
-- | Construct a 'PathName', assuming the provided contents are valid. -- | Construct a 'PathName', assuming the provided contents are valid.
pathName :: Text -> Maybe PathName pathName :: Text -> Maybe PathName
@ -58,6 +62,9 @@ pathName n = case matchTest nameRegex n of
data Path = Path !(Digest PathHashAlgo) !PathName data Path = Path !(Digest PathHashAlgo) !PathName
deriving (Eq, Ord, Show) deriving (Eq, Ord, Show)
pathToText :: Text -> Path -> Text
pathToText storeDir (Path h nm) = storeDir <> "/" <> printAsBase32 h <> "-" <> pathNameContents nm
type PathSet = HashSet Path type PathSet = HashSet Path
-- | Information about substitutes for a 'Path'. -- | Information about substitutes for a 'Path'.

View File

@ -0,0 +1,33 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module System.Nix.ReadonlyStore where
import Data.ByteString (ByteString)
import Data.ByteString.Base16 as Base16
import qualified Data.HashSet as HS
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding
import System.Nix.Internal.Hash
import System.Nix.Path
makeStorePath :: Text -> Text -> Digest 'SHA256 -> Text -> Path
makeStorePath storeDir ty h nm = Path storeHash (PathName nm)
where
s = T.intercalate ":"
[ ty
, digestText16 h
, storeDir
, nm
]
storeHash = truncateDigest $ hash $ encodeUtf8 s
makeTextPath :: Text -> Text -> Digest 'SHA256 -> PathSet -> Path
makeTextPath storeDir nm h refs = makeStorePath storeDir ty h nm
where
ty = T.intercalate ":" ("text" : map (pathToText storeDir) (HS.toList refs))
computeStorePathForText :: Text -> Text -> ByteString -> PathSet -> Path
computeStorePathForText storeDir nm s refs = makeTextPath storeDir nm (hash s) refs