19e78816f0
This adds the overhead of a copy when serializing and deserializing keys. I have not benchmarked much, but runtimes seem barely changed at all by that. When a lot of keys are in memory, it improves memory use. And, it prevents keys sometimes getting PINNED in memory and failing to GC, which is a problem ByteString has sometimes. In particular, git-annex sync from a borg special remote had that problem and this improved its memory use by a large amount. Sponsored-by: Shae Erisson on Patreon
108 lines
2.8 KiB
Haskell
108 lines
2.8 KiB
Haskell
{- git-annex Keys
|
|
-
|
|
- Copyright 2011-2020 Joey Hess <id@joeyh.name>
|
|
-
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
|
-}
|
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
module Key (
|
|
Key,
|
|
KeyData(..),
|
|
AssociatedFile(..),
|
|
fromKey,
|
|
mkKey,
|
|
alterKey,
|
|
keyParser,
|
|
serializeKey,
|
|
serializeKey',
|
|
deserializeKey,
|
|
deserializeKey',
|
|
nonChunkKey,
|
|
chunkKeyOffset,
|
|
isChunkKey,
|
|
isKeyPrefix,
|
|
splitKeyNameExtension,
|
|
|
|
prop_isomorphic_key_encode
|
|
) where
|
|
|
|
import qualified Data.Text as T
|
|
import qualified Data.ByteString as S
|
|
import qualified Data.ByteString.Short as S (toShort, fromShort)
|
|
import qualified Data.Attoparsec.ByteString as A
|
|
|
|
import Common
|
|
import Types.Key
|
|
import Utility.QuickCheck
|
|
import Utility.Bloom
|
|
import Utility.Aeson
|
|
import qualified Utility.SimpleProtocol as Proto
|
|
|
|
-- Gets the parent of a chunk key.
|
|
nonChunkKey :: Key -> Key
|
|
nonChunkKey k
|
|
| fromKey keyChunkSize k == Nothing && fromKey keyChunkNum k == Nothing = k
|
|
| otherwise = alterKey k $ \d -> d
|
|
{ keyChunkSize = Nothing
|
|
, keyChunkNum = Nothing
|
|
}
|
|
|
|
-- Where a chunk key is offset within its parent.
|
|
chunkKeyOffset :: Key -> Maybe Integer
|
|
chunkKeyOffset k = (*)
|
|
<$> fromKey keyChunkSize k
|
|
<*> (pred <$> fromKey keyChunkNum k)
|
|
|
|
isChunkKey :: Key -> Bool
|
|
isChunkKey k = isJust (fromKey keyChunkSize k) && isJust (fromKey keyChunkNum k)
|
|
|
|
serializeKey :: Key -> String
|
|
serializeKey = decodeBS . serializeKey'
|
|
|
|
serializeKey' :: Key -> S.ByteString
|
|
serializeKey' = S.fromShort . keySerialization
|
|
|
|
deserializeKey :: String -> Maybe Key
|
|
deserializeKey = deserializeKey' . encodeBS
|
|
|
|
deserializeKey' :: S.ByteString -> Maybe Key
|
|
deserializeKey' = eitherToMaybe . A.parseOnly keyParser
|
|
|
|
instance Arbitrary KeyData where
|
|
arbitrary = Key
|
|
<$> (S.toShort . encodeBS <$> (listOf1 $ elements $ ['A'..'Z'] ++ ['a'..'z'] ++ ['0'..'9'] ++ "-_\r\n \t"))
|
|
<*> (parseKeyVariety . encodeBS <$> (listOf1 $ elements ['A'..'Z'])) -- BACKEND
|
|
<*> ((abs <$>) <$> arbitrary) -- size cannot be negative
|
|
<*> ((abs . fromInteger <$>) <$> arbitrary) -- mtime cannot be negative
|
|
<*> ((abs <$>) <$> arbitrary) -- chunksize cannot be negative
|
|
<*> ((succ . abs <$>) <$> arbitrary) -- chunknum cannot be 0 or negative
|
|
|
|
instance Arbitrary AssociatedFile where
|
|
arbitrary = AssociatedFile
|
|
. fmap (toRawFilePath . fromTestableFilePath)
|
|
<$> arbitrary
|
|
|
|
instance Arbitrary Key where
|
|
arbitrary = mkKey . const <$> arbitrary
|
|
|
|
instance Hashable Key where
|
|
hashIO32 = hashIO32 . serializeKey'
|
|
hashIO64 = hashIO64 . serializeKey'
|
|
|
|
instance ToJSON' Key where
|
|
toJSON' = toJSON' . serializeKey
|
|
|
|
instance FromJSON Key where
|
|
parseJSON (String t) = maybe mempty pure $ deserializeKey $ T.unpack t
|
|
parseJSON _ = mempty
|
|
|
|
instance Proto.Serializable Key where
|
|
serialize = serializeKey
|
|
deserialize = deserializeKey
|
|
|
|
prop_isomorphic_key_encode :: Key -> Bool
|
|
prop_isomorphic_key_encode k = Just k == (deserializeKey . serializeKey) k
|
|
|