on master help sign in
Fix the Eq instance of Array
23750f3Harendra Kumar
core/src/Streamly/Internal/Data/Array/Type.hs+64−6
@@ -84,6 +84,8 @@ module Streamly.Internal.Data.Array.Type
84 , write
84 , write
85 , pinnedWrite
85 , pinnedWrite
86 , unsafeMakePure
86 , unsafeMakePure
87+ , byteCmp
88+ , byteEq
87
89
88 -- ** Streams of arrays
90 -- ** Streams of arrays
89 , chunksOf
91 , chunksOf
@@ -104,8 +106,9 @@ import Control.Exception (assert)
104import Control.Monad (replicateM)
106import Control.Monad (replicateM)
105import Control.Monad.IO.Class (MonadIO(..))
107import Control.Monad.IO.Class (MonadIO(..))
106import Data.Functor.Identity (Identity(..))
108import Data.Functor.Identity (Identity(..))
109+import Data.Int (Int8, Int16, Int32, Int64)
107import Data.Proxy (Proxy(..))
110import Data.Proxy (Proxy(..))
108−import Data.Word (Word8)
111+import Data.Word (Word8, Word16, Word32, Word64)
109import GHC.Base (build)
112import GHC.Base (build)
110import GHC.Exts (IsList, IsString(..), Addr#)
113import GHC.Exts (IsList, IsString(..), Addr#)
111
114
@@ -254,7 +257,10 @@ isPinned = MA.isPinned . unsafeThaw
254-- Construction
257-- Construction
255-------------------------------------------------------------------------------
258-------------------------------------------------------------------------------
256
259
257−-- Splice two immutable arrays creating a new array.
260+-- | Copy two immutable arrays into a new array. If you want to splice more
261+-- than two arrays then this operation would be highly inefficient because it
262+-- would make a copy on every splice operation, instead use the
263+-- 'fromArrayStreamK' operation to combine n immutable arrays.
258{-# INLINE splice #-}
264{-# INLINE splice #-}
259splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
265splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
260splice arr1 arr2 =
266splice arr1 arr2 =
@@ -687,12 +693,64 @@ instance Unbox a => IsList (Array a) where
687 {-# INLINE toList #-}
693 {-# INLINE toList #-}
688 toList = toList
694 toList = toList
689
695
690−-- XXX we are assuming that Unboxed equality means element equality. This may
696+-- | Byte compare two arrays. Compare the length of the arrays. If the length
691−-- or may not be correct? arrcmp is 40% faster compared to stream equality.
697+-- is equal, compare the lexicographical ordering of two underlying byte arrays
698+-- otherwise return the result of length comparison.
699+--
700+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of
701+-- different sizes may leave some memory uninitialized which can make byte
702+-- comparison unreliable.
703+--
704+-- /Pre-release/
705+{-# INLINE byteCmp #-}
706+byteCmp :: Array a -> Array a -> Ordering
707+byteCmp arr1 arr2 =
708+ unsafeInlineIO $! unsafeThaw arr1 `MA.byteCmp` unsafeThaw arr2
709+
710+-- | Byte equality of two arrays.
711+--
712+-- >>> byteEq arr1 arr2 = (==) EQ $ Array.byteCmp arr1 arr2
713+--
714+-- /Unsafe/: See 'byteCmp'.
715+{-# INLINE byteEq #-}
716+byteEq :: Array a -> Array a -> Bool
717+byteEq arr1 arr2 = (==) EQ $ byteCmp arr1 arr2
718+
719+#define MK_EQ_INSTANCE(typ) \
720+instance {-# OVERLAPPING #-} Eq (Array typ) where { \
721+; {-# INLINE (==) #-} \
722+; (==) = byteEq \
723+}
724+
725+MK_EQ_INSTANCE(Char)
726+MK_EQ_INSTANCE(Word8)
727+MK_EQ_INSTANCE(Word16)
728+MK_EQ_INSTANCE(Word32)
729+MK_EQ_INSTANCE(Word64)
730+MK_EQ_INSTANCE(Int)
731+MK_EQ_INSTANCE(Int8)
732+MK_EQ_INSTANCE(Int16)
733+MK_EQ_INSTANCE(Int32)
734+MK_EQ_INSTANCE(Int64)
735+
736+-- | If the type allows a byte-by-byte comparison (see 'byteCmp') this instance
737+-- can be overlapped by a more specific instance that uses a byte comparison.
738+-- Byte comparison can be significantly faster.
739+--
740+-- For example:
741+--
742+-- >>> (==) = byteCmp
743+--
692−instance (Unbox a, Eq a) => Eq (Array a) where
744+instance {-# OVERLAPPABLE #-} (Unbox a, Eq a) => Eq (Array a) where
693 {-# INLINE (==) #-}
745 {-# INLINE (==) #-}
694 arr1 == arr2 =
746 arr1 == arr2 =
747+ -- Does unboxed byte equality mean element equality?
748+ -- XXX This is incorrect for sum types, as we may have some
749+ -- uninitialized memory in that case. If we always initialize the
750+ -- unused memory to zero we can use this.
751+ -- Byte comparison is 40% faster compared to stream equality.
695− (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
752+ -- (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
753+ (toStreamD arr1 :: Stream Identity a) == toStreamD arr2
696
754
697instance (Unbox a, Ord a) => Ord (Array a) where
755instance (Unbox a, Ord a) => Ord (Array a) where
698 {-# INLINE compare #-}
756 {-# INLINE compare #-}
core/src/Streamly/Internal/Data/MutArray/Type.hs+26−6
@@ -181,6 +181,8 @@ module Streamly.Internal.Data.MutArray.Type
181 , foldl'
181 , foldl'
182 , foldr
182 , foldr
183 , cmp
183 , cmp
184+ , byteCmp
185+ , byteEq
184
186
185 -- ** Arrays of arrays
187 -- ** Arrays of arrays
186 -- We can add dimensionality parameter to the array type to get
188 -- We can add dimensionality parameter to the array type to get
@@ -2424,14 +2426,18 @@ asUnpinnedPtrUnsafe arr f =
2424-- Equality
2426-- Equality
2425-------------------------------------------------------------------------------
2427-------------------------------------------------------------------------------
2426
2428
2427−-- | Compare the length of the arrays. If the length is equal, compare the
2429+-- | Byte compare two arrays. Compare the length of the arrays. If the length
2428−-- lexicographical ordering of two underlying byte arrays otherwise return the
2430+-- is equal, compare the lexicographical ordering of two underlying byte arrays
2429−-- result of length comparison.
2431+-- otherwise return the result of length comparison.
2432+--
2433+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of
2434+-- different sizes may leave some memory uninitialized which can make byte
2435+-- comparison unreliable.
2430--
2436--
2431-- /Pre-release/
2437-- /Pre-release/
2432−{-# INLINE cmp #-}
2438+{-# INLINE byteCmp #-}
2433−cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2439+byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2434−cmp arr1 arr2 =
2440+byteCmp arr1 arr2 =
2435 liftIO
2441 liftIO
2436 $ do
2442 $ do
2437 let marr1 = getMutableByteArray# (arrContents arr1)
2443 let marr1 = getMutableByteArray# (arrContents arr1)
@@ -2454,6 +2460,20 @@ cmp arr1 arr2 =
2454 return $ compare r 0
2460 return $ compare r 0
2455 x -> return x
2461 x -> return x
2456
2462
2463+{-# INLINE cmp #-}
2464+{-# DEPRECATED cmp "Please use byteCmp instead." #-}
2465+cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2466+cmp = byteCmp
2467+
2468+-- | Byte equality of two arrays.
2469+--
2470+-- >>> byteEq arr1 arr2 = (==) EQ $ MArray.byteCmp arr1 arr2
2471+--
2472+-- /Unsafe/: See 'byteCmp'.
2473+{-# INLINE byteEq #-}
2474+byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
2475+byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2
2476+
2457-------------------------------------------------------------------------------
2477-------------------------------------------------------------------------------
2458-- NFData
2478-- NFData
2459-------------------------------------------------------------------------------
2479-------------------------------------------------------------------------------