on master help sign in
Add appending folds
2a90763Harendra Kumar
src/Streamly/Internal/Data/Array/Foreign/Mut/Type.hs+112−13
@@ -42,6 +42,7 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
42 -- * Mutation
42 -- * Mutation
43 , putIndexUnsafe
43 , putIndexUnsafe
44 , snocUnsafe
44 , snocUnsafe
45+ , snocWith
45 , snoc
46 , snoc
46 , snocLinear
47 , snocLinear
47
48
@@ -57,6 +58,12 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
57 , cmp
58 , cmp
58
59
59 -- * Composable Folds
60 -- * Composable Folds
61+ -- XXX These should be Refolds
62+ , appendNUnsafe
63+ , appendN
64+ , appendWith
65+ , append
66+
60 , writeWith
67 , writeWith
61 , writeNAllocWith
68 , writeNAllocWith
62 , writeN
69 , writeN
@@ -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
133+#define noinline
134+#else
135+import GHC.Base (noinline)
136+#endif
125import GHC.ForeignPtr (ForeignPtr(..))
137import GHC.ForeignPtr (ForeignPtr(..))
126import GHC.Ptr (Ptr(..))
138import GHC.Ptr (Ptr(..))
127
139
@@ -155,8 +167,12 @@ plusForeignPtr (ForeignPtr addr c) (I# d) = ForeignPtr (plusAddr# addr d) c
155#endif
167#endif
156
168
157-- $setup
169-- $setup
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
160
176
161-------------------------------------------------------------------------------
177-------------------------------------------------------------------------------
162-- Array Data Type
178-- Array Data Type
@@ -490,7 +506,7 @@ snocLinear = snocWith (+ arrayChunkSize)
490--
506--
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.
492--
508--
493−-- >>> snoc = snocWith (* 2)
509+-- >>> snoc = Array.snocWith (* 2)
494--
510--
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.
496--
512--
@@ -974,6 +990,95 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
974-- Folds
990-- Folds
975-------------------------------------------------------------------------------
991-------------------------------------------------------------------------------
976
992
993+data ArrayUnsafe a = ArrayUnsafe
994+ {-# UNPACK #-} !(ForeignPtr a) -- first address
995+ {-# UNPACK #-} !(Ptr a) -- first unused address
996+
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
1000+-- operation.
1001+
1002+-- XXX Keep the bound intact to not lose any free space? Perf impact?
1003+
1004+-- | Append up to @n@ input items to the supplied array.
1005+--
1006+-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
1007+-- corruption or segfault.
1008+--
1009+-- Any free space left in the array after appending @n@ elements is lost.
1010+--
1011+-- /Internal/
1012+{-# INLINE_NORMAL appendNUnsafe #-}
1013+appendNUnsafe :: forall m a. (MonadIO m, Storable a) =>
1014+ m (Array a)
1015+ -> Int
1016+ -> Fold m a (Array a)
1017+appendNUnsafe action n =
1018+ fmap extract $ FL.foldlM' step initial
1019+
1020+ where
1021+
1022+ initial = do
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 _) <-
1031+ if free < needed
1032+ then noinline reallocWith "appendNUnsafeWith" (+ needed) needed arr
1033+ else return arr
1034+ return $ ArrayUnsafe start1 end1
1035+
1036+ step (ArrayUnsafe start end) x = do
1037+ liftIO $ poke end x
1038+ return $ ArrayUnsafe start (end `plusPtr` sizeOf (undefined :: a))
1039+
1040+ extract (ArrayUnsafe start end) = Array start end end
1041+
1042+-- | Append @n@ elements to an existing array. Any free space left in the array
1043+-- after appending @n@ elements is lost.
1044+--
1045+-- >>> appendN initial n = Fold.take n (Array.appendNUnsafe initial n)
1046+--
1047+-- /Pre-release/
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)
1052+
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.
1057+--
1058+-- Note that the returned array may be a mutated version of original array.
1059+--
1060+-- >>> appendWith sizer = Fold.foldlM' (Array.snocWith sizer)
1061+--
1062+-- /Pre-release/
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)
1067+
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.
1071+--
1072+-- Note that the returned array may be a mutated version of original array.
1073+--
1074+-- >>> append = Array.appendWith (* 2)
1075+--
1076+-- /Pre-release/
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)
1081+
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'.
984--
1089--
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
987--
1092--
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)
1015
1120
1016−data ArrayUnsafe a = ArrayUnsafe
1017− {-# UNPACK #-} !(ForeignPtr a) -- first address
1018− {-# UNPACK #-} !(Ptr a) -- first unused address
1019−
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.
1022--
1123--
@@ -1058,7 +1159,7 @@ writeNUnsafe = writeNUnsafeWith newArray
1058--
1159--
1059-- | Buffer a stream into a stream of arrays.
1160-- | Buffer a stream into a stream of arrays.
1060--
1161--
1061−-- >>> writeChunks n = FL.many (writeN n) FL.toStreamK
1162+-- >>> writeChunks n = Fold.many (Array.writeN n) Fold.toStreamK
1062--
1163--
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 =
1150
1251
1151-- | Use the 'writeN' fold instead.
1252-- | Use the 'writeN' fold instead.
1152--
1253--
1153−-- >>> fromStreamDN n = StreamD.fold (writeN n)
1254+-- >>> fromStreamDN n = StreamD.fold (Array.writeN n)
1154--
1255--
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.
1207--
1308--
1208−-- @
1209−-- fromStreamD = StreamD.fold Array.write
1309+-- >>> fromStreamD = StreamD.fold Array.write
1210−-- @
1211--
1310--
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)