@@ -128,9 +128,16 @@ module Streamly.Mem.Array
128 , writeSlice
128 , writeSlice
129 , writeSliceRev
129 , writeSliceRev
131+ -- * Immutable Transformations
140+import Control.Monad (when)
134import Control.Monad.IO.Class (MonadIO(..))
141import Control.Monad.IO.Class (MonadIO(..))
135import Data.Functor.Identity (Identity)
142import Data.Functor.Identity (Identity)
136import Foreign.ForeignPtr (withForeignPtr)
143import Foreign.ForeignPtr (withForeignPtr)
@@ -169,7 +176,9 @@ newArray len = undefined
169-- @since 0.7.0
176-- @since 0.7.0
170{-# INLINE writeN #-}
177{-# INLINE writeN #-}
171writeN :: (MonadIO m, Storable a) => Int -> SerialT m a -> m (Array a)
178writeN :: (MonadIO m, Storable a) => Int -> SerialT m a -> m (Array a)
180+ if n < 0 then error "writeN: negative write count specified" else return ()
172−writeN n m = fromStreamDN n $ D.toStreamD m
181+ fromStreamDN n $ D.toStreamD m
174-------------------------------------------------------------------------------
183-------------------------------------------------------------------------------
175-- Elimination
184-- Elimination
@@ -386,11 +395,17 @@ _toArraysOf n = FL.lchunksOf n (toArrayN n) FL.toStream
386-- XXX check if GHC's memory allocator is efficient enough. We can try the C
395-- XXX check if GHC's memory allocator is efficient enough. We can try the C
387-- malloc to compare against.
396-- malloc to compare against.
398+{-# INLINE bytesToCount #-}
399+bytesToCount :: Storable a => a -> Int -> Int
401+ let elemSize = sizeOf x
402+ in n + elemSize - 1 `div` elemSize
389{-# INLINE toArrayMinChunk #-}
404{-# INLINE toArrayMinChunk #-}
390toArrayMinChunk :: forall m a. (MonadIO m, Storable a)
405toArrayMinChunk :: forall m a. (MonadIO m, Storable a)
391 => Int -> Fold m a (Array a)
406 => Int -> Fold m a (Array a)
392-- toArrayMinChunk n = FL.mapM spliceArrays $ toArraysOf n
407-- toArrayMinChunk n = FL.mapM spliceArrays $ toArraysOf n
393−toArrayMinChunk chunkSize = Fold step initial extract
408+toArrayMinChunk elemCount = Fold step initial extract
@@ -398,7 +413,9 @@ toArrayMinChunk chunkSize = Fold step initial extract
398 liftIO $ poke end x
413 liftIO $ poke end x
399 return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
414 return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
417+ when (elemCount < 0) $ error "toArrayMinChunk: elemCount is negative"
401− initial = liftIO $ A.newArray (mkChunkSize chunkSize)
418+ liftIO $ A.newArray elemCount
402 step arr@(Array _ end bound) x | end == bound = do
419 step arr@(Array _ end bound) x | end == bound = do
403 arr1 <- liftIO $ reallocDouble 1 arr
420 arr1 <- liftIO $ reallocDouble 1 arr
404 insertElem arr1 x
421 insertElem arr1 x
@@ -412,7 +429,7 @@ toArrayMinChunk chunkSize = Fold step initial extract
412-- @since 0.7.0
429-- @since 0.7.0
413{-# INLINE toArray #-}
430{-# INLINE toArray #-}
414toArray :: forall m a. (MonadIO m, Storable a) => Fold m a (Array a)
431toArray :: forall m a. (MonadIO m, Storable a) => Fold m a (Array a)
415−toArray = toArrayMinChunk 1024
432+toArray = toArrayMinChunk (bytesToCount (undefined :: a) (mkChunkSize 1024))
417-- | Convert a stream of arrays into a stream of their elements.
434-- | Convert a stream of arrays into a stream of their elements.
@@ -467,10 +484,11 @@ _spliceArraysRealloced s = do
467 return $ dst `plusPtr` len
484 return $ dst `plusPtr` len
469{-# INLINE spliceArraysBuffered #-}
486{-# INLINE spliceArraysBuffered #-}
470−spliceArraysBuffered :: (MonadIO m, Storable a)
487+spliceArraysBuffered :: forall m a. (MonadIO m, Storable a)
471 => SerialT m (Array a) -> m (Array a)
488 => SerialT m (Array a) -> m (Array a)
472spliceArraysBuffered s = do
489spliceArraysBuffered s = do
490+ idst <- liftIO $ A.newArray (bytesToCount (undefined :: a)
473− idst <- liftIO $ A.newArray (mkChunkSizeKB 4)
491+ (mkChunkSizeKB 4))
474 arr <- S.foldlM' appendArr idst s
492 arr <- S.foldlM' appendArr idst s
475 liftIO $ shrinkToFit arr
493 liftIO $ shrinkToFit arr
@@ -508,8 +526,40 @@ spliceArrays = spliceArraysBuffered
509{-# INLINE write #-}
527{-# INLINE write #-}
510write :: (MonadIO m, Storable a) => SerialT m a -> m (Array a)
528write :: (MonadIO m, Storable a) => SerialT m a -> m (Array a)
529+write = FL.foldl' toArray
511−write m = A.fromStreamD $ D.toStreamD m
530+-- write m = A.fromStreamD $ D.toStreamD m
513-- XXX efficiently compare two streams of arrays. Two streams can have chunks
532-- XXX efficiently compare two streams of arrays. Two streams can have chunks
514-- of different sizes, we can handle that in the stream comparison abstraction.
533-- of different sizes, we can handle that in the stream comparison abstraction.
515-- This could be useful e.g. to fast compare whether two files differ.
534-- This could be useful e.g. to fast compare whether two files differ.
536+-------------------------------------------------------------------------------
537+-- Transform via stream operations
538+-------------------------------------------------------------------------------
540+-- for non-length changing operations we can use the original length for
541+-- allocation. If we can predict the length then we can use the prediction for
542+-- new allocation. Otherwise we can use a hint and adjust dynamically.
544+-- | Transform an array into another array using a stream transformation
548+{-# INLINE transformWith #-}
549+transformWith :: (MonadIO m, Storable a, Storable b)
550+ => (SerialT m a -> SerialT m b) -> Array a -> m (Array b)
551+transformWith f arr = FL.foldl' (toArrayMinChunk (length arr)) $ f (read arr)
553+-- | Fold an array using a 'Fold'.
556+{-# INLINE foldArray #-}
557+foldArray :: (MonadIO m, Storable a) => Fold m a b -> Array a -> m b
558+foldArray f arr = FL.foldl' f (read arr)
560+-- | Fold an array using a stream fold operation.
563+{-# INLINE foldWith #-}
564+foldWith :: (MonadIO m, Storable a) => (SerialT m a -> m b) -> Array a -> m b
565+foldWith f arr = f (read arr)