commit ghc-store for openSUSE:Factory
Hello community, here is the log from the commit of package ghc-store for openSUSE:Factory checked in at 2017-08-31 20:59:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-store (Old) and /work/SRC/openSUSE:Factory/.ghc-store.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "ghc-store" Thu Aug 31 20:59:52 2017 rev:2 rq:513501 version:0.4.3.1 Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-store/ghc-store.changes 2017-05-10 20:51:04.318153140 +0200 +++ /work/SRC/openSUSE:Factory/.ghc-store.new/ghc-store.changes 2017-08-31 20:59:53.438525082 +0200 @@ -1,0 +2,5 @@ +Wed Jul 26 16:56:09 UTC 2017 - psimons@suse.com + +- Update to version 0.4.3.1. + +------------------------------------------------------------------- Old: ---- store-0.3.1.tar.gz New: ---- store-0.4.3.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-store.spec ++++++ --- /var/tmp/diff_new_pack.AkTH4d/_old 2017-08-31 20:59:54.550368864 +0200 +++ /var/tmp/diff_new_pack.AkTH4d/_new 2017-08-31 20:59:54.558367740 +0200 @@ -19,7 +19,7 @@ %global pkg_name store %bcond_with tests Name: ghc-%{pkg_name} -Version: 0.3.1 +Version: 0.4.3.1 Release: 0 Summary: Fast binary serialization License: MIT ++++++ store-0.3.1.tar.gz -> store-0.4.3.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/ChangeLog.md new/store-0.4.3.1/ChangeLog.md --- old/store-0.3.1/ChangeLog.md 2017-02-16 07:28:28.000000000 +0100 +++ new/store-0.4.3.1/ChangeLog.md 2017-05-09 02:43:17.000000000 +0200 @@ -1,5 +1,44 @@ # ChangeLog +## 0.4.3.1 + +* Fixed compilation on GHC 7.8 + +## 0.4.3 + +* Less aggressive inlining, resulting in faster compilation / simplifier + not running out of ticks + +## 0.4.2 + +* Fixed testsuite + +## 0.4.1 + +* Breaking change in the encoding of Map / Set / IntMap / IntSet, + to use ascending key order. Attempting to decode data written by + prior versions of store (and vice versa) will almost always fail + with a decent error message. If you're unlucky enough to have a + collision in the data with a random Word32 magic number, then the + error may not be so clear, or in extremely rare cases, + successfully decode, yielding incorrect results. See + [#97](https://github.com/fpco/store/issues/97) and + [#101](https://github.com/fpco/store/pull/101). + + +* Performance improvement of the 'Peek' monad, by introducing more + strictness. This required a change to the internal API. + +* API and behavior of 'Data.Store.Version' changed. Previously, it + would check the version tag after decoding the contents. It now + also stores a magic Word32 tag at the beginning, so that it fails + more gracefully when decoding input that lacks encoded version + info. + +## 0.4.0 + +Deprecated in favor of 0.4.1 + ## 0.3.1 * Fix to derivation of primitive vectors, only relevant when built with diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/bench/Bench.hs new/store-0.4.3.1/bench/Bench.hs --- old/store-0.3.1/bench/Bench.hs 2016-06-13 11:34:39.000000000 +0200 +++ new/store-0.4.3.1/bench/Bench.hs 2017-02-28 06:37:20.000000000 +0100 @@ -13,6 +13,10 @@ import Criterion.Main import qualified Data.ByteString as BS import Data.Int +import qualified Data.IntMap.Strict as IntMap +import qualified Data.IntSet as IntSet +import qualified Data.Map.Strict as Map +import qualified Data.Set as Set import Data.Store import Data.Typeable import qualified Data.Vector as V @@ -71,6 +75,16 @@ _ -> error "This does not compute." ) <$> V.enumFromTo 1 (100 :: Int) nestedTuples = (\i -> ((i,i+1),(i+2,i+3))) <$> V.enumFromTo (1::Int) 100 + + ints = [1..100] :: [Int] + pairs = map (\x -> (x, x)) ints + strings = show <$> ints + intsSet = Set.fromDistinctAscList ints + intSet = IntSet.fromDistinctAscList ints + intsMap = Map.fromDistinctAscList pairs + intMap = IntMap.fromDistinctAscList pairs + stringsSet = Set.fromList strings + stringsMap = Map.fromList (zip strings ints) #endif defaultMain [ bgroup "encode" @@ -80,6 +94,12 @@ , benchEncode' "10kb storable" (SV.fromList ([1..(256 * 10)] :: [Int32])) , benchEncode' "1kb normal" (V.fromList ([1..256] :: [Int32])) , benchEncode' "10kb normal" (V.fromList ([1..(256 * 10)] :: [Int32])) + , benchEncode intsSet + , benchEncode intSet + , benchEncode intsMap + , benchEncode intMap + , benchEncode stringsSet + , benchEncode stringsMap #endif , benchEncode smallprods , benchEncode smallmanualprods @@ -95,6 +115,12 @@ , benchDecode' "10kb storable" (SV.fromList ([1..(256 * 10)] :: [Int32])) , benchDecode' "1kb normal" (V.fromList ([1..256] :: [Int32])) , benchDecode' "10kb normal" (V.fromList ([1..(256 * 10)] :: [Int32])) + , benchDecode intsSet + , benchDecode intSet + , benchDecode intsMap + , benchDecode intMap + , benchDecode stringsSet + , benchDecode stringsMap #endif , benchDecode smallprods , benchDecode smallmanualprods diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/Data/Store/Impl.hs new/store-0.4.3.1/src/Data/Store/Impl.hs --- old/store-0.3.1/src/Data/Store/Impl.hs 2017-02-14 20:28:40.000000000 +0100 +++ new/store-0.4.3.1/src/Data/Store/Impl.hs 2017-05-06 04:41:28.000000000 +0200 @@ -90,26 +90,22 @@ -- this package) combined with auomatic definition of instances. encode :: Store a => a -> BS.ByteString encode x = unsafeEncodeWith (poke x) (getSize x) -{-# INLINE encode #-} -- | Decodes a value from a 'BS.ByteString'. Returns an exception if -- there's an error while decoding, or if decoding undershoots / -- overshoots the end of the buffer. decode :: Store a => BS.ByteString -> Either PeekException a decode = unsafePerformIO . try . decodeIO -{-# INLINE decode #-} -- | Decodes a value from a 'BS.ByteString', potentially throwing -- exceptions. It is an exception to not consume all input. decodeEx :: Store a => BS.ByteString -> a decodeEx = unsafePerformIO . decodeIO -{-# INLINE decodeEx #-} -- | Decodes a value from a 'BS.ByteString', potentially throwing -- exceptions. It is an exception to not consume all input. decodeIO :: Store a => BS.ByteString -> IO a decodeIO = decodeIOWith peek -{-# INLINE decodeIO #-} ------------------------------------------------------------------------ -- Size diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/Data/Store/Internal.hs new/store-0.4.3.1/src/Data/Store/Internal.hs --- old/store-0.3.1/src/Data/Store/Internal.hs 2017-02-14 20:29:04.000000000 +0100 +++ new/store-0.4.3.1/src/Data/Store/Internal.hs 2017-05-06 04:50:14.000000000 +0200 @@ -46,6 +46,8 @@ , sizeSet, pokeSet, peekSet -- ** Store instances in terms of IsMap , sizeMap, pokeMap, peekMap + -- *** Utilities for ordered maps + , sizeOrdMap, pokeOrdMap, peekOrdMapWith -- ** Store instances in terms of IArray , sizeArray, pokeArray, peekArray -- ** Store instances in terms of Generic @@ -54,6 +56,7 @@ , GStorePeek, genericPeek -- ** Peek utilities , skip, isolate + , peekMagic -- ** Static Size type -- -- This portion of the library is still work-in-progress. @@ -81,9 +84,12 @@ import Data.HashSet (HashSet) import Data.Hashable (Hashable) import Data.IntMap (IntMap) +import qualified Data.IntMap.Strict as IntMap import Data.IntSet (IntSet) +import qualified Data.IntSet as IntSet import qualified Data.List.NonEmpty as NE import Data.Map (Map) +import qualified Data.Map.Strict as Map import Data.MonoTraversable import Data.Monoid import Data.Orphans () @@ -92,6 +98,7 @@ import Data.Sequence (Seq) import Data.Sequences (IsSequence, Index, replicateM) import Data.Set (Set) +import qualified Data.Set as Set import Data.Store.Impl import Data.Store.Core import Data.Store.TH.Internal @@ -216,11 +223,7 @@ :: (Store (ContainerKey t), Store (MapValue t), IsMap t) => t -> Poke () -pokeMap t = do - poke (olength t) - ofoldl' (\acc (k, x) -> poke k >> poke x >> acc) - (return ()) - (mapToList t) +pokeMap = pokeSequence . mapToList {-# INLINE pokeMap #-} -- | Implement 'peek' for an 'IsMap' of where both 'ContainerKey' and @@ -231,6 +234,61 @@ peekMap = mapFromList <$> peek {-# INLINE peekMap #-} +------------------------------------------------------------------------ +-- Utilities for defining 'Store' instances for ordered containers like +-- 'IntMap' and 'Map' + +-- | Marker for maps that are encoded in ascending order instead of the +-- descending order mistakenly implemented in 'peekMap' in store versions +-- < 0.4. +-- +-- See https://github.com/fpco/store/issues/97. +markMapPokedInAscendingOrder :: Word32 +markMapPokedInAscendingOrder = 1217678090 + +-- | Ensure the presence of a given magic value. +-- +-- Throws a 'PeekException' if the value isn't present. +peekMagic + :: (Eq a, Show a, Store a) + => String -> a -> Peek () +peekMagic markedThing x = do + x' <- peek + when (x' /= x) $ + fail ("Expected marker for " ++ markedThing ++ ": " ++ show x ++ " but got: " ++ show x') +{-# INLINE peekMagic #-} + +-- | Like 'sizeMap' but should only be used for ordered containers where +-- 'Data.Containers.mapToList' returns an ascending list. +sizeOrdMap + :: forall t. + (Store (ContainerKey t), Store (MapValue t), IsMap t) + => Size t +sizeOrdMap = + combineSizeWith (const markMapPokedInAscendingOrder) id size sizeMap +{-# INLINE sizeOrdMap #-} + +-- | Like 'pokeMap' but should only be used for ordered containers where +-- 'Data.Containers.mapToList' returns an ascending list. +pokeOrdMap + :: (Store (ContainerKey t), Store (MapValue t), IsMap t) + => t -> Poke () +pokeOrdMap x = poke markMapPokedInAscendingOrder >> pokeMap x +{-# INLINE pokeOrdMap #-} + +-- | Decode the results of 'pokeOrdMap' using a given function to construct +-- the map. +peekOrdMapWith + :: (Store (ContainerKey t), Store (MapValue t)) + => ([(ContainerKey t, MapValue t)] -> t) + -- ^ A function to construct the map from an ascending list such as + -- 'Map.fromDistinctAscList'. + -> Peek t +peekOrdMapWith f = do + peekMagic "ascending Map / IntMap" markMapPokedInAscendingOrder + f <$> peek +{-# INLINE peekOrdMapWith #-} + {- ------------------------------------------------------------------------ -- Utilities for defining list-like 'Store' instances in terms of Foldable @@ -279,7 +337,7 @@ remaining = peekStateEndPtr ps `minusPtr` ptr when (len > remaining) $ -- Do not perform the check on the new pointer, since it could have overflowed tooManyBytes len remaining "skip" - return (ptr2, ()) + return $ PeekResult ptr2 () -- | Isolate the input to n bytes, skipping n bytes forward. Fails if @m@ -- advances the offset beyond the isolated region. @@ -291,10 +349,10 @@ remaining = end `minusPtr` ptr when (len > remaining) $ -- Do not perform the check on the new pointer, since it could have overflowed tooManyBytes len remaining "isolate" - (ptr', x) <- runPeek m ps ptr + PeekResult ptr' x <- runPeek m ps ptr when (ptr' > end) $ throwIO $ PeekException (ptr' `minusPtr` end) "Overshot end of isolated bytes" - return (ptr2, x) + return $ PeekResult ptr2 x ------------------------------------------------------------------------ -- Instances for types based on flat representations @@ -303,9 +361,6 @@ size = sizeSequence poke = pokeSequence peek = V.unsafeFreeze =<< peekMutableSequence MV.new MV.write - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Storable a => Store (SV.Vector a) where size = VarSize $ \x -> @@ -319,9 +374,6 @@ len <- peek fp <- peekToPlainForeignPtr "Data.Storable.Vector.Vector" (sizeOf (undefined :: a) * len) liftIO $ SV.unsafeFreeze (MSV.MVector len fp) - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store BS.ByteString where size = VarSize $ \x -> @@ -335,9 +387,6 @@ len <- peek fp <- peekToPlainForeignPtr "Data.ByteString.ByteString" len return (BS.PS fp 0 len) - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store SBS.ShortByteString where size = VarSize $ \x -> @@ -351,9 +400,6 @@ len <- peek ByteArray array <- peekToByteArray "Data.ByteString.Short.ShortByteString" len return (SBS.SBS array) - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store LBS.ByteString where -- FIXME: faster conversion? Is this ever going to be a problem? @@ -366,9 +412,6 @@ -- FIXME: more efficient implementation that avoids the double copy poke = poke . LBS.toStrict peek = fmap LBS.fromStrict peek - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store T.Text where size = VarSize $ \x -> @@ -382,9 +425,6 @@ w16Len <- peek ByteArray array <- peekToByteArray "Data.Text.Text" (2 * w16Len) return (T.Text (TA.Array array) 0 w16Len) - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} ------------------------------------------------------------------------ -- Known size instances @@ -421,9 +461,6 @@ let len = fromInteger (natVal (Proxy :: Proxy n)) fp <- peekToPlainForeignPtr ("StaticSize " ++ show len ++ " Data.ByteString.ByteString") len return (StaticSize (BS.PS fp 0 len)) - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} -- NOTE: this could be a 'Lift' instance, but we can't use type holes in -- TH. Alternatively we'd need a (TypeRep -> Type) function and Typeable @@ -446,9 +483,6 @@ size = sizeSequence poke = pokeSequence peek = peekSequence - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store a => Store (NE.NonEmpty a) @@ -456,75 +490,62 @@ size = sizeSequence poke = pokeSequence peek = peekSequence - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance (Store a, Ord a) => Store (Set a) where - size = sizeSet + size = + VarSize $ \t -> + sizeOf (undefined :: Int) + + case size of + ConstSize n -> n * Set.size t + VarSize f -> Set.foldl' (\acc a -> acc + f a) 0 t poke = pokeSet - peek = peekSet - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} + peek = Set.fromDistinctAscList <$> peek instance Store IntSet where size = sizeSet poke = pokeSet - peek = peekSet - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} + peek = IntSet.fromDistinctAscList <$> peek instance Store a => Store (IntMap a) where - size = sizeMap - poke = pokeMap - peek = peekMap - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} + size = sizeOrdMap + poke = pokeOrdMap + peek = peekOrdMapWith IntMap.fromDistinctAscList instance (Ord k, Store k, Store a) => Store (Map k a) where - size = sizeMap - poke = pokeMap - peek = peekMap - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} + size = + VarSize $ \t -> + sizeOf markMapPokedInAscendingOrder + sizeOf (undefined :: Int) + + case (size, size) of + (ConstSize nk, ConstSize na) -> (nk + na) * Map.size t + (szk, sza) -> + Map.foldlWithKey' + (\acc k a -> acc + getSizeWith szk k + getSizeWith sza a) + 0 + t + poke = pokeOrdMap + peek = peekOrdMapWith Map.fromDistinctAscList instance (Eq k, Hashable k, Store k, Store a) => Store (HashMap k a) where size = sizeMap poke = pokeMap peek = peekMap - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance (Eq a, Hashable a, Store a) => Store (HashSet a) where size = sizeSet poke = pokeSet peek = peekSet - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance (A.Ix i, Store i, Store e) => Store (A.Array i e) where -- TODO: Speed up poke and peek size = sizeArray poke = pokeArray peek = peekArray - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance (A.Ix i, A.IArray A.UArray e, Store i, Store e) => Store (A.UArray i e) where -- TODO: Speed up poke and peek size = sizeArray poke = pokeArray peek = peekArray - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} sizeArray :: (A.Ix i, A.IArray a e, Store i, Store e) => Size (a i e) sizeArray = VarSize $ \arr -> @@ -624,9 +645,6 @@ size = contramap (\(MkFixed x) -> x) (size :: Size Integer) poke (MkFixed x) = poke x peek = MkFixed <$> peek - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} -- instance Store a => Store (Tree a) where @@ -645,33 +663,21 @@ size = combineSize (\(x :% _) -> x) (\(_ :% y) -> y) poke (x :% y) = poke (x, y) peek = uncurry (:%) <$> peek - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store Time.Day where size = contramap Time.toModifiedJulianDay (size :: Size Integer) poke = poke . Time.toModifiedJulianDay peek = Time.ModifiedJulianDay <$> peek - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store Time.DiffTime where size = contramap (realToFrac :: Time.DiffTime -> Pico) (size :: Size Pico) poke = (poke :: Pico -> Poke ()) . realToFrac peek = Time.picosecondsToDiffTime <$> peek - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store Time.UTCTime where size = combineSize Time.utctDay Time.utctDayTime poke (Time.UTCTime day time) = poke (day, time) peek = uncurry Time.UTCTime <$> peek - {-# INLINE size #-} - {-# INLINE peek #-} - {-# INLINE poke #-} instance Store () instance Store a => Store (Dual a) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/Data/Store/Streaming.hs new/store-0.4.3.1/src/Data/Store/Streaming.hs --- old/store-0.3.1/src/Data/Store/Streaming.hs 2017-01-06 04:12:07.000000000 +0100 +++ new/store-0.4.3.1/src/Data/Store/Streaming.hs 2017-05-06 04:41:19.000000000 +0200 @@ -80,15 +80,13 @@ poke messageMagic poke bodyLength poke x -{-# INLINE encodeMessage #-} -- | The result of peeking at the next message can either be a -- successfully deserialised object, or a request for more input. type PeekMessage i m a = FT ((->) i) m a -needMoreInput :: Monad m => PeekMessage i m i +needMoreInput :: PeekMessage i m i needMoreInput = wrap return -{-# INLINE needMoreInput #-} -- | Given some sort of input, fills the 'ByteBuffer' with it. -- @@ -100,7 +98,6 @@ -- up the encoded message. decodeFromPtr :: (MonadIO m, Store a) => Ptr Word8 -> Int -> m a decodeFromPtr ptr n = liftIO $ decodeIOWithFromPtr peek ptr n -{-# INLINE decodeFromPtr #-} peekSized :: (MonadIO m, Store a) => FillByteBuffer i m -> ByteBuffer -> Int -> PeekMessage i m a peekSized fill bb n = go @@ -113,7 +110,6 @@ lift (fill bb needed inp) go Right ptr -> decodeFromPtr ptr n -{-# INLINE peekSized #-} -- | Read and check the magic number from a 'ByteBuffer' peekMessageMagic :: MonadIO m => FillByteBuffer i m -> ByteBuffer -> PeekMessage i m () @@ -122,12 +118,10 @@ mm | mm == messageMagic -> return () mm -> liftIO . throwIO $ PeekException 0 . T.pack $ "Wrong message magic, " ++ show mm -{-# INLINE peekMessageMagic #-} -- | Decode a 'SizeTag' from a 'ByteBuffer'. peekMessageSizeTag :: MonadIO m => FillByteBuffer i m -> ByteBuffer -> PeekMessage i m SizeTag peekMessageSizeTag fill bb = peekSized fill bb sizeTagLength -{-# INLINE peekMessageSizeTag #-} -- | Decode some object from a 'ByteBuffer', by first reading its -- header, and then the actual data. @@ -136,7 +130,6 @@ fmap Message $ do peekMessageMagic fill bb peekMessageSizeTag fill bb >>= peekSized fill bb -{-# INLINE peekMessage #-} -- | Decode a 'Message' from a 'ByteBuffer' and an action that can get -- additional inputs to refill the buffer when necessary. @@ -165,18 +158,15 @@ return Nothing where maybeDecode m = runMaybeT (iterTM (\consumeInp -> consumeInp =<< MaybeT getInp) m) -{-# INLINE decodeMessage #-} -- | Decode some 'Message' from a 'ByteBuffer', by first reading its -- header, and then the actual 'Message'. peekMessageBS :: (MonadIO m, Store a) => ByteBuffer -> PeekMessage ByteString m (Message a) peekMessageBS = peekMessage (\bb _ bs -> BB.copyByteString bb bs) -{-# INLINE peekMessageBS #-} decodeMessageBS :: (MonadIO m, Store a) => ByteBuffer -> m (Maybe ByteString) -> m (Maybe (Message a)) decodeMessageBS = decodeMessage (\bb _ bs -> BB.copyByteString bb bs) -{-# INLINE decodeMessageBS #-} #ifndef mingw32_HOST_OS @@ -193,7 +183,6 @@ peekMessageFd :: (MonadIO m, Store a) => ByteBuffer -> Fd -> PeekMessage ReadMoreData m (Message a) peekMessageFd bb fd = peekMessage (\bb_ needed ReadMoreData -> do _ <- BB.fillFromFd bb_ fd needed; return ()) bb -{-# INLINE peekMessageFd #-} -- | Decodes all the message using 'registerFd' to find out when a 'Socket' is -- ready for reading. @@ -207,17 +196,15 @@ case mbMsg of Just msg -> return msg Nothing -> liftIO (fail "decodeMessageFd: impossible: got Nothing") -{-# INLINE decodeMessageFd #-} #endif -- | Conduit for encoding 'Message's to 'ByteString's. conduitEncode :: (Monad m, Store a) => C.Conduit (Message a) m ByteString conduitEncode = C.map encodeMessage -{-# INLINE conduitEncode #-} -- | Conduit for decoding 'Message's from 'ByteString's. -conduitDecode :: (MonadIO m, MonadResource m, Store a) +conduitDecode :: (MonadResource m, Store a) => Maybe Int -- ^ Initial length of the 'ByteBuffer' used for -- buffering the incoming 'ByteString's. If 'Nothing', @@ -234,4 +221,3 @@ case mmessage of Nothing -> return () Just message -> C.yield message >> go buffer -{-# INLINE conduitDecode #-} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/Data/Store/TH/Internal.hs new/store-0.4.3.1/src/Data/Store/TH/Internal.hs --- old/store-0.3.1/src/Data/Store/TH/Internal.hs 2017-01-31 03:07:00.000000000 +0100 +++ new/store-0.4.3.1/src/Data/Store/TH/Internal.hs 2017-05-09 02:42:26.000000000 +0200 @@ -16,6 +16,7 @@ , deriveManyStorePrimVector , deriveManyStoreUnboxVector , deriveStore + , makeStore -- * Misc utilties used in Store test , getAllInstanceTypes1 , isMonoType @@ -46,7 +47,7 @@ import Safe (headMay) import TH.Derive (Deriver(..)) import TH.ReifySimple -import TH.Utilities (expectTyCon1, dequalify, plainInstanceD) +import TH.Utilities (expectTyCon1, dequalify, plainInstanceD, appsT) instance Deriver (Store a) where runDeriver _ preds ty = do @@ -54,6 +55,18 @@ dt <- reifyDataTypeSubstituted argTy (:[]) <$> deriveStore preds argTy (dtCons dt) +-- | Given the name of a type, generate a Store instance for it, +-- assuming that all type variables also need to be Store instances. +-- +-- Note that when used with datatypes that require type variables, the +-- ScopedTypeVariables extension is required. +makeStore :: Name -> Q [Dec] +makeStore name = do + dt <- reifyDataType name + let preds = map (storePred . VarT) (dtTvs dt) + argTy = appsT (ConT name) (map VarT (dtTvs dt)) + (:[]) <$> deriveStore preds argTy (dtCons dt) + deriveStore :: Cxt -> Type -> [DataCon] -> Q Dec deriveStore preds headTy cons0 = makeStoreInstance preds headTy @@ -244,16 +257,10 @@ deriveTupleStoreInstance :: Int -> Dec deriveTupleStoreInstance n = - deriveGenericInstance (map storeCxt tvs) + deriveGenericInstance (map storePred tvs) (foldl1 AppT (TupleT n : tvs)) where tvs = take n (map (VarT . mkName . (:[])) ['a'..'z']) - storeCxt ty = -#if MIN_VERSION_template_haskell(2,10,0) - AppT (ConT ''Store) ty -#else - ClassP ''Store [ty] -#endif deriveGenericInstance :: Cxt -> Type -> Dec deriveGenericInstance cs ty = plainInstanceD cs (AppT (ConT ''Store) ty) [] @@ -337,12 +344,6 @@ concatMap (map snd . dcFields) cons -} let extraPreds = map (storePred . AppT (ConT ''UV.Vector)) $ listify isVarT ty - storePred = -#if MIN_VERSION_template_haskell(2,10,0) - AppT (ConT ''Store) -#else - ClassP ''Store . (:[]) -#endif deriveStore (nub (preds ++ extraPreds)) ty cons _ -> fail "impossible case in deriveManyStoreUnboxVector" @@ -382,11 +383,8 @@ cs (AppT (ConT ''Store) ty) [ ValD (VarP 'size) (NormalB sizeExpr) [] - , PragmaD (InlineP 'size Inline FunLike AllPhases) , ValD (VarP 'peek) (NormalB peekExpr) [] - , PragmaD (InlineP 'peek Inline FunLike AllPhases) , ValD (VarP 'poke) (NormalB pokeExpr) [] - , PragmaD (InlineP 'poke Inline FunLike AllPhases) ] -- TODO: either generate random types that satisfy instances with @@ -427,3 +425,11 @@ getTyHead (ForallT _ _ x) = getTyHead x getTyHead (AppT l _) = getTyHead l getTyHead x = x + +storePred :: Type -> Pred +storePred ty = +#if MIN_VERSION_template_haskell(2,10,0) + AppT (ConT ''Store) ty +#else + ClassP ''Store [ty] +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/Data/Store/TH.hs new/store-0.4.3.1/src/Data/Store/TH.hs --- old/store-0.3.1/src/Data/Store/TH.hs 2016-10-07 01:21:44.000000000 +0200 +++ new/store-0.4.3.1/src/Data/Store/TH.hs 2017-05-09 01:45:00.000000000 +0200 @@ -2,18 +2,27 @@ -- | This module exports TH utilities intended to be useful to users. -- --- However, the visible exports do not show the main things that will be --- useful, which is using TH to generate 'Store' instances, via --- "TH.Derive". It's used like this: +-- 'makeStore' can be used to generate a 'Store' instance for types, +-- when all the type variables also require 'Store' instances. If some +-- do not, then instead use "TH.Derive" like this: -- -- @ --- data Foo = Foo Int | Bar Int +-- {-# LANGUAGE TemplateHaskell #-} +-- {-# LANGUAGE ScopedTypeVariables #-} +-- +-- import TH.Derive +-- import Data.Store +-- +-- data Foo a = Foo a | Bar Int -- -- $($(derive [d| --- instance Deriving (Store Foo) +-- instance Store a => Deriving (Store (Foo a)) -- |])) -- @ -- +-- Note that when used with datatypes that require type variables, the +-- ScopedTypeVariables extension is required. +-- -- One advantage of using this Template Haskell definition of Store -- instances is that in some cases they can be faster than the instances -- defined via Generics. Specifically, sum types which can yield @@ -21,9 +30,9 @@ -- The instances generated via generics always use 'VarSize' for sum -- types. module Data.Store.TH - ( + ( makeStore -- * Testing Store instances - smallcheckManyStore + , smallcheckManyStore , checkRoundtrip , assertRoundtrip ) where @@ -37,6 +46,7 @@ import Test.Hspec import Test.Hspec.SmallCheck (property) import Test.SmallCheck +import Data.Store.TH.Internal (makeStore) ------------------------------------------------------------------------ -- Testing diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/Data/Store/Version.hs new/store-0.4.3.1/src/Data/Store/Version.hs --- old/store-0.3.1/src/Data/Store/Version.hs 2016-08-05 03:21:03.000000000 +0200 +++ new/store-0.4.3.1/src/Data/Store/Version.hs 2017-03-04 07:37:56.000000000 +0100 @@ -23,16 +23,13 @@ -- will be minimized when directly feasible. module Data.Store.Version ( StoreVersion(..) - , WithVersion(..) , VersionConfig(..) , hashedVersionConfig , namedVersionConfig - , wrapVersion - , checkVersion - , VersionCheckException(..) + , encodeWithVersionQ + , decodeWithVersionQ ) where -import Control.Exception import Control.Monad import Control.Monad.Trans.State import qualified Crypto.Hash.SHA1 as SHA1 @@ -48,6 +45,7 @@ import Data.Text.Encoding.Error (lenientDecode) import qualified Data.Text.IO as T import Data.Typeable.Internal (TypeRep(..)) +import Data.Word (Word32) import GHC.Generics (Generic) import Language.Haskell.TH import System.Directory @@ -59,11 +57,6 @@ newtype StoreVersion = StoreVersion { unStoreVersion :: BS.ByteString } deriving (Eq, Show, Ord, Data, Typeable, Generic, Store) -data WithVersion a = WithVersion a StoreVersion - deriving (Eq, Show, Ord, Data, Typeable, Generic) - -instance Store a => Store (WithVersion a) - -- | Configuration for the version checking of a particular type. data VersionConfig a = VersionConfig { vcExpectedHash :: Maybe String @@ -92,13 +85,13 @@ , vcRenames = M.empty } -wrapVersion :: Data a => VersionConfig a -> Q Exp -wrapVersion = impl Wrap +encodeWithVersionQ :: Data a => VersionConfig a -> Q Exp +encodeWithVersionQ = impl Encode -checkVersion :: Data a => VersionConfig a -> Q Exp -checkVersion = impl Check +decodeWithVersionQ :: Data a => VersionConfig a -> Q Exp +decodeWithVersionQ = impl Decode -data WhichFunc = Wrap | Check +data WhichFunc = Encode | Decode impl :: forall a. Data a => WhichFunc -> VersionConfig a -> Q Exp impl wf vc = do @@ -131,15 +124,16 @@ ", but " ++ show expectedHash ++ " is specified.\n" ++ "The data used to construct the hash has been written to " ++ show newPath ++ extraMsg ++ "\n" + let atype = typeRepToType (typeRep proxy) case wf of - Wrap -> [e| (\x -> (x :: $(typeRepToType (typeRep proxy))) `WithVersion` $(version)) |] - Check -> [e| (\(WithVersion x gotVersion) -> - if gotVersion /= $(version) - then Left (VersionCheckException - { expectedVersion = $(version) - , receivedVersion = gotVersion - }) - else Right x) |] + Encode -> [e| \x -> ( getSize markEncodedVersion + getSize $(version) + getSize x + , poke markEncodedVersion >> poke $(version) >> poke (x :: $(atype))) |] + Decode -> [e| do + peekMagic "version tag" markEncodedVersion + gotVersion <- peek + if gotVersion /= $(version) + then fail (displayVersionError $(version) gotVersion) + else peek :: Peek $(atype) |] {- txtWithComments <- runIO $ T.readFile path @@ -286,26 +280,11 @@ tyConOf :: Typeable a => Proxy a -> TyCon tyConOf = typeRepTyCon . typeRep -data VersionCheckException = VersionCheckException - { expectedVersion :: StoreVersion - , receivedVersion :: StoreVersion - } deriving -#if MIN_VERSION_base(4,8,0) - (Typeable, Show) - -instance Exception VersionCheckException where - displayException = displayVCE -#else - (Typeable) - -instance Show VersionCheckException where - show = displayVCE - -instance Exception VersionCheckException -#endif - -displayVCE :: VersionCheckException -> String -displayVCE VersionCheckException{..} = +displayVersionError :: StoreVersion -> StoreVersion -> String +displayVersionError expectedVersion receivedVersion = "Mismatch detected by Data.Store.Version - expected " ++ T.unpack (decodeUtf8With lenientDecode (unStoreVersion expectedVersion)) ++ " but got " ++ T.unpack (decodeUtf8With lenientDecode (unStoreVersion receivedVersion)) + +markEncodedVersion :: Word32 +markEncodedVersion = 3908297288 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/src/System/IO/ByteBuffer.hs new/store-0.4.3.1/src/System/IO/ByteBuffer.hs --- old/store-0.3.1/src/System/IO/ByteBuffer.hs 2016-11-16 22:01:15.000000000 +0100 +++ new/store-0.4.3.1/src/System/IO/ByteBuffer.hs 2017-05-06 04:42:18.000000000 +0200 @@ -142,7 +142,6 @@ writeIORef bb (Left $ ByteBufferException loc (show e)) Left _ -> return () throwIO e -{-# INLINE bbHandler #-} -- | Try to use the 'BBRef' of a 'ByteBuffer', or throw a 'ByteBufferException' if it's invalid. useBBRef :: (BBRef -> IO a) -> ByteBuffer -> IO a @@ -185,7 +184,6 @@ , contained = 0 , consumed = 0 } -{-# INLINE new #-} -- | Free a byte buffer. free :: MonadIO m => ByteBuffer -> m () @@ -195,7 +193,6 @@ writeIORef bb $ Left (ByteBufferException "free" "ByteBuffer has explicitly been freed and is no longer valid.") Left _ -> return () -- the ByteBuffer is either invalid or has already been freed. -{-# INLINE free #-} -- | Perform some action with a bytebuffer, with automatic allocation -- and deallocation. @@ -224,7 +221,6 @@ , consumed = 0 , ptr = ptr bbref } -{-# INLINE resetBBRef #-} -- | Make sure the buffer is at least @minSize@ bytes long. -- @@ -246,7 +242,6 @@ , consumed = consumed bbref , ptr = ptr' } -{-# INLINE enlargeBBRef #-} -- | Copy the contents of a 'ByteString' to a 'ByteBuffer'. -- @@ -277,7 +272,6 @@ , contained = contained bbref'' + bsSize , consumed = consumed bbref'' , ptr = ptr bbref''} -{-# INLINE copyByteString #-} #ifndef mingw32_HOST_OS @@ -295,7 +289,6 @@ (bbref', readBytes) <- fillBBRefFromFd sock bbref maxBytes writeIORef bb $ Right bbref' return readBytes -{-# INLINE fillFromFd #-} {- Note: I'd like to use these two definitions: @@ -352,7 +345,6 @@ else do let bbref' = bbref{ contained = contained + bytes } go (readBytes + bytes) bbref' -{-# INLINE fillBBRefFromFd #-} foreign import ccall unsafe "recv" -- c_recv returns -1 in the case of errors. @@ -386,7 +378,6 @@ else do writeIORef bb $ Right bbref { consumed = consumed bbref + n } return $ Right (ptr bbref `plusPtr` consumed bbref) -{-# INLINE unsafeConsume #-} -- | As `unsafeConsume`, but instead of returning a `Ptr` into the -- contents of the `ByteBuffer`, it returns a `ByteString` containing @@ -404,7 +395,6 @@ bs <- liftIO $ createBS ptr n return (Right bs) Left missing -> return (Left missing) -{-# INLINE consume #-} {-@ createBS :: p:(Ptr Word8) -> {v:Nat | v <= plen p} -> IO ByteString @-} createBS :: Ptr Word8 -> Int -> IO ByteString @@ -412,7 +402,6 @@ fp <- mallocForeignPtrBytes n withForeignPtr fp (\p -> copyBytes p ptr n) return (BS.PS fp 0 n) -{-# INLINE createBS #-} -- below are liquid haskell qualifiers, and specifications for external functions. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/store.cabal new/store-0.4.3.1/store.cabal --- old/store-0.3.1/store.cabal 2017-02-16 07:29:02.000000000 +0100 +++ new/store-0.4.3.1/store.cabal 2017-05-09 02:44:12.000000000 +0200 @@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack name: store -version: 0.3.1 +version: 0.4.3.1 synopsis: Fast binary serialization category: Serialization, Data homepage: https://github.com/fpco/store#readme @@ -37,7 +37,7 @@ ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -O2 build-depends: base >=4.7 && <5 - , store-core >=0.3 && <0.4 + , store-core >=0.4 && <0.5 , th-utilities >=0.2 , primitive >=0.6 , th-reify-many >=0.1.6 @@ -102,7 +102,7 @@ ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -O2 -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 - , store-core >=0.3 && <0.4 + , store-core >=0.4 && <0.5 , th-utilities >=0.2 , primitive >=0.6 , th-reify-many >=0.1.6 @@ -160,7 +160,7 @@ ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -O2 -threaded -rtsopts -with-rtsopts=-N -with-rtsopts=-T -O2 build-depends: base >=4.7 && <5 - , store-core >=0.3 && <0.4 + , store-core >=0.4 && <0.5 , th-utilities >=0.2 , primitive >=0.6 , th-reify-many >=0.1.6 @@ -218,7 +218,7 @@ ghc-options: -Wall -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -O2 -threaded -rtsopts -with-rtsopts=-N1 -with-rtsopts=-s -with-rtsopts=-qg build-depends: base >=4.7 && <5 - , store-core >=0.3 && <0.4 + , store-core >=0.4 && <0.5 , th-utilities >=0.2 , primitive >=0.6 , th-reify-many >=0.1.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/test/Allocations.hs new/store-0.4.3.1/test/Allocations.hs --- old/store-0.3.1/test/Allocations.hs 2016-10-27 00:36:13.000000000 +0200 +++ new/store-0.4.3.1/test/Allocations.hs 2017-02-28 06:37:20.000000000 +0100 @@ -7,8 +7,11 @@ module Main where import Control.DeepSeq -import Data.List +import qualified Data.IntMap.Strict as IntMap +import qualified Data.IntSet as IntSet import qualified Data.Serialize as Cereal +import qualified Data.Set as Set +import qualified Data.Map.Strict as Map import qualified Data.Store as Store import qualified Data.Vector as Boxed import qualified Data.Vector.Serialize () @@ -19,26 +22,39 @@ -- | Main entry point. main :: IO () main = - mainWith encoding + mainWith weighing --- | Weigh encoding with Store vs Cereal. -encoding :: Weigh () -encoding = +-- | Weigh weighing with Store vs Cereal. +weighing :: Weigh () +weighing = do fortype "[Int]" (\n -> replicate n 0 :: [Int]) fortype "Boxed Vector Int" (\n -> Boxed.replicate n 0 :: Boxed.Vector Int) fortype "Storable Vector Int" (\n -> Storable.replicate n 0 :: Storable.Vector Int) + fortype "Set Int" (Set.fromDistinctAscList . ints) + fortype "IntSet" (IntSet.fromDistinctAscList . ints) + fortype "Map Int Int" (Map.fromDistinctAscList . intpairs) + fortype "IntMap Int" (IntMap.fromDistinctAscList . intpairs) where fortype label make = scale (\(n,nstr) -> do let title :: String -> String title for = printf "%12s %-20s %s" nstr (label :: String) for + encodeDecode en de = + (return . (`asTypeOf` make n) . de . force . en . make) n action (title "Allocate") (return (make n)) action (title "Encode: Store") (return (Store.encode (force (make n)))) action (title "Encode: Cereal") - (return (Cereal.encode (force (make n))))) - scale func = - mapM_ func + (return (Cereal.encode (force (make n)))) + action (title "Encode/Decode: Store") + (encodeDecode Store.encode Store.decodeEx) + action (title "Encode/Decode: Cereal") + (encodeDecode Cereal.encode (fromRight . Cereal.decode))) + scale f = + mapM_ f (map (\x -> (x,commas x)) [1000000,2000000,10000000]) + ints n = [1..n] :: [Int] + intpairs = map (\x -> (x, x)) . ints + fromRight = either (error "Left") id diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/store-0.3.1/test/Data/StoreSpec.hs new/store-0.4.3.1/test/Data/StoreSpec.hs --- old/store-0.3.1/test/Data/StoreSpec.hs 2016-11-16 22:52:41.000000000 +0100 +++ new/store-0.4.3.1/test/Data/StoreSpec.hs 2017-03-04 08:07:44.000000000 +0100 @@ -307,7 +307,7 @@ , [t| X |] ]) describe "Manually listed polymorphic store instances" - $(smallcheckManyStore verbose 2 + $(smallcheckManyStore verbose 4 [ [t| SV.Vector Int8 |] , [t| V.Vector Int8 |] , [t| SerialRatio Int8 |] @@ -396,6 +396,12 @@ assertRoundtrip verbose (250 :: Word8, 40918 :: Word16, 120471416 :: Word32) assertRoundtrip verbose (250 :: Word8, 10.1 :: Float, 8697.65 :: Double) (return () :: IO ()) + it "Expects the right marker when deserializing ordered maps (#97)" $ do + let m = mapFromList [(1, ()), (2, ()), (3, ())] :: HashMap Int () + bs = encode m + (decodeEx bs :: HashMap Int ()) `shouldBe` m + evaluate (decodeEx bs :: Map Int ()) `shouldThrow` isUnexpectedMarkerException + evaluate (decodeEx bs :: IntMap ()) `shouldThrow` isUnexpectedMarkerException isPokeException :: Test.Hspec.Selector PokeException isPokeException = const True @@ -405,3 +411,7 @@ isTooManyBytesException :: Test.Hspec.Selector PeekException isTooManyBytesException (PeekException _ t) = "Attempted to read too many bytes" `T.isPrefixOf` t + +isUnexpectedMarkerException :: Test.Hspec.Selector PeekException +isUnexpectedMarkerException (PeekException _ t) = + "Expected marker for ascending Map / IntMap: " `T.isPrefixOf` t
participants (1)
-
root@hilbert.suse.de