@@ -145,6 +145,9 @@ plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b
145plusForeignPtr (ForeignPtr addr c) (I# d) = ForeignPtr (plusAddr# addr d) c
145plusForeignPtr (ForeignPtr addr c) (I# d) = ForeignPtr (plusAddr# addr d) c
149+-- >>> import qualified Streamly.Internal.Data.Stream.StreamD as StreamD
148-------------------------------------------------------------------------------
151-------------------------------------------------------------------------------
149-- Array Data Type
152-- Array Data Type
150-------------------------------------------------------------------------------
153-------------------------------------------------------------------------------
@@ -158,6 +161,8 @@ foreign import ccall unsafe "string.h memchr" c_memchr
158foreign import ccall unsafe "string.h memcmp" c_memcmp
161foreign import ccall unsafe "string.h memcmp" c_memcmp
159 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
162 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
164+-- | Given a 'Storable' type (unused first arg) and a number of bytes, return
165+-- how many elements of that type are required to hold those bytes.
161{-# INLINE bytesToElemCount #-}
166{-# INLINE bytesToElemCount #-}
162bytesToElemCount :: Storable a => a -> Int -> Int
167bytesToElemCount :: Storable a => a -> Int -> Int
163bytesToElemCount x n =
168bytesToElemCount x n =
@@ -176,6 +181,7 @@ memcmp p1 p2 len = do
176 r <- c_memcmp p1 p2 (fromIntegral len)
181 r <- c_memcmp p1 p2 (fromIntegral len)
177 return $ r == 0
182 return $ r == 0
184+-- | Same as unsafeWithForeignPtr but lifted to any MonadIO monad.
179{-# INLINE unsafeWithForeignPtrM #-}
185{-# INLINE unsafeWithForeignPtrM #-}
180unsafeWithForeignPtrM :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b
186unsafeWithForeignPtrM :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b
181unsafeWithForeignPtrM fo f = do
187unsafeWithForeignPtrM fo f = do
@@ -193,8 +199,25 @@ unsafeWithForeignPtrM fo f = do
193-- be automatically provided to a function that pattern matches on the Array
199-- be automatically provided to a function that pattern matches on the Array
194-- type. However, it has huge performance cost, so we do not use it.
200-- type. However, it has huge performance cost, so we do not use it.
195-- Investigate a GHC improvement possiblity.
201-- Investigate a GHC improvement possiblity.
197-- XXX Rename the fields to better names.
203-- XXX Rename the fields to better names.
205+-- | An unboxed, pinned mutable array. An array is created with a given length
206+-- and capacity. Length is the number of valid elements in the array. Capacity
207+-- is the maximum number of elements that the array can be expanded to without
208+-- having to reallocate the memory.
210+-- The elements in the array can be mutated in-place without changing the
211+-- reference (constructor). However, the length of the array cannot be mutated
212+-- in-place. A new array reference is generated when the length changes. When
213+-- the length is increased (upto the maximum reserved capacity of the array),
214+-- the array is not reallocated and the new reference uses the same underlying
215+-- memory as the old one.
217+-- Several routines in this module allow the programmer to control the capacity
218+-- of the array. The programmer can control the trade-off between memory usage
219+-- and performance impact due to reallocations when growing or shrinking the
199data Array a =
222data Array a =
200#ifdef DEVBUILD
223#ifdef DEVBUILD
@@ -218,7 +241,7 @@ mutableArray = Array
218-- Construction
241-- Construction
219-------------------------------------------------------------------------------
242-------------------------------------------------------------------------------
221−-- | allocate a new array using the provided allocator function.
244+-- | allocate a new empty array using the provided allocator function.
222{-# INLINE newArrayAlignedAllocWith #-}
245{-# INLINE newArrayAlignedAllocWith #-}
223newArrayAlignedAllocWith :: forall m a. (MonadIO m, Storable a)
246newArrayAlignedAllocWith :: forall m a. (MonadIO m, Storable a)
224 => (Int -> Int -> m (ForeignPtr a)) -> Int -> Int -> m (Array a)
247 => (Int -> Int -> m (ForeignPtr a)) -> Int -> Int -> m (Array a)
@@ -232,9 +255,9 @@ newArrayAlignedAllocWith alloc alignSize count = do
232 , aBound = p `plusPtr` size
255 , aBound = p `plusPtr` size
235−-- | Allocate a new array aligned to the specified alignmend and using
258+-- | Allocate a new empty array using the specified alignment and using
236-- unmanaged pinned memory. The memory will not be automatically freed by GHC.
259-- unmanaged pinned memory. The memory will not be automatically freed by GHC.
237−-- This could be useful in allocate once global data structures. Use carefully
260+-- This could be useful in allocate-once global data structures. Use carefully
238-- as incorrect use can lead to memory leak.
261-- as incorrect use can lead to memory leak.
239{-# INLINE newArrayAlignedUnmanaged #-}
262{-# INLINE newArrayAlignedUnmanaged #-}
240newArrayAlignedUnmanaged :: (MonadIO m, Storable a) => Int -> Int -> m (Array a)
263newArrayAlignedUnmanaged :: (MonadIO m, Storable a) => Int -> Int -> m (Array a)
@@ -257,11 +280,9 @@ newArrayAligned = newArrayAlignedAllocWith mallocForeignPtrAlignedBytes
258-- XXX can unaligned allocation be more efficient when alignment is not needed?
281-- XXX can unaligned allocation be more efficient when alignment is not needed?
260−-- | Allocate an array that can hold 'count' items. The memory of the array is
283+-- | Allocate an empty array that can hold 'count' items. The memory of the
261−-- uninitialized.
284+-- array is uninitialized.
263−-- Note that this is internal routine, the reference to this array cannot be
264−-- given out until the array has been written to and frozen.
265{-# INLINE newArray #-}
286{-# INLINE newArray #-}
266newArray :: forall m a. (MonadIO m, Storable a) => Int -> m (Array a)
287newArray :: forall m a. (MonadIO m, Storable a) => Int -> m (Array a)
267newArray = newArrayAligned (alignment (undefined :: a))
288newArray = newArrayAligned (alignment (undefined :: a))
@@ -377,7 +398,7 @@ shrinkToFit arr@Array{..} = do
378-------------------------------------------------------------------------------
399-------------------------------------------------------------------------------
380−-- | Return element at the specified index without checking the bounds.
401+-- | Return the element at the specified index without checking the bounds.
382-- Unsafe because it does not check the bounds of the array.
403-- Unsafe because it does not check the bounds of the array.
383{-# INLINE_NORMAL unsafeIndex #-}
404{-# INLINE_NORMAL unsafeIndex #-}
@@ -404,9 +425,16 @@ byteLength Array{..} =
404 len = aEnd `minusPtr` p
425 len = aEnd `minusPtr` p
405 in assert (len >= 0) len
426 in assert (len >= 0) len
428+-- Note: try to avoid the use of length in performance sensitive internal
429+-- routines as it involves a costly 'div' operation. Instead use the end ptr
430+-- int he array to check the bounds etc.
407-- | /O(1)/ Get the length of the array i.e. the number of elements in the
432-- | /O(1)/ Get the length of the array i.e. the number of elements in the
435+-- Note that 'byteLength' is less expensive than this operation, as 'length'
436+-- involves a costly division operation.
410-- @since 0.7.0
438-- @since 0.7.0
411{-# INLINE length #-}
439{-# INLINE length #-}
412length :: forall a. Storable a => Array a -> Int
440length :: forall a. Storable a => Array a -> Int
@@ -455,8 +483,9 @@ arraysOf n (D.Stream step state) =
455 step' _ (GroupStart st) = do
483 step' _ (GroupStart st) = do
456 when (n <= 0) $
484 when (n <= 0) $
457 -- XXX we can pass the module string from the higher level API
485 -- XXX we can pass the module string from the higher level API
458− error $ "Streamly.Internal.Data.Array.Foreign.Mut.Type.fromStreamDArraysOf: the size of "
486+ error $ "Streamly.Internal.Data.Array.Foreign.Mut.Type.arraysOf: "
487+ ++ "the size of arrays [" ++ show n
459− ++ "arrays [" ++ show n ++ "] must be a natural number"
488+ ++ "] must be a natural number"
460 Array start end bound <- liftIO $ newArray n
489 Array start end bound <- liftIO $ newArray n
461 return $ D.Skip (GroupBuffer st start end bound)
490 return $ D.Skip (GroupBuffer st start end bound)
@@ -516,7 +545,7 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
516 D.Skip s -> D.Skip (OuterLoop s)
545 D.Skip s -> D.Skip (OuterLoop s)
517 D.Stop -> D.Stop
546 D.Stop -> D.Stop
519− step' _ (InnerLoop st _ p end) | p == end =
548+ step' _ (InnerLoop st _ p end) | assert (p <= end) (p == end) =
520 return $ D.Skip $ OuterLoop st
549 return $ D.Skip $ OuterLoop st
522 step' _ (InnerLoop st startf p end) = do
551 step' _ (InnerLoop st startf p end) = do
@@ -581,9 +610,10 @@ producer = Producer step inject extract
581 return $ ReadUState (ForeignPtr end contents) (Ptr start)
610 return $ ReadUState (ForeignPtr end contents) (Ptr start)
583 {-# INLINE_LATE step #-}
612 {-# INLINE_LATE step #-}
584− step (ReadUState fp@(ForeignPtr end _) p) | p == Ptr end =
613+ step (ReadUState fp@(ForeignPtr end _) p)
614+ | assert (p <= Ptr end) (p == Ptr end) =
585− let x = unsafeInlineIO $ touchForeignPtr fp
615+ let x = unsafeInlineIO $ touchForeignPtr fp
586− in x `seq` return D.Stop
616+ in x `seq` return D.Stop
587 step (ReadUState fp p) = do
617 step (ReadUState fp p) = do
588 -- unsafeInlineIO allows us to run this in Identity monad for pure
618 -- unsafeInlineIO allows us to run this in Identity monad for pure
589 -- toList/foldr case which makes them much faster due to not
619 -- toList/foldr case which makes them much faster due to not
@@ -643,7 +673,7 @@ toListFB :: forall a b. Storable a => (a -> b -> b) -> b -> Array a -> b
643toListFB c n Array{..} = go (unsafeForeignPtrToPtr aStart)
673toListFB c n Array{..} = go (unsafeForeignPtrToPtr aStart)
646− go p | p == aEnd = n
676+ go p | assert (p <= aEnd) (p == aEnd) = n
648 -- unsafeInlineIO allows us to run this in Identity monad for pure
678 -- unsafeInlineIO allows us to run this in Identity monad for pure
649 -- toList/foldr case which makes them much faster due to not
679 -- toList/foldr case which makes them much faster due to not
@@ -678,7 +708,7 @@ toStreamD Array{..} =
680 {-# INLINE_LATE step #-}
710 {-# INLINE_LATE step #-}
681− step _ p | p == aEnd = return D.Stop
711+ step _ p | assert (p <= aEnd) (p == aEnd) = return D.Stop
682 step _ p = do
712 step _ p = do
683 -- unsafeInlineIO allows us to run this in Identity monad for pure
713 -- unsafeInlineIO allows us to run this in Identity monad for pure
684 -- toList/foldr case which makes them much faster due to not
714 -- toList/foldr case which makes them much faster due to not
@@ -700,7 +730,7 @@ toStreamK Array{..} =
703− go p | p == aEnd = K.nil
733+ go p | assert (p <= aEnd) (p == aEnd) = K.nil
704 | otherwise =
734 | otherwise =
705 -- See Note in toStreamD.
735 -- See Note in toStreamD.
706 let !x = unsafeInlineIO $ do
736 let !x = unsafeInlineIO $ do
@@ -852,7 +882,8 @@ writeNUnsafe = writeNUnsafeWith newArray
853-- @writeChunks = Fold.many Fold.toStream (Array.writeN n)@
883-- @writeChunks = Fold.many Fold.toStream (Array.writeN n)@
855−-- See 'bufferChunks'.
885+-- Breaking an array into an array stream can be useful to consume a large
886+-- array sequentially such that memory of the array is released incrementatlly.
857-- /Unimplemented/
888-- /Unimplemented/
@@ -939,11 +970,12 @@ writeAligned alignSize =
940-- | Use the 'writeN' fold instead.
971-- | Use the 'writeN' fold instead.
942−-- @fromStreamDN n = D.fold (writeN n)@
973+-- >>> fromStreamDN n = StreamD.fold (writeN n)
944{-# INLINE_NORMAL fromStreamDN #-}
975{-# INLINE_NORMAL fromStreamDN #-}
945fromStreamDN :: forall m a. (MonadIO m, Storable a)
976fromStreamDN :: forall m a. (MonadIO m, Storable a)
946 => Int -> D.Stream m a -> m (Array a)
977 => Int -> D.Stream m a -> m (Array a)
978+-- fromStreamDN n = D.fold (writeN n)
947fromStreamDN limit str = do
979fromStreamDN limit str = do
948 arr <- liftIO $ newArray limit
980 arr <- liftIO $ newArray limit
949 end <- D.foldlM' fwrite (return $ aEnd arr) $ D.take limit str
981 end <- D.foldlM' fwrite (return $ aEnd arr) $ D.take limit str
@@ -1054,6 +1086,11 @@ spliceWithDoubling dst@(Array start end bound) src = do
1054 let oldStart = unsafeForeignPtrToPtr start
1086 let oldStart = unsafeForeignPtrToPtr start
1055 oldSize = end `minusPtr` oldStart
1087 oldSize = end `minusPtr` oldStart
1056 newSize = max (oldSize * 2) (oldSize + srcLen)
1088 newSize = max (oldSize * 2) (oldSize + srcLen)
1089+ when (newSize < oldSize + srcLen)
1091+ $ "spliceWithDoubling: newSize is less than the total size "
1092+ ++ "of arrays being appended. Please check the "
1093+ ++ "newSize function passed."
1057 liftIO $ realloc newSize dst
1094 liftIO $ realloc newSize dst
1058 else return dst
1095 else return dst
1059 spliceWith dst1 src
1096 spliceWith dst1 src
@@ -1153,25 +1190,3 @@ instance (Storable a, Eq a) => Eq (Array a) where
1153instance NFData (Array a) where
1190instance NFData (Array a) where
1154 {-# INLINE rnf #-}
1191 {-# INLINE rnf #-}
1155 rnf Array {} = ()
1192 rnf Array {} = ()
1158−-- Definitions using the Storable constraint from the Array type. These are to
1159−-- make the Foldable instance possible though it is much slower (7x slower).
1161−{-# INLINE_NORMAL toStreamD_ #-}
1162−toStreamD_ :: forall m a. MonadIO m => Int -> Array a -> D.Stream m a
1163−toStreamD_ size Array{..} =
1164− let p = unsafeForeignPtrToPtr aStart
1169− {-# INLINE_LATE step #-}
1170− step _ p | p == aEnd = return D.Stop
1174− touchForeignPtr aStart
1176− return $ D.Yield x (p `plusPtr` size)