diff --git a/Data/ByteString.hs b/Data/ByteString.hs index 2e04fd542..b486775dd 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -167,6 +167,7 @@ module Data.ByteString ( -- * Zipping and unzipping ByteStrings zip, -- :: ByteString -> ByteString -> [(Word8,Word8)] zipWith, -- :: (Word8 -> Word8 -> c) -> ByteString -> ByteString -> [c] + packZipWith, -- :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString unzip, -- :: [(Word8,Word8)] -> (ByteString,ByteString) -- * Ordered ByteStrings @@ -1622,14 +1623,10 @@ zipWith f ps qs | otherwise = f (unsafeHead ps) (unsafeHead qs) : zipWith f (unsafeTail ps) (unsafeTail qs) {-# NOINLINE [1] zipWith #-} --- --- | A specialised version of zipWith for the common case of a --- simultaneous map over two bytestrings, to build a 3rd. Rewrite rules --- are used to automatically covert zipWith into zipWith' when a pack is --- performed on the result of zipWith. --- -zipWith' :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString -zipWith' f (BS fp l) (BS fq m) = unsafeDupablePerformIO $ +-- | A specialised version of `zipWith` for the common case of a +-- simultaneous map over two ByteStrings, to build a 3rd. +packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString +packZipWith f (BS fp l) (BS fq m) = unsafeDupablePerformIO $ withForeignPtr fp $ \a -> withForeignPtr fq $ \b -> create len $ go a b @@ -1646,11 +1643,11 @@ zipWith' f (BS fp l) (BS fq m) = unsafeDupablePerformIO $ zipWith_ (n+1) r len = min l m -{-# INLINE zipWith' #-} +{-# INLINE packZipWith #-} {-# RULES "ByteString specialise zipWith" forall (f :: Word8 -> Word8 -> Word8) p q . - zipWith f p q = unpack (zipWith' f p q) + zipWith f p q = unpack (packZipWith f p q) #-} -- | /O(n)/ 'unzip' transforms a list of pairs of bytes into a pair of diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs index a6563e891..e1316ff67 100644 --- a/Data/ByteString/Char8.hs +++ b/Data/ByteString/Char8.hs @@ -176,6 +176,7 @@ module Data.ByteString.Char8 ( -- * Zipping and unzipping ByteStrings zip, -- :: ByteString -> ByteString -> [(Char,Char)] zipWith, -- :: (Char -> Char -> c) -> ByteString -> ByteString -> [c] + packZipWith, -- :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString unzip, -- :: [(Char,Char)] -> (ByteString,ByteString) -- * Ordered ByteStrings @@ -837,6 +838,14 @@ zip ps qs zipWith :: (Char -> Char -> a) -> ByteString -> ByteString -> [a] zipWith f = B.zipWith ((. w2c) . f . w2c) +-- | A specialised version of `zipWith` for the common case of a +-- simultaneous map over two ByteStrings, to build a 3rd. +packZipWith :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString +packZipWith f = B.packZipWith f' + where + f' c1 c2 = c2w $ f (w2c c1) (w2c c2) +{-# INLINE packZipWith #-} + -- | 'unzip' transforms a list of pairs of Chars into a pair of -- ByteStrings. Note that this performs two 'pack' operations. unzip :: [(Char,Char)] -> (ByteString,ByteString) diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs index 983ec6905..15f386a01 100644 --- a/Data/ByteString/Lazy.hs +++ b/Data/ByteString/Lazy.hs @@ -178,6 +178,7 @@ module Data.ByteString.Lazy ( -- * Zipping and unzipping ByteStrings zip, -- :: ByteString -> ByteString -> [(Word8,Word8)] zipWith, -- :: (Word8 -> Word8 -> c) -> ByteString -> ByteString -> [c] + packZipWith, -- :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString unzip, -- :: [(Word8,Word8)] -> (ByteString,ByteString) -- * Ordered ByteStrings @@ -1131,6 +1132,18 @@ zipWith f (Chunk a as) (Chunk b bs) = go a as b bs to _ (Chunk x' xs) y ys | not (S.null y) = go x' xs y ys to _ (Chunk x' xs) _ (Chunk y' ys) = go x' xs y' ys +-- | A specialised version of `zipWith` for the common case of a +-- simultaneous map over two ByteStrings, to build a 3rd. +packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString +packZipWith _ Empty _ = Empty +packZipWith _ _ Empty = Empty +packZipWith f (Chunk a@(S.BS _ al) as) (Chunk b@(S.BS _ bl) bs) = Chunk (S.packZipWith f a b) $ + case compare al bl of + LT -> packZipWith f as $ Chunk (S.drop al b) bs + EQ -> packZipWith f as bs + GT -> packZipWith f (Chunk (S.drop bl a) as) bs +{-# INLINE packZipWith #-} + -- | /O(n)/ 'unzip' transforms a list of pairs of bytes into a pair of -- ByteStrings. Note that this performs two 'pack' operations. unzip :: [(Word8,Word8)] -> (ByteString,ByteString) diff --git a/Data/ByteString/Lazy/Char8.hs b/Data/ByteString/Lazy/Char8.hs index ee0da8b5a..731320442 100644 --- a/Data/ByteString/Lazy/Char8.hs +++ b/Data/ByteString/Lazy/Char8.hs @@ -157,6 +157,7 @@ module Data.ByteString.Lazy.Char8 ( -- * Zipping and unzipping ByteStrings zip, -- :: ByteString -> ByteString -> [(Char,Char)] zipWith, -- :: (Char -> Char -> c) -> ByteString -> ByteString -> [c] + packZipWith, -- :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString -- unzip, -- :: [(Char,Char)] -> (ByteString,ByteString) -- * Ordered ByteStrings @@ -683,6 +684,14 @@ zip ps qs zipWith :: (Char -> Char -> a) -> ByteString -> ByteString -> [a] zipWith f = L.zipWith ((. w2c) . f . w2c) +-- | A specialised version of `zipWith` for the common case of a +-- simultaneous map over two ByteStrings, to build a 3rd. +packZipWith :: (Char -> Char -> Char) -> ByteString -> ByteString -> ByteString +packZipWith f = L.packZipWith f' + where + f' c1 c2 = c2w $ f (w2c c1) (w2c c2) +{-# INLINE packZipWith #-} + -- | 'lines' breaks a ByteString up into a list of ByteStrings at -- newline Chars (@'\\n'@). The resulting strings do not contain newlines. -- diff --git a/tests/Properties.hs b/tests/Properties.hs index bb1fdfa0a..03f2a7207 100644 --- a/tests/Properties.hs +++ b/tests/Properties.hs @@ -439,6 +439,8 @@ prop_unfoldrBL = ((\n f a -> take n $ unfoldr f a) :: Int -> (X -> Maybe (W,X)) -> X -> [W]) +prop_packZipWithBL = L.packZipWith `eq3` (zipWith :: (W -> W -> W) -> [W] -> [W] -> [W]) + -- -- And finally, check correspondance between Data.ByteString and List -- @@ -540,7 +542,7 @@ prop_scanr1CL f = eqnotnull2 (scanr1 :: (Char -> Char -> Char) -> [Char] -> [Char]) (castFn f) --- prop_zipWithPL' = P.zipWith' `eq3` (zipWith :: (W -> W -> W) -> [W] -> [W] -> [W]) +prop_packZipWithPL = P.packZipWith `eq3` (zipWith :: (W -> W -> W) -> [W] -> [W] -> [W]) prop_zipWithPL = (P.zipWith :: (W -> W -> X) -> P -> P -> [X]) `eq3` (zipWith :: (W -> W -> X) -> [W] -> [W] -> [X]) @@ -1350,7 +1352,12 @@ prop_zip1BB xs ys = P.zip xs ys == zip (P.unpack xs) (P.unpack ys) prop_zipWithBB xs ys = P.zipWith (,) xs ys == P.zip xs ys prop_zipWithCC xs ys = C.zipWith (,) xs ys == C.zip xs ys prop_zipWithLC xs ys = LC.zipWith (,) xs ys == LC.zip xs ys --- prop_zipWith'BB xs ys = P.pack (P.zipWith (+) xs ys) == P.zipWith' (+) xs ys + +prop_packZipWithBB f xs ys = P.pack (P.zipWith f xs ys) == P.packZipWith f xs ys +prop_packZipWithLL f xs ys = L.pack (L.zipWith f xs ys) == L.packZipWith f xs ys +prop_packZipWithBC f xs ys = C.pack (C.zipWith f xs ys) == C.packZipWith f xs ys +prop_packZipWithLC f xs ys = LC.pack (LC.zipWith f xs ys) == LC.packZipWith f xs ys + prop_unzipBB x = let (xs,ys) = unzip x in (P.pack xs, P.pack ys) == P.unzip x @@ -1887,6 +1894,7 @@ bl_tests = , testProperty "elemIndexEnd"prop_elemIndexEndBL , testProperty "elemIndices" prop_elemIndicesBL , testProperty "concatMap" prop_concatMapBL + , testProperty "zipWith/packZipWithLazy" prop_packZipWithBL ] ------------------------------------------------------------------------ @@ -2076,10 +2084,9 @@ pl_tests = , testProperty "unzip" prop_unzipPL , testProperty "unzip" prop_unzipLL , testProperty "unzip" prop_unzipCL - , testProperty "zipWith" prop_zipWithPL --- , testProperty "zipWith" prop_zipWithCL - , testProperty "zipWith rules" prop_zipWithPL_rules --- , testProperty "zipWith/zipWith'" prop_zipWithPL' + , testProperty "zipWithPL" prop_zipWithPL + , testProperty "zipWithPL rules" prop_zipWithPL_rules + , testProperty "packZipWithPL" prop_packZipWithPL , testProperty "isPrefixOf" prop_isPrefixOfPL , testProperty "isSuffixOf" prop_isSuffixOfPL @@ -2330,10 +2337,13 @@ bb_tests = , testProperty "zip" prop_zipBB , testProperty "zip" prop_zipLC , testProperty "zip1" prop_zip1BB - , testProperty "zipWith" prop_zipWithBB - , testProperty "zipWith" prop_zipWithCC - , testProperty "zipWith" prop_zipWithLC --- , testProperty "zipWith'" prop_zipWith'BB + , testProperty "zipWithBB" prop_zipWithBB + , testProperty "zipWithCC" prop_zipWithCC + , testProperty "zipWithLC" prop_zipWithLC + , testProperty "packZipWithBB" prop_packZipWithBB + , testProperty "packZipWithLL" prop_packZipWithLL + , testProperty "packZipWithBC" prop_packZipWithBC + , testProperty "packZipWithLC" prop_packZipWithLC , testProperty "unzip" prop_unzipBB , testProperty "concatMap" prop_concatMapBB -- , testProperty "join/joinByte" prop_join_spec