on master help sign in
Improve array combinators to work with unpinned arrays
3811e68Adithya Kumar
.github/workflows/haskell.yml−7
@@ -102,13 +102,6 @@ jobs:
102 stack_build_options: "--flag streamly-benchmarks:-opt"
102 stack_build_options: "--flag streamly-benchmarks:-opt"
103 cabal_version: 3.6
103 cabal_version: 3.6
104 ignore_error: false
104 ignore_error: false
105− - name: 8.10.7-storable-serialization
106− runner: ubuntu-latest
107− build: cabal
108− cabal_version: 3.6
109− cabal_project: cabal.project.storable
110− disable_sdist_build: "y"
111− ignore_error: false
112 - name: 8.10.7-macos
105 - name: 8.10.7-macos
113 ghc_version: 8.10.7
106 ghc_version: 8.10.7
114 runner: macos-latest
107 runner: macos-latest
.packcheck.ignore−1
@@ -23,7 +23,6 @@ cabal.project.report
23cabal.project.packdiff
23cabal.project.packdiff
24cabal.project.Werror
24cabal.project.Werror
25cabal.project.Werror-nocode
25cabal.project.Werror-nocode
26−cabal.project.storable
27default.nix
26default.nix
28examples/README.md
27examples/README.md
29hie.yaml
28hie.yaml
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+5−5
@@ -65,7 +65,7 @@ readLast = S.last . S.unfold FH.read
65inspect $ hasNoTypeClasses 'readLast
65inspect $ hasNoTypeClasses 'readLast
66inspect $ 'readLast `hasNoType` ''Step -- S.unfold
66inspect $ 'readLast `hasNoType` ''Step -- S.unfold
67inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
67inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
68−inspect $ 'readLast `hasNoType` ''MA.ReadUState -- FH.read/A.read
68+inspect $ 'readLast `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
69#endif
69#endif
70
70
71-- assert that flattenArrays constructors are not present
71-- assert that flattenArrays constructors are not present
@@ -77,7 +77,7 @@ readCountBytes = S.length . S.unfold FH.read
77inspect $ hasNoTypeClasses 'readCountBytes
77inspect $ hasNoTypeClasses 'readCountBytes
78inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
78inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
79inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
79inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
80−inspect $ 'readCountBytes `hasNoType` ''MA.ReadUState -- FH.read/A.read
80+inspect $ 'readCountBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
81#endif
81#endif
82
82
83-- | Count the number of lines in a file.
83-- | Count the number of lines in a file.
@@ -92,7 +92,7 @@ readCountLines =
92inspect $ hasNoTypeClasses 'readCountLines
92inspect $ hasNoTypeClasses 'readCountLines
93inspect $ 'readCountLines `hasNoType` ''Step
93inspect $ 'readCountLines `hasNoType` ''Step
94inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
94inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
95−inspect $ 'readCountLines `hasNoType` ''MA.ReadUState -- FH.read/A.read
95+inspect $ 'readCountLines `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
96#endif
96#endif
97
97
98-- | Count the number of words in a file.
98-- | Count the number of words in a file.
@@ -116,7 +116,7 @@ readSumBytes = S.sum . S.unfold FH.read
116inspect $ hasNoTypeClasses 'readSumBytes
116inspect $ hasNoTypeClasses 'readSumBytes
117inspect $ 'readSumBytes `hasNoType` ''Step
117inspect $ 'readSumBytes `hasNoType` ''Step
118inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
118inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
119−inspect $ 'readSumBytes `hasNoType` ''MA.ReadUState -- FH.read/A.read
119+inspect $ 'readSumBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
120#endif
120#endif
121
121
122-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
122-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -235,8 +235,8 @@ inspect $ hasNoTypeClasses 'chunksOf
235inspect $ 'chunksOf `hasNoType` ''Step
235inspect $ 'chunksOf `hasNoType` ''Step
236inspect $ 'chunksOf `hasNoType` ''FoldMany
236inspect $ 'chunksOf `hasNoType` ''FoldMany
237inspect $ 'chunksOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
237inspect $ 'chunksOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
238+ -- FH.read/A.read
238inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
239inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
239−inspect $ 'chunksOf `hasNoType` ''MA.ReadUState -- FH.read/A.read
240#endif
240#endif
241
241
242{-# INLINE arraysOf #-}
242{-# INLINE arraysOf #-}
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+3−4
@@ -42,7 +42,6 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
42import qualified Streamly.Internal.Data.Tuple.Strict as Strict
42import qualified Streamly.Internal.Data.Tuple.Strict as Strict
43import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as MAS
43import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as MAS
44import qualified Streamly.Internal.Data.Array.Foreign.Type as AT
44import qualified Streamly.Internal.Data.Array.Foreign.Type as AT
45−import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
46
45
47import Test.Inspection
46import Test.Inspection
48#endif
47#endif
@@ -82,8 +81,8 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.read inh)
82inspect $ hasNoTypeClasses 'copyStream
81inspect $ hasNoTypeClasses 'copyStream
83inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
82inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
84inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
83inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
85−inspect $ 'copyStream `hasNoType` ''MA.ReadUState -- FH.read/A.read
86inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
84inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
85+ -- FH.read/A.read
87inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
86inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
88#endif
87#endif
89
88
@@ -170,8 +169,8 @@ writeReadWith inh devNull = IUF.fold fld unf (defaultChunkSize, inh)
170inspect $ hasNoTypeClasses 'writeReadWith
169inspect $ hasNoTypeClasses 'writeReadWith
171inspect $ 'writeReadWith `hasNoType` ''Step
170inspect $ 'writeReadWith `hasNoType` ''Step
172inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
171inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
173−inspect $ 'writeReadWith `hasNoType` ''MA.ReadUState -- FH.read/A.read
174inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
172inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
173+ -- FH.read/A.read
175#endif
174#endif
176
175
177-- | Send the file contents ('AT.defaultChunkSize') to /dev/null
176-- | Send the file contents ('AT.defaultChunkSize') to /dev/null
@@ -187,8 +186,8 @@ writeRead inh devNull = IUF.fold fld unf inh
187inspect $ hasNoTypeClasses 'writeRead
186inspect $ hasNoTypeClasses 'writeRead
188inspect $ 'writeRead `hasNoType` ''Step
187inspect $ 'writeRead `hasNoType` ''Step
189inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
188inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
190−inspect $ 'writeRead `hasNoType` ''MA.ReadUState -- FH.read/A.read
191inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
189inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
190+ -- FH.read/A.read
192#endif
191#endif
193
192
194o_1_space_copy :: BenchEnv -> [Benchmark]
193o_1_space_copy :: BenchEnv -> [Benchmark]
benchmark/Streamly/Benchmark/Prelude/Serial/Split.hs+4−4
@@ -67,7 +67,7 @@ splitOn inh =
67inspect $ hasNoTypeClasses 'splitOn
67inspect $ hasNoTypeClasses 'splitOn
68inspect $ 'splitOn `hasNoType` ''Step
68inspect $ 'splitOn `hasNoType` ''Step
69inspect $ 'splitOn `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
69inspect $ 'splitOn `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
70−inspect $ 'splitOn `hasNoType` ''MA.ReadUState -- FH.read/A.read
70+inspect $ 'splitOn `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
71#endif
71#endif
72
72
73-- | Split suffix on line feed.
73-- | Split suffix on line feed.
@@ -80,7 +80,7 @@ splitOnSuffix inh =
80inspect $ hasNoTypeClasses 'splitOnSuffix
80inspect $ hasNoTypeClasses 'splitOnSuffix
81inspect $ 'splitOnSuffix `hasNoType` ''Step
81inspect $ 'splitOnSuffix `hasNoType` ''Step
82inspect $ 'splitOnSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
82inspect $ 'splitOnSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
83−inspect $ 'splitOnSuffix `hasNoType` ''MA.ReadUState -- FH.read/A.read
83+inspect $ 'splitOnSuffix `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
84#endif
84#endif
85
85
86-- | Split suffix with line feed.
86-- | Split suffix with line feed.
@@ -93,7 +93,7 @@ splitWithSuffix inh =
93inspect $ hasNoTypeClasses 'splitWithSuffix
93inspect $ hasNoTypeClasses 'splitWithSuffix
94inspect $ 'splitWithSuffix `hasNoType` ''Step
94inspect $ 'splitWithSuffix `hasNoType` ''Step
95inspect $ 'splitWithSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
95inspect $ 'splitWithSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
96−inspect $ 'splitWithSuffix `hasNoType` ''MA.ReadUState -- FH.read/A.read
96+inspect $ 'splitWithSuffix `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
97#endif
97#endif
98
98
99-- | Split on line feed.
99-- | Split on line feed.
@@ -124,7 +124,7 @@ wordsBy inh =
124inspect $ hasNoTypeClasses 'wordsBy
124inspect $ hasNoTypeClasses 'wordsBy
125inspect $ 'wordsBy `hasNoType` ''Step
125inspect $ 'wordsBy `hasNoType` ''Step
126inspect $ 'wordsBy `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
126inspect $ 'wordsBy `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
127−inspect $ 'wordsBy `hasNoType` ''MA.ReadUState -- FH.read/A.read
127+inspect $ 'wordsBy `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
128#endif
128#endif
129
129
130-- | Split on a word8 sequence.
130-- | Split on a word8 sequence.
benchmark/Streamly/Benchmark/Unicode/Stream.hs+2−3
@@ -40,7 +40,6 @@ import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
40import qualified Streamly.Internal.Data.Fold.Type as Fold
40import qualified Streamly.Internal.Data.Fold.Type as Fold
41import qualified Streamly.Internal.Data.Tuple.Strict as Strict
41import qualified Streamly.Internal.Data.Tuple.Strict as Strict
42import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
42import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
43−import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
44
43
45import Test.Inspection
44import Test.Inspection
46#endif
45#endif
@@ -208,10 +207,10 @@ copyStreamLatin1' inh outh =
208inspect $ hasNoTypeClasses 'copyStreamLatin1'
207inspect $ hasNoTypeClasses 'copyStreamLatin1'
209inspect $ 'copyStreamLatin1' `hasNoType` ''Step
208inspect $ 'copyStreamLatin1' `hasNoType` ''Step
210inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
209inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
211−inspect $ 'copyStreamLatin1' `hasNoType` ''MArray.ReadUState -- Handle.read/Array.read
212
210
213inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
211inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
214inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
212inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
213+ -- Handle.read/Array.read
215inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
214inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
216#endif
215#endif
217
216
@@ -228,11 +227,11 @@ copyStreamLatin1 inh outh =
228inspect $ hasNoTypeClasses 'copyStreamLatin1
227inspect $ hasNoTypeClasses 'copyStreamLatin1
229inspect $ 'copyStreamLatin1 `hasNoType` ''Step
228inspect $ 'copyStreamLatin1 `hasNoType` ''Step
230inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
229inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
231−inspect $ 'copyStreamLatin1 `hasNoType` ''MArray.ReadUState -- Handle.read/Array.read
232
230
233inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
231inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
234inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
232inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
235inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
233inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
234+ -- Handle.read/Array.read
236inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
235inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
237#endif
236#endif
238
237
cabal.project.storabledeleted−11
@@ -1,11 +0,0 @@
1−packages: streamly.cabal
2− , core/streamly-core.cabal
3− , test/streamly-tests.cabal
4− , benchmark/streamly-benchmarks.cabal
5− , docs/streamly-docs.cabal
6−
7−package streamly
8− flags: +use-storable
9−
10−package streamly-core
11− flags: +use-storable
core/src/Streamly/Internal/Data/Array/ArrayMacros.h+12
@@ -25,3 +25,15 @@
25#define PTR_VALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) <= end
25#define PTR_VALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) <= end
26#define PTR_INVALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) > end
26#define PTR_INVALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) > end
27
27
28+-------------------------------------------------------------------------------
29+-- Macros to access array indices
30+-------------------------------------------------------------------------------
31+
32+#define INDEX_NEXT(i,a) i + SIZE_OF(a)
33+#define INDEX_PREV(i,a) i - SIZE_OF(a)
34+
35+#define INDEX_OF(base,i,a) base + (SIZE_OF(a) * i)
36+#define RINDEX_OF(base,i,a) base - (SIZE_OF(a) * (i + 1))
37+
38+#define INDEX_VALID(i,end,a) i + SIZE_OF(a) <= end
39+#define INDEX_INVALID(i,end,a) i + SIZE_OF(a) > end
core/src/Streamly/Internal/Data/Array/Foreign.hs+30−31
@@ -34,13 +34,6 @@ module Streamly.Internal.Data.Array.Foreign
34
34
35 -- * Construction
35 -- * Construction
36
36
37− -- Pure, From Static Memory (Unsafe)
38− -- We can use fromPtrM#, fromCStringM# and fromAddrM# to create arrays from
39− -- a dynamic memory address which requires a finalizer.
40− , A.fromPtr
41− , A.fromAddr#
42− , A.fromCString#
43−
44 -- Pure List APIs
37 -- Pure List APIs
45 , A.fromListN
38 , A.fromListN
46 , A.fromList
39 , A.fromList
@@ -122,11 +115,18 @@ import Control.Monad.IO.Class (MonadIO(..))
122import Data.Functor.Identity (Identity)
115import Data.Functor.Identity (Identity)
123import Data.Word (Word8)
116import Data.Word (Word8)
124import Foreign.C.String (CString)
117import Foreign.C.String (CString)
125−import Foreign.Ptr (plusPtr, castPtr)
118+import Foreign.Ptr (castPtr)
119+import qualified Foreign.Storable as Storable (Storable)
126−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
120+import Streamly.Internal.Data.Unboxed
121+ ( Storable
122+ , Unboxed
123+ , castContents
124+ , peekWith
125+ , sizeOf
126+ )
127import Prelude hiding (length, null, last, map, (!!), read, concat)
127import Prelude hiding (length, null, last, map, (!!), read, concat)
128
128
129−import Streamly.Internal.Data.Array.Foreign.Mut.Type (ReadUState(..), touch)
129+import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayUnsafe(..))
130import Streamly.Internal.Data.Array.Foreign.Type
130import Streamly.Internal.Data.Array.Foreign.Type
131 (Array(..), length, asPtrUnsafe)
131 (Array(..), length, asPtrUnsafe)
132import Streamly.Internal.Data.Fold.Type (Fold(..))
132import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -213,10 +213,10 @@ unsafeRead = Unfold step inject
213 where
213 where
214
214
215 inject (Array contents start end) =
215 inject (Array contents start end) =
216− return (ReadUState contents end start)
216+ return (ArrayUnsafe contents end start)
217
217
218 {-# INLINE_LATE step #-}
218 {-# INLINE_LATE step #-}
219− step (ReadUState contents end p) = do
219+ step (ArrayUnsafe contents end p) = do
220 -- unsafeInlineIO allows us to run this in Identity monad for pure
220 -- unsafeInlineIO allows us to run this in Identity monad for pure
221 -- toList/foldr case which makes them much faster due to not
221 -- toList/foldr case which makes them much faster due to not
222 -- accumulating the list and fusing better with the pure consumers.
222 -- accumulating the list and fusing better with the pure consumers.
@@ -224,11 +224,10 @@ unsafeRead = Unfold step inject
224 -- This should be safe as the array contents are guaranteed to be
224 -- This should be safe as the array contents are guaranteed to be
225 -- evaluated/written to before we peek at them.
225 -- evaluated/written to before we peek at them.
226 let !x = unsafeInlineIO $ do
226 let !x = unsafeInlineIO $ do
227− r <- peek p
228− touch contents
227+ r <- peekWith contents p
229 return r
228 return r
230− let !p1 = PTR_NEXT(p,a)
229+ let !p1 = INDEX_NEXT(p,a)
231− return $ D.Yield x (ReadUState contents end p1)
230+ return $ D.Yield x (ArrayUnsafe contents end p1)
232
231
233-- |
232-- |
234--
233--
@@ -247,11 +246,10 @@ null arr = A.byteLength arr == 0
247getIndexRev :: forall a. Storable a => Int -> Array a -> Maybe a
246getIndexRev :: forall a. Storable a => Int -> Array a -> Maybe a
248getIndexRev i arr =
247getIndexRev i arr =
249 unsafeInlineIO
248 unsafeInlineIO
250− $ asPtrUnsafe arr
251− $ \ptr -> do
249+ $ do
252− let elemPtr = PTR_RINDEX(aEnd arr,i,a)
250+ let elemPtr = RINDEX_OF(aEnd arr, i, a)
253− if i >= 0 && elemPtr >= ptr
251+ if i >= 0 && elemPtr >= arrStart arr
254− then Just <$> peek elemPtr
252+ then Just <$> peekWith (arrContents arr) elemPtr
255 else return Nothing
253 else return Nothing
256
254
257-- |
255-- |
@@ -273,7 +271,8 @@ last = getIndexRev 0
273--
271--
274-- @since 0.8.0
272-- @since 0.8.0
275{-# INLINE writeLastN #-}
273{-# INLINE writeLastN #-}
274+writeLastN ::
276−writeLastN :: (Storable a, MonadIO m) => Int -> Fold m a (Array a)
275+ (Unboxed a, Storable.Storable a, MonadIO m) => Int -> Fold m a (Array a)
277writeLastN n
276writeLastN n
278 | n <= 0 = fmap (const mempty) FL.drain
277 | n <= 0 = fmap (const mempty) FL.drain
279 | otherwise = A.unsafeFreeze <$> Fold step initial done
278 | otherwise = A.unsafeFreeze <$> Fold step initial done
@@ -360,9 +359,9 @@ getSliceUnsafe ::
360 -> Array a
359 -> Array a
361getSliceUnsafe index len (Array contents start e) =
360getSliceUnsafe index len (Array contents start e) =
362 let size = SIZE_OF(a)
361 let size = SIZE_OF(a)
363− fp1 = start `plusPtr` (index * size)
362+ start1 = start + (index * size)
364− end = fp1 `plusPtr` (len * size)
363+ end1 = start1 + (len * size)
365− in assert (end <= e) (Array contents fp1 end)
364+ in assert (end1 <= e) (Array contents start1 end1)
366
365
367-- | Split the array into a stream of slices using a predicate. The element
366-- | Split the array into a stream of slices using a predicate. The element
368-- matching the predicate is dropped.
367-- matching the predicate is dropped.
@@ -412,11 +411,10 @@ getSlicesFromLen from len =
412getIndex :: forall a. Storable a => Int -> Array a -> Maybe a
411getIndex :: forall a. Storable a => Int -> Array a -> Maybe a
413getIndex i arr =
412getIndex i arr =
414 unsafeInlineIO
413 unsafeInlineIO
415− $ asPtrUnsafe arr
416− $ \ptr -> do
414+ $ do
417− let elemPtr = PTR_INDEX(ptr,i,a)
415+ let elemPtr = INDEX_OF(arrStart arr, i, a)
418− if i >= 0 && PTR_VALID(elemPtr,aEnd arr,a)
416+ if i >= 0 && INDEX_VALID(elemPtr, aEnd arr, a)
419− then Just <$> peek elemPtr
417+ then Just <$> peekWith (arrContents arr) elemPtr
420 else return Nothing
418 else return Nothing
421
419
422-- | Given a stream of array indices, read the elements on those indices from
420-- | Given a stream of array indices, read the elements on those indices from
@@ -509,7 +507,7 @@ castUnsafe ::
509#endif
507#endif
510 Array a -> Array b
508 Array a -> Array b
511castUnsafe (Array contents start end) =
509castUnsafe (Array contents start end) =
512− Array contents (castPtr start) (castPtr end)
510+ Array (castContents contents) start end
513
511
514-- | Cast an @Array a@ into an @Array Word8@.
512-- | Cast an @Array a@ into an @Array Word8@.
515--
513--
@@ -542,6 +540,7 @@ cast arr =
542--
540--
543asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
541asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
544asCStringUnsafe arr act = do
542asCStringUnsafe arr act = do
543+ -- XXX Ensure a pinned allocation here.
545 let arr1 = asBytes arr <> A.fromList [0]
544 let arr1 = asBytes arr <> A.fromList [0]
546 asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
545 asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
547
546
core/src/Streamly/Internal/Data/Array/Foreign/Mut/Type.hs+408−509
@@ -18,8 +18,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
18 -- $arrayNotes
18 -- $arrayNotes
19 Array (..)
19 Array (..)
20 , ArrayContents
20 , ArrayContents
21− , arrayToFptrContents
22− , fptrToArrayContents
23 , nilArrayContents
21 , nilArrayContents
24 , touch
22 , touch
25
23
@@ -29,9 +27,10 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
29
27
30 -- *** Uninitialized Arrays
28 -- *** Uninitialized Arrays
31 , newArray
29 , newArray
30+ , newPinnedArrayBytes
32 , newArrayAligned
31 , newArrayAligned
33− , newArrayAlignedUnmanaged
34 , newArrayWith
32 , newArrayWith
33+ , newAlignedArrayContents
35
34
36 -- *** Initialized Arrays
35 -- *** Initialized Arrays
37 , withNewArrayUnsafe
36 , withNewArrayUnsafe
@@ -43,7 +42,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
43 , writeNUnsafe
42 , writeNUnsafe
44 , writeN
43 , writeN
45 , writeNAligned
44 , writeNAligned
46− , writeNAlignedUnmanaged
47
45
48 , writeWith
46 , writeWith
49 , write
47 , write
@@ -52,7 +50,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
52 -- , writeRev
50 -- , writeRev
53
51
54 -- ** From containers
52 -- ** From containers
55− , fromForeignPtrUnsafe
56 , fromListN
53 , fromListN
57 , fromList
54 , fromList
58 , fromListRevN
55 , fromListRevN
@@ -98,7 +95,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
98 -- * Eliminating and Reading
95 -- * Eliminating and Reading
99
96
100 -- ** To streams
97 -- ** To streams
101− , ReadUState(..)
102 , read
98 , read
103 , readRevWith
99 , readRevWith
104 , readRev
100 , readRev
@@ -194,6 +190,8 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
194 , spliceWith
190 , spliceWith
195 , splice
191 , splice
196 , spliceExp
192 , spliceExp
193+ , spliceUnsafe
194+ , putSliceUnsafe
197 -- , putSlice
195 -- , putSlice
198 -- , appendSlice
196 -- , appendSlice
199 -- , appendSliceFrom
197 -- , appendSliceFrom
@@ -214,10 +212,6 @@ where
214#include "ArrayMacros.h"
212#include "ArrayMacros.h"
215#include "MachDeps.h"
213#include "MachDeps.h"
216
214
217−#ifdef USE_C_MALLOC
218−#define USE_FOREIGN_PTR
219−#endif
220−
221import Control.Exception (assert)
215import Control.Exception (assert)
222import Control.DeepSeq (NFData(..), NFData1(..))
216import Control.DeepSeq (NFData(..), NFData1(..))
223import Control.Monad (when, void)
217import Control.Monad (when, void)
@@ -228,27 +222,27 @@ import Data.Semigroup (Semigroup(..))
228#endif
222#endif
229import Data.Word (Word8)
223import Data.Word (Word8)
230import Foreign.C.Types (CSize(..), CInt(..))
224import Foreign.C.Types (CSize(..), CInt(..))
231−import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
232−#ifndef USE_FOREIGN_PTR
233−import Foreign.Marshal.Alloc (mallocBytes)
234−#endif
235−import Foreign.Ptr (plusPtr, minusPtr, castPtr, nullPtr)
225+import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
226+import Streamly.Internal.Control.Exception (assertM)
236import Streamly.Internal.Data.Unboxed
227import Streamly.Internal.Data.Unboxed
228+ ( ArrayContents(..)
229+ , Storable
237− ( Storable, alignment, peek, poke, sizeOf
230+ , alignment
231+ , castContents
232+ , getMutableByteArray#
233+ , peekWith
234+ , pokeWith
235+ , sizeOf
236+ , touch
238 )
237 )
239import GHC.Base
238import GHC.Base
240− ( touch#, IO(..), byteArrayContents#
239+ ( IO(..), byteArrayContents#
241 , Int(..), newAlignedPinnedByteArray#
240 , Int(..), newAlignedPinnedByteArray#
241+ , copyMutableByteArray#, compareByteArrays#
242 )
242 )
243−#ifndef USE_FOREIGN_PTR
244−import GHC.Base (RealWorld, MutableByteArray#)
245−#endif
246import GHC.Base (noinline)
243import GHC.Base (noinline)
247import GHC.Exts (unsafeCoerce#)
244import GHC.Exts (unsafeCoerce#)
248−import GHC.ForeignPtr (ForeignPtr(..), ForeignPtrContents(..))
245+
249−#ifdef USE_C_MALLOC
250−import GHC.ForeignPtr (mallocForeignPtrAlignedBytes)
251−#endif
252import GHC.Ptr (Ptr(..))
246import GHC.Ptr (Ptr(..))
253
247
254import Streamly.Internal.Data.Fold.Type (Fold(..))
248import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -267,9 +261,6 @@ import qualified Streamly.Internal.Data.Producer as Producer
267import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
261import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
268import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
262import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
269import qualified Streamly.Internal.Data.Stream.Type as Stream
263import qualified Streamly.Internal.Data.Stream.Type as Stream
270−#ifdef USE_FOREIGN_PTR
271−import qualified Streamly.Internal.Foreign.Malloc as Malloc
272−#endif
273import qualified Prelude
264import qualified Prelude
274
265
275import Prelude hiding
266import Prelude hiding
@@ -315,37 +306,6 @@ memcmp p1 p2 len = do
315 r <- c_memcmp p1 p2 (fromIntegral len)
306 r <- c_memcmp p1 p2 (fromIntegral len)
316 return $ r == 0
307 return $ r == 0
317
308
318−-------------------------------------------------------------------------------
319−-- Array Contents
320−-------------------------------------------------------------------------------
321−
322−-- We support using ForeignPtrContents or MutableByteArray.
323−
324−#ifdef USE_FOREIGN_PTR
325−newtype ArrayContents = ArrayContents ForeignPtrContents
326−#define UNPACKIF
327−#else
328−-- XXX can use UnliftedNewtypes
329−data ArrayContents = ArrayContents !(MutableByteArray# RealWorld)
330−#define UNPACKIF {-# UNPACK #-}
331−#endif
332−
333−{-# INLINE touch #-}
334−touch :: ArrayContents -> IO ()
335−touch (ArrayContents contents) =
336− IO $ \s -> case touch# contents s of s' -> (# s', () #)
337−
338−fptrToArrayContents :: ForeignPtrContents -> ArrayContents
339−arrayToFptrContents :: ArrayContents -> ForeignPtrContents
340−#ifdef USE_FOREIGN_PTR
341−fptrToArrayContents = ArrayContents
342−arrayToFptrContents (ArrayContents contents) = contents
343−#else
344−fptrToArrayContents (PlainPtr mbarr) = ArrayContents mbarr
345−fptrToArrayContents _ = error "Unsupported foreign ptr"
346−arrayToFptrContents (ArrayContents contents) = PlainPtr contents
347−#endif
348−
349-------------------------------------------------------------------------------
309-------------------------------------------------------------------------------
350-- Array Data Type
310-- Array Data Type
351-------------------------------------------------------------------------------
311-------------------------------------------------------------------------------
@@ -380,11 +340,15 @@ data Array a =
380#ifdef DEVBUILD
340#ifdef DEVBUILD
381 Storable a =>
341 Storable a =>
382#endif
342#endif
343+ -- The array is a range into arrContents. arrContents may be a superset of
344+ -- the slice represented by the array. All offsets are in bytes.
383 Array
345 Array
384− { arrContents :: UNPACKIF !ArrayContents
346+ { arrContents :: {-# UNPACK #-} !(ArrayContents a)
385− , arrStart :: {-# UNPACK #-} !(Ptr a) -- ^ first address
347+ , arrStart :: {-# UNPACK #-} !Int -- ^ index into arrContents
386− , aEnd :: {-# UNPACK #-} !(Ptr a) -- ^ first unused address
348+ , aEnd :: {-# UNPACK #-} !Int -- ^ index into arrContents
387− , aBound :: {-# UNPACK #-} !(Ptr a) -- ^ first address beyond allocated memory
349+ -- Represents the first invalid index of
350+ -- the array.
351+ , aBound :: {-# UNPACK #-} !Int -- ^ first invalid index of arrContents.
388 }
352 }
389
353
390-------------------------------------------------------------------------------
354-------------------------------------------------------------------------------
@@ -413,101 +377,37 @@ data Array a =
413-- /Pre-release/
377-- /Pre-release/
414{-# INLINE newArrayWith #-}
378{-# INLINE newArrayWith #-}
415newArrayWith :: forall m a. (MonadIO m, Storable a)
379newArrayWith :: forall m a. (MonadIO m, Storable a)
416− => (Int -> Int -> m (ArrayContents, Ptr a)) -> Int -> Int -> m (Array a)
380+ => (Int -> Int -> m (ArrayContents a)) -> Int -> Int -> m (Array a)
417newArrayWith alloc alignSize count = do
381newArrayWith alloc alignSize count = do
418 let size = max (count * SIZE_OF(a)) 0
382 let size = max (count * SIZE_OF(a)) 0
419− (contents, p) <- alloc size alignSize
383+ contents <- alloc size alignSize
420 return $ Array
384 return $ Array
421 { arrContents = contents
385 { arrContents = contents
422− , arrStart = p
386+ , arrStart = 0
423− , aEnd = p
387+ , aEnd = 0
424− , aBound = p `plusPtr` size
388+ , aBound = size
425 }
389 }
426
390
427−newAlignedArrayContents :: Int -> Int -> IO (ArrayContents, Ptr a)
391+newAlignedArrayContents :: Int -> Int -> IO (ArrayContents a)
428−#ifdef USE_C_MALLOC
429−newAlignedArrayContents size align = do
392+newAlignedArrayContents nbytes _align | nbytes < 0 =
430− (ForeignPtr addr contents) <- mallocForeignPtrAlignedBytes size align
431− return (ArrayContents contents, Ptr addr)
432−#else
433−newAlignedArrayContents size _align | size < 0 =
434 errorWithoutStackTrace "newAlignedArrayContents: size must be >= 0"
393 errorWithoutStackTrace "newAlignedArrayContents: size must be >= 0"
435−newAlignedArrayContents (I# size) (I# align) = IO $ \s ->
394+newAlignedArrayContents (I# nbytes) (I# align) = IO $ \s ->
436− case newAlignedPinnedByteArray# size align s of
395+ case newAlignedPinnedByteArray# nbytes align s of
437 (# s', mbarr# #) ->
396 (# s', mbarr# #) ->
438− let p = Ptr (byteArrayContents# (unsafeCoerce# mbarr#))
439−#ifdef USE_FOREIGN_PTR
440− c = ArrayContents (PlainPtr mbarr#)
441−#else
442− c = ArrayContents mbarr#
397+ let c = ArrayContents mbarr#
443−#endif
444− in (# s', (c, p) #)
398+ in (# s', c #)
445−#endif
446
399
447{-# NOINLINE nilArrayContents #-}
400{-# NOINLINE nilArrayContents #-}
448−nilArrayContents :: ArrayContents
401+nilArrayContents :: ArrayContents a
449nilArrayContents =
402nilArrayContents =
450− fst $ unsafePerformIO $ newAlignedArrayContents 0 0
403+ unsafePerformIO $ newAlignedArrayContents 0 0
451
404
452nil ::
405nil ::
453#ifdef DEVBUILD
406#ifdef DEVBUILD
454 Storable a =>
407 Storable a =>
455#endif
408#endif
456 Array a
409 Array a
457−nil = Array nilArrayContents nullPtr nullPtr nullPtr
410+nil = Array nilArrayContents 0 0 0
458−
459−-- | @fromForeignPtrUnsafe foreignPtr end bound@ creates an 'Array' that starts
460−-- at the memory pointed by the @foreignPtr@, @end@ is the first unused
461−-- address, and @bound@ is the first address beyond the allocated memory.
462−--
463−-- Unsafe: Make sure that foreignPtr <= end <= bound and (end - start) is an
464−-- integral multiple of the element size. Only PlainPtr type ForeignPtr is
465−-- supported.
466−--
467−-- /Pre-release/
468−--
469−{-# INLINE fromForeignPtrUnsafe #-}
470−fromForeignPtrUnsafe ::
471−#ifdef DEVBUILD
472− Storable a =>
473−#endif
474− ForeignPtr a -> Ptr a -> Ptr a -> Array a
475−fromForeignPtrUnsafe (ForeignPtr start _) _ _
476− | Ptr start == nullPtr = nil
477−fromForeignPtrUnsafe fp@(ForeignPtr start contents) end bound =
478− assert (unsafeForeignPtrToPtr fp <= end && end <= bound)
479− (Array (fptrToArrayContents contents) (Ptr start) end bound)
480−
481−-- | Like 'newArrayWith' but using an allocator that allocates unmanaged pinned
482−-- memory. The memory will never be freed by GHC. This could be useful in
483−-- allocate-once global data structures. Use carefully as incorrect use can
484−-- lead to memory leak.
485−--
486−-- /Internal/
487−{-# INLINE newArrayAlignedUnmanaged #-}
488−newArrayAlignedUnmanaged :: forall m a. (MonadIO m, Storable a) =>
489− Int -> Int -> m (Array a)
490−#ifdef USE_FOREIGN_PTR
491−newArrayAlignedUnmanaged = do
492− newArrayWith mallocForeignPtrAlignedUnmanagedBytes
493−
494− where
495−
496− mallocForeignPtrAlignedUnmanagedBytes size align = do
497− ForeignPtr addr contents <-
498− liftIO $ Malloc.mallocForeignPtrAlignedUnmanagedBytes size align
499− return (ArrayContents contents, Ptr addr)
500−#else
501−newArrayAlignedUnmanaged _align count = do
502− let size = max (count * SIZE_OF(a)) 0
503− p <- liftIO $ mallocBytes size
504− return $ Array
505− { arrContents = nilArrayContents
506− , arrStart = p
507− , aEnd = p
508− , aBound = p `plusPtr` size
509− }
510−#endif
511
411
512-- | Like 'newArrayWith' but using an allocator that aligns the memory to the
412-- | Like 'newArrayWith' but using an allocator that aligns the memory to the
513-- alignment dictated by the 'Storable' instance of the type.
413-- alignment dictated by the 'Storable' instance of the type.
@@ -528,6 +428,23 @@ newArrayAligned = newArrayWith (\s a -> liftIO $ newAlignedArrayContents s a)
528newArray :: forall m a. (MonadIO m, Storable a) => Int -> m (Array a)
428newArray :: forall m a. (MonadIO m, Storable a) => Int -> m (Array a)
529newArray = newArrayAligned (alignment (undefined :: a))
429newArray = newArrayAligned (alignment (undefined :: a))
530
430
431+-- | Allocates a pinned empty array that can hold 'count' items. The memory of
432+-- the array is uninitialized and the allocation is aligned as per the
433+-- 'Storable' instance of the type.
434+--
435+-- /Pre-release/
436+{-# INLINE newPinnedArrayBytes #-}
437+newPinnedArrayBytes :: forall m a. (MonadIO m, Storable a) => Int -> m (Array a)
438+newPinnedArrayBytes bytes = do
439+ contents <-
440+ liftIO $ newAlignedArrayContents bytes (alignment (undefined :: a))
441+ return $ Array
442+ { arrContents = contents
443+ , arrStart = 0
444+ , aEnd = 0
445+ , aBound = bytes
446+ }
447+
531-- | Allocate an Array of the given size and run an IO action passing the array
448-- | Allocate an Array of the given size and run an IO action passing the array
532-- start pointer.
449-- start pointer.
533--
450--
@@ -551,25 +468,15 @@ withNewArrayUnsafe count f = do
551{-# INLINE putIndexUnsafe #-}
468{-# INLINE putIndexUnsafe #-}
552putIndexUnsafe :: forall m a. (MonadIO m, Storable a)
469putIndexUnsafe :: forall m a. (MonadIO m, Storable a)
553 => Int -> a -> Array a -> m ()
470 => Int -> a -> Array a -> m ()
554−putIndexUnsafe i x arr@(Array{..}) =
471+putIndexUnsafe i x (Array {..}) = do
555− asPtrUnsafe arr $ \ptr -> do
472+ let index = INDEX_OF(arrStart, i, a)
556− let elemPtr = PTR_INDEX(ptr,i,a)
557− assert (i >= 0 && PTR_VALID(elemPtr,aEnd,a)) (return ())
473+ assert (i >= 0 && INDEX_VALID(index, aEnd, a)) (return ())
558− liftIO $ poke elemPtr x
474+ liftIO $ pokeWith arrContents index x
559
475
560invalidIndex :: String -> Int -> a
476invalidIndex :: String -> Int -> a
561invalidIndex label i =
477invalidIndex label i =
562 error $ label ++ ": invalid array index " ++ show i
478 error $ label ++ ": invalid array index " ++ show i
563
479
564−{-# INLINE putIndexPtr #-}
565−putIndexPtr :: forall m a. (MonadIO m, Storable a) =>
566− Ptr a -> Ptr a -> Int -> a -> m ()
567−putIndexPtr start end i x = do
568− let elemPtr = PTR_INDEX(start,i,a)
569− if i >= 0 && PTR_VALID(elemPtr,end,a)
570− then liftIO $ poke elemPtr x
571− else invalidIndex "putIndexPtr" i
572−
573-- | /O(1)/ Write the given element at the given index in the array.
480-- | /O(1)/ Write the given element at the given index in the array.
574-- Performs in-place mutation of the array.
481-- Performs in-place mutation of the array.
575--
482--
@@ -579,10 +486,12 @@ putIndexPtr start end i x = do
579--
486--
580-- /Pre-release/
487-- /Pre-release/
581{-# INLINE putIndex #-}
488{-# INLINE putIndex #-}
582−putIndex :: (MonadIO m, Storable a) => Int -> a -> Array a -> m ()
489+putIndex :: forall m a. (MonadIO m, Storable a) => Int -> a -> Array a -> m ()
583−putIndex i x arr =
490+putIndex i x Array{..} = do
584− asPtrUnsafe arr
491+ let index = INDEX_OF(arrStart,i,a)
585− $ \p -> putIndexPtr p (aEnd arr) i x
492+ if i >= 0 && INDEX_VALID(index,aEnd,a)
493+ then liftIO $ pokeWith arrContents index x
494+ else invalidIndex "putIndex" i
586
495
587-- | Write an input stream of (index, value) pairs to an array. Throws an
496-- | Write an input stream of (index, value) pairs to an array. Throws an
588-- error if any index is out of bounds.
497-- error if any index is out of bounds.
@@ -591,51 +500,40 @@ putIndex i x arr =
591{-# INLINE putIndices #-}
500{-# INLINE putIndices #-}
592putIndices :: forall m a. (MonadIO m, Storable a)
501putIndices :: forall m a. (MonadIO m, Storable a)
593 => Array a -> Fold m (Int, a) ()
502 => Array a -> Fold m (Int, a) ()
594−putIndices Array{..} = FL.rmapM extract $ FL.foldlM' step initial
503+putIndices arr = FL.foldlM' step (return ())
595
504
596 where
505 where
597
506
598− initial = return ()
599−
600− step () (i, x) = liftIO (putIndexPtr arrStart aEnd i x)
507+ step () (i, x) = liftIO (putIndex i x arr)
601−
602− extract () = liftIO $ touch arrContents
603
508
604-- | Modify a given index of an array using a modifier function.
509-- | Modify a given index of an array using a modifier function.
605--
510--
606-- /Pre-release/
511-- /Pre-release/
607modifyIndexUnsafe :: forall m a b. (MonadIO m, Storable a) =>
512modifyIndexUnsafe :: forall m a b. (MonadIO m, Storable a) =>
608 Int -> (a -> (a, b)) -> Array a -> m b
513 Int -> (a -> (a, b)) -> Array a -> m b
609−modifyIndexUnsafe i f arr@(Array{..}) = do
514+modifyIndexUnsafe i f (Array{..}) = liftIO $ do
610− liftIO $ asPtrUnsafe arr $ \ptr -> do
515+ let index = INDEX_OF(arrStart,i,a)
611− let elemPtr = PTR_INDEX(ptr,i,a)
612− assert (i >= 0 && PTR_NEXT(elemPtr,a) <= aEnd) (return ())
516+ assert (i >= 0 && INDEX_NEXT(index,a) <= aEnd) (return ())
613− r <- peek elemPtr
517+ r <- peekWith arrContents index
614 let (x, res) = f r
518 let (x, res) = f r
615− poke elemPtr x
519+ pokeWith arrContents index x
616 return res
520 return res
617
521
618−{-# INLINE modifyIndexPtr #-}
619−modifyIndexPtr :: forall m a b. (MonadIO m, Storable a) =>
620− Int -> (a -> (a, b)) -> Ptr a -> Ptr a -> m b
621−modifyIndexPtr i f start end = liftIO $ do
622− let elemPtr = PTR_INDEX(start,i,a)
623− if i >= 0 && PTR_VALID(elemPtr,end,a)
624− then do
625− r <- peek elemPtr
626− let (x, res) = f r
627− poke elemPtr x
628− return res
629− else invalidIndex "modifyIndex" i
630−
631-- | Modify a given index of an array using a modifier function.
522-- | Modify a given index of an array using a modifier function.
632--
523--
633-- /Pre-release/
524-- /Pre-release/
634modifyIndex :: forall m a b. (MonadIO m, Storable a) =>
525modifyIndex :: forall m a b. (MonadIO m, Storable a) =>
635 Int -> (a -> (a, b)) -> Array a -> m b
526 Int -> (a -> (a, b)) -> Array a -> m b
636−modifyIndex i f arr@(Array{..}) = do
527+modifyIndex i f Array{..} = do
637− liftIO $ asPtrUnsafe arr $ \ptr -> do
528+ let index = INDEX_OF(arrStart,i,a)
638− modifyIndexPtr i f ptr aEnd
529+ if i >= 0 && INDEX_VALID(index,aEnd,a)
530+ then liftIO $ do
531+ r <- peekWith arrContents index
532+ let (x, res) = f r
533+ pokeWith arrContents index x
534+ return res
535+ else invalidIndex "modifyIndex" i
536+
639
537
640-- | Modify the array indices generated by the supplied stream.
538-- | Modify the array indices generated by the supplied stream.
641--
539--
@@ -643,41 +541,39 @@ modifyIndex i f arr@(Array{..}) = do
643{-# INLINE modifyIndices #-}
541{-# INLINE modifyIndices #-}
644modifyIndices :: forall m a . (MonadIO m, Storable a)
542modifyIndices :: forall m a . (MonadIO m, Storable a)
645 => (Int -> a -> a) -> Array a -> Fold m Int ()
543 => (Int -> a -> a) -> Array a -> Fold m Int ()
646−modifyIndices f Array{..} = Fold step initial extract
544+modifyIndices f arr = FL.foldlM' step initial
647
545
648 where
546 where
649
547
650− initial = return $ FL.Partial ()
548+ initial = return ()
651
549
652 step () i =
550 step () i =
653 let f1 x = (f i x, ())
551 let f1 x = (f i x, ())
654− in FL.Partial <$> liftIO (modifyIndexPtr i f1 arrStart aEnd)
552+ in modifyIndex i f1 arr
655−
656− extract () = liftIO $ touch arrContents
657
553
658-- | Modify each element of an array using the supplied modifier function.
554-- | Modify each element of an array using the supplied modifier function.
659--
555--
660-- /Pre-release/
556-- /Pre-release/
661modify :: forall m a. (MonadIO m, Storable a)
557modify :: forall m a. (MonadIO m, Storable a)
662 => (a -> a) -> Array a -> m ()
558 => (a -> a) -> Array a -> m ()
663−modify f arr@Array{..} = liftIO $
559+modify f Array{..} = liftIO $
664− asPtrUnsafe arr go
560+ go arrStart
665
561
666 where
562 where
667
563
668− go ptr =
564+ go i =
669− when (PTR_VALID(ptr,aEnd,a)) $ do
565+ when (INDEX_VALID(i,aEnd,a)) $ do
670− r <- peek ptr
566+ r <- peekWith arrContents i
671− poke ptr (f r)
567+ pokeWith arrContents i (f r)
672− go (PTR_NEXT(ptr,a))
568+ go (INDEX_NEXT(i,a))
673
569
674−{-# INLINE swapPtrs #-}
570+{-# INLINE swapArrayByteIndices #-}
675−swapPtrs :: Storable a => Ptr a -> Ptr a -> IO ()
571+swapArrayByteIndices :: Storable a => ArrayContents a -> Int -> Int -> IO ()
676−swapPtrs ptr1 ptr2 = do
572+swapArrayByteIndices arrContents i1 i2 = do
677− r1 <- peek ptr1
573+ r1 <- peekWith arrContents i1
678− r2 <- peek ptr2
574+ r2 <- peekWith arrContents i2
679− poke ptr1 r2
575+ pokeWith arrContents i1 r2
680− poke ptr2 r1
576+ pokeWith arrContents i2 r1
681
577
682-- | Swap the elements at two indices without validating the indices.
578-- | Swap the elements at two indices without validating the indices.
683--
579--
@@ -687,11 +583,10 @@ swapPtrs ptr1 ptr2 = do
687{-# INLINE unsafeSwapIndices #-}
583{-# INLINE unsafeSwapIndices #-}
688unsafeSwapIndices :: forall m a. (MonadIO m, Storable a)
584unsafeSwapIndices :: forall m a. (MonadIO m, Storable a)
689 => Int -> Int -> Array a -> m ()
585 => Int -> Int -> Array a -> m ()
690−unsafeSwapIndices i1 i2 arr = liftIO $ do
586+unsafeSwapIndices i1 i2 Array{..} = liftIO $ do
691− asPtrUnsafe arr $ \ptr -> do
587+ let t1 = INDEX_OF(arrStart,i1,a)
692− let ptr1 = PTR_INDEX(ptr,i1,a)
588+ t2 = INDEX_OF(arrStart,i2,a)
693− ptr2 = PTR_INDEX(ptr,i2,a)
589+ swapArrayByteIndices arrContents t1 t2
694− swapPtrs ptr1 (ptr2 :: Ptr a)
695
590
696-- | Swap the elements at two indices.
591-- | Swap the elements at two indices.
697--
592--
@@ -699,13 +594,13 @@ unsafeSwapIndices i1 i2 arr = liftIO $ do
699swapIndices :: forall m a. (MonadIO m, Storable a)
594swapIndices :: forall m a. (MonadIO m, Storable a)
700 => Int -> Int -> Array a -> m ()
595 => Int -> Int -> Array a -> m ()
701swapIndices i1 i2 Array{..} = liftIO $ do
596swapIndices i1 i2 Array{..} = liftIO $ do
702− let ptr1 = PTR_INDEX(arrStart,i1,a)
597+ let t1 = INDEX_OF(arrStart,i1,a)
703− ptr2 = PTR_INDEX(arrStart,i2,a)
598+ t2 = INDEX_OF(arrStart,i2,a)
704− when (i1 < 0 || PTR_INVALID(ptr1,aEnd,a))
599+ when (i1 < 0 || INDEX_INVALID(t1,aEnd,a))
705 $ invalidIndex "swapIndices" i1
600 $ invalidIndex "swapIndices" i1
706− when (i2 < 0 || PTR_INVALID(ptr2,aEnd,a))
601+ when (i2 < 0 || INDEX_INVALID(t2,aEnd,a))
707 $ invalidIndex "swapIndices" i2
602 $ invalidIndex "swapIndices" i2
708− swapPtrs ptr1 (ptr2 :: Ptr a)
603+ swapArrayByteIndices arrContents t1 t2
709
604
710-------------------------------------------------------------------------------
605-------------------------------------------------------------------------------
711-- Rounding
606-- Rounding
@@ -797,31 +692,38 @@ roundDownTo elemSize size = size - (size `mod` elemSize)
797-- XXX See if resizing can be implemented by reading the old array as a stream
692-- XXX See if resizing can be implemented by reading the old array as a stream
798-- and then using writeN to the new array.
693-- and then using writeN to the new array.
799--
694--
695+-- NOTE: we are passing elemSize explicitly to avoid a Storable constraint.
696+-- Since this is not inlined Storable consrraint leads to dictionary passing
697+-- which complicates some inspection tests.
698+--
800{-# NOINLINE reallocAligned #-}
699{-# NOINLINE reallocAligned #-}
801reallocAligned :: Int -> Int -> Int -> Array a -> IO (Array a)
700reallocAligned :: Int -> Int -> Int -> Array a -> IO (Array a)
802−reallocAligned elemSize alignSize newCapacity Array{..} = do
701+reallocAligned elemSize alignSize newCapacityInBytes Array{..} = do
803− assert (aEnd <= aBound) (return ())
702+ assertM (aEnd <= aBound)
804
703
805 -- Allocate new array
704 -- Allocate new array
806− let newCapMax = roundUpLargeArray newCapacity
705+ let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
807− (contents, pNew) <- newAlignedArrayContents newCapMax alignSize
706+ contents <- newAlignedArrayContents newCapMaxInBytes alignSize
707+ let !(ArrayContents mbarrFrom#) = arrContents
708+ !(ArrayContents mbarrTo#) = contents
808
709
809 -- Copy old data
710 -- Copy old data
810 let oldStart = arrStart
711 let oldStart = arrStart
712+ !(I# oldStartInBytes#) = oldStart
811− oldSize = aEnd `minusPtr` oldStart
713+ oldSizeInBytes = aEnd - oldStart
812− newCap = roundDownTo elemSize newCapMax
714+ newCapInBytes = roundDownTo elemSize newCapMaxInBytes
813− newLen = min oldSize newCap
715+ !newLenInBytes@(I# newLenInBytes#) = min oldSizeInBytes newCapInBytes
814− assert (oldSize `mod` elemSize == 0) (return ())
716+ assert (oldSizeInBytes `mod` elemSize == 0) (return ())
815− assert (newLen >= 0) (return ())
717+ assert (newLenInBytes >= 0) (return ())
816− assert (newLen `mod` elemSize == 0) (return ())
718+ assert (newLenInBytes `mod` elemSize == 0) (return ())
817− memcpy (castPtr pNew) (castPtr oldStart) newLen
719+ IO $ \s# -> (# copyMutableByteArray# mbarrFrom# oldStartInBytes#
818− touch arrContents
720+ mbarrTo# 0# newLenInBytes# s#, () #)
819
721
820 return $ Array
722 return $ Array
821− { arrStart = pNew
723+ { arrStart = 0
822 , arrContents = contents
724 , arrContents = contents
823− , aEnd = pNew `plusPtr` newLen
725+ , aEnd = newLenInBytes
824− , aBound = pNew `plusPtr` newCap
726+ , aBound = newCapInBytes
825 }
727 }
826
728
827-- | @realloc newCapacity array@ reallocates the array to the specified
729-- | @realloc newCapacity array@ reallocates the array to the specified
@@ -834,10 +736,10 @@ reallocAligned elemSize alignSize newCapacity Array{..} = do
834--
736--
835{-# INLINABLE realloc #-}
737{-# INLINABLE realloc #-}
836realloc :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m (Array a)
738realloc :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m (Array a)
837−realloc n arr =
739+realloc bytes arr =
838− liftIO $ reallocAligned (SIZE_OF(a)) (alignment (undefined :: a)) n arr
740+ liftIO $ reallocAligned (SIZE_OF(a)) (alignment (undefined :: a)) bytes arr
839
741
840−-- | @reallocWith label capSizer minIncrement array@. The label is used
742+-- | @reallocWith label capSizer minIncrBytes array@. The label is used
841-- in error messages and the capSizer is used to determine the capacity of the
743-- in error messages and the capSizer is used to determine the capacity of the
842-- new array in bytes given the current byte length of the array.
744-- new array in bytes given the current byte length of the array.
843reallocWith :: forall m a. (MonadIO m , Storable a) =>
745reallocWith :: forall m a. (MonadIO m , Storable a) =>
@@ -846,22 +748,24 @@ reallocWith :: forall m a. (MonadIO m , Storable a) =>
846 -> Int
748 -> Int
847 -> Array a
749 -> Array a
848 -> m (Array a)
750 -> m (Array a)
849−reallocWith label capSizer minIncr arr = do
751+reallocWith label capSizer minIncrBytes arr = do
850− let oldSize = aEnd arr `minusPtr` arrStart arr
752+ let oldSizeBytes = aEnd arr - arrStart arr
851− newCap = capSizer oldSize
753+ newCapBytes = capSizer oldSizeBytes
852− newSize = oldSize + minIncr
754+ newSizeBytes = oldSizeBytes + minIncrBytes
853− safeCap = max newCap newSize
755+ safeCapBytes = max newCapBytes newSizeBytes
854− assert (newCap >= newSize || error (badSize newSize)) (return ())
756+ assertM (safeCapBytes >= newSizeBytes || error (badSize newSizeBytes))
757+
855− realloc safeCap arr
758+ realloc safeCapBytes arr
856
759
857 where
760 where
858
761
859− badSize newSize = concat
762+ badSize newSize =
763+ concat
860− [ label
764+ [ label
861− , ": new array size is less than required size "
765+ , ": new array size (in bytes) is less than required size "
862− , show newSize
766+ , show newSize
863− , ". Please check the sizing function passed."
767+ , ". Please check the sizing function passed."
864− ]
768+ ]
865
769
866-- | @resize newCapacity array@ changes the total capacity of the array so that
770-- | @resize newCapacity array@ changes the total capacity of the array so that
867-- it is enough to hold the specified number of elements. Nothing is done if
771-- it is enough to hold the specified number of elements. Nothing is done if
@@ -874,27 +778,27 @@ reallocWith label capSizer minIncr arr = do
874{-# INLINE resize #-}
778{-# INLINE resize #-}
875resize :: forall m a. (MonadIO m, Storable a) =>
779resize :: forall m a. (MonadIO m, Storable a) =>
876 Int -> Array a -> m (Array a)
780 Int -> Array a -> m (Array a)
877−resize n arr@Array{..} = do
781+resize nElems arr@Array{..} = do
878− let req = SIZE_OF(a) * n
782+ let req = SIZE_OF(a) * nElems
879− len = aEnd `minusPtr` arrStart
783+ len = aEnd - arrStart
880 if req < len
784 if req < len
881 then return arr
785 then return arr
882 else realloc req arr
786 else realloc req arr
883
787
884−-- | Like 'resize' but if the capacity is more than 'largeObjectThreshold' then
788+-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'
885−-- it is rounded up to the closest power of 2.
789+-- then it is rounded up to the closest power of 2.
886--
790--
887-- /Pre-release/
791-- /Pre-release/
888{-# INLINE resizeExp #-}
792{-# INLINE resizeExp #-}
889resizeExp :: forall m a. (MonadIO m, Storable a) =>
793resizeExp :: forall m a. (MonadIO m, Storable a) =>
890 Int -> Array a -> m (Array a)
794 Int -> Array a -> m (Array a)
891−resizeExp n arr@Array{..} = do
795+resizeExp nElems arr@Array{..} = do
892− let req = roundUpLargeArray (SIZE_OF(a) * n)
796+ let req = roundUpLargeArray (SIZE_OF(a) * nElems)
893 req1 =
797 req1 =
894 if req > largeObjectThreshold
798 if req > largeObjectThreshold
895 then roundUpToPower2 req
799 then roundUpToPower2 req
896 else req
800 else req
897− len = aEnd `minusPtr` arrStart
801+ len = aEnd - arrStart
898 if req1 < len
802 if req1 < len
899 then return arr
803 then return arr
900 else realloc req1 arr
804 else realloc req1 arr
@@ -912,10 +816,10 @@ rightSize :: forall m a. (MonadIO m, Storable a) => Array a -> m (Array a)
912rightSize arr@Array{..} = do
816rightSize arr@Array{..} = do
913 assert (aEnd <= aBound) (return ())
817 assert (aEnd <= aBound) (return ())
914 let start = arrStart
818 let start = arrStart
915− len = aEnd `minusPtr` start
819+ len = aEnd - start
916− capacity = aBound `minusPtr` start
820+ capacity = aBound - start
917 target = roundUpLargeArray len
821 target = roundUpLargeArray len
918− waste = aBound `minusPtr` aEnd
822+ waste = aBound - aEnd
919 assert (target >= len) (return ())
823 assert (target >= len) (return ())
920 assert (len `mod` SIZE_OF(a) == 0) (return ())
824 assert (len `mod` SIZE_OF(a) == 0) (return ())
921 -- We trade off some wastage (25%) to avoid reallocations and copying.
825 -- We trade off some wastage (25%) to avoid reallocations and copying.
@@ -940,11 +844,10 @@ rightSize arr@Array{..} = do
940--
844--
941-- /Internal/
845-- /Internal/
942{-# INLINE snocNewEnd #-}
846{-# INLINE snocNewEnd #-}
943−snocNewEnd :: (MonadIO m, Storable a) => Ptr a -> Array a -> a -> m (Array a)
847+snocNewEnd :: (MonadIO m, Storable a) => Int -> Array a -> a -> m (Array a)
944snocNewEnd newEnd arr@Array{..} x = liftIO $ do
848snocNewEnd newEnd arr@Array{..} x = liftIO $ do
945 assert (newEnd <= aBound) (return ())
849 assert (newEnd <= aBound) (return ())
946− poke aEnd x
850+ pokeWith arrContents aEnd x
947− touch arrContents
948 return $ arr {aEnd = newEnd}
851 return $ arr {aEnd = newEnd}
949
852
950-- | Really really unsafe, appends the element into the first array, may
853-- | Really really unsafe, appends the element into the first array, may
@@ -955,7 +858,7 @@ snocNewEnd newEnd arr@Array{..} x = liftIO $ do
955{-# INLINE snocUnsafe #-}
858{-# INLINE snocUnsafe #-}
956snocUnsafe :: forall m a. (MonadIO m, Storable a) =>
859snocUnsafe :: forall m a. (MonadIO m, Storable a) =>
957 Array a -> a -> m (Array a)
860 Array a -> a -> m (Array a)
958−snocUnsafe arr@Array{..} = snocNewEnd (PTR_NEXT(aEnd,a)) arr
861+snocUnsafe arr@Array{..} = snocNewEnd (INDEX_NEXT(aEnd,a)) arr
959
862
960-- | Like 'snoc' but does not reallocate when pre-allocated array capacity
863-- | Like 'snoc' but does not reallocate when pre-allocated array capacity
961-- becomes full.
864-- becomes full.
@@ -965,7 +868,7 @@ snocUnsafe arr@Array{..} = snocNewEnd (PTR_NEXT(aEnd,a)) arr
965snocMay :: forall m a. (MonadIO m, Storable a) =>
868snocMay :: forall m a. (MonadIO m, Storable a) =>
966 Array a -> a -> m (Maybe (Array a))
869 Array a -> a -> m (Maybe (Array a))
967snocMay arr@Array{..} x = liftIO $ do
870snocMay arr@Array{..} x = liftIO $ do
968− let newEnd = PTR_NEXT(aEnd,a)
871+ let newEnd = INDEX_NEXT(aEnd,a)
969 if newEnd <= aBound
872 if newEnd <= aBound
970 then Just <$> snocNewEnd newEnd arr x
873 then Just <$> snocNewEnd newEnd arr x
971 else return Nothing
874 else return Nothing
@@ -1001,7 +904,7 @@ snocWith :: forall m a. (MonadIO m, Storable a) =>
1001 -> a
904 -> a
1002 -> m (Array a)
905 -> m (Array a)
1003snocWith allocSize arr x = liftIO $ do
906snocWith allocSize arr x = liftIO $ do
1004− let newEnd = PTR_NEXT(aEnd arr,a)
907+ let newEnd = INDEX_NEXT(aEnd arr,a)
1005 if newEnd <= aBound arr
908 if newEnd <= aBound arr
1006 then snocNewEnd newEnd arr x
909 then snocNewEnd newEnd arr x
1007 else snocWithRealloc allocSize arr x
910 else snocWithRealloc allocSize arr x
@@ -1049,42 +952,27 @@ snoc = snocWith f
1049-- Random reads
952-- Random reads
1050-------------------------------------------------------------------------------
953-------------------------------------------------------------------------------
1051
954
955+-- XXX Can this be deduplicated with array/foreign
956+
1052-- | Return the element at the specified index without checking the bounds.
957-- | Return the element at the specified index without checking the bounds.
1053--
958--
1054-- Unsafe because it does not check the bounds of the array.
959-- Unsafe because it does not check the bounds of the array.
1055{-# INLINE_NORMAL getIndexUnsafe #-}
960{-# INLINE_NORMAL getIndexUnsafe #-}
1056getIndexUnsafe :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
961getIndexUnsafe :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
1057−getIndexUnsafe i arr@(Array {..}) =
962+getIndexUnsafe i (Array {..}) = do
1058− asPtrUnsafe arr $ \ptr -> do
963+ let index = INDEX_OF(arrStart,i,a)
1059− let elemPtr = PTR_INDEX(ptr,i,a)
1060− assert (i >= 0 && PTR_VALID(elemPtr,aEnd,a)) (return ())
964+ assert (i >= 0 && INDEX_VALID(index,aEnd,a)) (return ())
1061− liftIO $ peek elemPtr
965+ liftIO $ peekWith arrContents index
1062−
1063−{-# INLINE getIndexPtr #-}
1064−getIndexPtr :: forall m a. (MonadIO m, Storable a) =>
1065− Ptr a -> Ptr a -> Int -> m a
1066−getIndexPtr start end i = do
1067− let elemPtr = PTR_INDEX(start,i,a)
1068− if i >= 0 && PTR_VALID(elemPtr,end,a)
1069− then liftIO $ peek elemPtr
1070− else invalidIndex "getIndexPtr" i
1071
966
1072-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
967-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
1073--
968--
1074{-# INLINE getIndex #-}
969{-# INLINE getIndex #-}
1075−getIndex :: (MonadIO m, Storable a) => Int -> Array a -> m a
970+getIndex :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
1076−getIndex i arr =
971+getIndex i Array{..} = do
1077− asPtrUnsafe arr
972+ let index = INDEX_OF(arrStart,i,a)
1078− $ \p -> getIndexPtr p (aEnd arr) i
973+ if i >= 0 && INDEX_VALID(index,aEnd,a)
1079−
974+ then liftIO $ peekWith arrContents index
1080−{-# INLINE getIndexPtrRev #-}
1081−getIndexPtrRev :: forall m a. (MonadIO m, Storable a) =>
1082− Ptr a -> Ptr a -> Int -> m a
1083−getIndexPtrRev start end i = do
1084− let elemPtr = PTR_RINDEX(end,i,a)
1085− if i >= 0 && elemPtr >= start
1086− then liftIO $ peek elemPtr
1087− else invalidIndex "getIndexPtrRev" i
975+ else invalidIndex "getIndex" i
1088
976
1089-- | /O(1)/ Lookup the element at the given index from the end of the array.
977-- | /O(1)/ Lookup the element at the given index from the end of the array.
1090-- Index starts from 0.
978-- Index starts from 0.
@@ -1092,10 +980,12 @@ getIndexPtrRev start end i = do
1092-- Slightly faster than computing the forward index and using getIndex.
980-- Slightly faster than computing the forward index and using getIndex.
1093--
981--
1094{-# INLINE getIndexRev #-}
982{-# INLINE getIndexRev #-}
1095−getIndexRev :: (MonadIO m, Storable a) => Int -> Array a -> m a
983+getIndexRev :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
1096−getIndexRev i arr =
984+getIndexRev i Array{..} = do
1097− asPtrUnsafe arr
985+ let index = RINDEX_OF(aEnd,i,a)
1098− $ \p -> getIndexPtrRev p (aEnd arr) i
986+ if i >= 0 && index >= arrStart
987+ then liftIO $ peekWith arrContents index
988+ else invalidIndex "getIndexRev" i
1099
989
1100data GetIndicesState contents start end st =
990data GetIndicesState contents start end st =
1101 GetIndicesState contents start end st
991 GetIndicesState contents start end st
@@ -1112,20 +1002,18 @@ getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
1112
1002
1113 where
1003 where
1114
1004
1115− inject (Array contents start (Ptr end) _) =
1005+ inject (Array contents start end _) =
1116− return $ GetIndicesState contents start (Ptr end) sti
1006+ return $ GetIndicesState contents start end sti
1117
1007
1118 {-# INLINE_LATE step #-}
1008 {-# INLINE_LATE step #-}
1119 step (GetIndicesState contents start end st) = do
1009 step (GetIndicesState contents start end st) = do
1120 r <- stepi defState st
1010 r <- stepi defState st
1121 case r of
1011 case r of
1122 D.Yield i s -> do
1012 D.Yield i s -> do
1123− x <- liftio $ getIndexPtr start end i
1013+ x <- liftio $ getIndex i (Array contents start end undefined)
1124 return $ D.Yield x (GetIndicesState contents start end s)
1014 return $ D.Yield x (GetIndicesState contents start end s)
1125 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
1015 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
1126− D.Stop -> do
1127− liftio $ touch contents
1128− return D.Stop
1016+ D.Stop -> return D.Stop
1129
1017
1130{-# INLINE getIndices #-}
1018{-# INLINE getIndices #-}
1131getIndices :: (MonadIO m, Storable a) => Stream m Int -> Unfold m (Array a) a
1019getIndices :: (MonadIO m, Storable a) => Stream m Int -> Unfold m (Array a) a
@@ -1151,8 +1039,8 @@ getSliceUnsafe :: forall a. Storable a
1151 -> Array a
1039 -> Array a
1152 -> Array a
1040 -> Array a
1153getSliceUnsafe index len (Array contents start e _) =
1041getSliceUnsafe index len (Array contents start e _) =
1154− let fp1 = PTR_INDEX(start,index,a)
1042+ let fp1 = INDEX_OF(start,index,a)
1155− end = fp1 `plusPtr` (len * SIZE_OF(a))
1043+ end = fp1 + (len * SIZE_OF(a))
1156 in assert
1044 in assert
1157 (index >= 0 && len >= 0 && end <= e)
1045 (index >= 0 && len >= 0 && end <= e)
1158 -- Note: In a slice we always use bound = end so that the slice
1046 -- Note: In a slice we always use bound = end so that the slice
@@ -1170,8 +1058,8 @@ getSlice :: forall a. Storable a =>
1170 -> Array a
1058 -> Array a
1171 -> Array a
1059 -> Array a
1172getSlice index len (Array contents start e _) =
1060getSlice index len (Array contents start e _) =
1173− let fp1 = PTR_INDEX(start,index,a)
1061+ let fp1 = INDEX_OF(start,index,a)
1174− end = fp1 `plusPtr` (len * SIZE_OF(a))
1062+ end = fp1 + (len * SIZE_OF(a))
1175 in if index >= 0 && len >= 0 && end <= e
1063 in if index >= 0 && len >= 0 && end <= e
1176 -- Note: In a slice we always use bound = end so that the slice user
1064 -- Note: In a slice we always use bound = end so that the slice user
1177 -- cannot overwrite elements beyond the end of the slice.
1065 -- cannot overwrite elements beyond the end of the slice.
@@ -1196,15 +1084,15 @@ getSlice index len (Array contents start e _) =
1196reverse :: forall m a. (MonadIO m, Storable a) => Array a -> m ()
1084reverse :: forall m a. (MonadIO m, Storable a) => Array a -> m ()
1197reverse Array{..} = liftIO $ do
1085reverse Array{..} = liftIO $ do
1198 let l = arrStart
1086 let l = arrStart
1199− h = PTR_PREV(aEnd,a)
1087+ h = INDEX_PREV(aEnd,a)
1200 in swap l h
1088 in swap l h
1201
1089
1202 where
1090 where
1203
1091
1204 swap l h = do
1092 swap l h = do
1205 when (l < h) $ do
1093 when (l < h) $ do
1206− swapPtrs l h
1094+ swapArrayByteIndices arrContents l h
1207− swap (PTR_NEXT(l,a)) (PTR_PREV(h,a))
1095+ swap (INDEX_NEXT(l,a)) (INDEX_PREV(h,a))
1208
1096
1209-- | Generate the next permutation of the sequence, returns False if this is
1097-- | Generate the next permutation of the sequence, returns False if this is
1210-- the last permutation.
1098-- the last permutation.
@@ -1226,7 +1114,7 @@ partitionBy f arr@Array{..} = liftIO $ do
1226 if arrStart >= aEnd
1114 if arrStart >= aEnd
1227 then return (arr, arr)
1115 then return (arr, arr)
1228 else do
1116 else do
1229− ptr <- go arrStart (PTR_PREV(aEnd,a))
1117+ ptr <- go arrStart (INDEX_PREV(aEnd,a))
1230 let pl = Array arrContents arrStart ptr ptr
1118 let pl = Array arrContents arrStart ptr ptr
1231 pr = Array arrContents ptr aEnd aEnd
1119 pr = Array arrContents ptr aEnd aEnd
1232 return (pl, pr)
1120 return (pl, pr)
@@ -1235,11 +1123,11 @@ partitionBy f arr@Array{..} = liftIO $ do
1235
1123
1236 -- Invariant low < high on entry, and on return as well
1124 -- Invariant low < high on entry, and on return as well
1237 moveHigh low high = do
1125 moveHigh low high = do
1238− h <- peek high
1126+ h <- peekWith arrContents high
1239 if f h
1127 if f h
1240 then
1128 then
1241 -- Correctly classified, continue the loop
1129 -- Correctly classified, continue the loop
1242− let high1 = PTR_PREV(high,a)
1130+ let high1 = INDEX_PREV(high,a)
1243 in if low == high1
1131 in if low == high1
1244 then return Nothing
1132 then return Nothing
1245 else moveHigh low high1
1133 else moveHigh low high1
@@ -1255,7 +1143,7 @@ partitionBy f arr@Array{..} = liftIO $ do
1255 -- low <= high
1143 -- low <= high
1256 -- Both low and high are valid locations within the array
1144 -- Both low and high are valid locations within the array
1257 go low high = do
1145 go low high = do
1258− l <- peek low
1146+ l <- peekWith arrContents low
1259 if f l
1147 if f l
1260 then
1148 then
1261 -- low is wrongly classified
1149 -- low is wrongly classified
@@ -1266,17 +1154,17 @@ partitionBy f arr@Array{..} = liftIO $ do
1266 case r of
1154 case r of
1267 Nothing -> return low
1155 Nothing -> return low
1268 Just (high1, h) -> do -- low < high1
1156 Just (high1, h) -> do -- low < high1
1269− poke low h
1157+ pokeWith arrContents low h
1270− poke high1 l
1158+ pokeWith arrContents high1 l
1271− let low1 = PTR_NEXT(low,a)
1159+ let low1 = INDEX_NEXT(low,a)
1272− high2 = PTR_PREV(high1,a)
1160+ high2 = INDEX_PREV(high1,a)
1273 if low1 <= high2
1161 if low1 <= high2
1274 then go low1 high2
1162 then go low1 high2
1275 else return low1 -- low1 > high2
1163 else return low1 -- low1 > high2
1276
1164
1277 else do
1165 else do
1278 -- low is correctly classified
1166 -- low is correctly classified
1279− let low1 = PTR_NEXT(low,a)
1167+ let low1 = INDEX_NEXT(low,a)
1280 if low == high
1168 if low == high
1281 then return low1
1169 then return low1
1282 else go low1 high
1170 else go low1 high
@@ -1329,12 +1217,12 @@ mergeBy = undefined
1329{-# INLINE byteLength #-}
1217{-# INLINE byteLength #-}
1330byteLength :: Array a -> Int
1218byteLength :: Array a -> Int
1331byteLength Array{..} =
1219byteLength Array{..} =
1332− let len = aEnd `minusPtr` arrStart
1220+ let len = aEnd - arrStart
1333 in assert (len >= 0) len
1221 in assert (len >= 0) len
1334
1222
1335-- Note: try to avoid the use of length in performance sensitive internal
1223-- Note: try to avoid the use of length in performance sensitive internal
1336-- routines as it involves a costly 'div' operation. Instead use the end ptr
1224-- routines as it involves a costly 'div' operation. Instead use the end ptr
1337−-- int he array to check the bounds etc.
1225+-- in the array to check the bounds etc.
1338--
1226--
1339-- | /O(1)/ Get the length of the array i.e. the number of elements in the
1227-- | /O(1)/ Get the length of the array i.e. the number of elements in the
1340-- array.
1228-- array.
@@ -1357,7 +1245,7 @@ length arr =
1357{-# INLINE byteCapacity #-}
1245{-# INLINE byteCapacity #-}
1358byteCapacity :: Array a -> Int
1246byteCapacity :: Array a -> Int
1359byteCapacity Array{..} =
1247byteCapacity Array{..} =
1360− let len = aBound `minusPtr` arrStart
1248+ let len = aBound - arrStart
1361 in assert (len >= 0) len
1249 in assert (len >= 0) len
1362
1250
1363-- | The remaining capacity in the array for appending more elements without
1251-- | The remaining capacity in the array for appending more elements without
@@ -1367,7 +1255,7 @@ byteCapacity Array{..} =
1367{-# INLINE bytesFree #-}
1255{-# INLINE bytesFree #-}
1368bytesFree :: Array a -> Int
1256bytesFree :: Array a -> Int
1369bytesFree Array{..} =
1257bytesFree Array{..} =
1370− let n = aBound `minusPtr` aEnd
1258+ let n = aBound - aEnd
1371 in assert (n >= 0) n
1259 in assert (n >= 0) n
1372
1260
1373-------------------------------------------------------------------------------
1261-------------------------------------------------------------------------------
@@ -1413,8 +1301,8 @@ arraysOf n (D.Stream step state) =
1413 r <- step (adaptState gst) st
1301 r <- step (adaptState gst) st
1414 case r of
1302 case r of
1415 D.Yield x s -> do
1303 D.Yield x s -> do
1416− liftIO $ poke end x >> touch contents
1304+ liftIO $ pokeWith contents end x
1417− let end1 = PTR_NEXT(end,a)
1305+ let end1 = INDEX_NEXT(end,a)
1418 return $
1306 return $
1419 if end1 >= bound
1307 if end1 >= bound
1420 then D.Skip
1308 then D.Skip
@@ -1447,7 +1335,7 @@ arrayStreamKFromStreamD =
1447
1335
1448data FlattenState s contents a =
1336data FlattenState s contents a =
1449 OuterLoop s
1337 OuterLoop s
1450− | InnerLoop s contents !(Ptr a) !(Ptr a)
1338+ | InnerLoop s contents !Int !Int
1451
1339
1452-- | Use the "read" unfold instead.
1340-- | Use the "read" unfold instead.
1453--
1341--
@@ -1475,11 +1363,8 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
1475 return $ D.Skip $ OuterLoop st
1363 return $ D.Skip $ OuterLoop st
1476
1364
1477 step' _ (InnerLoop st contents p end) = do
1365 step' _ (InnerLoop st contents p end) = do
1478− x <- liftIO $ do
1479− r <- peek p
1480− touch contents
1366+ x <- liftIO $ peekWith contents p
1481− return r
1482− return $ D.Yield x (InnerLoop st contents (PTR_NEXT(p,a)) end)
1367+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
1483
1368
1484-- | Use the "readRev" unfold instead.
1369-- | Use the "readRev" unfold instead.
1485--
1370--
@@ -1499,7 +1384,7 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1499 r <- step (adaptState gst) st
1384 r <- step (adaptState gst) st
1500 return $ case r of
1385 return $ case r of
1501 D.Yield Array{..} s ->
1386 D.Yield Array{..} s ->
1502− let p = PTR_PREV(aEnd,a)
1387+ let p = INDEX_PREV(aEnd,a)
1503 in D.Skip (InnerLoop s arrContents p arrStart)
1388 in D.Skip (InnerLoop s arrContents p arrStart)
1504 D.Skip s -> D.Skip (OuterLoop s)
1389 D.Skip s -> D.Skip (OuterLoop s)
1505 D.Stop -> D.Stop
1390 D.Stop -> D.Stop
@@ -1509,47 +1394,50 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1509
1394
1510 step' _ (InnerLoop st contents p start) = do
1395 step' _ (InnerLoop st contents p start) = do
1511 x <- liftIO $ do
1396 x <- liftIO $ do
1512− r <- peek p
1513− touch contents
1397+ r <- peekWith contents p
1514 return r
1398 return r
1515− let cur = PTR_PREV(p,a)
1399+ let cur = INDEX_PREV(p,a)
1516 return $ D.Yield x (InnerLoop st contents cur start)
1400 return $ D.Yield x (InnerLoop st contents cur start)
1517
1401
1518-------------------------------------------------------------------------------
1402-------------------------------------------------------------------------------
1519-- Unfolds
1403-- Unfolds
1520-------------------------------------------------------------------------------
1404-------------------------------------------------------------------------------
1521
1405
1522−data ReadUState a = ReadUState
1406+data ArrayUnsafe a = ArrayUnsafe
1523− UNPACKIF !ArrayContents -- contents
1407+ {-# UNPACK #-} !(ArrayContents a) -- contents
1524− !(Ptr a) -- end address
1408+ {-# UNPACK #-} !Int -- index 1
1525− !(Ptr a) -- current address
1409+ {-# UNPACK #-} !Int -- index 2
1410+
1411+toArrayUnsafe :: Array a -> ArrayUnsafe a
1412+toArrayUnsafe (Array contents start end _) = ArrayUnsafe contents start end
1526
1413
1527−toReadUState :: Array a -> ReadUState a
1414+fromArrayUnsafe ::
1528−toReadUState (Array contents start end _) = ReadUState contents end start
1415+#ifdef DEVBUILD
1416+ Storable a =>
1417+#endif
1418+ ArrayUnsafe a -> Array a
1419+fromArrayUnsafe (ArrayUnsafe contents start end) =
1420+ Array contents start end end
1529
1421
1530{-# INLINE_NORMAL producerWith #-}
1422{-# INLINE_NORMAL producerWith #-}
1531producerWith ::
1423producerWith ::
1532 forall m a. (Monad m, Storable a)
1424 forall m a. (Monad m, Storable a)
1533 => (forall b. IO b -> m b) -> Producer m (Array a) a
1425 => (forall b. IO b -> m b) -> Producer m (Array a) a
1534−producerWith liftio = Producer step (return . toReadUState) extract
1426+producerWith liftio = Producer step (return . toArrayUnsafe) extract
1535 where
1427 where
1536
1428
1537 {-# INLINE_LATE step #-}
1429 {-# INLINE_LATE step #-}
1538− step (ReadUState contents end cur)
1430+ step (ArrayUnsafe _ cur end)
1539− | assert (cur <= end) (cur == end) = liftio $ do
1431+ | assert (cur <= end) (cur == end) = return D.Stop
1540− -- We bind it strictly here as we don't want this to be optimized
1541− -- out in a purely lazy monad like Identity.
1542− !_ <- touch contents
1543− return D.Stop
1544− step (ReadUState contents end cur) = do
1432+ step (ArrayUnsafe contents cur end) = do
1545 -- When we use a purely lazy Monad like Identity, we need to force a
1433 -- When we use a purely lazy Monad like Identity, we need to force a
1546 -- few actions for correctness and execution order sanity. We want
1434 -- few actions for correctness and execution order sanity. We want
1547 -- the peek to occur right here and not lazily at some later point
1435 -- the peek to occur right here and not lazily at some later point
1548 -- because we want the peek to be ordered with respect to the touch.
1436 -- because we want the peek to be ordered with respect to the touch.
1549− !x <- liftio $ peek cur
1437+ !x <- liftio $ peekWith contents cur
1550− return $ D.Yield x (ReadUState contents end (PTR_NEXT(cur,a)))
1438+ return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)
1551
1439
1552− extract (ReadUState contents end cur) = return $ Array contents cur end end
1440+ extract = return . fromArrayUnsafe
1553
1441
1554-- | Resumable unfold of an array.
1442-- | Resumable unfold of an array.
1555--
1443--
@@ -1572,16 +1460,14 @@ readRevWith liftio = Unfold step inject
1572 where
1460 where
1573
1461
1574 inject (Array contents start end _) =
1462 inject (Array contents start end _) =
1575− let p = PTR_PREV(end,a)
1463+ let p = INDEX_PREV(end,a)
1576− in return $ ReadUState contents start p
1464+ in return $ ArrayUnsafe contents start p
1577
1465
1578 {-# INLINE_LATE step #-}
1466 {-# INLINE_LATE step #-}
1579− step (ReadUState contents start p) | p < start = liftio $ do
1580− !_ <- touch contents
1581− return D.Stop
1467+ step (ArrayUnsafe _ start p) | p < start = return D.Stop
1582− step (ReadUState contents start p) = do
1468+ step (ArrayUnsafe contents start p) = do
1583− !x <- liftio $ peek p
1469+ !x <- liftio $ peekWith contents p
1584− return $ D.Yield x (ReadUState contents start (PTR_PREV(p,a)))
1470+ return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))
1585
1471
1586-- | Unfold an array into a stream in reverse order.
1472-- | Unfold an array into a stream in reverse order.
1587--
1473--
@@ -1612,8 +1498,7 @@ toListFB c n Array{..} = go arrStart
1612 -- evaluated/written to before we peek at them.
1498 -- evaluated/written to before we peek at them.
1613 -- XXX
1499 -- XXX
1614 let !x = unsafeInlineIO $ do
1500 let !x = unsafeInlineIO $ do
1615− r <- peek p
1616− touch arrContents
1501+ r <- peekWith arrContents p
1617 return r
1502 return r
1618 in c x (go (PTR_NEXT(p,a)))
1503 in c x (go (PTR_NEXT(p,a)))
1619-}
1504-}
@@ -1630,9 +1515,8 @@ toList Array{..} = liftIO $ go arrStart
1630
1515
1631 go p | assert (p <= aEnd) (p == aEnd) = return []
1516 go p | assert (p <= aEnd) (p == aEnd) = return []
1632 go p = do
1517 go p = do
1633− x <- peek p
1634− touch arrContents
1518+ x <- peekWith arrContents p
1635− (:) x <$> go (PTR_NEXT(p,a))
1519+ (:) x <$> go (INDEX_NEXT(p,a))
1636
1520
1637{-# INLINE_NORMAL toStreamDWith #-}
1521{-# INLINE_NORMAL toStreamDWith #-}
1638toStreamDWith ::
1522toStreamDWith ::
@@ -1645,9 +1529,8 @@ toStreamDWith liftio Array{..} = D.Stream step arrStart
1645 {-# INLINE_LATE step #-}
1529 {-# INLINE_LATE step #-}
1646 step _ p | assert (p <= aEnd) (p == aEnd) = return D.Stop
1530 step _ p | assert (p <= aEnd) (p == aEnd) = return D.Stop
1647 step _ p = liftio $ do
1531 step _ p = liftio $ do
1648− r <- peek p
1649− touch arrContents
1532+ r <- peekWith arrContents p
1650− return $ D.Yield r (PTR_NEXT(p,a))
1533+ return $ D.Yield r (INDEX_NEXT(p,a))
1651
1534
1652-- | Use the 'read' unfold instead.
1535-- | Use the 'read' unfold instead.
1653--
1536--
@@ -1669,10 +1552,9 @@ toStreamKWith liftio Array{..} = go arrStart
1669 go p | assert (p <= aEnd) (p == aEnd) = K.nil
1552 go p | assert (p <= aEnd) (p == aEnd) = K.nil
1670 | otherwise =
1553 | otherwise =
1671 let elemM = do
1554 let elemM = do
1672− r <- peek p
1673− touch arrContents
1555+ r <- peekWith arrContents p
1674 return r
1556 return r
1675− in liftio elemM `K.consM` go (PTR_NEXT(p,a))
1557+ in liftio elemM `K.consM` go (INDEX_NEXT(p,a))
1676
1558
1677{-# INLINE toStreamK #-}
1559{-# INLINE toStreamK #-}
1678toStreamK :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
1560toStreamK :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
@@ -1683,7 +1565,7 @@ toStreamDRevWith ::
1683 forall m a. (Monad m, Storable a)
1565 forall m a. (Monad m, Storable a)
1684 => (forall b. IO b -> m b) -> Array a -> D.Stream m a
1566 => (forall b. IO b -> m b) -> Array a -> D.Stream m a
1685toStreamDRevWith liftio Array{..} =
1567toStreamDRevWith liftio Array{..} =
1686− let p = PTR_PREV(aEnd,a)
1568+ let p = INDEX_PREV(aEnd,a)
1687 in D.Stream step p
1569 in D.Stream step p
1688
1570
1689 where
1571 where
@@ -1691,9 +1573,8 @@ toStreamDRevWith liftio Array{..} =
1691 {-# INLINE_LATE step #-}
1573 {-# INLINE_LATE step #-}
1692 step _ p | p < arrStart = return D.Stop
1574 step _ p | p < arrStart = return D.Stop
1693 step _ p = liftio $ do
1575 step _ p = liftio $ do
1694− r <- peek p
1695− touch arrContents
1576+ r <- peekWith arrContents p
1696− return $ D.Yield r (PTR_PREV(p,a))
1577+ return $ D.Yield r (INDEX_PREV(p,a))
1697
1578
1698-- | Use the 'readRev' unfold instead.
1579-- | Use the 'readRev' unfold instead.
1699--
1580--
@@ -1709,7 +1590,7 @@ toStreamKRevWith ::
1709 forall m a. (Monad m, Storable a)
1590 forall m a. (Monad m, Storable a)
1710 => (forall b. IO b -> m b) -> Array a -> K.Stream m a
1591 => (forall b. IO b -> m b) -> Array a -> K.Stream m a
1711toStreamKRevWith liftio Array {..} =
1592toStreamKRevWith liftio Array {..} =
1712− let p = PTR_PREV(aEnd,a)
1593+ let p = INDEX_PREV(aEnd,a)
1713 in go p
1594 in go p
1714
1595
1715 where
1596 where
@@ -1717,10 +1598,9 @@ toStreamKRevWith liftio Array {..} =
1717 go p | p < arrStart = K.nil
1598 go p | p < arrStart = K.nil
1718 | otherwise =
1599 | otherwise =
1719 let elemM = do
1600 let elemM = do
1720− r <- peek p
1721− touch arrContents
1601+ r <- peekWith arrContents p
1722 return r
1602 return r
1723− in liftio elemM `K.consM` go (PTR_PREV(p,a))
1603+ in liftio elemM `K.consM` go (INDEX_PREV(p,a))
1724
1604
1725{-# INLINE toStreamKRev #-}
1605{-# INLINE toStreamKRev #-}
1726toStreamKRev :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
1606toStreamKRev :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
@@ -1747,23 +1627,6 @@ foldr f z arr = D.foldr f z $ toStreamD arr
1747-- Folds
1627-- Folds
1748-------------------------------------------------------------------------------
1628-------------------------------------------------------------------------------
1749
1629
1750−data ArrayUnsafe a = ArrayUnsafe
1751− UNPACKIF !ArrayContents -- contents
1752− {-# UNPACK #-} !(Ptr a) -- start address
1753− {-# UNPACK #-} !(Ptr a) -- first unused address
1754−
1755−toArrayUnsafe :: Array a -> ArrayUnsafe a
1756−toArrayUnsafe (Array contents start end _) =
1757− ArrayUnsafe contents start end
1758−
1759−fromArrayUnsafe ::
1760−#ifdef DEVBUILD
1761− Storable a =>
1762−#endif
1763− ArrayUnsafe a -> Array a
1764−fromArrayUnsafe (ArrayUnsafe contents start end) =
1765− Array contents start end end
1766−
1767-- Note: Arrays may be allocated with a specific alignment at the beginning of
1630-- Note: Arrays may be allocated with a specific alignment at the beginning of
1768-- the array. If you need to maintain that alignment on reallocations then you
1631-- the array. If you need to maintain that alignment on reallocations then you
1769-- can resize the array manually before append, using an aligned resize
1632-- can resize the array manually before append, using an aligned resize
@@ -1771,7 +1634,8 @@ fromArrayUnsafe (ArrayUnsafe contents start end) =
1771
1634
1772-- XXX Keep the bound intact to not lose any free space? Perf impact?
1635-- XXX Keep the bound intact to not lose any free space? Perf impact?
1773
1636
1774−-- | Append up to @n@ input items to the supplied array.
1637+-- | @appendNUnsafe alloc n@ appends up to @n@ input items to the supplied
1638+-- array.
1775--
1639--
1776-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
1640-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
1777-- corruption or segfault.
1641-- corruption or segfault.
@@ -1792,7 +1656,7 @@ appendNUnsafe action n =
1792 initial = do
1656 initial = do
1793 assert (n >= 0) (return ())
1657 assert (n >= 0) (return ())
1794 arr@(Array _ _ end bound) <- action
1658 arr@(Array _ _ end bound) <- action
1795− let free = bound `minusPtr` end
1659+ let free = bound - end
1796 needed = n * SIZE_OF(a)
1660 needed = n * SIZE_OF(a)
1797 -- XXX We can also reallocate if the array has too much free space,
1661 -- XXX We can also reallocate if the array has too much free space,
1798 -- otherwise we lose that space.
1662 -- otherwise we lose that space.
@@ -1803,8 +1667,8 @@ appendNUnsafe action n =
1803 return $ toArrayUnsafe arr1
1667 return $ toArrayUnsafe arr1
1804
1668
1805 step (ArrayUnsafe contents start end) x = do
1669 step (ArrayUnsafe contents start end) x = do
1806− liftIO $ poke end x >> touch contents
1670+ liftIO $ pokeWith contents end x
1807− return $ ArrayUnsafe contents start (PTR_NEXT(end,a))
1671+ return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1808
1672
1809-- | Append @n@ elements to an existing array. Any free space left in the array
1673-- | Append @n@ elements to an existing array. Any free space left in the array
1810-- after appending @n@ elements is lost.
1674-- after appending @n@ elements is lost.
@@ -1858,17 +1722,16 @@ append = appendWith (* 2)
1858{-# INLINE_NORMAL writeNWithUnsafe #-}
1722{-# INLINE_NORMAL writeNWithUnsafe #-}
1859writeNWithUnsafe :: forall m a. (MonadIO m, Storable a)
1723writeNWithUnsafe :: forall m a. (MonadIO m, Storable a)
1860 => (Int -> m (Array a)) -> Int -> Fold m a (Array a)
1724 => (Int -> m (Array a)) -> Int -> Fold m a (Array a)
1861−writeNWithUnsafe alloc n = Fold step initial (return . fromArrayUnsafe)
1725+writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
1862
1726
1863 where
1727 where
1864
1728
1865− initial = FL.Partial . toArrayUnsafe <$> alloc (max n 0)
1729+ initial = toArrayUnsafe <$> alloc (max n 0)
1866
1730
1867 step (ArrayUnsafe contents start end) x = do
1731 step (ArrayUnsafe contents start end) x = do
1868− liftIO $ poke end x >> touch contents
1732+ liftIO $ pokeWith contents end x
1869 return
1733 return
1870− $ FL.Partial
1871− $ ArrayUnsafe contents start (PTR_NEXT(end,a))
1734+ $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1872
1735
1873-- | Like 'writeN' but does not check the array bounds when writing. The fold
1736-- | Like 'writeN' but does not check the array bounds when writing. The fold
1874-- driver must not call the step function more than 'n' times otherwise it will
1737-- driver must not call the step function more than 'n' times otherwise it will
@@ -1913,20 +1776,19 @@ writeN = writeNWith newArray
1913{-# INLINE_NORMAL writeRevNWithUnsafe #-}
1776{-# INLINE_NORMAL writeRevNWithUnsafe #-}
1914writeRevNWithUnsafe :: forall m a. (MonadIO m, Storable a)
1777writeRevNWithUnsafe :: forall m a. (MonadIO m, Storable a)
1915 => (Int -> m (Array a)) -> Int -> Fold m a (Array a)
1778 => (Int -> m (Array a)) -> Int -> Fold m a (Array a)
1916−writeRevNWithUnsafe alloc n = Fold step initial (return . fromArrayUnsafe)
1779+writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
1917
1780
1918 where
1781 where
1919
1782
1920 toArrayUnsafeRev (Array contents _ _ bound) =
1783 toArrayUnsafeRev (Array contents _ _ bound) =
1921 ArrayUnsafe contents bound bound
1784 ArrayUnsafe contents bound bound
1922
1785
1923− initial = FL.Partial . toArrayUnsafeRev <$> alloc (max n 0)
1786+ initial = toArrayUnsafeRev <$> alloc (max n 0)
1924
1787
1925 step (ArrayUnsafe contents start end) x = do
1788 step (ArrayUnsafe contents start end) x = do
1926− let ptr = PTR_PREV(start,a)
1789+ let ptr = INDEX_PREV(start,a)
1927− liftIO $ poke ptr x >> touch contents
1790+ liftIO $ pokeWith contents ptr x
1928 return
1791 return
1929− $ FL.Partial
1930 $ ArrayUnsafe contents ptr end
1792 $ ArrayUnsafe contents ptr end
1931
1793
1932-- | Like writeNWith but writes the array in reverse order.
1794-- | Like writeNWith but writes the array in reverse order.
@@ -1957,23 +1819,6 @@ writeNAligned :: forall m a. (MonadIO m, Storable a)
1957 => Int -> Int -> Fold m a (Array a)
1819 => Int -> Int -> Fold m a (Array a)
1958writeNAligned align = writeNWith (newArrayAligned align)
1820writeNAligned align = writeNWith (newArrayAligned align)
1959
1821
1960−-- | @writeNAlignedUnmanaged align n@ folds a maximum of @n@ elements from the
1961−-- input stream to an 'Array' whose starting address is aligned to @align@
1962−-- bytes and is allocated using unmanaged memory (never freed). This could be
1963−-- useful to allocate memory that we need to allocate only once in the lifetime
1964−-- of the program.
1965−--
1966−-- >>> f = Array.newArrayAlignedUnmanaged
1967−-- >>> writeNAlignedUnmanaged a = Array.writeNWith (f a)
1968−-- >>> writeNAlignedUnmanaged a n = Array.appendN (f a n) n
1969−--
1970−-- /Pre-release/
1971−--
1972−{-# INLINE_NORMAL writeNAlignedUnmanaged #-}
1973−writeNAlignedUnmanaged :: forall m a. (MonadIO m, Storable a)
1974− => Int -> Int -> Fold m a (Array a)
1975−writeNAlignedUnmanaged align = writeNWith (newArrayAlignedUnmanaged align)
1976−
1977-- XXX Buffer to a list instead?
1822-- XXX Buffer to a list instead?
1978--
1823--
1979-- | Buffer a stream into a stream of arrays.
1824-- | Buffer a stream into a stream of arrays.
@@ -2030,15 +1875,15 @@ writeWith elemCount =
2030 where
1875 where
2031
1876
2032 insertElem (Array contents start end bound) x = do
1877 insertElem (Array contents start end bound) x = do
2033− liftIO $ poke end x
1878+ liftIO $ pokeWith contents end x
2034− return $ Array contents start (PTR_NEXT(end,a)) bound
1879+ return $ Array contents start (INDEX_NEXT(end,a)) bound
2035
1880
2036 initial = do
1881 initial = do
2037 when (elemCount < 0) $ error "writeWith: elemCount is negative"
1882 when (elemCount < 0) $ error "writeWith: elemCount is negative"
2038 liftIO $ newArrayAligned (alignment (undefined :: a)) elemCount
1883 liftIO $ newArrayAligned (alignment (undefined :: a)) elemCount
2039 step arr@(Array _ start end bound) x
1884 step arr@(Array _ start end bound) x
2040− | PTR_NEXT(end,a) > bound = do
1885+ | INDEX_NEXT(end,a) > bound = do
2041− let oldSize = end `minusPtr` start
1886+ let oldSize = end - start
2042 newSize = max (oldSize * 2) 1
1887 newSize = max (oldSize * 2) 1
2043 arr1 <-
1888 arr1 <-
2044 liftIO
1889 liftIO
@@ -2077,14 +1922,14 @@ fromStreamDN :: forall m a. (MonadIO m, Storable a)
2077-- fromStreamDN n = D.fold (writeN n)
1922-- fromStreamDN n = D.fold (writeN n)
2078fromStreamDN limit str = do
1923fromStreamDN limit str = do
2079 arr <- liftIO $ newArray limit
1924 arr <- liftIO $ newArray limit
2080− end <- D.foldlM' fwrite (return $ aEnd arr) $ D.take limit str
1925+ end <- D.foldlM' (fwrite (arrContents arr)) (return $ aEnd arr) $ D.take limit str
2081 return $ arr {aEnd = end}
1926 return $ arr {aEnd = end}
2082
1927
2083 where
1928 where
2084
1929
2085− fwrite ptr x = do
1930+ fwrite arrContents ptr x = do
2086− liftIO $ poke ptr x
1931+ liftIO $ pokeWith arrContents ptr x
2087− return $ PTR_NEXT(ptr,a)
1932+ return $ INDEX_NEXT(ptr,a)
2088
1933
2089-- | Create an 'Array' from the first N elements of a list. The array is
1934-- | Create an 'Array' from the first N elements of a list. The array is
2090-- allocated to size N, if the list terminates before N elements then the
1935-- allocated to size N, if the list terminates before N elements then the
@@ -2156,45 +2001,60 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
2156-- Combining
2001-- Combining
2157-------------------------------------------------------------------------------
2002-------------------------------------------------------------------------------
2158
2003
2004+-- | Put a sub range of a source array into a subrange of a destination array.
2005+-- This is not safe as it does not check the bounds.
2006+{-# INLINE putSliceUnsafe #-}
2007+putSliceUnsafe :: MonadIO m => Array a -> Int -> Array a -> Int -> Int -> m ()
2008+putSliceUnsafe src srcStartBytes dst dstStartBytes lenBytes = liftIO $ do
2009+ assertM (lenBytes <= aBound dst - dstStartBytes)
2010+ assertM (lenBytes <= aEnd src - srcStartBytes)
2011+ let !(I# srcStartBytes#) = srcStartBytes
2012+ !(I# dstStartBytes#) = dstStartBytes
2013+ !(I# lenBytes#) = lenBytes
2014+ let arrS# = getMutableByteArray# (arrContents src)
2015+ arrD# = getMutableByteArray# (arrContents dst)
2016+ IO $ \s# -> (# copyMutableByteArray#
2017+ arrS# srcStartBytes# arrD# dstStartBytes# lenBytes# s#
2018+ , () #)
2019+
2159-- | Copy two arrays into a newly allocated array.
2020-- | Copy two arrays into a newly allocated array.
2160{-# INLINE spliceCopy #-}
2021{-# INLINE spliceCopy #-}
2161−spliceCopy :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
2022+spliceCopy :: forall m a. (MonadIO m, Storable a) =>
2023+ Array a -> Array a -> m (Array a)
2162−spliceCopy arr1 arr2 = do
2024+spliceCopy arr1 arr2 = liftIO $ do
2163− let src1 = arrStart arr1
2025+ let start1 = arrStart arr1
2164− src2 = arrStart arr2
2026+ start2 = arrStart arr2
2165− len1 = aEnd arr1 `minusPtr` src1
2027+ len1 = aEnd arr1 - start1
2166− len2 = aEnd arr2 `minusPtr` src2
2028+ len2 = aEnd arr2 - start2
2167−
2029+ newArrContents <-
2030+ liftIO
2031+ $ newAlignedArrayContents
2168− arr <- liftIO $ newArray (len1 + len2)
2032+ (len1 + len2)
2169− let dst = arrStart arr
2033+ (alignment (undefined :: a))
2170−
2034+ let len = len1 + len2
2171− -- XXX Should we use copyMutableByteArray# instead? Is there an overhead to
2035+ newArr = Array newArrContents 0 len len
2172− -- ccall?
2036+ putSliceUnsafe arr1 start1 newArr 0 len1
2173− liftIO $ do
2037+ putSliceUnsafe arr2 start2 newArr len1 len2
2174− memcpy (castPtr dst) (castPtr src1) len1
2038+ return newArr
2175− touch (arrContents arr1)
2176− memcpy (castPtr (dst `plusPtr` len1)) (castPtr src2) len2
2177− touch (arrContents arr2)
2178− return arr { aEnd = dst `plusPtr` (len1 + len2) }
2179
2039
2180-- | Really really unsafe, appends the second array into the first array. If
2040-- | Really really unsafe, appends the second array into the first array. If
2181-- the first array does not have enough space it may cause silent data
2041-- the first array does not have enough space it may cause silent data
2182-- corruption or if you are lucky a segfault.
2042-- corruption or if you are lucky a segfault.
2183{-# INLINE spliceUnsafe #-}
2043{-# INLINE spliceUnsafe #-}
2184−spliceUnsafe :: MonadIO m => Array a -> (Array a, Int) -> m (Array a)
2044+spliceUnsafe :: MonadIO m =>
2045+ Array a -> Array a -> m (Array a)
2185−spliceUnsafe dst (src, srcLen) =
2046+spliceUnsafe dst src =
2186 liftIO $ do
2047 liftIO $ do
2187− let psrc = arrStart src
2048+ let startSrc = arrStart src
2049+ srcLen = aEnd src - startSrc
2188− let pdst = aEnd dst
2050+ endDst = aEnd dst
2189− assert (pdst `plusPtr` srcLen <= aBound dst) (return ())
2051+ assertM (endDst + srcLen <= aBound dst)
2190− memcpy (castPtr pdst) (castPtr psrc) srcLen
2052+ putSliceUnsafe src startSrc dst endDst srcLen
2191− touch (arrContents src)
2192− touch (arrContents dst)
2193− return $ dst {aEnd = pdst `plusPtr` srcLen}
2053+ return $ dst {aEnd = endDst + srcLen}
2194
2054
2195-- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no
2055-- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no
2196-- reserved space available in @dst@ it is reallocated to a size determined by
2056-- reserved space available in @dst@ it is reallocated to a size determined by
2197−-- the @sizer dstBytesn srcBytes@ function, where @dstBytes@ is the size of the
2057+-- the @sizer dstBytes srcBytes@ function, where @dstBytes@ is the size of the
2198-- first array and @srcBytes@ is the size of the second array, in bytes.
2058-- first array and @srcBytes@ is the size of the second array, in bytes.
2199--
2059--
2200-- Note that the returned array may be a mutated version of first array.
2060-- Note that the returned array may be a mutated version of first array.
@@ -2209,21 +2069,21 @@ spliceWith sizer dst@(Array _ start end bound) src = do
2209 in D.fold f (toStreamD src)
2069 in D.fold f (toStreamD src)
2210-}
2070-}
2211 assert (end <= bound) (return ())
2071 assert (end <= bound) (return ())
2212− let srcLen = aEnd src `minusPtr` arrStart src
2072+ let srcBytes = aEnd src - arrStart src
2213
2073
2214 dst1 <-
2074 dst1 <-
2215− if end `plusPtr` srcLen >= bound
2075+ if end + srcBytes >= bound
2216 then do
2076 then do
2217− let oldSize = end `minusPtr` start
2077+ let dstBytes = end - start
2218− newSize = sizer oldSize srcLen
2078+ newSizeInBytes = sizer dstBytes srcBytes
2219− when (newSize < oldSize + srcLen)
2079+ when (newSizeInBytes < dstBytes + srcBytes)
2220 $ error
2080 $ error
2221 $ "splice: newSize is less than the total size "
2081 $ "splice: newSize is less than the total size "
2222 ++ "of arrays being appended. Please check the "
2082 ++ "of arrays being appended. Please check the "
2223− ++ "newSize function passed."
2083+ ++ "sizer function passed."
2224− liftIO $ realloc newSize dst
2084+ liftIO $ realloc newSizeInBytes dst
2225 else return dst
2085 else return dst
2226− spliceUnsafe dst1 (src, srcLen)
2086+ spliceUnsafe dst1 src
2227
2087
2228-- | The first array is mutated to append the second array. If there is no
2088-- | The first array is mutated to append the second array. If there is no
2229-- reserved space available in the first array a new allocation of exact
2089-- reserved space available in the first array a new allocation of exact
@@ -2260,9 +2120,12 @@ spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))
2260{-# INLINE breakOn #-}
2120{-# INLINE breakOn #-}
2261breakOn :: MonadIO m
2121breakOn :: MonadIO m
2262 => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
2122 => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
2263−breakOn sep arr@Array{..} = liftIO $ do
2123+breakOn sep arr@Array{..} = asPtrUnsafe arr $ \p -> liftIO $ do
2264− let p = arrStart
2124+ -- XXX Instead of using asPtrUnsafe (pinning memory) we can pass unlifted
2125+ -- Addr# to memchr and it should be safe (from ghc 8.4).
2126+ -- XXX We do not need memchr here, we can use a Haskell equivalent.
2265− loc <- c_memchr p sep (fromIntegral $ aEnd `minusPtr` p)
2127+ loc <- c_memchr p sep (fromIntegral $ byteLength arr)
2128+ let sepIndex = loc `minusPtr` p
2266 return $
2129 return $
2267 if loc == nullPtr
2130 if loc == nullPtr
2268 then (arr, Nothing)
2131 then (arr, Nothing)
@@ -2270,12 +2133,12 @@ breakOn sep arr@Array{..} = liftIO $ do
2270 ( Array
2133 ( Array
2271 { arrContents = arrContents
2134 { arrContents = arrContents
2272 , arrStart = arrStart
2135 , arrStart = arrStart
2273− , aEnd = loc
2136+ , aEnd = arrStart + sepIndex -- exclude the separator
2274− , aBound = loc
2137+ , aBound = arrStart + sepIndex
2275 }
2138 }
2276 , Just $ Array
2139 , Just $ Array
2277 { arrContents = arrContents
2140 { arrContents = arrContents
2278− , arrStart = arrStart `plusPtr` (loc `minusPtr` p + 1)
2141+ , arrStart = arrStart + (sepIndex + 1)
2279 , aEnd = aEnd
2142 , aEnd = aEnd
2280 , aBound = aBound
2143 , aBound = aBound
2281 }
2144 }
@@ -2294,7 +2157,7 @@ splitAt i arr@Array{..} =
2294 then error $ "sliceAt: specified array index " ++ show i
2157 then error $ "sliceAt: specified array index " ++ show i
2295 ++ " is beyond the maximum index " ++ show maxIndex
2158 ++ " is beyond the maximum index " ++ show maxIndex
2296 else let off = i * SIZE_OF(a)
2159 else let off = i * SIZE_OF(a)
2297− p = arrStart `plusPtr` off
2160+ p = arrStart + off
2298 in ( Array
2161 in ( Array
2299 { arrContents = arrContents
2162 { arrContents = arrContents
2300 , arrStart = arrStart
2163 , arrStart = arrStart
@@ -2303,7 +2166,7 @@ splitAt i arr@Array{..} =
2303 }
2166 }
2304 , Array
2167 , Array
2305 { arrContents = arrContents
2168 { arrContents = arrContents
2306− , arrStart = arrStart `plusPtr` off
2169+ , arrStart = p
2307 , aEnd = aEnd
2170 , aEnd = aEnd
2308 , aBound = aBound
2171 , aBound = aBound
2309 }
2172 }
@@ -2326,7 +2189,7 @@ castUnsafe ::
2326#endif
2189#endif
2327 Array a -> Array b
2190 Array a -> Array b
2328castUnsafe (Array contents start end bound) =
2191castUnsafe (Array contents start end bound) =
2329− Array contents (castPtr start) (castPtr end) (castPtr bound)
2192+ Array (castContents contents) start end bound
2330
2193
2331-- | Cast an @Array a@ into an @Array Word8@.
2194-- | Cast an @Array a@ into an @Array Word8@.
2332--
2195--
@@ -2349,44 +2212,82 @@ cast arr =
2349 then Nothing
2212 then Nothing
2350 else Just $ castUnsafe arr
2213 else Just $ castUnsafe arr
2351
2214
2215+-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
2216+-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
2217+-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
2218+-- before the pointer is wrapped.
2219+--
2220+-- From the GHC manual:
2221+--
2222+-- GHC, since version 8.4, guarantees that garbage collection will never occur
2223+-- during an unsafe call, even in the bytecode interpreter, and further
2224+-- guarantees that unsafe calls will be performed in the calling thread. Making
2225+-- it safe to pass heap-allocated objects to unsafe functions.
2226+
2227+-- Unsafe because of direct pointer operations. The user must ensure that they
2228+-- are writing within the legal bounds of the array. Should we just name it
2229+-- asPtr, the unsafety is implicit for any pointer operations. And we are safe
2230+-- from Haskell perspective because we will be pinning the memory.
2231+
2352−-- | Use an @Array a@ as @Ptr a@.
2232+-- | Use an @Array a@ as @Ptr a@. This is useful when we want to pass an array
2233+-- as a pointer to some operating system call or to a "safe" FFI call.
2234+--
2235+-- If the array is not pinned it is copied to pinned memory before passing it
2236+-- to the monadic action.
2237+--
2238+-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised
2239+-- that the programmer keeps this in mind and creates a pinned array
2240+-- opportunistically before this operation occurs, to avoid the cost of a copy
2241+-- if possible.
2353--
2242--
2354-- /Unsafe/
2243-- /Unsafe/
2355--
2244--
2356-- /Pre-release/
2245-- /Pre-release/
2357--
2246--
2358asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
2247asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
2359−asPtrUnsafe Array{..} f = do
2248+asPtrUnsafe arr f = do
2249+ let contents = arrContents arr
2250+ !ptr = Ptr (byteArrayContents#
2251+ (unsafeCoerce# (getMutableByteArray# contents)))
2252+ -- XXX Check if the array is pinned, if not, copy it to a pinned array
2253+ -- XXX We should probably pass to the IO action the byte length of the array
2254+ -- as well so that bounds can be checked.
2360− r <- f arrStart
2255+ r <- f (ptr `plusPtr` arrStart arr)
2361− liftIO $ touch arrContents
2256+ liftIO $ touch contents
2362 return r
2257 return r
2363
2258
2364-------------------------------------------------------------------------------
2259-------------------------------------------------------------------------------
2365-- Equality
2260-- Equality
2366-------------------------------------------------------------------------------
2261-------------------------------------------------------------------------------
2367
2262
2368−-- | Compare if two arrays are equal.
2263+-- | Compare the length of the arrays. If the length is equal, compare the
2264+-- lexicographical ordering of two underlying byte arrays
2369--
2265--
2370-- /Pre-release/
2266-- /Pre-release/
2371{-# INLINE cmp #-}
2267{-# INLINE cmp #-}
2372−cmp :: MonadIO m => Array a -> Array a -> m Bool
2268+cmp :: MonadIO m => Array a -> Array a -> m Ordering
2373cmp arr1 arr2 =
2269cmp arr1 arr2 =
2374− liftIO $ do
2270+ liftIO
2375− let ptr1 = arrStart arr1
2271+ $ do
2376− let ptr2 = arrStart arr2
2272+ let marr1 = getMutableByteArray# (arrContents arr1)
2377− let len1 = aEnd arr1 `minusPtr` ptr1
2273+ marr2 = getMutableByteArray# (arrContents arr2)
2378− let len2 = aEnd arr2 `minusPtr` ptr2
2274+ !(I# st1#) = arrStart arr1
2379−
2275+ !(I# st2#) = arrStart arr2
2380− if len1 == len2
2276+ !(I# len#) = byteLength arr1
2381− then
2277+ case compare (byteLength arr1) (byteLength arr2) of
2382− if ptr1 == ptr2
2383− then return True
2384− else do
2278+ EQ -> do
2385− r <- memcmp (castPtr ptr1) (castPtr ptr2) len1
2279+ r <- liftIO $ IO $ \s# ->
2386− touch (arrContents arr1)
2280+ let res =
2387− touch (arrContents arr2)
2281+ I#
2282+ (compareByteArrays#
2283+ (unsafeCoerce# marr1)
2284+ st1#
2285+ (unsafeCoerce# marr2)
2286+ st2#
2287+ len#)
2288+ in (# s#, res #)
2388− return r
2289+ return $ compare r 0
2389− else return False
2290+ x -> return x
2390
2291
2391-------------------------------------------------------------------------------
2292-------------------------------------------------------------------------------
2392-- NFData
2293-- NFData
@@ -2408,12 +2309,10 @@ instance NFData1 Array where
2408{-# INLINE strip #-}
2309{-# INLINE strip #-}
2409strip :: forall a m. (Storable a, MonadIO m) =>
2310strip :: forall a m. (Storable a, MonadIO m) =>
2410 (a -> Bool) -> Array a -> m (Array a)
2311 (a -> Bool) -> Array a -> m (Array a)
2411−strip eq arr@Array{..} = do
2312+strip eq arr@Array{..} = liftIO $ do
2412− st <- liftIO $ getStart arrStart
2313+ st <- getStart arrStart
2413− end <- liftIO $ getLast aEnd st
2314+ end <- getLast aEnd st
2414− liftIO $ do
2415− touch arrContents
2416− return arr {arrStart = st, aEnd = end, aBound = end}
2315+ return arr {arrStart = st, aEnd = end, aBound = end}
2417
2316
2418 where
2317 where
2419
2318
@@ -2430,17 +2329,17 @@ strip eq arr@Array{..} = do
2430 getStart cur = do
2329 getStart cur = do
2431 if cur < aEnd
2330 if cur < aEnd
2432 then do
2331 then do
2433− r <- peek cur
2332+ r <- peekWith arrContents cur
2434 if eq r
2333 if eq r
2435− then getStart (PTR_NEXT(cur, a))
2334+ then getStart (INDEX_NEXT(cur,a))
2436 else return cur
2335 else return cur
2437 else return cur
2336 else return cur
2438
2337
2439 getLast cur low = do
2338 getLast cur low = do
2440 if cur > low
2339 if cur > low
2441 then do
2340 then do
2442− let prev = PTR_PREV(cur,a)
2341+ let prev = INDEX_PREV(cur,a)
2443− r <- peek prev
2342+ r <- peekWith arrContents prev
2444 if eq r
2343 if eq r
2445 then getLast prev low
2344 then getLast prev low
2446 else return cur
2345 else return cur
core/src/Streamly/Internal/Data/Array/Foreign/Type.hs+20−170
@@ -1,3 +1,4 @@
1+{-# LANGUAGE UnboxedTuples #-}
1-- |
2-- |
2-- Module : Streamly.Internal.Data.Array.Foreign.Type
3-- Module : Streamly.Internal.Data.Array.Foreign.Type
3-- Copyright : (c) 2020 Composewell Technologies
4-- Copyright : (c) 2020 Composewell Technologies
@@ -23,10 +24,6 @@ module Streamly.Internal.Data.Array.Foreign.Type
23 -- * Construction
24 -- * Construction
24 , splice
25 , splice
25
26
26− , fromPtr
27− , fromForeignPtrUnsafe
28− , fromAddr#
29− , fromCString#
30 , fromList
27 , fromList
31 , fromListN
28 , fromListN
32 , fromListRev
29 , fromListRev
@@ -62,7 +59,6 @@ module Streamly.Internal.Data.Array.Foreign.Type
62 , writeNUnsafe
59 , writeNUnsafe
63 , MA.ArrayUnsafe (..)
60 , MA.ArrayUnsafe (..)
64 , writeNAligned
61 , writeNAligned
65− , writeNAlignedUnmanaged
66 , write
62 , write
67
63
68 -- * Streams of arrays
64 -- * Streams of arrays
@@ -81,20 +77,15 @@ import Control.DeepSeq (NFData(..), NFData1(..))
81import Control.Monad.IO.Class (MonadIO(..))
77import Control.Monad.IO.Class (MonadIO(..))
82import Data.Functor.Identity (Identity(..))
78import Data.Functor.Identity (Identity(..))
83import Data.Word (Word8)
79import Data.Word (Word8)
84−import Foreign.C.String (CString)
85−import Foreign.C.Types (CSize(..))
86−import Foreign.Ptr (plusPtr, castPtr)
87−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
88−import GHC.Base (Addr#, nullAddr#, build)
80+import GHC.Base (build)
89import GHC.Exts (IsList, IsString(..))
81import GHC.Exts (IsList, IsString(..))
90−import GHC.ForeignPtr (ForeignPtr)
91
82
92import GHC.IO (unsafePerformIO)
83import GHC.IO (unsafePerformIO)
93import GHC.Ptr (Ptr(..))
84import GHC.Ptr (Ptr(..))
94−import Streamly.Internal.Data.Array.Foreign.Mut.Type
85+import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayContents)
95− (ArrayContents, touch)
96import Streamly.Internal.Data.Fold.Type (Fold(..))
86import Streamly.Internal.Data.Fold.Type (Fold(..))
97import Streamly.Internal.Data.Stream.Type (Stream)
87import Streamly.Internal.Data.Stream.Type (Stream)
88+import Streamly.Internal.Data.Unboxed (Storable, peekWith, sizeOf)
98import Streamly.Internal.Data.Unfold.Type (Unfold(..))
89import Streamly.Internal.Data.Unfold.Type (Unfold(..))
99import Text.Read (readPrec, readListPrec, readListPrecDefault)
90import Text.Read (readPrec, readListPrec, readListPrecDefault)
100
91
@@ -141,34 +132,27 @@ data Array a =
141#ifdef DEVBUILD
132#ifdef DEVBUILD
142 Storable a =>
133 Storable a =>
143#endif
134#endif
135+ -- All offsets are in terms of bytes from the start of arraycontents
144 Array
136 Array
145− { arrContents ::
137+ { arrContents :: {-# UNPACK #-} !(ArrayContents a)
146−#ifndef USE_FOREIGN_PTR
147− {-# UNPACK #-}
148−#endif
149− !ArrayContents -- ^ first address
150− , arrStart :: {-# UNPACK #-} !(Ptr a) -- start address
138+ , arrStart :: {-# UNPACK #-} !Int -- offset
151− , aEnd :: {-# UNPACK #-} !(Ptr a) -- first unused addres
139+ , aEnd :: {-# UNPACK #-} !Int -- offset + len
152 }
140 }
153
141
154-------------------------------------------------------------------------------
142-------------------------------------------------------------------------------
155-- Utility functions
143-- Utility functions
156-------------------------------------------------------------------------------
144-------------------------------------------------------------------------------
157
145
158−foreign import ccall unsafe "string.h strlen" c_strlen
159− :: CString -> IO CSize
160−
161-- | Use an @Array a@ as @Ptr a@.
146-- | Use an @Array a@ as @Ptr a@.
162--
147--
148+-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
149+--
163-- /Unsafe/
150-- /Unsafe/
164--
151--
165-- /Pre-release/
152-- /Pre-release/
166--
153--
167asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
154asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
168−asPtrUnsafe Array{..} f = do
155+asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
169− r <- f arrStart
170− liftIO $ touch arrContents
171− return r
172
156
173-------------------------------------------------------------------------------
157-------------------------------------------------------------------------------
174-- Freezing and Thawing
158-- Freezing and Thawing
@@ -221,124 +205,6 @@ splice :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
221splice arr1 arr2 =
205splice arr1 arr2 =
222 unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)
206 unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)
223
207
224−-- | Create an 'Array' of the given number of elements of type @a@ from a read
225−-- only pointer @Ptr a@. The pointer is not freed when the array is garbage
226−-- collected. This API is unsafe for the following reasons:
227−--
228−-- 1. The pointer must point to static pinned memory or foreign memory that
229−-- does not require freeing..
230−-- 2. The pointer must be legally accessible upto the given length.
231−-- 3. To guarantee that the array is immutable, the contents of the address
232−-- must be guaranteed to not change.
233−--
234−-- /Unsafe/
235−--
236−-- /Pre-release/
237−--
238−{-# INLINE fromPtr #-}
239−fromPtr ::
240−#ifdef DEVBUILD
241− Storable a =>
242−#endif
243− Int -> Ptr a -> Array a
244−fromPtr n ptr = unsafeInlineIO $ do
245− let end = ptr `plusPtr` n
246− return $ Array
247− { arrContents = MA.nilArrayContents
248− , arrStart = ptr
249− , aEnd = end
250− }
251−
252−-- | @fromForeignPtrUnsafe foreignPtr end bound@ creates an 'Array' that starts
253−-- at the memory pointed by the @foreignPtr@, @end@ is the first unused
254−-- address.
255−--
256−-- Unsafe: Make sure that foreignPtr <= end and (end - start) is an
257−-- integral multiple of the element size. Only PlainPtr type ForeignPtr is
258−-- supported.
259−--
260−-- /Pre-release/
261−--
262−{-# INLINE fromForeignPtrUnsafe #-}
263−fromForeignPtrUnsafe ::
264−#ifdef DEVBUILD
265− Storable a =>
266−#endif
267− ForeignPtr a -> Ptr a -> Array a
268−fromForeignPtrUnsafe fp end = unsafeFreeze $ MA.fromForeignPtrUnsafe fp end end
269−
270−-- XXX when converting an array of Word8 from a literal string we can simply
271−-- refer to the literal string. Is it possible to write rules such that
272−-- fromList Word8 can be rewritten so that GHC does not first convert the
273−-- literal to [Char] and then we convert it back to an Array Word8?
274−--
275−-- TBD: We can also add template haskell quasiquotes to specify arrays of other
276−-- literal types. TH will encode them into a string literal and we read that as
277−-- an array of the required type. With template Haskell we can provide a safe
278−-- version of fromString#.
279−--
280−-- | Create an @Array Word8@ of the given length from a static, read only
281−-- machine address 'Addr#'. See 'fromPtr' for safety caveats.
282−--
283−-- A common use case for this API is to create an array from a static unboxed
284−-- string literal. GHC string literals are of type 'Addr#', and must contain
285−-- characters that can be encoded in a byte i.e. characters or literal bytes in
286−-- the range from 0-255.
287−--
288−-- >>> import Data.Word (Word8)
289−-- >>> Array.fromAddr# 5 "hello world!"# :: Array Word8
290−-- [104,101,108,108,111]
291−--
292−-- >>> Array.fromAddr# 3 "\255\NUL\255"# :: Array Word8
293−-- [255,0,255]
294−--
295−-- /See also: 'fromString#'/
296−--
297−-- /Unsafe/
298−--
299−-- /Time complexity: O(1)/
300−--
301−-- /Pre-release/
302−--
303−{-# INLINE fromAddr# #-}
304−fromAddr# ::
305−#ifdef DEVBUILD
306− Storable a =>
307−#endif
308− Int -> Addr# -> Array a
309−fromAddr# n addr# = fromPtr n (castPtr $ Ptr addr#)
310−
311−-- | Generate a byte array from an 'Addr#' that contains a sequence of NUL
312−-- (@0@) terminated bytes. The array would not include the NUL byte. The
313−-- address must be in static read-only memory and must be legally accessible up
314−-- to and including the first NUL byte.
315−--
316−-- An unboxed string literal (e.g. @"hello"#@) is a common example of an
317−-- 'Addr#' in static read only memory. It represents the UTF8 encoded sequence
318−-- of bytes terminated by a NUL byte (a 'CString') corresponding to the
319−-- given unicode string.
320−--
321−-- >>> Array.fromCString# "hello world!"#
322−-- [104,101,108,108,111,32,119,111,114,108,100,33]
323−--
324−-- >>> Array.fromCString# "\255\NUL\255"#
325−-- [255]
326−--
327−-- /See also: 'fromAddr#'/
328−--
329−-- /Unsafe/
330−--
331−-- /Time complexity: O(n) (computes the length of the string)/
332−--
333−-- /Pre-release/
334−--
335−{-# INLINE fromCString# #-}
336−fromCString# :: Addr# -> Array Word8
337−fromCString# addr# = do
338− let cstr = Ptr addr#
339− len = unsafeInlineIO $ c_strlen cstr
340− fromPtr (fromIntegral len) (castPtr cstr)
341−
342-- | Create an 'Array' from the first N elements of a list. The array is
208-- | Create an 'Array' from the first N elements of a list. The array is
343-- allocated to size N, if the list terminates before N elements then the
209-- allocated to size N, if the list terminates before N elements then the
344-- array may hold less than N elements.
210-- array may hold less than N elements.
@@ -462,7 +328,7 @@ byteLength = MA.byteLength . unsafeThaw
462--
328--
463-- @since 0.8.0
329-- @since 0.8.0
464{-# INLINE length #-}
330{-# INLINE length #-}
465−length :: forall a. Storable a => Array a -> Int
331+length :: Storable a => Array a -> Int
466length arr = MA.length (unsafeThaw arr)
332length arr = MA.length (unsafeThaw arr)
467
333
468-- | Unfold an array into a stream in reverse order.
334-- | Unfold an array into a stream in reverse order.
@@ -523,7 +389,7 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
523-- specified index @i@ is the first index of the second slice.
389-- specified index @i@ is the first index of the second slice.
524--
390--
525-- @since 0.7.0
391-- @since 0.7.0
526−splitAt :: forall a. Storable a => Int -> Array a -> (Array a, Array a)
392+splitAt :: Storable a => Int -> Array a -> (Array a, Array a)
527splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
393splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
528 where
394 where
529 (a, b) = MA.splitAt i (unsafeThaw arr)
395 (a, b) = MA.splitAt i (unsafeThaw arr)
@@ -542,12 +408,9 @@ toListFB c n Array{..} = go arrStart
542 -- accumulating the list and fusing better with the pure consumers.
408 -- accumulating the list and fusing better with the pure consumers.
543 --
409 --
544 -- This should be safe as the array contents are guaranteed to be
410 -- This should be safe as the array contents are guaranteed to be
545− -- evaluated/written to before we peek at them.
411+ -- evaluated/written to before we peekWith at them.
546− let !x = unsafeInlineIO $ do
412+ let !x = unsafeInlineIO $ peekWith arrContents p
547− r <- peek p
548− touch arrContents
549− return r
550− in c x (go (PTR_NEXT(p,a)))
413+ in c x (go (INDEX_NEXT(p,a)))
551
414
552-- | Convert an 'Array' into a list.
415-- | Convert an 'Array' into a list.
553--
416--
@@ -582,19 +445,6 @@ writeNAligned :: forall m a. (MonadIO m, Storable a)
582 => Int -> Int -> Fold m a (Array a)
445 => Int -> Int -> Fold m a (Array a)
583writeNAligned alignSize = fmap unsafeFreeze . MA.writeNAligned alignSize
446writeNAligned alignSize = fmap unsafeFreeze . MA.writeNAligned alignSize
584
447
585−-- | @writeNAlignedUnmanaged n@ folds a maximum of @n@ elements from the input
586−-- stream to an 'Array' aligned to the given size and using unmanaged memory.
587−-- This could be useful to allocate memory that we need to allocate only once
588−-- in the lifetime of the program.
589−--
590−-- /Pre-release/
591−--
592−{-# INLINE_NORMAL writeNAlignedUnmanaged #-}
593−writeNAlignedUnmanaged :: forall m a. (MonadIO m, Storable a)
594− => Int -> Int -> Fold m a (Array a)
595−writeNAlignedUnmanaged alignSize =
596− fmap unsafeFreeze . MA.writeNAlignedUnmanaged alignSize
597−
598-- | Like 'writeN' but does not check the array bounds when writing. The fold
448-- | Like 'writeN' but does not check the array bounds when writing. The fold
599-- driver must not call the step function more than 'n' times otherwise it will
449-- driver must not call the step function more than 'n' times otherwise it will
600-- corrupt the memory and crash. This function exists mainly because any
450-- corrupt the memory and crash. This function exists mainly because any
@@ -655,7 +505,8 @@ instance Storable a => IsList (Array a) where
655-- or may not be correct? arrcmp is 40% faster compared to stream equality.
505-- or may not be correct? arrcmp is 40% faster compared to stream equality.
656instance (Storable a, Eq a) => Eq (Array a) where
506instance (Storable a, Eq a) => Eq (Array a) where
657 {-# INLINE (==) #-}
507 {-# INLINE (==) #-}
508+ arr1 == arr2 =
658− arr1 == arr2 = unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
509+ (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
659
510
660-- Since this is a Storable array we cannot have unevaluated data in it so
511-- Since this is a Storable array we cannot have unevaluated data in it so
661-- this is just a no op.
512-- this is just a no op.
@@ -706,8 +557,7 @@ toStreamD_ size Array{..} = D.Stream step arrStart
706 {-# INLINE_LATE step #-}
557 {-# INLINE_LATE step #-}
707 step _ p | p == aEnd = return D.Stop
558 step _ p | p == aEnd = return D.Stop
708 step _ p = liftIO $ do
559 step _ p = liftIO $ do
709− x <- peek p
710− touch arrContents
560+ x <- peekWith arrContents p
711 return $ D.Yield x (p `plusPtr` size)
561 return $ D.Yield x (p `plusPtr` size)
712
562
713{-
563{-
@@ -741,7 +591,7 @@ nil ::
741 Storable a =>
591 Storable a =>
742#endif
592#endif
743 Array a
593 Array a
744−nil = Array MA.nilArrayContents (Ptr nullAddr#) (Ptr nullAddr#)
594+nil = Array MA.nilArrayContents 0 0
745
595
746instance Storable a => Monoid (Array a) where
596instance Storable a => Monoid (Array a) where
747 mempty = nil
597 mempty = nil
core/src/Streamly/Internal/Data/Array/Stream/Fold/Foreign.hs+9−11
@@ -60,8 +60,7 @@ import Control.Applicative (liftA2)
60import Control.Exception (assert)
60import Control.Exception (assert)
61import Control.Monad.Catch (MonadThrow)
61import Control.Monad.Catch (MonadThrow)
62import Control.Monad.IO.Class (MonadIO(..))
62import Control.Monad.IO.Class (MonadIO(..))
63−import Foreign.Ptr (minusPtr, plusPtr)
64−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
63+import Streamly.Internal.Data.Unboxed (Storable, peekWith, sizeOf)
65import GHC.Types (SPEC(..))
64import GHC.Types (SPEC(..))
66import Streamly.Internal.Data.Array.Foreign.Mut.Type (touch)
65import Streamly.Internal.Data.Array.Foreign.Mut.Type (touch)
67import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
66import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
@@ -119,16 +118,15 @@ fromFold (Fold.Fold fstep finitial fextract) =
119
118
120 goArray !_ !cur !fs | cur >= end = do
119 goArray !_ !cur !fs | cur >= end = do
121 assert (cur == end) (return ())
120 assert (cur == end) (return ())
122− liftIO $ touch contents
123 return $ Partial 0 fs
121 return $ Partial 0 fs
124 goArray !_ !cur !fs = do
122 goArray !_ !cur !fs = do
125− x <- liftIO $ peek cur
123+ x <- liftIO $ peekWith contents cur
126 res <- fstep fs x
124 res <- fstep fs x
127 let elemSize = SIZE_OF(a)
125 let elemSize = SIZE_OF(a)
128− next = PTR_NEXT(cur,a)
126+ next = INDEX_NEXT(cur,a)
129 case res of
127 case res of
130 Fold.Done b ->
128 Fold.Done b ->
131− return $ Done ((end `minusPtr` next) `div` elemSize) b
129+ return $ Done ((end - next) `div` elemSize) b
132 Fold.Partial fs1 ->
130 Fold.Partial fs1 ->
133 goArray SPEC next fs1
131 goArray SPEC next fs1
134
132
@@ -153,18 +151,18 @@ fromParserD (ParserD.Parser step1 initial1 extract1) =
153
151
154 {-# INLINE partial #-}
152 {-# INLINE partial #-}
155 partial arrRem cur next elemSize st n fs1 = do
153 partial arrRem cur next elemSize st n fs1 = do
156− let next1 = next `plusPtr` negate (n * elemSize)
154+ let next1 = next - (n * elemSize)
157 if next1 >= start && cur < end
155 if next1 >= start && cur < end
158 then goArray SPEC next1 fs1
156 then goArray SPEC next1 fs1
159 else return $ st (arrRem + n) fs1
157 else return $ st (arrRem + n) fs1
160
158
161 goArray !_ !cur !fs = do
159 goArray !_ !cur !fs = do
162− x <- liftIO $ peek cur
160+ x <- liftIO $ peekWith contents cur
163 liftIO $ touch contents
161 liftIO $ touch contents
164 res <- step1 fs x
162 res <- step1 fs x
165 let elemSize = SIZE_OF(a)
163 let elemSize = SIZE_OF(a)
166− next = PTR_NEXT(cur,a)
164+ next = INDEX_NEXT(cur,a)
167− arrRem = (end `minusPtr` next) `div` elemSize
165+ arrRem = (end - next) `div` elemSize
168 case res of
166 case res of
169 ParserD.Done n b -> do
167 ParserD.Done n b -> do
170 return $ Done (arrRem + n) b
168 return $ Done (arrRem + n) b
@@ -340,7 +338,7 @@ take n (ArrayFold (ParserD.Parser step1 initial1 extract1)) =
340 Error err -> return $ Error err
338 Error err -> return $ Error err
341 else do
339 else do
342 let !(Array contents start _) = arr
340 let !(Array contents start _) = arr
343− end = PTR_INDEX(start,i,a)
341+ end = INDEX_OF(start,i,a)
344 arr1 = Array contents start end
342 arr1 = Array contents start end
345 remaining = negate i1
343 remaining = negate i1
346 res <- step1 r arr1
344 res <- step1 r arr1
core/src/Streamly/Internal/Data/Fold.hs+7−6
@@ -306,9 +306,10 @@ import Data.IORef (newIORef, readIORef, writeIORef)
306import Data.Map.Strict (Map)
306import Data.Map.Strict (Map)
307import Data.Maybe (isJust, fromJust)
307import Data.Maybe (isJust, fromJust)
308import Data.Word (Word32)
308import Data.Word (Word32)
309−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
309+import Foreign.Storable (Storable, peek, sizeOf)
310import Streamly.Internal.Data.IsMap (IsMap(..))
310import Streamly.Internal.Data.IsMap (IsMap(..))
311import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
311import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
312+import Streamly.Internal.Data.Unboxed (Unboxed)
312import Streamly.Internal.Data.Unfold.Type (Unfold(..))
313import Streamly.Internal.Data.Unfold.Type (Unfold(..))
313import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
314import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
314import Streamly.Internal.Data.Stream.Type (Stream)
315import Streamly.Internal.Data.Stream.Type (Stream)
@@ -1483,7 +1484,7 @@ data SplitOnSeqState acc a rb rh w ck =
1483--
1484--
1484-- /Pre-release/
1485-- /Pre-release/
1485{-# INLINE takeEndBySeq #-}
1486{-# INLINE takeEndBySeq #-}
1486−takeEndBySeq :: forall m a b. (MonadIO m, Storable a, Enum a, Eq a) =>
1487+takeEndBySeq :: forall m a b. (MonadIO m, Storable a, Unboxed a, Enum a, Eq a) =>
1487 Array.Array a
1488 Array.Array a
1488 -> Fold m a b
1489 -> Fold m a b
1489 -> Fold m a b
1490 -> Fold m a b
@@ -1619,7 +1620,7 @@ takeEndBySeq patArr (Fold fstep finitial fextract) =
1619-- /Pre-release/
1620-- /Pre-release/
1620--
1621--
1621{-# INLINE takeEndBySeq_ #-}
1622{-# INLINE takeEndBySeq_ #-}
1622−takeEndBySeq_ :: forall m a b. (MonadIO m, Storable a, Enum a, Eq a) =>
1623+takeEndBySeq_ :: forall m a b. (MonadIO m, Storable a, Unboxed a, Enum a, Eq a) =>
1623 Array.Array a
1624 Array.Array a
1624 -> Fold m a b
1625 -> Fold m a b
1625 -> Fold m a b
1626 -> Fold m a b
@@ -2598,7 +2599,7 @@ unfoldMany (Unfold ustep inject) (Fold fstep initial extract) =
2598 consume s a = inject a >>= produce s
2599 consume s a = inject a >>= produce s
2599
2600
2600{-# INLINE topBy #-}
2601{-# INLINE topBy #-}
2601−topBy :: (MonadIO m, Storable a) =>
2602+topBy :: (MonadIO m, Unboxed a) =>
2602 (a -> a -> Ordering)
2603 (a -> a -> Ordering)
2603 -> Int
2604 -> Int
2604 -> Fold m a (MA.Array a)
2605 -> Fold m a (MA.Array a)
@@ -2637,7 +2638,7 @@ topBy cmp n = Fold step initial extract
2637--
2638--
2638-- /Pre-release/
2639-- /Pre-release/
2639{-# INLINE top #-}
2640{-# INLINE top #-}
2640−top :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (MA.Array a)
2641+top :: (MonadIO m, Unboxed a, Ord a) => Int -> Fold m a (MA.Array a)
2641top = topBy $ flip compare
2642top = topBy $ flip compare
2642
2643
2643-- | Fold the input stream to bottom n elements.
2644-- | Fold the input stream to bottom n elements.
@@ -2648,5 +2649,5 @@ top = topBy $ flip compare
2648--
2649--
2649-- /Pre-release/
2650-- /Pre-release/
2650{-# INLINE bottom #-}
2651{-# INLINE bottom #-}
2651−bottom :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (MA.Array a)
2652+bottom :: (MonadIO m, Unboxed a, Ord a) => Int -> Fold m a (MA.Array a)
2652bottom = topBy compare
2653bottom = topBy compare
core/src/Streamly/Internal/Data/Fold/Window.hs+1−1
@@ -56,7 +56,7 @@ where
56
56
57import Control.Monad.IO.Class (MonadIO (liftIO))
57import Control.Monad.IO.Class (MonadIO (liftIO))
58import Data.Bifunctor(bimap)
58import Data.Bifunctor(bimap)
59−import Streamly.Internal.Data.Unboxed (Storable, peek)
59+import Foreign.Storable (Storable, peek)
60
60
61import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..))
61import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..))
62import Streamly.Internal.Data.Tuple.Strict
62import Streamly.Internal.Data.Tuple.Strict
core/src/Streamly/Internal/Data/Ring/Foreign.hs+54−36
@@ -75,15 +75,19 @@ module Streamly.Internal.Data.Ring.Foreign
75import Control.Exception (assert)
75import Control.Exception (assert)
76import Control.Monad.IO.Class (MonadIO(..))
76import Control.Monad.IO.Class (MonadIO(..))
77import Data.Word (Word8)
77import Data.Word (Word8)
78+import Foreign.Storable
78import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)
79import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)
79import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
80import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
80import Foreign.Ptr (plusPtr, minusPtr, castPtr)
81import Foreign.Ptr (plusPtr, minusPtr, castPtr)
81−import Streamly.Internal.Data.Unboxed
82+import Streamly.Internal.Data.Unboxed as Unboxed
82− ( Storable, alignment, peek, poke, sizeOf
83+ ( ArrayContents
84+ , Unboxed
85+ , castContents
86+ , peekWith
83 )
87 )
84import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
88import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
85import GHC.Ptr (Ptr(..))
89import GHC.Ptr (Ptr(..))
86−import Streamly.Internal.Data.Array.Foreign.Mut.Type (Array, memcmp)
90+import Streamly.Internal.Data.Array.Foreign.Mut.Type (Array)
87import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..), lmap)
91import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..), lmap)
88import Streamly.Internal.Data.Stream.Type (Stream)
92import Streamly.Internal.Data.Stream.Type (Stream)
89import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
93import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
@@ -129,7 +133,7 @@ startOf = unsafeForeignPtrToPtr . ringStart
129{-# INLINE new #-}
133{-# INLINE new #-}
130new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)
134new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)
131new count = do
135new count = do
132− let size = count * SIZE_OF(a)
136+ let size = count * max 1 (sizeOf (undefined :: a))
133 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
137 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
134 let p = unsafeForeignPtrToPtr fptr
138 let p = unsafeForeignPtrToPtr fptr
135 return (Ring
139 return (Ring
@@ -401,22 +405,32 @@ cast arr =
401-- be in range.
405-- be in range.
402{-# INLINE unsafeEqArrayN #-}
406{-# INLINE unsafeEqArrayN #-}
403unsafeEqArrayN :: Ring a -> Ptr a -> A.Array a -> Int -> Bool
407unsafeEqArrayN :: Ring a -> Ptr a -> A.Array a -> Int -> Bool
404−unsafeEqArrayN Ring{..} rh A.Array{..} n =
408+unsafeEqArrayN Ring{..} rh A.Array{..} nBytes
409+ | nBytes < 0 = error "unsafeEqArrayN: n should be >= 0"
410+ | nBytes == 0 = True
405− let !res = unsafeInlineIO $ do
411+ | otherwise = unsafeInlineIO $ check (castPtr rh) 0
406− let rs = unsafeForeignPtrToPtr ringStart
412+
407− as = arrStart
413+ where
408− assert (aEnd `minusPtr` as >= ringBound `minusPtr` rs) (return ())
414+
409− let len = ringBound `minusPtr` rh
415+ w8Contents = castContents arrContents :: ArrayContents Word8
410− r1 <- memcmp (castPtr rh) (castPtr as) (min len n)
416+
411− r2 <- if n > len
417+ check p i = do
418+ relem <- peek p
419+ aelem <- peekWith w8Contents i
420+ if relem == aelem
412− then memcmp (castPtr rs) (castPtr (as `plusPtr` len))
421+ then go (p `plusPtr` 1) (i + 1)
413− (min (rh `minusPtr` rs) (n - len))
414− else return True
422+ else return False
415− -- XXX enable these, check perf impact
423+
416− -- touchForeignPtr ringStart
424+ go p i
417− -- touchForeignPtr aStart
425+ | i == nBytes = return True
418− return (r1 && r2)
426+ | castPtr p == ringBound =
419− in res
427+ go (castPtr (unsafeForeignPtrToPtr ringStart)) i
428+ | castPtr p == rh = touchForeignPtr ringStart >> return True
429+ | otherwise = check p i
430+
431+-- XXX This is not modular. We should probably just convert the array and the
432+-- ring buffer to streams and compare the two streams. Need to check perf
433+-- though.
420
434
421-- | Byte compare the entire length of ringBuffer with the given array,
435-- | Byte compare the entire length of ringBuffer with the given array,
422-- starting at the supplied ringHead pointer. Returns true if the Array and
436-- starting at the supplied ringHead pointer. Returns true if the Array and
@@ -428,20 +442,24 @@ unsafeEqArrayN Ring{..} rh A.Array{..} n =
428{-# INLINE unsafeEqArray #-}
442{-# INLINE unsafeEqArray #-}
429unsafeEqArray :: Ring a -> Ptr a -> A.Array a -> Bool
443unsafeEqArray :: Ring a -> Ptr a -> A.Array a -> Bool
430unsafeEqArray Ring{..} rh A.Array{..} =
444unsafeEqArray Ring{..} rh A.Array{..} =
431− let !res = unsafeInlineIO $ do
445+ unsafeInlineIO $ check (castPtr rh) 0
432− let rs = unsafeForeignPtrToPtr ringStart
446+
433− let as = arrStart
447+ where
434− assert (aEnd `minusPtr` as >= ringBound `minusPtr` rs)
448+
435− (return ())
449+ w8Contents = castContents arrContents :: ArrayContents Word8
436− let len = ringBound `minusPtr` rh
450+
437− r1 <- memcmp (castPtr rh) (castPtr as) len
451+ check p i = do
438− r2 <- memcmp (castPtr rs) (castPtr (as `plusPtr` len))
452+ relem <- peek p
439− (rh `minusPtr` rs)
453+ aelem <- peekWith w8Contents i
440− -- XXX enable these, check perf impact
454+ if relem == aelem
441− -- touchForeignPtr ringStart
455+ then go (p `plusPtr` 1) (i + 1)
456+ else return False
457+
458+ go p i
459+ | castPtr p ==
460+ ringBound = go (castPtr (unsafeForeignPtrToPtr ringStart)) i
442− -- touchForeignPtr aStart
461+ | castPtr p == rh = touchForeignPtr ringStart >> return True
443− return (r1 && r2)
462+ | otherwise = check p i
444− in res
445
463
446-------------------------------------------------------------------------------
464-------------------------------------------------------------------------------
447-- Folding
465-- Folding
@@ -543,7 +561,7 @@ data Tuple4' a b c d = Tuple4' !a !b !c !d deriving Show
543-- a))@ action depends on when it is executed. It does not capture the sanpshot
561-- a))@ action depends on when it is executed. It does not capture the sanpshot
544-- of the ring at a particular time.
562-- of the ring at a particular time.
545{-# INLINE slidingWindowWith #-}
563{-# INLINE slidingWindowWith #-}
546−slidingWindowWith :: forall m a b. (MonadIO m, Storable a)
564+slidingWindowWith :: forall m a b. (MonadIO m, Storable a, Unboxed a)
547 => Int -> Fold m ((a, Maybe a), (m (Array a))) b -> Fold m a b
565 => Int -> Fold m ((a, Maybe a), (m (Array a))) b -> Fold m a b
548slidingWindowWith n (Fold step1 initial1 extract1) = Fold step initial extract
566slidingWindowWith n (Fold step1 initial1 extract1) = Fold step initial extract
549
567
@@ -597,6 +615,6 @@ slidingWindowWith n (Fold step1 initial1 extract1) = Fold step initial extract
597-- there is no old element.
615-- there is no old element.
598--
616--
599{-# INLINE slidingWindow #-}
617{-# INLINE slidingWindow #-}
600−slidingWindow :: forall m a b. (MonadIO m, Storable a)
618+slidingWindow :: forall m a b. (MonadIO m, Storable a, Unboxed a)
601 => Int -> Fold m (a, Maybe a) b -> Fold m a b
619 => Int -> Fold m (a, Maybe a) b -> Fold m a b
602slidingWindow n f = slidingWindowWith n (lmap fst f)
620slidingWindow n f = slidingWindowWith n (lmap fst f)
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+3−2
@@ -73,8 +73,9 @@ where
73import Control.Monad.Catch (MonadThrow)
73import Control.Monad.Catch (MonadThrow)
74import Control.Monad.IO.Class (MonadIO(..))
74import Control.Monad.IO.Class (MonadIO(..))
75import Control.Monad.Trans.Class (MonadTrans(..))
75import Control.Monad.Trans.Class (MonadTrans(..))
76+import Foreign.Storable (Storable)
76import Streamly.Internal.Data.Parser (Parser (..))
77import Streamly.Internal.Data.Parser (Parser (..))
77−import Streamly.Internal.Data.Unboxed (Storable)
78+import Streamly.Internal.Data.Unboxed (Unboxed)
78
79
79import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
80import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
80import qualified Streamly.Internal.Data.Fold as Fold
81import qualified Streamly.Internal.Data.Fold as Fold
@@ -277,7 +278,7 @@ isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
277-- /Requires 'Storable' constraint/
278-- /Requires 'Storable' constraint/
278--
279--
279{-# INLINE isInfixOf #-}
280{-# INLINE isInfixOf #-}
280−isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a)
281+isInfixOf :: (MonadIO m, Eq a, Enum a, Unboxed a, Storable a)
281 => Stream m a -> Stream m a -> m Bool
282 => Stream m a -> Stream m a -> m Bool
282isInfixOf infx stream = do
283isInfixOf infx stream = do
283 arr <- fold Array.write infx
284 arr <- fold Array.write infx
core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+4−3
@@ -155,7 +155,7 @@ import Control.Monad.IO.Class (MonadIO(..))
155import Data.Bits (shiftR, shiftL, (.|.), (.&.))
155import Data.Bits (shiftR, shiftL, (.|.), (.&.))
156import Data.Functor.Identity ( Identity )
156import Data.Functor.Identity ( Identity )
157import Data.Word (Word32)
157import Data.Word (Word32)
158−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
158+import Foreign.Storable (Storable, peek, sizeOf)
159import Fusion.Plugin.Types (Fuse(..))
159import Fusion.Plugin.Types (Fuse(..))
160import GHC.Types (SPEC(..))
160import GHC.Types (SPEC(..))
161
161
@@ -166,6 +166,7 @@ import Streamly.Internal.Data.Parser (ParseError(..))
166import Streamly.Internal.Data.Refold.Type (Refold(..))
166import Streamly.Internal.Data.Refold.Type (Refold(..))
167import Streamly.Internal.Data.SVar.Type (adaptState)
167import Streamly.Internal.Data.SVar.Type (adaptState)
168import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
168import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
169+import Streamly.Internal.Data.Unboxed (Unboxed)
169import Streamly.Internal.Data.Unfold.Type (Unfold(..))
170import Streamly.Internal.Data.Unfold.Type (Unfold(..))
170
171
171import qualified Streamly.Internal.Data.Array.Foreign.Type as A
172import qualified Streamly.Internal.Data.Array.Foreign.Type as A
@@ -1702,7 +1703,7 @@ data SplitOnSeqState rb rh ck w fs s b x =
1702
1703
1703{-# INLINE_NORMAL splitOnSeq #-}
1704{-# INLINE_NORMAL splitOnSeq #-}
1704splitOnSeq
1705splitOnSeq
1705− :: forall m a b. (MonadIO m, Storable a, Enum a, Eq a)
1706+ :: forall m a b. (MonadIO m, Storable a, Unboxed a, Enum a, Eq a)
1706 => Array a
1707 => Array a
1707 -> Fold m a b
1708 -> Fold m a b
1708 -> Stream m a
1709 -> Stream m a
@@ -2015,7 +2016,7 @@ data SplitOnSuffixSeqState rb rh ck w fs s b x =
2015
2016
2016{-# INLINE_NORMAL splitOnSuffixSeq #-}
2017{-# INLINE_NORMAL splitOnSuffixSeq #-}
2017splitOnSuffixSeq
2018splitOnSuffixSeq
2018− :: forall m a b. (MonadIO m, Storable a, Enum a, Eq a)
2019+ :: forall m a b. (MonadIO m, Storable a, Unboxed a, Enum a, Eq a)
2019 => Bool
2020 => Bool
2020 -> Array a
2021 -> Array a
2021 -> Fold m a b
2022 -> Fold m a b
core/src/Streamly/Internal/Data/Unboxed.hs+289−47
@@ -3,58 +3,300 @@
3
3
4module Streamly.Internal.Data.Unboxed
4module Streamly.Internal.Data.Unboxed
5 ( Storable
5 ( Storable
6+ , Unboxed
6 , alignment
7 , alignment
7− , peek
8+ , peekWith
8− , poke
9+ , pokeWith
9 , sizeOf
10 , sizeOf
11+ , ArrayContents(..)
12+ , castContents
13+ , touch
14+ , getMutableByteArray#
10 ) where
15 ) where
11
16
12−#ifdef USE_STORABLE
17+#include "MachDeps.h"
13−import Foreign.Storable (Storable(..))
18+
14−#else
19+#include "ArrayMacros.h"
20+
15−import Data.Int (Int8)
21+import Data.Complex (Complex((:+)), realPart)
16−import Data.Primitive.Types (Prim(..), sizeOf, alignment)
17import GHC.Base (IO(..))
22import GHC.Base (IO(..))
18−import GHC.Ptr (Ptr(..))
23+import GHC.Int (Int32(..))
24+import GHC.Word (Word8(..), Word64(..))
25+
26+import GHC.Exts
27+
28+--------------------------------------------------------------------------------
29+-- The ArrayContents type
30+--------------------------------------------------------------------------------
31+
32+-- XXX can use UnliftedNewtypes
33+data ArrayContents a = ArrayContents !(MutableByteArray# RealWorld)
34+
35+{-# INLINE getMutableByteArray# #-}
36+getMutableByteArray# :: ArrayContents a -> MutableByteArray# RealWorld
37+getMutableByteArray# (ArrayContents mbarr) = mbarr
38+
39+{-# INLINE castContents #-}
40+castContents :: ArrayContents a -> ArrayContents b
41+castContents (ArrayContents mbarr) = ArrayContents mbarr
42+
43+{-# INLINE touch #-}
44+touch :: ArrayContents a -> IO ()
45+touch (ArrayContents contents) =
46+ IO $ \s -> case touch# contents s of s' -> (# s', () #)
47+
48+--------------------------------------------------------------------------------
49+-- The Unboxed type class
50+--------------------------------------------------------------------------------
51+
52+#if !MIN_VERSION_base(4,12,0)
53+
54+#define SIZEOF_HSCHAR_PRIMITIVE 4#
55+#define SIZEOF_HSINT_PRIMITIVE 8#
56+#define SIZEOF_HSWORD_PRIMITIVE 8#
57+#define SIZEOF_WORD8_PRIMITIVE 1#
58+#define SIZEOF_WORD64_PRIMITIVE 8#
59+#define SIZEOF_HSDOUBLE_PRIMITIVE 8#
60+#define SIZEOF_INT32_PRIMITIVE 4#
61+
62+#ifdef __GHCJS__
63+#define WORD64TYP Word64#
64+#else
65+#define WORD64TYP Word#
19#endif
66#endif
20
67
21−#ifndef USE_STORABLE
68+{-# INLINE readWord8ArrayAsWideChar# #-}
22−type Storable = Prim
69+readWord8ArrayAsWideChar# ::
23−
70+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Char# #)
24−{-# INLINE peek #-}
71+readWord8ArrayAsWideChar# arr# i# s# =
25−peek :: Prim a => Ptr a -> IO a
72+ readWideCharArray# arr# (quotInt# i# SIZEOF_HSCHAR_PRIMITIVE) s#
26−peek (Ptr addr#) = IO $ \s# -> readOffAddr# addr# 0# s#
73+
27−
74+{-# INLINE writeWord8ArrayAsWideChar# #-}
28−{-# INLINE poke #-}
75+writeWord8ArrayAsWideChar# ::
29−poke :: Prim a => Ptr a -> a -> IO ()
76+ MutableByteArray# d -> Int# -> Char# -> State# d -> State# d
30−poke (Ptr addr#) a = IO $ \s# -> (# writeOffAddr# addr# 0# a s#, () #)
77+writeWord8ArrayAsWideChar# arr# i# a# s# =
31−
78+ writeWideCharArray# arr# (quotInt# i# SIZEOF_HSCHAR_PRIMITIVE) a# s#
32−-- | Orphan Prim instance of Bool implemented using Int8
79+
33−instance Prim Bool where
80+{-# INLINE readWord8ArrayAsInt# #-}
34− sizeOf# _ = sizeOf# (undefined :: Int8)
81+readWord8ArrayAsInt# ::
35− alignment# _ = alignment# (undefined :: Int8)
82+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Int# #)
36− indexByteArray# arr# i# = indexByteArray# arr# i# /= (0 :: Int8)
83+readWord8ArrayAsInt# arr# i# s# =
37− readByteArray# arr# i# s# =
84+ readIntArray# arr# (quotInt# i# SIZEOF_HSINT_PRIMITIVE) s#
38− case readByteArray# arr# i# s# of
85+
39− (# s1#, i :: Int8 #) -> (# s1#, i /= 0 #)
86+{-# INLINE writeWord8ArrayAsInt# #-}
40− writeByteArray# arr# i# a s# =
87+writeWord8ArrayAsInt# ::
41− case a of
88+ MutableByteArray# d -> Int# -> Int# -> State# d -> State# d
42− True -> writeByteArray# arr# i# (1 :: Int8) s#
89+writeWord8ArrayAsInt# arr# i# a# s# =
43− False -> writeByteArray# arr# i# (0 :: Int8) s#
90+ writeIntArray# arr# (quotInt# i# SIZEOF_HSINT_PRIMITIVE) a# s#
44− setByteArray# arr# off# len# a s# =
91+
45− case a of
92+{-# INLINE readWord8ArrayAsInt32# #-}
46− True -> setByteArray# arr# off# len# (1 :: Int8) s#
93+readWord8ArrayAsInt32# ::
47− False -> setByteArray# arr# off# len# (0 :: Int8) s#
94+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Int# #)
48− indexOffAddr# addr# i# = indexOffAddr# addr# i# /= (0 :: Int8)
95+readWord8ArrayAsInt32# arr# i# s# =
49− readOffAddr# addr# i# s# =
96+ readInt32Array# arr# (quotInt# i# SIZEOF_INT32_PRIMITIVE) s#
50− case readOffAddr# addr# i# s# of
97+
51− (# s1#, i :: Int8 #) -> (# s1#, i /= 0 #)
98+{-# INLINE writeWord8ArrayAsInt32# #-}
52− writeOffAddr# addr# i# a s# =
99+writeWord8ArrayAsInt32# ::
53− case a of
100+ MutableByteArray# d -> Int# -> Int# -> State# d -> State# d
54− True -> writeOffAddr# addr# i# (1 :: Int8) s#
101+writeWord8ArrayAsInt32# arr# i# a# s# =
55− False -> writeOffAddr# addr# i# (0 :: Int8) s#
102+ writeInt32Array# arr# (quotInt# i# SIZEOF_INT32_PRIMITIVE) a# s#
56− setOffAddr# addr# off# len# a s# =
103+
57− case a of
104+{-# INLINE readWord8ArrayAsWord# #-}
58− True -> setOffAddr# addr# off# len# (1 :: Int8) s#
105+readWord8ArrayAsWord# ::
59− False -> setOffAddr# addr# off# len# (0 :: Int8) s#
106+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Word# #)
107+readWord8ArrayAsWord# arr# i# s# =
108+ readWordArray# arr# (quotInt# i# SIZEOF_HSWORD_PRIMITIVE) s#
109+
110+{-# INLINE writeWord8ArrayAsWord# #-}
111+writeWord8ArrayAsWord# ::
112+ MutableByteArray# d -> Int# -> Word# -> State# d -> State# d
113+writeWord8ArrayAsWord# arr# i# a# s# =
114+ writeWordArray# arr# (quotInt# i# SIZEOF_HSWORD_PRIMITIVE) a# s#
115+
116+{-# INLINE readWord8ArrayAsWord64# #-}
117+readWord8ArrayAsWord64# ::
118+ MutableByteArray# d -> Int# -> State# d -> (# State# d, WORD64TYP #)
119+readWord8ArrayAsWord64# arr# i# s# =
120+ readWord64Array# arr# (quotInt# i# SIZEOF_WORD64_PRIMITIVE) s#
121+
122+{-# INLINE writeWord8ArrayAsWord64# #-}
123+writeWord8ArrayAsWord64# ::
124+ MutableByteArray# d -> Int# -> WORD64TYP -> State# d -> State# d
125+writeWord8ArrayAsWord64# arr# i# a# s# =
126+ writeWord64Array# arr# (quotInt# i# SIZEOF_WORD64_PRIMITIVE) a# s#
127+
128+{-# INLINE readWord8ArrayAsDouble# #-}
129+readWord8ArrayAsDouble# ::
130+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Double# #)
131+readWord8ArrayAsDouble# arr# i# s# =
132+ readDoubleArray# arr# (quotInt# i# SIZEOF_HSDOUBLE_PRIMITIVE) s#
133+
134+{-# INLINE writeWord8ArrayAsDouble# #-}
135+writeWord8ArrayAsDouble# ::
136+ MutableByteArray# d -> Int# -> Double# -> State# d -> State# d
137+writeWord8ArrayAsDouble# arr# i# a# s# =
138+ writeDoubleArray# arr# (quotInt# i# SIZEOF_HSDOUBLE_PRIMITIVE) a# s#
139+
60#endif
140#endif
141+
142+-- In theory we could convert a type to and from a byte stream and use that
143+-- to implement boxing, unboxing. But that would be inefficient. This type
144+-- class allows each primitive type to have its own specific efficient
145+-- implementation to read and write the type to memory.
146+--
147+-- The ArrayContents type currently uses a MutableByteArray#. However, we could
148+-- also use a foreign ptr instead. That would just make the implementation a
149+-- bit more complicated. In that case the type class would have to support
150+-- reading and writing to a Ptr as well, basically what Storable does. We will
151+-- also need to touch the anchoring ptr at the right points which is prone to
152+-- errors. However, it should be simple to implement unmanaged/read-only memory
153+-- arrays by allowing a Ptr type in ArrayContents, though it would require all
154+-- instances to support reading from a Ptr.
155+--
156+-- There is a reason for using byte offset instead of element index in read and
157+-- write operations in the type class. If we use element index slicing of the
158+-- array becomes rigid. We can only slice the array at addresses that are
159+-- aligned with the type, therefore, we cannot slice at misaligned location and
160+-- then cast the slice into another type which does not ncessarily align with
161+-- the original type.
162+--
163+-- As a side note, there seem to be no performance advantage of alignment
164+-- anymore, see
165+-- https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/
166+--
167+-- We try to keep the instances compatible with Storable. But we may have to
168+-- differ, for example a Bool instance can be implemented more efficiently
169+-- using a bit vector. Need to check the performance. We can possibly write
170+-- tests for checking compatibility with Storable.
171+
172+-- | An 'Unboxed' type supplies operations for reading and writing the type
173+-- from and to a byte array in memory. The read operation constructs the boxed
174+-- type from an unboxed byte representation in memory. The write operation
175+-- writes the boxed type to an unboxed byte representation in memory.
176+--
177+-- This type class is similar to Storable. While Storable allows writing to a
178+-- Ptr, this type class allows writing to a MutableByteArray#.
179+class Unboxed a where
180+ sizeOf :: a -> Int
181+ alignment :: a -> Int
182+
183+ -- Read an element of type "a" from a MutableByteArray given the byte
184+ -- index.
185+ readByteArray :: ArrayContents a -> Int -> IO a
186+ writeByteArray :: ArrayContents a -> Int -> a -> IO ()
187+
188+#define DERIVE_UNBOXED(_type, _constructor, _sizeOf, _alignment, _readArray, _writeArray) \
189+instance Unboxed _type where { \
190+; {-# INLINE sizeOf #-} \
191+; sizeOf _ = _sizeOf \
192+; {-# INLINE alignment #-} \
193+; alignment _ = _alignment \
194+; {-# INLINE readByteArray #-} \
195+; readByteArray (ArrayContents mbarr) (I# n) = IO $ \s -> \
196+ case _readArray mbarr n s of \
197+ { (# s1, i #) -> (# s1, _constructor i #) } \
198+; {-# INLINE writeByteArray #-} \
199+; writeByteArray (ArrayContents mbarr) (I# n) (_constructor val) = \
200+ IO $ \s -> (# _writeArray mbarr n val s, () #) \
201+}
202+
203+DERIVE_UNBOXED( Char
204+ , C#
205+ , SIZEOF_HSCHAR
206+ , ALIGNMENT_HSCHAR
207+ , readWord8ArrayAsWideChar#
208+ , writeWord8ArrayAsWideChar#)
209+
210+DERIVE_UNBOXED( Int32
211+ , I32#
212+ , SIZEOF_INT32
213+ , ALIGNMENT_INT32
214+ , readWord8ArrayAsInt32#
215+ , writeWord8ArrayAsInt32#)
216+
217+DERIVE_UNBOXED( Int
218+ , I#
219+ , SIZEOF_HSINT
220+ , ALIGNMENT_HSINT
221+ , readWord8ArrayAsInt#
222+ , writeWord8ArrayAsInt#)
223+
224+DERIVE_UNBOXED( Word
225+ , W#
226+ , SIZEOF_HSWORD
227+ , ALIGNMENT_HSWORD
228+ , readWord8ArrayAsWord#
229+ , writeWord8ArrayAsWord#)
230+
231+DERIVE_UNBOXED( Word8
232+ , W8#
233+ , SIZEOF_WORD8
234+ , ALIGNMENT_WORD8
235+ , readWord8Array#
236+ , writeWord8Array#)
237+
238+DERIVE_UNBOXED( Word64
239+ , W64#
240+ , SIZEOF_WORD64
241+ , ALIGNMENT_WORD64
242+ , readWord8ArrayAsWord64#
243+ , writeWord8ArrayAsWord64#)
244+
245+DERIVE_UNBOXED( Double
246+ , D#
247+ , SIZEOF_DOUBLE
248+ , ALIGNMENT_DOUBLE
249+ , readWord8ArrayAsDouble#
250+ , writeWord8ArrayAsDouble#)
251+
252+instance Unboxed Bool where
253+ {-# INLINE sizeOf #-}
254+ sizeOf _ = sizeOf (undefined :: Word8)
255+
256+ {-# INLINE alignment #-}
257+ alignment _ = alignment (undefined :: Word8)
258+
259+ {-# INLINE readByteArray #-}
260+ readByteArray arr i = do
261+ res <- readByteArray (castContents arr) i
262+ return $ res /= (0 :: Word8)
263+
264+ {-# INLINE writeByteArray #-}
265+ writeByteArray arr i a =
266+ case a of
267+ True -> writeByteArray (castContents arr) i (1 :: Word8)
268+ False -> writeByteArray (castContents arr) i (0 :: Word8)
269+
270+instance forall a. Unboxed a => Unboxed (Complex a) where
271+ {-# INLINE sizeOf #-}
272+ sizeOf a = 2 * sizeOf (realPart a)
273+
274+ {-# INLINE alignment #-}
275+ alignment a = alignment (realPart a)
276+
277+ {-# INLINE readByteArray #-}
278+ readByteArray arr i = do
279+ let contents = castContents arr :: ArrayContents a
280+ real <- readByteArray contents i
281+ img <- readByteArray contents (i + SIZE_OF(a))
282+ return $ real :+ img
283+
284+ {-# INLINE writeByteArray #-}
285+ writeByteArray arr i (real :+ img) = do
286+ let contents = castContents arr :: ArrayContents a
287+ writeByteArray contents i real
288+ writeByteArray contents (i + SIZE_OF(a)) img
289+
290+--------------------------------------------------------------------------------
291+-- Functions
292+--------------------------------------------------------------------------------
293+
294+type Storable a = Unboxed a
295+
296+{-# INLINE peekWith #-}
297+peekWith :: Unboxed a => ArrayContents a -> Int -> IO a
298+peekWith = readByteArray
299+
300+{-# INLINE pokeWith #-}
301+pokeWith :: Unboxed a => ArrayContents a -> Int -> a -> IO ()
302+pokeWith = writeByteArray
core/streamly-core.cabal−8
@@ -82,11 +82,6 @@ flag use-unliftio
82 manual: True
82 manual: True
83 default: False
83 default: False
84
84
85−flag use-storable
86− description: Use Storable for unboxed arrays
87− manual: True
88− default: False
89−
90flag use-unfolds
85flag use-unfolds
91 description: Use unfolds for generation everywhere
86 description: Use unfolds for generation everywhere
92 manual: True
87 manual: True
@@ -122,9 +117,6 @@ common compile-options
122 if flag(use-c-malloc)
117 if flag(use-c-malloc)
123 cpp-options: -DUSE_C_MALLOC
118 cpp-options: -DUSE_C_MALLOC
124
119
125− if flag(use-storable)
126− cpp-options: -DUSE_STORABLE
127−
128 ghc-options: -Wall
120 ghc-options: -Wall
129 -Wcompat
121 -Wcompat
130 -Wunrecognised-warning-flags
122 -Wunrecognised-warning-flags
src/Streamly/Internal/Data/Array/ArrayMacros.h+12
@@ -25,3 +25,15 @@
25#define PTR_VALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) <= end
25#define PTR_VALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) <= end
26#define PTR_INVALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) > end
26#define PTR_INVALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) > end
27
27
28+-------------------------------------------------------------------------------
29+-- Macros to access array indices
30+-------------------------------------------------------------------------------
31+
32+#define INDEX_NEXT(i,a) i + SIZE_OF(a)
33+#define INDEX_PREV(i,a) i - SIZE_OF(a)
34+
35+#define INDEX_OF(base,i,a) base + (SIZE_OF(a) * i)
36+#define RINDEX_OF(base,i,a) base - (SIZE_OF(a) * (i + 1))
37+
38+#define INDEX_VALID(i,end,a) i + SIZE_OF(a) <= end
39+#define INDEX_INVALID(i,end,a) i + SIZE_OF(a) > end
src/Streamly/Internal/Data/Array/Stream/Foreign.hs+60−87
@@ -58,18 +58,12 @@ import Control.Monad.IO.Class (MonadIO(..))
58import Data.Semigroup (Semigroup(..))
58import Data.Semigroup (Semigroup(..))
59#endif
59#endif
60import Data.Word (Word8)
60import Data.Word (Word8)
61−import Foreign.ForeignPtr (touchForeignPtr)
62−import Foreign.Ptr (minusPtr, plusPtr, castPtr)
63−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
61+import Streamly.Internal.Data.Unboxed (Storable, peekWith, sizeOf)
64import Fusion.Plugin.Types (Fuse(..))
62import Fusion.Plugin.Types (Fuse(..))
65import GHC.Exts (SpecConstrAnnotation(..))
63import GHC.Exts (SpecConstrAnnotation(..))
66−import GHC.ForeignPtr (ForeignPtr(..))
67−import GHC.Ptr (Ptr(..))
68import GHC.Types (SPEC(..))
64import GHC.Types (SPEC(..))
69import Prelude hiding (null, last, (!!), read, concat, unlines)
65import Prelude hiding (null, last, (!!), read, concat, unlines)
70
66
71−import Streamly.Internal.Data.Array.Foreign.Mut.Type
72− (arrayToFptrContents, fptrToArrayContents, touch)
73import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
67import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
74import Streamly.Internal.Data.Array.Stream.Fold.Foreign (ArrayFold(..))
68import Streamly.Internal.Data.Array.Stream.Fold.Foreign (ArrayFold(..))
75import Streamly.Internal.Data.Fold.Type (Fold(..))
69import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -79,7 +73,8 @@ import Streamly.Internal.Data.Stream.IsStream.Type
79 (IsStream, fromStreamD, toStreamD)
73 (IsStream, fromStreamD, toStreamD)
80import Streamly.Internal.Data.SVar (adaptState, defState)
74import Streamly.Internal.Data.SVar (adaptState, defState)
81import Streamly.Internal.Data.Array.Foreign.Mut.Type
75import Streamly.Internal.Data.Array.Foreign.Mut.Type
82− (memcpy, allocBytesToElemCount)
76+ (allocBytesToElemCount)
77+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
83
78
84import qualified Streamly.Internal.Data.Array.Foreign as A
79import qualified Streamly.Internal.Data.Array.Foreign as A
85import qualified Streamly.Internal.Data.Array.Foreign as Array
80import qualified Streamly.Internal.Data.Array.Foreign as Array
@@ -175,7 +170,7 @@ interposeSuffix x = S.interposeSuffix x A.read
175
170
176data FlattenState s a =
171data FlattenState s a =
177 OuterLoop s
172 OuterLoop s
178− | InnerLoop s !MA.ArrayContents !(Ptr a) !(Ptr a)
173+ | InnerLoop s !(MA.ArrayContents a) !Int !Int
179
174
180-- XXX Remove monadIO constraint
175-- XXX Remove monadIO constraint
181{-# INLINE_NORMAL unlines #-}
176{-# INLINE_NORMAL unlines #-}
@@ -196,11 +191,8 @@ unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
196 return $ D.Yield sep $ OuterLoop st
191 return $ D.Yield sep $ OuterLoop st
197
192
198 step' _ (InnerLoop st contents p end) = do
193 step' _ (InnerLoop st contents p end) = do
199− x <- liftIO $ do
200− r <- peek p
201− touch contents
194+ x <- liftIO $ peekWith contents p
202− return r
203− return $ D.Yield x (InnerLoop st contents (PTR_NEXT(p,a)) end)
195+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
204
196
205-------------------------------------------------------------------------------
197-------------------------------------------------------------------------------
206-- Compact
198-- Compact
@@ -339,25 +331,24 @@ foldBreakD (Fold fstep initial extract) stream@(D.Stream step state) = do
339 go !_ st !fs = do
331 go !_ st !fs = do
340 r <- step defState st
332 r <- step defState st
341 case r of
333 case r of
342− D.Yield (Array contents start (Ptr end)) s ->
334+ D.Yield (Array contents start end) s ->
343− let fp = ForeignPtr end (arrayToFptrContents contents)
335+ let fp = Tuple' end contents
344 in goArray SPEC s fp start fs
336 in goArray SPEC s fp start fs
345 D.Skip s -> go SPEC s fs
337 D.Skip s -> go SPEC s fs
346 D.Stop -> do
338 D.Stop -> do
347 b <- extract fs
339 b <- extract fs
348 return (b, D.nil)
340 return (b, D.nil)
349
341
350− goArray !_ s fp@(ForeignPtr end _) !cur !fs
342+ goArray !_ s (Tuple' end _) !cur !fs
351− | cur == Ptr end = do
343+ | cur == end = do
352− liftIO $ touchForeignPtr fp
353 go SPEC s fs
344 go SPEC s fs
354− goArray !_ st fp@(ForeignPtr end contents) !cur !fs = do
345+ goArray !_ st fp@(Tuple' end contents) !cur !fs = do
355− x <- liftIO $ peek cur
346+ x <- liftIO $ peekWith contents cur
356 res <- fstep fs x
347 res <- fstep fs x
357− let next = PTR_NEXT(cur,a)
348+ let next = INDEX_NEXT(cur,a)
358 case res of
349 case res of
359 FL.Done b -> do
350 FL.Done b -> do
360− let arr = Array (fptrToArrayContents contents) next (Ptr end)
351+ let arr = Array contents next end
361 return $! (b, D.cons arr (D.Stream step st))
352 return $! (b, D.cons arr (D.Stream step st))
362 FL.Partial fs1 -> goArray SPEC st fp next fs1
353 FL.Partial fs1 -> goArray SPEC st fp next fs1
363
354
@@ -376,22 +367,21 @@ foldBreakK (Fold fstep initial extract) stream = do
376 go !fs st = do
367 go !fs st = do
377 let stop = (, K.nil) <$> extract fs
368 let stop = (, K.nil) <$> extract fs
378 single a = yieldk a K.nil
369 single a = yieldk a K.nil
379− yieldk (Array contents start (Ptr end)) r =
370+ yieldk (Array contents start end) r =
380− let fp = ForeignPtr end (arrayToFptrContents contents)
371+ let fp = Tuple' end contents
381 in goArray fs r fp start
372 in goArray fs r fp start
382 in K.foldStream defState yieldk single stop st
373 in K.foldStream defState yieldk single stop st
383
374
384− goArray !fs st fp@(ForeignPtr end _) !cur
375+ goArray !fs st (Tuple' end _) !cur
385− | cur == Ptr end = do
376+ | cur == end = do
386− liftIO $ touchForeignPtr fp
387 go fs st
377 go fs st
388− goArray !fs st fp@(ForeignPtr end contents) !cur = do
378+ goArray !fs st fp@(Tuple' end contents) !cur = do
389− x <- liftIO $ peek cur
379+ x <- liftIO $ peekWith contents cur
390 res <- fstep fs x
380 res <- fstep fs x
391− let next = PTR_NEXT(cur,a)
381+ let next = INDEX_NEXT(cur,a)
392 case res of
382 case res of
393 FL.Done b -> do
383 FL.Done b -> do
394− let arr = Array (fptrToArrayContents contents) next (Ptr end)
384+ let arr = Array contents next end
395 return $! (b, K.cons arr st)
385 return $! (b, K.cons arr st)
396 FL.Partial fs1 -> goArray fs1 st fp next
386 FL.Partial fs1 -> goArray fs1 st fp next
397
387
@@ -437,14 +427,14 @@ takeArrayListRev = go
437 else if n == len
427 else if n == len
438 then [x]
428 then [x]
439 else let !(Array contents _ end) = x
429 else let !(Array contents _ end) = x
440− !start = end `plusPtr` negate (n * SIZE_OF(a))
430+ !start = end - (n * SIZE_OF(a))
441 in [Array contents start end]
431 in [Array contents start end]
442
432
443-- When we have to take an array partially, take the last part of the array in
433-- When we have to take an array partially, take the last part of the array in
444-- the first split.
434-- the first split.
445{-# INLINE splitAtArrayListRev #-}
435{-# INLINE splitAtArrayListRev #-}
446−splitAtArrayListRev :: forall a. Storable a =>
436+splitAtArrayListRev ::
447− Int -> [Array a] -> ([Array a],[Array a])
437+ forall a. Storable a => Int -> [Array a] -> ([Array a],[Array a])
448splitAtArrayListRev n ls
438splitAtArrayListRev n ls
449 | n <= 0 = ([], ls)
439 | n <= 0 = ([], ls)
450 | otherwise = go n ls
440 | otherwise = go n ls
@@ -459,7 +449,7 @@ splitAtArrayListRev n ls
459 else if m == len
449 else if m == len
460 then ([x],xs)
450 then ([x],xs)
461 else let !(Array contents start end) = x
451 else let !(Array contents start end) = x
462− end1 = end `plusPtr` negate (m * SIZE_OF(a))
452+ end1 = end - (m * SIZE_OF(a))
463 arr2 = Array contents start end1
453 arr2 = Array contents start end1
464 arr1 = Array contents end1 end
454 arr1 = Array contents end1 end
465 in ([arr1], arr2:xs)
455 in ([arr1], arr2:xs)
@@ -478,17 +468,7 @@ spliceArraysLenUnsafe :: (MonadIO m, Storable a)
478 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
468 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
479spliceArraysLenUnsafe len buffered = do
469spliceArraysLenUnsafe len buffered = do
480 arr <- liftIO $ MA.newArray len
470 arr <- liftIO $ MA.newArray len
481− end <- S.foldlM' writeArr (return $ MA.aEnd arr) buffered
471+ S.foldlM' MA.spliceUnsafe (return arr) buffered
482− return $ arr {MA.aEnd = end}
483−
484− where
485−
486− writeArr dst (MA.Array ac src ae _) =
487− liftIO $ do
488− let count = ae `minusPtr` src
489− memcpy (castPtr dst) (castPtr src) count
490− touch ac
491− return $ dst `plusPtr` count
492
472
493{-# INLINE _spliceArrays #-}
473{-# INLINE _spliceArrays #-}
494_spliceArrays :: (MonadIO m, Storable a)
474_spliceArrays :: (MonadIO m, Storable a)
@@ -497,17 +477,12 @@ _spliceArrays s = do
497 buffered <- S.foldr S.cons S.nil s
477 buffered <- S.foldr S.cons S.nil s
498 len <- S.sum (S.map Array.length buffered)
478 len <- S.sum (S.map Array.length buffered)
499 arr <- liftIO $ MA.newArray len
479 arr <- liftIO $ MA.newArray len
500− end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
480+ final <- S.foldlM' writeArr (return arr) s
501− return $ A.unsafeFreeze $ arr {MA.aEnd = end}
481+ return $ A.unsafeFreeze final
502
482
503 where
483 where
504
484
505− writeArr dst (Array ac src ae) =
485+ writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)
506− liftIO $ do
507− let count = ae `minusPtr` src
508− memcpy (castPtr dst) (castPtr src) count
509− touch ac
510− return $ dst `plusPtr` count
511
486
512{-# INLINE _spliceArraysBuffered #-}
487{-# INLINE _spliceArraysBuffered #-}
513_spliceArraysBuffered :: (MonadIO m, Storable a)
488_spliceArraysBuffered :: (MonadIO m, Storable a)
@@ -598,23 +573,22 @@ parseBreakD
598 go !_ st backBuf !pst = do
573 go !_ st backBuf !pst = do
599 r <- step defState st
574 r <- step defState st
600 case r of
575 case r of
601− D.Yield (Array contents start (Ptr end)) s ->
576+ D.Yield (Array contents start end) s ->
602 gobuf SPEC s backBuf
577 gobuf SPEC s backBuf
603− (ForeignPtr end (arrayToFptrContents contents)) start pst
578+ (Tuple' end contents) start pst
604 D.Skip s -> go SPEC s backBuf pst
579 D.Skip s -> go SPEC s backBuf pst
605 D.Stop -> do
580 D.Stop -> do
606 b <- extract pst
581 b <- extract pst
607 return (b, D.nil)
582 return (b, D.nil)
608
583
609 -- Use strictness on "cur" to keep it unboxed
584 -- Use strictness on "cur" to keep it unboxed
610− gobuf !_ s backBuf fp@(ForeignPtr end _) !cur !pst
585+ gobuf !_ s backBuf (Tuple' end _) !cur !pst
611− | cur == Ptr end = do
586+ | cur == end = do
612− liftIO $ touchForeignPtr fp
613 go SPEC s backBuf pst
587 go SPEC s backBuf pst
614− gobuf !_ s backBuf fp@(ForeignPtr end contents) !cur !pst = do
588+ gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
615− x <- liftIO $ peek cur
589+ x <- liftIO $ peekWith contents cur
616 pRes <- pstep pst x
590 pRes <- pstep pst x
617− let next = PTR_NEXT(cur,a)
591+ let next = INDEX_NEXT(cur,a)
618 case pRes of
592 case pRes of
619 PR.Partial 0 pst1 ->
593 PR.Partial 0 pst1 ->
620 gobuf SPEC s (List []) fp next pst1
594 gobuf SPEC s (List []) fp next pst1
@@ -622,10 +596,10 @@ parseBreakD
622 assert (n <= Prelude.length (x:getList backBuf)) (return ())
596 assert (n <= Prelude.length (x:getList backBuf)) (return ())
623 let src0 = Prelude.take n (x:getList backBuf)
597 let src0 = Prelude.take n (x:getList backBuf)
624 arr0 = A.fromListN n (Prelude.reverse src0)
598 arr0 = A.fromListN n (Prelude.reverse src0)
625− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
599+ arr1 = Array contents next end
626 src = arr0 <> arr1
600 src = arr0 <> arr1
627− let !(Array cont1 start (Ptr end1)) = src
601+ let !(Array cont1 start end1) = src
628− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
602+ fp1 = Tuple' end1 cont1
629 gobuf SPEC s (List []) fp1 start pst1
603 gobuf SPEC s (List []) fp1 start pst1
630 PR.Continue 0 pst1 ->
604 PR.Continue 0 pst1 ->
631 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
605 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
@@ -633,20 +607,20 @@ parseBreakD
633 assert (n <= Prelude.length (x:getList backBuf)) (return ())
607 assert (n <= Prelude.length (x:getList backBuf)) (return ())
634 let (src0, buf1) = splitAt n (x:getList backBuf)
608 let (src0, buf1) = splitAt n (x:getList backBuf)
635 arr0 = A.fromListN n (Prelude.reverse src0)
609 arr0 = A.fromListN n (Prelude.reverse src0)
636− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
610+ arr1 = Array contents next end
637 src = arr0 <> arr1
611 src = arr0 <> arr1
638− let !(Array cont1 start (Ptr end1)) = src
612+ let !(Array cont1 start end1) = src
639− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
613+ fp1 = Tuple' end1 cont1
640 gobuf SPEC s (List buf1) fp1 start pst1
614 gobuf SPEC s (List buf1) fp1 start pst1
641 PR.Done 0 b -> do
615 PR.Done 0 b -> do
642− let arr = Array (fptrToArrayContents contents) next (Ptr end)
616+ let arr = Array contents next end
643 return (b, D.cons arr (D.Stream step s))
617 return (b, D.cons arr (D.Stream step s))
644 PR.Done n b -> do
618 PR.Done n b -> do
645 assert (n <= Prelude.length (x:getList backBuf)) (return ())
619 assert (n <= Prelude.length (x:getList backBuf)) (return ())
646 let src0 = Prelude.take n (x:getList backBuf)
620 let src0 = Prelude.take n (x:getList backBuf)
647 -- XXX create the array in reverse instead
621 -- XXX create the array in reverse instead
648 arr0 = A.fromListN n (Prelude.reverse src0)
622 arr0 = A.fromListN n (Prelude.reverse src0)
649− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
623+ arr1 = Array contents next end
650 -- XXX Use StreamK to avoid adding arbitrary layers of
624 -- XXX Use StreamK to avoid adding arbitrary layers of
651 -- constructors every time.
625 -- constructors every time.
652 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
626 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
@@ -677,20 +651,19 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
677 go !pst st backBuf = do
651 go !pst st backBuf = do
678 let stop = (, K.nil) <$> extract pst
652 let stop = (, K.nil) <$> extract pst
679 single a = yieldk a K.nil
653 single a = yieldk a K.nil
680− yieldk (Array contents start (Ptr end)) r =
654+ yieldk (Array contents start end) r =
681− let fp = ForeignPtr end (arrayToFptrContents contents)
655+ let fp = Tuple' end contents
682 in goArray pst backBuf r fp start
656 in goArray pst backBuf r fp start
683 in K.foldStream defState yieldk single stop st
657 in K.foldStream defState yieldk single stop st
684
658
685 -- Use strictness on "cur" to keep it unboxed
659 -- Use strictness on "cur" to keep it unboxed
686− goArray !pst backBuf st fp@(ForeignPtr end _) !cur
660+ goArray !pst backBuf st (Tuple' end _) !cur
687− | cur == Ptr end = do
661+ | cur == end = do
688− liftIO $ touchForeignPtr fp
689 go pst st backBuf
662 go pst st backBuf
690− goArray !pst backBuf st fp@(ForeignPtr end contents) !cur = do
663+ goArray !pst backBuf st fp@(Tuple' end contents) !cur = do
691− x <- liftIO $ peek cur
664+ x <- liftIO $ peekWith contents cur
692 pRes <- pstep pst x
665 pRes <- pstep pst x
693− let next = PTR_NEXT(cur,a)
666+ let next = INDEX_NEXT(cur,a)
694 case pRes of
667 case pRes of
695 PR.Partial 0 s ->
668 PR.Partial 0 s ->
696 goArray s [] st fp next
669 goArray s [] st fp next
@@ -698,10 +671,10 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
698 assert (n <= Prelude.length (x:backBuf)) (return ())
671 assert (n <= Prelude.length (x:backBuf)) (return ())
699 let src0 = Prelude.take n (x:backBuf)
672 let src0 = Prelude.take n (x:backBuf)
700 arr0 = A.fromListN n (Prelude.reverse src0)
673 arr0 = A.fromListN n (Prelude.reverse src0)
701− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
674+ arr1 = Array contents next end
702 src = arr0 <> arr1
675 src = arr0 <> arr1
703− let !(Array cont1 start (Ptr end1)) = src
676+ let !(Array cont1 start end1) = src
704− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
677+ fp1 = Tuple' end1 cont1
705 goArray s [] st fp1 start
678 goArray s [] st fp1 start
706 PR.Continue 0 s ->
679 PR.Continue 0 s ->
707 goArray s (x:backBuf) st fp next
680 goArray s (x:backBuf) st fp next
@@ -709,13 +682,13 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
709 assert (n <= Prelude.length (x:backBuf)) (return ())
682 assert (n <= Prelude.length (x:backBuf)) (return ())
710 let (src0, buf1) = splitAt n (x:backBuf)
683 let (src0, buf1) = splitAt n (x:backBuf)
711 arr0 = A.fromListN n (Prelude.reverse src0)
684 arr0 = A.fromListN n (Prelude.reverse src0)
712− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
685+ arr1 = Array contents next end
713 src = arr0 <> arr1
686 src = arr0 <> arr1
714− let !(Array cont1 start (Ptr end1)) = src
687+ let !(Array cont1 start end1) = src
715− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
688+ fp1 = Tuple' end1 cont1
716 goArray s buf1 st fp1 start
689 goArray s buf1 st fp1 start
717 PR.Done 0 b -> do
690 PR.Done 0 b -> do
718− let arr = Array (fptrToArrayContents contents) next (Ptr end)
691+ let arr = Array contents next end
719 return (b, K.cons arr st)
692 return (b, K.cons arr st)
720 PR.Done n b -> do
693 PR.Done n b -> do
721 assert (n <= Prelude.length (x:backBuf)) (return ())
694 assert (n <= Prelude.length (x:backBuf)) (return ())
@@ -723,7 +696,7 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
723 -- XXX Use fromListRevN once implemented
696 -- XXX Use fromListRevN once implemented
724 -- arr0 = A.fromListRevN n src0
697 -- arr0 = A.fromListRevN n src0
725 arr0 = A.fromListN n (Prelude.reverse src0)
698 arr0 = A.fromListN n (Prelude.reverse src0)
726− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
699+ arr1 = Array contents next end
727 str = K.cons arr0 (K.cons arr1 st)
700 str = K.cons arr0 (K.cons arr1 st)
728 return (b, str)
701 return (b, str)
729 PR.Error err -> throwM $ ParseError err
702 PR.Error err -> throwM $ ParseError err
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+3−2
@@ -69,7 +69,7 @@ where
69
69
70import Control.Concurrent (threadDelay)
70import Control.Concurrent (threadDelay)
71import Control.Monad.IO.Class (MonadIO(..))
71import Control.Monad.IO.Class (MonadIO(..))
72−import Streamly.Internal.Data.Unboxed (Storable)
72+import qualified Foreign.Storable as Storable (Storable)
73import Streamly.Internal.Control.Concurrent (MonadAsync)
73import Streamly.Internal.Control.Concurrent (MonadAsync)
74import Streamly.Internal.Data.Array.Foreign.Type (Array)
74import Streamly.Internal.Data.Array.Foreign.Type (Array)
75import Streamly.Internal.Data.Fold.Type (Fold (..))
75import Streamly.Internal.Data.Fold.Type (Fold (..))
@@ -79,6 +79,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
79import Streamly.Internal.Data.Stream.Serial (SerialT)
79import Streamly.Internal.Data.Stream.Serial (SerialT)
80import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
80import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
81import Streamly.Internal.System.IO (defaultChunkSize)
81import Streamly.Internal.System.IO (defaultChunkSize)
82+import Streamly.Internal.Data.Unboxed (Storable, Unboxed)
82
83
83import qualified Streamly.Internal.Data.Array.Foreign.Type as A
84import qualified Streamly.Internal.Data.Array.Foreign.Type as A
84import qualified Streamly.Internal.Data.Stream.Async as Async
85import qualified Streamly.Internal.Data.Stream.Async as Async
@@ -678,7 +679,7 @@ foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
678-- that we can search files in files for example.
679-- that we can search files in files for example.
679{-# INLINE splitOnSeq #-}
680{-# INLINE splitOnSeq #-}
680splitOnSeq
681splitOnSeq
681− :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
682+ :: (IsStream t, MonadIO m, Storable.Storable a, Unboxed a, Enum a, Eq a)
682 => Array a -> Fold m a b -> t m a -> t m b
683 => Array a -> Fold m a b -> t m a -> t m b
683splitOnSeq patt f m =
684splitOnSeq patt f m =
684 IsStream.fromStreamD $ D.splitOnSeq patt f (IsStream.toStreamD m)
685 IsStream.fromStreamD $ D.splitOnSeq patt f (IsStream.toStreamD m)
src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+3−2
@@ -157,7 +157,7 @@ where
157import Control.Monad.Catch (MonadThrow)
157import Control.Monad.Catch (MonadThrow)
158import Control.Monad.IO.Class (MonadIO(..))
158import Control.Monad.IO.Class (MonadIO(..))
159import Control.Monad.Trans.Class (MonadTrans(..))
159import Control.Monad.Trans.Class (MonadTrans(..))
160−import Streamly.Internal.Data.Unboxed (Storable)
160+import qualified Foreign.Storable as Storable (Storable)
161import Streamly.Internal.Control.Concurrent (MonadAsync)
161import Streamly.Internal.Control.Concurrent (MonadAsync)
162import Streamly.Internal.Data.Parser (Parser (..))
162import Streamly.Internal.Data.Parser (Parser (..))
163import Streamly.Internal.Data.SVar (defState)
163import Streamly.Internal.Data.SVar (defState)
@@ -167,6 +167,7 @@ import Streamly.Internal.Data.Stream.IsStream.Common
167import Streamly.Internal.Data.Stream.IsStream.Type
167import Streamly.Internal.Data.Stream.IsStream.Type
168 (IsStream, toStreamS, fromStreamD, toStreamD)
168 (IsStream, toStreamS, fromStreamD, toStreamD)
169import Streamly.Internal.Data.Stream.Serial (SerialT)
169import Streamly.Internal.Data.Stream.Serial (SerialT)
170+import Streamly.Internal.Data.Unboxed (Unboxed)
170
171
171import qualified Streamly.Internal.Data.Array.Foreign.Type as A
172import qualified Streamly.Internal.Data.Array.Foreign.Type as A
172import qualified Streamly.Internal.Data.Fold as FL
173import qualified Streamly.Internal.Data.Fold as FL
@@ -937,7 +938,7 @@ isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
937-- /Requires 'Storable' constraint/
938-- /Requires 'Storable' constraint/
938--
939--
939{-# INLINE isInfixOf #-}
940{-# INLINE isInfixOf #-}
940−isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a)
941+isInfixOf :: (MonadIO m, Eq a, Enum a, Storable.Storable a, Unboxed a)
941 => SerialT m a -> SerialT m a -> m Bool
942 => SerialT m a -> SerialT m a -> m Bool
942isInfixOf infx stream = do
943isInfixOf infx stream = do
943 arr <- fold A.write infx
944 arr <- fold A.write infx
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+5−4
@@ -157,7 +157,7 @@ import Data.Kind (Type)
157import Data.Map (Map)
157import Data.Map (Map)
158import Data.Maybe (isNothing)
158import Data.Maybe (isNothing)
159import Data.Proxy (Proxy(..))
159import Data.Proxy (Proxy(..))
160−import Streamly.Internal.Data.Unboxed (Storable)
160+import qualified Foreign.Storable as Storable (Storable)
161import Streamly.Internal.Control.Concurrent (MonadAsync)
161import Streamly.Internal.Control.Concurrent (MonadAsync)
162import Streamly.Internal.Data.Fold.Type (Fold (..))
162import Streamly.Internal.Data.Fold.Type (Fold (..))
163import Streamly.Internal.Data.IsMap (IsMap(..))
163import Streamly.Internal.Data.IsMap (IsMap(..))
@@ -178,6 +178,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
178import Streamly.Internal.Data.Time.Units
178import Streamly.Internal.Data.Time.Units
179 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
179 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
180 , toAbsTime)
180 , toAbsTime)
181+import Streamly.Internal.Data.Unboxed (Storable, Unboxed)
181
182
182import qualified Data.Heap as H
183import qualified Data.Heap as H
183import qualified Streamly.Data.Unfold as Unfold
184import qualified Streamly.Data.Unfold as Unfold
@@ -823,7 +824,7 @@ splitOnAny _subseq _f _m =
823-- /Pre-release/
824-- /Pre-release/
824{-# INLINE splitBySeq #-}
825{-# INLINE splitBySeq #-}
825splitBySeq
826splitBySeq
826− :: (IsStream t, MonadAsync m, Storable a, Enum a, Eq a)
827+ :: (IsStream t, MonadAsync m, Storable.Storable a, Unboxed a, Enum a, Eq a)
827 => Array a -> Fold m a b -> t m a -> t m b
828 => Array a -> Fold m a b -> t m a -> t m b
828splitBySeq patt f m =
829splitBySeq patt f m =
829 intersperseM (fold f (A.toStream patt)) $ splitOnSeq patt f m
830 intersperseM (fold f (A.toStream patt)) $ splitOnSeq patt f m
@@ -874,7 +875,7 @@ splitBySeq patt f m =
874-- /Pre-release/
875-- /Pre-release/
875{-# INLINE splitOnSuffixSeq #-}
876{-# INLINE splitOnSuffixSeq #-}
876splitOnSuffixSeq
877splitOnSuffixSeq
877− :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
878+ :: (IsStream t, MonadIO m, Storable.Storable a, Unboxed a, Enum a, Eq a)
878 => Array a -> Fold m a b -> t m a -> t m b
879 => Array a -> Fold m a b -> t m a -> t m b
879splitOnSuffixSeq patt f m =
880splitOnSuffixSeq patt f m =
880 fromStreamD $ D.splitOnSuffixSeq False patt f (toStreamD m)
881 fromStreamD $ D.splitOnSuffixSeq False patt f (toStreamD m)
@@ -926,7 +927,7 @@ wordsOn _subseq _f _m =
926-- /Pre-release/
927-- /Pre-release/
927{-# INLINE splitWithSuffixSeq #-}
928{-# INLINE splitWithSuffixSeq #-}
928splitWithSuffixSeq
929splitWithSuffixSeq
929− :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
930+ :: (IsStream t, MonadIO m, Storable.Storable a, Unboxed a, Enum a, Eq a)
930 => Array a -> Fold m a b -> t m a -> t m b
931 => Array a -> Fold m a b -> t m a -> t m b
931splitWithSuffixSeq patt f m =
932splitWithSuffixSeq patt f m =
932 fromStreamD $ D.splitOnSuffixSeq True patt f (toStreamD m)
933 fromStreamD $ D.splitOnSuffixSeq True patt f (toStreamD m)
src/Streamly/Internal/FileSystem/Event/Darwin.hs+2−2
@@ -14,7 +14,7 @@
14--
14--
15-- @
15-- @
16-- {-\# LANGUAGE MagicHash #-}
16-- {-\# LANGUAGE MagicHash #-}
17−-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromCString\# "path"#]
17+-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromList "path"]
18-- @
18-- @
19--
19--
20-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
20-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
@@ -560,7 +560,7 @@ watchToStream (Watch handle _ _) =
560-- {-\# LANGUAGE MagicHash #-}
560-- {-\# LANGUAGE MagicHash #-}
561-- watchWith
561-- watchWith
562-- ('setIgnoreSelf' 'On' . 'setRootPathEvents' 'On')
562-- ('setIgnoreSelf' 'On' . 'setRootPathEvents' 'On')
563−-- [Array.fromCString\# "path"#]
563+-- [Array.fromList "path"]
564-- @
564-- @
565--
565--
566-- /Pre-release/
566-- /Pre-release/
src/Streamly/Internal/FileSystem/Event/Linux.hs+6−4
@@ -13,7 +13,7 @@
13-- occurring under the watched paths.
13-- occurring under the watched paths.
14--
14--
15-- @
15-- @
16−-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchPaths' [Array.fromCString\# "dir"#]
16+-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchPaths' [Array.fromList "dir"]
17-- @
17-- @
18--
18--
19-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
19-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
@@ -636,10 +636,12 @@ ensureTrailingSlash path =
636 in case mx of
636 in case mx of
637 Nothing -> error "ensureTrailingSlash: Bug: Invalid index"
637 Nothing -> error "ensureTrailingSlash: Bug: Invalid index"
638 Just x ->
638 Just x ->
639− if x /= fromIntegral (ord '/')
639+ if x /= forwardSlashByte
640− then path <> A.fromCString# "/"#
640+ then path <> forwardSlash
641 else path
641 else path
642 else path
642 else path
643+ where forwardSlash = A.fromList [ forwardSlashByte ]
644+ forwardSlashByte = fromIntegral (ord '/')
643
645
644removeTrailingSlash :: Array Word8 -> Array Word8
646removeTrailingSlash :: Array Word8 -> Array Word8
645removeTrailingSlash path =
647removeTrailingSlash path =
@@ -908,7 +910,7 @@ watchToStream cfg wt@(Watch handle _) = do
908-- @
910-- @
909-- watchwith
911-- watchwith
910-- ('setFollowSymLinks' On . 'setUnwatchMoved' Off)
912-- ('setFollowSymLinks' On . 'setUnwatchMoved' Off)
911−-- [Array.fromCString\# "dir"#]
913+-- [Array.fromList "dir"]
912-- @
914-- @
913--
915--
914-- /Pre-release/
916-- /Pre-release/
src/Streamly/Internal/FileSystem/Event/Windows.hs+5−3
@@ -18,7 +18,7 @@
18--
18--
19-- @
19-- @
20-- {-\# LANGUAGE MagicHash #-}
20-- {-\# LANGUAGE MagicHash #-}
21−-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromCString\# "path"#]
21+-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromList "path"]
22-- @
22-- @
23--
23--
24-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
24-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
@@ -92,6 +92,7 @@ where
92
92
93import Control.Monad.IO.Class (MonadIO(liftIO))
93import Control.Monad.IO.Class (MonadIO(liftIO))
94import Data.Bits ((.|.), (.&.), complement)
94import Data.Bits ((.|.), (.&.), complement)
95+import Data.Char (ord)
95import Data.Functor.Identity (runIdentity)
96import Data.Functor.Identity (runIdentity)
96import Data.List.NonEmpty (NonEmpty)
97import Data.List.NonEmpty (NonEmpty)
97import Data.Word (Word8)
98import Data.Word (Word8)
@@ -465,7 +466,7 @@ closePathHandleStream = S.mapM_ (\(h, _, _) -> closeHandle h)
465-- @
466-- @
466-- watchWith
467-- watchWith
467-- ('setAttrsModified' On . 'setLastWriteTimeModified' Off)
468-- ('setAttrsModified' On . 'setLastWriteTimeModified' Off)
468−-- [Array.fromCString\# "dir"#]
469+-- [Array.fromList "dir"]
469-- @
470-- @
470--
471--
471-- /Pre-release/
472-- /Pre-release/
@@ -527,7 +528,8 @@ getRoot Event{..} = (UTF8.toArray . UTF8.pack) eventRootPath
527-- /Pre-release/
528-- /Pre-release/
528--
529--
529getAbsPath :: Event -> Array Word8
530getAbsPath :: Event -> Array Word8
530−getAbsPath ev = getRoot ev <> A.fromCString# "\\"# <> getRelPath ev
531+getAbsPath ev = getRoot ev <> backSlash <> getRelPath ev
532+ where backSlash = A.fromList [ fromIntegral (ord '\\') ]
531
533
532-- XXX need to document the exact semantics of these.
534-- XXX need to document the exact semantics of these.
533--
535--
src/Streamly/Internal/FileSystem/FD.hs+5−7
@@ -118,11 +118,9 @@ where
118
118
119import Control.Monad.IO.Class (MonadIO(..))
119import Control.Monad.IO.Class (MonadIO(..))
120import Data.Word (Word8)
120import Data.Word (Word8)
121−import Foreign.ForeignPtr (withForeignPtr)
122-- import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
121-- import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
123−import Foreign.Ptr (plusPtr, castPtr)
122+import Foreign.Ptr (castPtr)
124import Streamly.Internal.Data.Unboxed (Storable)
123import Streamly.Internal.Data.Unboxed (Storable)
125−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
126-- import System.IO (Handle, hGetBufSome, hPutBuf)
124-- import System.IO (Handle, hGetBufSome, hPutBuf)
127import System.IO (IOMode)
125import System.IO (IOMode)
128import Prelude hiding (read)
126import Prelude hiding (read)
@@ -132,7 +130,6 @@ import qualified GHC.IO.Device as RawIO
132
130
133import Streamly.Internal.Data.Array.Foreign.Type
131import Streamly.Internal.Data.Array.Foreign.Type
134 (Array(..), byteLength, unsafeFreeze, asPtrUnsafe)
132 (Array(..), byteLength, unsafeFreeze, asPtrUnsafe)
135−import Streamly.Internal.Data.Array.Foreign.Mut.Type (fromForeignPtrUnsafe)
136import Streamly.Internal.System.IO (defaultChunkSize)
133import Streamly.Internal.System.IO (defaultChunkSize)
137import Streamly.Internal.Data.Stream.Serial (SerialT)
134import Streamly.Internal.Data.Stream.Serial (SerialT)
138import Streamly.Internal.Data.Stream.IsStream.Type
135import Streamly.Internal.Data.Stream.IsStream.Type
@@ -149,6 +146,7 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
149-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
146-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
150
147
151import qualified Streamly.Data.Array.Foreign as A
148import qualified Streamly.Data.Array.Foreign as A
149+import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
152import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
150import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
153import qualified Streamly.Prelude as S
151import qualified Streamly.Prelude as S
154import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
152import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
@@ -216,9 +214,9 @@ openFile path mode = Handle . fst <$> FD.openFile path mode True
216{-# INLINABLE readArrayUpto #-}
214{-# INLINABLE readArrayUpto #-}
217readArrayUpto :: Int -> Handle -> IO (Array Word8)
215readArrayUpto :: Int -> Handle -> IO (Array Word8)
218readArrayUpto size (Handle fd) = do
216readArrayUpto size (Handle fd) = do
219− ptr <- mallocPlainForeignPtrBytes size
217+ arr <- MArray.newPinnedArrayBytes size
220 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
218 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
221− withForeignPtr ptr $ \p -> do
219+ MArray.asPtrUnsafe arr $ \p -> do
222 -- n <- hGetBufSome h p size
220 -- n <- hGetBufSome h p size
223#if MIN_VERSION_base(4,15,0)
221#if MIN_VERSION_base(4,15,0)
224 n <- RawIO.read fd p 0 size
222 n <- RawIO.read fd p 0 size
@@ -229,7 +227,7 @@ readArrayUpto size (Handle fd) = do
229 -- Use unsafeFreezeWithShrink
227 -- Use unsafeFreezeWithShrink
230 return
228 return
231 $ unsafeFreeze
229 $ unsafeFreeze
232− $ fromForeignPtrUnsafe ptr (p `plusPtr` n) (p `plusPtr` size)
230+ $ arr { MArray.aEnd = n, MArray.aBound = size }
233
231
234-------------------------------------------------------------------------------
232-------------------------------------------------------------------------------
235-- Array IO (output)
233-- Array IO (output)
src/Streamly/Internal/FileSystem/Handle.hs+9−11
@@ -114,20 +114,15 @@ import Control.Monad.IO.Class (MonadIO(..))
114import Data.Function ((&))
114import Data.Function ((&))
115import Data.Maybe (isNothing, fromJust)
115import Data.Maybe (isNothing, fromJust)
116import Data.Word (Word8)
116import Data.Word (Word8)
117−import Foreign.ForeignPtr (withForeignPtr)
118−import Foreign.Ptr (minusPtr, plusPtr)
119import Streamly.Internal.Data.Unboxed (Storable)
117import Streamly.Internal.Data.Unboxed (Storable)
120−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
121import System.IO (Handle, SeekMode(..), hGetBufSome, hPutBuf, hSeek)
118import System.IO (Handle, SeekMode(..), hGetBufSome, hPutBuf, hSeek)
122import Prelude hiding (read)
119import Prelude hiding (read)
123
120
124−import Streamly.Internal.Data.Array.Foreign.Mut.Type (touch)
125import Streamly.Internal.Data.Fold (Fold)
121import Streamly.Internal.Data.Fold (Fold)
126import Streamly.Internal.Data.Refold.Type (Refold(..))
122import Streamly.Internal.Data.Refold.Type (Refold(..))
127import Streamly.Internal.Data.Unfold.Type (Unfold(..))
123import Streamly.Internal.Data.Unfold.Type (Unfold(..))
128import Streamly.Internal.Data.Array.Foreign.Type
124import Streamly.Internal.Data.Array.Foreign.Type
129 (Array(..), writeNUnsafe, unsafeFreezeWithShrink, byteLength)
125 (Array(..), writeNUnsafe, unsafeFreezeWithShrink, byteLength)
130−import Streamly.Internal.Data.Array.Foreign.Mut.Type (fromForeignPtrUnsafe)
131import Streamly.Internal.Data.Stream.Serial (SerialT)
126import Streamly.Internal.Data.Stream.Serial (SerialT)
132import Streamly.Internal.Data.Stream.IsStream.Type
127import Streamly.Internal.Data.Stream.IsStream.Type
133 (IsStream, mkStream, fromStreamD)
128 (IsStream, mkStream, fromStreamD)
@@ -136,6 +131,8 @@ import Streamly.Internal.Data.Array.Stream.Foreign (lpackArraysChunksOf)
136import Streamly.Internal.System.IO (defaultChunkSize)
131import Streamly.Internal.System.IO (defaultChunkSize)
137
132
138import qualified Streamly.Internal.Data.Array.Foreign as A
133import qualified Streamly.Internal.Data.Array.Foreign as A
134+import qualified Streamly.Internal.Data.Array.Foreign.Type as A
135+import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
139import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
136import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
140import qualified Streamly.Internal.Data.Refold.Type as Refold
137import qualified Streamly.Internal.Data.Refold.Type as Refold
141import qualified Streamly.Internal.Data.Fold as FL
138import qualified Streamly.Internal.Data.Fold as FL
@@ -183,14 +180,14 @@ import qualified Streamly.Internal.Data.Unfold as UF
183{-# INLINABLE getChunk #-}
180{-# INLINABLE getChunk #-}
184getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
181getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
185getChunk size h = liftIO $ do
182getChunk size h = liftIO $ do
186− ptr <- mallocPlainForeignPtrBytes size
183+ arr <- MArray.newPinnedArrayBytes size
187 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
184 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
188− withForeignPtr ptr $ \p -> do
185+ MArray.asPtrUnsafe arr $ \p -> do
189 n <- hGetBufSome h p size
186 n <- hGetBufSome h p size
190 -- XXX shrink only if the diff is significant
187 -- XXX shrink only if the diff is significant
191 return $
188 return $
192 unsafeFreezeWithShrink $
189 unsafeFreezeWithShrink $
193− fromForeignPtrUnsafe ptr (p `plusPtr` n) (p `plusPtr` size)
190+ arr { MArray.aEnd = n, MArray.aBound = size }
194
191
195-- This could be useful in implementing the "reverse" read APIs or if you want
192-- This could be useful in implementing the "reverse" read APIs or if you want
196-- to read arrays of exact size instead of compacting them later. Compacting
193-- to read arrays of exact size instead of compacting them later. Compacting
@@ -404,12 +401,13 @@ getBytes = AS.concat . getChunks
404{-# INLINABLE putChunk #-}
401{-# INLINABLE putChunk #-}
405putChunk :: MonadIO m => Handle -> Array a -> m ()
402putChunk :: MonadIO m => Handle -> Array a -> m ()
406putChunk _ arr | byteLength arr == 0 = return ()
403putChunk _ arr | byteLength arr == 0 = return ()
407−putChunk h Array{..} =
404+putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
408− liftIO $ hPutBuf h arrStart aLen >> touch arrContents
405+ liftIO $ hPutBuf h ptr aLen
409
406
410 where
407 where
411
408
412− aLen = aEnd `minusPtr` arrStart
409+ -- XXX We should have the length passed by asPtrUnsafe itself.
410+ aLen = A.byteLength arr
413
411
414-------------------------------------------------------------------------------
412-------------------------------------------------------------------------------
415-- Stream of Arrays IO
413-- Stream of Arrays IO
src/Streamly/Internal/Network/Socket.hs+7−11
@@ -75,9 +75,8 @@ import Control.Monad.IO.Class (MonadIO(..))
75import Control.Monad (forM_, when)
75import Control.Monad (forM_, when)
76import Data.Maybe (isNothing, fromJust)
76import Data.Maybe (isNothing, fromJust)
77import Data.Word (Word8)
77import Data.Word (Word8)
78−import Foreign.Ptr (minusPtr, plusPtr, Ptr, castPtr)
78+import Foreign.Ptr (plusPtr, Ptr, castPtr)
79import Streamly.Internal.Data.Unboxed (Storable)
79import Streamly.Internal.Data.Unboxed (Storable)
80−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
81import Network.Socket
80import Network.Socket
82 (Socket, SocketOption(..), Family(..), SockAddr(..),
81 (Socket, SocketOption(..), Family(..), SockAddr(..),
83 ProtocolNumber, withSocketsDo, SocketType(..), socket, bind,
82 ProtocolNumber, withSocketsDo, SocketType(..), socket, bind,
@@ -91,10 +90,7 @@ import Prelude hiding (read)
91
90
92import qualified Network.Socket as Net
91import qualified Network.Socket as Net
93
92
94−import Streamly.Internal.BaseCompat
95import Streamly.Internal.Control.Concurrent (MonadAsync)
93import Streamly.Internal.Control.Concurrent (MonadAsync)
96−import Streamly.Internal.Data.Array.Foreign.Mut.Type
97− (fromForeignPtrUnsafe, touch)
98import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
94import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
99import Streamly.Internal.Data.Array.Stream.Foreign (lpackArraysChunksOf)
95import Streamly.Internal.Data.Array.Stream.Foreign (lpackArraysChunksOf)
100import Streamly.Internal.Data.Fold (Fold)
96import Streamly.Internal.Data.Fold (Fold)
@@ -107,6 +103,7 @@ import Streamly.Internal.System.IO (defaultChunkSize)
107
103
108import qualified Streamly.Internal.Data.Array.Foreign as A
104import qualified Streamly.Internal.Data.Array.Foreign as A
109import qualified Streamly.Internal.Data.Array.Foreign.Type as A
105import qualified Streamly.Internal.Data.Array.Foreign.Type as A
106+import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
110import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
107import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
111import qualified Streamly.Internal.Data.Fold as FL
108import qualified Streamly.Internal.Data.Fold as FL
112import qualified Streamly.Internal.Data.Stream.IsStream as S
109import qualified Streamly.Internal.Data.Stream.IsStream as S
@@ -261,12 +258,12 @@ readArrayUptoWith
261 -> h
258 -> h
262 -> IO (Array Word8)
259 -> IO (Array Word8)
263readArrayUptoWith f size h = do
260readArrayUptoWith f size h = do
264− ptr <- mallocPlainForeignPtrBytes size
261+ arr <- MArray.newPinnedArrayBytes size
265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
262 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
266− unsafeWithForeignPtr ptr $ \p -> do
263+ MArray.asPtrUnsafe arr $ \p -> do
267 n <- f h p size
264 n <- f h p size
268 let v = A.unsafeFreeze
265 let v = A.unsafeFreeze
269− $ fromForeignPtrUnsafe ptr (p `plusPtr` n) (p `plusPtr` size)
266+ $ arr { MArray.aEnd = n, MArray.aBound = size }
270
267
271 -- XXX shrink only if the diff is significant
268 -- XXX shrink only if the diff is significant
272 -- A.shrinkToFit v
269 -- A.shrinkToFit v
@@ -312,12 +309,11 @@ writeArrayWith :: Storable a
312 -> Array a
309 -> Array a
313 -> IO ()
310 -> IO ()
314writeArrayWith _ _ arr | A.length arr == 0 = return ()
311writeArrayWith _ _ arr | A.length arr == 0 = return ()
315−writeArrayWith f h Array{..} =
312+writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
316− f h (castPtr arrStart) aLen >> touch arrContents
317
313
318 where
314 where
319
315
320− aLen = aEnd `minusPtr` arrStart
316+ aLen = A.byteLength arr
321
317
322-- | Write an Array to a file handle.
318-- | Write an Array to a file handle.
323--
319--
src/Streamly/Internal/Unicode/Stream.hs+61−49
@@ -81,6 +81,7 @@ where
81
81
82#include "inline.hs"
82#include "inline.hs"
83
83
84+import Control.Monad (void)
84import Control.Monad.Catch (MonadThrow(..), MonadCatch)
85import Control.Monad.Catch (MonadThrow(..), MonadCatch)
85import Control.Monad.IO.Class (MonadIO, liftIO)
86import Control.Monad.IO.Class (MonadIO, liftIO)
86import Data.Bits (shiftR, shiftL, (.|.), (.&.))
87import Data.Bits (shiftR, shiftL, (.|.), (.&.))
@@ -89,6 +90,7 @@ import Data.Char (chr, ord)
89import Data.Char (generalCategory, GeneralCategory(Space))
90import Data.Char (generalCategory, GeneralCategory(Space))
90#endif
91#endif
91import Data.Word (Word8)
92import Data.Word (Word8)
93+import Foreign.Marshal.Alloc (mallocBytes)
92import Foreign.Storable (Storable(..))
94import Foreign.Storable (Storable(..))
93import Fusion.Plugin.Types (Fuse(..))
95import Fusion.Plugin.Types (Fuse(..))
94import GHC.Base (assert, unsafeChr)
96import GHC.Base (assert, unsafeChr)
@@ -96,7 +98,7 @@ import GHC.IO.Encoding.Failure (isSurrogate)
96import GHC.Ptr (Ptr (..), plusPtr)
98import GHC.Ptr (Ptr (..), plusPtr)
97import System.IO.Unsafe (unsafePerformIO)
99import System.IO.Unsafe (unsafePerformIO)
98import Streamly.Internal.Data.Array.Foreign (Array)
100import Streamly.Internal.Data.Array.Foreign (Array)
99−import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayContents, touch)
101+import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayContents)
100import Streamly.Internal.Data.Fold (Fold)
102import Streamly.Internal.Data.Fold (Fold)
101import Streamly.Internal.Data.Stream.IsStream.Type
103import Streamly.Internal.Data.Stream.IsStream.Type
102 (IsStream, fromStreamD, toStreamD, adapt)
104 (IsStream, fromStreamD, toStreamD, adapt)
@@ -104,9 +106,11 @@ import Streamly.Internal.Data.Stream.Serial (SerialT)
104import Streamly.Internal.Data.Stream.StreamD (Stream(..), Step (..))
106import Streamly.Internal.Data.Stream.StreamD (Stream(..), Step (..))
105import Streamly.Internal.Data.SVar (adaptState)
107import Streamly.Internal.Data.SVar (adaptState)
106import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
108import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
109+import Streamly.Internal.Data.Unboxed (peekWith)
107import Streamly.Internal.Data.Unfold.Type (Unfold(..))
110import Streamly.Internal.Data.Unfold.Type (Unfold(..))
108import Streamly.Internal.System.IO (unsafeInlineIO)
111import Streamly.Internal.System.IO (unsafeInlineIO)
109
112
113+import qualified Streamly.Internal.Data.Fold as Fold
110import qualified Streamly.Internal.Data.Unfold as Unfold
114import qualified Streamly.Internal.Data.Unfold as Unfold
111import qualified Streamly.Internal.Data.Parser as Parser
115import qualified Streamly.Internal.Data.Parser as Parser
112import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
116import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
@@ -233,20 +237,41 @@ decodeTable = [
233 12,36,12,12,12,12,12,12,12,12,12,12
237 12,36,12,12,12,12,12,12,12,12,12,12
234 ]
238 ]
235
239
240+{-# INLINE utf8dLength #-}
241+utf8dLength :: Int
242+utf8dLength = length decodeTable
243+
244+-- | We do not want to garbage collect this and free the memory, we want to
245+-- keep this persistent. We don't know how to do that with GHC without having a
246+-- reference in some global structure. So we use a hack, use mallocBytes so
247+-- that the GC has no way to free it.
236{-# NOINLINE utf8d #-}
248{-# NOINLINE utf8d #-}
237−utf8d :: A.Array Word8
249+utf8d :: Ptr Word8
238−utf8d =
239− unsafePerformIO
250+utf8d = unsafePerformIO $ do
240− -- Aligning to cacheline makes a barely noticeable difference
251+ let size = utf8dLength
241− -- XXX currently alignment is not implemented for unmanaged allocation
252+ p <- liftIO $ mallocBytes size
242− $ D.fold (A.writeNAlignedUnmanaged 64 (length decodeTable))
253+ void $ D.fold
254+ (Fold.foldlM' (\b a -> poke b a >> return (b `plusPtr` 1)) (return p))
243− (D.fromList decodeTable)
255+ (D.fromList decodeTable)
256+ return p
244
257
245-- | Return element at the specified index without checking the bounds.
258-- | Return element at the specified index without checking the bounds.
246-- and without touching the foreign ptr.
259-- and without touching the foreign ptr.
247{-# INLINE_NORMAL unsafePeekElemOff #-}
260{-# INLINE_NORMAL unsafePeekElemOff #-}
248unsafePeekElemOff :: forall a. Storable a => Ptr a -> Int -> a
261unsafePeekElemOff :: forall a. Storable a => Ptr a -> Int -> a
262+unsafePeekElemOff p i =
249−unsafePeekElemOff p i = let !x = unsafeInlineIO $ peekElemOff p i in x
263+ let !x = unsafeInlineIO $ peekElemOff p i
264+ in x
265+
266+-- XXX We can use a fromPtr stream to implement it.
267+{-# INLINE showMemory #-}
268+showMemory ::
269+ forall a. (Show a, Storable a) => Ptr a -> Ptr a -> String
270+showMemory cur end
271+ | cur < end =
272+ let cur1 = cur `plusPtr` sizeOf (undefined :: a)
273+ in show (unsafeInlineIO $ peek cur) ++ " " ++ showMemory cur1 end
274+showMemory _ _ = ""
250
275
251-- decode is split into two separate cases to avoid branching instructions.
276-- decode is split into two separate cases to avoid branching instructions.
252-- From the higher level flow we already know which case we are in so we can
277-- From the higher level flow we already know which case we are in so we can
@@ -265,11 +290,9 @@ decode0 table byte =
265
290
266 where
291 where
267
292
268− utf8table =
269− let end = table `plusPtr` 364
293+ utf8tableEnd = table `plusPtr` 364
270− in A.Array undefined table end :: A.Array Word8
271 showByte = "Streamly: decode0: byte: " ++ show byte
294 showByte = "Streamly: decode0: byte: " ++ show byte
272− showTable = " table: " ++ show utf8table
295+ showTable = " table: " ++ showMemory table utf8tableEnd
273
296
274-- When the state is not 0
297-- When the state is not 0
275{-# INLINE decode1 #-}
298{-# INLINE decode1 #-}
@@ -291,14 +314,12 @@ decode1 table state codep byte =
291 (Tuple' state' codep')
314 (Tuple' state' codep')
292 where
315 where
293
316
294− utf8table =
295− let end = table `plusPtr` 364
317+ utf8tableEnd = table `plusPtr` 364
296− in A.Array undefined table end :: A.Array Word8
297 showByte = "Streamly: decode1: byte: " ++ show byte
318 showByte = "Streamly: decode1: byte: " ++ show byte
298 showState st cp =
319 showState st cp =
299 " state: " ++ show st ++
320 " state: " ++ show st ++
300 " codepoint: " ++ show cp ++
321 " codepoint: " ++ show cp ++
301− " table: " ++ show utf8table
322+ " table: " ++ showMemory table utf8tableEnd
302
323
303-------------------------------------------------------------------------------
324-------------------------------------------------------------------------------
304-- Resumable UTF-8 decoding
325-- Resumable UTF-8 decoding
@@ -324,13 +345,11 @@ resumeDecodeUtf8EitherD
324 -> Stream m Word8
345 -> Stream m Word8
325 -> Stream m (Either DecodeError Char)
346 -> Stream m (Either DecodeError Char)
326resumeDecodeUtf8EitherD dst codep (Stream step state) =
347resumeDecodeUtf8EitherD dst codep (Stream step state) =
327− let A.Array _ p _ = utf8d
328− !ptr = p
329− stt =
348+ let stt =
330 if dst == 0
349 if dst == 0
331 then UTF8DecodeInit state
350 then UTF8DecodeInit state
332 else UTF8Decoding state dst codep
351 else UTF8Decoding state dst codep
333− in Stream (step' ptr) stt
352+ in Stream (step' utf8d) stt
334 where
353 where
335 {-# INLINE_LATE step' #-}
354 {-# INLINE_LATE step' #-}
336 step' _ gst (UTF8DecodeInit st) = do
355 step' _ gst (UTF8DecodeInit st) = do
@@ -439,9 +458,7 @@ data UTF8CharDecodeState a
439{-# INLINE parseCharUtf8WithD #-}
458{-# INLINE parseCharUtf8WithD #-}
440parseCharUtf8WithD ::
459parseCharUtf8WithD ::
441 MonadThrow m => CodingFailureMode -> ParserD.Parser m Word8 Char
460 MonadThrow m => CodingFailureMode -> ParserD.Parser m Word8 Char
442−parseCharUtf8WithD cfm =
443− let A.Array _ ptr _ = utf8d
444− in ParserD.Parser (step' ptr) initial extract
461+parseCharUtf8WithD cfm = ParserD.Parser (step' utf8d) initial extract
445
462
446 where
463 where
447
464
@@ -541,8 +558,7 @@ parseCharUtf8With = ParserD.toParserK . parseCharUtf8WithD
541decodeUtf8WithD :: Monad m
558decodeUtf8WithD :: Monad m
542 => CodingFailureMode -> Stream m Word8 -> Stream m Char
559 => CodingFailureMode -> Stream m Word8 -> Stream m Char
543decodeUtf8WithD cfm (Stream step state) =
560decodeUtf8WithD cfm (Stream step state) =
544− let A.Array _ ptr _ = utf8d
545− in Stream (step' ptr) (UTF8DecodeInit state)
561+ Stream (step' utf8d) (UTF8DecodeInit state)
546
562
547 where
563 where
548
564
@@ -679,9 +695,9 @@ decodeUtf8Lax = decodeUtf8
679#endif
695#endif
680data FlattenState s a
696data FlattenState s a
681 = OuterLoop s !(Maybe (DecodeState, CodePoint))
697 = OuterLoop s !(Maybe (DecodeState, CodePoint))
682− | InnerLoopDecodeInit s ArrayContents !(Ptr a) !(Ptr a)
698+ | InnerLoopDecodeInit s (ArrayContents a) !Int !Int
683− | InnerLoopDecodeFirst s ArrayContents !(Ptr a) !(Ptr a) Word8
699+ | InnerLoopDecodeFirst s (ArrayContents a) !Int !Int Word8
684− | InnerLoopDecoding s ArrayContents !(Ptr a) !(Ptr a)
700+ | InnerLoopDecoding s (ArrayContents a) !Int !Int
685 !DecodeState !CodePoint
701 !DecodeState !CodePoint
686 | YAndC !Char (FlattenState s a) -- These constructors can be
702 | YAndC !Char (FlattenState s a) -- These constructors can be
687 -- encoded in the UTF8DecodeState
703 -- encoded in the UTF8DecodeState
@@ -702,8 +718,7 @@ decodeUtf8ArraysWithD ::
702 -> Stream m (A.Array Word8)
718 -> Stream m (A.Array Word8)
703 -> Stream m Char
719 -> Stream m Char
704decodeUtf8ArraysWithD cfm (Stream step state) =
720decodeUtf8ArraysWithD cfm (Stream step state) =
705− let A.Array _ ptr _ = utf8d
706− in Stream (step' ptr) (OuterLoop state Nothing)
721+ Stream (step' utf8d) (OuterLoop state Nothing)
707 where
722 where
708 {-# INLINE transliterateOrError #-}
723 {-# INLINE transliterateOrError #-}
709 transliterateOrError e s =
724 transliterateOrError e s =
@@ -737,12 +752,11 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
737 Skip (InnerLoopDecoding s arrContents arrStart aEnd ds cp)
752 Skip (InnerLoopDecoding s arrContents arrStart aEnd ds cp)
738 Skip s -> Skip (OuterLoop s dst)
753 Skip s -> Skip (OuterLoop s dst)
739 Stop -> Skip inputUnderflow
754 Stop -> Skip inputUnderflow
740− step' _ _ (InnerLoopDecodeInit st startf p end)
755+ step' _ _ (InnerLoopDecodeInit st _ p end)
741 | p == end = do
756 | p == end = do
742− liftIO $ touch startf
743 return $ Skip $ OuterLoop st Nothing
757 return $ Skip $ OuterLoop st Nothing
744− step' _ _ (InnerLoopDecodeInit st startf p end) = do
758+ step' _ _ (InnerLoopDecodeInit st contents p end) = do
745− x <- liftIO $ peek p
759+ x <- liftIO $ peekWith contents p
746 -- Note: It is important to use a ">" instead of a "<=" test here for
760 -- Note: It is important to use a ">" instead of a "<=" test here for
747 -- GHC to generate code layout for default branch prediction for the
761 -- GHC to generate code layout for default branch prediction for the
748 -- common case. This is fragile and might change with the compiler
762 -- common case. This is fragile and might change with the compiler
@@ -752,13 +766,13 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
752 False ->
766 False ->
753 return $ Skip $ YAndC
767 return $ Skip $ YAndC
754 (unsafeChr (fromIntegral x))
768 (unsafeChr (fromIntegral x))
755− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
769+ (InnerLoopDecodeInit st contents (p + 1) end)
756 -- Using a separate state here generates a jump to a separate code
770 -- Using a separate state here generates a jump to a separate code
757 -- block in the core which seems to perform slightly better for the
771 -- block in the core which seems to perform slightly better for the
758 -- non-ascii case.
772 -- non-ascii case.
759− True -> return $ Skip $ InnerLoopDecodeFirst st startf p end x
773+ True -> return $ Skip $ InnerLoopDecodeFirst st contents p end x
760
774
761− step' table _ (InnerLoopDecodeFirst st startf p end x) = do
775+ step' table _ (InnerLoopDecodeFirst st contents p end x) = do
762 let (Tuple' sv cp) = decode0 table x
776 let (Tuple' sv cp) = decode0 table x
763 return $
777 return $
764 case sv of
778 case sv of
@@ -770,15 +784,13 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
770 ++ "decodeUtf8ArraysWith: Invalid UTF8"
784 ++ "decodeUtf8ArraysWith: Invalid UTF8"
771 ++ " codepoint encountered"
785 ++ " codepoint encountered"
772 )
786 )
773− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
787+ (InnerLoopDecodeInit st contents (p + 1) end)
774 0 -> error "unreachable state"
788 0 -> error "unreachable state"
775− _ -> Skip (InnerLoopDecoding st startf (p `plusPtr` 1) end sv cp)
789+ _ -> Skip (InnerLoopDecoding st contents (p + 1) end sv cp)
776− step' _ _ (InnerLoopDecoding st startf p end sv cp)
790+ step' _ _ (InnerLoopDecoding st _ p end sv cp)
777− | p == end = do
778− liftIO $ touch startf
779− return $ Skip $ OuterLoop st (Just (sv, cp))
791+ | p == end = return $ Skip $ OuterLoop st (Just (sv, cp))
780− step' table _ (InnerLoopDecoding st startf p end statePtr codepointPtr) = do
792+ step' table _ (InnerLoopDecoding st contents p end statePtr codepointPtr) = do
781− x <- liftIO $ peek p
793+ x <- liftIO $ peekWith contents p
782 let (Tuple' sv cp) = decode1 table statePtr codepointPtr x
794 let (Tuple' sv cp) = decode1 table statePtr codepointPtr x
783 return $
795 return $
784 case sv of
796 case sv of
@@ -786,7 +798,7 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
786 Skip $
798 Skip $
787 YAndC
799 YAndC
788 (unsafeChr cp)
800 (unsafeChr cp)
789− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
801+ (InnerLoopDecodeInit st contents (p + 1) end)
790 12 ->
802 12 ->
791 Skip $
803 Skip $
792 transliterateOrError
804 transliterateOrError
@@ -795,10 +807,10 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
795 ++ "decodeUtf8ArraysWith: Invalid UTF8"
807 ++ "decodeUtf8ArraysWith: Invalid UTF8"
796 ++ " codepoint encountered"
808 ++ " codepoint encountered"
797 )
809 )
798− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
810+ (InnerLoopDecodeInit st contents (p + 1) end)
799 _ ->
811 _ ->
800 Skip
812 Skip
801− (InnerLoopDecoding st startf (p `plusPtr` 1) end sv cp)
813+ (InnerLoopDecoding st contents (p + 1) end sv cp)
802 step' _ _ (YAndC c s) = return $ Yield c s
814 step' _ _ (YAndC c s) = return $ Yield c s
803 step' _ _ D = return Stop
815 step' _ _ D = return Stop
804
816
streamly.cabal−8
@@ -308,11 +308,6 @@ flag limit-build-mem
308 manual: True
308 manual: True
309 default: False
309 default: False
310
310
311−flag use-storable
312− description: Use Storable for unboxed arrays
313− manual: True
314− default: False
315−
316-------------------------------------------------------------------------------
311-------------------------------------------------------------------------------
317-- Common stanzas
312-- Common stanzas
318-------------------------------------------------------------------------------
313-------------------------------------------------------------------------------
@@ -341,9 +336,6 @@ common compile-options
341 if flag(use-c-malloc)
336 if flag(use-c-malloc)
342 cpp-options: -DUSE_C_MALLOC
337 cpp-options: -DUSE_C_MALLOC
343
338
344− if flag(use-storable)
345− cpp-options: -DUSE_STORABLE
346−
347 ghc-options: -Wall
339 ghc-options: -Wall
348 -Wcompat
340 -Wcompat
349 -Wunrecognised-warning-flags
341 -Wunrecognised-warning-flags
test/Streamly/Test/Data/Array/Foreign.hs+5−27
@@ -13,17 +13,16 @@ module Streamly.Test.Data.Array.Foreign (main) where
13import Data.Char (isLower)
13import Data.Char (isLower)
14import Data.List (sort)
14import Data.List (sort)
15import Data.Word(Word8)
15import Data.Word(Word8)
16+import Foreign.Storable (peek)
17+import Streamly.Internal.Data.Unboxed (Unboxed)
16import Test.QuickCheck (chooseInt, listOf)
18import Test.QuickCheck (chooseInt, listOf)
17−import Streamly.Internal.Data.Unboxed (Storable, alignment, peek, poke)
18import GHC.Ptr (plusPtr)
19import GHC.Ptr (plusPtr)
19−import Foreign.ForeignPtr (withForeignPtr)
20
20
21import qualified Streamly.Internal.Data.Fold as Fold
21import qualified Streamly.Internal.Data.Fold as Fold
22import qualified Streamly.Internal.Data.Array.Foreign as A
22import qualified Streamly.Internal.Data.Array.Foreign as A
23import qualified Streamly.Internal.Data.Array.Foreign.Type as A
23import qualified Streamly.Internal.Data.Array.Foreign.Type as A
24import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
24import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
25import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
25import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
26−import qualified Streamly.Internal.Foreign.Malloc as Malloc
27
26
28type Array = A.Array
27type Array = A.Array
29
28
@@ -185,7 +184,7 @@ testBubbleAsc = testBubbleWith True
185testBubbleDesc :: Property
184testBubbleDesc :: Property
186testBubbleDesc = testBubbleWith False
185testBubbleDesc = testBubbleWith False
187
186
188−testByteLengthWithMA :: forall a. Storable a => a -> IO ()
187+testByteLengthWithMA :: forall a. Unboxed a => a -> IO ()
189testByteLengthWithMA _ = do
188testByteLengthWithMA _ = do
190 arrA <- MA.newArray 100 :: IO (MA.Array a)
189 arrA <- MA.newArray 100 :: IO (MA.Array a)
191 let arrW8 = MA.castUnsafe arrA :: MA.Array Word8
190 let arrW8 = MA.castUnsafe arrA :: MA.Array Word8
@@ -210,28 +209,6 @@ testUnsafeIndxedFromList inp =
210 let arr = A.fromList inp
209 let arr = A.fromList inp
211 in fmap (flip A.unsafeIndex arr) [0 .. (length inp - 1)] `shouldBe` inp
210 in fmap (flip A.unsafeIndex arr) [0 .. (length inp - 1)] `shouldBe` inp
212
211
213−testFromForeignPtrUnsafeMA :: IO ()
214−testFromForeignPtrUnsafeMA = do
215− fp <- Malloc.mallocForeignPtrAlignedBytes numBytes alignmentInt
216− arr <-
217− withForeignPtr fp $ \p -> do
218− writeNext (0 :: Int) p
219− return
220− $ MA.fromForeignPtrUnsafe
221− fp
222− (p `plusPtr` numBytes)
223− (p `plusPtr` numBytes)
224− MA.toList arr `shouldReturn` [0..99]
225−
226− where
227−
228− sizeOfInt = sizeOf (undefined :: Int)
229− alignmentInt = alignment (undefined :: Int)
230− numBytes = sizeOfInt * 100
231− writeNext i _
232− | i >= 100 = return ()
233− writeNext i p = poke p i >> writeNext (i + 1) (p `plusPtr` sizeOfInt)
234−
235testAsPtrUnsafeMA :: IO ()
212testAsPtrUnsafeMA :: IO ()
236testAsPtrUnsafeMA = do
213testAsPtrUnsafeMA = do
237 arr <- MA.fromList ([0 .. 99] :: [Int])
214 arr <- MA.fromList ([0 .. 99] :: [Int])
@@ -241,6 +218,8 @@ testAsPtrUnsafeMA = do
241
218
242 sizeOfInt = sizeOf (undefined :: Int)
219 sizeOfInt = sizeOf (undefined :: Int)
243
220
221+ -- We need to be careful here. We assume Unboxed and Storable are compatible
222+ -- with each other. For Int, they are compatible.
244 getList i _
223 getList i _
245 | i >= 100 = return []
224 | i >= 100 = return []
246 getList i p = do
225 getList i p = do
@@ -306,7 +285,6 @@ main =
306 (testByteLengthWithMA (undefined :: Int))
285 (testByteLengthWithMA (undefined :: Int))
307 it "testByteLengthWithMA Char"
286 it "testByteLengthWithMA Char"
308 (testByteLengthWithMA (undefined :: Char))
287 (testByteLengthWithMA (undefined :: Char))
309− it "testFromForeignPtrUnsafeMA" testFromForeignPtrUnsafeMA
310 it "testAsPtrUnsafeMA" testAsPtrUnsafeMA
288 it "testAsPtrUnsafeMA" testAsPtrUnsafeMA
311 it "reallocMA" reallocMA
289 it "reallocMA" reallocMA
312 describe "breakOn" $ do
290 describe "breakOn" $ do
test/Streamly/Test/Prelude/Serial.hs+3−2
@@ -17,11 +17,12 @@ import Data.Int (Int64)
17import Data.List (sort, group, intercalate)
17import Data.List (sort, group, intercalate)
18import Data.Maybe ( isJust, fromJust )
18import Data.Maybe ( isJust, fromJust )
19import Data.Word (Word8)
19import Data.Word (Word8)
20−import Streamly.Internal.Data.Unboxed (Storable)
21#if __GLASGOW_HASKELL__ < 808
20#if __GLASGOW_HASKELL__ < 808
22import Data.Semigroup ((<>))
21import Data.Semigroup ((<>))
23#endif
22#endif
24import Data.Semigroup (Sum(..), getSum)
23import Data.Semigroup (Sum(..), getSum)
24+import Foreign.Storable (Storable)
25+import Streamly.Internal.Data.Unboxed (Unboxed)
25import Test.Hspec.QuickCheck
26import Test.Hspec.QuickCheck
26import Test.QuickCheck
27import Test.QuickCheck
27 ( Gen
28 ( Gen
@@ -112,7 +113,7 @@ splitOnSuffixSeq = do
112 $ IS.splitOnSuffixSeq (A.fromList pat) FL.toList (S.fromList xs)
113 $ IS.splitOnSuffixSeq (A.fromList pat) FL.toList (S.fromList xs)
113
114
114splitterProperties ::
115splitterProperties ::
115− forall a. (Arbitrary a, Eq a, Show a, Storable a, Enum a)
116+ forall a. (Arbitrary a, Eq a, Show a, Storable a, Unboxed a, Enum a)
116 => a
117 => a
117 -> String
118 -> String
118 -> Spec
119 -> Spec