@@ -42,6 +42,7 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
42 -- * Mutation
42 -- * Mutation
43 , putIndexUnsafe
43 , putIndexUnsafe
44 , snocUnsafe
44 , snocUnsafe
46 , snocLinear
47 , snocLinear
@@ -57,6 +58,12 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
59 -- * Composable Folds
60 -- * Composable Folds
61+ -- XXX These should be Refolds
60 , writeWith
67 , writeWith
61 , writeNAllocWith
68 , writeNAllocWith
@@ -122,6 +129,11 @@ import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
122import Foreign.Ptr (plusPtr, minusPtr, castPtr, nullPtr)
129import Foreign.Ptr (plusPtr, minusPtr, castPtr, nullPtr)
123import Foreign.Storable (Storable(..))
130import Foreign.Storable (Storable(..))
124import GHC.Base (build)
131import GHC.Base (build)
132+#if __GLASGOW_HASKELL__ < 802
135+import GHC.Base (noinline)
125import GHC.ForeignPtr (ForeignPtr(..))
137import GHC.ForeignPtr (ForeignPtr(..))
126import GHC.Ptr (Ptr(..))
138import GHC.Ptr (Ptr(..))
@@ -155,8 +167,12 @@ plusForeignPtr (ForeignPtr addr c) (I# d) = ForeignPtr (plusAddr# addr d) c
158−-- >>> import qualified Streamly.Internal.Data.Stream.StreamD as StreamD
170+-- >>> :m
159-- >>> import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as Array
171-- >>> import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as Array
172+-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
173+-- >>> import qualified Streamly.Internal.Data.Stream.StreamD as StreamD
174+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
175+-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
161-------------------------------------------------------------------------------
177-------------------------------------------------------------------------------
162-- Array Data Type
178-- Array Data Type
@@ -490,7 +506,7 @@ snocLinear = snocWith (+ arrayChunkSize)
491-- Note that the returned array may be a mutated version of the original array.
507-- Note that the returned array may be a mutated version of the original array.
493−-- >>> snoc = snocWith (* 2)
509+-- >>> snoc = Array.snocWith (* 2)
495-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.
511-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.
@@ -974,6 +990,95 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
975-------------------------------------------------------------------------------
991-------------------------------------------------------------------------------
993+data ArrayUnsafe a = ArrayUnsafe
994+ {-# UNPACK #-} !(ForeignPtr a) -- first address
995+ {-# UNPACK #-} !(Ptr a) -- first unused address
997+-- Note: Arrays may be allocated with a specific alignment at the beginning of
998+-- the array. If you need to maintain that alignment on reallocations then you
999+-- can resize the array manually before append, using an aligned resize
1002+-- XXX Keep the bound intact to not lose any free space? Perf impact?
1004+-- | Append up to @n@ input items to the supplied array.
1006+-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
1007+-- corruption or segfault.
1009+-- Any free space left in the array after appending @n@ elements is lost.
1012+{-# INLINE_NORMAL appendNUnsafe #-}
1013+appendNUnsafe :: forall m a. (MonadIO m, Storable a) =>
1016+ -> Fold m a (Array a)
1017+appendNUnsafe action n =
1018+ fmap extract $ FL.foldlM' step initial
1023+ assert (n >= 0) (return ())
1024+ arr@(Array _ end bound) <- action
1025+ let free = bound `minusPtr` end
1026+ elemSize = sizeOf (undefined :: a)
1027+ needed = n * elemSize
1028+ -- XXX We can also reallocate if the array has too much free space,
1029+ -- otherwise we lose that space.
1030+ (Array start1 end1 _) <-
1032+ then noinline reallocWith "appendNUnsafeWith" (+ needed) needed arr
1034+ return $ ArrayUnsafe start1 end1
1036+ step (ArrayUnsafe start end) x = do
1037+ liftIO $ poke end x
1038+ return $ ArrayUnsafe start (end `plusPtr` sizeOf (undefined :: a))
1040+ extract (ArrayUnsafe start end) = Array start end end
1042+-- | Append @n@ elements to an existing array. Any free space left in the array
1043+-- after appending @n@ elements is lost.
1045+-- >>> appendN initial n = Fold.take n (Array.appendNUnsafe initial n)
1048+{-# INLINE_NORMAL appendN #-}
1049+appendN :: forall m a. (MonadIO m, Storable a) =>
1050+ m (Array a) -> Int -> Fold m a (Array a)
1051+appendN initial n = FL.take n (appendNUnsafe initial n)
1053+-- | @appendWith realloc action@ mutates the array generated by @action@ to
1054+-- append the input stream. If there is no reserved space available in the
1055+-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
1056+-- where @oldSize@ is the current size of the array in bytes.
1058+-- Note that the returned array may be a mutated version of original array.
1060+-- >>> appendWith sizer = Fold.foldlM' (Array.snocWith sizer)
1063+{-# INLINE appendWith #-}
1064+appendWith :: forall m a. (MonadIO m, Storable a) =>
1065+ (Int -> Int) -> m (Array a) -> Fold m a (Array a)
1066+appendWith sizer = FL.foldlM' (snocWith sizer)
1068+-- | @append action@ mutates the array generated by @action@ to append the
1069+-- input stream. If there is no reserved space available in the array it is
1070+-- reallocated to double the size.
1072+-- Note that the returned array may be a mutated version of original array.
1074+-- >>> append = Array.appendWith (* 2)
1077+{-# INLINE append #-}
1078+append :: forall m a. (MonadIO m, Storable a) =>
1079+ m (Array a) -> Fold m a (Array a)
1080+append = appendWith (* 2)
977{-# INLINE_NORMAL writeNAllocWith #-}
1082{-# INLINE_NORMAL writeNAllocWith #-}
978writeNAllocWith :: forall m a. (MonadIO m, Storable a)
1083writeNAllocWith :: forall m a. (MonadIO m, Storable a)
979 => (Int -> m (Array a)) -> Int -> Fold m a (Array a)
1084 => (Int -> m (Array a)) -> Int -> Fold m a (Array a)
@@ -982,8 +1087,8 @@ writeNAllocWith alloc n = FL.take n (writeNUnsafeWith alloc n)
982-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
1087-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
983-- 'Array'.
1088-- 'Array'.
985−-- >>> import qualified Streamly.Data.Fold as Fold
986−-- >>> writeN n = Fold.take n (writeNUnsafe n)
1090+-- >>> writeN n = Fold.take n (Array.writeNUnsafe n)
1091+-- >>> writeN n = Array.appendN (Array.newArray n) n
988-- @since 0.7.0
1093-- @since 0.7.0
989{-# INLINE_NORMAL writeN #-}
1094{-# INLINE_NORMAL writeN #-}
@@ -1013,10 +1118,6 @@ writeNAlignedUnmanaged :: forall m a. (MonadIO m, Storable a)
1013writeNAlignedUnmanaged alignSize =
1118writeNAlignedUnmanaged alignSize =
1014 writeNAllocWith (newArrayAlignedUnmanaged alignSize)
1119 writeNAllocWith (newArrayAlignedUnmanaged alignSize)
1016−data ArrayUnsafe a = ArrayUnsafe
1017− {-# UNPACK #-} !(ForeignPtr a) -- first address
1018− {-# UNPACK #-} !(Ptr a) -- first unused address
1020-- XXX We can carry bound as well in the state to make sure we do not lose the
1121-- XXX We can carry bound as well in the state to make sure we do not lose the
1021-- remaining capacity. Need to check perf impact.
1122-- remaining capacity. Need to check perf impact.
@@ -1058,7 +1159,7 @@ writeNUnsafe = writeNUnsafeWith newArray
1059-- | Buffer a stream into a stream of arrays.
1160-- | Buffer a stream into a stream of arrays.
1061−-- >>> writeChunks n = FL.many (writeN n) FL.toStreamK
1162+-- >>> writeChunks n = Fold.many (Array.writeN n) Fold.toStreamK
1063-- Breaking an array into an array stream can be useful to consume a large
1164-- Breaking an array into an array stream can be useful to consume a large
1064-- array sequentially such that memory of the array is released incrementatlly.
1165-- array sequentially such that memory of the array is released incrementatlly.
@@ -1150,7 +1251,7 @@ writeAligned alignSize =
1151-- | Use the 'writeN' fold instead.
1252-- | Use the 'writeN' fold instead.
1153−-- >>> fromStreamDN n = StreamD.fold (writeN n)
1254+-- >>> fromStreamDN n = StreamD.fold (Array.writeN n)
1155{-# INLINE_NORMAL fromStreamDN #-}
1256{-# INLINE_NORMAL fromStreamDN #-}
1156fromStreamDN :: forall m a. (MonadIO m, Storable a)
1257fromStreamDN :: forall m a. (MonadIO m, Storable a)
@@ -1205,9 +1306,7 @@ fromArrayStreamK as = do
1205-- the chunking approach. However, if we have to shrink in the end then it may
1306-- the chunking approach. However, if we have to shrink in the end then it may
1206-- result in an extra copy of the entire data.
1307-- result in an extra copy of the entire data.
1209−-- fromStreamD = StreamD.fold Array.write
1309+-- >>> fromStreamD = StreamD.fold Array.write
1212{-# INLINE fromStreamD #-}
1311{-# INLINE fromStreamD #-}
1213fromStreamD :: (MonadIO m, Storable a) => D.Stream m a -> m (Array a)
1312fromStreamD :: (MonadIO m, Storable a) => D.Stream m a -> m (Array a)