on master help sign in
Improve array combinators to work with unpinned arrays
commit3811e683ce3bdf8675673d1ebd14620a044eb97f authorAdithya Kumar <adithya@composewell.com> dateTue 26 Jul 2022, 18:22 (4 years ago) parenteb586c0
34 files changed+1037−1078
.github/workflows/haskell.yml−7 .packcheck.ignore−1 benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+5−5 benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+3−4 benchmark/Streamly/Benchmark/Prelude/Serial/Split.hs+4−4 benchmark/Streamly/Benchmark/Unicode/Stream.hs+2−3 cabal.project.storable−11 core/src/Streamly/Internal/Data/Array/ArrayMacros.h+12 core/src/Streamly/Internal/Data/Array/Foreign.hs+30−31 core/src/Streamly/Internal/Data/Array/Foreign/Mut/Type.hs+408−509 core/src/Streamly/Internal/Data/Array/Foreign/Type.hs+20−170 core/src/Streamly/Internal/Data/Array/Stream/Fold/Foreign.hs+9−11 core/src/Streamly/Internal/Data/Fold.hs+7−6 core/src/Streamly/Internal/Data/Fold/Window.hs+1−1 core/src/Streamly/Internal/Data/Ring/Foreign.hs+54−36 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+3−2 core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+4−3 core/src/Streamly/Internal/Data/Unboxed.hs+289−47 core/streamly-core.cabal−8 src/Streamly/Internal/Data/Array/ArrayMacros.h+12 src/Streamly/Internal/Data/Array/Stream/Foreign.hs+60−87 src/Streamly/Internal/Data/Stream/IsStream/Common.hs+3−2 src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+3−2 src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+5−4 src/Streamly/Internal/FileSystem/Event/Darwin.hs+2−2 src/Streamly/Internal/FileSystem/Event/Linux.hs+6−4 src/Streamly/Internal/FileSystem/Event/Windows.hs+5−3 src/Streamly/Internal/FileSystem/FD.hs+5−7 src/Streamly/Internal/FileSystem/Handle.hs+9−11 src/Streamly/Internal/Network/Socket.hs+7−11 src/Streamly/Internal/Unicode/Stream.hs+61−49 streamly.cabal−8 test/Streamly/Test/Data/Array/Foreign.hs+5−27 test/Streamly/Test/Prelude/Serial.hs+3−2
.github/workflows/haskell.yml−7
@@ -102,13 +102,6 @@ jobs:
102102 stack_build_options: "--flag streamly-benchmarks:-opt"
103103 cabal_version: 3.6
104104 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
112105 - name: 8.10.7-macos
113106 ghc_version: 8.10.7
114107 runner: macos-latest
.packcheck.ignore−1
@@ -23,7 +23,6 @@ cabal.project.report
2323cabal.project.packdiff
2424cabal.project.Werror
2525cabal.project.Werror-nocode
26−cabal.project.storable
2726default.nix
2827examples/README.md
2928hie.yaml
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+5−5
@@ -65,7 +65,7 @@ readLast = S.last . S.unfold FH.read
6565inspect $ hasNoTypeClasses 'readLast
6666inspect $ 'readLast `hasNoType` ''Step -- S.unfold
6767inspect $ '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
6969#endif
7070
7171-- assert that flattenArrays constructors are not present
@@ -77,7 +77,7 @@ readCountBytes = S.length . S.unfold FH.read
7777inspect $ hasNoTypeClasses 'readCountBytes
7878inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
7979inspect $ '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
8181#endif
8282
8383-- | Count the number of lines in a file.
@@ -92,7 +92,7 @@ readCountLines =
9292inspect $ hasNoTypeClasses 'readCountLines
9393inspect $ 'readCountLines `hasNoType` ''Step
9494inspect $ '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
9696#endif
9797
9898-- | Count the number of words in a file.
@@ -116,7 +116,7 @@ readSumBytes = S.sum . S.unfold FH.read
116116inspect $ hasNoTypeClasses 'readSumBytes
117117inspect $ 'readSumBytes `hasNoType` ''Step
118118inspect $ '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
120120#endif
121121
122122-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -235,8 +235,8 @@ inspect $ hasNoTypeClasses 'chunksOf
235235inspect $ 'chunksOf `hasNoType` ''Step
236236inspect $ 'chunksOf `hasNoType` ''FoldMany
237237inspect $ 'chunksOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
238+ -- FH.read/A.read
238239inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
239−inspect $ 'chunksOf `hasNoType` ''MA.ReadUState -- FH.read/A.read
240240#endif
241241
242242{-# 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
4242import qualified Streamly.Internal.Data.Tuple.Strict as Strict
4343import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as MAS
4444import qualified Streamly.Internal.Data.Array.Foreign.Type as AT
45−import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
4645
4746import Test.Inspection
4847#endif
@@ -82,8 +81,8 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.read inh)
8281inspect $ hasNoTypeClasses 'copyStream
8382inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
8483inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
85−inspect $ 'copyStream `hasNoType` ''MA.ReadUState -- FH.read/A.read
8684inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
85+ -- FH.read/A.read
8786inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
8887#endif
8988
@@ -170,8 +169,8 @@ writeReadWith inh devNull = IUF.fold fld unf (defaultChunkSize, inh)
170169inspect $ hasNoTypeClasses 'writeReadWith
171170inspect $ 'writeReadWith `hasNoType` ''Step
172171inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
173−inspect $ 'writeReadWith `hasNoType` ''MA.ReadUState -- FH.read/A.read
174172inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
173+ -- FH.read/A.read
175174#endif
176175
177176-- | Send the file contents ('AT.defaultChunkSize') to /dev/null
@@ -187,8 +186,8 @@ writeRead inh devNull = IUF.fold fld unf inh
187186inspect $ hasNoTypeClasses 'writeRead
188187inspect $ 'writeRead `hasNoType` ''Step
189188inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
190−inspect $ 'writeRead `hasNoType` ''MA.ReadUState -- FH.read/A.read
191189inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
190+ -- FH.read/A.read
192191#endif
193192
194193o_1_space_copy :: BenchEnv -> [Benchmark]
benchmark/Streamly/Benchmark/Prelude/Serial/Split.hs+4−4
@@ -67,7 +67,7 @@ splitOn inh =
6767inspect $ hasNoTypeClasses 'splitOn
6868inspect $ 'splitOn `hasNoType` ''Step
6969inspect $ '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
7171#endif
7272
7373-- | Split suffix on line feed.
@@ -80,7 +80,7 @@ splitOnSuffix inh =
8080inspect $ hasNoTypeClasses 'splitOnSuffix
8181inspect $ 'splitOnSuffix `hasNoType` ''Step
8282inspect $ '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
8484#endif
8585
8686-- | Split suffix with line feed.
@@ -93,7 +93,7 @@ splitWithSuffix inh =
9393inspect $ hasNoTypeClasses 'splitWithSuffix
9494inspect $ 'splitWithSuffix `hasNoType` ''Step
9595inspect $ '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
9797#endif
9898
9999-- | Split on line feed.
@@ -124,7 +124,7 @@ wordsBy inh =
124124inspect $ hasNoTypeClasses 'wordsBy
125125inspect $ 'wordsBy `hasNoType` ''Step
126126inspect $ '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
128128#endif
129129
130130-- | 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(..))
4040import qualified Streamly.Internal.Data.Fold.Type as Fold
4141import qualified Streamly.Internal.Data.Tuple.Strict as Strict
4242import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
43−import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
4443
4544import Test.Inspection
4645#endif
@@ -208,10 +207,10 @@ copyStreamLatin1' inh outh =
208207inspect $ hasNoTypeClasses 'copyStreamLatin1'
209208inspect $ 'copyStreamLatin1' `hasNoType` ''Step
210209inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
211−inspect $ 'copyStreamLatin1' `hasNoType` ''MArray.ReadUState -- Handle.read/Array.read
212210
213211inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
214212inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
213+ -- Handle.read/Array.read
215214inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
216215#endif
217216
@@ -228,11 +227,11 @@ copyStreamLatin1 inh outh =
228227inspect $ hasNoTypeClasses 'copyStreamLatin1
229228inspect $ 'copyStreamLatin1 `hasNoType` ''Step
230229inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
231−inspect $ 'copyStreamLatin1 `hasNoType` ''MArray.ReadUState -- Handle.read/Array.read
232230
233231inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
234232inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
235233inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
234+ -- Handle.read/Array.read
236235inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
237236#endif
238237
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 @@
2525#define PTR_VALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) <= end
2626#define PTR_INVALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) > end
2727
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
3434
3535 -- * Construction
3636
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−
4437 -- Pure List APIs
4538 , A.fromListN
4639 , A.fromList
@@ -122,11 +115,18 @@ import Control.Monad.IO.Class (MonadIO(..))
122115import Data.Functor.Identity (Identity)
123116import Data.Word (Word8)
124117import Foreign.C.String (CString)
125−import Foreign.Ptr (plusPtr, castPtr)
126−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
118+import Foreign.Ptr (castPtr)
119+import qualified Foreign.Storable as Storable (Storable)
120+import Streamly.Internal.Data.Unboxed
121+ ( Storable
122+ , Unboxed
123+ , castContents
124+ , peekWith
125+ , sizeOf
126+ )
127127import Prelude hiding (length, null, last, map, (!!), read, concat)
128128
129−import Streamly.Internal.Data.Array.Foreign.Mut.Type (ReadUState(..), touch)
129+import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayUnsafe(..))
130130import Streamly.Internal.Data.Array.Foreign.Type
131131 (Array(..), length, asPtrUnsafe)
132132import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -213,10 +213,10 @@ unsafeRead = Unfold step inject
213213 where
214214
215215 inject (Array contents start end) =
216− return (ReadUState contents end start)
216+ return (ArrayUnsafe contents end start)
217217
218218 {-# INLINE_LATE step #-}
219− step (ReadUState contents end p) = do
219+ step (ArrayUnsafe contents end p) = do
220220 -- unsafeInlineIO allows us to run this in Identity monad for pure
221221 -- toList/foldr case which makes them much faster due to not
222222 -- accumulating the list and fusing better with the pure consumers.
@@ -224,11 +224,10 @@ unsafeRead = Unfold step inject
224224 -- This should be safe as the array contents are guaranteed to be
225225 -- evaluated/written to before we peek at them.
226226 let !x = unsafeInlineIO $ do
227− r <- peek p
228− touch contents
227+ r <- peekWith contents p
229228 return r
230− let !p1 = PTR_NEXT(p,a)
231− return $ D.Yield x (ReadUState contents end p1)
229+ let !p1 = INDEX_NEXT(p,a)
230+ return $ D.Yield x (ArrayUnsafe contents end p1)
232231
233232-- |
234233--
@@ -247,11 +246,10 @@ null arr = A.byteLength arr == 0
247246getIndexRev :: forall a. Storable a => Int -> Array a -> Maybe a
248247getIndexRev i arr =
249248 unsafeInlineIO
250− $ asPtrUnsafe arr
251− $ \ptr -> do
252− let elemPtr = PTR_RINDEX(aEnd arr,i,a)
253− if i >= 0 && elemPtr >= ptr
254− then Just <$> peek elemPtr
249+ $ do
250+ let elemPtr = RINDEX_OF(aEnd arr, i, a)
251+ if i >= 0 && elemPtr >= arrStart arr
252+ then Just <$> peekWith (arrContents arr) elemPtr
255253 else return Nothing
256254
257255-- |
@@ -273,7 +271,8 @@ last = getIndexRev 0
273271--
274272-- @since 0.8.0
275273{-# INLINE writeLastN #-}
276−writeLastN :: (Storable a, MonadIO m) => Int -> Fold m a (Array a)
274+writeLastN ::
275+ (Unboxed a, Storable.Storable a, MonadIO m) => Int -> Fold m a (Array a)
277276writeLastN n
278277 | n <= 0 = fmap (const mempty) FL.drain
279278 | otherwise = A.unsafeFreeze <$> Fold step initial done
@@ -360,9 +359,9 @@ getSliceUnsafe ::
360359 -> Array a
361360getSliceUnsafe index len (Array contents start e) =
362361 let size = SIZE_OF(a)
363− fp1 = start `plusPtr` (index * size)
364− end = fp1 `plusPtr` (len * size)
365− in assert (end <= e) (Array contents fp1 end)
362+ start1 = start + (index * size)
363+ end1 = start1 + (len * size)
364+ in assert (end1 <= e) (Array contents start1 end1)
366365
367366-- | Split the array into a stream of slices using a predicate. The element
368367-- matching the predicate is dropped.
@@ -412,11 +411,10 @@ getSlicesFromLen from len =
412411getIndex :: forall a. Storable a => Int -> Array a -> Maybe a
413412getIndex i arr =
414413 unsafeInlineIO
415− $ asPtrUnsafe arr
416− $ \ptr -> do
417− let elemPtr = PTR_INDEX(ptr,i,a)
418− if i >= 0 && PTR_VALID(elemPtr,aEnd arr,a)
419− then Just <$> peek elemPtr
414+ $ do
415+ let elemPtr = INDEX_OF(arrStart arr, i, a)
416+ if i >= 0 && INDEX_VALID(elemPtr, aEnd arr, a)
417+ then Just <$> peekWith (arrContents arr) elemPtr
420418 else return Nothing
421419
422420-- | Given a stream of array indices, read the elements on those indices from
@@ -509,7 +507,7 @@ castUnsafe ::
509507#endif
510508 Array a -> Array b
511509castUnsafe (Array contents start end) =
512− Array contents (castPtr start) (castPtr end)
510+ Array (castContents contents) start end
513511
514512-- | Cast an @Array a@ into an @Array Word8@.
515513--
@@ -542,6 +540,7 @@ cast arr =
542540--
543541asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
544542asCStringUnsafe arr act = do
543+ -- XXX Ensure a pinned allocation here.
545544 let arr1 = asBytes arr <> A.fromList [0]
546545 asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
547546
core/src/Streamly/Internal/Data/Array/Foreign/Mut/Type.hs+408−509
@@ -18,8 +18,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
1818 -- $arrayNotes
1919 Array (..)
2020 , ArrayContents
21− , arrayToFptrContents
22− , fptrToArrayContents
2321 , nilArrayContents
2422 , touch
2523
@@ -29,9 +27,10 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
2927
3028 -- *** Uninitialized Arrays
3129 , newArray
30+ , newPinnedArrayBytes
3231 , newArrayAligned
33− , newArrayAlignedUnmanaged
3432 , newArrayWith
33+ , newAlignedArrayContents
3534
3635 -- *** Initialized Arrays
3736 , withNewArrayUnsafe
@@ -43,7 +42,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
4342 , writeNUnsafe
4443 , writeN
4544 , writeNAligned
46− , writeNAlignedUnmanaged
4745
4846 , writeWith
4947 , write
@@ -52,7 +50,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
5250 -- , writeRev
5351
5452 -- ** From containers
55− , fromForeignPtrUnsafe
5653 , fromListN
5754 , fromList
5855 , fromListRevN
@@ -98,7 +95,6 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
9895 -- * Eliminating and Reading
9996
10097 -- ** To streams
101− , ReadUState(..)
10298 , read
10399 , readRevWith
104100 , readRev
@@ -194,6 +190,8 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
194190 , spliceWith
195191 , splice
196192 , spliceExp
193+ , spliceUnsafe
194+ , putSliceUnsafe
197195 -- , putSlice
198196 -- , appendSlice
199197 -- , appendSliceFrom
@@ -214,10 +212,6 @@ where
214212#include "ArrayMacros.h"
215213#include "MachDeps.h"
216214
217−#ifdef USE_C_MALLOC
218−#define USE_FOREIGN_PTR
219−#endif
220−
221215import Control.Exception (assert)
222216import Control.DeepSeq (NFData(..), NFData1(..))
223217import Control.Monad (when, void)
@@ -228,27 +222,27 @@ import Data.Semigroup (Semigroup(..))
228222#endif
229223import Data.Word (Word8)
230224import 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)
236227import Streamly.Internal.Data.Unboxed
237− ( Storable, alignment, peek, poke, sizeOf
228+ ( ArrayContents(..)
229+ , Storable
230+ , alignment
231+ , castContents
232+ , getMutableByteArray#
233+ , peekWith
234+ , pokeWith
235+ , sizeOf
236+ , touch
238237 )
239238import GHC.Base
240− ( touch#, IO(..), byteArrayContents#
239+ ( IO(..), byteArrayContents#
241240 , Int(..), newAlignedPinnedByteArray#
241+ , copyMutableByteArray#, compareByteArrays#
242242 )
243−#ifndef USE_FOREIGN_PTR
244−import GHC.Base (RealWorld, MutableByteArray#)
245−#endif
246243import GHC.Base (noinline)
247244import GHC.Exts (unsafeCoerce#)
248−import GHC.ForeignPtr (ForeignPtr(..), ForeignPtrContents(..))
249−#ifdef USE_C_MALLOC
250−import GHC.ForeignPtr (mallocForeignPtrAlignedBytes)
251−#endif
245+
252246import GHC.Ptr (Ptr(..))
253247
254248import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -267,9 +261,6 @@ import qualified Streamly.Internal.Data.Producer as Producer
267261import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
268262import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
269263import qualified Streamly.Internal.Data.Stream.Type as Stream
270−#ifdef USE_FOREIGN_PTR
271−import qualified Streamly.Internal.Foreign.Malloc as Malloc
272−#endif
273264import qualified Prelude
274265
275266import Prelude hiding
@@ -315,37 +306,6 @@ memcmp p1 p2 len = do
315306 r <- c_memcmp p1 p2 (fromIntegral len)
316307 return $ r == 0
317308
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−
349309-------------------------------------------------------------------------------
350310-- Array Data Type
351311-------------------------------------------------------------------------------
@@ -380,11 +340,15 @@ data Array a =
380340#ifdef DEVBUILD
381341 Storable a =>
382342#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.
383345 Array
384− { arrContents :: UNPACKIF !ArrayContents
385− , arrStart :: {-# UNPACK #-} !(Ptr a) -- ^ first address
386− , aEnd :: {-# UNPACK #-} !(Ptr a) -- ^ first unused address
387− , aBound :: {-# UNPACK #-} !(Ptr a) -- ^ first address beyond allocated memory
346+ { arrContents :: {-# UNPACK #-} !(ArrayContents a)
347+ , arrStart :: {-# UNPACK #-} !Int -- ^ index into arrContents
348+ , aEnd :: {-# UNPACK #-} !Int -- ^ index into arrContents
349+ -- Represents the first invalid index of
350+ -- the array.
351+ , aBound :: {-# UNPACK #-} !Int -- ^ first invalid index of arrContents.
388352 }
389353
390354-------------------------------------------------------------------------------
@@ -413,101 +377,37 @@ data Array a =
413377-- /Pre-release/
414378{-# INLINE newArrayWith #-}
415379newArrayWith :: 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)
417381newArrayWith alloc alignSize count = do
418382 let size = max (count * SIZE_OF(a)) 0
419− (contents, p) <- alloc size alignSize
383+ contents <- alloc size alignSize
420384 return $ Array
421385 { arrContents = contents
422− , arrStart = p
423− , aEnd = p
424− , aBound = p `plusPtr` size
386+ , arrStart = 0
387+ , aEnd = 0
388+ , aBound = size
425389 }
426390
427−newAlignedArrayContents :: Int -> Int -> IO (ArrayContents, Ptr a)
428−#ifdef USE_C_MALLOC
429−newAlignedArrayContents size align = do
430− (ForeignPtr addr contents) <- mallocForeignPtrAlignedBytes size align
431− return (ArrayContents contents, Ptr addr)
432−#else
433−newAlignedArrayContents size _align | size < 0 =
391+newAlignedArrayContents :: Int -> Int -> IO (ArrayContents a)
392+newAlignedArrayContents nbytes _align | nbytes < 0 =
434393 errorWithoutStackTrace "newAlignedArrayContents: size must be >= 0"
435−newAlignedArrayContents (I# size) (I# align) = IO $ \s ->
436− case newAlignedPinnedByteArray# size align s of
394+newAlignedArrayContents (I# nbytes) (I# align) = IO $ \s ->
395+ case newAlignedPinnedByteArray# nbytes align s of
437396 (# 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#
443−#endif
444− in (# s', (c, p) #)
445−#endif
397+ let c = ArrayContents mbarr#
398+ in (# s', c #)
446399
447400{-# NOINLINE nilArrayContents #-}
448−nilArrayContents :: ArrayContents
401+nilArrayContents :: ArrayContents a
449402nilArrayContents =
450− fst $ unsafePerformIO $ newAlignedArrayContents 0 0
403+ unsafePerformIO $ newAlignedArrayContents 0 0
451404
452405nil ::
453406#ifdef DEVBUILD
454407 Storable a =>
455408#endif
456409 Array a
457−nil = Array nilArrayContents nullPtr nullPtr nullPtr
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
410+nil = Array nilArrayContents 0 0 0
511411
512412-- | Like 'newArrayWith' but using an allocator that aligns the memory to the
513413-- alignment dictated by the 'Storable' instance of the type.
@@ -528,6 +428,23 @@ newArrayAligned = newArrayWith (\s a -> liftIO $ newAlignedArrayContents s a)
528428newArray :: forall m a. (MonadIO m, Storable a) => Int -> m (Array a)
529429newArray = newArrayAligned (alignment (undefined :: a))
530430
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+
531448-- | Allocate an Array of the given size and run an IO action passing the array
532449-- start pointer.
533450--
@@ -551,25 +468,15 @@ withNewArrayUnsafe count f = do
551468{-# INLINE putIndexUnsafe #-}
552469putIndexUnsafe :: forall m a. (MonadIO m, Storable a)
553470 => Int -> a -> Array a -> m ()
554−putIndexUnsafe i x arr@(Array{..}) =
555− asPtrUnsafe arr $ \ptr -> do
556− let elemPtr = PTR_INDEX(ptr,i,a)
557− assert (i >= 0 && PTR_VALID(elemPtr,aEnd,a)) (return ())
558− liftIO $ poke elemPtr x
471+putIndexUnsafe i x (Array {..}) = do
472+ let index = INDEX_OF(arrStart, i, a)
473+ assert (i >= 0 && INDEX_VALID(index, aEnd, a)) (return ())
474+ liftIO $ pokeWith arrContents index x
559475
560476invalidIndex :: String -> Int -> a
561477invalidIndex label i =
562478 error $ label ++ ": invalid array index " ++ show i
563479
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−
573480-- | /O(1)/ Write the given element at the given index in the array.
574481-- Performs in-place mutation of the array.
575482--
@@ -579,10 +486,12 @@ putIndexPtr start end i x = do
579486--
580487-- /Pre-release/
581488{-# INLINE putIndex #-}
582−putIndex :: (MonadIO m, Storable a) => Int -> a -> Array a -> m ()
583−putIndex i x arr =
584− asPtrUnsafe arr
585− $ \p -> putIndexPtr p (aEnd arr) i x
489+putIndex :: forall m a. (MonadIO m, Storable a) => Int -> a -> Array a -> m ()
490+putIndex i x Array{..} = do
491+ let index = INDEX_OF(arrStart,i,a)
492+ if i >= 0 && INDEX_VALID(index,aEnd,a)
493+ then liftIO $ pokeWith arrContents index x
494+ else invalidIndex "putIndex" i
586495
587496-- | Write an input stream of (index, value) pairs to an array. Throws an
588497-- error if any index is out of bounds.
@@ -591,51 +500,40 @@ putIndex i x arr =
591500{-# INLINE putIndices #-}
592501putIndices :: forall m a. (MonadIO m, Storable a)
593502 => Array a -> Fold m (Int, a) ()
594−putIndices Array{..} = FL.rmapM extract $ FL.foldlM' step initial
503+putIndices arr = FL.foldlM' step (return ())
595504
596505 where
597506
598− initial = return ()
599−
600− step () (i, x) = liftIO (putIndexPtr arrStart aEnd i x)
601−
602− extract () = liftIO $ touch arrContents
507+ step () (i, x) = liftIO (putIndex i x arr)
603508
604509-- | Modify a given index of an array using a modifier function.
605510--
606511-- /Pre-release/
607512modifyIndexUnsafe :: forall m a b. (MonadIO m, Storable a) =>
608513 Int -> (a -> (a, b)) -> Array a -> m b
609−modifyIndexUnsafe i f arr@(Array{..}) = do
610− liftIO $ asPtrUnsafe arr $ \ptr -> do
611− let elemPtr = PTR_INDEX(ptr,i,a)
612− assert (i >= 0 && PTR_NEXT(elemPtr,a) <= aEnd) (return ())
613− r <- peek elemPtr
514+modifyIndexUnsafe i f (Array{..}) = liftIO $ do
515+ let index = INDEX_OF(arrStart,i,a)
516+ assert (i >= 0 && INDEX_NEXT(index,a) <= aEnd) (return ())
517+ r <- peekWith arrContents index
614518 let (x, res) = f r
615− poke elemPtr x
519+ pokeWith arrContents index x
616520 return res
617521
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−
631522-- | Modify a given index of an array using a modifier function.
632523--
633524-- /Pre-release/
634525modifyIndex :: forall m a b. (MonadIO m, Storable a) =>
635526 Int -> (a -> (a, b)) -> Array a -> m b
636−modifyIndex i f arr@(Array{..}) = do
637− liftIO $ asPtrUnsafe arr $ \ptr -> do
638− modifyIndexPtr i f ptr aEnd
527+modifyIndex i f Array{..} = do
528+ let index = INDEX_OF(arrStart,i,a)
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+
639537
640538-- | Modify the array indices generated by the supplied stream.
641539--
@@ -643,41 +541,39 @@ modifyIndex i f arr@(Array{..}) = do
643541{-# INLINE modifyIndices #-}
644542modifyIndices :: forall m a . (MonadIO m, Storable a)
645543 => (Int -> a -> a) -> Array a -> Fold m Int ()
646−modifyIndices f Array{..} = Fold step initial extract
544+modifyIndices f arr = FL.foldlM' step initial
647545
648546 where
649547
650− initial = return $ FL.Partial ()
548+ initial = return ()
651549
652550 step () i =
653551 let f1 x = (f i x, ())
654− in FL.Partial <$> liftIO (modifyIndexPtr i f1 arrStart aEnd)
655−
656− extract () = liftIO $ touch arrContents
552+ in modifyIndex i f1 arr
657553
658554-- | Modify each element of an array using the supplied modifier function.
659555--
660556-- /Pre-release/
661557modify :: forall m a. (MonadIO m, Storable a)
662558 => (a -> a) -> Array a -> m ()
663−modify f arr@Array{..} = liftIO $
664− asPtrUnsafe arr go
559+modify f Array{..} = liftIO $
560+ go arrStart
665561
666562 where
667563
668− go ptr =
669− when (PTR_VALID(ptr,aEnd,a)) $ do
670− r <- peek ptr
671− poke ptr (f r)
672− go (PTR_NEXT(ptr,a))
564+ go i =
565+ when (INDEX_VALID(i,aEnd,a)) $ do
566+ r <- peekWith arrContents i
567+ pokeWith arrContents i (f r)
568+ go (INDEX_NEXT(i,a))
673569
674−{-# INLINE swapPtrs #-}
675−swapPtrs :: Storable a => Ptr a -> Ptr a -> IO ()
676−swapPtrs ptr1 ptr2 = do
677− r1 <- peek ptr1
678− r2 <- peek ptr2
679− poke ptr1 r2
680− poke ptr2 r1
570+{-# INLINE swapArrayByteIndices #-}
571+swapArrayByteIndices :: Storable a => ArrayContents a -> Int -> Int -> IO ()
572+swapArrayByteIndices arrContents i1 i2 = do
573+ r1 <- peekWith arrContents i1
574+ r2 <- peekWith arrContents i2
575+ pokeWith arrContents i1 r2
576+ pokeWith arrContents i2 r1
681577
682578-- | Swap the elements at two indices without validating the indices.
683579--
@@ -687,11 +583,10 @@ swapPtrs ptr1 ptr2 = do
687583{-# INLINE unsafeSwapIndices #-}
688584unsafeSwapIndices :: forall m a. (MonadIO m, Storable a)
689585 => Int -> Int -> Array a -> m ()
690−unsafeSwapIndices i1 i2 arr = liftIO $ do
691− asPtrUnsafe arr $ \ptr -> do
692− let ptr1 = PTR_INDEX(ptr,i1,a)
693− ptr2 = PTR_INDEX(ptr,i2,a)
694− swapPtrs ptr1 (ptr2 :: Ptr a)
586+unsafeSwapIndices i1 i2 Array{..} = liftIO $ do
587+ let t1 = INDEX_OF(arrStart,i1,a)
588+ t2 = INDEX_OF(arrStart,i2,a)
589+ swapArrayByteIndices arrContents t1 t2
695590
696591-- | Swap the elements at two indices.
697592--
@@ -699,13 +594,13 @@ unsafeSwapIndices i1 i2 arr = liftIO $ do
699594swapIndices :: forall m a. (MonadIO m, Storable a)
700595 => Int -> Int -> Array a -> m ()
701596swapIndices i1 i2 Array{..} = liftIO $ do
702− let ptr1 = PTR_INDEX(arrStart,i1,a)
703− ptr2 = PTR_INDEX(arrStart,i2,a)
704− when (i1 < 0 || PTR_INVALID(ptr1,aEnd,a))
597+ let t1 = INDEX_OF(arrStart,i1,a)
598+ t2 = INDEX_OF(arrStart,i2,a)
599+ when (i1 < 0 || INDEX_INVALID(t1,aEnd,a))
705600 $ invalidIndex "swapIndices" i1
706− when (i2 < 0 || PTR_INVALID(ptr2,aEnd,a))
601+ when (i2 < 0 || INDEX_INVALID(t2,aEnd,a))
707602 $ invalidIndex "swapIndices" i2
708− swapPtrs ptr1 (ptr2 :: Ptr a)
603+ swapArrayByteIndices arrContents t1 t2
709604
710605-------------------------------------------------------------------------------
711606-- Rounding
@@ -797,31 +692,38 @@ roundDownTo elemSize size = size - (size `mod` elemSize)
797692-- XXX See if resizing can be implemented by reading the old array as a stream
798693-- and then using writeN to the new array.
799694--
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+--
800699{-# NOINLINE reallocAligned #-}
801700reallocAligned :: Int -> Int -> Int -> Array a -> IO (Array a)
802−reallocAligned elemSize alignSize newCapacity Array{..} = do
803− assert (aEnd <= aBound) (return ())
701+reallocAligned elemSize alignSize newCapacityInBytes Array{..} = do
702+ assertM (aEnd <= aBound)
804703
805704 -- Allocate new array
806− let newCapMax = roundUpLargeArray newCapacity
807− (contents, pNew) <- newAlignedArrayContents newCapMax alignSize
705+ let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
706+ contents <- newAlignedArrayContents newCapMaxInBytes alignSize
707+ let !(ArrayContents mbarrFrom#) = arrContents
708+ !(ArrayContents mbarrTo#) = contents
808709
809710 -- Copy old data
810711 let oldStart = arrStart
811− oldSize = aEnd `minusPtr` oldStart
812− newCap = roundDownTo elemSize newCapMax
813− newLen = min oldSize newCap
814− assert (oldSize `mod` elemSize == 0) (return ())
815− assert (newLen >= 0) (return ())
816− assert (newLen `mod` elemSize == 0) (return ())
817− memcpy (castPtr pNew) (castPtr oldStart) newLen
818− touch arrContents
712+ !(I# oldStartInBytes#) = oldStart
713+ oldSizeInBytes = aEnd - oldStart
714+ newCapInBytes = roundDownTo elemSize newCapMaxInBytes
715+ !newLenInBytes@(I# newLenInBytes#) = min oldSizeInBytes newCapInBytes
716+ assert (oldSizeInBytes `mod` elemSize == 0) (return ())
717+ assert (newLenInBytes >= 0) (return ())
718+ assert (newLenInBytes `mod` elemSize == 0) (return ())
719+ IO $ \s# -> (# copyMutableByteArray# mbarrFrom# oldStartInBytes#
720+ mbarrTo# 0# newLenInBytes# s#, () #)
819721
820722 return $ Array
821− { arrStart = pNew
723+ { arrStart = 0
822724 , arrContents = contents
823− , aEnd = pNew `plusPtr` newLen
824− , aBound = pNew `plusPtr` newCap
725+ , aEnd = newLenInBytes
726+ , aBound = newCapInBytes
825727 }
826728
827729-- | @realloc newCapacity array@ reallocates the array to the specified
@@ -834,10 +736,10 @@ reallocAligned elemSize alignSize newCapacity Array{..} = do
834736--
835737{-# INLINABLE realloc #-}
836738realloc :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m (Array a)
837−realloc n arr =
838− liftIO $ reallocAligned (SIZE_OF(a)) (alignment (undefined :: a)) n arr
739+realloc bytes arr =
740+ liftIO $ reallocAligned (SIZE_OF(a)) (alignment (undefined :: a)) bytes arr
839741
840−-- | @reallocWith label capSizer minIncrement array@. The label is used
742+-- | @reallocWith label capSizer minIncrBytes array@. The label is used
841743-- in error messages and the capSizer is used to determine the capacity of the
842744-- new array in bytes given the current byte length of the array.
843745reallocWith :: forall m a. (MonadIO m , Storable a) =>
@@ -846,22 +748,24 @@ reallocWith :: forall m a. (MonadIO m , Storable a) =>
846748 -> Int
847749 -> Array a
848750 -> m (Array a)
849−reallocWith label capSizer minIncr arr = do
850− let oldSize = aEnd arr `minusPtr` arrStart arr
851− newCap = capSizer oldSize
852− newSize = oldSize + minIncr
853− safeCap = max newCap newSize
854− assert (newCap >= newSize || error (badSize newSize)) (return ())
855− realloc safeCap arr
751+reallocWith label capSizer minIncrBytes arr = do
752+ let oldSizeBytes = aEnd arr - arrStart arr
753+ newCapBytes = capSizer oldSizeBytes
754+ newSizeBytes = oldSizeBytes + minIncrBytes
755+ safeCapBytes = max newCapBytes newSizeBytes
756+ assertM (safeCapBytes >= newSizeBytes || error (badSize newSizeBytes))
757+
758+ realloc safeCapBytes arr
856759
857760 where
858761
859− badSize newSize = concat
860− [ label
861− , ": new array size is less than required size "
862− , show newSize
863− , ". Please check the sizing function passed."
864− ]
762+ badSize newSize =
763+ concat
764+ [ label
765+ , ": new array size (in bytes) is less than required size "
766+ , show newSize
767+ , ". Please check the sizing function passed."
768+ ]
865769
866770-- | @resize newCapacity array@ changes the total capacity of the array so that
867771-- it is enough to hold the specified number of elements. Nothing is done if
@@ -874,27 +778,27 @@ reallocWith label capSizer minIncr arr = do
874778{-# INLINE resize #-}
875779resize :: forall m a. (MonadIO m, Storable a) =>
876780 Int -> Array a -> m (Array a)
877−resize n arr@Array{..} = do
878− let req = SIZE_OF(a) * n
879− len = aEnd `minusPtr` arrStart
781+resize nElems arr@Array{..} = do
782+ let req = SIZE_OF(a) * nElems
783+ len = aEnd - arrStart
880784 if req < len
881785 then return arr
882786 else realloc req arr
883787
884−-- | Like 'resize' but if the capacity is more than 'largeObjectThreshold' then
885−-- it is rounded up to the closest power of 2.
788+-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'
789+-- then it is rounded up to the closest power of 2.
886790--
887791-- /Pre-release/
888792{-# INLINE resizeExp #-}
889793resizeExp :: forall m a. (MonadIO m, Storable a) =>
890794 Int -> Array a -> m (Array a)
891−resizeExp n arr@Array{..} = do
892− let req = roundUpLargeArray (SIZE_OF(a) * n)
795+resizeExp nElems arr@Array{..} = do
796+ let req = roundUpLargeArray (SIZE_OF(a) * nElems)
893797 req1 =
894798 if req > largeObjectThreshold
895799 then roundUpToPower2 req
896800 else req
897− len = aEnd `minusPtr` arrStart
801+ len = aEnd - arrStart
898802 if req1 < len
899803 then return arr
900804 else realloc req1 arr
@@ -912,10 +816,10 @@ rightSize :: forall m a. (MonadIO m, Storable a) => Array a -> m (Array a)
912816rightSize arr@Array{..} = do
913817 assert (aEnd <= aBound) (return ())
914818 let start = arrStart
915− len = aEnd `minusPtr` start
916− capacity = aBound `minusPtr` start
819+ len = aEnd - start
820+ capacity = aBound - start
917821 target = roundUpLargeArray len
918− waste = aBound `minusPtr` aEnd
822+ waste = aBound - aEnd
919823 assert (target >= len) (return ())
920824 assert (len `mod` SIZE_OF(a) == 0) (return ())
921825 -- We trade off some wastage (25%) to avoid reallocations and copying.
@@ -940,11 +844,10 @@ rightSize arr@Array{..} = do
940844--
941845-- /Internal/
942846{-# 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)
944848snocNewEnd newEnd arr@Array{..} x = liftIO $ do
945849 assert (newEnd <= aBound) (return ())
946− poke aEnd x
947− touch arrContents
850+ pokeWith arrContents aEnd x
948851 return $ arr {aEnd = newEnd}
949852
950853-- | Really really unsafe, appends the element into the first array, may
@@ -955,7 +858,7 @@ snocNewEnd newEnd arr@Array{..} x = liftIO $ do
955858{-# INLINE snocUnsafe #-}
956859snocUnsafe :: forall m a. (MonadIO m, Storable a) =>
957860 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
959862
960863-- | Like 'snoc' but does not reallocate when pre-allocated array capacity
961864-- becomes full.
@@ -965,7 +868,7 @@ snocUnsafe arr@Array{..} = snocNewEnd (PTR_NEXT(aEnd,a)) arr
965868snocMay :: forall m a. (MonadIO m, Storable a) =>
966869 Array a -> a -> m (Maybe (Array a))
967870snocMay arr@Array{..} x = liftIO $ do
968− let newEnd = PTR_NEXT(aEnd,a)
871+ let newEnd = INDEX_NEXT(aEnd,a)
969872 if newEnd <= aBound
970873 then Just <$> snocNewEnd newEnd arr x
971874 else return Nothing
@@ -1001,7 +904,7 @@ snocWith :: forall m a. (MonadIO m, Storable a) =>
1001904 -> a
1002905 -> m (Array a)
1003906snocWith allocSize arr x = liftIO $ do
1004− let newEnd = PTR_NEXT(aEnd arr,a)
907+ let newEnd = INDEX_NEXT(aEnd arr,a)
1005908 if newEnd <= aBound arr
1006909 then snocNewEnd newEnd arr x
1007910 else snocWithRealloc allocSize arr x
@@ -1049,42 +952,27 @@ snoc = snocWith f
1049952-- Random reads
1050953-------------------------------------------------------------------------------
1051954
955+-- XXX Can this be deduplicated with array/foreign
956+
1052957-- | Return the element at the specified index without checking the bounds.
1053958--
1054959-- Unsafe because it does not check the bounds of the array.
1055960{-# INLINE_NORMAL getIndexUnsafe #-}
1056961getIndexUnsafe :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
1057−getIndexUnsafe i arr@(Array {..}) =
1058− asPtrUnsafe arr $ \ptr -> do
1059− let elemPtr = PTR_INDEX(ptr,i,a)
1060− assert (i >= 0 && PTR_VALID(elemPtr,aEnd,a)) (return ())
1061− liftIO $ peek elemPtr
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
962+getIndexUnsafe i (Array {..}) = do
963+ let index = INDEX_OF(arrStart,i,a)
964+ assert (i >= 0 && INDEX_VALID(index,aEnd,a)) (return ())
965+ liftIO $ peekWith arrContents index
1071966
1072967-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
1073968--
1074969{-# INLINE getIndex #-}
1075−getIndex :: (MonadIO m, Storable a) => Int -> Array a -> m a
1076−getIndex i arr =
1077− asPtrUnsafe arr
1078− $ \p -> getIndexPtr p (aEnd arr) i
1079−
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
970+getIndex :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
971+getIndex i Array{..} = do
972+ let index = INDEX_OF(arrStart,i,a)
973+ if i >= 0 && INDEX_VALID(index,aEnd,a)
974+ then liftIO $ peekWith arrContents index
975+ else invalidIndex "getIndex" i
1088976
1089977-- | /O(1)/ Lookup the element at the given index from the end of the array.
1090978-- Index starts from 0.
@@ -1092,10 +980,12 @@ getIndexPtrRev start end i = do
1092980-- Slightly faster than computing the forward index and using getIndex.
1093981--
1094982{-# INLINE getIndexRev #-}
1095−getIndexRev :: (MonadIO m, Storable a) => Int -> Array a -> m a
1096−getIndexRev i arr =
1097− asPtrUnsafe arr
1098− $ \p -> getIndexPtrRev p (aEnd arr) i
983+getIndexRev :: forall m a. (MonadIO m, Storable a) => Int -> Array a -> m a
984+getIndexRev i Array{..} = do
985+ let index = RINDEX_OF(aEnd,i,a)
986+ if i >= 0 && index >= arrStart
987+ then liftIO $ peekWith arrContents index
988+ else invalidIndex "getIndexRev" i
1099989
1100990data GetIndicesState contents start end st =
1101991 GetIndicesState contents start end st
@@ -1112,20 +1002,18 @@ getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
11121002
11131003 where
11141004
1115− inject (Array contents start (Ptr end) _) =
1116− return $ GetIndicesState contents start (Ptr end) sti
1005+ inject (Array contents start end _) =
1006+ return $ GetIndicesState contents start end sti
11171007
11181008 {-# INLINE_LATE step #-}
11191009 step (GetIndicesState contents start end st) = do
11201010 r <- stepi defState st
11211011 case r of
11221012 D.Yield i s -> do
1123− x <- liftio $ getIndexPtr start end i
1013+ x <- liftio $ getIndex i (Array contents start end undefined)
11241014 return $ D.Yield x (GetIndicesState contents start end s)
11251015 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
11291017
11301018{-# INLINE getIndices #-}
11311019getIndices :: (MonadIO m, Storable a) => Stream m Int -> Unfold m (Array a) a
@@ -1151,8 +1039,8 @@ getSliceUnsafe :: forall a. Storable a
11511039 -> Array a
11521040 -> Array a
11531041getSliceUnsafe index len (Array contents start e _) =
1154− let fp1 = PTR_INDEX(start,index,a)
1155− end = fp1 `plusPtr` (len * SIZE_OF(a))
1042+ let fp1 = INDEX_OF(start,index,a)
1043+ end = fp1 + (len * SIZE_OF(a))
11561044 in assert
11571045 (index >= 0 && len >= 0 && end <= e)
11581046 -- Note: In a slice we always use bound = end so that the slice
@@ -1170,8 +1058,8 @@ getSlice :: forall a. Storable a =>
11701058 -> Array a
11711059 -> Array a
11721060getSlice index len (Array contents start e _) =
1173− let fp1 = PTR_INDEX(start,index,a)
1174− end = fp1 `plusPtr` (len * SIZE_OF(a))
1061+ let fp1 = INDEX_OF(start,index,a)
1062+ end = fp1 + (len * SIZE_OF(a))
11751063 in if index >= 0 && len >= 0 && end <= e
11761064 -- Note: In a slice we always use bound = end so that the slice user
11771065 -- cannot overwrite elements beyond the end of the slice.
@@ -1196,15 +1084,15 @@ getSlice index len (Array contents start e _) =
11961084reverse :: forall m a. (MonadIO m, Storable a) => Array a -> m ()
11971085reverse Array{..} = liftIO $ do
11981086 let l = arrStart
1199− h = PTR_PREV(aEnd,a)
1087+ h = INDEX_PREV(aEnd,a)
12001088 in swap l h
12011089
12021090 where
12031091
12041092 swap l h = do
12051093 when (l < h) $ do
1206− swapPtrs l h
1207− swap (PTR_NEXT(l,a)) (PTR_PREV(h,a))
1094+ swapArrayByteIndices arrContents l h
1095+ swap (INDEX_NEXT(l,a)) (INDEX_PREV(h,a))
12081096
12091097-- | Generate the next permutation of the sequence, returns False if this is
12101098-- the last permutation.
@@ -1226,7 +1114,7 @@ partitionBy f arr@Array{..} = liftIO $ do
12261114 if arrStart >= aEnd
12271115 then return (arr, arr)
12281116 else do
1229− ptr <- go arrStart (PTR_PREV(aEnd,a))
1117+ ptr <- go arrStart (INDEX_PREV(aEnd,a))
12301118 let pl = Array arrContents arrStart ptr ptr
12311119 pr = Array arrContents ptr aEnd aEnd
12321120 return (pl, pr)
@@ -1235,11 +1123,11 @@ partitionBy f arr@Array{..} = liftIO $ do
12351123
12361124 -- Invariant low < high on entry, and on return as well
12371125 moveHigh low high = do
1238− h <- peek high
1126+ h <- peekWith arrContents high
12391127 if f h
12401128 then
12411129 -- Correctly classified, continue the loop
1242− let high1 = PTR_PREV(high,a)
1130+ let high1 = INDEX_PREV(high,a)
12431131 in if low == high1
12441132 then return Nothing
12451133 else moveHigh low high1
@@ -1255,7 +1143,7 @@ partitionBy f arr@Array{..} = liftIO $ do
12551143 -- low <= high
12561144 -- Both low and high are valid locations within the array
12571145 go low high = do
1258− l <- peek low
1146+ l <- peekWith arrContents low
12591147 if f l
12601148 then
12611149 -- low is wrongly classified
@@ -1266,17 +1154,17 @@ partitionBy f arr@Array{..} = liftIO $ do
12661154 case r of
12671155 Nothing -> return low
12681156 Just (high1, h) -> do -- low < high1
1269− poke low h
1270− poke high1 l
1271− let low1 = PTR_NEXT(low,a)
1272− high2 = PTR_PREV(high1,a)
1157+ pokeWith arrContents low h
1158+ pokeWith arrContents high1 l
1159+ let low1 = INDEX_NEXT(low,a)
1160+ high2 = INDEX_PREV(high1,a)
12731161 if low1 <= high2
12741162 then go low1 high2
12751163 else return low1 -- low1 > high2
12761164
12771165 else do
12781166 -- low is correctly classified
1279− let low1 = PTR_NEXT(low,a)
1167+ let low1 = INDEX_NEXT(low,a)
12801168 if low == high
12811169 then return low1
12821170 else go low1 high
@@ -1329,12 +1217,12 @@ mergeBy = undefined
13291217{-# INLINE byteLength #-}
13301218byteLength :: Array a -> Int
13311219byteLength Array{..} =
1332− let len = aEnd `minusPtr` arrStart
1220+ let len = aEnd - arrStart
13331221 in assert (len >= 0) len
13341222
13351223-- Note: try to avoid the use of length in performance sensitive internal
13361224-- 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.
13381226--
13391227-- | /O(1)/ Get the length of the array i.e. the number of elements in the
13401228-- array.
@@ -1357,7 +1245,7 @@ length arr =
13571245{-# INLINE byteCapacity #-}
13581246byteCapacity :: Array a -> Int
13591247byteCapacity Array{..} =
1360− let len = aBound `minusPtr` arrStart
1248+ let len = aBound - arrStart
13611249 in assert (len >= 0) len
13621250
13631251-- | The remaining capacity in the array for appending more elements without
@@ -1367,7 +1255,7 @@ byteCapacity Array{..} =
13671255{-# INLINE bytesFree #-}
13681256bytesFree :: Array a -> Int
13691257bytesFree Array{..} =
1370− let n = aBound `minusPtr` aEnd
1258+ let n = aBound - aEnd
13711259 in assert (n >= 0) n
13721260
13731261-------------------------------------------------------------------------------
@@ -1413,8 +1301,8 @@ arraysOf n (D.Stream step state) =
14131301 r <- step (adaptState gst) st
14141302 case r of
14151303 D.Yield x s -> do
1416− liftIO $ poke end x >> touch contents
1417− let end1 = PTR_NEXT(end,a)
1304+ liftIO $ pokeWith contents end x
1305+ let end1 = INDEX_NEXT(end,a)
14181306 return $
14191307 if end1 >= bound
14201308 then D.Skip
@@ -1447,7 +1335,7 @@ arrayStreamKFromStreamD =
14471335
14481336data FlattenState s contents a =
14491337 OuterLoop s
1450− | InnerLoop s contents !(Ptr a) !(Ptr a)
1338+ | InnerLoop s contents !Int !Int
14511339
14521340-- | Use the "read" unfold instead.
14531341--
@@ -1475,11 +1363,8 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
14751363 return $ D.Skip $ OuterLoop st
14761364
14771365 step' _ (InnerLoop st contents p end) = do
1478− x <- liftIO $ do
1479− r <- peek p
1480− touch contents
1481− return r
1482− return $ D.Yield x (InnerLoop st contents (PTR_NEXT(p,a)) end)
1366+ x <- liftIO $ peekWith contents p
1367+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
14831368
14841369-- | Use the "readRev" unfold instead.
14851370--
@@ -1499,7 +1384,7 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
14991384 r <- step (adaptState gst) st
15001385 return $ case r of
15011386 D.Yield Array{..} s ->
1502− let p = PTR_PREV(aEnd,a)
1387+ let p = INDEX_PREV(aEnd,a)
15031388 in D.Skip (InnerLoop s arrContents p arrStart)
15041389 D.Skip s -> D.Skip (OuterLoop s)
15051390 D.Stop -> D.Stop
@@ -1509,47 +1394,50 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
15091394
15101395 step' _ (InnerLoop st contents p start) = do
15111396 x <- liftIO $ do
1512− r <- peek p
1513− touch contents
1397+ r <- peekWith contents p
15141398 return r
1515− let cur = PTR_PREV(p,a)
1399+ let cur = INDEX_PREV(p,a)
15161400 return $ D.Yield x (InnerLoop st contents cur start)
15171401
15181402-------------------------------------------------------------------------------
15191403-- Unfolds
15201404-------------------------------------------------------------------------------
15211405
1522−data ReadUState a = ReadUState
1523− UNPACKIF !ArrayContents -- contents
1524− !(Ptr a) -- end address
1525− !(Ptr a) -- current address
1406+data ArrayUnsafe a = ArrayUnsafe
1407+ {-# UNPACK #-} !(ArrayContents a) -- contents
1408+ {-# UNPACK #-} !Int -- index 1
1409+ {-# UNPACK #-} !Int -- index 2
1410+
1411+toArrayUnsafe :: Array a -> ArrayUnsafe a
1412+toArrayUnsafe (Array contents start end _) = ArrayUnsafe contents start end
15261413
1527−toReadUState :: Array a -> ReadUState a
1528−toReadUState (Array contents start end _) = ReadUState contents end start
1414+fromArrayUnsafe ::
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
15291421
15301422{-# INLINE_NORMAL producerWith #-}
15311423producerWith ::
15321424 forall m a. (Monad m, Storable a)
15331425 => (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
15351427 where
15361428
15371429 {-# INLINE_LATE step #-}
1538− step (ReadUState contents end cur)
1539− | assert (cur <= end) (cur == end) = liftio $ do
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
1430+ step (ArrayUnsafe _ cur end)
1431+ | assert (cur <= end) (cur == end) = return D.Stop
1432+ step (ArrayUnsafe contents cur end) = do
15451433 -- When we use a purely lazy Monad like Identity, we need to force a
15461434 -- few actions for correctness and execution order sanity. We want
15471435 -- the peek to occur right here and not lazily at some later point
15481436 -- because we want the peek to be ordered with respect to the touch.
1549− !x <- liftio $ peek cur
1550− return $ D.Yield x (ReadUState contents end (PTR_NEXT(cur,a)))
1437+ !x <- liftio $ peekWith contents cur
1438+ return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)
15511439
1552− extract (ReadUState contents end cur) = return $ Array contents cur end end
1440+ extract = return . fromArrayUnsafe
15531441
15541442-- | Resumable unfold of an array.
15551443--
@@ -1572,16 +1460,14 @@ readRevWith liftio = Unfold step inject
15721460 where
15731461
15741462 inject (Array contents start end _) =
1575− let p = PTR_PREV(end,a)
1576− in return $ ReadUState contents start p
1463+ let p = INDEX_PREV(end,a)
1464+ in return $ ArrayUnsafe contents start p
15771465
15781466 {-# INLINE_LATE step #-}
1579− step (ReadUState contents start p) | p < start = liftio $ do
1580− !_ <- touch contents
1581− return D.Stop
1582− step (ReadUState contents start p) = do
1583− !x <- liftio $ peek p
1584− return $ D.Yield x (ReadUState contents start (PTR_PREV(p,a)))
1467+ step (ArrayUnsafe _ start p) | p < start = return D.Stop
1468+ step (ArrayUnsafe contents start p) = do
1469+ !x <- liftio $ peekWith contents p
1470+ return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))
15851471
15861472-- | Unfold an array into a stream in reverse order.
15871473--
@@ -1612,8 +1498,7 @@ toListFB c n Array{..} = go arrStart
16121498 -- evaluated/written to before we peek at them.
16131499 -- XXX
16141500 let !x = unsafeInlineIO $ do
1615− r <- peek p
1616− touch arrContents
1501+ r <- peekWith arrContents p
16171502 return r
16181503 in c x (go (PTR_NEXT(p,a)))
16191504-}
@@ -1630,9 +1515,8 @@ toList Array{..} = liftIO $ go arrStart
16301515
16311516 go p | assert (p <= aEnd) (p == aEnd) = return []
16321517 go p = do
1633− x <- peek p
1634− touch arrContents
1635− (:) x <$> go (PTR_NEXT(p,a))
1518+ x <- peekWith arrContents p
1519+ (:) x <$> go (INDEX_NEXT(p,a))
16361520
16371521{-# INLINE_NORMAL toStreamDWith #-}
16381522toStreamDWith ::
@@ -1645,9 +1529,8 @@ toStreamDWith liftio Array{..} = D.Stream step arrStart
16451529 {-# INLINE_LATE step #-}
16461530 step _ p | assert (p <= aEnd) (p == aEnd) = return D.Stop
16471531 step _ p = liftio $ do
1648− r <- peek p
1649− touch arrContents
1650− return $ D.Yield r (PTR_NEXT(p,a))
1532+ r <- peekWith arrContents p
1533+ return $ D.Yield r (INDEX_NEXT(p,a))
16511534
16521535-- | Use the 'read' unfold instead.
16531536--
@@ -1669,10 +1552,9 @@ toStreamKWith liftio Array{..} = go arrStart
16691552 go p | assert (p <= aEnd) (p == aEnd) = K.nil
16701553 | otherwise =
16711554 let elemM = do
1672− r <- peek p
1673− touch arrContents
1555+ r <- peekWith arrContents p
16741556 return r
1675− in liftio elemM `K.consM` go (PTR_NEXT(p,a))
1557+ in liftio elemM `K.consM` go (INDEX_NEXT(p,a))
16761558
16771559{-# INLINE toStreamK #-}
16781560toStreamK :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
@@ -1683,7 +1565,7 @@ toStreamDRevWith ::
16831565 forall m a. (Monad m, Storable a)
16841566 => (forall b. IO b -> m b) -> Array a -> D.Stream m a
16851567toStreamDRevWith liftio Array{..} =
1686− let p = PTR_PREV(aEnd,a)
1568+ let p = INDEX_PREV(aEnd,a)
16871569 in D.Stream step p
16881570
16891571 where
@@ -1691,9 +1573,8 @@ toStreamDRevWith liftio Array{..} =
16911573 {-# INLINE_LATE step #-}
16921574 step _ p | p < arrStart = return D.Stop
16931575 step _ p = liftio $ do
1694− r <- peek p
1695− touch arrContents
1696− return $ D.Yield r (PTR_PREV(p,a))
1576+ r <- peekWith arrContents p
1577+ return $ D.Yield r (INDEX_PREV(p,a))
16971578
16981579-- | Use the 'readRev' unfold instead.
16991580--
@@ -1709,7 +1590,7 @@ toStreamKRevWith ::
17091590 forall m a. (Monad m, Storable a)
17101591 => (forall b. IO b -> m b) -> Array a -> K.Stream m a
17111592toStreamKRevWith liftio Array {..} =
1712− let p = PTR_PREV(aEnd,a)
1593+ let p = INDEX_PREV(aEnd,a)
17131594 in go p
17141595
17151596 where
@@ -1717,10 +1598,9 @@ toStreamKRevWith liftio Array {..} =
17171598 go p | p < arrStart = K.nil
17181599 | otherwise =
17191600 let elemM = do
1720− r <- peek p
1721− touch arrContents
1601+ r <- peekWith arrContents p
17221602 return r
1723− in liftio elemM `K.consM` go (PTR_PREV(p,a))
1603+ in liftio elemM `K.consM` go (INDEX_PREV(p,a))
17241604
17251605{-# INLINE toStreamKRev #-}
17261606toStreamKRev :: 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
17471627-- Folds
17481628-------------------------------------------------------------------------------
17491629
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−
17671630-- Note: Arrays may be allocated with a specific alignment at the beginning of
17681631-- the array. If you need to maintain that alignment on reallocations then you
17691632-- can resize the array manually before append, using an aligned resize
@@ -1771,7 +1634,8 @@ fromArrayUnsafe (ArrayUnsafe contents start end) =
17711634
17721635-- XXX Keep the bound intact to not lose any free space? Perf impact?
17731636
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.
17751639--
17761640-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
17771641-- corruption or segfault.
@@ -1792,7 +1656,7 @@ appendNUnsafe action n =
17921656 initial = do
17931657 assert (n >= 0) (return ())
17941658 arr@(Array _ _ end bound) <- action
1795− let free = bound `minusPtr` end
1659+ let free = bound - end
17961660 needed = n * SIZE_OF(a)
17971661 -- XXX We can also reallocate if the array has too much free space,
17981662 -- otherwise we lose that space.
@@ -1803,8 +1667,8 @@ appendNUnsafe action n =
18031667 return $ toArrayUnsafe arr1
18041668
18051669 step (ArrayUnsafe contents start end) x = do
1806− liftIO $ poke end x >> touch contents
1807− return $ ArrayUnsafe contents start (PTR_NEXT(end,a))
1670+ liftIO $ pokeWith contents end x
1671+ return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
18081672
18091673-- | Append @n@ elements to an existing array. Any free space left in the array
18101674-- after appending @n@ elements is lost.
@@ -1858,17 +1722,16 @@ append = appendWith (* 2)
18581722{-# INLINE_NORMAL writeNWithUnsafe #-}
18591723writeNWithUnsafe :: forall m a. (MonadIO m, Storable a)
18601724 => (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
18621726
18631727 where
18641728
1865− initial = FL.Partial . toArrayUnsafe <$> alloc (max n 0)
1729+ initial = toArrayUnsafe <$> alloc (max n 0)
18661730
18671731 step (ArrayUnsafe contents start end) x = do
1868− liftIO $ poke end x >> touch contents
1732+ liftIO $ pokeWith contents end x
18691733 return
1870− $ FL.Partial
1871− $ ArrayUnsafe contents start (PTR_NEXT(end,a))
1734+ $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
18721735
18731736-- | Like 'writeN' but does not check the array bounds when writing. The fold
18741737-- driver must not call the step function more than 'n' times otherwise it will
@@ -1913,20 +1776,19 @@ writeN = writeNWith newArray
19131776{-# INLINE_NORMAL writeRevNWithUnsafe #-}
19141777writeRevNWithUnsafe :: forall m a. (MonadIO m, Storable a)
19151778 => (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
19171780
19181781 where
19191782
19201783 toArrayUnsafeRev (Array contents _ _ bound) =
19211784 ArrayUnsafe contents bound bound
19221785
1923− initial = FL.Partial . toArrayUnsafeRev <$> alloc (max n 0)
1786+ initial = toArrayUnsafeRev <$> alloc (max n 0)
19241787
19251788 step (ArrayUnsafe contents start end) x = do
1926− let ptr = PTR_PREV(start,a)
1927− liftIO $ poke ptr x >> touch contents
1789+ let ptr = INDEX_PREV(start,a)
1790+ liftIO $ pokeWith contents ptr x
19281791 return
1929− $ FL.Partial
19301792 $ ArrayUnsafe contents ptr end
19311793
19321794-- | Like writeNWith but writes the array in reverse order.
@@ -1957,23 +1819,6 @@ writeNAligned :: forall m a. (MonadIO m, Storable a)
19571819 => Int -> Int -> Fold m a (Array a)
19581820writeNAligned align = writeNWith (newArrayAligned align)
19591821
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−
19771822-- XXX Buffer to a list instead?
19781823--
19791824-- | Buffer a stream into a stream of arrays.
@@ -2030,15 +1875,15 @@ writeWith elemCount =
20301875 where
20311876
20321877 insertElem (Array contents start end bound) x = do
2033− liftIO $ poke end x
2034− return $ Array contents start (PTR_NEXT(end,a)) bound
1878+ liftIO $ pokeWith contents end x
1879+ return $ Array contents start (INDEX_NEXT(end,a)) bound
20351880
20361881 initial = do
20371882 when (elemCount < 0) $ error "writeWith: elemCount is negative"
20381883 liftIO $ newArrayAligned (alignment (undefined :: a)) elemCount
20391884 step arr@(Array _ start end bound) x
2040− | PTR_NEXT(end,a) > bound = do
2041− let oldSize = end `minusPtr` start
1885+ | INDEX_NEXT(end,a) > bound = do
1886+ let oldSize = end - start
20421887 newSize = max (oldSize * 2) 1
20431888 arr1 <-
20441889 liftIO
@@ -2077,14 +1922,14 @@ fromStreamDN :: forall m a. (MonadIO m, Storable a)
20771922-- fromStreamDN n = D.fold (writeN n)
20781923fromStreamDN limit str = do
20791924 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
20811926 return $ arr {aEnd = end}
20821927
20831928 where
20841929
2085− fwrite ptr x = do
2086− liftIO $ poke ptr x
2087− return $ PTR_NEXT(ptr,a)
1930+ fwrite arrContents ptr x = do
1931+ liftIO $ pokeWith arrContents ptr x
1932+ return $ INDEX_NEXT(ptr,a)
20881933
20891934-- | Create an 'Array' from the first N elements of a list. The array is
20901935-- allocated to size N, if the list terminates before N elements then the
@@ -2156,45 +2001,60 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
21562001-- Combining
21572002-------------------------------------------------------------------------------
21582003
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+
21592020-- | Copy two arrays into a newly allocated array.
21602021{-# INLINE spliceCopy #-}
2161−spliceCopy :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
2162−spliceCopy arr1 arr2 = do
2163− let src1 = arrStart arr1
2164− src2 = arrStart arr2
2165− len1 = aEnd arr1 `minusPtr` src1
2166− len2 = aEnd arr2 `minusPtr` src2
2167−
2168− arr <- liftIO $ newArray (len1 + len2)
2169− let dst = arrStart arr
2170−
2171− -- XXX Should we use copyMutableByteArray# instead? Is there an overhead to
2172− -- ccall?
2173− liftIO $ do
2174− memcpy (castPtr dst) (castPtr src1) len1
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) }
2022+spliceCopy :: forall m a. (MonadIO m, Storable a) =>
2023+ Array a -> Array a -> m (Array a)
2024+spliceCopy arr1 arr2 = liftIO $ do
2025+ let start1 = arrStart arr1
2026+ start2 = arrStart arr2
2027+ len1 = aEnd arr1 - start1
2028+ len2 = aEnd arr2 - start2
2029+ newArrContents <-
2030+ liftIO
2031+ $ newAlignedArrayContents
2032+ (len1 + len2)
2033+ (alignment (undefined :: a))
2034+ let len = len1 + len2
2035+ newArr = Array newArrContents 0 len len
2036+ putSliceUnsafe arr1 start1 newArr 0 len1
2037+ putSliceUnsafe arr2 start2 newArr len1 len2
2038+ return newArr
21792039
21802040-- | Really really unsafe, appends the second array into the first array. If
21812041-- the first array does not have enough space it may cause silent data
21822042-- corruption or if you are lucky a segfault.
21832043{-# INLINE spliceUnsafe #-}
2184−spliceUnsafe :: MonadIO m => Array a -> (Array a, Int) -> m (Array a)
2185−spliceUnsafe dst (src, srcLen) =
2044+spliceUnsafe :: MonadIO m =>
2045+ Array a -> Array a -> m (Array a)
2046+spliceUnsafe dst src =
21862047 liftIO $ do
2187− let psrc = arrStart src
2188− let pdst = aEnd dst
2189− assert (pdst `plusPtr` srcLen <= aBound dst) (return ())
2190− memcpy (castPtr pdst) (castPtr psrc) srcLen
2191− touch (arrContents src)
2192− touch (arrContents dst)
2193− return $ dst {aEnd = pdst `plusPtr` srcLen}
2048+ let startSrc = arrStart src
2049+ srcLen = aEnd src - startSrc
2050+ endDst = aEnd dst
2051+ assertM (endDst + srcLen <= aBound dst)
2052+ putSliceUnsafe src startSrc dst endDst srcLen
2053+ return $ dst {aEnd = endDst + srcLen}
21942054
21952055-- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no
21962056-- 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
21982058-- first array and @srcBytes@ is the size of the second array, in bytes.
21992059--
22002060-- 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
22092069 in D.fold f (toStreamD src)
22102070-}
22112071 assert (end <= bound) (return ())
2212− let srcLen = aEnd src `minusPtr` arrStart src
2072+ let srcBytes = aEnd src - arrStart src
22132073
22142074 dst1 <-
2215− if end `plusPtr` srcLen >= bound
2075+ if end + srcBytes >= bound
22162076 then do
2217− let oldSize = end `minusPtr` start
2218− newSize = sizer oldSize srcLen
2219− when (newSize < oldSize + srcLen)
2077+ let dstBytes = end - start
2078+ newSizeInBytes = sizer dstBytes srcBytes
2079+ when (newSizeInBytes < dstBytes + srcBytes)
22202080 $ error
22212081 $ "splice: newSize is less than the total size "
22222082 ++ "of arrays being appended. Please check the "
2223− ++ "newSize function passed."
2224− liftIO $ realloc newSize dst
2083+ ++ "sizer function passed."
2084+ liftIO $ realloc newSizeInBytes dst
22252085 else return dst
2226− spliceUnsafe dst1 (src, srcLen)
2086+ spliceUnsafe dst1 src
22272087
22282088-- | The first array is mutated to append the second array. If there is no
22292089-- 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))
22602120{-# INLINE breakOn #-}
22612121breakOn :: MonadIO m
22622122 => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
2263−breakOn sep arr@Array{..} = liftIO $ do
2264− let p = arrStart
2265− loc <- c_memchr p sep (fromIntegral $ aEnd `minusPtr` p)
2123+breakOn sep arr@Array{..} = asPtrUnsafe arr $ \p -> liftIO $ do
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.
2127+ loc <- c_memchr p sep (fromIntegral $ byteLength arr)
2128+ let sepIndex = loc `minusPtr` p
22662129 return $
22672130 if loc == nullPtr
22682131 then (arr, Nothing)
@@ -2270,12 +2133,12 @@ breakOn sep arr@Array{..} = liftIO $ do
22702133 ( Array
22712134 { arrContents = arrContents
22722135 , arrStart = arrStart
2273− , aEnd = loc
2274− , aBound = loc
2136+ , aEnd = arrStart + sepIndex -- exclude the separator
2137+ , aBound = arrStart + sepIndex
22752138 }
22762139 , Just $ Array
22772140 { arrContents = arrContents
2278− , arrStart = arrStart `plusPtr` (loc `minusPtr` p + 1)
2141+ , arrStart = arrStart + (sepIndex + 1)
22792142 , aEnd = aEnd
22802143 , aBound = aBound
22812144 }
@@ -2294,7 +2157,7 @@ splitAt i arr@Array{..} =
22942157 then error $ "sliceAt: specified array index " ++ show i
22952158 ++ " is beyond the maximum index " ++ show maxIndex
22962159 else let off = i * SIZE_OF(a)
2297− p = arrStart `plusPtr` off
2160+ p = arrStart + off
22982161 in ( Array
22992162 { arrContents = arrContents
23002163 , arrStart = arrStart
@@ -2303,7 +2166,7 @@ splitAt i arr@Array{..} =
23032166 }
23042167 , Array
23052168 { arrContents = arrContents
2306− , arrStart = arrStart `plusPtr` off
2169+ , arrStart = p
23072170 , aEnd = aEnd
23082171 , aBound = aBound
23092172 }
@@ -2326,7 +2189,7 @@ castUnsafe ::
23262189#endif
23272190 Array a -> Array b
23282191castUnsafe (Array contents start end bound) =
2329− Array contents (castPtr start) (castPtr end) (castPtr bound)
2192+ Array (castContents contents) start end bound
23302193
23312194-- | Cast an @Array a@ into an @Array Word8@.
23322195--
@@ -2349,44 +2212,82 @@ cast arr =
23492212 then Nothing
23502213 else Just $ castUnsafe arr
23512214
2352−-- | Use an @Array a@ as @Ptr a@.
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+
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.
23532242--
23542243-- /Unsafe/
23552244--
23562245-- /Pre-release/
23572246--
23582247asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
2359−asPtrUnsafe Array{..} f = do
2360− r <- f arrStart
2361− liftIO $ touch arrContents
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.
2255+ r <- f (ptr `plusPtr` arrStart arr)
2256+ liftIO $ touch contents
23622257 return r
23632258
23642259-------------------------------------------------------------------------------
23652260-- Equality
23662261-------------------------------------------------------------------------------
23672262
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
23692265--
23702266-- /Pre-release/
23712267{-# INLINE cmp #-}
2372−cmp :: MonadIO m => Array a -> Array a -> m Bool
2268+cmp :: MonadIO m => Array a -> Array a -> m Ordering
23732269cmp arr1 arr2 =
2374− liftIO $ do
2375− let ptr1 = arrStart arr1
2376− let ptr2 = arrStart arr2
2377− let len1 = aEnd arr1 `minusPtr` ptr1
2378− let len2 = aEnd arr2 `minusPtr` ptr2
2379−
2380− if len1 == len2
2381− then
2382− if ptr1 == ptr2
2383− then return True
2384− else do
2385− r <- memcmp (castPtr ptr1) (castPtr ptr2) len1
2386− touch (arrContents arr1)
2387− touch (arrContents arr2)
2388− return r
2389− else return False
2270+ liftIO
2271+ $ do
2272+ let marr1 = getMutableByteArray# (arrContents arr1)
2273+ marr2 = getMutableByteArray# (arrContents arr2)
2274+ !(I# st1#) = arrStart arr1
2275+ !(I# st2#) = arrStart arr2
2276+ !(I# len#) = byteLength arr1
2277+ case compare (byteLength arr1) (byteLength arr2) of
2278+ EQ -> do
2279+ r <- liftIO $ IO $ \s# ->
2280+ let res =
2281+ I#
2282+ (compareByteArrays#
2283+ (unsafeCoerce# marr1)
2284+ st1#
2285+ (unsafeCoerce# marr2)
2286+ st2#
2287+ len#)
2288+ in (# s#, res #)
2289+ return $ compare r 0
2290+ x -> return x
23902291
23912292-------------------------------------------------------------------------------
23922293-- NFData
@@ -2408,12 +2309,10 @@ instance NFData1 Array where
24082309{-# INLINE strip #-}
24092310strip :: forall a m. (Storable a, MonadIO m) =>
24102311 (a -> Bool) -> Array a -> m (Array a)
2411−strip eq arr@Array{..} = do
2412− st <- liftIO $ getStart arrStart
2413− end <- liftIO $ getLast aEnd st
2414− liftIO $ do
2415− touch arrContents
2416− return arr {arrStart = st, aEnd = end, aBound = end}
2312+strip eq arr@Array{..} = liftIO $ do
2313+ st <- getStart arrStart
2314+ end <- getLast aEnd st
2315+ return arr {arrStart = st, aEnd = end, aBound = end}
24172316
24182317 where
24192318
@@ -2430,17 +2329,17 @@ strip eq arr@Array{..} = do
24302329 getStart cur = do
24312330 if cur < aEnd
24322331 then do
2433− r <- peek cur
2332+ r <- peekWith arrContents cur
24342333 if eq r
2435− then getStart (PTR_NEXT(cur, a))
2334+ then getStart (INDEX_NEXT(cur,a))
24362335 else return cur
24372336 else return cur
24382337
24392338 getLast cur low = do
24402339 if cur > low
24412340 then do
2442− let prev = PTR_PREV(cur,a)
2443− r <- peek prev
2341+ let prev = INDEX_PREV(cur,a)
2342+ r <- peekWith arrContents prev
24442343 if eq r
24452344 then getLast prev low
24462345 else return cur
core/src/Streamly/Internal/Data/Array/Foreign/Type.hs+20−170
@@ -1,3 +1,4 @@
1+{-# LANGUAGE UnboxedTuples #-}
12-- |
23-- Module : Streamly.Internal.Data.Array.Foreign.Type
34-- Copyright : (c) 2020 Composewell Technologies
@@ -23,10 +24,6 @@ module Streamly.Internal.Data.Array.Foreign.Type
2324 -- * Construction
2425 , splice
2526
26− , fromPtr
27− , fromForeignPtrUnsafe
28− , fromAddr#
29− , fromCString#
3027 , fromList
3128 , fromListN
3229 , fromListRev
@@ -62,7 +59,6 @@ module Streamly.Internal.Data.Array.Foreign.Type
6259 , writeNUnsafe
6360 , MA.ArrayUnsafe (..)
6461 , writeNAligned
65− , writeNAlignedUnmanaged
6662 , write
6763
6864 -- * Streams of arrays
@@ -81,20 +77,15 @@ import Control.DeepSeq (NFData(..), NFData1(..))
8177import Control.Monad.IO.Class (MonadIO(..))
8278import Data.Functor.Identity (Identity(..))
8379import 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)
8981import GHC.Exts (IsList, IsString(..))
90−import GHC.ForeignPtr (ForeignPtr)
9182
9283import GHC.IO (unsafePerformIO)
9384import GHC.Ptr (Ptr(..))
94−import Streamly.Internal.Data.Array.Foreign.Mut.Type
95− (ArrayContents, touch)
85+import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayContents)
9686import Streamly.Internal.Data.Fold.Type (Fold(..))
9787import Streamly.Internal.Data.Stream.Type (Stream)
88+import Streamly.Internal.Data.Unboxed (Storable, peekWith, sizeOf)
9889import Streamly.Internal.Data.Unfold.Type (Unfold(..))
9990import Text.Read (readPrec, readListPrec, readListPrecDefault)
10091
@@ -141,34 +132,27 @@ data Array a =
141132#ifdef DEVBUILD
142133 Storable a =>
143134#endif
135+ -- All offsets are in terms of bytes from the start of arraycontents
144136 Array
145− { arrContents ::
146−#ifndef USE_FOREIGN_PTR
147− {-# UNPACK #-}
148−#endif
149− !ArrayContents -- ^ first address
150− , arrStart :: {-# UNPACK #-} !(Ptr a) -- start address
151− , aEnd :: {-# UNPACK #-} !(Ptr a) -- first unused addres
137+ { arrContents :: {-# UNPACK #-} !(ArrayContents a)
138+ , arrStart :: {-# UNPACK #-} !Int -- offset
139+ , aEnd :: {-# UNPACK #-} !Int -- offset + len
152140 }
153141
154142-------------------------------------------------------------------------------
155143-- Utility functions
156144-------------------------------------------------------------------------------
157145
158−foreign import ccall unsafe "string.h strlen" c_strlen
159− :: CString -> IO CSize
160−
161146-- | Use an @Array a@ as @Ptr a@.
162147--
148+-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
149+--
163150-- /Unsafe/
164151--
165152-- /Pre-release/
166153--
167154asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
168−asPtrUnsafe Array{..} f = do
169− r <- f arrStart
170− liftIO $ touch arrContents
171− return r
155+asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
172156
173157-------------------------------------------------------------------------------
174158-- Freezing and Thawing
@@ -221,124 +205,6 @@ splice :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
221205splice arr1 arr2 =
222206 unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)
223207
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−
342208-- | Create an 'Array' from the first N elements of a list. The array is
343209-- allocated to size N, if the list terminates before N elements then the
344210-- array may hold less than N elements.
@@ -462,7 +328,7 @@ byteLength = MA.byteLength . unsafeThaw
462328--
463329-- @since 0.8.0
464330{-# INLINE length #-}
465−length :: forall a. Storable a => Array a -> Int
331+length :: Storable a => Array a -> Int
466332length arr = MA.length (unsafeThaw arr)
467333
468334-- | Unfold an array into a stream in reverse order.
@@ -523,7 +389,7 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
523389-- specified index @i@ is the first index of the second slice.
524390--
525391-- @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)
527393splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
528394 where
529395 (a, b) = MA.splitAt i (unsafeThaw arr)
@@ -542,12 +408,9 @@ toListFB c n Array{..} = go arrStart
542408 -- accumulating the list and fusing better with the pure consumers.
543409 --
544410 -- This should be safe as the array contents are guaranteed to be
545− -- evaluated/written to before we peek at them.
546− let !x = unsafeInlineIO $ do
547− r <- peek p
548− touch arrContents
549− return r
550− in c x (go (PTR_NEXT(p,a)))
411+ -- evaluated/written to before we peekWith at them.
412+ let !x = unsafeInlineIO $ peekWith arrContents p
413+ in c x (go (INDEX_NEXT(p,a)))
551414
552415-- | Convert an 'Array' into a list.
553416--
@@ -582,19 +445,6 @@ writeNAligned :: forall m a. (MonadIO m, Storable a)
582445 => Int -> Int -> Fold m a (Array a)
583446writeNAligned alignSize = fmap unsafeFreeze . MA.writeNAligned alignSize
584447
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−
598448-- | Like 'writeN' but does not check the array bounds when writing. The fold
599449-- driver must not call the step function more than 'n' times otherwise it will
600450-- corrupt the memory and crash. This function exists mainly because any
@@ -655,7 +505,8 @@ instance Storable a => IsList (Array a) where
655505-- or may not be correct? arrcmp is 40% faster compared to stream equality.
656506instance (Storable a, Eq a) => Eq (Array a) where
657507 {-# INLINE (==) #-}
658− arr1 == arr2 = unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
508+ arr1 == arr2 =
509+ (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
659510
660511-- Since this is a Storable array we cannot have unevaluated data in it so
661512-- this is just a no op.
@@ -706,8 +557,7 @@ toStreamD_ size Array{..} = D.Stream step arrStart
706557 {-# INLINE_LATE step #-}
707558 step _ p | p == aEnd = return D.Stop
708559 step _ p = liftIO $ do
709− x <- peek p
710− touch arrContents
560+ x <- peekWith arrContents p
711561 return $ D.Yield x (p `plusPtr` size)
712562
713563{-
@@ -741,7 +591,7 @@ nil ::
741591 Storable a =>
742592#endif
743593 Array a
744−nil = Array MA.nilArrayContents (Ptr nullAddr#) (Ptr nullAddr#)
594+nil = Array MA.nilArrayContents 0 0
745595
746596instance Storable a => Monoid (Array a) where
747597 mempty = nil
core/src/Streamly/Internal/Data/Array/Stream/Fold/Foreign.hs+9−11
@@ -60,8 +60,7 @@ import Control.Applicative (liftA2)
6060import Control.Exception (assert)
6161import Control.Monad.Catch (MonadThrow)
6262import 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)
6564import GHC.Types (SPEC(..))
6665import Streamly.Internal.Data.Array.Foreign.Mut.Type (touch)
6766import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
@@ -119,16 +118,15 @@ fromFold (Fold.Fold fstep finitial fextract) =
119118
120119 goArray !_ !cur !fs | cur >= end = do
121120 assert (cur == end) (return ())
122− liftIO $ touch contents
123121 return $ Partial 0 fs
124122 goArray !_ !cur !fs = do
125− x <- liftIO $ peek cur
123+ x <- liftIO $ peekWith contents cur
126124 res <- fstep fs x
127125 let elemSize = SIZE_OF(a)
128− next = PTR_NEXT(cur,a)
126+ next = INDEX_NEXT(cur,a)
129127 case res of
130128 Fold.Done b ->
131− return $ Done ((end `minusPtr` next) `div` elemSize) b
129+ return $ Done ((end - next) `div` elemSize) b
132130 Fold.Partial fs1 ->
133131 goArray SPEC next fs1
134132
@@ -153,18 +151,18 @@ fromParserD (ParserD.Parser step1 initial1 extract1) =
153151
154152 {-# INLINE partial #-}
155153 partial arrRem cur next elemSize st n fs1 = do
156− let next1 = next `plusPtr` negate (n * elemSize)
154+ let next1 = next - (n * elemSize)
157155 if next1 >= start && cur < end
158156 then goArray SPEC next1 fs1
159157 else return $ st (arrRem + n) fs1
160158
161159 goArray !_ !cur !fs = do
162− x <- liftIO $ peek cur
160+ x <- liftIO $ peekWith contents cur
163161 liftIO $ touch contents
164162 res <- step1 fs x
165163 let elemSize = SIZE_OF(a)
166− next = PTR_NEXT(cur,a)
167− arrRem = (end `minusPtr` next) `div` elemSize
164+ next = INDEX_NEXT(cur,a)
165+ arrRem = (end - next) `div` elemSize
168166 case res of
169167 ParserD.Done n b -> do
170168 return $ Done (arrRem + n) b
@@ -340,7 +338,7 @@ take n (ArrayFold (ParserD.Parser step1 initial1 extract1)) =
340338 Error err -> return $ Error err
341339 else do
342340 let !(Array contents start _) = arr
343− end = PTR_INDEX(start,i,a)
341+ end = INDEX_OF(start,i,a)
344342 arr1 = Array contents start end
345343 remaining = negate i1
346344 res <- step1 r arr1
core/src/Streamly/Internal/Data/Fold.hs+7−6
@@ -306,9 +306,10 @@ import Data.IORef (newIORef, readIORef, writeIORef)
306306import Data.Map.Strict (Map)
307307import Data.Maybe (isJust, fromJust)
308308import Data.Word (Word32)
309−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
309+import Foreign.Storable (Storable, peek, sizeOf)
310310import Streamly.Internal.Data.IsMap (IsMap(..))
311311import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
312+import Streamly.Internal.Data.Unboxed (Unboxed)
312313import Streamly.Internal.Data.Unfold.Type (Unfold(..))
313314import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
314315import Streamly.Internal.Data.Stream.Type (Stream)
@@ -1483,7 +1484,7 @@ data SplitOnSeqState acc a rb rh w ck =
14831484--
14841485-- /Pre-release/
14851486{-# 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) =>
14871488 Array.Array a
14881489 -> Fold m a b
14891490 -> Fold m a b
@@ -1619,7 +1620,7 @@ takeEndBySeq patArr (Fold fstep finitial fextract) =
16191620-- /Pre-release/
16201621--
16211622{-# 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) =>
16231624 Array.Array a
16241625 -> Fold m a b
16251626 -> Fold m a b
@@ -2598,7 +2599,7 @@ unfoldMany (Unfold ustep inject) (Fold fstep initial extract) =
25982599 consume s a = inject a >>= produce s
25992600
26002601{-# INLINE topBy #-}
2601−topBy :: (MonadIO m, Storable a) =>
2602+topBy :: (MonadIO m, Unboxed a) =>
26022603 (a -> a -> Ordering)
26032604 -> Int
26042605 -> Fold m a (MA.Array a)
@@ -2637,7 +2638,7 @@ topBy cmp n = Fold step initial extract
26372638--
26382639-- /Pre-release/
26392640{-# 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)
26412642top = topBy $ flip compare
26422643
26432644-- | Fold the input stream to bottom n elements.
@@ -2648,5 +2649,5 @@ top = topBy $ flip compare
26482649--
26492650-- /Pre-release/
26502651{-# 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)
26522653bottom = topBy compare
core/src/Streamly/Internal/Data/Fold/Window.hs+1−1
@@ -56,7 +56,7 @@ where
5656
5757import Control.Monad.IO.Class (MonadIO (liftIO))
5858import Data.Bifunctor(bimap)
59−import Streamly.Internal.Data.Unboxed (Storable, peek)
59+import Foreign.Storable (Storable, peek)
6060
6161import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..))
6262import 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
7575import Control.Exception (assert)
7676import Control.Monad.IO.Class (MonadIO(..))
7777import Data.Word (Word8)
78+import Foreign.Storable
7879import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)
7980import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
8081import Foreign.Ptr (plusPtr, minusPtr, castPtr)
81−import Streamly.Internal.Data.Unboxed
82− ( Storable, alignment, peek, poke, sizeOf
82+import Streamly.Internal.Data.Unboxed as Unboxed
83+ ( ArrayContents
84+ , Unboxed
85+ , castContents
86+ , peekWith
8387 )
8488import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
8589import GHC.Ptr (Ptr(..))
86−import Streamly.Internal.Data.Array.Foreign.Mut.Type (Array, memcmp)
90+import Streamly.Internal.Data.Array.Foreign.Mut.Type (Array)
8791import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..), lmap)
8892import Streamly.Internal.Data.Stream.Type (Stream)
8993import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
@@ -129,7 +133,7 @@ startOf = unsafeForeignPtrToPtr . ringStart
129133{-# INLINE new #-}
130134new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)
131135new count = do
132− let size = count * SIZE_OF(a)
136+ let size = count * max 1 (sizeOf (undefined :: a))
133137 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
134138 let p = unsafeForeignPtrToPtr fptr
135139 return (Ring
@@ -401,22 +405,32 @@ cast arr =
401405-- be in range.
402406{-# INLINE unsafeEqArrayN #-}
403407unsafeEqArrayN :: Ring a -> Ptr a -> A.Array a -> Int -> Bool
404−unsafeEqArrayN Ring{..} rh A.Array{..} n =
405− let !res = unsafeInlineIO $ do
406− let rs = unsafeForeignPtrToPtr ringStart
407− as = arrStart
408− assert (aEnd `minusPtr` as >= ringBound `minusPtr` rs) (return ())
409− let len = ringBound `minusPtr` rh
410− r1 <- memcmp (castPtr rh) (castPtr as) (min len n)
411− r2 <- if n > len
412− then memcmp (castPtr rs) (castPtr (as `plusPtr` len))
413− (min (rh `minusPtr` rs) (n - len))
414− else return True
415− -- XXX enable these, check perf impact
416− -- touchForeignPtr ringStart
417− -- touchForeignPtr aStart
418− return (r1 && r2)
419− in res
408+unsafeEqArrayN Ring{..} rh A.Array{..} nBytes
409+ | nBytes < 0 = error "unsafeEqArrayN: n should be >= 0"
410+ | nBytes == 0 = True
411+ | otherwise = unsafeInlineIO $ check (castPtr rh) 0
412+
413+ where
414+
415+ w8Contents = castContents arrContents :: ArrayContents Word8
416+
417+ check p i = do
418+ relem <- peek p
419+ aelem <- peekWith w8Contents i
420+ if relem == aelem
421+ then go (p `plusPtr` 1) (i + 1)
422+ else return False
423+
424+ go p i
425+ | i == nBytes = return True
426+ | castPtr p == ringBound =
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.
420434
421435-- | Byte compare the entire length of ringBuffer with the given array,
422436-- starting at the supplied ringHead pointer. Returns true if the Array and
@@ -428,20 +442,24 @@ unsafeEqArrayN Ring{..} rh A.Array{..} n =
428442{-# INLINE unsafeEqArray #-}
429443unsafeEqArray :: Ring a -> Ptr a -> A.Array a -> Bool
430444unsafeEqArray Ring{..} rh A.Array{..} =
431− let !res = unsafeInlineIO $ do
432− let rs = unsafeForeignPtrToPtr ringStart
433− let as = arrStart
434− assert (aEnd `minusPtr` as >= ringBound `minusPtr` rs)
435− (return ())
436− let len = ringBound `minusPtr` rh
437− r1 <- memcmp (castPtr rh) (castPtr as) len
438− r2 <- memcmp (castPtr rs) (castPtr (as `plusPtr` len))
439− (rh `minusPtr` rs)
440− -- XXX enable these, check perf impact
441− -- touchForeignPtr ringStart
442− -- touchForeignPtr aStart
443− return (r1 && r2)
444− in res
445+ unsafeInlineIO $ check (castPtr rh) 0
446+
447+ where
448+
449+ w8Contents = castContents arrContents :: ArrayContents Word8
450+
451+ check p i = do
452+ relem <- peek p
453+ aelem <- peekWith w8Contents i
454+ if relem == aelem
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
461+ | castPtr p == rh = touchForeignPtr ringStart >> return True
462+ | otherwise = check p i
445463
446464-------------------------------------------------------------------------------
447465-- Folding
@@ -543,7 +561,7 @@ data Tuple4' a b c d = Tuple4' !a !b !c !d deriving Show
543561-- a))@ action depends on when it is executed. It does not capture the sanpshot
544562-- of the ring at a particular time.
545563{-# INLINE slidingWindowWith #-}
546−slidingWindowWith :: forall m a b. (MonadIO m, Storable a)
564+slidingWindowWith :: forall m a b. (MonadIO m, Storable a, Unboxed a)
547565 => Int -> Fold m ((a, Maybe a), (m (Array a))) b -> Fold m a b
548566slidingWindowWith n (Fold step1 initial1 extract1) = Fold step initial extract
549567
@@ -597,6 +615,6 @@ slidingWindowWith n (Fold step1 initial1 extract1) = Fold step initial extract
597615-- there is no old element.
598616--
599617{-# INLINE slidingWindow #-}
600−slidingWindow :: forall m a b. (MonadIO m, Storable a)
618+slidingWindow :: forall m a b. (MonadIO m, Storable a, Unboxed a)
601619 => Int -> Fold m (a, Maybe a) b -> Fold m a b
602620slidingWindow n f = slidingWindowWith n (lmap fst f)
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+3−2
@@ -73,8 +73,9 @@ where
7373import Control.Monad.Catch (MonadThrow)
7474import Control.Monad.IO.Class (MonadIO(..))
7575import Control.Monad.Trans.Class (MonadTrans(..))
76+import Foreign.Storable (Storable)
7677import Streamly.Internal.Data.Parser (Parser (..))
77−import Streamly.Internal.Data.Unboxed (Storable)
78+import Streamly.Internal.Data.Unboxed (Unboxed)
7879
7980import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
8081import qualified Streamly.Internal.Data.Fold as Fold
@@ -277,7 +278,7 @@ isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
277278-- /Requires 'Storable' constraint/
278279--
279280{-# INLINE isInfixOf #-}
280−isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a)
281+isInfixOf :: (MonadIO m, Eq a, Enum a, Unboxed a, Storable a)
281282 => Stream m a -> Stream m a -> m Bool
282283isInfixOf infx stream = do
283284 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(..))
155155import Data.Bits (shiftR, shiftL, (.|.), (.&.))
156156import Data.Functor.Identity ( Identity )
157157import Data.Word (Word32)
158−import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
158+import Foreign.Storable (Storable, peek, sizeOf)
159159import Fusion.Plugin.Types (Fuse(..))
160160import GHC.Types (SPEC(..))
161161
@@ -166,6 +166,7 @@ import Streamly.Internal.Data.Parser (ParseError(..))
166166import Streamly.Internal.Data.Refold.Type (Refold(..))
167167import Streamly.Internal.Data.SVar.Type (adaptState)
168168import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
169+import Streamly.Internal.Data.Unboxed (Unboxed)
169170import Streamly.Internal.Data.Unfold.Type (Unfold(..))
170171
171172import qualified Streamly.Internal.Data.Array.Foreign.Type as A
@@ -1702,7 +1703,7 @@ data SplitOnSeqState rb rh ck w fs s b x =
17021703
17031704{-# INLINE_NORMAL splitOnSeq #-}
17041705splitOnSeq
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)
17061707 => Array a
17071708 -> Fold m a b
17081709 -> Stream m a
@@ -2015,7 +2016,7 @@ data SplitOnSuffixSeqState rb rh ck w fs s b x =
20152016
20162017{-# INLINE_NORMAL splitOnSuffixSeq #-}
20172018splitOnSuffixSeq
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)
20192020 => Bool
20202021 -> Array a
20212022 -> Fold m a b
core/src/Streamly/Internal/Data/Unboxed.hs+289−47
@@ -3,58 +3,300 @@
33
44module Streamly.Internal.Data.Unboxed
55 ( Storable
6+ , Unboxed
67 , alignment
7− , peek
8− , poke
8+ , peekWith
9+ , pokeWith
910 , sizeOf
11+ , ArrayContents(..)
12+ , castContents
13+ , touch
14+ , getMutableByteArray#
1015 ) where
1116
12−#ifdef USE_STORABLE
13−import Foreign.Storable (Storable(..))
14−#else
15−import Data.Int (Int8)
16−import Data.Primitive.Types (Prim(..), sizeOf, alignment)
17+#include "MachDeps.h"
18+
19+#include "ArrayMacros.h"
20+
21+import Data.Complex (Complex((:+)), realPart)
1722import 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#
1966#endif
2067
21−#ifndef USE_STORABLE
22−type Storable = Prim
23−
24−{-# INLINE peek #-}
25−peek :: Prim a => Ptr a -> IO a
26−peek (Ptr addr#) = IO $ \s# -> readOffAddr# addr# 0# s#
27−
28−{-# INLINE poke #-}
29−poke :: Prim a => Ptr a -> a -> IO ()
30−poke (Ptr addr#) a = IO $ \s# -> (# writeOffAddr# addr# 0# a s#, () #)
31−
32−-- | Orphan Prim instance of Bool implemented using Int8
33−instance Prim Bool where
34− sizeOf# _ = sizeOf# (undefined :: Int8)
35− alignment# _ = alignment# (undefined :: Int8)
36− indexByteArray# arr# i# = indexByteArray# arr# i# /= (0 :: Int8)
37− readByteArray# arr# i# s# =
38− case readByteArray# arr# i# s# of
39− (# s1#, i :: Int8 #) -> (# s1#, i /= 0 #)
40− writeByteArray# arr# i# a s# =
41− case a of
42− True -> writeByteArray# arr# i# (1 :: Int8) s#
43− False -> writeByteArray# arr# i# (0 :: Int8) s#
44− setByteArray# arr# off# len# a s# =
45− case a of
46− True -> setByteArray# arr# off# len# (1 :: Int8) s#
47− False -> setByteArray# arr# off# len# (0 :: Int8) s#
48− indexOffAddr# addr# i# = indexOffAddr# addr# i# /= (0 :: Int8)
49− readOffAddr# addr# i# s# =
50− case readOffAddr# addr# i# s# of
51− (# s1#, i :: Int8 #) -> (# s1#, i /= 0 #)
52− writeOffAddr# addr# i# a s# =
53− case a of
54− True -> writeOffAddr# addr# i# (1 :: Int8) s#
55− False -> writeOffAddr# addr# i# (0 :: Int8) s#
56− setOffAddr# addr# off# len# a s# =
57− case a of
58− True -> setOffAddr# addr# off# len# (1 :: Int8) s#
59− False -> setOffAddr# addr# off# len# (0 :: Int8) s#
68+{-# INLINE readWord8ArrayAsWideChar# #-}
69+readWord8ArrayAsWideChar# ::
70+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Char# #)
71+readWord8ArrayAsWideChar# arr# i# s# =
72+ readWideCharArray# arr# (quotInt# i# SIZEOF_HSCHAR_PRIMITIVE) s#
73+
74+{-# INLINE writeWord8ArrayAsWideChar# #-}
75+writeWord8ArrayAsWideChar# ::
76+ MutableByteArray# d -> Int# -> Char# -> State# d -> State# d
77+writeWord8ArrayAsWideChar# arr# i# a# s# =
78+ writeWideCharArray# arr# (quotInt# i# SIZEOF_HSCHAR_PRIMITIVE) a# s#
79+
80+{-# INLINE readWord8ArrayAsInt# #-}
81+readWord8ArrayAsInt# ::
82+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Int# #)
83+readWord8ArrayAsInt# arr# i# s# =
84+ readIntArray# arr# (quotInt# i# SIZEOF_HSINT_PRIMITIVE) s#
85+
86+{-# INLINE writeWord8ArrayAsInt# #-}
87+writeWord8ArrayAsInt# ::
88+ MutableByteArray# d -> Int# -> Int# -> State# d -> State# d
89+writeWord8ArrayAsInt# arr# i# a# s# =
90+ writeIntArray# arr# (quotInt# i# SIZEOF_HSINT_PRIMITIVE) a# s#
91+
92+{-# INLINE readWord8ArrayAsInt32# #-}
93+readWord8ArrayAsInt32# ::
94+ MutableByteArray# d -> Int# -> State# d -> (# State# d, Int# #)
95+readWord8ArrayAsInt32# arr# i# s# =
96+ readInt32Array# arr# (quotInt# i# SIZEOF_INT32_PRIMITIVE) s#
97+
98+{-# INLINE writeWord8ArrayAsInt32# #-}
99+writeWord8ArrayAsInt32# ::
100+ MutableByteArray# d -> Int# -> Int# -> State# d -> State# d
101+writeWord8ArrayAsInt32# arr# i# a# s# =
102+ writeInt32Array# arr# (quotInt# i# SIZEOF_INT32_PRIMITIVE) a# s#
103+
104+{-# INLINE readWord8ArrayAsWord# #-}
105+readWord8ArrayAsWord# ::
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+
60140#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
8282 manual: True
8383 default: False
8484
85−flag use-storable
86− description: Use Storable for unboxed arrays
87− manual: True
88− default: False
89−
9085flag use-unfolds
9186 description: Use unfolds for generation everywhere
9287 manual: True
@@ -122,9 +117,6 @@ common compile-options
122117 if flag(use-c-malloc)
123118 cpp-options: -DUSE_C_MALLOC
124119
125− if flag(use-storable)
126− cpp-options: -DUSE_STORABLE
127−
128120 ghc-options: -Wall
129121 -Wcompat
130122 -Wunrecognised-warning-flags
src/Streamly/Internal/Data/Array/ArrayMacros.h+12
@@ -25,3 +25,15 @@
2525#define PTR_VALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) <= end
2626#define PTR_INVALID(ptr,end,a) ptr `plusPtr` SIZE_OF(a) > end
2727
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(..))
5858import Data.Semigroup (Semigroup(..))
5959#endif
6060import 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)
6462import Fusion.Plugin.Types (Fuse(..))
6563import GHC.Exts (SpecConstrAnnotation(..))
66−import GHC.ForeignPtr (ForeignPtr(..))
67−import GHC.Ptr (Ptr(..))
6864import GHC.Types (SPEC(..))
6965import Prelude hiding (null, last, (!!), read, concat, unlines)
7066
71−import Streamly.Internal.Data.Array.Foreign.Mut.Type
72− (arrayToFptrContents, fptrToArrayContents, touch)
7367import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
7468import Streamly.Internal.Data.Array.Stream.Fold.Foreign (ArrayFold(..))
7569import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -79,7 +73,8 @@ import Streamly.Internal.Data.Stream.IsStream.Type
7973 (IsStream, fromStreamD, toStreamD)
8074import Streamly.Internal.Data.SVar (adaptState, defState)
8175import Streamly.Internal.Data.Array.Foreign.Mut.Type
82− (memcpy, allocBytesToElemCount)
76+ (allocBytesToElemCount)
77+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
8378
8479import qualified Streamly.Internal.Data.Array.Foreign as A
8580import qualified Streamly.Internal.Data.Array.Foreign as Array
@@ -175,7 +170,7 @@ interposeSuffix x = S.interposeSuffix x A.read
175170
176171data FlattenState s a =
177172 OuterLoop s
178− | InnerLoop s !MA.ArrayContents !(Ptr a) !(Ptr a)
173+ | InnerLoop s !(MA.ArrayContents a) !Int !Int
179174
180175-- XXX Remove monadIO constraint
181176{-# INLINE_NORMAL unlines #-}
@@ -196,11 +191,8 @@ unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
196191 return $ D.Yield sep $ OuterLoop st
197192
198193 step' _ (InnerLoop st contents p end) = do
199− x <- liftIO $ do
200− r <- peek p
201− touch contents
202− return r
203− return $ D.Yield x (InnerLoop st contents (PTR_NEXT(p,a)) end)
194+ x <- liftIO $ peekWith contents p
195+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
204196
205197-------------------------------------------------------------------------------
206198-- Compact
@@ -339,25 +331,24 @@ foldBreakD (Fold fstep initial extract) stream@(D.Stream step state) = do
339331 go !_ st !fs = do
340332 r <- step defState st
341333 case r of
342− D.Yield (Array contents start (Ptr end)) s ->
343− let fp = ForeignPtr end (arrayToFptrContents contents)
334+ D.Yield (Array contents start end) s ->
335+ let fp = Tuple' end contents
344336 in goArray SPEC s fp start fs
345337 D.Skip s -> go SPEC s fs
346338 D.Stop -> do
347339 b <- extract fs
348340 return (b, D.nil)
349341
350− goArray !_ s fp@(ForeignPtr end _) !cur !fs
351− | cur == Ptr end = do
352− liftIO $ touchForeignPtr fp
342+ goArray !_ s (Tuple' end _) !cur !fs
343+ | cur == end = do
353344 go SPEC s fs
354− goArray !_ st fp@(ForeignPtr end contents) !cur !fs = do
355− x <- liftIO $ peek cur
345+ goArray !_ st fp@(Tuple' end contents) !cur !fs = do
346+ x <- liftIO $ peekWith contents cur
356347 res <- fstep fs x
357− let next = PTR_NEXT(cur,a)
348+ let next = INDEX_NEXT(cur,a)
358349 case res of
359350 FL.Done b -> do
360− let arr = Array (fptrToArrayContents contents) next (Ptr end)
351+ let arr = Array contents next end
361352 return $! (b, D.cons arr (D.Stream step st))
362353 FL.Partial fs1 -> goArray SPEC st fp next fs1
363354
@@ -376,22 +367,21 @@ foldBreakK (Fold fstep initial extract) stream = do
376367 go !fs st = do
377368 let stop = (, K.nil) <$> extract fs
378369 single a = yieldk a K.nil
379− yieldk (Array contents start (Ptr end)) r =
380− let fp = ForeignPtr end (arrayToFptrContents contents)
370+ yieldk (Array contents start end) r =
371+ let fp = Tuple' end contents
381372 in goArray fs r fp start
382373 in K.foldStream defState yieldk single stop st
383374
384− goArray !fs st fp@(ForeignPtr end _) !cur
385− | cur == Ptr end = do
386− liftIO $ touchForeignPtr fp
375+ goArray !fs st (Tuple' end _) !cur
376+ | cur == end = do
387377 go fs st
388− goArray !fs st fp@(ForeignPtr end contents) !cur = do
389− x <- liftIO $ peek cur
378+ goArray !fs st fp@(Tuple' end contents) !cur = do
379+ x <- liftIO $ peekWith contents cur
390380 res <- fstep fs x
391− let next = PTR_NEXT(cur,a)
381+ let next = INDEX_NEXT(cur,a)
392382 case res of
393383 FL.Done b -> do
394− let arr = Array (fptrToArrayContents contents) next (Ptr end)
384+ let arr = Array contents next end
395385 return $! (b, K.cons arr st)
396386 FL.Partial fs1 -> goArray fs1 st fp next
397387
@@ -437,14 +427,14 @@ takeArrayListRev = go
437427 else if n == len
438428 then [x]
439429 else let !(Array contents _ end) = x
440− !start = end `plusPtr` negate (n * SIZE_OF(a))
430+ !start = end - (n * SIZE_OF(a))
441431 in [Array contents start end]
442432
443433-- When we have to take an array partially, take the last part of the array in
444434-- the first split.
445435{-# INLINE splitAtArrayListRev #-}
446−splitAtArrayListRev :: forall a. Storable a =>
447− Int -> [Array a] -> ([Array a],[Array a])
436+splitAtArrayListRev ::
437+ forall a. Storable a => Int -> [Array a] -> ([Array a],[Array a])
448438splitAtArrayListRev n ls
449439 | n <= 0 = ([], ls)
450440 | otherwise = go n ls
@@ -459,7 +449,7 @@ splitAtArrayListRev n ls
459449 else if m == len
460450 then ([x],xs)
461451 else let !(Array contents start end) = x
462− end1 = end `plusPtr` negate (m * SIZE_OF(a))
452+ end1 = end - (m * SIZE_OF(a))
463453 arr2 = Array contents start end1
464454 arr1 = Array contents end1 end
465455 in ([arr1], arr2:xs)
@@ -478,17 +468,7 @@ spliceArraysLenUnsafe :: (MonadIO m, Storable a)
478468 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
479469spliceArraysLenUnsafe len buffered = do
480470 arr <- liftIO $ MA.newArray len
481− end <- S.foldlM' writeArr (return $ MA.aEnd 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
471+ S.foldlM' MA.spliceUnsafe (return arr) buffered
492472
493473{-# INLINE _spliceArrays #-}
494474_spliceArrays :: (MonadIO m, Storable a)
@@ -497,17 +477,12 @@ _spliceArrays s = do
497477 buffered <- S.foldr S.cons S.nil s
498478 len <- S.sum (S.map Array.length buffered)
499479 arr <- liftIO $ MA.newArray len
500− end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
501− return $ A.unsafeFreeze $ arr {MA.aEnd = end}
480+ final <- S.foldlM' writeArr (return arr) s
481+ return $ A.unsafeFreeze final
502482
503483 where
504484
505− writeArr dst (Array ac src ae) =
506− liftIO $ do
507− let count = ae `minusPtr` src
508− memcpy (castPtr dst) (castPtr src) count
509− touch ac
510− return $ dst `plusPtr` count
485+ writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)
511486
512487{-# INLINE _spliceArraysBuffered #-}
513488_spliceArraysBuffered :: (MonadIO m, Storable a)
@@ -598,23 +573,22 @@ parseBreakD
598573 go !_ st backBuf !pst = do
599574 r <- step defState st
600575 case r of
601− D.Yield (Array contents start (Ptr end)) s ->
576+ D.Yield (Array contents start end) s ->
602577 gobuf SPEC s backBuf
603− (ForeignPtr end (arrayToFptrContents contents)) start pst
578+ (Tuple' end contents) start pst
604579 D.Skip s -> go SPEC s backBuf pst
605580 D.Stop -> do
606581 b <- extract pst
607582 return (b, D.nil)
608583
609584 -- Use strictness on "cur" to keep it unboxed
610− gobuf !_ s backBuf fp@(ForeignPtr end _) !cur !pst
611− | cur == Ptr end = do
612− liftIO $ touchForeignPtr fp
585+ gobuf !_ s backBuf (Tuple' end _) !cur !pst
586+ | cur == end = do
613587 go SPEC s backBuf pst
614− gobuf !_ s backBuf fp@(ForeignPtr end contents) !cur !pst = do
615− x <- liftIO $ peek cur
588+ gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
589+ x <- liftIO $ peekWith contents cur
616590 pRes <- pstep pst x
617− let next = PTR_NEXT(cur,a)
591+ let next = INDEX_NEXT(cur,a)
618592 case pRes of
619593 PR.Partial 0 pst1 ->
620594 gobuf SPEC s (List []) fp next pst1
@@ -622,10 +596,10 @@ parseBreakD
622596 assert (n <= Prelude.length (x:getList backBuf)) (return ())
623597 let src0 = Prelude.take n (x:getList backBuf)
624598 arr0 = A.fromListN n (Prelude.reverse src0)
625− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
599+ arr1 = Array contents next end
626600 src = arr0 <> arr1
627− let !(Array cont1 start (Ptr end1)) = src
628− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
601+ let !(Array cont1 start end1) = src
602+ fp1 = Tuple' end1 cont1
629603 gobuf SPEC s (List []) fp1 start pst1
630604 PR.Continue 0 pst1 ->
631605 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
@@ -633,20 +607,20 @@ parseBreakD
633607 assert (n <= Prelude.length (x:getList backBuf)) (return ())
634608 let (src0, buf1) = splitAt n (x:getList backBuf)
635609 arr0 = A.fromListN n (Prelude.reverse src0)
636− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
610+ arr1 = Array contents next end
637611 src = arr0 <> arr1
638− let !(Array cont1 start (Ptr end1)) = src
639− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
612+ let !(Array cont1 start end1) = src
613+ fp1 = Tuple' end1 cont1
640614 gobuf SPEC s (List buf1) fp1 start pst1
641615 PR.Done 0 b -> do
642− let arr = Array (fptrToArrayContents contents) next (Ptr end)
616+ let arr = Array contents next end
643617 return (b, D.cons arr (D.Stream step s))
644618 PR.Done n b -> do
645619 assert (n <= Prelude.length (x:getList backBuf)) (return ())
646620 let src0 = Prelude.take n (x:getList backBuf)
647621 -- XXX create the array in reverse instead
648622 arr0 = A.fromListN n (Prelude.reverse src0)
649− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
623+ arr1 = Array contents next end
650624 -- XXX Use StreamK to avoid adding arbitrary layers of
651625 -- constructors every time.
652626 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
@@ -677,20 +651,19 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
677651 go !pst st backBuf = do
678652 let stop = (, K.nil) <$> extract pst
679653 single a = yieldk a K.nil
680− yieldk (Array contents start (Ptr end)) r =
681− let fp = ForeignPtr end (arrayToFptrContents contents)
654+ yieldk (Array contents start end) r =
655+ let fp = Tuple' end contents
682656 in goArray pst backBuf r fp start
683657 in K.foldStream defState yieldk single stop st
684658
685659 -- Use strictness on "cur" to keep it unboxed
686− goArray !pst backBuf st fp@(ForeignPtr end _) !cur
687− | cur == Ptr end = do
688− liftIO $ touchForeignPtr fp
660+ goArray !pst backBuf st (Tuple' end _) !cur
661+ | cur == end = do
689662 go pst st backBuf
690− goArray !pst backBuf st fp@(ForeignPtr end contents) !cur = do
691− x <- liftIO $ peek cur
663+ goArray !pst backBuf st fp@(Tuple' end contents) !cur = do
664+ x <- liftIO $ peekWith contents cur
692665 pRes <- pstep pst x
693− let next = PTR_NEXT(cur,a)
666+ let next = INDEX_NEXT(cur,a)
694667 case pRes of
695668 PR.Partial 0 s ->
696669 goArray s [] st fp next
@@ -698,10 +671,10 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
698671 assert (n <= Prelude.length (x:backBuf)) (return ())
699672 let src0 = Prelude.take n (x:backBuf)
700673 arr0 = A.fromListN n (Prelude.reverse src0)
701− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
674+ arr1 = Array contents next end
702675 src = arr0 <> arr1
703− let !(Array cont1 start (Ptr end1)) = src
704− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
676+ let !(Array cont1 start end1) = src
677+ fp1 = Tuple' end1 cont1
705678 goArray s [] st fp1 start
706679 PR.Continue 0 s ->
707680 goArray s (x:backBuf) st fp next
@@ -709,13 +682,13 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
709682 assert (n <= Prelude.length (x:backBuf)) (return ())
710683 let (src0, buf1) = splitAt n (x:backBuf)
711684 arr0 = A.fromListN n (Prelude.reverse src0)
712− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
685+ arr1 = Array contents next end
713686 src = arr0 <> arr1
714− let !(Array cont1 start (Ptr end1)) = src
715− fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
687+ let !(Array cont1 start end1) = src
688+ fp1 = Tuple' end1 cont1
716689 goArray s buf1 st fp1 start
717690 PR.Done 0 b -> do
718− let arr = Array (fptrToArrayContents contents) next (Ptr end)
691+ let arr = Array contents next end
719692 return (b, K.cons arr st)
720693 PR.Done n b -> do
721694 assert (n <= Prelude.length (x:backBuf)) (return ())
@@ -723,7 +696,7 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
723696 -- XXX Use fromListRevN once implemented
724697 -- arr0 = A.fromListRevN n src0
725698 arr0 = A.fromListN n (Prelude.reverse src0)
726− arr1 = Array (fptrToArrayContents contents) next (Ptr end)
699+ arr1 = Array contents next end
727700 str = K.cons arr0 (K.cons arr1 st)
728701 return (b, str)
729702 PR.Error err -> throwM $ ParseError err
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+3−2
@@ -69,7 +69,7 @@ where
6969
7070import Control.Concurrent (threadDelay)
7171import Control.Monad.IO.Class (MonadIO(..))
72−import Streamly.Internal.Data.Unboxed (Storable)
72+import qualified Foreign.Storable as Storable (Storable)
7373import Streamly.Internal.Control.Concurrent (MonadAsync)
7474import Streamly.Internal.Data.Array.Foreign.Type (Array)
7575import Streamly.Internal.Data.Fold.Type (Fold (..))
@@ -79,6 +79,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
7979import Streamly.Internal.Data.Stream.Serial (SerialT)
8080import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
8181import Streamly.Internal.System.IO (defaultChunkSize)
82+import Streamly.Internal.Data.Unboxed (Storable, Unboxed)
8283
8384import qualified Streamly.Internal.Data.Array.Foreign.Type as A
8485import qualified Streamly.Internal.Data.Stream.Async as Async
@@ -678,7 +679,7 @@ foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
678679-- that we can search files in files for example.
679680{-# INLINE splitOnSeq #-}
680681splitOnSeq
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)
682683 => Array a -> Fold m a b -> t m a -> t m b
683684splitOnSeq patt f m =
684685 IsStream.fromStreamD $ D.splitOnSeq patt f (IsStream.toStreamD m)
src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+3−2
@@ -157,7 +157,7 @@ where
157157import Control.Monad.Catch (MonadThrow)
158158import Control.Monad.IO.Class (MonadIO(..))
159159import Control.Monad.Trans.Class (MonadTrans(..))
160−import Streamly.Internal.Data.Unboxed (Storable)
160+import qualified Foreign.Storable as Storable (Storable)
161161import Streamly.Internal.Control.Concurrent (MonadAsync)
162162import Streamly.Internal.Data.Parser (Parser (..))
163163import Streamly.Internal.Data.SVar (defState)
@@ -167,6 +167,7 @@ import Streamly.Internal.Data.Stream.IsStream.Common
167167import Streamly.Internal.Data.Stream.IsStream.Type
168168 (IsStream, toStreamS, fromStreamD, toStreamD)
169169import Streamly.Internal.Data.Stream.Serial (SerialT)
170+import Streamly.Internal.Data.Unboxed (Unboxed)
170171
171172import qualified Streamly.Internal.Data.Array.Foreign.Type as A
172173import qualified Streamly.Internal.Data.Fold as FL
@@ -937,7 +938,7 @@ isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
937938-- /Requires 'Storable' constraint/
938939--
939940{-# 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)
941942 => SerialT m a -> SerialT m a -> m Bool
942943isInfixOf infx stream = do
943944 arr <- fold A.write infx
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+5−4
@@ -157,7 +157,7 @@ import Data.Kind (Type)
157157import Data.Map (Map)
158158import Data.Maybe (isNothing)
159159import Data.Proxy (Proxy(..))
160−import Streamly.Internal.Data.Unboxed (Storable)
160+import qualified Foreign.Storable as Storable (Storable)
161161import Streamly.Internal.Control.Concurrent (MonadAsync)
162162import Streamly.Internal.Data.Fold.Type (Fold (..))
163163import Streamly.Internal.Data.IsMap (IsMap(..))
@@ -178,6 +178,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
178178import Streamly.Internal.Data.Time.Units
179179 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
180180 , toAbsTime)
181+import Streamly.Internal.Data.Unboxed (Storable, Unboxed)
181182
182183import qualified Data.Heap as H
183184import qualified Streamly.Data.Unfold as Unfold
@@ -823,7 +824,7 @@ splitOnAny _subseq _f _m =
823824-- /Pre-release/
824825{-# INLINE splitBySeq #-}
825826splitBySeq
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)
827828 => Array a -> Fold m a b -> t m a -> t m b
828829splitBySeq patt f m =
829830 intersperseM (fold f (A.toStream patt)) $ splitOnSeq patt f m
@@ -874,7 +875,7 @@ splitBySeq patt f m =
874875-- /Pre-release/
875876{-# INLINE splitOnSuffixSeq #-}
876877splitOnSuffixSeq
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)
878879 => Array a -> Fold m a b -> t m a -> t m b
879880splitOnSuffixSeq patt f m =
880881 fromStreamD $ D.splitOnSuffixSeq False patt f (toStreamD m)
@@ -926,7 +927,7 @@ wordsOn _subseq _f _m =
926927-- /Pre-release/
927928{-# INLINE splitWithSuffixSeq #-}
928929splitWithSuffixSeq
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)
930931 => Array a -> Fold m a b -> t m a -> t m b
931932splitWithSuffixSeq patt f m =
932933 fromStreamD $ D.splitOnSuffixSeq True patt f (toStreamD m)
src/Streamly/Internal/FileSystem/Event/Darwin.hs+2−2
@@ -14,7 +14,7 @@
1414--
1515-- @
1616-- {-\# LANGUAGE MagicHash #-}
17−-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromCString\# "path"#]
17+-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromList "path"]
1818-- @
1919--
2020-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
@@ -560,7 +560,7 @@ watchToStream (Watch handle _ _) =
560560-- {-\# LANGUAGE MagicHash #-}
561561-- watchWith
562562-- ('setIgnoreSelf' 'On' . 'setRootPathEvents' 'On')
563−-- [Array.fromCString\# "path"#]
563+-- [Array.fromList "path"]
564564-- @
565565--
566566-- /Pre-release/
src/Streamly/Internal/FileSystem/Event/Linux.hs+6−4
@@ -13,7 +13,7 @@
1313-- occurring under the watched paths.
1414--
1515-- @
16−-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchPaths' [Array.fromCString\# "dir"#]
16+-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchPaths' [Array.fromList "dir"]
1717-- @
1818--
1919-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
@@ -636,10 +636,12 @@ ensureTrailingSlash path =
636636 in case mx of
637637 Nothing -> error "ensureTrailingSlash: Bug: Invalid index"
638638 Just x ->
639− if x /= fromIntegral (ord '/')
640− then path <> A.fromCString# "/"#
639+ if x /= forwardSlashByte
640+ then path <> forwardSlash
641641 else path
642642 else path
643+ where forwardSlash = A.fromList [ forwardSlashByte ]
644+ forwardSlashByte = fromIntegral (ord '/')
643645
644646removeTrailingSlash :: Array Word8 -> Array Word8
645647removeTrailingSlash path =
@@ -908,7 +910,7 @@ watchToStream cfg wt@(Watch handle _) = do
908910-- @
909911-- watchwith
910912-- ('setFollowSymLinks' On . 'setUnwatchMoved' Off)
911−-- [Array.fromCString\# "dir"#]
913+-- [Array.fromList "dir"]
912914-- @
913915--
914916-- /Pre-release/
src/Streamly/Internal/FileSystem/Event/Windows.hs+5−3
@@ -18,7 +18,7 @@
1818--
1919-- @
2020-- {-\# LANGUAGE MagicHash #-}
21−-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromCString\# "path"#]
21+-- Stream.mapM_ (putStrLn . 'showEvent') $ 'watchRecursive' [Array.fromList "path"]
2222-- @
2323--
2424-- 'Event' is an opaque type. Accessor functions (e.g. 'showEvent' above)
@@ -92,6 +92,7 @@ where
9292
9393import Control.Monad.IO.Class (MonadIO(liftIO))
9494import Data.Bits ((.|.), (.&.), complement)
95+import Data.Char (ord)
9596import Data.Functor.Identity (runIdentity)
9697import Data.List.NonEmpty (NonEmpty)
9798import Data.Word (Word8)
@@ -465,7 +466,7 @@ closePathHandleStream = S.mapM_ (\(h, _, _) -> closeHandle h)
465466-- @
466467-- watchWith
467468-- ('setAttrsModified' On . 'setLastWriteTimeModified' Off)
468−-- [Array.fromCString\# "dir"#]
469+-- [Array.fromList "dir"]
469470-- @
470471--
471472-- /Pre-release/
@@ -527,7 +528,8 @@ getRoot Event{..} = (UTF8.toArray . UTF8.pack) eventRootPath
527528-- /Pre-release/
528529--
529530getAbsPath :: 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 '\\') ]
531533
532534-- XXX need to document the exact semantics of these.
533535--
src/Streamly/Internal/FileSystem/FD.hs+5−7
@@ -118,11 +118,9 @@ where
118118
119119import Control.Monad.IO.Class (MonadIO(..))
120120import Data.Word (Word8)
121−import Foreign.ForeignPtr (withForeignPtr)
122121-- import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
123−import Foreign.Ptr (plusPtr, castPtr)
122+import Foreign.Ptr (castPtr)
124123import Streamly.Internal.Data.Unboxed (Storable)
125−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
126124-- import System.IO (Handle, hGetBufSome, hPutBuf)
127125import System.IO (IOMode)
128126import Prelude hiding (read)
@@ -132,7 +130,6 @@ import qualified GHC.IO.Device as RawIO
132130
133131import Streamly.Internal.Data.Array.Foreign.Type
134132 (Array(..), byteLength, unsafeFreeze, asPtrUnsafe)
135−import Streamly.Internal.Data.Array.Foreign.Mut.Type (fromForeignPtrUnsafe)
136133import Streamly.Internal.System.IO (defaultChunkSize)
137134import Streamly.Internal.Data.Stream.Serial (SerialT)
138135import Streamly.Internal.Data.Stream.IsStream.Type
@@ -149,6 +146,7 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
149146-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
150147
151148import qualified Streamly.Data.Array.Foreign as A
149+import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
152150import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
153151import qualified Streamly.Prelude as S
154152import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
@@ -216,9 +214,9 @@ openFile path mode = Handle . fst <$> FD.openFile path mode True
216214{-# INLINABLE readArrayUpto #-}
217215readArrayUpto :: Int -> Handle -> IO (Array Word8)
218216readArrayUpto size (Handle fd) = do
219− ptr <- mallocPlainForeignPtrBytes size
217+ arr <- MArray.newPinnedArrayBytes size
220218 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
221− withForeignPtr ptr $ \p -> do
219+ MArray.asPtrUnsafe arr $ \p -> do
222220 -- n <- hGetBufSome h p size
223221#if MIN_VERSION_base(4,15,0)
224222 n <- RawIO.read fd p 0 size
@@ -229,7 +227,7 @@ readArrayUpto size (Handle fd) = do
229227 -- Use unsafeFreezeWithShrink
230228 return
231229 $ unsafeFreeze
232− $ fromForeignPtrUnsafe ptr (p `plusPtr` n) (p `plusPtr` size)
230+ $ arr { MArray.aEnd = n, MArray.aBound = size }
233231
234232-------------------------------------------------------------------------------
235233-- Array IO (output)
src/Streamly/Internal/FileSystem/Handle.hs+9−11
@@ -114,20 +114,15 @@ import Control.Monad.IO.Class (MonadIO(..))
114114import Data.Function ((&))
115115import Data.Maybe (isNothing, fromJust)
116116import Data.Word (Word8)
117−import Foreign.ForeignPtr (withForeignPtr)
118−import Foreign.Ptr (minusPtr, plusPtr)
119117import Streamly.Internal.Data.Unboxed (Storable)
120−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
121118import System.IO (Handle, SeekMode(..), hGetBufSome, hPutBuf, hSeek)
122119import Prelude hiding (read)
123120
124−import Streamly.Internal.Data.Array.Foreign.Mut.Type (touch)
125121import Streamly.Internal.Data.Fold (Fold)
126122import Streamly.Internal.Data.Refold.Type (Refold(..))
127123import Streamly.Internal.Data.Unfold.Type (Unfold(..))
128124import Streamly.Internal.Data.Array.Foreign.Type
129125 (Array(..), writeNUnsafe, unsafeFreezeWithShrink, byteLength)
130−import Streamly.Internal.Data.Array.Foreign.Mut.Type (fromForeignPtrUnsafe)
131126import Streamly.Internal.Data.Stream.Serial (SerialT)
132127import Streamly.Internal.Data.Stream.IsStream.Type
133128 (IsStream, mkStream, fromStreamD)
@@ -136,6 +131,8 @@ import Streamly.Internal.Data.Array.Stream.Foreign (lpackArraysChunksOf)
136131import Streamly.Internal.System.IO (defaultChunkSize)
137132
138133import 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
139136import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
140137import qualified Streamly.Internal.Data.Refold.Type as Refold
141138import qualified Streamly.Internal.Data.Fold as FL
@@ -183,14 +180,14 @@ import qualified Streamly.Internal.Data.Unfold as UF
183180{-# INLINABLE getChunk #-}
184181getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
185182getChunk size h = liftIO $ do
186− ptr <- mallocPlainForeignPtrBytes size
183+ arr <- MArray.newPinnedArrayBytes size
187184 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
188− withForeignPtr ptr $ \p -> do
185+ MArray.asPtrUnsafe arr $ \p -> do
189186 n <- hGetBufSome h p size
190187 -- XXX shrink only if the diff is significant
191188 return $
192189 unsafeFreezeWithShrink $
193− fromForeignPtrUnsafe ptr (p `plusPtr` n) (p `plusPtr` size)
190+ arr { MArray.aEnd = n, MArray.aBound = size }
194191
195192-- This could be useful in implementing the "reverse" read APIs or if you want
196193-- to read arrays of exact size instead of compacting them later. Compacting
@@ -404,12 +401,13 @@ getBytes = AS.concat . getChunks
404401{-# INLINABLE putChunk #-}
405402putChunk :: MonadIO m => Handle -> Array a -> m ()
406403putChunk _ arr | byteLength arr == 0 = return ()
407−putChunk h Array{..} =
408− liftIO $ hPutBuf h arrStart aLen >> touch arrContents
404+putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
405+ liftIO $ hPutBuf h ptr aLen
409406
410407 where
411408
412− aLen = aEnd `minusPtr` arrStart
409+ -- XXX We should have the length passed by asPtrUnsafe itself.
410+ aLen = A.byteLength arr
413411
414412-------------------------------------------------------------------------------
415413-- Stream of Arrays IO
src/Streamly/Internal/Network/Socket.hs+7−11
@@ -75,9 +75,8 @@ import Control.Monad.IO.Class (MonadIO(..))
7575import Control.Monad (forM_, when)
7676import Data.Maybe (isNothing, fromJust)
7777import Data.Word (Word8)
78−import Foreign.Ptr (minusPtr, plusPtr, Ptr, castPtr)
78+import Foreign.Ptr (plusPtr, Ptr, castPtr)
7979import Streamly.Internal.Data.Unboxed (Storable)
80−import GHC.ForeignPtr (mallocPlainForeignPtrBytes)
8180import Network.Socket
8281 (Socket, SocketOption(..), Family(..), SockAddr(..),
8382 ProtocolNumber, withSocketsDo, SocketType(..), socket, bind,
@@ -91,10 +90,7 @@ import Prelude hiding (read)
9190
9291import qualified Network.Socket as Net
9392
94−import Streamly.Internal.BaseCompat
9593import Streamly.Internal.Control.Concurrent (MonadAsync)
96−import Streamly.Internal.Data.Array.Foreign.Mut.Type
97− (fromForeignPtrUnsafe, touch)
9894import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
9995import Streamly.Internal.Data.Array.Stream.Foreign (lpackArraysChunksOf)
10096import Streamly.Internal.Data.Fold (Fold)
@@ -107,6 +103,7 @@ import Streamly.Internal.System.IO (defaultChunkSize)
107103
108104import qualified Streamly.Internal.Data.Array.Foreign as A
109105import qualified Streamly.Internal.Data.Array.Foreign.Type as A
106+import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
110107import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
111108import qualified Streamly.Internal.Data.Fold as FL
112109import qualified Streamly.Internal.Data.Stream.IsStream as S
@@ -261,12 +258,12 @@ readArrayUptoWith
261258 -> h
262259 -> IO (Array Word8)
263260readArrayUptoWith f size h = do
264− ptr <- mallocPlainForeignPtrBytes size
261+ arr <- MArray.newPinnedArrayBytes size
265262 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
266− unsafeWithForeignPtr ptr $ \p -> do
263+ MArray.asPtrUnsafe arr $ \p -> do
267264 n <- f h p size
268265 let v = A.unsafeFreeze
269− $ fromForeignPtrUnsafe ptr (p `plusPtr` n) (p `plusPtr` size)
266+ $ arr { MArray.aEnd = n, MArray.aBound = size }
270267
271268 -- XXX shrink only if the diff is significant
272269 -- A.shrinkToFit v
@@ -312,12 +309,11 @@ writeArrayWith :: Storable a
312309 -> Array a
313310 -> IO ()
314311writeArrayWith _ _ arr | A.length arr == 0 = return ()
315−writeArrayWith f h Array{..} =
316− f h (castPtr arrStart) aLen >> touch arrContents
312+writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
317313
318314 where
319315
320− aLen = aEnd `minusPtr` arrStart
316+ aLen = A.byteLength arr
321317
322318-- | Write an Array to a file handle.
323319--
src/Streamly/Internal/Unicode/Stream.hs+61−49
@@ -81,6 +81,7 @@ where
8181
8282#include "inline.hs"
8383
84+import Control.Monad (void)
8485import Control.Monad.Catch (MonadThrow(..), MonadCatch)
8586import Control.Monad.IO.Class (MonadIO, liftIO)
8687import Data.Bits (shiftR, shiftL, (.|.), (.&.))
@@ -89,6 +90,7 @@ import Data.Char (chr, ord)
8990import Data.Char (generalCategory, GeneralCategory(Space))
9091#endif
9192import Data.Word (Word8)
93+import Foreign.Marshal.Alloc (mallocBytes)
9294import Foreign.Storable (Storable(..))
9395import Fusion.Plugin.Types (Fuse(..))
9496import GHC.Base (assert, unsafeChr)
@@ -96,7 +98,7 @@ import GHC.IO.Encoding.Failure (isSurrogate)
9698import GHC.Ptr (Ptr (..), plusPtr)
9799import System.IO.Unsafe (unsafePerformIO)
98100import 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)
100102import Streamly.Internal.Data.Fold (Fold)
101103import Streamly.Internal.Data.Stream.IsStream.Type
102104 (IsStream, fromStreamD, toStreamD, adapt)
@@ -104,9 +106,11 @@ import Streamly.Internal.Data.Stream.Serial (SerialT)
104106import Streamly.Internal.Data.Stream.StreamD (Stream(..), Step (..))
105107import Streamly.Internal.Data.SVar (adaptState)
106108import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
109+import Streamly.Internal.Data.Unboxed (peekWith)
107110import Streamly.Internal.Data.Unfold.Type (Unfold(..))
108111import Streamly.Internal.System.IO (unsafeInlineIO)
109112
113+import qualified Streamly.Internal.Data.Fold as Fold
110114import qualified Streamly.Internal.Data.Unfold as Unfold
111115import qualified Streamly.Internal.Data.Parser as Parser
112116import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
@@ -233,20 +237,41 @@ decodeTable = [
233237 12,36,12,12,12,12,12,12,12,12,12,12
234238 ]
235239
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.
236248{-# NOINLINE utf8d #-}
237−utf8d :: A.Array Word8
238−utf8d =
239− unsafePerformIO
240− -- Aligning to cacheline makes a barely noticeable difference
241− -- XXX currently alignment is not implemented for unmanaged allocation
242− $ D.fold (A.writeNAlignedUnmanaged 64 (length decodeTable))
243− (D.fromList decodeTable)
249+utf8d :: Ptr Word8
250+utf8d = unsafePerformIO $ do
251+ let size = utf8dLength
252+ p <- liftIO $ mallocBytes size
253+ void $ D.fold
254+ (Fold.foldlM' (\b a -> poke b a >> return (b `plusPtr` 1)) (return p))
255+ (D.fromList decodeTable)
256+ return p
244257
245258-- | Return element at the specified index without checking the bounds.
246259-- and without touching the foreign ptr.
247260{-# INLINE_NORMAL unsafePeekElemOff #-}
248261unsafePeekElemOff :: forall a. Storable a => Ptr a -> Int -> a
249−unsafePeekElemOff p i = let !x = unsafeInlineIO $ peekElemOff p i in x
262+unsafePeekElemOff p i =
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 _ _ = ""
250275
251276-- decode is split into two separate cases to avoid branching instructions.
252277-- From the higher level flow we already know which case we are in so we can
@@ -265,11 +290,9 @@ decode0 table byte =
265290
266291 where
267292
268− utf8table =
269− let end = table `plusPtr` 364
270− in A.Array undefined table end :: A.Array Word8
293+ utf8tableEnd = table `plusPtr` 364
271294 showByte = "Streamly: decode0: byte: " ++ show byte
272− showTable = " table: " ++ show utf8table
295+ showTable = " table: " ++ showMemory table utf8tableEnd
273296
274297-- When the state is not 0
275298{-# INLINE decode1 #-}
@@ -291,14 +314,12 @@ decode1 table state codep byte =
291314 (Tuple' state' codep')
292315 where
293316
294− utf8table =
295− let end = table `plusPtr` 364
296− in A.Array undefined table end :: A.Array Word8
317+ utf8tableEnd = table `plusPtr` 364
297318 showByte = "Streamly: decode1: byte: " ++ show byte
298319 showState st cp =
299320 " state: " ++ show st ++
300321 " codepoint: " ++ show cp ++
301− " table: " ++ show utf8table
322+ " table: " ++ showMemory table utf8tableEnd
302323
303324-------------------------------------------------------------------------------
304325-- Resumable UTF-8 decoding
@@ -324,13 +345,11 @@ resumeDecodeUtf8EitherD
324345 -> Stream m Word8
325346 -> Stream m (Either DecodeError Char)
326347resumeDecodeUtf8EitherD dst codep (Stream step state) =
327− let A.Array _ p _ = utf8d
328− !ptr = p
329− stt =
348+ let stt =
330349 if dst == 0
331350 then UTF8DecodeInit state
332351 else UTF8Decoding state dst codep
333− in Stream (step' ptr) stt
352+ in Stream (step' utf8d) stt
334353 where
335354 {-# INLINE_LATE step' #-}
336355 step' _ gst (UTF8DecodeInit st) = do
@@ -439,9 +458,7 @@ data UTF8CharDecodeState a
439458{-# INLINE parseCharUtf8WithD #-}
440459parseCharUtf8WithD ::
441460 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
445462
446463 where
447464
@@ -541,8 +558,7 @@ parseCharUtf8With = ParserD.toParserK . parseCharUtf8WithD
541558decodeUtf8WithD :: Monad m
542559 => CodingFailureMode -> Stream m Word8 -> Stream m Char
543560decodeUtf8WithD cfm (Stream step state) =
544− let A.Array _ ptr _ = utf8d
545− in Stream (step' ptr) (UTF8DecodeInit state)
561+ Stream (step' utf8d) (UTF8DecodeInit state)
546562
547563 where
548564
@@ -679,9 +695,9 @@ decodeUtf8Lax = decodeUtf8
679695#endif
680696data FlattenState s a
681697 = OuterLoop s !(Maybe (DecodeState, CodePoint))
682− | InnerLoopDecodeInit s ArrayContents !(Ptr a) !(Ptr a)
683− | InnerLoopDecodeFirst s ArrayContents !(Ptr a) !(Ptr a) Word8
684− | InnerLoopDecoding s ArrayContents !(Ptr a) !(Ptr a)
698+ | InnerLoopDecodeInit s (ArrayContents a) !Int !Int
699+ | InnerLoopDecodeFirst s (ArrayContents a) !Int !Int Word8
700+ | InnerLoopDecoding s (ArrayContents a) !Int !Int
685701 !DecodeState !CodePoint
686702 | YAndC !Char (FlattenState s a) -- These constructors can be
687703 -- encoded in the UTF8DecodeState
@@ -702,8 +718,7 @@ decodeUtf8ArraysWithD ::
702718 -> Stream m (A.Array Word8)
703719 -> Stream m Char
704720decodeUtf8ArraysWithD 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)
707722 where
708723 {-# INLINE transliterateOrError #-}
709724 transliterateOrError e s =
@@ -737,12 +752,11 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
737752 Skip (InnerLoopDecoding s arrContents arrStart aEnd ds cp)
738753 Skip s -> Skip (OuterLoop s dst)
739754 Stop -> Skip inputUnderflow
740− step' _ _ (InnerLoopDecodeInit st startf p end)
755+ step' _ _ (InnerLoopDecodeInit st _ p end)
741756 | p == end = do
742− liftIO $ touch startf
743757 return $ Skip $ OuterLoop st Nothing
744− step' _ _ (InnerLoopDecodeInit st startf p end) = do
745− x <- liftIO $ peek p
758+ step' _ _ (InnerLoopDecodeInit st contents p end) = do
759+ x <- liftIO $ peekWith contents p
746760 -- Note: It is important to use a ">" instead of a "<=" test here for
747761 -- GHC to generate code layout for default branch prediction for the
748762 -- common case. This is fragile and might change with the compiler
@@ -752,13 +766,13 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
752766 False ->
753767 return $ Skip $ YAndC
754768 (unsafeChr (fromIntegral x))
755− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
769+ (InnerLoopDecodeInit st contents (p + 1) end)
756770 -- Using a separate state here generates a jump to a separate code
757771 -- block in the core which seems to perform slightly better for the
758772 -- non-ascii case.
759− True -> return $ Skip $ InnerLoopDecodeFirst st startf p end x
773+ True -> return $ Skip $ InnerLoopDecodeFirst st contents p end x
760774
761− step' table _ (InnerLoopDecodeFirst st startf p end x) = do
775+ step' table _ (InnerLoopDecodeFirst st contents p end x) = do
762776 let (Tuple' sv cp) = decode0 table x
763777 return $
764778 case sv of
@@ -770,15 +784,13 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
770784 ++ "decodeUtf8ArraysWith: Invalid UTF8"
771785 ++ " codepoint encountered"
772786 )
773− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
787+ (InnerLoopDecodeInit st contents (p + 1) end)
774788 0 -> error "unreachable state"
775− _ -> Skip (InnerLoopDecoding st startf (p `plusPtr` 1) end sv cp)
776− step' _ _ (InnerLoopDecoding st startf p end sv cp)
777− | p == end = do
778− liftIO $ touch startf
779− return $ Skip $ OuterLoop st (Just (sv, cp))
780− step' table _ (InnerLoopDecoding st startf p end statePtr codepointPtr) = do
781− x <- liftIO $ peek p
789+ _ -> Skip (InnerLoopDecoding st contents (p + 1) end sv cp)
790+ step' _ _ (InnerLoopDecoding st _ p end sv cp)
791+ | p == end = return $ Skip $ OuterLoop st (Just (sv, cp))
792+ step' table _ (InnerLoopDecoding st contents p end statePtr codepointPtr) = do
793+ x <- liftIO $ peekWith contents p
782794 let (Tuple' sv cp) = decode1 table statePtr codepointPtr x
783795 return $
784796 case sv of
@@ -786,7 +798,7 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
786798 Skip $
787799 YAndC
788800 (unsafeChr cp)
789− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
801+ (InnerLoopDecodeInit st contents (p + 1) end)
790802 12 ->
791803 Skip $
792804 transliterateOrError
@@ -795,10 +807,10 @@ decodeUtf8ArraysWithD cfm (Stream step state) =
795807 ++ "decodeUtf8ArraysWith: Invalid UTF8"
796808 ++ " codepoint encountered"
797809 )
798− (InnerLoopDecodeInit st startf (p `plusPtr` 1) end)
810+ (InnerLoopDecodeInit st contents (p + 1) end)
799811 _ ->
800812 Skip
801− (InnerLoopDecoding st startf (p `plusPtr` 1) end sv cp)
813+ (InnerLoopDecoding st contents (p + 1) end sv cp)
802814 step' _ _ (YAndC c s) = return $ Yield c s
803815 step' _ _ D = return Stop
804816
streamly.cabal−8
@@ -308,11 +308,6 @@ flag limit-build-mem
308308 manual: True
309309 default: False
310310
311−flag use-storable
312− description: Use Storable for unboxed arrays
313− manual: True
314− default: False
315−
316311-------------------------------------------------------------------------------
317312-- Common stanzas
318313-------------------------------------------------------------------------------
@@ -341,9 +336,6 @@ common compile-options
341336 if flag(use-c-malloc)
342337 cpp-options: -DUSE_C_MALLOC
343338
344− if flag(use-storable)
345− cpp-options: -DUSE_STORABLE
346−
347339 ghc-options: -Wall
348340 -Wcompat
349341 -Wunrecognised-warning-flags
test/Streamly/Test/Data/Array/Foreign.hs+5−27
@@ -13,17 +13,16 @@ module Streamly.Test.Data.Array.Foreign (main) where
1313import Data.Char (isLower)
1414import Data.List (sort)
1515import Data.Word(Word8)
16+import Foreign.Storable (peek)
17+import Streamly.Internal.Data.Unboxed (Unboxed)
1618import Test.QuickCheck (chooseInt, listOf)
17−import Streamly.Internal.Data.Unboxed (Storable, alignment, peek, poke)
1819import GHC.Ptr (plusPtr)
19−import Foreign.ForeignPtr (withForeignPtr)
2020
2121import qualified Streamly.Internal.Data.Fold as Fold
2222import qualified Streamly.Internal.Data.Array.Foreign as A
2323import qualified Streamly.Internal.Data.Array.Foreign.Type as A
2424import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
2525import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
26−import qualified Streamly.Internal.Foreign.Malloc as Malloc
2726
2827type Array = A.Array
2928
@@ -185,7 +184,7 @@ testBubbleAsc = testBubbleWith True
185184testBubbleDesc :: Property
186185testBubbleDesc = testBubbleWith False
187186
188−testByteLengthWithMA :: forall a. Storable a => a -> IO ()
187+testByteLengthWithMA :: forall a. Unboxed a => a -> IO ()
189188testByteLengthWithMA _ = do
190189 arrA <- MA.newArray 100 :: IO (MA.Array a)
191190 let arrW8 = MA.castUnsafe arrA :: MA.Array Word8
@@ -210,28 +209,6 @@ testUnsafeIndxedFromList inp =
210209 let arr = A.fromList inp
211210 in fmap (flip A.unsafeIndex arr) [0 .. (length inp - 1)] `shouldBe` inp
212211
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−
235212testAsPtrUnsafeMA :: IO ()
236213testAsPtrUnsafeMA = do
237214 arr <- MA.fromList ([0 .. 99] :: [Int])
@@ -241,6 +218,8 @@ testAsPtrUnsafeMA = do
241218
242219 sizeOfInt = sizeOf (undefined :: Int)
243220
221+ -- We need to be careful here. We assume Unboxed and Storable are compatible
222+ -- with each other. For Int, they are compatible.
244223 getList i _
245224 | i >= 100 = return []
246225 getList i p = do
@@ -306,7 +285,6 @@ main =
306285 (testByteLengthWithMA (undefined :: Int))
307286 it "testByteLengthWithMA Char"
308287 (testByteLengthWithMA (undefined :: Char))
309− it "testFromForeignPtrUnsafeMA" testFromForeignPtrUnsafeMA
310288 it "testAsPtrUnsafeMA" testAsPtrUnsafeMA
311289 it "reallocMA" reallocMA
312290 describe "breakOn" $ do
test/Streamly/Test/Prelude/Serial.hs+3−2
@@ -17,11 +17,12 @@ import Data.Int (Int64)
1717import Data.List (sort, group, intercalate)
1818import Data.Maybe ( isJust, fromJust )
1919import Data.Word (Word8)
20−import Streamly.Internal.Data.Unboxed (Storable)
2120#if __GLASGOW_HASKELL__ < 808
2221import Data.Semigroup ((<>))
2322#endif
2423import Data.Semigroup (Sum(..), getSum)
24+import Foreign.Storable (Storable)
25+import Streamly.Internal.Data.Unboxed (Unboxed)
2526import Test.Hspec.QuickCheck
2627import Test.QuickCheck
2728 ( Gen
@@ -112,7 +113,7 @@ splitOnSuffixSeq = do
112113 $ IS.splitOnSuffixSeq (A.fromList pat) FL.toList (S.fromList xs)
113114
114115splitterProperties ::
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)
116117 => a
117118 -> String
118119 -> Spec