packages feed

streamly-core 0.2.1 → 0.2.2

raw patch · 33 files changed

+5788/−3734 lines, 33 filesdep ~base

Dependency ranges changed: base

Files

Changelog.md view
@@ -1,5 +1,13 @@ # Changelog +## 0.2.2 (Jan 2024)++* Add fixities `infixr 5` for `cons` and `consM` functions.+* Fix a bug in Array `Eq` instance when the type is a sum type with+  differently sized constructors.+* lpackArraysChunksOf, compact, writeChunksWith, putChunksWith now take the+  buffer size in number of array elements instead of bytes.+ ## 0.2.1 (Dec 2023)  * Make the serialization of the unit constructor deterministic.
configure view
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.71 for streamly-core 0.2.1.+# Generated by GNU Autoconf 2.71 for streamly-core 0.2.2. # # Report bugs to <streamly@composewell.com>. #@@ -610,8 +610,8 @@ # Identity of this package. PACKAGE_NAME='streamly-core' PACKAGE_TARNAME='streamly-core'-PACKAGE_VERSION='0.2.1'-PACKAGE_STRING='streamly-core 0.2.1'+PACKAGE_VERSION='0.2.2'+PACKAGE_STRING='streamly-core 0.2.2' PACKAGE_BUGREPORT='streamly@composewell.com' PACKAGE_URL='https://streamly.composewell.com' @@ -1256,7 +1256,7 @@   # Omit some internal or obsolete options to make the list less imposing.   # This message is too long to be a string in the A/UX 3.1 sh.   cat <<_ACEOF-\`configure' configures streamly-core 0.2.1 to adapt to many kinds of systems.+\`configure' configures streamly-core 0.2.2 to adapt to many kinds of systems.  Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1318,7 +1318,7 @@  if test -n "$ac_init_help"; then   case $ac_init_help in-     short | recursive ) echo "Configuration of streamly-core 0.2.1:";;+     short | recursive ) echo "Configuration of streamly-core 0.2.2:";;    esac   cat <<\_ACEOF @@ -1404,7 +1404,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then   cat <<\_ACEOF-streamly-core configure 0.2.1+streamly-core configure 0.2.2 generated by GNU Autoconf 2.71  Copyright (C) 2021 Free Software Foundation, Inc.@@ -1622,7 +1622,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by streamly-core $as_me 0.2.1, which was+It was created by streamly-core $as_me 0.2.2, which was generated by GNU Autoconf 2.71.  Invocation command line was    $ $0$ac_configure_args_raw@@ -3832,7 +3832,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log="-This file was extended by streamly-core $as_me 0.2.1, which was+This file was extended by streamly-core $as_me 0.2.2, which was generated by GNU Autoconf 2.71.  Invocation command line was    CONFIG_FILES    = $CONFIG_FILES@@ -3888,7 +3888,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\-streamly-core config.status 0.2.1+streamly-core config.status 0.2.2 configured by $0, generated by GNU Autoconf 2.71,   with options \\"\$ac_cs_config\\" 
configure.ac view
@@ -3,7 +3,7 @@ # See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on # the macros used in this file. -AC_INIT([streamly-core], [0.2.1], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])+AC_INIT([streamly-core], [0.2.2], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])  # To suppress "WARNING: unrecognized options: --with-compiler" AC_ARG_WITH([compiler], [GHC])
+ docs/ApiChangelogs/0.2.0-0.2.2.txt view
@@ -0,0 +1,306 @@+---------------------------------+API Annotations+---------------------------------++[A] : Added+[R] : Removed+[C] : Changed+[O] : Old definition+[N] : New definition+[D] : Deprecated++---------------------------------+API diff+---------------------------------++[C] Streamly.Data.Stream+    [A] (FixityR,5)+    [A] (FixityR,5)+[C] Streamly.Data.MutArray.Generic+    [A] emptyOf :: MonadIO m => Int -> m (MutArray a)+    [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] create :: MonadIO m => Fold m a (MutArray a)+[C] Streamly.Data.MutArray+    [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+    [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)+    [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)+[C] Streamly.Data.Array.Generic+    [A] createOf :: MonadIO m => Int -> Fold m a (Array a)+    [A] create :: MonadIO m => Fold m a (Array a)+[C] Streamly.Data.Array+    [A] class Serialize a+    [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int+    [A] pinnedSerialize :: Serialize a => a -> Array Word8+    [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)+    [A] deserialize :: Serialize a => Array Word8 -> a+    [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+    [A] addSizeTo :: Serialize a => Int -> a -> Int++---------------------------------+Internal API diff+---------------------------------++[C] Streamly.Internal.Unicode.Stream+    [A] encodeUtf16le' :: Stream m Char -> Stream m Word16+    [A] decodeUtf16le' :: Stream m Word16 -> Stream m Char+[A] Streamly.Internal.FileSystem.Path+    [A] class IsPath a+    [A] Rel+    [A] File+    [A] Dir+    [A] Abs+    [A] Streamly.Internal.FileSystem.Path.IsPath+        [A] instance Streamly.Internal.FileSystem.Path.IsPath Streamly.Internal.FileSystem.Path.Path+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel Streamly.Internal.FileSystem.Path.Path)+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path)+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path)+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs Streamly.Internal.FileSystem.Path.Path)+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))+        [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))+    [A] GHC.Show.Show+        [A] instance GHC.Show.Show Streamly.Internal.FileSystem.Path.PathException+    [A] GHC.Exception.Type.Exception+        [A] instance GHC.Exception.Type.Exception Streamly.Internal.FileSystem.Path.PathException+    [A] GHC.Classes.Eq+        [A] instance GHC.Classes.Eq Streamly.Internal.FileSystem.Path.PathException+    [A] Path+        [A] Path :: Array Word8 -> Path+    [A] toString :: Path -> [Char]+    [A] toPath :: IsPath a => a -> Path+    [A] toChunk :: Path -> Array Word8+    [A] toChars :: Monad m => Path -> Stream m Char+    [A] relfile :: QuasiQuoter+    [A] reldir :: QuasiQuoter+    [A] rel :: QuasiQuoter+    [A] primarySeparator :: Char+    [A] path :: QuasiQuoter+    [A] mkRelFile :: String -> Q Exp+    [A] mkRelDir :: String -> Q Exp+    [A] mkRel :: String -> Q Exp+    [A] mkPath :: String -> Q Exp+    [A] mkFile :: String -> Q Exp+    [A] mkDir :: String -> Q Exp+    [A] mkAbsFile :: String -> Q Exp+    [A] mkAbsDir :: String -> Q Exp+    [A] mkAbs :: String -> Q Exp+    [A] isSeparator :: Char -> Bool+    [A] fromString :: MonadThrow m => [Char] -> m Path+    [A] fromPathUnsafe :: IsPath a => Path -> a+    [A] fromPath :: (IsPath a, MonadThrow m) => Path -> m a+    [A] fromChunkUnsafe :: Array Word8 -> Path+    [A] fromChunk :: MonadThrow m => Array Word8 -> m Path+    [A] fromChars :: MonadThrow m => Stream Identity Char -> m Path+    [A] file :: QuasiQuoter+    [A] extendPath :: Path -> Path -> Path+    [A] extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) => a (Dir Path) -> Rel b -> a b+    [A] dir :: QuasiQuoter+    [A] adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b+    [A] absfile :: QuasiQuoter+    [A] absdir :: QuasiQuoter+    [A] abs :: QuasiQuoter+[C] Streamly.Internal.Data.Stream+    [A] (FixityR,5)+    [A] (FixityR,5)+    [C] splitInnerBySuffix+        [O] splitInnerBySuffix :: (Monad m, Eq (f a), Monoid (f a)) => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)+        [N] splitInnerBySuffix :: Monad m => (f a -> Bool) -> (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)+    [D] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+    [A] indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+[C] Streamly.Internal.Data.MutByteArray+    [A] unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+    [A] unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+    [D] nil :: MutByteArray+    [A] empty :: MutByteArray+    [D] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+[D] Streamly.Internal.Data.MutArray.Stream+    [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))+    [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)+    [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+[C] Streamly.Internal.Data.MutArray.Generic+    [D] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)+    [D] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] emptyOf :: MonadIO m => Int -> m (MutArray a)+    [A] createWith :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] create :: MonadIO m => Fold m a (MutArray a)+[C] Streamly.Internal.Data.MutArray+    [A] SpliceState+        [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr+        [A] SpliceInitial :: s -> SpliceState s arr+        [A] SpliceFinish :: SpliceState s arr+        [A] SpliceBuffering :: s -> arr -> SpliceState s arr+    [R] MutByteArray+    [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [D] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [D] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+    [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))+    [D] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)+    [D] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)+    [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+    [A] unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+    [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+    [A] unsafeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)+    [A] toStreamWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a+    [A] toStreamRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a+    [R] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a+    [R] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a+    [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)+    [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)+    [A] revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [D] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [D] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [A] pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8+    [A] pokeAppendMay :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (Maybe (MutArray Word8))+    [A] pokeAppend :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (MutArray Word8)+    [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+    [D] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)+    [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+    [A] pinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (a, MutArray Word8)+    [A] peekUncons :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (Maybe a, MutArray Word8)+    [A] peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8+    [A] pPinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)+    [A] pCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)+    [D] nil :: MutArray a+    [A] lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+    [A] lCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+    [A] indexReaderWith :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a+    [A] indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a+    [A] growExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [A] grow :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)+    [R] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a+    [D] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a+    [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)+    [A] fromStreamN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)+    [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)+    [D] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)+    [A] fromPureStreamN :: (MonadIO m, Unbox a) => Int -> Stream Identity a -> m (MutArray a)+    [A] fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)+    [A] fromChunksRealloced :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> m (MutArray a)+    [A] fromChunksK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)+    [A] fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)+    [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)+    [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] fPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)+    [A] fCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)+    [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] empty :: MutArray a+    [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] createOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+    [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+    [A] concatWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a+    [A] concatRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a+    [A] concatRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] concat :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)+    [A] compactOnByte :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)+    [A] compactLeAs :: forall m a. (MonadIO m, Unbox a) => PinnedState -> Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [D] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering+    [A] byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool+    [A] byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering+    [A] buildChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))+    [D] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+    [A] appendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)+    [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)+    [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)+[D] Streamly.Internal.Data.Array.Stream+    [C] interposeSuffix+        [O] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+        [N] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+    [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [C] concatRev+        [O] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+        [N] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+    [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))+[C] Streamly.Internal.Data.Array.Generic+    [A] createOf :: MonadIO m => Int -> Fold m a (Array a)+    [A] create :: MonadIO m => Fold m a (Array a)+[C] Streamly.Internal.Data.Array+    [R] ArrayUnsafe+    [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b+    [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [D] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a+    [D] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a+    [C] splice+        [O] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)+        [N] splice :: MonadIO m => Array a -> Array a -> m (Array a)+    [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)+    [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)+    [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [D] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)+    [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+    [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+    [A] pinnedCompactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)+    [A] parseBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [D] nil :: Array a+    [A] lPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()+    [A] lCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()+    [A] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+    [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+    [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a+    [A] indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a+    [A] indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a+    [A] indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int+    [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)+    [R] getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a+    [D] getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a+    [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)+    [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)+    [D] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)+    [A] fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a+    [A] fromPtrN :: Int -> Ptr Word8 -> Array Word8+    [A] fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)+    [A] fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)+    [A] fromByteStr :: Ptr Word8 -> Array Word8+    [A] foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b+    [A] foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))+    [A] foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))+    [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [C] findIndicesOf+        [O] findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int+        [N] findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int+    [A] fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)+    [A] fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)+    [A] empty :: Array a+    [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+    [A] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)+    [A] compactOnByte :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)+    [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)+    [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)+    [A] byteEq :: Array a -> Array a -> Bool+    [A] byteCmp :: Array a -> Array a -> Ordering+    [A] buildChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))+    [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))+    [D] asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
docs/Changelog.md view
@@ -1,5 +1,13 @@ # Changelog +## 0.2.2 (Jan 2024)++* Add fixities `infixr 5` for `cons` and `consM` functions.+* Fix a bug in Array `Eq` instance when the type is a sum type with+  differently sized constructors.+* lpackArraysChunksOf, compact, writeChunksWith, putChunksWith now take the+  buffer size in number of array elements instead of bytes.+ ## 0.2.1 (Dec 2023)  * Make the serialization of the unit constructor deterministic.
src/DocTestDataArray.hs view
@@ -11,9 +11,12 @@  >>> import qualified Streamly.Data.Array as Array >>> import qualified Streamly.Data.Fold as Fold+>>> import qualified Streamly.Data.ParserK as ParserK >>> import qualified Streamly.Data.Stream as Stream+>>> import qualified Streamly.Data.StreamK as StreamK  For APIs that have not been released yet.  >>> import qualified Streamly.Internal.Data.Array as Array+>>> import qualified Streamly.Internal.Data.Stream as Stream -}
src/DocTestFileSystemHandle.hs view
@@ -8,7 +8,7 @@  For APIs that have not been released yet. ->>> import qualified Streamly.Internal.Data.Array as Array (writeNUnsafe)+>>> import qualified Streamly.Internal.Data.Array as Array (unsafeCreateOf) >>> import qualified Streamly.Internal.Data.Unfold as Unfold (first) >>> import qualified Streamly.Internal.FileSystem.Handle as Handle >>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)
src/Streamly/Data/Array.hs view
@@ -31,42 +31,42 @@     -- * The Array Type       Array +    -- * Pinning & Unpinning+    -- | Arrays are created unpinned by default unless pinned versions of+    -- creation APIs are used. Look for APIs with @pinned@ prefix in+    -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.+    -- If an array is to be sent to the OS without any further modification+    -- then it should be created pinned in the first place instead of pinning+    -- it later. Pinning an unpinned array has a copy overhead. OS interfacing+    -- APIs create a pinned array directly or convert an unpinned array to+    -- pinned array before sending it to the OS.+    , pin+    , unpin+    , isPinned+     -- * Construction     -- | When performance matters, the fastest way to generate an array is-    -- 'writeN'. 'IsList' and 'IsString' instances can be+    -- 'createOf'. 'IsList' and 'IsString' instances can be     -- used to conveniently construct arrays from literal values.     -- 'OverloadedLists' extension or 'fromList' can be used to construct an     -- array from a list literal.  Similarly, 'OverloadedStrings' extension or     -- 'fromList' can be used to construct an array from a string literal. -    -- Pure List APIs+    -- ** From Stream+    , createOf+    , create+    , writeLastN    -- drop old (ring buffer)++    -- ** From List     , fromListN     , fromList -    -- Monadic APIs-    , writeN      -- drop new-    , write       -- full buffer-    , writeLastN    -- drop old (ring buffer)--    -- * Conversion+    -- * To List     -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a     -- list.     , toList -    -- * Pinning & Unpinning-    -- | Arrays are created unpinned by default unless pinned versions of-    -- creation APIs are used. Look for APIs with @pinned@ prefix in-    -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.-    -- If an array is to be sent to the OS without any further modification-    -- then it should be created pinned in the first place instead of pinning-    -- it later. Pinning an unpinned array has a copy overhead. OS interfacing-    -- APIs create a pinned array directly or convert an unpinned array to-    -- pinned array before sending it to the OS.-    , pin-    , unpin-    , isPinned--    -- * Streams+    -- * To Stream     , read     , readRev @@ -90,6 +90,10 @@     -- * Re-exports     , Unbox (..)     , Serialize(..)++    -- * Deprecated+    , writeN      -- drop new+    , write       -- full buffer     ) where @@ -122,7 +126,7 @@ -- -- Convert array to stream, transform, and fold back to array: ----- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.write+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.create -- >>> amap (+1) (Array.fromList [1,2,3::Int]) -- fromList [2,3,4] --@@ -154,7 +158,7 @@ -- -- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int -- >>> generally = Stream.morphInner (return . runIdentity)--- >>> Stream.fold Array.write (generally pure :: Stream IO Int)+-- >>> Stream.fold Array.create (generally pure :: Stream IO Int) -- fromList [1,2,3] -- -- == Programming Tips
src/Streamly/Data/Array/Generic.hs view
@@ -15,34 +15,37 @@     ( Array      -- * Construction-    , A.fromListN-    , A.fromList+    , fromListN+    , fromList      -- MonadicAPIs-    , A.writeN-    , A.write+    , createOf+    , create      -- * Conversion-    , A.toList+    , toList      -- * Streams-    , A.read-    , A.readRev+    , read+    , readRev      -- * Unfolds-    , A.reader+    , reader     -- , A.readerRev      -- * Random Access-    , A.length-    , A.getIndex+    , length+    , getIndex      -- -- * Folding Arrays     -- , A.streamFold     -- , A.fold++    -- * Deprecated+    , writeN+    , write     ) where -import Streamly.Internal.Data.Array.Generic (Array)--import qualified Streamly.Internal.Data.Array.Generic as A+import Streamly.Internal.Data.Array.Generic+import Prelude hiding (length, read)
src/Streamly/Data/MutArray.hs view
@@ -32,14 +32,14 @@     -- * Construction      -- Uninitialized Arrays-    , new-    , pinnedNew+    , emptyOf+    , pinnedEmptyOf      -- From containers     , fromListN     , fromList-    , writeN      -- drop new-    , write       -- full buffer+    , createOf+    , create     -- writeLastN      -- * Pinning & Unpinning@@ -51,8 +51,8 @@     , snoc      -- * Appending streams-    , writeAppendN -- XXX Rename to writeSnocN?-    , writeAppend -- XXX Rename to writeSnoc?+    , appendN+    , append      -- * Inplace mutation     , putIndex@@ -88,6 +88,12 @@      -- * Deprecated     , newPinned+    , new+    , pinnedNew+    , writeN+    , write+    , writeAppendN+    , writeAppend     ) where 
src/Streamly/Data/MutArray/Generic.hs view
@@ -23,11 +23,11 @@       MutArray      -- * Construction-    , new+    , emptyOf     , fromListN     , fromList-    , writeN-    , write+    , createOf+    , create      -- * Appending elements     , snoc@@ -56,6 +56,11 @@      -- * Size     , length++    -- * Deprecated+    , new+    , writeN+    , write     ) where 
src/Streamly/Internal/Data/Array.hs view
@@ -2,7 +2,6 @@ -- | -- Module      : Streamly.Internal.Data.Array -- Copyright   : (c) 2019 Composewell Technologies--- -- License     : BSD3 -- Maintainer  : streamly@composewell.com -- Stability   : experimental@@ -20,30 +19,21 @@       module Streamly.Internal.Data.Array.Type      -- * Construction-    -- Stream Folds-    , fromStreamN-    , fromStream-     -- Monadic Folds     , writeLastN -    -- * Unfolds-    , reader-    , readerUnsafe-    , producer -- experimental-     -- * Random Access     -- , (!!)     , getIndex     , getIndexRev-    , last           -- XXX getIndexLast?-    , getIndices-    , getIndicesFromThenTo+    , last           -- XXX getLastIndex?     -- , getIndicesFrom    -- read from a given position to the end of file     -- , getIndicesUpto    -- read from beginning up to the given position     -- , getIndicesFromTo     -- , getIndicesFromRev  -- read from a given position to the beginning of file     -- , getIndicesUptoRev  -- read from end to the given position in file+    , indexReader+    , indexReaderFromThenTo      -- * Size     , null@@ -51,6 +41,8 @@     -- * Search     , binarySearch     , findIndicesOf+    -- getIndicesOf+    , indexFinder -- see splitOn     -- , findIndexOf     -- , find @@ -63,8 +55,8 @@     -- * Subarrays     , getSliceUnsafe     -- , getSlice-    , genSlicesFromLen-    , getSlicesFromLen+    , sliceIndexerFromLen+    , slicerFromLen     , splitOn      -- * Streaming Operations@@ -74,11 +66,34 @@     , streamFold     , fold +    -- * Stream of Arrays++    -- XXX these are probably not very useful to have in this module as we can+    -- express these idiomatically using streams.+    , interpose+    , interposeSuffix+    , intercalateSuffix++    , compactLE+    , pinnedCompactLE+    , compactOnByte+    , compactOnByteSuffix++    , foldBreakChunks+    , foldChunks+    , foldBreakChunksK+    , parseBreakChunksK+     -- * Serialization     , encodeAs     , serialize     , pinnedSerialize     , deserialize++    -- * Deprecated+    , genSlicesFromLen+    , getSlicesFromLen+    , getIndices     ) where @@ -86,37 +101,43 @@ #include "inline.hs" #include "ArrayMacros.h" -import Control.Monad (when) import Control.Monad.IO.Class (MonadIO(..))+-- import Data.Bifunctor (first)+-- import Data.Either (fromRight) import Data.Functor.Identity (Identity) import Data.Proxy (Proxy(..)) import Data.Word (Word8) import Foreign.C.String (CString) import Foreign.Ptr (castPtr) import Foreign.Storable (Storable)+import GHC.Types (SPEC(..)) import Streamly.Internal.Data.Unbox (Unbox(..)) import Prelude hiding (length, null, last, map, (!!), read, concat) -import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..), MutByteArray) import Streamly.Internal.Data.Serialize.Type (Serialize) import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Producer.Type (Producer(..))-import Streamly.Internal.Data.Stream (Stream)-import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..))+import Streamly.Internal.Data.Parser (Parser(..), Initial(..), ParseError(..))+import Streamly.Internal.Data.Stream (Stream(..))+import Streamly.Internal.Data.StreamK (StreamK)+import Streamly.Internal.Data.SVar.Type (adaptState, defState)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3Fused'(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Streamly.Internal.System.IO (unsafeInlineIO) +import qualified Streamly.Internal.Data.Fold.Type as Fold import qualified Streamly.Internal.Data.Serialize.Type as Serialize import qualified Streamly.Internal.Data.MutByteArray.Type as MBA import qualified Streamly.Internal.Data.MutArray as MA-import qualified Streamly.Internal.Data.Array.Type as A import qualified Streamly.Internal.Data.Fold as FL-import qualified Streamly.Internal.Data.Producer.Type as Producer-import qualified Streamly.Internal.Data.Producer as Producer import qualified Streamly.Internal.Data.Ring as RB+import qualified Streamly.Internal.Data.Parser as Parser+-- import qualified Streamly.Internal.Data.ParserK as ParserK import qualified Streamly.Internal.Data.Stream as D import qualified Streamly.Internal.Data.Stream as Stream+import qualified Streamly.Internal.Data.StreamK as StreamK import qualified Streamly.Internal.Data.Unfold as Unfold+import qualified Prelude  import Streamly.Internal.Data.Array.Type @@ -146,81 +167,9 @@ -- pressure to GC.  ---------------------------------------------------------------------------------- Construction------------------------------------------------------------------------------------ | Create an 'Array' from the first N elements of a stream. The array is--- allocated to size N, if the stream terminates before N elements then the--- array may hold less than N elements.------ /Pre-release/-{-# INLINE fromStreamN #-}-fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)-fromStreamN n m = do-    when (n < 0) $ error "writeN: negative write count specified"-    A.fromStreamDN n m---- | Create an 'Array' from a stream. This is useful when we want to create a--- single array from a stream of unknown size. 'writeN' is at least twice--- as efficient when the size is already known.------ Note that if the input stream is too large memory allocation for the array--- may fail.  When the stream size is not known, `chunksOf` followed by--- processing of indvidual arrays in the resulting stream should be preferred.------ /Pre-release/-{-# INLINE fromStream #-}-fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)-fromStream = Stream.fold A.write--- write m = A.fromStreamD $ D.fromStreamK m--------------------------------------------------------------------------------- -- Elimination ------------------------------------------------------------------------------- -{-# INLINE_NORMAL producer #-}-producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a-producer =-    Producer.translate A.unsafeThaw A.unsafeFreeze-        $ MA.producerWith (return . unsafeInlineIO)---- | Unfold an array into a stream.----{-# INLINE_NORMAL reader #-}-reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a-reader = Producer.simplify producer---- | Unfold an array into a stream, does not check the end of the array, the--- user is responsible for terminating the stream within the array bounds. For--- high performance application where the end condition can be determined by--- a terminating fold.------ Written in the hope that it may be faster than "read", however, in the case--- for which this was written, "read" proves to be faster even though the core--- generated with unsafeRead looks simpler.------ /Pre-release/----{-# INLINE_NORMAL readerUnsafe #-}-readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a-readerUnsafe = Unfold step inject-    where--    inject (Array contents start end) =-        return (ArrayUnsafe contents end start)--    {-# INLINE_LATE step #-}-    step (ArrayUnsafe contents end p) = do-            -- unsafeInlineIO allows us to run this in Identity monad for pure-            -- toList/foldr case which makes them much faster due to not-            -- accumulating the list and fusing better with the pure consumers.-            ---            -- This should be safe as the array contents are guaranteed to be-            -- evaluated/written to before we peek at them.-            let !x = unsafeInlineIO $ peekAt p contents-            let !p1 = INDEX_NEXT(p,a)-            return $ D.Yield x (ArrayUnsafe contents end p1)- -- | -- -- >>> null arr = Array.byteLength arr == 0@@ -228,7 +177,7 @@ -- /Pre-release/ {-# INLINE null #-} null :: Array a -> Bool-null arr = A.byteLength arr == 0+null arr = byteLength arr == 0  -- | Like 'getIndex' but indexes the array in reverse from the end. --@@ -256,6 +205,8 @@ -- Folds with Array as the container ------------------------------------------------------------------------------- +-- XXX We should generate this from Ring.+ -- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input -- stream to an 'Array'. --@@ -264,7 +215,7 @@        (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a) writeLastN n     | n <= 0 = fmap (const mempty) FL.drain-    | otherwise = A.unsafeFreeze <$> Fold step initial done done+    | otherwise = unsafeFreeze <$> Fold step initial done done      where @@ -277,11 +228,9 @@          in fmap f $ liftIO $ RB.new n      done (Tuple3Fused' rb rh i) = do-        arr <- liftIO $ MA.new n-        foldFunc i rh snoc' arr rb--    -- XXX We should write a read unfold for ring.-    snoc' b a = liftIO $ MA.snocUnsafe b a+        arr <- MA.new n+        -- XXX We should write a read unfold for ring.+        foldFunc i rh MA.snocUnsafe arr rb      foldFunc i         | i < n = RB.unsafeFoldRingM@@ -307,19 +256,26 @@ -- compared to streams by using SIMD instructions. -- We can also return a bit array instead. +-- Can use SIMD.+ -- | Perform a linear search to find all the indices where a given element is -- present in an array. -- -- /Unimplemented/-findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int-findIndicesOf = undefined+indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int+indexFinder = undefined +-- |+-- /Unimplemented/+findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int+findIndicesOf p = Stream.unfold (indexFinder p)+ {- findIndexOf :: (a -> Bool) -> Array a -> Maybe Int-findIndexOf p = Unfold.fold Fold.one . Stream.unfold (findIndicesOf p)+findIndexOf p = Unfold.fold Fold.one . Stream.unfold (indexFinder p)  find :: (a -> Bool) -> Array a -> Bool-find = Unfold.fold Fold.null . Stream.unfold (findIndicesOf p)+find = Unfold.fold Fold.null . Stream.unfold (indexFinder p) -}  -------------------------------------------------------------------------------@@ -361,29 +317,45 @@     (a -> Bool) -> Array a -> Stream m (Array a) splitOn predicate arr =     fmap (\(i, len) -> getSliceUnsafe i len arr)-        $ D.sliceOnSuffix predicate (A.toStreamD arr)+        $ D.indexOnSuffix predicate (read arr) +{-# INLINE sliceIndexerFromLen #-}+sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> Unfold m (Array a) (Int, Int)+sliceIndexerFromLen from len =+    Unfold.lmap unsafeThaw (MA.sliceIndexerFromLen from len)++{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-} {-# INLINE genSlicesFromLen #-} genSlicesFromLen :: forall m a. (Monad m, Unbox a)     => Int -- ^ from index     -> Int -- ^ length of the slice     -> Unfold m (Array a) (Int, Int)-genSlicesFromLen from len =-    Unfold.lmap A.unsafeThaw (MA.genSlicesFromLen from len)+genSlicesFromLen = sliceIndexerFromLen  -- | Generate a stream of slices of specified length from an array, starting -- from the supplied array index. The last slice may be shorter than the -- requested length. -- -- /Pre-release//+{-# INLINE slicerFromLen #-}+slicerFromLen :: forall m a. (Monad m, Unbox a)+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> Unfold m (Array a) (Array a)+slicerFromLen from len =+    fmap unsafeFreeze+        $ Unfold.lmap unsafeThaw (MA.slicerFromLen from len)++{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-} {-# INLINE getSlicesFromLen #-} getSlicesFromLen :: forall m a. (Monad m, Unbox a)     => Int -- ^ from index     -> Int -- ^ length of the slice     -> Unfold m (Array a) (Array a)-getSlicesFromLen from len =-    fmap A.unsafeFreeze-        $ Unfold.lmap A.unsafeThaw (MA.getSlicesFromLen from len)+getSlicesFromLen = slicerFromLen  ------------------------------------------------------------------------------- -- Random reads and writes@@ -413,19 +385,25 @@ -- @ -- read = --      let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo---       in Unfold.lmap f (getIndices arr)+--       in Unfold.lmap f (indexReader arr) -- -- readRev = --      let i = length arr - 1---       in Unfold.lmap f (getIndicesFromThenTo i (i - 1) 0)+--       in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0) -- @ -- -- /Pre-release/+{-# INLINE indexReader #-}+indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a+indexReader m =+    let unf = MA.indexReaderWith (return . unsafeInlineIO) m+     in Unfold.lmap unsafeThaw unf++-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold+{-# DEPRECATED getIndices "Please use getIndices instead." #-} {-# INLINE getIndices #-} getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a-getIndices m =-    let unf = MA.getIndicesD (return . unsafeInlineIO) m-     in Unfold.lmap A.unsafeThaw unf+getIndices = indexReader  -- | Unfolds @(from, then, to, array)@ generating a finite stream whose first -- element is the array value from the index @from@ and the successive elements@@ -441,9 +419,9 @@ -- @ -- -- /Unimplemented/-{-# INLINE getIndicesFromThenTo #-}-getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a-getIndicesFromThenTo = undefined+{-# INLINE indexReaderFromThenTo #-}+indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a+indexReaderFromThenTo = undefined  ------------------------------------------------------------------------------- -- Transform via stream operations@@ -460,7 +438,7 @@ {-# INLINE runPipe #-} runPipe :: (MonadIO m, Unbox a, Unbox b)     => Pipe m a b -> Array a -> m (Array b)-runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)+runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr) -}  -- XXX For transformations that cannot change the number of elements e.g. "map"@@ -474,7 +452,7 @@ streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)     => (Stream m a -> Stream m b) -> Array a -> m (Array b) streamTransform f arr =-    Stream.fold (A.writeWith (length arr)) $ f (A.read arr)+    Stream.fold (createWith (length arr)) $ f (read arr)  ------------------------------------------------------------------------------- -- Casts@@ -508,7 +486,7 @@ -- cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b) cast arr =-    let len = A.byteLength arr+    let len = byteLength arr         r = len `mod` SIZE_OF(b)      in if r /= 0         then Nothing@@ -526,9 +504,9 @@ -- asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b asCStringUnsafe arr act = do-    let arr1 = asBytes arr <> A.fromList [0]-    -- asPtrUnsafe makes sure the array is pinned-    asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)+    let arr1 = asBytes arr <> fromList [0]+    -- unsafePinnedAsPtr makes sure the array is pinned+    unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)  ------------------------------------------------------------------------------- -- Folds@@ -541,14 +519,14 @@ -- /Pre-release/ {-# INLINE fold #-} fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b-fold f arr = Stream.fold f (A.read arr)+fold f arr = Stream.fold f (read arr)  -- | Fold an array using a stream fold operation. -- -- /Pre-release/ {-# INLINE streamFold #-} streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b-streamFold f arr = f (A.read arr)+streamFold f arr = f (read arr)  -------------------------------------------------------------------------------- -- Serialization@@ -593,3 +571,448 @@         Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)     assertM(off == arrStart + lenArr)     pure val++-------------------------------------------------------------------------------+-- Streams of Arrays+-------------------------------------------------------------------------------++-- TODO: efficiently compare two streams of arrays. Two streams can have chunks+-- of different sizes, we can handle that in the stream comparison abstraction.+-- This could be useful e.g. to fast compare whether two files differ.++-- | Insert the given element between arrays and flatten.+--+-- >>> interpose x = Stream.interpose x Array.reader+--+{-# INLINE interpose #-}+interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+interpose x = D.interpose x reader++data FlattenState s =+      OuterLoop s+    | InnerLoop s !MutByteArray !Int !Int++-- | Insert the given element after each array and flatten. This is similar to+-- unlines.+--+-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader+--+{-# INLINE_NORMAL interposeSuffix #-}+interposeSuffix :: forall m a. (Monad m, Unbox a)+    => a -> Stream m (Array a) -> Stream m a+-- This does not require MonadIO constraint.+-- interposeSuffix x = D.interposeSuffix x reader+interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (OuterLoop st) = do+        r <- step (adaptState gst) st+        return $ case r of+            D.Yield Array{..} s ->+                D.Skip (InnerLoop s arrContents arrStart arrEnd)+            D.Skip s -> D.Skip (OuterLoop s)+            D.Stop -> D.Stop++    step' _ (InnerLoop st _ p end) | p == end =+        return $ D.Yield sep $ OuterLoop st++    step' _ (InnerLoop st contents p end) = do+        x <- pure $ unsafeInlineIO $ peekAt p contents+        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)++-- | Insert the given array after each array and flatten.+--+-- >>> intercalateSuffix = Stream.intercalateSuffix Array.reader+--+{-# INLINE intercalateSuffix #-}+intercalateSuffix :: (Monad m, Unbox a)+    => Array a -> Stream m (Array a) -> Stream m a+intercalateSuffix = D.intercalateSuffix reader++-- | @compactLE n@ coalesces adjacent arrays in the input stream+-- only if the combined size would be less than or equal to n.+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE_NORMAL compactLE #-}+compactLE :: (MonadIO m, Unbox a)+    => Int -> Stream m (Array a) -> Stream m (Array a)+compactLE n stream =+    D.map unsafeFreeze $ MA.compactLE n $ D.map unsafeThaw stream++-- | Pinned version of 'compactLE'.+{-# INLINE_NORMAL pinnedCompactLE #-}+pinnedCompactLE :: (MonadIO m, Unbox a)+    => Int -> Stream m (Array a) -> Stream m (Array a)+pinnedCompactLE n stream =+    D.map unsafeFreeze $ MA.pinnedCompactLE n $ D.map unsafeThaw stream++-- | Split a stream of arrays on a given separator byte, dropping the separator+-- and coalescing all the arrays between two separators into a single array.+--+{-# INLINE compactOnByte #-}+compactOnByte+    :: (MonadIO m)+    => Word8+    -> Stream m (Array Word8)+    -> Stream m (Array Word8)+compactOnByte byte =+    fmap unsafeFreeze . MA.compactOnByte byte . fmap unsafeThaw++-- | Like 'compactOnByte' considers the separator in suffix position instead of+-- infix position.+{-# INLINE compactOnByteSuffix #-}+compactOnByteSuffix+    :: (MonadIO m)+    => Word8+    -> Stream m (Array Word8)+    -> Stream m (Array Word8)+compactOnByteSuffix byte =+    fmap unsafeFreeze . MA.compactOnByteSuffix byte . fmap unsafeThaw++-------------------------------------------------------------------------------+-- Folding Streams of Arrays+-------------------------------------------------------------------------------++-- XXX This should not be used for breaking a stream as the D.cons used in+-- reconstructing the stream could be very bad for performance. This can only+-- be useful in folding without breaking.+{-# INLINE_NORMAL foldBreakChunks #-}+foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) =>+    Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))+foldBreakChunks (Fold fstep initial _ final) stream@(Stream step state) = do+    res <- initial+    case res of+        Fold.Partial fs -> go SPEC state fs+        Fold.Done fb -> return $! (fb, stream)++    where++    {-# INLINE go #-}+    go !_ st !fs = do+        r <- step defState st+        case r of+            Stream.Yield (Array contents start end) s ->+                let fp = Tuple' end contents+                 in goArray SPEC s fp start fs+            Stream.Skip s -> go SPEC s fs+            Stream.Stop -> do+                b <- final fs+                return (b, D.nil)++    goArray !_ s (Tuple' end _) !cur !fs+        | cur == end = do+            go SPEC s fs+    goArray !_ st fp@(Tuple' end contents) !cur !fs = do+        x <- liftIO $ peekAt cur contents+        res <- fstep fs x+        let next = INDEX_NEXT(cur,a)+        case res of+            Fold.Done b -> do+                let arr = Array contents next end+                return $! (b, D.cons arr (D.Stream step st))+            Fold.Partial fs1 -> goArray SPEC st fp next fs1++-- This may be more robust wrt fusion compared to unfoldMany?++-- | Fold a stream of arrays using a 'Fold'. This is equivalent to the+-- following:+--+-- >>> foldChunks f = Stream.fold f . Stream.unfoldMany Array.reader+--+foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b+foldChunks f s = fmap fst (foldBreakChunks f s)+-- foldStream f = Stream.fold f . Stream.unfoldMany reader++-- | Fold a stream of arrays using a 'Fold' and return the remaining stream.+--+-- The following alternative to this function allows composing the fold using+-- the parser Monad:+--+-- @+-- foldBreakStreamK f s =+--       fmap (first (fromRight undefined))+--     $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s+-- @+--+-- We can compare perf and remove this one or define it in terms of that.+--+foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) =>+    Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))+{-+foldBreakChunksK f s =+      fmap (first (fromRight undefined))+    $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s+-}+foldBreakChunksK (Fold fstep initial _ final) stream = do+    res <- initial+    case res of+        Fold.Partial fs -> go fs stream+        Fold.Done fb -> return (fb, stream)++    where++    {-# INLINE go #-}+    go !fs st = do+        let stop = (, StreamK.nil) <$> final fs+            single a = yieldk a StreamK.nil+            yieldk (Array contents start end) r =+                let fp = Tuple' end contents+                 in goArray fs r fp start+         in StreamK.foldStream defState yieldk single stop st++    goArray !fs st (Tuple' end _) !cur+        | cur == end = do+            go fs st+    goArray !fs st fp@(Tuple' end contents) !cur = do+        x <- liftIO $ peekAt cur contents+        res <- fstep fs x+        let next = INDEX_NEXT(cur,a)+        case res of+            Fold.Done b -> do+                let arr = Array contents next end+                return $! (b, StreamK.cons arr st)+            Fold.Partial fs1 -> goArray fs1 st fp next++{-+-- This can be generalized to any type provided it can be unfolded to a stream+-- and it can be combined using a semigroup operation.+--+{-# INLINE_NORMAL parseBreakD #-}+parseBreakD ::+       forall m a b. (MonadIO m, MonadThrow m, Unbox a)+    => PRD.Parser a m b+    -> D.Stream m (Array.Array a)+    -> m (b, D.Stream m (Array.Array a))+parseBreakD+    (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do++    res <- initial+    case res of+        PRD.IPartial s -> go SPEC state (List []) s+        PRD.IDone b -> return (b, stream)+        PRD.IError err -> throwM $ ParseError err++    where++    -- "backBuf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    go !_ st backBuf !pst = do+        r <- step defState st+        case r of+            D.Yield (Array contents start end) s ->+                gobuf SPEC s backBuf+                    (Tuple' end contents) start pst+            D.Skip s -> go SPEC s backBuf pst+            D.Stop -> do+                b <- extract pst+                return (b, D.nil)++    -- Use strictness on "cur" to keep it unboxed+    gobuf !_ s backBuf (Tuple' end _) !cur !pst+        | cur == end = do+            go SPEC s backBuf pst+    gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do+        x <- liftIO $ peekByteIndex contents cur+        pRes <- pstep pst x+        let next = INDEX_NEXT(cur,a)+        case pRes of+            PR.Partial 0 pst1 ->+                 gobuf SPEC s (List []) fp next pst1+            PR.Partial n pst1 -> do+                assert (n <= Prelude.length (x:getList backBuf)) (return ())+                let src0 = Prelude.take n (x:getList backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                let !(Array cont1 start end1) = src+                    fp1 = Tuple' end1 cont1+                gobuf SPEC s (List []) fp1 start pst1+            PR.Continue 0 pst1 ->+                gobuf SPEC s (List (x:getList backBuf)) fp next pst1+            PR.Continue n pst1 -> do+                assert (n <= Prelude.length (x:getList backBuf)) (return ())+                let (src0, buf1) = splitAt n (x:getList backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                let !(Array cont1 start end1) = src+                    fp1 = Tuple' end1 cont1+                gobuf SPEC s (List buf1) fp1 start pst1+            PR.Done 0 b -> do+                let arr = Array contents next end+                return (b, D.cons arr (D.Stream step s))+            PR.Done n b -> do+                assert (n <= Prelude.length (x:getList backBuf)) (return ())+                let src0 = Prelude.take n (x:getList backBuf)+                    -- XXX create the array in reverse instead+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    -- XXX Use StreamK to avoid adding arbitrary layers of+                    -- constructors every time.+                    str = D.cons arr0 (D.cons arr1 (D.Stream step s))+                return (b, str)+            PR.Error err -> throwM $ ParseError err+-}++-- | Parse an array stream using the supplied 'Parser'.  Returns the parse+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.+--+-- The following alternative to this function allows composing the parser using+-- the parser Monad:+--+-- >>> parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)+--+-- We can compare perf and remove this one or define it in terms of that.+--+-- /Internal/+--+{-# INLINE_NORMAL parseBreakChunksK #-}+parseBreakChunksK ::+       forall m a b. (MonadIO m, Unbox a)+    => Parser a m b+    -> StreamK m (Array a)+    -> m (Either ParseError b, StreamK m (Array a))+-- parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)+parseBreakChunksK (Parser pstep initial extract) stream = do+    res <- initial+    case res of+        IPartial s -> go s stream []+        IDone b -> return (Right b, stream)+        IError err -> return (Left (ParseError err), stream)++    where++    -- "backBuf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    go !pst st backBuf = do+        let stop = goStop pst backBuf -- (, K.nil) <$> extract pst+            single a = yieldk a StreamK.nil+            yieldk arr r = goArray pst backBuf r arr+         in StreamK.foldStream defState yieldk single stop st++    -- Use strictness on "cur" to keep it unboxed+    goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf+    goArray !pst backBuf st (Array contents cur end) = do+        x <- liftIO $ peekAt cur contents+        pRes <- pstep pst x+        let next = INDEX_NEXT(cur,a)+        case pRes of+            Parser.Partial 0 s ->+                 goArray s [] st (Array contents next end)+            Parser.Partial n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let src0 = Prelude.take n (x:backBuf)+                    arr0 = fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goArray s [] st src+            Parser.Continue 0 s ->+                goArray s (x:backBuf) st (Array contents next end)+            Parser.Continue n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let (src0, buf1) = Prelude.splitAt n (x:backBuf)+                    arr0 = fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goArray s buf1 st src+            Parser.Done 0 b -> do+                let arr = Array contents next end+                return (Right b, StreamK.cons arr st)+            Parser.Done n b -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let src0 = Prelude.take n (x:backBuf)+                    -- XXX Use fromListRevN once implemented+                    -- arr0 = A.fromListRevN n src0+                    arr0 = fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    str = StreamK.cons arr0 (StreamK.cons arr1 st)+                return (Right b, str)+            Parser.Error err -> do+                let n = Prelude.length backBuf+                    arr0 = fromListN n (Prelude.reverse backBuf)+                    arr1 = Array contents cur end+                    str = StreamK.cons arr0 (StreamK.cons arr1 stream)+                return (Left (ParseError err), str)++    -- This is a simplified goArray+    goExtract !pst backBuf (Array _ cur end)+        | cur == end = goStop pst backBuf+    goExtract !pst backBuf (Array contents cur end) = do+        x <- liftIO $ peekAt cur contents+        pRes <- pstep pst x+        let next = INDEX_NEXT(cur,a)+        case pRes of+            Parser.Partial 0 s ->+                 goExtract s [] (Array contents next end)+            Parser.Partial n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let src0 = Prelude.take n (x:backBuf)+                    arr0 = fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goExtract s [] src+            Parser.Continue 0 s ->+                goExtract s backBuf (Array contents next end)+            Parser.Continue n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let (src0, buf1) = Prelude.splitAt n (x:backBuf)+                    arr0 = fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goExtract s buf1 src+            Parser.Done 0 b -> do+                let arr = Array contents next end+                return (Right b, StreamK.fromPure arr)+            Parser.Done n b -> do+                assert (n <= Prelude.length backBuf) (return ())+                let src0 = Prelude.take n backBuf+                    -- XXX Use fromListRevN once implemented+                    -- arr0 = A.fromListRevN n src0+                    arr0 = fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    str = StreamK.cons arr0 (StreamK.fromPure arr1)+                return (Right b, str)+            Parser.Error err -> do+                let n = Prelude.length backBuf+                    arr0 = fromListN n (Prelude.reverse backBuf)+                    arr1 = Array contents cur end+                    str = StreamK.cons arr0 (StreamK.cons arr1 stream)+                return (Left (ParseError err), str)++    -- This is a simplified goExtract+    {-# INLINE goStop #-}+    goStop !pst backBuf = do+        pRes <- extract pst+        case pRes of+            Parser.Partial _ _ -> error "Bug: parseBreak: Partial in extract"+            Parser.Continue 0 s ->+                goStop s backBuf+            Parser.Continue n s -> do+                assert (n <= Prelude.length backBuf) (return ())+                let (src0, buf1) = Prelude.splitAt n backBuf+                    arr = fromListN n (Prelude.reverse src0)+                goExtract s buf1 arr+            Parser.Done 0 b ->+                return (Right b, StreamK.nil)+            Parser.Done n b -> do+                assert (n <= Prelude.length backBuf) (return ())+                let src0 = Prelude.take n backBuf+                    -- XXX Use fromListRevN once implemented+                    -- arr0 = A.fromListRevN n src0+                    arr0 = fromListN n (Prelude.reverse src0)+                return (Right b, StreamK.fromPure arr0)+            Parser.Error err -> do+                let n = Prelude.length backBuf+                    arr0 = fromListN n (Prelude.reverse backBuf)+                return (Left (ParseError err), StreamK.fromPure arr0)
src/Streamly/Internal/Data/Array/Generic.hs view
@@ -11,8 +11,8 @@      -- * Construction     , nil-    , writeN-    , write+    , createOf+    , create     , writeWith     , writeLastN @@ -44,6 +44,10 @@     , getIndex     , getSliceUnsafe     , strip++    -- * Deprecated+    , writeN+    , write     ) where @@ -104,21 +108,33 @@ -- Construction - Folds ------------------------------------------------------------------------------- -{-# INLINE_NORMAL writeN #-}+{-# INLINE_NORMAL createOf #-}+createOf :: MonadIO m => Int -> Fold m a (Array a)+createOf = fmap unsafeFreeze <$> MArray.createOf++-- XXX Deprecate in major+-- {-# DEPRECATED writeN "Please use createOf instead." #-}+{-# INLINE writeN #-} writeN :: MonadIO m => Int -> Fold m a (Array a)-writeN = fmap unsafeFreeze <$> MArray.writeN+writeN = createOf  {-# INLINE_NORMAL writeWith #-} writeWith :: MonadIO m => Int -> Fold m a (Array a)-writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount+writeWith elemCount = unsafeFreeze <$> MArray.createWith elemCount  -- | Fold the whole input to a single array. -- -- /Caution! Do not use this on infinite streams./ --+{-# INLINE create #-}+create :: MonadIO m => Fold m a (Array a)+create = fmap unsafeFreeze MArray.create++-- XXX Deprecate in major+-- {-# DEPRECATED write "Please use create instead." #-} {-# INLINE write #-} write :: MonadIO m => Fold m a (Array a)-write = fmap unsafeFreeze MArray.write+write = create  fromPureStream :: Stream Identity a -> Array a fromPureStream x =
src/Streamly/Internal/Data/Array/Stream.hs view
@@ -12,6 +12,7 @@ -- keep this as a separate module and release it. -- module Streamly.Internal.Data.Array.Stream+{-# DEPRECATED "Please use \"Streamly.Internal.Data.Array\" instead." #-}     (     -- * Creation       Array.chunksOf@@ -19,13 +20,13 @@     , Array.bufferChunks      -- * Flattening to elements-    , concat+    , Array.concat     , Array.flattenArrays-    , concatRev+    , Array.concatRev     , Array.flattenArraysRev-    , interpose-    , interposeSuffix-    , intercalateSuffix+    , Array.interpose+    , Array.interposeSuffix+    , Array.intercalateSuffix     , unlines      -- * Elimination@@ -36,10 +37,11 @@     -- could also be implemented using parseBreak.     , foldBreak     , foldBreakD-    -- The byte level parseBreak cannot work efficiently. Because the stream-    -- will have to be a StreamK for backtracking, StreamK at byte level would-    -- not be efficient.-    , parseBreak -- StreamK.parseBreakChunks+    -- This is chunked parseBreak. A byte level parseBreak cannot work+    -- efficiently. Because the stream will have to be a StreamK for+    -- backtracking, StreamK at byte level would not be efficient.+    -- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)+    , parseBreak     -- , parseBreakD     -- , foldManyChunks     -- , parseManyChunks@@ -77,6 +79,8 @@ import Data.Bifunctor (second) import Control.Exception (assert) import Control.Monad.IO.Class (MonadIO(..))+-- import Data.Bifunctor (first)+-- import Data.Either (fromRight) import Data.Proxy (Proxy(..)) import Data.Word (Word8) import Streamly.Internal.Data.Unbox (Unbox(..))@@ -92,17 +96,13 @@ import Streamly.Internal.Data.Stream (Stream) import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream) import Streamly.Internal.Data.SVar.Type (adaptState, defState)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..)) -import qualified Streamly.Data.Fold as FL import qualified Streamly.Internal.Data.Array as A import qualified Streamly.Internal.Data.Array as Array-import qualified Streamly.Internal.Data.MutArray.Type as MA-import qualified Streamly.Internal.Data.MutArray.Stream as AS-import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..)) import qualified Streamly.Internal.Data.Parser as PR import qualified Streamly.Internal.Data.Parser as PRD     (Parser(..), Initial(..))+-- import qualified Streamly.Internal.Data.ParserK as ParserK import qualified Streamly.Internal.Data.Stream as D import qualified Streamly.Internal.Data.StreamK as K @@ -110,109 +110,18 @@ -- most places.  ---------------------------------------------------------------------------------- Append------------------------------------------------------------------------------------ XXX efficiently compare two streams of arrays. Two streams can have chunks--- of different sizes, we can handle that in the stream comparison abstraction.--- This could be useful e.g. to fast compare whether two files differ.---- | Convert a stream of arrays into a stream of their elements.------ Same as the following:------ > concat = Stream.unfoldMany Array.read------ @since 0.7.0-{-# INLINE concat #-}-concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a--- concat m = fromStreamD $ A.flattenArrays (toStreamD m)--- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)-concat = D.unfoldMany A.reader---- | Convert a stream of arrays into a stream of their elements reversing the--- contents of each array before flattening.------ > concatRev = Stream.unfoldMany Array.readerRev------ @since 0.7.0-{-# INLINE concatRev #-}-concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a--- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)-concatRev = D.unfoldMany A.readerRev--------------------------------------------------------------------------------- -- Intersperse and append ------------------------------------------------------------------------------- --- | Flatten a stream of arrays after inserting the given element between--- arrays.------ /Pre-release/-{-# INLINE interpose #-}-interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a-interpose x = D.interpose x A.reader--{-# INLINE intercalateSuffix #-}-intercalateSuffix :: (Monad m, Unbox a)-    => Array a -> Stream m (Array a) -> Stream m a-intercalateSuffix = D.intercalateSuffix A.reader---- | Flatten a stream of arrays appending the given element after each--- array.------ @since 0.7.0-{-# INLINE interposeSuffix #-}-interposeSuffix :: (Monad m, Unbox a)-    => a -> Stream m (Array a) -> Stream m a--- interposeSuffix x = fromStreamD . A.unlines x . toStreamD-interposeSuffix x = D.interposeSuffix x A.reader--data FlattenState s =-      OuterLoop s-    | InnerLoop s !MA.MutableByteArray !Int !Int---- XXX This is a special case of interposeSuffix, can be removed.--- XXX Remove monadIO constraint {-# INLINE_NORMAL unlines #-} unlines :: forall m a. (MonadIO m, Unbox a)     => a -> D.Stream m (Array a) -> D.Stream m a-unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)-    where-    {-# INLINE_LATE step' #-}-    step' gst (OuterLoop st) = do-        r <- step (adaptState gst) st-        return $ case r of-            D.Yield Array{..} s ->-                D.Skip (InnerLoop s arrContents arrStart arrEnd)-            D.Skip s -> D.Skip (OuterLoop s)-            D.Stop -> D.Stop--    step' _ (InnerLoop st _ p end) | p == end =-        return $ D.Yield sep $ OuterLoop st--    step' _ (InnerLoop st contents p end) = do-        x <- liftIO $ peekByteIndex p contents-        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)+unlines = Array.interposeSuffix  ------------------------------------------------------------------------------- -- Compact ------------------------------------------------------------------------------- --- XXX These would not be needed once we implement compactLEFold, see--- module Streamly.Internal.Data.Stream.MutChunked------ XXX Note that this thaws immutable arrays for appending, that may be--- problematic if multiple users do the same thing, however, immutable arrays--- would usually have no capacity to append, therefore, a copy will be forced--- anyway. Confirm this. We can forcefully trim the array capacity before thaw--- to ensure this.-{-# INLINE_NORMAL packArraysChunksOf #-}-packArraysChunksOf :: (MonadIO m, Unbox a)-    => Int -> D.Stream m (Array a) -> D.Stream m (Array a)-packArraysChunksOf n str =-    D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str- -- XXX instead of writing two different versions of this operation, we should -- write it as a pipe. --@@ -220,8 +129,7 @@ {-# INLINE_NORMAL lpackArraysChunksOf #-} lpackArraysChunksOf :: (MonadIO m, Unbox a)     => Int -> Fold m (Array a) () -> Fold m (Array a) ()-lpackArraysChunksOf n fld =-    FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)+lpackArraysChunksOf = Array.lCompactGE  -- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a -- maximum specified size in bytes.@@ -230,68 +138,19 @@ {-# INLINE compact #-} compact :: (MonadIO m, Unbox a)     => Int -> Stream m (Array a) -> Stream m (Array a)-compact = packArraysChunksOf------------------------------------------------------------------------------------ Split----------------------------------------------------------------------------------data SplitState s arr-    = Initial s-    | Buffering s arr-    | Splitting s arr-    | Yielding arr (SplitState s arr)-    | Finishing+compact = Array.compactLE --- | Split a stream of arrays on a given separator byte, dropping the separator--- and coalescing all the arrays between two separators into a single array.+-- | Given a stream of arrays, splice them all together to generate a single+-- array. The stream must be /finite/. -- -- @since 0.7.0-{-# INLINE_NORMAL _splitOn #-}-_splitOn-    :: MonadIO m-    => Word8-    -> D.Stream m (Array Word8)-    -> D.Stream m (Array Word8)-_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (Initial st) = do-        r <- step gst st-        case r of-            D.Yield arr s -> do-                (arr1, marr2) <- A.breakOn byte arr-                return $ case marr2 of-                    Nothing   -> D.Skip (Buffering s arr1)-                    Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))-            D.Skip s -> return $ D.Skip (Initial s)-            D.Stop -> return D.Stop--    step' gst (Buffering st buf) = do-        r <- step gst st-        case r of-            D.Yield arr s -> do-                (arr1, marr2) <- A.breakOn byte arr-                buf' <- A.splice buf arr1-                return $ case marr2 of-                    Nothing -> D.Skip (Buffering s buf')-                    Just x -> D.Skip (Yielding buf' (Splitting s x))-            D.Skip s -> return $ D.Skip (Buffering s buf)-            D.Stop -> return $-                if A.byteLength buf == 0-                then D.Stop-                else D.Skip (Yielding buf Finishing)--    step' _ (Splitting st buf) = do-        (arr1, marr2) <- A.breakOn byte buf-        return $ case marr2 of-                Nothing -> D.Skip $ Buffering st arr1-                Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)+{-# INLINE toArray #-}+toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)+toArray = Array.fromChunks -    step' _ (Yielding arr next) = return $ D.Yield arr next-    step' _ Finishing = return D.Stop+-------------------------------------------------------------------------------+-- Split+-------------------------------------------------------------------------------  -- XXX Remove MonadIO constraint. -- | Split a stream of arrays on a given separator byte, dropping the separator@@ -304,7 +163,7 @@     => Word8     -> Stream m (Array Word8)     -> Stream m (Array Word8)-splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice+splitOn = Array.compactOnByte  {-# INLINE splitOnSuffix #-} splitOnSuffix@@ -312,8 +171,7 @@     => Word8     -> Stream m (Array Word8)     -> Stream m (Array Word8)--- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s-splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice+splitOnSuffix = Array.compactOnByteSuffix  ------------------------------------------------------------------------------- -- Elimination - Running folds@@ -322,77 +180,24 @@ {-# INLINE_NORMAL foldBreakD #-} foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>     Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))-foldBreakD (FL.Fold fstep initial _ final) stream@(D.Stream step state) = do-    res <- initial-    case res of-        FL.Partial fs -> go SPEC state fs-        FL.Done fb -> return $! (fb, stream)--    where--    {-# INLINE go #-}-    go !_ st !fs = do-        r <- step defState st-        case r of-            D.Yield (Array contents start end) s ->-                let fp = Tuple' end contents-                 in goArray SPEC s fp start fs-            D.Skip s -> go SPEC s fs-            D.Stop -> do-                b <- final fs-                return (b, D.nil)--    goArray !_ s (Tuple' end _) !cur !fs-        | cur == end = do-            go SPEC s fs-    goArray !_ st fp@(Tuple' end contents) !cur !fs = do-        x <- liftIO $ peekByteIndex cur contents-        res <- fstep fs x-        let next = INDEX_NEXT(cur,a)-        case res of-            FL.Done b -> do-                let arr = Array contents next end-                return $! (b, D.cons arr (D.Stream step st))-            FL.Partial fs1 -> goArray SPEC st fp next fs1--{-# INLINE_NORMAL foldBreakK #-}-foldBreakK :: forall m a b. (MonadIO m, Unbox a) =>-    Fold m a b -> K.StreamK m (Array a) -> m (b, K.StreamK m (Array a))-foldBreakK (FL.Fold fstep initial _ final) stream = do-    res <- initial-    case res of-        FL.Partial fs -> go fs stream-        FL.Done fb -> return (fb, stream)--    where--    {-# INLINE go #-}-    go !fs st = do-        let stop = (, K.nil) <$> final fs-            single a = yieldk a K.nil-            yieldk (Array contents start end) r =-                let fp = Tuple' end contents-                 in goArray fs r fp start-         in K.foldStream defState yieldk single stop st--    goArray !fs st (Tuple' end _) !cur-        | cur == end = do-            go fs st-    goArray !fs st fp@(Tuple' end contents) !cur = do-        x <- liftIO $ peekByteIndex cur contents-        res <- fstep fs x-        let next = INDEX_NEXT(cur,a)-        case res of-            FL.Done b -> do-                let arr = Array contents next end-                return $! (b, K.cons arr st)-            FL.Partial fs1 -> goArray fs1 st fp next+foldBreakD = Array.foldBreakChunks  -- | Fold an array stream using the supplied 'Fold'. Returns the fold result -- and the unconsumed stream. -- -- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f) --+-- Instead of using this we can adapt the fold to ParserK and use+-- parseBreakChunks instead. ParserK allows composing using Monad as well.+--+-- @+-- foldBreak f s =+--       fmap (first (fromRight undefined))+--     $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s+-- @+--+-- We can compare perf and remove this one or define it in terms of that.+-- -- /Internal/ -- {-# INLINE_NORMAL foldBreak #-}@@ -401,14 +206,20 @@     => Fold m a b     -> StreamK m (A.Array a)     -> m (b, StreamK m (A.Array a))+foldBreak = Array.foldBreakChunksK+-- -- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)-foldBreak = foldBreakK+--+-- foldBreak f s =+--       fmap (first (fromRight undefined))+--     $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s+-- -- If foldBreak performs better than runArrayFoldBreak we can use a rewrite -- rule to rewrite runArrayFoldBreak to fold. -- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)  ---------------------------------------------------------------------------------- Fold to a single Array+-- Elimination - running element parsers -------------------------------------------------------------------------------  -- When we have to take an array partially, take the last part of the array.@@ -454,341 +265,22 @@                          arr1 = Array contents end1 end                       in ([arr1], arr2:xs) ----------------------------------------------------------------------------------- Fold to a single Array----------------------------------------------------------------------------------{----- XXX Both of these implementations of splicing seem to perform equally well.--- We need to perform benchmarks over a range of sizes though.---- CAUTION! length must more than equal to lengths of all the arrays in the--- stream.-{-# INLINE spliceArraysLenUnsafe #-}-spliceArraysLenUnsafe :: (MonadIO m, Unbox a)-    => Int -> Stream m (MutArray a) -> m (MutArray a)-spliceArraysLenUnsafe len buffered = do-    -- XXX The new array's pinned state should depend on the first element-    -- of the stream. We should uncons the stream and do the required.-    arr <- undefined-    D.foldlM' MA.spliceUnsafe (return arr) buffered--{-# INLINE _spliceArrays #-}-_spliceArrays :: (MonadIO m, Unbox a)-    => Stream m (Array a) -> m (Array a)-_spliceArrays s = do-    buffered <- D.foldr K.cons K.nil s-    len <- K.fold FL.sum (fmap Array.length buffered)-    -- XXX The new array's pinned state should depend on the first element-    -- of the stream. We should uncons the stream and do the required.-    arr <- undefined-    final <- D.foldlM' writeArr (return arr) (toStream buffered)-    return $ A.unsafeFreeze final--    where--    writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)--{-# INLINE _spliceArraysBuffered #-}-_spliceArraysBuffered :: (MonadIO m, Unbox a)-    => Stream m (Array a) -> m (Array a)-_spliceArraysBuffered s = do-    buffered <- D.foldr K.cons K.nil s-    len <- K.fold FL.sum (fmap Array.length buffered)-    A.unsafeFreeze <$>-        spliceArraysLenUnsafe len (fmap A.unsafeThaw (toStream buffered))---}--{-# INLINE spliceArraysRealloced #-}-spliceArraysRealloced :: forall m a. (MonadIO m, Unbox a)-    => Stream m (Array a) -> m (Array a)-spliceArraysRealloced s = do-    res <- D.uncons s-    case res of-        Just (a, strm) -> do-            arr <--                D.foldlM'-                    MA.spliceExp-                    (pure (A.unsafeThaw a))-                    (fmap A.unsafeThaw strm)-            liftIO $ A.unsafeFreeze <$> MA.rightSize arr-        Nothing -> pure A.nil---- XXX This should just be "fold A.write"------ | Given a stream of arrays, splice them all together to generate a single--- array. The stream must be /finite/.------ @since 0.7.0-{-# INLINE toArray #-}-toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)-toArray = spliceArraysRealloced--- spliceArrays = _spliceArraysBuffered---- exponentially increasing sizes of the chunks upto the max limit.--- XXX this will be easier to implement with parsers/terminating folds--- With this we should be able to reduce the number of chunks/allocations.--- The reallocation/copy based toArray can also be implemented using this.----{--{-# INLINE toArraysInRange #-}-toArraysInRange :: (MonadIO m, Unbox a)-    => Int -> Int -> Fold m (Array a) b -> Fold m a b-toArraysInRange low high (Fold step initial extract) =--}--{---- | Fold the input to a pure buffered stream (List) of arrays.-{-# INLINE _toArraysOf #-}-_toArraysOf :: (MonadIO m, Unbox a)-    => Int -> Fold m a (Stream Identity (Array a))-_toArraysOf n = FL.groupsOf n (A.writeNF n) FL.toStream--}------------------------------------------------------------------------------------ Elimination - running element parsers--------------------------------------------------------------------------------- -- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need -- to make a newtype. {-# ANN type List NoSpecConstr #-} newtype List a = List {getList :: [a]} -{---- This can be generalized to any type provided it can be unfolded to a stream--- and it can be combined using a semigroup operation.------ XXX This should be written using CPS (as parseK) if we want it to scale wrt--- to the number of times it can be called on the same stream.-{-# INLINE_NORMAL parseBreakD #-}-parseBreakD ::-       forall m a b. (MonadIO m, MonadThrow m, Unbox a)-    => PRD.Parser a m b-    -> D.Stream m (Array.Array a)-    -> m (b, D.Stream m (Array.Array a))-parseBreakD-    (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do--    res <- initial-    case res of-        PRD.IPartial s -> go SPEC state (List []) s-        PRD.IDone b -> return (b, stream)-        PRD.IError err -> throwM $ ParseError err--    where--    -- "backBuf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    go !_ st backBuf !pst = do-        r <- step defState st-        case r of-            D.Yield (Array contents start end) s ->-                gobuf SPEC s backBuf-                    (Tuple' end contents) start pst-            D.Skip s -> go SPEC s backBuf pst-            D.Stop -> do-                b <- extract pst-                return (b, D.nil)--    -- Use strictness on "cur" to keep it unboxed-    gobuf !_ s backBuf (Tuple' end _) !cur !pst-        | cur == end = do-            go SPEC s backBuf pst-    gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do-        x <- liftIO $ peekByteIndex contents cur-        pRes <- pstep pst x-        let next = INDEX_NEXT(cur,a)-        case pRes of-            PR.Partial 0 pst1 ->-                 gobuf SPEC s (List []) fp next pst1-            PR.Partial n pst1 -> do-                assert (n <= Prelude.length (x:getList backBuf)) (return ())-                let src0 = Prelude.take n (x:getList backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                let !(Array cont1 start end1) = src-                    fp1 = Tuple' end1 cont1-                gobuf SPEC s (List []) fp1 start pst1-            PR.Continue 0 pst1 ->-                gobuf SPEC s (List (x:getList backBuf)) fp next pst1-            PR.Continue n pst1 -> do-                assert (n <= Prelude.length (x:getList backBuf)) (return ())-                let (src0, buf1) = splitAt n (x:getList backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                let !(Array cont1 start end1) = src-                    fp1 = Tuple' end1 cont1-                gobuf SPEC s (List buf1) fp1 start pst1-            PR.Done 0 b -> do-                let arr = Array contents next end-                return (b, D.cons arr (D.Stream step s))-            PR.Done n b -> do-                assert (n <= Prelude.length (x:getList backBuf)) (return ())-                let src0 = Prelude.take n (x:getList backBuf)-                    -- XXX create the array in reverse instead-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    -- XXX Use StreamK to avoid adding arbitrary layers of-                    -- constructors every time.-                    str = D.cons arr0 (D.cons arr1 (D.Stream step s))-                return (b, str)-            PR.Error err -> throwM $ ParseError err--}--{-# INLINE_NORMAL parseBreakK #-}-parseBreakK ::-       forall m a b. (MonadIO m, Unbox a)-    => PRD.Parser a m b-    -> K.StreamK m (Array.Array a)-    -> m (Either ParseError b, K.StreamK m (Array.Array a))-parseBreakK (PRD.Parser pstep initial extract) stream = do-    res <- initial-    case res of-        PRD.IPartial s -> go s stream []-        PRD.IDone b -> return (Right b, stream)-        PRD.IError err -> return (Left (ParseError err), stream)--    where--    -- "backBuf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    go !pst st backBuf = do-        let stop = goStop pst backBuf -- (, K.nil) <$> extract pst-            single a = yieldk a K.nil-            yieldk arr r = goArray pst backBuf r arr-         in K.foldStream defState yieldk single stop st--    -- Use strictness on "cur" to keep it unboxed-    goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf-    goArray !pst backBuf st (Array contents cur end) = do-        x <- liftIO $ peekByteIndex cur contents-        pRes <- pstep pst x-        let next = INDEX_NEXT(cur,a)-        case pRes of-            PR.Partial 0 s ->-                 goArray s [] st (Array contents next end)-            PR.Partial n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let src0 = Prelude.take n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goArray s [] st src-            PR.Continue 0 s ->-                goArray s (x:backBuf) st (Array contents next end)-            PR.Continue n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let (src0, buf1) = splitAt n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goArray s buf1 st src-            PR.Done 0 b -> do-                let arr = Array contents next end-                return (Right b, K.cons arr st)-            PR.Done n b -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let src0 = Prelude.take n (x:backBuf)-                    -- XXX Use fromListRevN once implemented-                    -- arr0 = A.fromListRevN n src0-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    str = K.cons arr0 (K.cons arr1 st)-                return (Right b, str)-            PR.Error err -> do-                let n = Prelude.length backBuf-                    arr0 = A.fromListN n (Prelude.reverse backBuf)-                    arr1 = Array contents cur end-                    str = K.cons arr0 (K.cons arr1 stream)-                return (Left (ParseError err), str)--    -- This is a simplified goArray-    goExtract !pst backBuf (Array _ cur end)-        | cur == end = goStop pst backBuf-    goExtract !pst backBuf (Array contents cur end) = do-        x <- liftIO $ peekByteIndex cur contents-        pRes <- pstep pst x-        let next = INDEX_NEXT(cur,a)-        case pRes of-            PR.Partial 0 s ->-                 goExtract s [] (Array contents next end)-            PR.Partial n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let src0 = Prelude.take n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goExtract s [] src-            PR.Continue 0 s ->-                goExtract s backBuf (Array contents next end)-            PR.Continue n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let (src0, buf1) = splitAt n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goExtract s buf1 src-            PR.Done 0 b -> do-                let arr = Array contents next end-                return (Right b, K.fromPure arr)-            PR.Done n b -> do-                assert (n <= Prelude.length backBuf) (return ())-                let src0 = Prelude.take n backBuf-                    -- XXX Use fromListRevN once implemented-                    -- arr0 = A.fromListRevN n src0-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    str = K.cons arr0 (K.fromPure arr1)-                return (Right b, str)-            PR.Error err -> do-                let n = Prelude.length backBuf-                    arr0 = A.fromListN n (Prelude.reverse backBuf)-                    arr1 = Array contents cur end-                    str = K.cons arr0 (K.cons arr1 stream)-                return (Left (ParseError err), str)--    -- This is a simplified goExtract-    {-# INLINE goStop #-}-    goStop !pst backBuf = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"-            PR.Continue 0 s ->-                goStop s backBuf-            PR.Continue n s -> do-                assert (n <= Prelude.length backBuf) (return ())-                let (src0, buf1) = splitAt n backBuf-                    arr = A.fromListN n (Prelude.reverse src0)-                goExtract s buf1 arr-            PR.Done 0 b ->-                return (Right b, K.nil)-            PR.Done n b -> do-                assert (n <= Prelude.length backBuf) (return ())-                let src0 = Prelude.take n backBuf-                    -- XXX Use fromListRevN once implemented-                    -- arr0 = A.fromListRevN n src0-                    arr0 = A.fromListN n (Prelude.reverse src0)-                return (Right b, K.fromPure arr0)-            PR.Error err -> do-                let n = Prelude.length backBuf-                    arr0 = A.fromListN n (Prelude.reverse backBuf)-                return (Left (ParseError err), K.fromPure arr0)- -- | Parse an array stream using the supplied 'Parser'.  Returns the parse -- result and the unconsumed stream. Throws 'ParseError' if the parse fails. --+-- >> parseBreak p = K.parseBreakChunks (ParserK.adaptC p)+--+-- This is redundant and we can just use parseBreakChunks, as ParserK can be+-- composed using Monad. The only advantage of this is that we do not need to+-- adapt.+--+-- We can compare perf and remove this one or define it in terms of that.+-- -- /Internal/ -- {-# INLINE_NORMAL parseBreak #-}@@ -801,7 +293,8 @@ parseBreak p s =     fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s) -}-parseBreak = parseBreakK+parseBreak = Array.parseBreakChunksK+-- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)  ------------------------------------------------------------------------------- -- Elimination - Running Array Folds and parsers
src/Streamly/Internal/Data/Array/Type.hs view
@@ -2,7 +2,6 @@ -- | -- Module      : Streamly.Internal.Data.Array.Type -- Copyright   : (c) 2020 Composewell Technologies--- -- License     : BSD3-3-Clause -- Maintainer  : streamly@composewell.com -- Stability   : experimental@@ -10,87 +9,146 @@ -- -- See notes in "Streamly.Internal.Data.MutArray.Type" --+ module Streamly.Internal.Data.Array.Type     (     -- ** Type     -- $arrayNotes       Array (..)-    , asPtrUnsafe-    , nil -    -- ** Freezing and Thawing+    -- ** Conversion+    -- *** Mutable and Immutable     , unsafeFreeze     , unsafeFreezeWithShrink     , unsafeThaw -    -- ** Pinning and Unpinning+    -- *** Pinned and Unpinned     , pin     , unpin     , isPinned +    -- *** Casting+    , unsafePinnedAsPtr+     -- ** Construction-    , splice+    , empty -    , fromList-    , pinnedFromList+    -- *** Cloning+    -- XXX Why would we clone an immutable array?+    , clone+    , pinnedClone++    -- *** Slicing+    -- | Get a subarray without copying+    , splitAt+    , breakOn -- XXX requires MonadIO++    -- *** Stream Folds+    , unsafeMakePure+    , createOf+    , pinnedCreateOf+    , unsafeCreateOf+    , unsafePinnedCreateOf+    , create+    , pinnedCreate+    , createWith++    -- *** From containers     , fromListN     , pinnedFromListN-    , fromListRev+    , fromList+    , pinnedFromList     , fromListRevN-    , fromStreamDN-    , fromStreamD+    , fromListRev+    , fromStreamN+    , fromStream+    , fromPureStreamN     , fromPureStream     , fromByteStr#+    , fromByteStr+    , fromPtrN+    , fromChunks+    , fromChunksK -    -- ** Split-    , breakOn+    -- ** Reading -    -- ** Cloning arrays-    , clone-    , pinnedClone+    -- *** Indexing+    , unsafeIndexIO -- XXX unsafeGetIndexIO+    , getIndexUnsafe -- XXX unsafeGetIndex -    -- ** Elimination-    , unsafeIndexIO-    , getIndexUnsafe-    , byteLength+    -- *** To Streams+    , read+    , readRev+    , toStreamK+    , toStreamKRev++    -- *** To Containers+    , toList++    -- *** Unfolds+    , producer -- experimental+    , readerUnsafe+    , reader+    , readerRev++    -- *** Size     , length+    , byteLength +    -- ** Folding     , foldl'     , foldr-    , splitAt+    , byteCmp+    , byteEq +    -- ** Appending+    , splice -- XXX requires MonadIO++    -- ** Streams of arrays+    -- *** Chunk+    -- | Group a stream into arrays.+    , chunksOf+    , pinnedChunksOf+    , buildChunks++    -- *** Split+    -- | Split an array into slices.++    -- *** Concat+    -- | Append the arrays in a stream to form a stream of elements.+    , concat+    , concatRev++    -- *** Compact+    -- | Append the arrays in a stream to form a stream of larger arrays.+    , fCompactGE+    , fPinnedCompactGE+    , lCompactGE+    , lPinnedCompactGE+    , compactGE++    -- ** Deprecated+    , asPtrUnsafe+    , unsafeIndex+    , bufferChunks+    , flattenArrays+    , flattenArraysRev+    , fromArrayStreamK+    , fromStreamDN+    , fromStreamD     , toStreamD     , toStreamDRev-    , toStreamK-    , toStreamKRev     , toStream     , toStreamRev-    , read-    , readRev-    , readerRev-    , toList--    -- ** Folds+    , nil     , writeWith     , writeN     , pinnedWriteN     , writeNUnsafe     , pinnedWriteNUnsafe-    , MA.ArrayUnsafe (..)     , pinnedWriteNAligned     , write     , pinnedWrite-    , unsafeMakePure--    -- ** Streams of arrays-    , chunksOf-    , pinnedChunksOf-    , bufferChunks-    , flattenArrays-    , flattenArraysRev--    -- ** Deprecated-    , unsafeIndex     ) where @@ -98,31 +156,36 @@ #include "inline.hs"  import Control.Exception (assert)-import Control.Monad (replicateM)+import Control.Monad (replicateM, when) import Control.Monad.IO.Class (MonadIO(..)) import Data.Functor.Identity (Identity(..))+import Data.Int (Int8, Int16, Int32, Int64) import Data.Proxy (Proxy(..))-import Data.Word (Word8)+import Data.Word (Word8, Word16, Word32, Word64) import GHC.Base (build) import GHC.Exts (IsList, IsString(..), Addr#)  import GHC.IO (unsafePerformIO) import GHC.Ptr (Ptr(..))-import Streamly.Internal.Data.MutArray.Type (MutArray(..), MutByteArray)+import Streamly.Internal.Data.Producer.Type (Producer(..))+import Streamly.Internal.Data.MutArray.Type (MutArray(..))+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray) import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.StreamK.Type (StreamK) import Streamly.Internal.Data.Unbox (Unbox(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Text.Read (readPrec) -import Prelude hiding (Foldable(..), read, unlines, splitAt)+import Prelude hiding (Foldable(..), concat, read, unlines, splitAt)  import qualified GHC.Exts as Exts+import qualified Streamly.Internal.Data.Fold.Type as Fold import qualified Streamly.Internal.Data.MutArray.Type as MA import qualified Streamly.Internal.Data.Stream.Type as D-import qualified Streamly.Internal.Data.Stream.Generate as D import qualified Streamly.Internal.Data.StreamK.Type as K import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed+import qualified Streamly.Internal.Data.Producer as Producer import qualified Streamly.Internal.Data.Unfold.Type as Unfold import qualified Text.ParserCombinators.ReadPrec as ReadPrec @@ -176,15 +239,20 @@  -- | Use an @Array a@ as @Ptr a@. ----- See 'MA.asPtrUnsafe' in the Mutable array module for more details.+-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details. -- -- /Unsafe/ -- -- /Pre-release/ --+{-# INLINE unsafePinnedAsPtr #-}+unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b+unsafePinnedAsPtr arr = MA.unsafePinnedAsPtr (unsafeThaw arr)++{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-} {-# INLINE asPtrUnsafe #-} asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b-asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)+asPtrUnsafe = unsafePinnedAsPtr  ------------------------------------------------------------------------------- -- Freezing and Thawing@@ -252,11 +320,14 @@ -- Construction ------------------------------------------------------------------------------- --- Splice two immutable arrays creating a new array.+-- | Copy two immutable arrays into a new array. If you want to splice more+-- than two arrays then this operation would be highly inefficient because it+-- would make a copy on every splice operation, instead use the+-- 'fromChunksK' operation to combine n immutable arrays. {-# INLINE splice #-}-splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)+splice :: MonadIO m => Array a -> Array a -> m (Array a) splice arr1 arr2 =-    unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)+    unsafeFreeze <$> MA.spliceCopy (unsafeThaw arr1) (unsafeThaw arr2)  -- | Create an 'Array' from the first N elements of a list. The array is -- allocated to size N, if the list terminates before N elements then the@@ -300,15 +371,45 @@ fromListRev :: Unbox a => [a] -> Array a fromListRev xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListRev xs -{-# INLINE_NORMAL fromStreamDN #-}+-- | Create an 'Array' from the first N elements of a stream. The array is+-- allocated to size N, if the stream terminates before N elements then the+-- array may hold less than N elements.+--+-- >>> fromStreamN n = Stream.fold (Array.writeN n)+--+-- /Pre-release/+{-# INLINE_NORMAL fromStreamN #-}+fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)+fromStreamN n m = do+    when (n < 0) $ error "writeN: negative write count specified"+    unsafeFreeze <$> MA.fromStreamN n m+-- fromStreamN n = D.fold (writeN n)++{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-} fromStreamDN :: forall m a. (MonadIO m, Unbox a)     => Int -> D.Stream m a -> m (Array a)-fromStreamDN limit str = unsafeFreeze <$> MA.fromStreamDN limit str+fromStreamDN = fromStreamN +-- | Create an 'Array' from a stream. This is useful when we want to create a+-- single array from a stream of unknown size. 'writeN' is at least twice+-- as efficient when the size is already known.+--+-- >>> fromStream = Stream.fold Array.write+--+-- Note that if the input stream is too large memory allocation for the array+-- may fail.  When the stream size is not known, `chunksOf` followed by+-- processing of indvidual arrays in the resulting stream should be preferred.+--+-- /Pre-release/ {-# INLINE_NORMAL fromStreamD #-}+fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)+fromStream = D.fold write+-- fromStreamD str = unsafeFreeze <$> MA.fromStream str++{-# DEPRECATED fromStreamD "Please use fromStream instead." #-} fromStreamD :: forall m a. (MonadIO m, Unbox a)     => D.Stream m a -> m (Array a)-fromStreamD str = unsafeFreeze <$> MA.fromStreamD str+fromStreamD = fromStream  ------------------------------------------------------------------------------- -- Cloning@@ -338,10 +439,15 @@ -- Streams of arrays ------------------------------------------------------------------------------- -{-# INLINE bufferChunks #-}+{-# INLINE buildChunks #-}+buildChunks :: (MonadIO m, Unbox a) =>+    D.Stream m a -> m (K.StreamK m (Array a))+buildChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m++{-# DEPRECATED bufferChunks "Please use buildChunks instead." #-} bufferChunks :: (MonadIO m, Unbox a) =>     D.Stream m a -> m (K.StreamK m (Array a))-bufferChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m+bufferChunks = buildChunks  -- | @chunksOf n stream@ groups the elements in the input stream into arrays of -- @n@ elements each.@@ -362,28 +468,101 @@     => Int -> D.Stream m a -> D.Stream m (Array a) pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str --- | Use the "read" unfold instead.------ @flattenArrays = unfoldMany read@+-- | Convert a stream of arrays into a stream of their elements. ----- We can try this if there are any fusion issues in the unfold.+-- >>> concat = Stream.unfoldMany Array.reader ---{-# INLINE_NORMAL flattenArrays #-}+{-# INLINE_NORMAL concat #-}+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+-- XXX this requires MonadIO whereas the unfoldMany version does not+concat = MA.concatWith (pure . unsafeInlineIO) . D.map unsafeThaw+-- concat = D.unfoldMany reader++{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}+{-# INLINE flattenArrays #-} flattenArrays :: forall m a. (MonadIO m, Unbox a)     => D.Stream m (Array a) -> D.Stream m a-flattenArrays = MA.flattenArrays . D.map unsafeThaw+flattenArrays = concat --- | Use the "readRev" unfold instead.------ @flattenArrays = unfoldMany readRev@+-- | Convert a stream of arrays into a stream of their elements reversing the+-- contents of each array before flattening. ----- We can try this if there are any fusion issues in the unfold.+-- >>> concatRev = Stream.unfoldMany Array.readerRev ---{-# INLINE_NORMAL flattenArraysRev #-}+{-# INLINE_NORMAL concatRev #-}+concatRev :: forall m a. (Monad m, Unbox a)+    => D.Stream m (Array a) -> D.Stream m a+-- XXX this requires MonadIO whereas the unfoldMany version does not+concatRev = MA.concatRevWith (pure . unsafeInlineIO) . D.map unsafeThaw+-- concatRev = D.unfoldMany readerRev++{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}+{-# INLINE flattenArraysRev #-} flattenArraysRev :: forall m a. (MonadIO m, Unbox a)     => D.Stream m (Array a) -> D.Stream m a-flattenArraysRev = MA.flattenArraysRev . D.map unsafeThaw+flattenArraysRev = concatRev +-------------------------------------------------------------------------------+-- Compact+-------------------------------------------------------------------------------++-- XXX Note that this thaws immutable arrays for appending, that may be+-- problematic if multiple users do the same thing, however, thawed immutable+-- arrays would have no capacity to append, therefore, a copy will be forced+-- anyway.++-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream+-- until the size becomes greater than or equal to n.+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE_NORMAL fCompactGE #-}+fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)+fCompactGE n = fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fCompactGE n++-- | PInned version of 'fCompactGE'.+{-# INLINE_NORMAL fPinnedCompactGE #-}+fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)+fPinnedCompactGE n =+    fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fPinnedCompactGE n++-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until+-- the size becomes greater than or equal to @n@.+--+-- >>> compactGE n = Stream.foldMany (Array.fCompactGE n)+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE compactGE #-}+compactGE ::+       (MonadIO m, Unbox a)+    => Int -> Stream m (Array a) -> Stream m (Array a)+compactGE n stream =+    D.map unsafeFreeze $ MA.compactGE n $ D.map unsafeThaw stream++-- | Like 'compactGE' but for transforming folds instead of stream.+--+-- >>> lCompactGE n = Fold.many (Array.fCompactGE n)+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE_NORMAL lCompactGE #-}+lCompactGE :: (MonadIO m, Unbox a)+    => Int -> Fold m (Array a) () -> Fold m (Array a) ()+lCompactGE n fld =+    Fold.lmap unsafeThaw $ MA.lCompactGE n (Fold.lmap unsafeFreeze fld)++-- | Pinned version of 'lCompactGE'.+{-# INLINE_NORMAL lPinnedCompactGE #-}+lPinnedCompactGE :: (MonadIO m, Unbox a)+    => Int -> Fold m (Array a) () -> Fold m (Array a) ()+lPinnedCompactGE n fld =+    Fold.lmap unsafeThaw $ MA.lPinnedCompactGE n (Fold.lmap unsafeFreeze fld)++-------------------------------------------------------------------------------+-- Splitting+-------------------------------------------------------------------------------+ -- Drops the separator byte {-# INLINE breakOn #-} breakOn :: MonadIO m@@ -426,24 +605,68 @@ length :: Unbox a => Array a -> Int length arr = MA.length (unsafeThaw arr) +{-# INLINE_NORMAL producer #-}+producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a+producer =+    Producer.translate unsafeThaw unsafeFreeze+        $ MA.producerWith (return . unsafeInlineIO)++-- | Unfold an array into a stream.+--+{-# INLINE_NORMAL reader #-}+reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a+reader = Producer.simplify producer++-- | Unfold an array into a stream, does not check the end of the array, the+-- user is responsible for terminating the stream within the array bounds. For+-- high performance application where the end condition can be determined by+-- a terminating fold.+--+-- Written in the hope that it may be faster than "read", however, in the case+-- for which this was written, "read" proves to be faster even though the core+-- generated with unsafeRead looks simpler.+--+-- /Pre-release/+--+{-# INLINE_NORMAL readerUnsafe #-}+readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a+readerUnsafe = Unfold step inject+    where++    inject (Array contents start end) =+        return (MA.ArrayUnsafe contents end start)++    {-# INLINE_LATE step #-}+    step (MA.ArrayUnsafe contents end p) = do+            -- unsafeInlineIO allows us to run this in Identity monad for pure+            -- toList/foldr case which makes them much faster due to not+            -- accumulating the list and fusing better with the pure consumers.+            --+            -- This should be safe as the array contents are guaranteed to be+            -- evaluated/written to before we peek at them.+            let !x = unsafeInlineIO $ peekAt p contents+            let !p1 = INDEX_NEXT(p,a)+            return $ D.Yield x (MA.ArrayUnsafe contents end p1)+ -- | Unfold an array into a stream in reverse order. -- {-# INLINE_NORMAL readerRev #-} readerRev :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a readerRev = Unfold.lmap unsafeThaw $ MA.readerRevWith (return . unsafeInlineIO) +{-# DEPRECATED toStreamD "Please use 'read' instead." #-} {-# INLINE_NORMAL toStreamD #-} toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a-toStreamD arr = MA.toStreamDWith (return . unsafeInlineIO) (unsafeThaw arr)+toStreamD = read  {-# INLINE toStreamK #-} toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a toStreamK arr = MA.toStreamKWith (return . unsafeInlineIO) (unsafeThaw arr) +{-# DEPRECATED toStreamDRev "Please use 'readRev' instead." #-} {-# INLINE_NORMAL toStreamDRev #-} toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a-toStreamDRev arr =-    MA.toStreamDRevWith (return . unsafeInlineIO) (unsafeThaw arr)+toStreamDRev = readRev  {-# INLINE toStreamKRev #-} toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a@@ -455,7 +678,7 @@ -- /Pre-release/ {-# INLINE_EARLY read #-} read :: (Monad m, Unbox a) => Array a -> Stream m a-read = toStreamD+read arr = MA.toStreamWith (return . unsafeInlineIO) (unsafeThaw arr)  -- | Same as 'read' --@@ -472,7 +695,7 @@ -- /Pre-release/ {-# INLINE_EARLY readRev #-} readRev :: (Monad m, Unbox a) => Array a -> Stream m a-readRev = toStreamDRev+readRev arr = MA.toStreamRevWith (return . unsafeInlineIO) (unsafeThaw arr)  -- | Same as 'readRev' --@@ -496,6 +719,7 @@ -- | Create two slices of an array without copying the original array. The -- specified index @i@ is the first index of the second slice. --+{-# INLINE splitAt #-} splitAt :: Unbox a => Int -> Array a -> (Array a, Array a) splitAt i arr = (unsafeFreeze a, unsafeFreeze b)   where@@ -529,17 +753,28 @@ -- Folds ------------------------------------------------------------------------------- --- | @writeN n@ folds a maximum of @n@ elements from the input stream to an+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an -- 'Array'. ---{-# INLINE_NORMAL writeN #-}+{-# INLINE_NORMAL createOf #-}+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+createOf = fmap unsafeFreeze . MA.createOf++-- XXX Deprecate in major+-- {-# DEPRECATED writeN  "Please use createOf instead." #-}+{-# INLINE writeN #-} writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)-writeN = fmap unsafeFreeze . MA.writeN+writeN = createOf --- | Like 'fromListN' but creates a pinned array.-{-# INLINE_NORMAL pinnedWriteN #-}+-- | Like 'createOf' but creates a pinned array.+{-# INLINE_NORMAL pinnedCreateOf #-}+pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+pinnedCreateOf = fmap unsafeFreeze . MA.pinnedCreateOf++{-# DEPRECATED pinnedWriteN  "Please use pinnedCreateOf instead." #-}+{-# INLINE pinnedWriteN #-} pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)-pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN+pinnedWriteN = pinnedCreateOf  -- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input -- stream to an 'Array' aligned to the given size.@@ -547,44 +782,74 @@ -- /Pre-release/ -- {-# INLINE_NORMAL pinnedWriteNAligned #-}+{-# DEPRECATED pinnedWriteNAligned  "To be removed." #-} pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)     => Int -> Int -> Fold m a (Array a) pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize --- | Like 'writeN' but does not check the array bounds when writing. The fold+-- | Like 'createOf' but does not check the array bounds when writing. The fold -- driver must not call the step function more than 'n' times otherwise it will -- corrupt the memory and crash. This function exists mainly because any -- conditional in the step function blocks fusion causing 10x performance -- slowdown. ---{-# INLINE_NORMAL writeNUnsafe #-}+{-# INLINE_NORMAL unsafeCreateOf #-}+unsafeCreateOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (Array a)+unsafeCreateOf n = unsafeFreeze <$> MA.unsafeCreateOf n++{-# DEPRECATED writeNUnsafe  "Please use unsafeCreateOf instead." #-}+{-# INLINE writeNUnsafe #-} writeNUnsafe :: forall m a. (MonadIO m, Unbox a)     => Int -> Fold m a (Array a)-writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n+writeNUnsafe = unsafeCreateOf -{-# INLINE_NORMAL pinnedWriteNUnsafe #-}+{-# INLINE_NORMAL unsafePinnedCreateOf #-}+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (Array a)+unsafePinnedCreateOf n = unsafeFreeze <$> MA.unsafePinnedCreateOf n++{-# DEPRECATED pinnedWriteNUnsafe  "Please use unsafePinnedCreateOf instead." #-}+{-# INLINE pinnedWriteNUnsafe #-} pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)     => Int -> Fold m a (Array a)-pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n+pinnedWriteNUnsafe = unsafePinnedCreateOf -{-# INLINE_NORMAL writeWith #-}+{-# INLINE_NORMAL createWith #-}+createWith :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (Array a)+-- createWith n = FL.rmapM spliceArrays $ toArraysOf n+createWith elemCount = unsafeFreeze <$> MA.createWith elemCount++{-# DEPRECATED writeWith "Please use createWith instead." #-}+{-# INLINE writeWith #-} writeWith :: forall m a. (MonadIO m, Unbox a)     => Int -> Fold m a (Array a)--- writeWith n = FL.rmapM spliceArrays $ toArraysOf n-writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount+writeWith = createWith  -- | Fold the whole input to a single array. -- -- /Caution! Do not use this on infinite streams./ --+{-# INLINE create #-}+create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+create = fmap unsafeFreeze MA.create++-- XXX Deprecate in major+-- {-# DEPRECATED write  "Please use create instead." #-} {-# INLINE write #-} write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)-write = fmap unsafeFreeze MA.write+write = create --- | Like 'write' but creates a pinned array.+-- | Like 'create' but creates a pinned array.+{-# INLINE pinnedCreate #-}+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+pinnedCreate = fmap unsafeFreeze MA.pinnedCreate++{-# DEPRECATED pinnedWrite  "Please use pinnedCreate instead." #-} {-# INLINE pinnedWrite #-} pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)-pinnedWrite = fmap unsafeFreeze MA.pinnedWrite+pinnedWrite = pinnedCreate  -- | Fold "step" has a dependency on "initial", and each step is dependent on -- the previous invocation of step due to state passing, finally extract@@ -616,6 +881,11 @@          (\s -> return $! unsafeInlineIO $ extract s)          (\s -> return $! unsafeInlineIO $ final s) +{-# INLINE fromPureStreamN #-}+fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a+fromPureStreamN n x =+    unsafePerformIO $ fmap unsafeFreeze (MA.fromPureStreamN n x)+ -- | Convert a pure stream in Identity monad to an immutable array. -- -- Same as the following but with better performance:@@ -627,6 +897,19 @@ -- fromPureStream = runIdentity . D.fold (unsafeMakePure write) -- fromPureStream = fromList . runIdentity . D.toList +-- XXX This should be monadic.++-- | Copy an immutable 'Ptr Word8' sequence into an array.+--+-- /Unsafe:/ The caller is responsible for safe addressing.+--+-- Note that this should be evaluated strictly to ensure that we do not hold+-- the reference to the pointer in a lazy thunk.+fromPtrN :: Int -> Ptr Word8 -> Array Word8+fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)++-- XXX This should be monadic.+ -- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array. -- -- /Unsafe:/ The caller is responsible for safe addressing.@@ -637,9 +920,47 @@ -- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"# -- [1,2,3] --+-- Note that this should be evaluated strictly to ensure that we do not hold+-- the reference to the pointer in a lazy thunk. fromByteStr# :: Addr# -> Array Word8-fromByteStr# addr = fromPureStream (D.fromByteStr# addr)+fromByteStr# addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromByteStr# addr) +-- XXX This should be monadic.++-- | Note that this should be evaluated strictly to ensure that we do not hold+-- the reference to the pointer in a lazy thunk.+fromByteStr :: Ptr Word8 -> Array Word8+fromByteStr (Ptr addr#) = fromByteStr# addr#++-- XXX implement fromChunks/fromChunkList instead?++-- | Convert an array stream to an array. Note that this requires peak memory+-- that is double the size of the array stream.+--+{-# INLINE fromChunksK #-}+fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)+fromChunksK stream =+    -- We buffer the entire stream and then allocate the target array of the+    -- same size, thus requiring double the memory.+    fmap unsafeFreeze $ MA.fromChunksK $ fmap unsafeThaw stream++{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}+fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (Array a) -> m (Array a)+fromArrayStreamK = fromChunksK++-- | Given a stream of arrays, splice them all together to generate a single+-- array. The stream must be /finite/.+--+{-# INLINE fromChunks #-}+fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)+fromChunks s =+    -- XXX Check which implementation is better+    -- This may also require double the memory as we double the space every+    -- time, when copying the last array we may have reallocated almost double+    -- the space required before we right size it.+    fmap unsafeFreeze $ MA.fromChunksRealloced (fmap unsafeThaw s)+    -- fromChunkStreamK $ D.toStreamK s+ ------------------------------------------------------------------------------- -- Instances -------------------------------------------------------------------------------@@ -670,12 +991,66 @@     {-# INLINE toList #-}     toList = toList --- XXX we are assuming that Unboxed equality means element equality. This may--- or may not be correct? arrcmp is 40% faster compared to stream equality.-instance (Unbox a, Eq a) => Eq (Array a) where+-- | Byte compare two arrays. Compare the length of the arrays. If the length+-- is equal, compare the lexicographical ordering of two underlying byte arrays+-- otherwise return the result of length comparison.+--+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of+-- different sizes may leave some memory uninitialized which can make byte+-- comparison unreliable.+--+-- /Pre-release/+{-# INLINE byteCmp #-}+byteCmp :: Array a -> Array a -> Ordering+byteCmp arr1 arr2 =+    -- unsafePerformIO?+    unsafeInlineIO $! unsafeThaw arr1 `MA.byteCmp` unsafeThaw arr2++-- | Byte equality of two arrays.+--+-- >>> byteEq arr1 arr2 = (==) EQ $ Array.byteCmp arr1 arr2+--+-- /Unsafe/: See 'byteCmp'.+{-# INLINE byteEq #-}+byteEq :: Array a -> Array a -> Bool+byteEq arr1 arr2 = (==) EQ $ byteCmp arr1 arr2++#define MK_EQ_INSTANCE(typ)                              \+instance {-# OVERLAPPING #-} Eq (Array typ) where {      \+;    {-# INLINE (==) #-}                                 \+;    (==) = byteEq \+}++MK_EQ_INSTANCE(Char)+MK_EQ_INSTANCE(Word8)+MK_EQ_INSTANCE(Word16)+MK_EQ_INSTANCE(Word32)++-- XXX The Word64 default instance should be as fast because we are comparing+-- 64-bit at a time.+MK_EQ_INSTANCE(Word64)+MK_EQ_INSTANCE(Int)+MK_EQ_INSTANCE(Int8)+MK_EQ_INSTANCE(Int16)+MK_EQ_INSTANCE(Int32)++-- XXX The Int64 default instance should be as fast.+MK_EQ_INSTANCE(Int64)++-- | If the type allows a byte-by-byte comparison this instance can be+-- overlapped by a more specific instance that uses 'byteCmp'. Byte comparison+-- can be significantly faster.+--+instance {-# OVERLAPPABLE #-} (Unbox a, Eq a) => Eq (Array a) where     {-# INLINE (==) #-}     arr1 == arr2 =-        (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2+        -- Does unboxed byte equality mean element equality?+        -- XXX This is incorrect for sum types, as we may have some+        -- uninitialized memory in that case. If we always initialize the+        -- unused memory to zero we can use this.+        -- Byte comparison is 40% faster compared to stream equality.+        -- (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2+           (toStreamD arr1 :: Stream Identity a) == toStreamD arr2  instance (Unbox a, Ord a) => Ord (Array a) where     {-# INLINE compare #-}@@ -740,15 +1115,29 @@ -- Semigroup and Monoid ------------------------------------------------------------------------------- +-- XXX Deprecate and remove the Semigroup and Monoid instances because of+-- potential misuse chances.++-- | This should not be used for combining many or N arrays as it would copy+-- the two arrays everytime to a new array. For coalescing multiple arrays use+-- 'fromChunksK' instead. instance Unbox a => Semigroup (Array a) where     arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2 +empty ::+#ifdef DEVBUILD+    Unbox a =>+#endif+    Array a+empty = Array Unboxed.empty 0 0++{-# DEPRECATED nil "Please use empty instead." #-} nil :: #ifdef DEVBUILD     Unbox a => #endif     Array a-nil = Array Unboxed.nil 0 0+nil = empty  instance Unbox a => Monoid (Array a) where     mempty = nil
src/Streamly/Internal/Data/Fold/Combinators.hs view
@@ -30,8 +30,6 @@     -- *** Reducers     , drainMapM     , the-    , length-    , lengthGeneric     , mean     , rollingHash     , defaultSalt@@ -166,8 +164,6 @@     -- ** Trimming      -- By elements-    , takeEndBy-    , takeEndBy_     , takeEndBySeq     , takeEndBySeq_     {-@@ -709,30 +705,6 @@ -- To Summary ------------------------------------------------------------------------------ --- | Like 'length', except with a more general 'Num' return value------ Definition:------ >>> lengthGeneric = fmap getSum $ Fold.foldMap (Sum . const  1)--- >>> lengthGeneric = Fold.foldl' (\n _ -> n + 1) 0------ /Pre-release/-{-# INLINE lengthGeneric #-}-lengthGeneric :: (Monad m, Num b) => Fold m a b-lengthGeneric = foldl' (\n _ -> n + 1) 0---- | Determine the length of the input stream.------ Definition:------ >>> length = Fold.lengthGeneric--- >>> length = fmap getSum $ Fold.foldMap (Sum . const  1)----{-# INLINE length #-}-length :: Monad m => Fold m a Int-length = lengthGeneric-- -- | Determine the sum of all elements of a stream of numbers. Returns additive -- identity (@0@) when the stream is empty. Note that this is not numerically -- stable for floating point numbers.@@ -1509,70 +1481,6 @@ {-# INLINE droppingWhile #-} droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a) droppingWhile p = droppingWhileM (return . p)---- Note: Keep this consistent with S.splitOn. In fact we should eliminate--- S.splitOn in favor of the fold.------ XXX Use Fold.many instead once it is fixed.--- > Stream.splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)---- | Like 'takeEndBy' but drops the element on which the predicate succeeds.------ Example:------ >>> input = Stream.fromList "hello\nthere\n"--- >>> line = Fold.takeEndBy_ (== '\n') Fold.toList--- >>> Stream.fold line input--- "hello"------ >>> Stream.fold Fold.toList $ Stream.foldMany line input--- ["hello","there"]----{-# INLINE takeEndBy_ #-}-takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b--- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)-takeEndBy_ predicate (Fold fstep finitial fextract ffinal) =-    Fold step finitial fextract ffinal--    where--    step s a =-        if not (predicate a)-        then fstep s a-        else Done <$> ffinal s---- Note:--- > Stream.splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)---- | Take the input, stop when the predicate succeeds taking the succeeding--- element as well.------ Example:------ >>> input = Stream.fromList "hello\nthere\n"--- >>> line = Fold.takeEndBy (== '\n') Fold.toList--- >>> Stream.fold line input--- "hello\n"------ >>> Stream.fold Fold.toList $ Stream.foldMany line input--- ["hello\n","there\n"]----{-# INLINE takeEndBy #-}-takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b--- takeEndBy predicate = scanMaybe (takingEndBy predicate)-takeEndBy predicate (Fold fstep finitial fextract ffinal) =-    Fold step finitial fextract ffinal--    where--    step s a = do-        res <- fstep s a-        if not (predicate a)-        then return res-        else do-            case res of-                Partial s1 -> Done <$> ffinal s1-                Done b -> return $ Done b  ------------------------------------------------------------------------------ -- Binary splitting on a separator
src/Streamly/Internal/Data/Fold/Type.hs view
@@ -367,6 +367,8 @@     , toList     , toStreamK     , toStreamKRev+    , lengthGeneric+    , length      -- * Combinators @@ -391,6 +393,8 @@     -- ** Trimming     , take     , taking+    , takeEndBy_+    , takeEndBy     , dropping      -- ** Sequential application@@ -767,6 +771,29 @@ toStreamK :: Monad m => Fold m a (K.StreamK n a) toStreamK = foldr K.cons K.nil +-- | Like 'length', except with a more general 'Num' return value+--+-- Definition:+--+-- >>> lengthGeneric = fmap getSum $ Fold.foldMap (Sum . const  1)+-- >>> lengthGeneric = Fold.foldl' (\n _ -> n + 1) 0+--+-- /Pre-release/+{-# INLINE lengthGeneric #-}+lengthGeneric :: (Monad m, Num b) => Fold m a b+lengthGeneric = foldl' (\n _ -> n + 1) 0++-- | Determine the length of the input stream.+--+-- Definition:+--+-- >>> length = Fold.lengthGeneric+-- >>> length = fmap getSum $ Fold.foldMap (Sum . const  1)+--+{-# INLINE length #-}+length :: Monad m => Fold m a Int+length = lengthGeneric+ ------------------------------------------------------------------------------ -- Instances ------------------------------------------------------------------------------@@ -1516,6 +1543,70 @@     extract (Tuple'Fused _ r) = fextract r      final (Tuple'Fused _ r) = ffinal r++-- Note: Keep this consistent with S.splitOn. In fact we should eliminate+-- S.splitOn in favor of the fold.+--+-- XXX Use Fold.many instead once it is fixed.+-- > Stream.splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)++-- | Like 'takeEndBy' but drops the element on which the predicate succeeds.+--+-- Example:+--+-- >>> input = Stream.fromList "hello\nthere\n"+-- >>> line = Fold.takeEndBy_ (== '\n') Fold.toList+-- >>> Stream.fold line input+-- "hello"+--+-- >>> Stream.fold Fold.toList $ Stream.foldMany line input+-- ["hello","there"]+--+{-# INLINE takeEndBy_ #-}+takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b+-- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)+takeEndBy_ predicate (Fold fstep finitial fextract ffinal) =+    Fold step finitial fextract ffinal++    where++    step s a =+        if not (predicate a)+        then fstep s a+        else Done <$> ffinal s++-- Note:+-- > Stream.splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)++-- | Take the input, stop when the predicate succeeds taking the succeeding+-- element as well.+--+-- Example:+--+-- >>> input = Stream.fromList "hello\nthere\n"+-- >>> line = Fold.takeEndBy (== '\n') Fold.toList+-- >>> Stream.fold line input+-- "hello\n"+--+-- >>> Stream.fold Fold.toList $ Stream.foldMany line input+-- ["hello\n","there\n"]+--+{-# INLINE takeEndBy #-}+takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b+-- takeEndBy predicate = scanMaybe (takingEndBy predicate)+takeEndBy predicate (Fold fstep finitial fextract ffinal) =+    Fold step finitial fextract ffinal++    where++    step s a = do+        res <- fstep s a+        if not (predicate a)+        then return res+        else do+            case res of+                Partial s1 -> Done <$> ffinal s1+                Done b -> return $ Done b  ------------------------------------------------------------------------------ -- Nesting
src/Streamly/Internal/Data/MutArray.hs view
@@ -11,81 +11,197 @@     -- * MutArray.Type module       module Streamly.Internal.Data.MutArray.Type     -- * MutArray module-    , splitOn-    , genSlicesFromLen-    , getSlicesFromLen-    , fromStream+    , sliceIndexerFromLen+    , slicerFromLen+    , compactLE+    , pinnedCompactLE+    , compactOnByte+    , compactOnByteSuffix     -- * Unboxed IORef     , module Streamly.Internal.Data.IORef.Unboxed++    -- * Deprecated+    , genSlicesFromLen+    , getSlicesFromLen     ) where  #include "inline.hs"  import Control.Monad.IO.Class (MonadIO(..))+import Data.Word (Word8)+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))+import Streamly.Internal.Data.Stream.Type (Stream) import Streamly.Internal.Data.Unbox (Unbox)-import Streamly.Internal.Data.Stream (Stream) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) -import qualified Streamly.Internal.Data.Stream as D+import qualified Streamly.Internal.Data.Stream.Type as Stream+import qualified Streamly.Internal.Data.Stream.Nesting as Stream+-- import qualified Streamly.Internal.Data.Stream.Transform as Stream import qualified Streamly.Internal.Data.Unfold as Unfold  import Prelude hiding (foldr, length, read, splitAt) import Streamly.Internal.Data.MutArray.Type import Streamly.Internal.Data.IORef.Unboxed --- | Split the array into a stream of slices using a predicate. The element--- matching the predicate is dropped.------ /Pre-release/-{-# INLINE splitOn #-}-splitOn :: (MonadIO m, Unbox a) =>-    (a -> Bool) -> MutArray a -> Stream m (MutArray a)-splitOn predicate arr =-    fmap (\(i, len) -> getSliceUnsafe i len arr)-        $ D.sliceOnSuffix predicate (read arr)- -- | Generate a stream of array slice descriptors ((index, len)) of specified -- length from an array, starting from the supplied array index. The last slice -- may be shorter than the requested length depending on the array length. -- -- /Pre-release/-{-# INLINE genSlicesFromLen #-}-genSlicesFromLen :: forall m a. (Monad m, Unbox a)+{-# INLINE sliceIndexerFromLen #-}+sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)     => Int -- ^ from index     -> Int -- ^ length of the slice     -> Unfold m (MutArray a) (Int, Int)-genSlicesFromLen from len =+sliceIndexerFromLen from len =     let fromThenTo n = (from, from + len, n - 1)         mkSlice n i = return (i, min len (n - i))      in Unfold.lmap length         $ Unfold.mapM2 mkSlice         $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo +{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}+genSlicesFromLen :: forall m a. (Monad m, Unbox a)+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> Unfold m (MutArray a) (Int, Int)+genSlicesFromLen = sliceIndexerFromLen+ -- | Generate a stream of slices of specified length from an array, starting -- from the supplied array index. The last slice may be shorter than the -- requested length depending on the array length. -- -- /Pre-release/-{-# INLINE getSlicesFromLen #-}-getSlicesFromLen :: forall m a. (Monad m, Unbox a)+{-# INLINE slicerFromLen #-}+slicerFromLen :: forall m a. (Monad m, Unbox a)     => Int -- ^ from index     -> Int -- ^ length of the slice     -> Unfold m (MutArray a) (MutArray a)-getSlicesFromLen from len =+slicerFromLen from len =     let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr-     in Unfold.mapM2 mkSlice (genSlicesFromLen from len)+     in Unfold.mapM2 mkSlice (sliceIndexerFromLen from len) --- | Create an 'Array' from a stream. This is useful when we want to create a--- single array from a stream of unknown size. 'writeN' is at least twice--- as efficient when the size is already known.+{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}+getSlicesFromLen :: forall m a. (Monad m, Unbox a)+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> Unfold m (MutArray a) (MutArray a)+getSlicesFromLen = slicerFromLen++-------------------------------------------------------------------------------+-- Compacting Streams of Arrays+-------------------------------------------------------------------------------++-- | @compactLE maxElems@ coalesces adjacent arrays in the input stream+-- only if the combined size would be less than or equal to @maxElems@+-- elements. Note that it won't split an array if the original array is already+-- larger than maxElems. ----- Note that if the input stream is too large memory allocation for the array--- may fail.  When the stream size is not known, `chunksOf` followed by--- processing of indvidual arrays in the resulting stream should be preferred.+-- @maxElems@ must be greater than 0. ----- /Pre-release/-{-# INLINE fromStream #-}-fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)-fromStream = fromStreamD--- fromStream (Stream m) = P.fold write m+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE compactLE #-}+compactLE :: (MonadIO m, Unbox a) =>+    Int -> Stream m (MutArray a) -> Stream m (MutArray a)+-- XXX compactLE can be moved to MutArray/Type if we are not using the parser+-- to implement it.+compactLE = compactLeAs Unpinned+-- The parser version turns out to be a little bit slower.+-- compactLE n = Stream.catRights . Stream.parseManyD (pCompactLE n)++-- | Pinned version of 'compactLE'.+{-# INLINE pinnedCompactLE #-}+pinnedCompactLE :: forall m a. (MonadIO m, Unbox a)+    => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+pinnedCompactLE = compactLeAs Pinned+-- pinnedCompactLE n = Stream.catRights . Stream.parseManyD (pPinnedCompactLE n)++data SplitState s arr+    = Initial s+    | Buffering s arr+    | Splitting s arr+    | Yielding arr (SplitState s arr)+    | Finishing++-- | Split a stream of arrays on a given separator byte, dropping the separator+-- and coalescing all the arrays between two separators into a single array.+--+{-# INLINE_NORMAL _compactOnByteCustom #-}+_compactOnByteCustom+    :: MonadIO m+    => Word8+    -> Stream m (MutArray Word8)+    -> Stream m (MutArray Word8)+_compactOnByteCustom byte (Stream.Stream step state) =+    Stream.Stream step' (Initial state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (Initial st) = do+        r <- step gst st+        case r of+            Stream.Yield arr s -> do+                (arr1, marr2) <- breakOn byte arr+                return $ case marr2 of+                    Nothing   -> Stream.Skip (Buffering s arr1)+                    Just arr2 -> Stream.Skip (Yielding arr1 (Splitting s arr2))+            Stream.Skip s -> return $ Stream.Skip (Initial s)+            Stream.Stop -> return Stream.Stop++    step' gst (Buffering st buf) = do+        r <- step gst st+        case r of+            Stream.Yield arr s -> do+                (arr1, marr2) <- breakOn byte arr+                -- XXX Use spliceExp instead and then rightSize?+                buf1 <- splice buf arr1+                return $ case marr2 of+                    Nothing -> Stream.Skip (Buffering s buf1)+                    Just x -> Stream.Skip (Yielding buf1 (Splitting s x))+            Stream.Skip s -> return $ Stream.Skip (Buffering s buf)+            Stream.Stop -> return $+                if byteLength buf == 0+                then Stream.Stop+                else Stream.Skip (Yielding buf Finishing)++    step' _ (Splitting st buf) = do+        (arr1, marr2) <- breakOn byte buf+        return $ case marr2 of+                Nothing -> Stream.Skip $ Buffering st arr1+                Just arr2 -> Stream.Skip $ Yielding arr1 (Splitting st arr2)++    step' _ (Yielding arr next) = return $ Stream.Yield arr next+    step' _ Finishing = return Stream.Stop++-- XXX implement predicate based version of this+-- XXX Naming of predicate based vs custom version++-- | Split a stream of arrays on a given separator byte, dropping the separator+-- and coalescing all the arrays between two separators into a single array.+--+{-# INLINE compactOnByte #-}+compactOnByte+    :: (MonadIO m)+    => Word8+    -> Stream m (MutArray Word8)+    -> Stream m (MutArray Word8)+-- XXX compare perf of custom vs idiomatic version+-- compactOnByte = _compactOnByteCustom+-- XXX use spliceExp and rightSize?+compactOnByte byte = Stream.splitInnerBy (breakOn byte) splice++-- | Like 'compactOnByte' considers the separator in suffix position instead of+-- infix position.+{-# INLINE compactOnByteSuffix #-}+compactOnByteSuffix+    :: (MonadIO m)+    => Word8+    -> Stream m (MutArray Word8)+    -> Stream m (MutArray Word8)+compactOnByteSuffix byte =+        -- XXX use spliceExp and rightSize?+        Stream.splitInnerBySuffix+            (\arr -> byteLength arr == 0) (breakOn byte) splice
src/Streamly/Internal/Data/MutArray/Generic.hs view
@@ -19,14 +19,14 @@     , nil      -- *** Uninitialized Arrays-    , new+    , emptyOf     -- , newArrayWith      -- *** From streams-    , writeNUnsafe-    , writeN-    , writeWith-    , write+    , unsafeCreateOf+    , createOf+    , createWith -- createOfMin/createMin/createGE?+    , create     , fromStreamN     , fromStream     , fromPureStream@@ -160,6 +160,13 @@     -- , appendSliceFrom      , clone++    -- * Deprecated+    , new+    , writeNUnsafe+    , writeN+    , writeWith+    , write     ) where @@ -236,13 +243,13 @@ -- XXX Would be nice if GHC can provide something like newUninitializedArray# so -- that we do not have to write undefined or error in the whole array. --- | @new count@ allocates a zero length array that can be extended to hold+-- | @emptyOf count@ allocates a zero length array that can be extended to hold -- up to 'count' items without reallocating. -- -- /Pre-release/-{-# INLINE new #-}-new :: MonadIO m => Int -> m (MutArray a)-new n@(I# n#) =+{-# INLINE emptyOf #-}+emptyOf :: MonadIO m => Int -> m (MutArray a)+emptyOf n@(I# n#) =     liftIO         $ IO         $ \s# ->@@ -251,6 +258,12 @@                       let ma = MutArray arr# 0 0 n                        in (# s1#, ma #) +-- XXX Deprecate in major+-- {-# DEPRECATED new "Please use emptyOf instead." #-}+{-# INLINE new #-}+new :: MonadIO m => Int -> m (MutArray a)+new = emptyOf+ -- XXX This could be pure?  -- |@@ -318,7 +331,7 @@      where -    step () (i, x) = liftIO (putIndex i arr x)+    step () (i, x) = putIndex i arr x  -- | Modify a given index of an array using a modifier function without checking -- the bounds.@@ -589,16 +602,16 @@ arrayChunkSize :: Int arrayChunkSize = 1024 --- | Like 'writeN' but does not check the array bounds when writing. The fold+-- | Like 'createOf' but does not check the array bounds when writing. The fold -- driver must not call the step function more than 'n' times otherwise it will -- corrupt the memory and crash. This function exists mainly because any -- conditional in the step function blocks fusion causing 10x performance -- slowdown. -- -- /Pre-release/-{-# INLINE_NORMAL writeNUnsafe #-}-writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)-writeNUnsafe n = Fold step initial return return+{-# INLINE_NORMAL unsafeCreateOf #-}+unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)+unsafeCreateOf n = Fold step initial return return      where @@ -606,59 +619,81 @@      step arr x = FL.Partial <$> snocUnsafe arr x --- | @writeN n@ folds a maximum of @n@ elements from the input stream to an+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}+{-# INLINE writeNUnsafe #-}+writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)+writeNUnsafe = unsafeCreateOf++-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an -- 'Array'. ----- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n) -- -- /Pre-release/-{-# INLINE_NORMAL writeN #-}+{-# INLINE_NORMAL createOf #-}+createOf :: MonadIO m => Int -> Fold m a (MutArray a)+createOf n = FL.take n $ unsafeCreateOf n++-- XXX Deprecate in major+-- {-# DEPRECATED writeN "Please use createOf instead." #-}+{-# INLINE writeN #-} writeN :: MonadIO m => Int -> Fold m a (MutArray a)-writeN n = FL.take n $ writeNUnsafe n+writeN = createOf  -- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n) -- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n) -- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n) --- | @writeWith minCount@ folds the whole input to a single array. The array+-- | @createWith minCount@ folds the whole input to a single array. The array -- starts at a size big enough to hold minCount elements, the size is doubled -- every time the array needs to be grown. -- -- /Caution! Do not use this on infinite streams./ -- -- /Pre-release/-{-# INLINE_NORMAL writeWith #-}-writeWith :: MonadIO m => Int -> Fold m a (MutArray a)+{-# INLINE_NORMAL createWith #-}+createWith :: MonadIO m => Int -> Fold m a (MutArray a) -- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)-writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial+createWith elemCount = FL.rmapM extract $ FL.foldlM' step initial      where      initial = do-        when (elemCount < 0) $ error "writeWith: elemCount is negative"-        liftIO $ new elemCount+        when (elemCount < 0) $ error "createWith: elemCount is negative"+        new elemCount      step arr@(MutArray _ start end bound) x         | end == bound = do         let oldSize = end - start             newSize = max (oldSize * 2) 1-        arr1 <- liftIO $ realloc newSize arr+        arr1 <- realloc newSize arr         snocUnsafe arr1 x     step arr x = snocUnsafe arr x -    -- extract = liftIO . rightSize+    -- extract = rightSize     extract = return +{-# DEPRECATED writeWith "Please use createWith instead." #-}+{-# INLINE writeWith #-}+writeWith :: MonadIO m => Int -> Fold m a (MutArray a)+writeWith = createWith+ -- | Fold the whole input to a single array. ----- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes+-- Same as 'createWith' using an initial array size of 'arrayChunkSize' bytes -- rounded up to the element size. -- -- /Caution! Do not use this on infinite streams./ --+{-# INLINE create #-}+create :: MonadIO m => Fold m a (MutArray a)+create = writeWith arrayChunkSize++-- XXX Deprecate in major+-- {-# DEPRECATED write "Please use create instead." #-} {-# INLINE write #-} write :: MonadIO m => Fold m a (MutArray a)-write = writeWith arrayChunkSize+write = create  -- | Create a 'MutArray' from the first @n@ elements of a stream. The -- array is allocated to size @n@, if the stream terminates before @n@@@ -683,7 +718,7 @@ {-# INLINABLE fromPureStream #-} fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a) fromPureStream xs =-    liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs+    D.fold write $ D.morphInner (return . runIdentity) xs  ------------------------------------------------------------------------------- -- Chunking@@ -815,7 +850,7 @@  {-# INLINE clone #-} clone :: MonadIO m => MutArray a -> m (MutArray a)-clone src = liftIO $ do+clone src = do     let len = arrLen src     dst <- new len     putSliceUnsafe src 0 dst 0 len
src/Streamly/Internal/Data/MutArray/Stream.hs view
@@ -12,6 +12,7 @@ -- keep this as a separate module and release it. -- module Streamly.Internal.Data.MutArray.Stream+{-# DEPRECATED "Please use \"Streamly.Internal.Data.MutArray\" instead." #-}     (     -- * Generation       MArray.chunksOf@@ -21,7 +22,7 @@      -- * Compaction     , packArraysChunksOf-    , SpliceState (..)+    , MArray.SpliceState (..)     , lpackArraysChunksOf     , compact -- chunksCompact     , compactLE@@ -35,19 +36,12 @@     ) where -#include "inline.hs"-#include "ArrayMacros.h"- import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad (when)-import Data.Bifunctor (first)-import Data.Proxy (Proxy(..))-import Streamly.Internal.Data.Unbox (Unbox, sizeOf)+import Streamly.Internal.Data.Unbox (Unbox) import Streamly.Internal.Data.MutArray.Type (MutArray(..)) import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Internal.Data.Parser (ParseError) import Streamly.Internal.Data.Stream.Type (Stream)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))  import qualified Streamly.Internal.Data.MutArray as MArray import qualified Streamly.Internal.Data.Fold.Type as FL@@ -58,12 +52,6 @@ -- Compact ------------------------------------------------------------------------------- -data SpliceState s arr-    = SpliceInitial s-    | SpliceBuffering s arr-    | SpliceYielding arr (SpliceState s arr)-    | SpliceFinish- -- XXX This can be removed once compactLEFold/compactLE are implemented. -- -- | This mutates the first array (if it has space) to append values from the@@ -78,117 +66,18 @@ -- actual array size may be less than the specified chunk size. -- -- @since 0.7.0-{-# INLINE_NORMAL packArraysChunksOf #-}+{-# INLINE packArraysChunksOf #-} packArraysChunksOf :: (MonadIO m, Unbox a)     => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)-packArraysChunksOf n (D.Stream step state) =-    D.Stream step' (SpliceInitial state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (SpliceInitial st) = do-        when (n <= 0) $-            -- XXX we can pass the module string from the higher level API-            error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "-                 ++ "arrays [" ++ show n ++ "] must be a natural number"-        r <- step gst st-        case r of-            D.Yield arr s -> return $-                let len = MArray.byteLength arr-                 in if len >= n-                    then D.Skip (SpliceYielding arr (SpliceInitial s))-                    else D.Skip (SpliceBuffering s arr)-            D.Skip s -> return $ D.Skip (SpliceInitial s)-            D.Stop -> return D.Stop--    step' gst (SpliceBuffering st buf) = do-        r <- step gst st-        case r of-            D.Yield arr s -> do-                let len = MArray.byteLength buf + MArray.byteLength arr-                if len > n-                then return $-                    D.Skip (SpliceYielding buf (SpliceBuffering s arr))-                else do-                    buf' <- if MArray.byteCapacity buf < n-                            then liftIO $ MArray.realloc n buf-                            else return buf-                    buf'' <- MArray.splice buf' arr-                    return $ D.Skip (SpliceBuffering s buf'')-            D.Skip s -> return $ D.Skip (SpliceBuffering s buf)-            D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)--    step' _ SpliceFinish = return D.Stop--    step' _ (SpliceYielding arr next) = return $ D.Yield arr next+packArraysChunksOf = MArray.compactLE  -- XXX Remove this once compactLEFold is implemented -- lpackArraysChunksOf = Fold.many compactLEFold ---{-# INLINE_NORMAL lpackArraysChunksOf #-}+{-# INLINE lpackArraysChunksOf #-} lpackArraysChunksOf :: (MonadIO m, Unbox a)     => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()-lpackArraysChunksOf n (Fold step1 initial1 _ final1) =-    Fold step initial extract final--    where--    initial = do-        when (n <= 0) $-            -- XXX we can pass the module string from the higher level API-            error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "-                 ++ "arrays [" ++ show n ++ "] must be a natural number"--        r <- initial1-        return $ first (Tuple' Nothing) r--    step (Tuple' Nothing r1) arr =-            let len = MArray.byteLength arr-             in if len >= n-                then do-                    r <- step1 r1 arr-                    case r of-                        FL.Done _ -> return $ FL.Done ()-                        FL.Partial s -> do-                            _ <- final1 s-                            res <- initial1-                            return $ first (Tuple' Nothing) res-                else return $ FL.Partial $ Tuple' (Just arr) r1--    step (Tuple' (Just buf) r1) arr = do-            let len = MArray.byteLength buf + MArray.byteLength arr-            buf' <- if MArray.byteCapacity buf < len-                    then liftIO $ MArray.realloc (max n len) buf-                    else return buf-            buf'' <- MArray.splice buf' arr--            -- XXX this is common in both the equations of step-            if len >= n-            then do-                r <- step1 r1 buf''-                case r of-                    FL.Done _ -> return $ FL.Done ()-                    FL.Partial s -> do-                        _ <- final1 s-                        res <- initial1-                        return $ first (Tuple' Nothing) res-            else return $ FL.Partial $ Tuple' (Just buf'') r1--    -- XXX Several folds do extract >=> final, therefore, we need to make final-    -- return  "m b" rather than using extract post it if we want extract to be-    -- partial.-    ---    -- extract forces the pending buffer to be sent to the fold which is not-    -- what we want.-    extract _ = error "lpackArraysChunksOf: not designed for scanning"--    final (Tuple' Nothing r1) = final1 r1-    final (Tuple' (Just buf) r1) = do-        r <- step1 r1 buf-        case r of-            FL.Partial rr -> final1 rr-            FL.Done _ -> return ()+lpackArraysChunksOf = MArray.lCompactGE  -- XXX Same as compactLE, to be removed once that is implemented. --@@ -208,48 +97,11 @@ -- actual array size may be less than the specified chunk size. -- -- /Internal/-{-# INLINE_NORMAL compactLEParserD #-}+{-# INLINE compactLEParserD #-} compactLEParserD ::        forall m a. (MonadIO m, Unbox a)     => Int -> ParserD.Parser (MutArray a) m (MutArray a)-compactLEParserD n = ParserD.Parser step initial extract--    where--    nBytes = n * SIZE_OF(a)--    initial =-        return-            $ if n <= 0-              then error-                       $ functionPath-                       ++ ": the size of arrays ["-                       ++ show n ++ "] must be a natural number"-              else ParserD.IPartial Nothing--    step Nothing arr =-        return-            $ let len = MArray.byteLength arr-               in if len >= nBytes-                  then ParserD.Done 0 arr-                  else ParserD.Partial 0 (Just arr)-    step (Just buf) arr =-        let len = MArray.byteLength buf + MArray.byteLength arr-         in if len > nBytes-            then return $ ParserD.Done 1 buf-            else do-                buf1 <--                    if MArray.byteCapacity buf < nBytes-                    then liftIO $ MArray.realloc nBytes buf-                    else return buf-                buf2 <- MArray.splice buf1 arr-                return $ ParserD.Partial 0 (Just buf2)--    extract Nothing = return $ ParserD.Done 0 MArray.nil-    extract (Just buf) = return $ ParserD.Done 0 buf--    functionPath =-        "Streamly.Internal.Data.Stream.MutChunked.compactLEParserD"+compactLEParserD = MArray.pCompactLE  -- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a -- minimum specified size. Note that if all the arrays in the stream together@@ -258,47 +110,11 @@ -- the specified size we stop coalescing further. -- -- /Internal/-{-# INLINE_NORMAL compactGEFold #-}+{-# INLINE compactGEFold #-} compactGEFold ::        forall m a. (MonadIO m, Unbox a)     => Int -> FL.Fold m (MutArray a) (MutArray a)-compactGEFold n = Fold step initial extract extract--    where--    nBytes = n * SIZE_OF(a)--    initial =-        return-            $ if n < 0-              then error-                       $ functionPath-                       ++ ": the size of arrays ["-                       ++ show n ++ "] must be a natural number"-              else FL.Partial Nothing--    step Nothing arr =-        return-            $ let len = MArray.byteLength arr-               in if len >= nBytes-                  then FL.Done arr-                  else FL.Partial (Just arr)-    step (Just buf) arr = do-        let len = MArray.byteLength buf + MArray.byteLength arr-        buf1 <--            if MArray.byteCapacity buf < len-            then liftIO $ MArray.realloc (max len nBytes) buf-            else return buf-        buf2 <- MArray.splice buf1 arr-        if len >= n-        then return $ FL.Done buf2-        else return $ FL.Partial (Just buf2)--    extract Nothing = return MArray.nil-    extract (Just buf) = return buf--    functionPath =-        "Streamly.Internal.Data.Stream.MutChunked.compactGEFold"+compactGEFold = MArray.fCompactGE  -- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a -- maximum specified size in bytes.
src/Streamly/Internal/Data/MutArray/Type.hs view
@@ -31,2382 +31,3221 @@     -- ** Type     -- $arrayNotes       MutArray (..)-    , MutByteArray-    , MutableByteArray-    , pin-    , unpin-    , isPinned--    -- -- * Constructing and Writing-    -- ** Construction-    , nil--    -- *** Uninitialized Arrays-    , pinnedNew-    , pinnedNewBytes-    , pinnedNewAligned-    , new-    , newArrayWith--    -- *** From streams-    , ArrayUnsafe (..)-    , writeNWithUnsafe-    , writeNWith-    , writeNUnsafe-    , pinnedWriteNUnsafe-    , writeN-    , pinnedWriteN-    , pinnedWriteNAligned--    , writeWith-    , write-    , pinnedWrite--    , writeRevN-    -- , writeRev--    -- *** From containers-    , fromListN-    , pinnedFromListN-    , fromList-    , pinnedFromList-    , fromListRevN-    , fromListRev-    , fromStreamDN-    , fromStreamD-    , fromPureStream--    -- ** Random writes-    , putIndex-    , putIndexUnsafe-    , putIndices-    -- , putFromThenTo-    -- , putFrom -- start writing at the given position-    -- , putUpto -- write from beginning up to the given position-    -- , putFromTo-    -- , putFromRev-    -- , putUptoRev-    , modifyIndexUnsafe-    , modifyIndex-    , modifyIndices-    , modify-    , swapIndices-    , unsafeSwapIndices--    -- ** Growing and Shrinking-    -- | Arrays grow only at the end, though it is possible to grow on both sides-    -- and therefore have a cons as well as snoc. But that will require two-    -- bounds in the array representation.--    -- *** Appending elements-    , snocWith-    , snoc-    , snocLinear-    , snocMay-    , snocUnsafe--    -- *** Appending streams-    , writeAppendNUnsafe-    , writeAppendN-    , writeAppendWith-    , writeAppend--    -- ** Eliminating and Reading--    -- *** To streams-    , reader-    , readerRevWith-    , readerRev--    -- *** To containers-    , toStreamDWith-    , toStreamDRevWith-    , toStreamKWith-    , toStreamKRevWith-    , read-    , readRev-    , toStreamK-    , toStreamKRev-    , toList--    -- experimental-    , producerWith-    , producer--    -- *** Random reads-    , getIndex-    , getIndexUnsafe-    , getIndices-    , getIndicesD-    -- , getFromThenTo-    , getIndexRev--    -- ** Memory Management-    , blockSize-    , arrayChunkBytes-    , allocBytesToElemCount-    , realloc-    , resize-    , resizeExp-    , rightSize--    -- ** Size-    , length-    , byteLength-    -- , capacity-    , byteCapacity-    , bytesFree--    -- ** In-place Mutation Algorithms-    , strip-    , reverse-    , permute-    , partitionBy-    , shuffleBy-    , divideBy-    , mergeBy-    , bubble--    -- ** Casting-    , cast-    , castUnsafe-    , asBytes-    , asPtrUnsafe--    -- ** Folding-    , foldl'-    , foldr-    , cmp--    -- ** Arrays of arrays-    --  We can add dimensionality parameter to the array type to get-    --  multidimensional arrays. Multidimensional arrays would just be a-    --  convenience wrapper on top of single dimensional arrays.--    -- | Operations dealing with multiple arrays, streams of arrays or-    -- multidimensional array representations.--    -- *** Construct from streams-    , chunksOf-    , pinnedChunksOf-    , writeChunks--    -- *** Eliminate to streams-    , flattenArrays-    , flattenArraysRev-    , fromArrayStreamK--    -- *** Construct from arrays-    -- get chunks without copying-    , getSliceUnsafe-    , getSlice-    -- , getSlicesFromLenN-    , splitAt -- XXX should be able to express using getSlice-    , breakOn--    -- ** Cloning arrays-    , clone-    , pinnedClone--    -- ** Appending arrays-    , spliceCopy-    , spliceWith-    , splice-    , spliceExp-    , spliceUnsafe-    -- , putSlice-    -- , appendSlice-    -- , appendSliceFrom--    -- ** Utilities-    , roundUpToPower2-    , memcpy-    , memcmp-    , c_memchr-    )-where--#include "assert.hs"-#include "inline.hs"-#include "ArrayMacros.h"-#include "MachDeps.h"--import Control.Monad (when, void)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Bits (shiftR, (.|.), (.&.))-import Data.Functor.Identity (Identity(..))-import Data.Proxy (Proxy(..))-import Data.Word (Word8)-import Foreign.C.Types (CSize(..), CInt(..))-import Foreign.Ptr (plusPtr, minusPtr, nullPtr)-import Streamly.Internal.Data.MutByteArray.Type-    ( MutByteArray(..)-    , MutableByteArray-    , PinnedState(..)-    , getMutableByteArray#-    , putSliceUnsafe-    )-import Streamly.Internal.Data.Unbox (Unbox(..))-import GHC.Base-    ( IO(..)-    , Int(..)-    , compareByteArrays#-    , copyMutableByteArray#-    )-import GHC.Base (noinline)-import GHC.Exts (unsafeCoerce#)-import GHC.Ptr (Ptr(..))--import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Producer.Type (Producer (..))-import Streamly.Internal.Data.Stream.Type (Stream)-import Streamly.Internal.Data.StreamK.Type (StreamK)-import Streamly.Internal.Data.SVar.Type (adaptState, defState)-import Streamly.Internal.Data.Unfold.Type (Unfold(..))-import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)--import qualified Streamly.Internal.Data.Fold.Type as FL-import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed-import qualified Streamly.Internal.Data.Producer as Producer-import qualified Streamly.Internal.Data.Stream.Type as D-import qualified Streamly.Internal.Data.Stream.Lift as D-import qualified Streamly.Internal.Data.StreamK.Type as K-import qualified Prelude--import Prelude hiding-    (Foldable(..), read, unlines, splitAt, reverse, truncate)--#include "DocTestDataMutArray.hs"------------------------------------------------------------------------------------ Foreign helpers----------------------------------------------------------------------------------foreign import ccall unsafe "string.h memcpy" c_memcpy-    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)--foreign import ccall unsafe "string.h memchr" c_memchr-    :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)--foreign import ccall unsafe "string.h memcmp" c_memcmp-    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt---- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return--- how many elements of that type will completely fit in those bytes.----{-# INLINE bytesToElemCount #-}-bytesToElemCount :: forall a. Unbox a => a -> Int -> Int-bytesToElemCount _ n = n `div` SIZE_OF(a)---- XXX we are converting Int to CSize-memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()-memcpy dst src len = void (c_memcpy dst src (fromIntegral len))---- XXX we are converting Int to CSize--- return True if the memory locations have identical contents-{-# INLINE memcmp #-}-memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO Bool-memcmp p1 p2 len = do-    r <- c_memcmp p1 p2 (fromIntegral len)-    return $ r == 0------------------------------------------------------------------------------------ MutArray Data Type------------------------------------------------------------------------------------ $arrayNotes------ We can use an 'Unboxed' constraint in the MutArray type and the constraint--- can be automatically provided to a function that pattern matches on the--- MutArray type. However, it has huge performance cost, so we do not use it.--- Investigate a GHC improvement possiblity.---- | An unboxed mutable array. An array is created with a given length--- and capacity. Length is the number of valid elements in the array.  Capacity--- is the maximum number of elements that the array can be expanded to without--- having to reallocate the memory.------ The elements in the array can be mutated in-place without changing the--- reference (constructor). However, the length of the array cannot be mutated--- in-place.  A new array reference is generated when the length changes.  When--- the length is increased (upto the maximum reserved capacity of the array),--- the array is not reallocated and the new reference uses the same underlying--- memory as the old one.------ Several routines in this module allow the programmer to control the capacity--- of the array. The programmer can control the trade-off between memory usage--- and performance impact due to reallocations when growing or shrinking the--- array.----data MutArray a =-#ifdef DEVBUILD-    Unbox a =>-#endif-    -- The array is a range into arrContents. arrContents may be a superset of-    -- the slice represented by the array. All offsets are in bytes.-    MutArray-    { arrContents :: {-# UNPACK #-} !MutByteArray-    , arrStart :: {-# UNPACK #-} !Int  -- ^ index into arrContents-    , arrEnd   :: {-# UNPACK #-} !Int    -- ^ index into arrContents-                                       -- Represents the first invalid index of-                                       -- the array.-    , arrBound :: {-# UNPACK #-} !Int    -- ^ first invalid index of arrContents.-    }------------------------------------------------------------------------------------ Pinning & Unpinning------------------------------------------------------------------------------------ | Return a copy of the array in pinned memory if unpinned, else return the--- original array.-{-# INLINE pin #-}-pin :: MutArray a -> IO (MutArray a)-pin arr@MutArray{..} =-    if Unboxed.isPinned arrContents-    then pure arr-    else pinnedClone arr---- | Return a copy of the array in unpinned memory if pinned, else return the--- original array.-{-# INLINE unpin #-}-unpin :: MutArray a -> IO (MutArray a)-unpin arr@MutArray{..} =-    if Unboxed.isPinned arrContents-    then clone arr-    else pure arr---- | Return 'True' if the array is allocated in pinned memory.-{-# INLINE isPinned #-}-isPinned :: MutArray a -> Bool-isPinned MutArray{..} = Unboxed.isPinned arrContents------------------------------------------------------------------------------------ Construction------------------------------------------------------------------------------------ XXX Change the names to use "new" instead of "newArray". That way we can use--- the same names for managed file system objects as well. For unmanaged ones--- we can use open/create etc as usual.------ A new array is similar to "touch" creating a zero length file. An mmapped--- array would be similar to a sparse file with holes. TBD: support mmapped--- files and arrays.---- GHC always guarantees word-aligned memory, alignment is important only when--- we need more than that.  See stg_pinnedNewAlignedByteArrayzh and--- allocatePinned in GHC source.---- | @newArrayWith allocator alignment count@ allocates a new array of zero--- length and with a capacity to hold @count@ elements, using @allocator--- size alignment@ as the memory allocator function.------ Alignment must be greater than or equal to machine word size and a power of--- 2.------ Alignment is ignored if the allocator allocates unpinned memory.------ /Pre-release/-{-# INLINE newArrayWith #-}-newArrayWith :: forall m a. (MonadIO m, Unbox a)-    => (Int -> Int -> m MutByteArray) -> Int -> Int -> m (MutArray a)-newArrayWith alloc alignSize count = do-    let size = max (count * SIZE_OF(a)) 0-    contents <- alloc size alignSize-    return $ MutArray-        { arrContents = contents-        , arrStart = 0-        , arrEnd   = 0-        , arrBound = size-        }--nil ::-#ifdef DEVBUILD-    Unbox a =>-#endif-    MutArray a-nil = MutArray Unboxed.nil 0 0 0--{-# INLINE newBytesAs #-}-newBytesAs :: MonadIO m =>-#ifdef DEVBUILD-    Unbox a =>-#endif-    PinnedState -> Int -> m (MutArray a)-newBytesAs ps bytes = do-    contents <- liftIO $ Unboxed.newBytesAs ps bytes-    return $ MutArray-        { arrContents = contents-        , arrStart = 0-        , arrEnd   = 0-        , arrBound = bytes-        }---- | Allocates a pinned empty array that can hold 'count' items.  The memory of--- the array is uninitialized and the allocation is aligned as per the--- 'Unboxed' instance of the type.------ /Pre-release/-{-# INLINE pinnedNewBytes #-}-pinnedNewBytes :: MonadIO m =>-#ifdef DEVBUILD-    Unbox a =>-#endif-    Int -> m (MutArray a)-pinnedNewBytes = newBytesAs Pinned---- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and--- the alignment is dictated by the 'Unboxed' instance of the type.------ /Internal/-{-# INLINE pinnedNewAligned #-}-pinnedNewAligned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)-pinnedNewAligned =-    newArrayWith (\s a -> liftIO $ Unboxed.pinnedNewAlignedBytes s a)--{-# INLINE newAs #-}-newAs :: (MonadIO m, Unbox a) => PinnedState -> Int -> m (MutArray a)-newAs ps =-    newArrayWith-        (\s _ -> liftIO $ Unboxed.newBytesAs ps s)-        (error "new: alignment is not used in unpinned arrays.")---- XXX can unaligned allocation be more efficient when alignment is not needed?------ | Allocates an empty pinned array that can hold 'count' items.  The memory of--- the array is uninitialized and the allocation is aligned as per the 'Unboxed'--- instance of the type.----{-# INLINE pinnedNew #-}-pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)-pinnedNew = newAs Pinned---- | Allocates an empty unpinned array that can hold 'count' items.  The memory--- of the array is uninitialized.----{-# INLINE new #-}-new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)-new = newAs Unpinned------------------------------------------------------------------------------------ Random writes------------------------------------------------------------------------------------ | Write the given element to the given index of the array. Does not check if--- the index is out of bounds of the array.------ /Pre-release/-{-# INLINE putIndexUnsafe #-}-putIndexUnsafe :: forall m a. (MonadIO m, Unbox a)-    => Int -> MutArray a -> a -> m ()-putIndexUnsafe i MutArray{..} x = do-    let index = INDEX_OF(arrStart, i, a)-    assert (i >= 0 && INDEX_VALID(index, arrEnd, a)) (return ())-    liftIO $ pokeAt index arrContents  x--invalidIndex :: String -> Int -> a-invalidIndex label i =-    error $ label ++ ": invalid array index " ++ show i---- | /O(1)/ Write the given element at the given index in the array.--- Performs in-place mutation of the array.------ >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))--- >>> f = MutArray.putIndices--- >>> putIndex ix arr val = Stream.fold (f arr) (Stream.fromPure (ix, val))----{-# INLINE putIndex #-}-putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()-putIndex i MutArray{..} x = do-    let index = INDEX_OF(arrStart,i,a)-    if i >= 0 && INDEX_VALID(index,arrEnd,a)-    then liftIO $ pokeAt index arrContents  x-    else invalidIndex "putIndex" i---- | Write an input stream of (index, value) pairs to an array. Throws an--- error if any index is out of bounds.------ /Pre-release/-{-# INLINE putIndices #-}-putIndices :: forall m a. (MonadIO m, Unbox a)-    => MutArray a -> Fold m (Int, a) ()-putIndices arr = FL.foldlM' step (return ())--    where--    step () (i, x) = liftIO (putIndex i arr x)---- | Modify a given index of an array using a modifier function.------ Unsafe because it does not check the bounds of the array.------ /Pre-release/-modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> (a -> (a, b)) -> m b-modifyIndexUnsafe i MutArray{..} f = liftIO $ do-        let index = INDEX_OF(arrStart,i,a)-        assert (i >= 0 && INDEX_NEXT(index,a) <= arrEnd) (return ())-        r <- peekAt index arrContents-        let (x, res) = f r-        pokeAt index arrContents  x-        return res---- | Modify a given index of an array using a modifier function.------ /Pre-release/-modifyIndex :: forall m a b. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> (a -> (a, b)) -> m b-modifyIndex i MutArray{..} f = do-    let index = INDEX_OF(arrStart,i,a)-    if i >= 0 && INDEX_VALID(index,arrEnd,a)-    then liftIO $ do-        r <- peekAt index arrContents-        let (x, res) = f r-        pokeAt index arrContents  x-        return res-    else invalidIndex "modifyIndex" i---- | Modify the array indices generated by the supplied stream.------ /Pre-release/-{-# INLINE modifyIndices #-}-modifyIndices :: forall m a . (MonadIO m, Unbox a)-    => MutArray a -> (Int -> a -> a) -> Fold m Int ()-modifyIndices arr f = FL.foldlM' step initial--    where--    initial = return ()--    step () i =-        let f1 x = (f i x, ())-         in modifyIndex i arr f1---- | Modify each element of an array using the supplied modifier function.------ This is an in-place equivalent of an immutable map operation.------ /Pre-release/-modify :: forall m a. (MonadIO m, Unbox a)-    => MutArray a -> (a -> a) -> m ()-modify MutArray{..} f = liftIO $-    go arrStart--    where--    go i =-        when (INDEX_VALID(i,arrEnd,a)) $ do-            r <- peekAt i arrContents-            pokeAt i arrContents (f r)-            go (INDEX_NEXT(i,a))---- XXX We could specify the number of bytes to swap instead of Proxy. Need--- to ensure that the memory does not overlap.-{-# INLINE swapArrayByteIndices #-}-swapArrayByteIndices ::-       forall a. Unbox a-    => Proxy a-    -> MutByteArray-    -> Int-    -> Int-    -> IO ()-swapArrayByteIndices _ arrContents i1 i2 = do-    r1 <- peekAt i1 arrContents-    r2 <- peekAt i2 arrContents-    pokeAt i1 arrContents (r2 :: a)-    pokeAt i2 arrContents (r1 :: a)---- | Swap the elements at two indices without validating the indices.------ /Unsafe/: This could result in memory corruption if indices are not valid.------ /Pre-release/-{-# INLINE unsafeSwapIndices #-}-unsafeSwapIndices :: forall m a. (MonadIO m, Unbox a)-    => Int -> Int -> MutArray a -> m ()-unsafeSwapIndices i1 i2 MutArray{..} = liftIO $ do-        let t1 = INDEX_OF(arrStart,i1,a)-            t2 = INDEX_OF(arrStart,i2,a)-        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2---- | Swap the elements at two indices.------ /Pre-release/-swapIndices :: forall m a. (MonadIO m, Unbox a)-    => Int -> Int -> MutArray a -> m ()-swapIndices i1 i2 MutArray{..} = liftIO $ do-        let t1 = INDEX_OF(arrStart,i1,a)-            t2 = INDEX_OF(arrStart,i2,a)-        when (i1 < 0 || INDEX_INVALID(t1,arrEnd,a))-            $ invalidIndex "swapIndices" i1-        when (i2 < 0 || INDEX_INVALID(t2,arrEnd,a))-            $ invalidIndex "swapIndices" i2-        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2------------------------------------------------------------------------------------ Rounding------------------------------------------------------------------------------------ XXX Should we use bitshifts in calculations or it gets optimized by the--- compiler/processor itself?------ | The page or block size used by the GHC allocator. Allocator allocates at--- least a block and then allocates smaller allocations from within a block.-blockSize :: Int-blockSize = 4 * 1024---- | Allocations larger than 'largeObjectThreshold' are in multiples of block--- size and are always pinned. The space beyond the end of a large object up to--- the end of the block is unused.-largeObjectThreshold :: Int-largeObjectThreshold = (blockSize * 8) `div` 10---- XXX Should be done only when we are using the GHC allocator.--- | Round up an array larger than 'largeObjectThreshold' to use the whole--- block.-{-# INLINE roundUpLargeArray #-}-roundUpLargeArray :: Int -> Int-roundUpLargeArray size =-    if size >= largeObjectThreshold-    then-        assert-            (blockSize /= 0 && ((blockSize .&. (blockSize - 1)) == 0))-            ((size + blockSize - 1) .&. negate blockSize)-    else size--{-# INLINE isPower2 #-}-isPower2 :: Int -> Bool-isPower2 n = n .&. (n - 1) == 0--{-# INLINE roundUpToPower2 #-}-roundUpToPower2 :: Int -> Int-roundUpToPower2 n =-#if WORD_SIZE_IN_BITS == 64-    1 + z6-#else-    1 + z5-#endif--    where--    z0 = n - 1-    z1 = z0 .|. z0 `shiftR` 1-    z2 = z1 .|. z1 `shiftR` 2-    z3 = z2 .|. z2 `shiftR` 4-    z4 = z3 .|. z3 `shiftR` 8-    z5 = z4 .|. z4 `shiftR` 16-    z6 = z5 .|. z5 `shiftR` 32---- | @allocBytesToBytes elem allocatedBytes@ returns the array size in bytes--- such that the real allocation is less than or equal to @allocatedBytes@,--- unless @allocatedBytes@ is less than the size of one array element in which--- case it returns one element's size.----{-# INLINE allocBytesToBytes #-}-allocBytesToBytes :: forall a. Unbox a => a -> Int -> Int-allocBytesToBytes _ n = max (arrayPayloadSize n) (SIZE_OF(a))---- | Given an 'Unboxed' type (unused first arg) and real allocation size--- (including overhead), return how many elements of that type will completely--- fit in it, returns at least 1.----{-# INLINE allocBytesToElemCount #-}-allocBytesToElemCount :: Unbox a => a -> Int -> Int-allocBytesToElemCount x bytes =-    let n = bytesToElemCount x (allocBytesToBytes x bytes)-     in assert (n >= 1) n---- | The default chunk size by which the array creation routines increase the--- size of the array when the array is grown linearly.-arrayChunkBytes :: Int-arrayChunkBytes = 1024------------------------------------------------------------------------------------ Resizing------------------------------------------------------------------------------------ | Round the second argument down to multiples of the first argument.-{-# INLINE roundDownTo #-}-roundDownTo :: Int -> Int -> Int-roundDownTo elemSize size = size - (size `mod` elemSize)---- XXX See if resizing can be implemented by reading the old array as a stream--- and then using writeN to the new array.------ NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.--- Since this is not inlined Unboxed consrraint leads to dictionary passing--- which complicates some inspection tests.----{-# NOINLINE reallocExplicit #-}-reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)-reallocExplicit elemSize newCapacityInBytes MutArray{..} = do-    assertM(arrEnd <= arrBound)--    -- Allocate new array-    let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes-    contents <--        if Unboxed.isPinned arrContents-        then Unboxed.pinnedNew newCapMaxInBytes-        else Unboxed.new newCapMaxInBytes-    let !(MutByteArray mbarrFrom#) = arrContents-        !(MutByteArray mbarrTo#) = contents--    -- Copy old data-    let oldStart = arrStart-        !(I# oldStartInBytes#) = oldStart-        oldSizeInBytes = arrEnd - oldStart-        newCapInBytes = roundDownTo elemSize newCapMaxInBytes-        !newLenInBytes@(I# newLenInBytes#) = min oldSizeInBytes newCapInBytes-    assert (oldSizeInBytes `mod` elemSize == 0) (return ())-    assert (newLenInBytes >= 0) (return ())-    assert (newLenInBytes `mod` elemSize == 0) (return ())-    IO $ \s# -> (# copyMutableByteArray# mbarrFrom# oldStartInBytes#-                        mbarrTo# 0# newLenInBytes# s#, () #)--    return $ MutArray-        { arrStart = 0-        , arrContents = contents-        , arrEnd   = newLenInBytes-        , arrBound = newCapInBytes-        }---- | @realloc newCapacity array@ reallocates the array to the specified--- capacity in bytes.------ If the new size is less than the original array the array gets truncated.--- If the new size is not a multiple of array element size then it is rounded--- down to multiples of array size.  If the new size is more than--- 'largeObjectThreshold' then it is rounded up to the block size (4K).------ If the original array is pinned, the newly allocated array is also pinned.-{-# INLINABLE realloc #-}-realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)-realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr---- | @reallocWith label capSizer minIncrBytes array@. The label is used--- in error messages and the capSizer is used to determine the capacity of the--- new array in bytes given the current byte length of the array.-reallocWith :: forall m a. (MonadIO m , Unbox a) =>-       String-    -> (Int -> Int)-    -> Int-    -> MutArray a-    -> m (MutArray a)-reallocWith label capSizer minIncrBytes arr = do-    let oldSizeBytes = arrEnd arr - arrStart arr-        newCapBytes = capSizer oldSizeBytes-        newSizeBytes = oldSizeBytes + minIncrBytes-        safeCapBytes = max newCapBytes newSizeBytes-    assertM(safeCapBytes >= newSizeBytes || error (badSize newSizeBytes))--    realloc safeCapBytes arr--    where--    badSize newSize =-        concat-            [ label-            , ": new array size (in bytes) is less than required size "-            , show newSize-            , ". Please check the sizing function passed."-            ]---- | @resize newCapacity array@ changes the total capacity of the array so that--- it is enough to hold the specified number of elements.  Nothing is done if--- the specified capacity is less than the length of the array.------ If the capacity is more than 'largeObjectThreshold' then it is rounded up to--- the block size (4K).------ /Pre-release/-{-# INLINE resize #-}-resize :: forall m a. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> m (MutArray a)-resize nElems arr@MutArray{..} = do-    let req = SIZE_OF(a) * nElems-        len = arrEnd - arrStart-    if req < len-    then return arr-    else realloc req arr---- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'--- then it is rounded up to the closest power of 2.------ /Pre-release/-{-# INLINE resizeExp #-}-resizeExp :: forall m a. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> m (MutArray a)-resizeExp nElems arr@MutArray{..} = do-    let req = roundUpLargeArray (SIZE_OF(a) * nElems)-        req1 =-            if req > largeObjectThreshold-            then roundUpToPower2 req-            else req-        len = arrEnd - arrStart-    if req1 < len-    then return arr-    else realloc req1 arr---- | Resize the allocated memory to drop any reserved free space at the end of--- the array and reallocate it to reduce wastage.------ Up to 25% wastage is allowed to avoid reallocations.  If the capacity is--- more than 'largeObjectThreshold' then free space up to the 'blockSize' is--- retained.------ /Pre-release/-{-# INLINE rightSize #-}-rightSize :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m (MutArray a)-rightSize arr@MutArray{..} = do-    assert (arrEnd <= arrBound) (return ())-    let start = arrStart-        len = arrEnd - start-        capacity = arrBound - start-        target = roundUpLargeArray len-        waste = arrBound - arrEnd-    assert (target >= len) (return ())-    assert (len `mod` SIZE_OF(a) == 0) (return ())-    -- We trade off some wastage (25%) to avoid reallocations and copying.-    if target < capacity && len < 3 * waste-    then realloc target arr-    else return arr------------------------------------------------------------------------------------ Snoc------------------------------------------------------------------------------------ XXX We can possibly use a smallMutableByteArray to hold the start, end,--- bound pointers.  Using fully mutable handle will ensure that we do not have--- multiple references to the same array of different lengths lying around and--- potentially misused. In that case "snoc" need not return a new array (snoc--- :: MutArray a -> a -> m ()), it will just modify the old reference.  The array--- length will be mutable.  This means the length function would also be--- monadic.  Mutable arrays would behave more like files that grow in that--- case.---- | Snoc using a 'Ptr'. Low level reusable function.------ /Internal/-{-# INLINE snocNewEnd #-}-snocNewEnd :: (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m (MutArray a)-snocNewEnd newEnd arr@MutArray{..} x = liftIO $ do-    assert (newEnd <= arrBound) (return ())-    pokeAt arrEnd arrContents x-    return $ arr {arrEnd = newEnd}---- | Really really unsafe, appends the element into the first array, may--- cause silent data corruption or if you are lucky a segfault if the first--- array does not have enough space to append the element.------ /Internal/-{-# INLINE snocUnsafe #-}-snocUnsafe :: forall m a. (MonadIO m, Unbox a) =>-    MutArray a -> a -> m (MutArray a)-snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr---- | Like 'snoc' but does not reallocate when pre-allocated array capacity--- becomes full.------ /Internal/-{-# INLINE snocMay #-}-snocMay :: forall m a. (MonadIO m, Unbox a) =>-    MutArray a -> a -> m (Maybe (MutArray a))-snocMay arr@MutArray{..} x = liftIO $ do-    let newEnd = INDEX_NEXT(arrEnd,a)-    if newEnd <= arrBound-    then Just <$> snocNewEnd newEnd arr x-    else return Nothing---- NOINLINE to move it out of the way and not pollute the instruction cache.-{-# NOINLINE snocWithRealloc #-}-snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>-       (Int -> Int)-    -> MutArray a-    -> a-    -> m (MutArray a)-snocWithRealloc sizer arr x = do-    arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr-    snocUnsafe arr1 x---- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of--- the array increases by 1.------ If there is no reserved space available in @arr@ it is reallocated to a size--- in bytes determined by the @sizer oldSizeBytes@ function, where--- @oldSizeBytes@ is the original size of the array in bytes.------ If the new array size is more than 'largeObjectThreshold' we automatically--- round it up to 'blockSize'.------ Note that the returned array may be a mutated version of the original array.------ /Pre-release/-{-# INLINE snocWith #-}-snocWith :: forall m a. (MonadIO m, Unbox a) =>-       (Int -> Int)-    -> MutArray a-    -> a-    -> m (MutArray a)-snocWith allocSize arr x = liftIO $ do-    let newEnd = INDEX_NEXT(arrEnd arr,a)-    if newEnd <= arrBound arr-    then snocNewEnd newEnd arr x-    else snocWithRealloc allocSize arr x---- | The array is mutated to append an additional element to it. If there--- is no reserved space available in the array then it is reallocated to grow--- it by 'arrayChunkBytes' rounded up to 'blockSize' when the size becomes more--- than 'largeObjectThreshold'.------ Note that the returned array may be a mutated version of the original array.------ Performs O(n^2) copies to grow but is thrifty on memory.------ /Pre-release/-{-# INLINE snocLinear #-}-snocLinear :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)-snocLinear = snocWith (+ allocBytesToBytes (undefined :: a) arrayChunkBytes)---- | The array is mutated to append an additional element to it. If there is no--- reserved space available in the array then it is reallocated to double the--- original size.------ This is useful to reduce allocations when appending unknown number of--- elements.------ Note that the returned array may be a mutated version of the original array.------ >>> snoc = MutArray.snocWith (* 2)------ Performs O(n * log n) copies to grow, but is liberal with memory allocation.----{-# INLINE snoc #-}-snoc :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)-snoc = snocWith f--    where--    f oldSize =-        if isPower2 oldSize-        then oldSize * 2-        else roundUpToPower2 oldSize * 2------------------------------------------------------------------------------------ Random reads------------------------------------------------------------------------------------ XXX Can this be deduplicated with array/foreign---- | Return the element at the specified index without checking the bounds.------ Unsafe because it does not check the bounds of the array.-{-# INLINE_NORMAL getIndexUnsafe #-}-getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a-getIndexUnsafe i MutArray{..} = do-    let index = INDEX_OF(arrStart,i,a)-    assert (i >= 0 && INDEX_VALID(index,arrEnd,a)) (return ())-    liftIO $ peekAt index arrContents---- | /O(1)/ Lookup the element at the given index. Index starts from 0.----{-# INLINE getIndex #-}-getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)-getIndex i MutArray{..} = do-    let index = INDEX_OF(arrStart,i,a)-    if i >= 0 && INDEX_VALID(index,arrEnd,a)-    then liftIO $ Just <$> peekAt index arrContents-    else return Nothing---- | /O(1)/ Lookup the element at the given index from the end of the array.--- Index starts from 0.------ Slightly faster than computing the forward index and using getIndex.----{-# INLINE getIndexRev #-}-getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a-getIndexRev i MutArray{..} = do-    let index = RINDEX_OF(arrEnd,i,a)-    if i >= 0 && index >= arrStart-    then liftIO $ peekAt index arrContents-    else invalidIndex "getIndexRev" i--data GetIndicesState contents start end st =-    GetIndicesState contents start end st---- | Given an unfold that generates array indices, read the elements on those--- indices from the supplied MutArray. An error is thrown if an index is out of--- bounds.------ /Pre-release/-{-# INLINE getIndicesD #-}-getIndicesD :: (Monad m, Unbox a) =>-    (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a-getIndicesD liftio (D.Stream stepi sti) = Unfold step inject--    where--    inject (MutArray contents start end _) =-        return $ GetIndicesState contents start end sti--    {-# INLINE_LATE step #-}-    step (GetIndicesState contents start end st) = do-        r <- stepi defState st-        case r of-            D.Yield i s -> do-                x <- liftio $ getIndex i (MutArray contents start end undefined)-                case x of-                    Just v -> return $ D.Yield v (GetIndicesState contents start end s)-                    Nothing -> error "Invalid Index"-            D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)-            D.Stop -> return D.Stop--{-# INLINE getIndices #-}-getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a-getIndices = getIndicesD liftIO------------------------------------------------------------------------------------ Subarrays------------------------------------------------------------------------------------ XXX We can also get immutable slices.---- | /O(1)/ Slice an array in constant time.------ Unsafe: The bounds of the slice are not checked.------ /Unsafe/------ /Pre-release/-{-# INLINE getSliceUnsafe #-}-getSliceUnsafe :: forall a. Unbox a-    => Int -- ^ from index-    -> Int -- ^ length of the slice-    -> MutArray a-    -> MutArray a-getSliceUnsafe index len (MutArray contents start e _) =-    let fp1 = INDEX_OF(start,index,a)-        end = fp1 + (len * SIZE_OF(a))-     in assert-            (index >= 0 && len >= 0 && end <= e)-            -- Note: In a slice we always use bound = end so that the slice-            -- user cannot overwrite elements beyond the end of the slice.-            (MutArray contents fp1 end end)---- | /O(1)/ Slice an array in constant time. Throws an error if the slice--- extends out of the array bounds.------ /Pre-release/-{-# INLINE getSlice #-}-getSlice :: forall a. Unbox a =>-       Int -- ^ from index-    -> Int -- ^ length of the slice-    -> MutArray a-    -> MutArray a-getSlice index len (MutArray contents start e _) =-    let fp1 = INDEX_OF(start,index,a)-        end = fp1 + (len * SIZE_OF(a))-     in if index >= 0 && len >= 0 && end <= e-        -- Note: In a slice we always use bound = end so that the slice user-        -- cannot overwrite elements beyond the end of the slice.-        then MutArray contents fp1 end end-        else error-                $ "getSlice: invalid slice, index "-                ++ show index ++ " length " ++ show len------------------------------------------------------------------------------------ In-place mutation algorithms------------------------------------------------------------------------------------ XXX consider the bulk update/accumulation/permutation APIs from vector.---- | You may not need to reverse an array because you can consume it in reverse--- using 'readerRev'. To reverse large arrays you can read in reverse and write--- to another array. However, in-place reverse can be useful to take adavantage--- of cache locality and when you do not want to allocate additional memory.----{-# INLINE reverse #-}-reverse :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m ()-reverse MutArray{..} = liftIO $ do-    let l = arrStart-        h = INDEX_PREV(arrEnd,a)-     in swap l h--    where--    swap l h = do-        when (l < h) $ do-            swapArrayByteIndices (Proxy :: Proxy a) arrContents l h-            swap (INDEX_NEXT(l,a)) (INDEX_PREV(h,a))---- | Generate the next permutation of the sequence, returns False if this is--- the last permutation.------ /Unimplemented/-{-# INLINE permute #-}-permute :: MutArray a -> m Bool-permute = undefined---- | Partition an array into two halves using a partitioning predicate. The--- first half retains values where the predicate is 'False' and the second half--- retains values where the predicate is 'True'.------ /Pre-release/-{-# INLINE partitionBy #-}-partitionBy :: forall m a. (MonadIO m, Unbox a)-    => (a -> Bool) -> MutArray a -> m (MutArray a, MutArray a)-partitionBy f arr@MutArray{..} = liftIO $ do-    if arrStart >= arrEnd-    then return (arr, arr)-    else do-        ptr <- go arrStart (INDEX_PREV(arrEnd,a))-        let pl = MutArray arrContents arrStart ptr ptr-            pr = MutArray arrContents ptr arrEnd arrEnd-        return (pl, pr)--    where--    -- Invariant low < high on entry, and on return as well-    moveHigh low high = do-        h <- peekAt high arrContents-        if f h-        then-            -- Correctly classified, continue the loop-            let high1 = INDEX_PREV(high,a)-             in if low == high1-                then return Nothing-                else moveHigh low high1-        else return (Just (high, h)) -- incorrectly classified--    -- Keep a low pointer starting at the start of the array (first partition)-    -- and a high pointer starting at the end of the array (second partition).-    -- Keep incrementing the low ptr and decrementing the high ptr until both-    -- are wrongly classified, at that point swap the two and continue until-    -- the two pointer cross each other.-    ---    -- Invariants when entering this loop:-    -- low <= high-    -- Both low and high are valid locations within the array-    go low high = do-        l <- peekAt low arrContents-        if f l-        then-            -- low is wrongly classified-            if low == high-            then return low-            else do -- low < high-                r <- moveHigh low high-                case r of-                    Nothing -> return low-                    Just (high1, h) -> do -- low < high1-                        pokeAt low arrContents h-                        pokeAt high1 arrContents l-                        let low1 = INDEX_NEXT(low,a)-                            high2 = INDEX_PREV(high1,a)-                        if low1 <= high2-                        then go low1 high2-                        else return low1 -- low1 > high2--        else do-            -- low is correctly classified-            let low1 = INDEX_NEXT(low,a)-            if low == high-            then return low1-            else go low1 high---- | Shuffle corresponding elements from two arrays using a shuffle function.--- If the shuffle function returns 'False' then do nothing otherwise swap the--- elements. This can be used in a bottom up fold to shuffle or reorder the--- elements.------ /Unimplemented/-{-# INLINE shuffleBy #-}-shuffleBy :: (a -> a -> m Bool) -> MutArray a -> MutArray a -> m ()-shuffleBy = undefined---- XXX we can also make the folds partial by stopping at a certain level.------ | @divideBy level partition array@  performs a top down hierarchical--- recursive partitioning fold of items in the container using the given--- function as the partition function.  Level indicates the level in the tree--- where the fold would stop.------ This performs a quick sort if the partition function is--- 'partitionBy (< pivot)'.------ /Unimplemented/-{-# INLINABLE divideBy #-}-divideBy ::-    Int -> (MutArray a -> m (MutArray a, MutArray a)) -> MutArray a -> m ()-divideBy = undefined---- | @mergeBy level merge array@ performs a pairwise bottom up fold recursively--- merging the pairs using the supplied merge function. Level indicates the--- level in the tree where the fold would stop.------ This performs a random shuffle if the merge function is random.  If we--- stop at level 0 and repeatedly apply the function then we can do a bubble--- sort.------ /Unimplemented/-mergeBy :: Int -> (MutArray a -> MutArray a -> m ()) -> MutArray a -> m ()-mergeBy = undefined------------------------------------------------------------------------------------ Size------------------------------------------------------------------------------------ | /O(1)/ Get the byte length of the array.----{-# INLINE byteLength #-}-byteLength :: MutArray a -> Int-byteLength MutArray{..} =-    let len = arrEnd - arrStart-    in assert (len >= 0) len---- Note: try to avoid the use of length in performance sensitive internal--- routines as it involves a costly 'div' operation. Instead use the end ptr--- in the array to check the bounds etc.------ | /O(1)/ Get the length of the array i.e. the number of elements in the--- array.------ Note that 'byteLength' is less expensive than this operation, as 'length'--- involves a costly division operation.----{-# INLINE length #-}-length :: forall a. Unbox a => MutArray a -> Int-length arr =-    let elemSize = SIZE_OF(a)-        blen = byteLength arr-     in assert (blen `mod` elemSize == 0) (blen `div` elemSize)---- | Get the total capacity of an array. An array may have space reserved--- beyond the current used length of the array.------ /Pre-release/-{-# INLINE byteCapacity #-}-byteCapacity :: MutArray a -> Int-byteCapacity MutArray{..} =-    let len = arrBound - arrStart-    in assert (len >= 0) len---- | The remaining capacity in the array for appending more elements without--- reallocation.------ /Pre-release/-{-# INLINE bytesFree #-}-bytesFree :: MutArray a -> Int-bytesFree MutArray{..} =-    let n = arrBound - arrEnd-    in assert (n >= 0) n------------------------------------------------------------------------------------ Streams of arrays - Creation----------------------------------------------------------------------------------data GroupState s contents start end bound-    = GroupStart s-    | GroupBuffer s contents start end bound-    | GroupYield-        contents start end bound (GroupState s contents start end bound)-    | GroupFinish--{-# INLINE_NORMAL chunksOfAs #-}-chunksOfAs :: forall m a. (MonadIO m, Unbox a)-    => PinnedState -> Int -> D.Stream m a -> D.Stream m (MutArray a)-chunksOfAs ps n (D.Stream step state) =-    D.Stream step' (GroupStart state)--    where--    {-# INLINE_LATE step' #-}-    step' _ (GroupStart st) = do-        when (n <= 0) $-            -- XXX we can pass the module string from the higher level API-            error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "-                    ++ "the size of arrays [" ++ show n-                    ++ "] must be a natural number"-        (MutArray contents start end bound :: MutArray a) <- newAs ps n-        return $ D.Skip (GroupBuffer st contents start end bound)--    step' gst (GroupBuffer st contents start end bound) = do-        r <- step (adaptState gst) st-        case r of-            D.Yield x s -> do-                liftIO $ pokeAt end contents  x-                let end1 = INDEX_NEXT(end,a)-                return $-                    if end1 >= bound-                    then D.Skip-                            (GroupYield-                                contents start end1 bound (GroupStart s))-                    else D.Skip (GroupBuffer s contents start end1 bound)-            D.Skip s ->-                return $ D.Skip (GroupBuffer s contents start end bound)-            D.Stop ->-                return-                    $ D.Skip (GroupYield contents start end bound GroupFinish)--    step' _ (GroupYield contents start end bound next) =-        return $ D.Yield (MutArray contents start end bound) next--    step' _ GroupFinish = return D.Stop---- | @chunksOf n stream@ groups the elements in the input stream into arrays of--- @n@ elements each.------ Same as the following but may be more efficient:------ >>> chunksOf n = Stream.foldMany (MutArray.writeN n)------ /Pre-release/-{-# INLINE_NORMAL chunksOf #-}-chunksOf :: forall m a. (MonadIO m, Unbox a)-    => Int -> D.Stream m a -> D.Stream m (MutArray a)--- XXX the idiomatic implementation leads to large regression in the D.reverse'--- benchmark. It seems it has difficulty producing optimized code when--- converting to StreamK. Investigate GHC optimizations.--- chunksOf n = D.foldMany (writeN n)-chunksOf = chunksOfAs Unpinned---- | Like 'chunksOf' but creates pinned arrays.-{-# INLINE_NORMAL pinnedChunksOf #-}-pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)-    => Int -> D.Stream m a -> D.Stream m (MutArray a)--- pinnedChunksOf n = D.foldMany (pinnedWriteN n)-pinnedChunksOf = chunksOfAs Pinned---- XXX This should take a PinnedState--- XXX buffer to a list instead?--- | Buffer the stream into arrays in memory.-{-# INLINE arrayStreamKFromStreamDAs #-}-arrayStreamKFromStreamDAs :: forall m a. (MonadIO m, Unbox a) =>-    PinnedState -> D.Stream m a -> m (StreamK m (MutArray a))-arrayStreamKFromStreamDAs ps =-    let n = allocBytesToElemCount (undefined :: a) defaultChunkSize-     in D.foldr K.cons K.nil . chunksOfAs ps n------------------------------------------------------------------------------------ Streams of arrays - Flattening----------------------------------------------------------------------------------data FlattenState s contents a =-      OuterLoop s-    | InnerLoop s contents !Int !Int---- | Use the "reader" unfold instead.------ @flattenArrays = unfoldMany reader@------ We can try this if there are any fusion issues in the unfold.----{-# INLINE_NORMAL flattenArrays #-}-flattenArrays :: forall m a. (MonadIO m, Unbox a)-    => D.Stream m (MutArray a) -> D.Stream m a-flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (OuterLoop st) = do-        r <- step (adaptState gst) st-        return $ case r of-            D.Yield MutArray{..} s ->-                D.Skip (InnerLoop s arrContents arrStart arrEnd)-            D.Skip s -> D.Skip (OuterLoop s)-            D.Stop -> D.Stop--    step' _ (InnerLoop st _ p end) | assert (p <= end) (p == end) =-        return $ D.Skip $ OuterLoop st--    step' _ (InnerLoop st contents p end) = do-        x <- liftIO $ peekAt p contents-        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)---- | Use the "readerRev" unfold instead.------ @flattenArrays = unfoldMany readerRev@------ We can try this if there are any fusion issues in the unfold.----{-# INLINE_NORMAL flattenArraysRev #-}-flattenArraysRev :: forall m a. (MonadIO m, Unbox a)-    => D.Stream m (MutArray a) -> D.Stream m a-flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (OuterLoop st) = do-        r <- step (adaptState gst) st-        return $ case r of-            D.Yield MutArray{..} s ->-                let p = INDEX_PREV(arrEnd,a)-                 in D.Skip (InnerLoop s arrContents p arrStart)-            D.Skip s -> D.Skip (OuterLoop s)-            D.Stop -> D.Stop--    step' _ (InnerLoop st _ p start) | p < start =-        return $ D.Skip $ OuterLoop st--    step' _ (InnerLoop st contents p start) = do-        x <- liftIO $ peekAt p contents-        let cur = INDEX_PREV(p,a)-        return $ D.Yield x (InnerLoop st contents cur start)------------------------------------------------------------------------------------ Unfolds----------------------------------------------------------------------------------data ArrayUnsafe a = ArrayUnsafe-    {-# UNPACK #-} !MutByteArray   -- contents-    {-# UNPACK #-} !Int                -- index 1-    {-# UNPACK #-} !Int                -- index 2--toArrayUnsafe :: MutArray a -> ArrayUnsafe a-toArrayUnsafe (MutArray contents start end _) = ArrayUnsafe contents start end--fromArrayUnsafe ::-#ifdef DEVBUILD-    Unbox a =>-#endif-    ArrayUnsafe a -> MutArray a-fromArrayUnsafe (ArrayUnsafe contents start end) =-         MutArray contents start end end--{-# INLINE_NORMAL producerWith #-}-producerWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> Producer m (MutArray a) a-producerWith liftio = Producer step (return . toArrayUnsafe) extract-    where--    {-# INLINE_LATE step #-}-    step (ArrayUnsafe _ cur end)-        | assert (cur <= end) (cur == end) = return D.Stop-    step (ArrayUnsafe contents cur end) = do-            -- When we use a purely lazy Monad like Identity, we need to force a-            -- few actions for correctness and execution order sanity. We want-            -- the peek to occur right here and not lazily at some later point-            -- because we want the peek to be ordered with respect to the touch.-            !x <- liftio $ peekAt cur contents-            return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)--    extract = return . fromArrayUnsafe---- | Resumable unfold of an array.----{-# INLINE_NORMAL producer #-}-producer :: forall m a. (MonadIO m, Unbox a) => Producer m (MutArray a) a-producer = producerWith liftIO---- | Unfold an array into a stream.----{-# INLINE_NORMAL reader #-}-reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a-reader = Producer.simplify producer--{-# INLINE_NORMAL readerRevWith #-}-readerRevWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> Unfold m (MutArray a) a-readerRevWith liftio = Unfold step inject-    where--    inject (MutArray contents start end _) =-        let p = INDEX_PREV(end,a)-         in return $ ArrayUnsafe contents start p--    {-# INLINE_LATE step #-}-    step (ArrayUnsafe _ start p) | p < start = return D.Stop-    step (ArrayUnsafe contents start p) = do-        !x <- liftio $ peekAt p contents-        return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))---- | Unfold an array into a stream in reverse order.----{-# INLINE_NORMAL readerRev #-}-readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a-readerRev = readerRevWith liftIO------------------------------------------------------------------------------------ to Lists and streams----------------------------------------------------------------------------------{---- Use foldr/build fusion to fuse with list consumers--- This can be useful when using the IsList instance-{-# INLINE_LATE toListFB #-}-toListFB :: forall a b. Unbox a => (a -> b -> b) -> b -> MutArray a -> b-toListFB c n MutArray{..} = go arrStart-    where--    go p | assert (p <= arrEnd) (p == arrEnd) = n-    go p =-        -- unsafeInlineIO allows us to run this in Identity monad for pure-        -- toList/foldr case which makes them much faster due to not-        -- accumulating the list and fusing better with the pure consumers.-        ---        -- This should be safe as the array contents are guaranteed to be-        -- evaluated/written to before we peek at them.-        -- XXX-        let !x = unsafeInlineIO $ do-                    r <- peekAt arrContents p-                    return r-        in c x (go (PTR_NEXT(p,a)))--}---- XXX Monadic foldr/build fusion?--- Reference: https://www.researchgate.net/publication/220676509_Monadic_augment_and_generalised_short_cut_fusion---- | Convert a 'MutArray' into a list.----{-# INLINE toList #-}-toList :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m [a]-toList MutArray{..} = liftIO $ go arrStart-    where--    go p | assert (p <= arrEnd) (p == arrEnd) = return []-    go p = do-        x <- peekAt p arrContents-        (:) x <$> go (INDEX_NEXT(p,a))--{-# INLINE_NORMAL toStreamDWith #-}-toStreamDWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a-toStreamDWith liftio MutArray{..} = D.Stream step arrStart--    where--    {-# INLINE_LATE step #-}-    step _ p | assert (p <= arrEnd) (p == arrEnd) = return D.Stop-    step _ p = liftio $ do-        r <- peekAt p arrContents-        return $ D.Yield r (INDEX_NEXT(p,a))---- | Convert a 'MutArray' into a stream.------ >>> read = Stream.unfold MutArray.reader----{-# INLINE_NORMAL read #-}-read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a-read = toStreamDWith liftIO--{-# INLINE toStreamKWith #-}-toStreamKWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a-toStreamKWith liftio MutArray{..} = go arrStart--    where--    go p | assert (p <= arrEnd) (p == arrEnd) = K.nil-         | otherwise =-        let elemM = peekAt p arrContents-        in liftio elemM `K.consM` go (INDEX_NEXT(p,a))--{-# INLINE toStreamK #-}-toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a-toStreamK = toStreamKWith liftIO--{-# INLINE_NORMAL toStreamDRevWith #-}-toStreamDRevWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a-toStreamDRevWith liftio MutArray{..} =-    let p = INDEX_PREV(arrEnd,a)-    in D.Stream step p--    where--    {-# INLINE_LATE step #-}-    step _ p | p < arrStart = return D.Stop-    step _ p = liftio $ do-        r <- peekAt p arrContents-        return $ D.Yield r (INDEX_PREV(p,a))---- | Convert a 'MutArray' into a stream in reverse order.------ >>> readRev = Stream.unfold MutArray.readerRev----{-# INLINE_NORMAL readRev #-}-readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a-readRev = toStreamDRevWith liftIO--{-# INLINE toStreamKRevWith #-}-toStreamKRevWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a-toStreamKRevWith liftio MutArray {..} =-    let p = INDEX_PREV(arrEnd,a)-    in go p--    where--    go p | p < arrStart = K.nil-         | otherwise =-        let elemM = peekAt p arrContents-        in liftio elemM `K.consM` go (INDEX_PREV(p,a))--{-# INLINE toStreamKRev #-}-toStreamKRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a-toStreamKRev = toStreamKRevWith liftIO------------------------------------------------------------------------------------ Folding------------------------------------------------------------------------------------ XXX Need something like "MutArray m a" enforcing monadic action to avoid the--- possibility of such APIs.------ | Strict left fold of an array.-{-# INLINE_NORMAL foldl' #-}-foldl' :: (MonadIO m, Unbox a) => (b -> a -> b) -> b -> MutArray a -> m b-foldl' f z arr = D.foldl' f z $ read arr---- | Right fold of an array.-{-# INLINE_NORMAL foldr #-}-foldr :: (MonadIO m, Unbox a) => (a -> b -> b) -> b -> MutArray a -> m b-foldr f z arr = D.foldr f z $ read arr------------------------------------------------------------------------------------ Folds------------------------------------------------------------------------------------ Note: Arrays may be allocated with a specific alignment at the beginning of--- the array. If you need to maintain that alignment on reallocations then you--- can resize the array manually before append, using an aligned resize--- operation.---- XXX Keep the bound intact to not lose any free space? Perf impact?---- | @writeAppendNUnsafe n arr@ appends up to @n@ input items to the supplied--- array.------ Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory--- corruption or segfault.------ Any free space left in the array after appending @n@ elements is lost.------ /Internal/-{-# INLINE_NORMAL writeAppendNUnsafe #-}-writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>-       Int-    -> m (MutArray a)-    -> Fold m a (MutArray a)-writeAppendNUnsafe n action =-    fmap fromArrayUnsafe $ FL.foldlM' step initial--    where--    initial = do-        assert (n >= 0) (return ())-        arr@(MutArray _ _ end bound) <- action-        let free = bound - end-            needed = n * SIZE_OF(a)-        -- XXX We can also reallocate if the array has too much free space,-        -- otherwise we lose that space.-        arr1 <--            if free < needed-            then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr-            else return arr-        return $ toArrayUnsafe arr1--    step (ArrayUnsafe contents start end) x = do-        liftIO $ pokeAt end contents x-        return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))---- | Append @n@ elements to an existing array. Any free space left in the array--- after appending @n@ elements is lost.------ >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)----{-# INLINE_NORMAL writeAppendN #-}-writeAppendN :: forall m a. (MonadIO m, Unbox a) =>-    Int -> m (MutArray a) -> Fold m a (MutArray a)-writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)---- | @writeAppendWith realloc action@ mutates the array generated by @action@ to--- append the input stream. If there is no reserved space available in the--- array it is reallocated to a size in bytes  determined by @realloc oldSize@,--- where @oldSize@ is the current size of the array in bytes.------ Note that the returned array may be a mutated version of original array.------ >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)------ /Pre-release/-{-# INLINE writeAppendWith #-}-writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>-    (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)-writeAppendWith sizer = FL.foldlM' (snocWith sizer)---- | @append action@ mutates the array generated by @action@ to append the--- input stream. If there is no reserved space available in the array it is--- reallocated to double the size.------ Note that the returned array may be a mutated version of original array.------ >>> writeAppend = MutArray.writeAppendWith (* 2)----{-# INLINE writeAppend #-}-writeAppend :: forall m a. (MonadIO m, Unbox a) =>-    m (MutArray a) -> Fold m a (MutArray a)-writeAppend = writeAppendWith (* 2)---- XXX We can carry bound as well in the state to make sure we do not lose the--- remaining capacity. Need to check perf impact.------ | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function--- as argument.------ >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n------ /Pre-release/-{-# INLINE_NORMAL writeNWithUnsafe #-}-writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial--    where--    initial = toArrayUnsafe <$> alloc (max n 0)--    step (ArrayUnsafe contents start end) x = do-        liftIO $ pokeAt end contents x-        return-          $ ArrayUnsafe contents start (INDEX_NEXT(end,a))--{-# INLINE_NORMAL writeNUnsafeAs #-}-writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)-    => PinnedState -> Int -> Fold m a (MutArray a)-writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)---- | Like 'writeN' but does not check the array bounds when writing. The fold--- driver must not call the step function more than 'n' times otherwise it will--- corrupt the memory and crash. This function exists mainly because any--- conditional in the step function blocks fusion causing 10x performance--- slowdown.------ >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new----{-# INLINE_NORMAL writeNUnsafe #-}-writeNUnsafe :: forall m a. (MonadIO m, Unbox a)-    => Int -> Fold m a (MutArray a)-writeNUnsafe = writeNUnsafeAs Unpinned---- | Like 'writeNUnsafe' but creates a pinned array.-{-# INLINE_NORMAL pinnedWriteNUnsafe #-}-pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)-    => Int -> Fold m a (MutArray a)-pinnedWriteNUnsafe = writeNUnsafeAs Pinned---- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array--- allocated using the @alloc@ function.------ >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)--- >>> writeNWith alloc n = MutArray.writeAppendN (alloc n) n----{-# INLINE_NORMAL writeNWith #-}-writeNWith :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)--{-# INLINE_NORMAL writeNAs #-}-writeNAs ::-       forall m a. (MonadIO m, Unbox a)-    => PinnedState-    -> Int-    -> Fold m a (MutArray a)-writeNAs ps = writeNWith (newAs ps)---- | @writeN n@ folds a maximum of @n@ elements from the input stream to an--- 'MutArray'.------ >>> writeN = MutArray.writeNWith MutArray.new--- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)--- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)----{-# INLINE_NORMAL writeN #-}-writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)-writeN = writeNAs Unpinned---- | Like 'writeN' but creates a pinned array.-{-# INLINE_NORMAL pinnedWriteN #-}-pinnedWriteN ::-       forall m a. (MonadIO m, Unbox a)-    => Int-    -> Fold m a (MutArray a)-pinnedWriteN = writeNAs Pinned---- | Like writeNWithUnsafe but writes the array in reverse order.------ /Internal/-{-# INLINE_NORMAL writeRevNWithUnsafe #-}-writeRevNWithUnsafe :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial--    where--    toArrayUnsafeRev (MutArray contents _ _ bound) =-         ArrayUnsafe contents bound bound--    initial = toArrayUnsafeRev <$> alloc (max n 0)--    step (ArrayUnsafe contents start end) x = do-        let ptr = INDEX_PREV(start,a)-        liftIO $ pokeAt ptr contents x-        return-          $ ArrayUnsafe contents ptr end---- | Like writeNWith but writes the array in reverse order.------ /Internal/-{-# INLINE_NORMAL writeRevNWith #-}-writeRevNWith :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)---- | Like writeN but writes the array in reverse order.------ /Pre-release/-{-# INLINE_NORMAL writeRevN #-}-writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)-writeRevN = writeRevNWith new---- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the--- input stream to a 'MutArray' aligned to the given size.------ >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)--- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.pinnedNewAligned align n)------ /Pre-release/----{-# INLINE_NORMAL pinnedWriteNAligned #-}-pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)-    => Int -> Int -> Fold m a (MutArray a)-pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)---- XXX Buffer to a list instead?---- | Buffer a stream into a stream of arrays.------ >>> writeChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK------ Breaking an array into an array stream  can be useful to consume a large--- array sequentially such that memory of the array is released incrementatlly.------ See also: 'arrayStreamKFromStreamD'.------ /Unimplemented/----{-# INLINE_NORMAL writeChunks #-}-writeChunks :: (MonadIO m, Unbox a) =>-    Int -> Fold m a (StreamK n (MutArray a))-writeChunks n = FL.many (writeN n) FL.toStreamK--{-# INLINE_NORMAL writeWithAs #-}-writeWithAs :: forall m a. (MonadIO m, Unbox a)-    => PinnedState -> Int -> Fold m a (MutArray a)--- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)-writeWithAs ps elemCount =-    FL.rmapM extract $ FL.foldlM' step initial--    where--    initial = do-        when (elemCount < 0) $ error "writeWith: elemCount is negative"-        liftIO $ newAs ps elemCount--    step arr@(MutArray _ start end bound) x-        | INDEX_NEXT(end,a) > bound = do-        let oldSize = end - start-            newSize = max (oldSize * 2) 1-        arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr-        snocUnsafe arr1 x-    step arr x = snocUnsafe arr x--    extract = liftIO . rightSize---- XXX Compare writeWith with fromStreamD which uses an array of streams--- implementation. We can write this using writeChunks above if that is faster.--- If writeWith is faster then we should use that to implement--- fromStreamD.------ XXX The realloc based implementation needs to make one extra copy if we use--- shrinkToFit.  On the other hand, the stream of arrays implementation may--- buffer the array chunk pointers in memory but it does not have to shrink as--- we know the exact size in the end. However, memory copying does not seem to--- be as expensive as the allocations. Therefore, we need to reduce the number--- of allocations instead. Also, the size of allocations matters, right sizing--- an allocation even at the cost of copying sems to help.  Should be measured--- on a big stream with heavy calls to toArray to see the effect.------ XXX check if GHC's memory allocator is efficient enough. We can try the C--- malloc to compare against.---- | @writeWith minCount@ folds the whole input to a single array. The array--- starts at a size big enough to hold minCount elements, the size is doubled--- every time the array needs to be grown.------ /Caution! Do not use this on infinite streams./------ >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)--- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)--- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)------ /Pre-release/-{-# INLINE_NORMAL writeWith #-}-writeWith :: forall m a. (MonadIO m, Unbox a)-    => Int -> Fold m a (MutArray a)--- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)-writeWith = writeWithAs Unpinned---- | Fold the whole input to a single array.------ Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes--- rounded up to the element size.------ /Caution! Do not use this on infinite streams./----{-# INLINE write #-}-write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)-write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)---- | Like 'write' but creates a pinned array.-{-# INLINE pinnedWrite #-}-pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)-pinnedWrite =-    writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)------------------------------------------------------------------------------------ construct from streams, known size----------------------------------------------------------------------------------{-# INLINE_NORMAL fromStreamDNAs #-}-fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)-    => PinnedState -> Int -> D.Stream m a -> m (MutArray a)-fromStreamDNAs ps limit str = do-    (arr :: MutArray a) <- liftIO $ newAs ps limit-    end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str-    return $ arr {arrEnd = end}--    where--    fwrite arrContents ptr x = do-        liftIO $ pokeAt ptr arrContents  x-        return $ INDEX_NEXT(ptr,a)---- | Use the 'writeN' fold instead.------ >>> fromStreamDN n = Stream.fold (MutArray.writeN n)----{-# INLINE_NORMAL fromStreamDN #-}-fromStreamDN :: forall m a. (MonadIO m, Unbox a)-    => Int -> D.Stream m a -> m (MutArray a)--- fromStreamDN n = D.fold (writeN n)-fromStreamDN = fromStreamDNAs Unpinned---- | Create a 'MutArray' from the first N elements of a list. The array is--- allocated to size N, if the list terminates before N elements then the--- array may hold less than N elements.----{-# INLINABLE fromListN #-}-fromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)-fromListN n xs = fromStreamDN n $ D.fromList xs---- | Like 'fromListN' but creates a pinned array.-{-# INLINABLE pinnedFromListN #-}-pinnedFromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)-pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs---- | Like fromListN but writes the array in reverse order.------ /Pre-release/-{-# INLINE fromListRevN #-}-fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)-fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs---- | Convert a pure stream in Identity monad to a mutable array.-{-# INLINABLE fromPureStream #-}-fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)-fromPureStream xs =-    liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs------------------------------------------------------------------------------------ convert stream to a single array----------------------------------------------------------------------------------{-# INLINE arrayStreamKLength #-}-arrayStreamKLength :: (Monad m, Unbox a) => StreamK m (MutArray a) -> m Int-arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)---- | Convert an array stream to an array. Note that this requires peak memory--- that is double the size of the array stream.----{-# INLINE fromArrayStreamK #-}-fromArrayStreamK :: (Unbox a, MonadIO m) =>-    StreamK m (MutArray a) -> m (MutArray a)-fromArrayStreamK as = do-    len <- arrayStreamKLength as-    fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as--{-# INLINE fromStreamDAs #-}-fromStreamDAs ::-       (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)-fromStreamDAs ps m = arrayStreamKFromStreamDAs ps m >>= fromArrayStreamK---- CAUTION: a very large number (millions) of arrays can degrade performance--- due to GC overhead because we need to buffer the arrays before we flatten--- all the arrays.------ XXX Compare if this is faster or "fold write".------ | We could take the approach of doubling the memory allocation on each--- overflow. This would result in more or less the same amount of copying as in--- the chunking approach. However, if we have to shrink in the end then it may--- result in an extra copy of the entire data.------ >>> fromStreamD = StreamD.fold MutArray.write----{-# INLINE fromStreamD #-}-fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)-fromStreamD = fromStreamDAs Unpinned---- | Create a 'MutArray' from a list. The list must be of finite size.----{-# INLINE fromList #-}-fromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)-fromList xs = fromStreamD $ D.fromList xs---- | Like 'fromList' but creates a pinned array.-{-# INLINE pinnedFromList #-}-pinnedFromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)-pinnedFromList xs = fromStreamDAs Pinned $ D.fromList xs---- XXX We are materializing the whole list first for getting the length. Check--- if the 'fromList' like chunked implementation would fare better.---- | Like 'fromList' but writes the contents of the list in reverse order.-{-# INLINE fromListRev #-}-fromListRev :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)-fromListRev xs = fromListRevN (Prelude.length xs) xs------------------------------------------------------------------------------------ Cloning----------------------------------------------------------------------------------{-# INLINE cloneAs #-}-cloneAs ::-    ( MonadIO m-#ifdef DEVBUILD-    , Unbox a-#endif-    )-    => PinnedState -> MutArray a -> m (MutArray a)-cloneAs ps src =-    liftIO $ do-        let startSrc = arrStart src-            srcLen = arrEnd src - startSrc-        newArrContents <--            Unboxed.cloneSliceUnsafeAs ps startSrc srcLen (arrContents src)-        return $ MutArray newArrContents 0 srcLen srcLen--{-# INLINE clone #-}-clone ::-    ( MonadIO m-#ifdef DEVBUILD-    , Unbox a-#endif-    )-    => MutArray a -> m (MutArray a)-clone = cloneAs Unpinned--{-# INLINE pinnedClone #-}-pinnedClone ::-    ( MonadIO m-#ifdef DEVBUILD-    , Unbox a-#endif-    )-    => MutArray a -> m (MutArray a)-pinnedClone = cloneAs Pinned------------------------------------------------------------------------------------ Combining------------------------------------------------------------------------------------ | Copy two arrays into a newly allocated array. If the first array is pinned--- the spliced array is also pinned.-{-# INLINE spliceCopy #-}-spliceCopy :: forall m a. MonadIO m =>-#ifdef DEVBUILD-    Unbox a =>-#endif-    MutArray a -> MutArray a -> m (MutArray a)-spliceCopy arr1 arr2 = liftIO $ do-    let start1 = arrStart arr1-        start2 = arrStart arr2-        len1 = arrEnd arr1 - start1-        len2 = arrEnd arr2 - start2-    let newLen = len1 + len2-    newArrContents <--        if Unboxed.isPinned (arrContents arr1)-        then Unboxed.pinnedNew newLen-        else Unboxed.new newLen-    let len = len1 + len2-    putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1-    putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2-    return $ MutArray newArrContents 0 len len---- | Really really unsafe, appends the second array into the first array. If--- the first array does not have enough space it may cause silent data--- corruption or if you are lucky a segfault.-{-# INLINE spliceUnsafe #-}-spliceUnsafe :: MonadIO m =>-    MutArray a -> MutArray a -> m (MutArray a)-spliceUnsafe dst src =-    liftIO $ do-         let startSrc = arrStart src-             srcLen = arrEnd src - startSrc-             endDst = arrEnd dst-         assertM(endDst + srcLen <= arrBound dst)-         putSliceUnsafe-             (arrContents src) startSrc (arrContents dst) endDst srcLen-         return $ dst {arrEnd = endDst + srcLen}---- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no--- reserved space available in @dst@ it is reallocated to a size determined by--- the @sizer dstBytes srcBytes@ function, where @dstBytes@ is the size of the--- first array and @srcBytes@ is the size of the second array, in bytes.------ Note that the returned array may be a mutated version of first array.------ /Pre-release/-{-# INLINE spliceWith #-}-spliceWith :: forall m a. (MonadIO m, Unbox a) =>-    (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)-spliceWith sizer dst@(MutArray _ start end bound) src = do-{--    let f = writeAppendWith (`sizer` byteLength src) (return dst)-     in D.fold f (toStreamD src)--}-    assert (end <= bound) (return ())-    let srcBytes = arrEnd src - arrStart src--    dst1 <--        if end + srcBytes >= bound-        then do-            let dstBytes = end - start-                newSizeInBytes = sizer dstBytes srcBytes-            when (newSizeInBytes < dstBytes + srcBytes)-                $ error-                    $ "splice: newSize is less than the total size "-                    ++ "of arrays being appended. Please check the "-                    ++ "sizer function passed."-            liftIO $ realloc newSizeInBytes dst-        else return dst-    spliceUnsafe dst1 src---- | The first array is mutated to append the second array. If there is no--- reserved space available in the first array a new allocation of exact--- required size is done.------ Note that the returned array may be a mutated version of first array.------ >>> splice = MutArray.spliceWith (+)------ If the original array is pinned the spliced array is also pinned.------ /Pre-release/-{-# INLINE splice #-}-splice :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)-splice = spliceWith (+)---- | Like 'append' but the growth of the array is exponential. Whenever a new--- allocation is required the previous array size is at least doubled.------ This is useful to reduce allocations when folding many arrays together.------ Note that the returned array may be a mutated version of first array.------ >>> spliceExp = MutArray.spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))------ /Pre-release/-{-# INLINE spliceExp #-}-spliceExp :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)-spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))------------------------------------------------------------------------------------ Splitting------------------------------------------------------------------------------------ | Drops the separator byte-{-# INLINE breakOn #-}-breakOn :: MonadIO m-    => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))-breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do-    -- XXX Instead of using asPtrUnsafe (pinning memory) we can pass unlifted-    -- Addr# to memchr and it should be safe (from ghc 8.4).-    -- XXX We do not need memchr here, we can use a Haskell equivalent.-    loc <- c_memchr p sep (fromIntegral $ byteLength arr)-    let sepIndex = loc `minusPtr` p-    return $-        if loc == nullPtr-        then (arr, Nothing)-        else-            ( MutArray-                { arrContents = arrContents-                , arrStart = arrStart-                , arrEnd = arrStart + sepIndex -- exclude the separator-                , arrBound = arrStart + sepIndex-                }-            , Just $ MutArray-                    { arrContents = arrContents-                    , arrStart = arrStart + (sepIndex + 1)-                    , arrEnd = arrEnd-                    , arrBound = arrBound-                    }-            )---- | Create two slices of an array without copying the original array. The--- specified index @i@ is the first index of the second slice.----splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)-splitAt i arr@MutArray{..} =-    let maxIndex = length arr - 1-    in  if i < 0-        then error "sliceAt: negative array index"-        else if i > maxIndex-             then error $ "sliceAt: specified array index " ++ show i-                        ++ " is beyond the maximum index " ++ show maxIndex-             else let off = i * SIZE_OF(a)-                      p = arrStart + off-                in ( MutArray-                  { arrContents = arrContents-                  , arrStart = arrStart-                  , arrEnd = p-                  , arrBound = p-                  }-                , MutArray-                  { arrContents = arrContents-                  , arrStart = p-                  , arrEnd = arrEnd-                  , arrBound = arrBound-                  }-                )------------------------------------------------------------------------------------ Casting------------------------------------------------------------------------------------ | Cast an array having elements of type @a@ into an array having elements of--- type @b@. The array size must be a multiple of the size of type @b@--- otherwise accessing the last element of the array may result into a crash or--- a random value.------ /Pre-release/----castUnsafe ::-#ifdef DEVBUILD-    Unbox b =>-#endif-    MutArray a -> MutArray b-castUnsafe (MutArray contents start end bound) =-    MutArray contents start end bound---- | Cast an @MutArray a@ into an @MutArray Word8@.----asBytes :: MutArray a -> MutArray Word8-asBytes = castUnsafe---- | Cast an array having elements of type @a@ into an array having elements of--- type @b@. The length of the array should be a multiple of the size of the--- target element otherwise 'Nothing' is returned.----cast :: forall a b. Unbox b => MutArray a -> Maybe (MutArray b)-cast arr =-    let len = byteLength arr-        r = len `mod` SIZE_OF(b)-     in if r /= 0-        then Nothing-        else Just $ castUnsafe arr---- XXX We can provide another API for "unsafe" FFI calls passing an unlifted--- pointer to the FFI call. For unsafe calls we do not need to pin the array.--- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in--- before the pointer is wrapped.------ From the GHC manual:------ GHC, since version 8.4, guarantees that garbage collection will never occur--- during an unsafe call, even in the bytecode interpreter, and further--- guarantees that unsafe calls will be performed in the calling thread. Making--- it safe to pass heap-allocated objects to unsafe functions.---- Should we just name it asPtr, the unsafety is implicit for any pointer--- operations. And we are safe from Haskell perspective because we will be--- pinning the memory.---- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an--- array as a pointer to some operating system call or to a "safe" FFI call.------ If the array is not pinned it is copied to pinned memory before passing it--- to the monadic action.------ /Performance Notes:/ Forces a copy if the array is not pinned. It is advised--- that the programmer keeps this in mind and creates a pinned array--- opportunistically before this operation occurs, to avoid the cost of a copy--- if possible.------ /Unsafe/ because of direct pointer operations. The user must ensure that--- they are writing within the legal bounds of the array.------ /Pre-release/----{-# INLINE asPtrUnsafe #-}-asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b-asPtrUnsafe arr f =-    Unboxed.asPtrUnsafe-        (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))------------------------------------------------------------------------------------ Equality------------------------------------------------------------------------------------ | Compare the length of the arrays. If the length is equal, compare the--- lexicographical ordering of two underlying byte arrays otherwise return the--- result of length comparison.------ /Pre-release/-{-# INLINE cmp #-}-cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering-cmp arr1 arr2 =-    liftIO-        $ do-            let marr1 = getMutableByteArray# (arrContents arr1)-                marr2 = getMutableByteArray# (arrContents arr2)-                !(I# st1#) = arrStart arr1-                !(I# st2#) = arrStart arr2-                !(I# len#) = byteLength arr1-            case compare (byteLength arr1) (byteLength arr2) of-                EQ -> do-                    r <- liftIO $ IO $ \s# ->-                             let res =-                                     I#-                                         (compareByteArrays#-                                              (unsafeCoerce# marr1)-                                              st1#-                                              (unsafeCoerce# marr2)-                                              st2#-                                              len#)-                              in (# s#, res #)-                    return $ compare r 0-                x -> return x------------------------------------------------------------------------------------ NFData++    -- ** Conversion+    -- *** Pinned and Unpinned+    , pin+    , unpin+    , isPinned++    -- ** Casting+    , cast+    , castUnsafe -- XXX unsafeCast+    , asBytes+    , unsafePinnedAsPtr+    , unsafeAsPtr++    -- ** Construction+    , empty++    -- *** New+    -- | New arrays are always empty arrays with some reserve capacity to+    -- extend the length without reallocating.+    , emptyOf+    , newArrayWith -- emptyAlignedWith+    , pinnedEmptyOf+    , pinnedNewAligned -- XXX not required+    -- , new -- uninitialized array of specified length++    -- *** Cloning+    , clone+    , pinnedClone++    -- *** Slicing+    -- | Get a subarray without copying+    , getSliceUnsafe -- XXX unsafeGetSlice+    , getSlice+    , splitAt -- XXX should be able to express using getSlice+    , breakOn++    -- *** Stream Folds+    , ArrayUnsafe (..)+    , unsafeCreateOfWith+    , unsafeCreateOf+    , unsafePinnedCreateOf+    , pinnedCreateOf+    , createOfWith+    , createOf+    , revCreateOf++    , pinnedCreate+    , createWith+    , create+    -- , revCreate++    -- *** From containers+    , fromListN+    , pinnedFromListN+    , fromList+    , pinnedFromList+    , fromListRevN+    , fromListRev+    , fromStreamN+    , fromStream+    , fromPureStreamN+    , fromPureStream+    , fromByteStr#+    , fromPtrN+    , fromChunksK+    , fromChunksRealloced -- fromSmallChunks++    -- ** Random writes+    , putIndex+    , putIndexUnsafe -- XXX unsafePutIndex+    , putIndices+    -- , putFromThenTo+    -- , putFrom -- start writing at the given position+    -- , putUpto -- write from beginning up to the given position+    -- , putFromTo+    -- , putFromRev+    -- , putUptoRev+    , modifyIndexUnsafe -- XXX unsafeModifyIndex+    , modifyIndex+    , modifyIndices+    , modify+    , swapIndices+    , unsafeSwapIndices++    -- ** Reading++    -- *** Indexing+    , getIndex+    , getIndexUnsafe -- XXX unsafeGetIndex+    -- , getFromThenTo+    , getIndexRev -- getRevIndex?+    , indexReader+    , indexReaderWith++    -- *** To Streams+    , read+    , readRev+    , toStreamWith+    , toStreamRevWith+    , toStreamK+    , toStreamKWith+    , toStreamKRev+    , toStreamKRevWith++    -- *** To Containers+    , toList++    -- *** Unfolds+    -- experimental+    , producerWith+    , producer++    , reader+    , readerRevWith+    , readerRev++    -- ** Size and Capacity+    -- *** Size+    , length+    , byteLength++    -- *** Capacity+    -- , capacity+    , byteCapacity+    , bytesFree++    -- *** Capacity Management+    , blockSize+    , arrayChunkBytes+    , allocBytesToElemCount+    , realloc -- this is "resize"+    , grow+    , growExp+    , rightSize++    -- ** Folding+    , foldl'+    , foldr+    , byteCmp+    , byteEq++    -- ** In-place Mutation Algorithms+    , strip+    , reverse+    , permute+    , partitionBy+    , shuffleBy+    , divideBy+    , mergeBy+    , bubble++    -- ** Growing and Shrinking+    -- | Arrays grow only at the end, though it is possible to grow on both+    -- sides and therefore have a cons as well as snoc. But that will require+    -- both lower and upper bound in the array representation.++    -- *** Appending elements+    , snocWith+    , snoc+    , snocLinear+    , snocMay+    , snocUnsafe -- XXX unsafeSnoc++    -- *** Appending streams+    , unsafeAppendN+    , appendN+    , appendWith+    , append++    -- *** Appending arrays+    , spliceCopy+    , spliceWith+    , splice+    , spliceExp+    , spliceUnsafe -- XXX unsafeSplice+    -- , putSlice+    -- , appendSlice+    -- , appendSliceFrom++    -- ** Serialization using Unbox+    , pokeAppend+    , pokeAppendMay+    , pokeSkipUnsafe -- unsafePokeSkip++    -- ** Deserialization using Unbox+    , peekUncons+    , peekUnconsUnsafe -- unsafePeekUncons+    , peekSkipUnsafe -- unsafePeekSkip++    -- Arrays of arrays+    --  We can add dimensionality parameter to the array type to get+    --  multidimensional arrays. Multidimensional arrays would just be a+    --  convenience wrapper on top of single dimensional arrays.++    -- ** Streams of Arrays+    -- *** Chunk+    -- | Group a stream into arrays.+    , chunksOf+    , pinnedChunksOf+    , buildChunks++    -- *** Split+    -- | Split an array into slices.++    -- , getSlicesFromLenN+    , splitOn+    -- , slicesOf++    -- *** Concat+    -- | Append the arrays in a stream to form a stream of elements.+    , concatWith+    , concatRevWith+    , concat+    , concatRev++    -- *** Compact+    -- | Append the arrays in a stream to form a stream of larger arrays.+    , SpliceState (..)+    , pCompactLE+    , pPinnedCompactLE+    , compactLeAs+    , fCompactGE+    , fPinnedCompactGE+    , lCompactGE+    , lPinnedCompactGE+    , compactGE+    , compactEQ++    -- ** Utilities+    , roundUpToPower2+    , memcpy+    , memcmp+    , c_memchr++    -- * Deprecated+    , asPtrUnsafe+    , writeChunks+    , flattenArrays+    , flattenArraysRev+    , fromArrayStreamK+    , fromStreamDN+    , fromStreamD+    , cmp+    , getIndices+    , getIndicesWith+    , resize+    , resizeExp+    , nil+    , new+    , pinnedNew+    , pinnedNewBytes+    , writeAppendNUnsafe+    , writeAppendN+    , writeAppendWith+    , writeAppend+    , writeNWithUnsafe+    , writeNWith+    , writeNUnsafe+    , pinnedWriteNUnsafe+    , writeN+    , pinnedWriteN+    , pinnedWriteNAligned -- XXX not required+    , writeWith+    , write+    , pinnedWrite+    , writeRevN+    )+where++#include "assert.hs"+#include "inline.hs"+#include "ArrayMacros.h"+#include "MachDeps.h"++import Control.Monad (when, void)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Bifunctor (first)+import Data.Bits (shiftR, (.|.), (.&.))+import Data.Functor.Identity (Identity(..))+import Data.Proxy (Proxy(..))+import Data.Word (Word8)+import Foreign.C.Types (CSize(..), CInt(..))+import Foreign.Ptr (plusPtr, minusPtr, nullPtr)+import Streamly.Internal.Data.MutByteArray.Type+    ( MutByteArray(..)+    , PinnedState(..)+    , getMutableByteArray#+    , putSliceUnsafe+    )+import Streamly.Internal.Data.Unbox (Unbox(..))+import GHC.Base+    ( IO(..)+    , Int(..)+    , compareByteArrays#+    , copyMutableByteArray#+    )+import GHC.Base (noinline)+import GHC.Exts (unsafeCoerce#, Addr#)+import GHC.Ptr (Ptr(..))++import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Producer.Type (Producer (..))+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.Parser.Type (Parser (..))+import Streamly.Internal.Data.StreamK.Type (StreamK)+import Streamly.Internal.Data.SVar.Type (adaptState, defState)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))+import Streamly.Internal.Data.Unfold.Type (Unfold(..))+import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)++import qualified Streamly.Internal.Data.Fold.Type as FL+import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed+import qualified Streamly.Internal.Data.Parser.Type as Parser+-- import qualified Streamly.Internal.Data.Fold.Type as Fold+import qualified Streamly.Internal.Data.Producer as Producer+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.Stream.Lift as D+import qualified Streamly.Internal.Data.StreamK.Type as K+import qualified Prelude++import Prelude hiding+    (Foldable(..), concat, read, unlines, splitAt, reverse, truncate)++#include "DocTestDataMutArray.hs"++-------------------------------------------------------------------------------+-- Foreign helpers+-------------------------------------------------------------------------------++-- NOTE: Have to be "ccall unsafe" so that we can pass unpinned memory to these+foreign import ccall unsafe "string.h memcpy" c_memcpy+    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)++foreign import ccall unsafe "string.h memchr" c_memchr+    :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)++foreign import ccall unsafe "string.h memcmp" c_memcmp+    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt++foreign import ccall unsafe "string.h strlen" c_strlen+    :: Ptr Word8 -> IO CSize++-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return+-- how many elements of that type will completely fit in those bytes.+--+{-# INLINE bytesToElemCount #-}+bytesToElemCount :: forall a. Unbox a => a -> Int -> Int+bytesToElemCount _ n = n `div` SIZE_OF(a)++-- XXX we are converting Int to CSize+memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()+memcpy dst src len = void (c_memcpy dst src (fromIntegral len))++-- XXX we are converting Int to CSize+-- return True if the memory locations have identical contents+{-# INLINE memcmp #-}+memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO Bool+memcmp p1 p2 len = do+    r <- c_memcmp p1 p2 (fromIntegral len)+    return $ r == 0++-------------------------------------------------------------------------------+-- MutArray Data Type+-------------------------------------------------------------------------------++-- $arrayNotes+--+-- We can use an 'Unboxed' constraint in the MutArray type and the constraint+-- can be automatically provided to a function that pattern matches on the+-- MutArray type. However, it has huge performance cost, so we do not use it.+-- Investigate a GHC improvement possiblity.++-- | An unboxed mutable array. An array is created with a given length+-- and capacity. Length is the number of valid elements in the array.  Capacity+-- is the maximum number of elements that the array can be expanded to without+-- having to reallocate the memory.+--+-- The elements in the array can be mutated in-place without changing the+-- reference (constructor). However, the length of the array cannot be mutated+-- in-place.  A new array reference is generated when the length changes.  When+-- the length is increased (upto the maximum reserved capacity of the array),+-- the array is not reallocated and the new reference uses the same underlying+-- memory as the old one.+--+-- Several routines in this module allow the programmer to control the capacity+-- of the array. The programmer can control the trade-off between memory usage+-- and performance impact due to reallocations when growing or shrinking the+-- array.+--+data MutArray a =+#ifdef DEVBUILD+    Unbox a =>+#endif+    -- The array is a range into arrContents. arrContents may be a superset of+    -- the slice represented by the array. All offsets are in bytes.+    MutArray+    { arrContents :: {-# UNPACK #-} !MutByteArray+    , arrStart :: {-# UNPACK #-} !Int  -- ^ index into arrContents+    , arrEnd   :: {-# UNPACK #-} !Int    -- ^ index into arrContents+                                       -- Represents the first invalid index of+                                       -- the array.+    , arrBound :: {-# UNPACK #-} !Int    -- ^ first invalid index of arrContents.+    }++-------------------------------------------------------------------------------+-- Pinning & Unpinning+-------------------------------------------------------------------------------++-- | Return a copy of the array in pinned memory if unpinned, else return the+-- original array.+{-# INLINE pin #-}+pin :: MutArray a -> IO (MutArray a)+pin arr@MutArray{..} =+    if Unboxed.isPinned arrContents+    then pure arr+    else pinnedClone arr++-- | Return a copy of the array in unpinned memory if pinned, else return the+-- original array.+{-# INLINE unpin #-}+unpin :: MutArray a -> IO (MutArray a)+unpin arr@MutArray{..} =+    if Unboxed.isPinned arrContents+    then clone arr+    else pure arr++-- | Return 'True' if the array is allocated in pinned memory.+{-# INLINE isPinned #-}+isPinned :: MutArray a -> Bool+isPinned MutArray{..} = Unboxed.isPinned arrContents++-------------------------------------------------------------------------------+-- Construction+-------------------------------------------------------------------------------++-- XXX Change the names to use "new" instead of "newArray". That way we can use+-- the same names for managed file system objects as well. For unmanaged ones+-- we can use open/create etc as usual.+--+-- A new array is similar to "touch" creating a zero length file. An mmapped+-- array would be similar to a sparse file with holes. TBD: support mmapped+-- files and arrays.++-- GHC always guarantees word-aligned memory, alignment is important only when+-- we need more than that.  See stg_pinnedNewAlignedByteArrayzh and+-- allocatePinned in GHC source.++-- XXX Rename to emptyAlignedWith, alignSize should be first arg.++-- | @newArrayWith allocator alignment count@ allocates a new array of zero+-- length and with a capacity to hold @count@ elements, using @allocator+-- size alignment@ as the memory allocator function.+--+-- Alignment must be greater than or equal to machine word size and a power of+-- 2.+--+-- Alignment is ignored if the allocator allocates unpinned memory.+--+-- /Pre-release/+{-# INLINE newArrayWith #-}+newArrayWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> Int -> m MutByteArray) -> Int -> Int -> m (MutArray a)+newArrayWith alloc alignSize count = do+    let size = max (count * SIZE_OF(a)) 0+    contents <- alloc size alignSize+    return $ MutArray+        { arrContents = contents+        , arrStart = 0+        , arrEnd   = 0+        , arrBound = size+        }++-- For arrays "nil" sounds a bit odd. empty is better. The only problem with+-- empty is that it is also used by the Alternative type class. But assuming we+-- will mostly import the Array module qualified this should be fine.++-- | Create an empty array.+empty ::+#ifdef DEVBUILD+    Unbox a =>+#endif+    MutArray a+empty = MutArray Unboxed.empty 0 0 0++{-# DEPRECATED nil "Please use empty instead." #-}+nil ::+#ifdef DEVBUILD+    Unbox a =>+#endif+    MutArray a+nil = empty++{-# INLINE newBytesAs #-}+newBytesAs :: MonadIO m =>+#ifdef DEVBUILD+    Unbox a =>+#endif+    PinnedState -> Int -> m (MutArray a)+newBytesAs ps bytes = do+    contents <- liftIO $ Unboxed.newBytesAs ps bytes+    return $ MutArray+        { arrContents = contents+        , arrStart = 0+        , arrEnd   = 0+        , arrBound = bytes+        }++-- | Allocates a pinned empty array that with a reserved capacity of bytes.+-- The memory of the array is uninitialized and the allocation is aligned as+-- per the 'Unboxed' instance of the type.+--+-- /Pre-release/+{-# INLINE pinnedNewBytes #-}+{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}+pinnedNewBytes :: MonadIO m =>+#ifdef DEVBUILD+    Unbox a =>+#endif+    Int -> m (MutArray a)+pinnedNewBytes = newBytesAs Pinned++-- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and+-- the alignment is dictated by the 'Unboxed' instance of the type.+--+-- /Internal/+{-# INLINE pinnedNewAligned #-}+pinnedNewAligned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)+pinnedNewAligned =+    newArrayWith (\s a -> liftIO $ Unboxed.pinnedNewAlignedBytes s a)++{-# INLINE newAs #-}+newAs :: (MonadIO m, Unbox a) => PinnedState -> Int -> m (MutArray a)+newAs ps =+    newArrayWith+        (\s _ -> liftIO $ Unboxed.newBytesAs ps s)+        (error "new: alignment is not used in unpinned arrays.")++-- XXX can unaligned allocation be more efficient when alignment is not needed?++-- | Allocates a pinned array of zero length but growable to the specified+-- capacity without reallocation.+{-# INLINE pinnedEmptyOf #-}+pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+pinnedEmptyOf = newAs Pinned++-- XXX Deprecate in major+-- {-# DEPRECATED pinnedNew "Please use pinnedEmptyOf instead." #-}+{-# INLINE pinnedNew #-}+pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+pinnedNew = pinnedEmptyOf++-- | Allocates an unpinned array of zero length but growable to the specified+-- capacity without reallocation.+--+{-# INLINE emptyOf #-}+emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)+emptyOf = newAs Unpinned++-- XXX Deprecate in major+-- {-# DEPRECATED new "Please use emptyOf instead." #-}+{-# INLINE new #-}+new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)+new = emptyOf++-------------------------------------------------------------------------------+-- Random writes+-------------------------------------------------------------------------------++-- | Write the given element to the given index of the array. Does not check if+-- the index is out of bounds of the array.+--+-- /Pre-release/+{-# INLINE putIndexUnsafe #-}+putIndexUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> MutArray a -> a -> m ()+putIndexUnsafe i MutArray{..} x = do+    let index = INDEX_OF(arrStart, i, a)+    assert (i >= 0 && INDEX_VALID(index, arrEnd, a)) (return ())+    liftIO $ pokeAt index arrContents  x++invalidIndex :: String -> Int -> a+invalidIndex label i =+    error $ label ++ ": invalid array index " ++ show i++-- | /O(1)/ Write the given element at the given index in the array.+-- Performs in-place mutation of the array.+--+-- >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))+-- >>> f = MutArray.putIndices+-- >>> putIndex ix arr val = Stream.fold (f arr) (Stream.fromPure (ix, val))+--+{-# INLINE putIndex #-}+putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()+putIndex i MutArray{..} x = do+    let index = INDEX_OF(arrStart,i,a)+    if i >= 0 && INDEX_VALID(index,arrEnd,a)+    then liftIO $ pokeAt index arrContents  x+    else invalidIndex "putIndex" i++-- | Write an input stream of (index, value) pairs to an array. Throws an+-- error if any index is out of bounds.+--+-- /Pre-release/+{-# INLINE putIndices #-}+putIndices :: forall m a. (MonadIO m, Unbox a)+    => MutArray a -> Fold m (Int, a) ()+putIndices arr = FL.foldlM' step (return ())++    where++    step () (i, x) = putIndex i arr x++-- | Modify a given index of an array using a modifier function.+--+-- Unsafe because it does not check the bounds of the array.+--+-- /Pre-release/+modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> (a -> (a, b)) -> m b+modifyIndexUnsafe i MutArray{..} f = liftIO $ do+        let index = INDEX_OF(arrStart,i,a)+        assert (i >= 0 && INDEX_NEXT(index,a) <= arrEnd) (return ())+        r <- peekAt index arrContents+        let (x, res) = f r+        pokeAt index arrContents  x+        return res++-- | Modify a given index of an array using a modifier function.+--+-- /Pre-release/+modifyIndex :: forall m a b. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> (a -> (a, b)) -> m b+modifyIndex i MutArray{..} f = do+    let index = INDEX_OF(arrStart,i,a)+    if i >= 0 && INDEX_VALID(index,arrEnd,a)+    then liftIO $ do+        r <- peekAt index arrContents+        let (x, res) = f r+        pokeAt index arrContents  x+        return res+    else invalidIndex "modifyIndex" i++-- | Modify the array indices generated by the supplied stream.+--+-- /Pre-release/+{-# INLINE modifyIndices #-}+modifyIndices :: forall m a . (MonadIO m, Unbox a)+    => MutArray a -> (Int -> a -> a) -> Fold m Int ()+modifyIndices arr f = FL.foldlM' step initial++    where++    initial = return ()++    step () i =+        let f1 x = (f i x, ())+         in modifyIndex i arr f1++-- | Modify each element of an array using the supplied modifier function.+--+-- This is an in-place equivalent of an immutable map operation.+--+-- /Pre-release/+modify :: forall m a. (MonadIO m, Unbox a)+    => MutArray a -> (a -> a) -> m ()+modify MutArray{..} f = liftIO $+    go arrStart++    where++    go i =+        when (INDEX_VALID(i,arrEnd,a)) $ do+            r <- peekAt i arrContents+            pokeAt i arrContents (f r)+            go (INDEX_NEXT(i,a))++-- XXX We could specify the number of bytes to swap instead of Proxy. Need+-- to ensure that the memory does not overlap.+{-# INLINE swapArrayByteIndices #-}+swapArrayByteIndices ::+       forall a. Unbox a+    => Proxy a+    -> MutByteArray+    -> Int+    -> Int+    -> IO ()+swapArrayByteIndices _ arrContents i1 i2 = do+    r1 <- peekAt i1 arrContents+    r2 <- peekAt i2 arrContents+    pokeAt i1 arrContents (r2 :: a)+    pokeAt i2 arrContents (r1 :: a)++-- | Swap the elements at two indices without validating the indices.+--+-- /Unsafe/: This could result in memory corruption if indices are not valid.+--+-- /Pre-release/+{-# INLINE unsafeSwapIndices #-}+unsafeSwapIndices :: forall m a. (MonadIO m, Unbox a)+    => Int -> Int -> MutArray a -> m ()+unsafeSwapIndices i1 i2 MutArray{..} = liftIO $ do+        let t1 = INDEX_OF(arrStart,i1,a)+            t2 = INDEX_OF(arrStart,i2,a)+        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2++-- | Swap the elements at two indices.+--+-- /Pre-release/+swapIndices :: forall m a. (MonadIO m, Unbox a)+    => Int -> Int -> MutArray a -> m ()+swapIndices i1 i2 MutArray{..} = liftIO $ do+        let t1 = INDEX_OF(arrStart,i1,a)+            t2 = INDEX_OF(arrStart,i2,a)+        when (i1 < 0 || INDEX_INVALID(t1,arrEnd,a))+            $ invalidIndex "swapIndices" i1+        when (i2 < 0 || INDEX_INVALID(t2,arrEnd,a))+            $ invalidIndex "swapIndices" i2+        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2++-------------------------------------------------------------------------------+-- Rounding+-------------------------------------------------------------------------------++-- XXX Should we use bitshifts in calculations or it gets optimized by the+-- compiler/processor itself?+--+-- | The page or block size used by the GHC allocator. Allocator allocates at+-- least a block and then allocates smaller allocations from within a block.+blockSize :: Int+blockSize = 4 * 1024++-- | Allocations larger than 'largeObjectThreshold' are in multiples of block+-- size and are always pinned. The space beyond the end of a large object up to+-- the end of the block is unused.+largeObjectThreshold :: Int+largeObjectThreshold = (blockSize * 8) `div` 10++-- XXX Should be done only when we are using the GHC allocator.+-- | Round up an array larger than 'largeObjectThreshold' to use the whole+-- block.+{-# INLINE roundUpLargeArray #-}+roundUpLargeArray :: Int -> Int+roundUpLargeArray size =+    if size >= largeObjectThreshold+    then+        assert+            (blockSize /= 0 && ((blockSize .&. (blockSize - 1)) == 0))+            ((size + blockSize - 1) .&. negate blockSize)+    else size++{-# INLINE isPower2 #-}+isPower2 :: Int -> Bool+isPower2 n = n .&. (n - 1) == 0++{-# INLINE roundUpToPower2 #-}+roundUpToPower2 :: Int -> Int+roundUpToPower2 n =+#if WORD_SIZE_IN_BITS == 64+    1 + z6+#else+    1 + z5+#endif++    where++    z0 = n - 1+    z1 = z0 .|. z0 `shiftR` 1+    z2 = z1 .|. z1 `shiftR` 2+    z3 = z2 .|. z2 `shiftR` 4+    z4 = z3 .|. z3 `shiftR` 8+    z5 = z4 .|. z4 `shiftR` 16+    z6 = z5 .|. z5 `shiftR` 32++-- | @allocBytesToBytes elem allocatedBytes@ returns the array size in bytes+-- such that the real allocation is less than or equal to @allocatedBytes@,+-- unless @allocatedBytes@ is less than the size of one array element in which+-- case it returns one element's size.+--+{-# INLINE allocBytesToBytes #-}+allocBytesToBytes :: forall a. Unbox a => a -> Int -> Int+allocBytesToBytes _ n = max (arrayPayloadSize n) (SIZE_OF(a))++-- | Given an 'Unboxed' type (unused first arg) and real allocation size+-- (including overhead), return how many elements of that type will completely+-- fit in it, returns at least 1.+--+{-# INLINE allocBytesToElemCount #-}+allocBytesToElemCount :: Unbox a => a -> Int -> Int+allocBytesToElemCount x bytes =+    let n = bytesToElemCount x (allocBytesToBytes x bytes)+     in assert (n >= 1) n++-- | The default chunk size by which the array creation routines increase the+-- size of the array when the array is grown linearly.+arrayChunkBytes :: Int+arrayChunkBytes = 1024++-------------------------------------------------------------------------------+-- Resizing+-------------------------------------------------------------------------------++-- | Round the second argument down to multiples of the first argument.+{-# INLINE roundDownTo #-}+roundDownTo :: Int -> Int -> Int+roundDownTo elemSize size = size - (size `mod` elemSize)++-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.+-- Since this is not inlined Unboxed consrraint leads to dictionary passing+-- which complicates some inspection tests.+--+{-# NOINLINE reallocExplicitAs #-}+reallocExplicitAs :: PinnedState -> Int -> Int -> MutArray a -> IO (MutArray a)+reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do+    assertM(arrEnd <= arrBound)++    -- Allocate new array+    let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes+    contents <-+        if ps == Pinned+        then Unboxed.pinnedNew newCapMaxInBytes+        else Unboxed.new newCapMaxInBytes+    let !(MutByteArray mbarrFrom#) = arrContents+        !(MutByteArray mbarrTo#) = contents++    -- Copy old data+    let oldStart = arrStart+        !(I# oldStartInBytes#) = oldStart+        oldSizeInBytes = arrEnd - oldStart+        newCapInBytes = roundDownTo elemSize newCapMaxInBytes+        !newLenInBytes@(I# newLenInBytes#) = min oldSizeInBytes newCapInBytes+    assert (oldSizeInBytes `mod` elemSize == 0) (return ())+    assert (newLenInBytes >= 0) (return ())+    assert (newLenInBytes `mod` elemSize == 0) (return ())+    IO $ \s# -> (# copyMutableByteArray# mbarrFrom# oldStartInBytes#+                        mbarrTo# 0# newLenInBytes# s#, () #)++    return $ MutArray+        { arrStart = 0+        , arrContents = contents+        , arrEnd   = newLenInBytes+        , arrBound = newCapInBytes+        }++-- XXX Should these be called reallocBytes etc?+-- XXX We may also need reallocAs to allocate as pinned/unpinned explicitly. In+-- fact clone/pinnedClone can be implemented using reallocAs.++-- | @realloc newCapacity array@ reallocates the array to the specified+-- capacity in bytes.+--+-- If the new size is less than the original array the array gets truncated.+-- If the new size is not a multiple of array element size then it is rounded+-- down to multiples of array size.  If the new size is more than+-- 'largeObjectThreshold' then it is rounded up to the block size (4K).+--+-- If the original array is pinned, the newly allocated array is also pinned.+{-# INLINABLE realloc #-}+realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+realloc bytes arr =+    let ps =+            if isPinned arr+            then Pinned+            else Unpinned+     in liftIO $ reallocExplicitAs ps (SIZE_OF(a)) bytes arr++-- | @reallocWith label capSizer minIncrBytes array@. The label is used+-- in error messages and the capSizer is used to determine the capacity of the+-- new array in bytes given the current byte length of the array.+reallocWith :: forall m a. (MonadIO m , Unbox a) =>+       String+    -> (Int -> Int)+    -> Int+    -> MutArray a+    -> m (MutArray a)+reallocWith label capSizer minIncrBytes arr = do+    let oldSizeBytes = arrEnd arr - arrStart arr+        newCapBytes = capSizer oldSizeBytes+        newSizeBytes = oldSizeBytes + minIncrBytes+        safeCapBytes = max newCapBytes newSizeBytes+    assertM(safeCapBytes >= newSizeBytes || error (badSize newSizeBytes))++    realloc safeCapBytes arr++    where++    badSize newSize =+        Prelude.concat+            [ label+            , ": new array size (in bytes) is less than required size "+            , show newSize+            , ". Please check the sizing function passed."+            ]++-- | @grow newCapacity array@ changes the total capacity of the array so that+-- it is enough to hold the specified number of elements.  Nothing is done if+-- the specified capacity is less than the length of the array.+--+-- If the capacity is more than 'largeObjectThreshold' then it is rounded up to+-- the block size (4K).+--+-- /Pre-release/+{-# INLINE grow #-}+grow :: forall m a. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> m (MutArray a)+grow nElems arr@MutArray{..} = do+    let req = SIZE_OF(a) * nElems+        cap = arrBound - arrStart+    if req < cap+    then return arr+    else realloc req arr++{-# DEPRECATED resize "Please use grow instead." #-}+{-# INLINE resize #-}+resize :: forall m a. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> m (MutArray a)+resize = grow++-- | Like 'grow' but if the requested byte capacity is more than+-- 'largeObjectThreshold' then it is rounded up to the closest power of 2.+--+-- /Pre-release/+{-# INLINE growExp #-}+growExp :: forall m a. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> m (MutArray a)+growExp nElems arr@MutArray{..} = do+    let req = roundUpLargeArray (SIZE_OF(a) * nElems)+        req1 =+            if req > largeObjectThreshold+            then roundUpToPower2 req+            else req+        cap = arrBound - arrStart+    if req1 < cap+    then return arr+    else realloc req1 arr++{-# DEPRECATED resizeExp "Please use growExp instead." #-}+{-# INLINE resizeExp #-}+resizeExp :: forall m a. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> m (MutArray a)+resizeExp = growExp++-- | Resize the allocated memory to drop any reserved free space at the end of+-- the array and reallocate it to reduce wastage.+--+-- Up to 25% wastage is allowed to avoid reallocations.  If the capacity is+-- more than 'largeObjectThreshold' then free space up to the 'blockSize' is+-- retained.+--+-- /Pre-release/+{-# INLINE rightSize #-}+rightSize :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m (MutArray a)+rightSize arr@MutArray{..} = do+    assert (arrEnd <= arrBound) (return ())+    let start = arrStart+        len = arrEnd - start+        capacity = arrBound - start+        target = roundUpLargeArray len+        waste = arrBound - arrEnd+    assert (target >= len) (return ())+    assert (len `mod` SIZE_OF(a) == 0) (return ())+    -- We trade off some wastage (25%) to avoid reallocations and copying.+    if target < capacity && len < 3 * waste+    then realloc target arr+    else return arr++-------------------------------------------------------------------------------+-- Snoc+-------------------------------------------------------------------------------++-- XXX We can possibly use a smallMutableByteArray to hold the start, end,+-- bound pointers.  Using fully mutable handle will ensure that we do not have+-- multiple references to the same array of different lengths lying around and+-- potentially misused. In that case "snoc" need not return a new array (snoc+-- :: MutArray a -> a -> m ()), it will just modify the old reference.  The array+-- length will be mutable.  This means the length function would also be+-- monadic.  Mutable arrays would behave more like files that grow in that+-- case.++-- | Snoc using a 'Ptr'. Low level reusable function.+--+-- /Internal/+{-# INLINE snocNewEnd #-}+snocNewEnd :: (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m (MutArray a)+snocNewEnd newEnd arr@MutArray{..} x = liftIO $ do+    assert (newEnd <= arrBound) (return ())+    pokeAt arrEnd arrContents x+    return $ arr {arrEnd = newEnd}++-- | Really really unsafe, appends the element into the first array, may+-- cause silent data corruption or if you are lucky a segfault if the first+-- array does not have enough space to append the element.+--+-- /Internal/+{-# INLINE snocUnsafe #-}+snocUnsafe :: forall m a. (MonadIO m, Unbox a) =>+    MutArray a -> a -> m (MutArray a)+snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr++-- | Like 'snoc' but does not reallocate when pre-allocated array capacity+-- becomes full.+--+-- /Internal/+{-# INLINE snocMay #-}+snocMay :: forall m a. (MonadIO m, Unbox a) =>+    MutArray a -> a -> m (Maybe (MutArray a))+snocMay arr@MutArray{..} x = do+    let newEnd = INDEX_NEXT(arrEnd,a)+    if newEnd <= arrBound+    then Just <$> snocNewEnd newEnd arr x+    else return Nothing++-- NOINLINE to move it out of the way and not pollute the instruction cache.+{-# NOINLINE snocWithRealloc #-}+snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>+       (Int -> Int)+    -> MutArray a+    -> a+    -> m (MutArray a)+snocWithRealloc sizer arr x = do+    arr1 <- reallocWith "snocWith" sizer (SIZE_OF(a)) arr+    snocUnsafe arr1 x++-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of+-- the array increases by 1.+--+-- If there is no reserved space available in @arr@ it is reallocated to a size+-- in bytes determined by the @sizer oldSizeBytes@ function, where+-- @oldSizeBytes@ is the original size of the array in bytes.+--+-- If the new array size is more than 'largeObjectThreshold' we automatically+-- round it up to 'blockSize'.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- /Pre-release/+{-# INLINE snocWith #-}+snocWith :: forall m a. (MonadIO m, Unbox a) =>+       (Int -> Int)+    -> MutArray a+    -> a+    -> m (MutArray a)+snocWith allocSize arr x = do+    let newEnd = INDEX_NEXT(arrEnd arr,a)+    if newEnd <= arrBound arr+    then snocNewEnd newEnd arr x+    else snocWithRealloc allocSize arr x++-- | The array is mutated to append an additional element to it. If there+-- is no reserved space available in the array then it is reallocated to grow+-- it by 'arrayChunkBytes' rounded up to 'blockSize' when the size becomes more+-- than 'largeObjectThreshold'.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- Performs O(n^2) copies to grow but is thrifty on memory.+--+-- /Pre-release/+{-# INLINE snocLinear #-}+snocLinear :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+snocLinear = snocWith (+ allocBytesToBytes (undefined :: a) arrayChunkBytes)++-- | The array is mutated to append an additional element to it. If there is no+-- reserved space available in the array then it is reallocated to double the+-- original size.+--+-- This is useful to reduce allocations when appending unknown number of+-- elements.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- >>> snoc = MutArray.snocWith (* 2)+--+-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.+--+{-# INLINE snoc #-}+snoc :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+snoc = snocWith f++    where++    f oldSize =+        if isPower2 oldSize+        then oldSize * 2+        else roundUpToPower2 oldSize * 2++-------------------------------------------------------------------------------+-- Serialization/Deserialization+-------------------------------------------------------------------------------++{-# INLINE pokeNewEnd #-}+pokeNewEnd :: (MonadIO m, Unbox a) =>+    Int -> MutArray Word8 -> a -> m (MutArray Word8)+pokeNewEnd newEnd arr@MutArray{..} x = liftIO $ do+    assert (newEnd <= arrBound) (return ())+    liftIO $ pokeAt arrEnd arrContents x+    return $ arr {arrEnd = newEnd}++-- | Really really unsafe, unboxes a Haskell type and appends the resulting+-- bytes to the byte array, may cause silent data corruption or if you are+-- lucky a segfault if the array does not have enough space to append the+-- element.+--+-- /Internal/+{-# INLINE pokeAppendUnsafe #-}+pokeAppendUnsafe :: forall m a. (MonadIO m, Unbox a) =>+    MutArray Word8 -> a -> m (MutArray Word8)+pokeAppendUnsafe arr@MutArray{..} = pokeNewEnd (arrEnd + SIZE_OF(a)) arr++-- | Skip the specified number of bytes in the array. The data in the skipped+-- region remains uninitialzed.+{-# INLINE pokeSkipUnsafe #-}+pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8+pokeSkipUnsafe n arr@MutArray{..} =  do+    let newEnd = arrEnd + n+     in assert (newEnd <= arrBound) (arr {arrEnd = newEnd})++-- | Like 'pokeAppend' but does not grow the array when pre-allocated array+-- capacity becomes full.+--+-- /Internal/+{-# INLINE pokeAppendMay #-}+pokeAppendMay :: forall m a. (MonadIO m, Unbox a) =>+    MutArray Word8 -> a -> m (Maybe (MutArray Word8))+pokeAppendMay arr@MutArray{..} x = liftIO $ do+    let newEnd = arrEnd + SIZE_OF(a)+    if newEnd <= arrBound+    then Just <$> pokeNewEnd newEnd arr x+    else return Nothing++{-# NOINLINE pokeWithRealloc #-}+pokeWithRealloc :: forall m a. (MonadIO m, Unbox a) =>+       (Int -> Int)+    -> MutArray Word8+    -> a+    -> m (MutArray Word8)+pokeWithRealloc sizer arr x = do+    arr1 <- liftIO $ reallocWith "pokeWithRealloc" sizer (SIZE_OF(a)) arr+    pokeAppendUnsafe arr1 x++{-# INLINE pokeAppendWith #-}+pokeAppendWith :: forall m a. (MonadIO m, Unbox a) =>+       (Int -> Int)+    -> MutArray Word8+    -> a+    -> m (MutArray Word8)+pokeAppendWith allocSize arr x = liftIO $ do+    let newEnd = arrEnd arr + SIZE_OF(a)+    if newEnd <= arrBound arr+    then pokeNewEnd newEnd arr x+    else pokeWithRealloc allocSize arr x++-- | Unbox a Haskell type and append the resulting bytes to a mutable byte+-- array. The array is grown exponentially when more space is needed.+--+-- Definition:+--+-- >>> pokeAppend arr x = MutArray.castUnsafe <$> MutArray.snoc (MutArray.castUnsafe arr) x+--+{-# INLINE pokeAppend #-}+pokeAppend :: forall m a. (MonadIO m, Unbox a) =>+    MutArray Word8 -> a -> m (MutArray Word8)+pokeAppend = pokeAppendWith f++    where++    f oldSize =+        if isPower2 oldSize+        then oldSize * 2+        else roundUpToPower2 oldSize * 2++-- | Really really unsafe, create a Haskell value from an unboxed byte array,+-- does not check if the array is big enough, may return garbage or if you are+-- lucky may cause a segfault.+--+-- /Internal/+{-# INLINE peekUnconsUnsafe #-}+peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) =>+    MutArray Word8 -> m (a, MutArray Word8)+peekUnconsUnsafe MutArray{..} = do+    let start1 = arrStart + SIZE_OF(a)+    assert (start1 <= arrEnd) (return ())+    liftIO $ do+        r <- peekAt arrStart arrContents+        return (r, MutArray arrContents start1 arrEnd arrBound)++-- | Discard the specified number of bytes in the array.+{-# INLINE peekSkipUnsafe #-}+peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8+peekSkipUnsafe n MutArray{..} =+    let start1 = arrStart + n+     in assert (start1 <= arrEnd) (MutArray arrContents start1 arrEnd arrBound)++-- | Create a Haskell value from its unboxed representation from the head of a+-- byte array, return the value and the remaining array.+{-# INLINE peekUncons #-}+peekUncons :: forall m a. (MonadIO m, Unbox a) =>+    MutArray Word8 -> m (Maybe a, MutArray Word8)+peekUncons arr@MutArray{..} = do+    let start1 = arrStart + SIZE_OF(a)+    if start1 > arrEnd+    then return (Nothing, arr)+    else liftIO $ do+        r <- peekAt arrStart arrContents+        return (Just r, MutArray arrContents start1 arrEnd arrBound)++-------------------------------------------------------------------------------+-- Random reads+-------------------------------------------------------------------------------++-- XXX Can this be deduplicated with array/foreign++-- | Return the element at the specified index without checking the bounds.+--+-- Unsafe because it does not check the bounds of the array.+{-# INLINE_NORMAL getIndexUnsafe #-}+getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+getIndexUnsafe i MutArray{..} = do+    let index = INDEX_OF(arrStart,i,a)+    assert (i >= 0 && INDEX_VALID(index,arrEnd,a)) (return ())+    liftIO $ peekAt index arrContents++-- | /O(1)/ Lookup the element at the given index. Index starts from 0.+--+{-# INLINE getIndex #-}+getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)+getIndex i MutArray{..} = do+    let index = INDEX_OF(arrStart,i,a)+    if i >= 0 && INDEX_VALID(index,arrEnd,a)+    then liftIO $ Just <$> peekAt index arrContents+    else return Nothing++-- | /O(1)/ Lookup the element at the given index from the end of the array.+-- Index starts from 0.+--+-- Slightly faster than computing the forward index and using getIndex.+--+{-# INLINE getIndexRev #-}+getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+getIndexRev i MutArray{..} = do+    let index = RINDEX_OF(arrEnd,i,a)+    if i >= 0 && index >= arrStart+    then liftIO $ peekAt index arrContents+    else invalidIndex "getIndexRev" i++data GetIndicesState contents start end st =+    GetIndicesState contents start end st++{-# INLINE indexReaderWith #-}+indexReaderWith :: (Monad m, Unbox a) =>+    (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a+indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject++    where++    inject (MutArray contents start end _) =+        return $ GetIndicesState contents start end sti++    {-# INLINE_LATE step #-}+    step (GetIndicesState contents start end st) = do+        r <- stepi defState st+        case r of+            D.Yield i s -> do+                x <- liftio $ getIndex i (MutArray contents start end undefined)+                case x of+                    Just v -> return $ D.Yield v (GetIndicesState contents start end s)+                    Nothing -> error "Invalid Index"+            D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)+            D.Stop -> return D.Stop++{-# DEPRECATED getIndicesWith "Please use indexReaderWith instead." #-}+{-# INLINE getIndicesWith #-}+getIndicesWith :: (Monad m, Unbox a) =>+    (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a+getIndicesWith = indexReaderWith++-- | Given an unfold that generates array indices, read the elements on those+-- indices from the supplied MutArray. An error is thrown if an index is out of+-- bounds.+--+-- /Pre-release/+{-# INLINE indexReader #-}+indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a+indexReader = indexReaderWith liftIO++-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold+{-# DEPRECATED getIndices "Please use indexReader instead." #-}+{-# INLINE getIndices #-}+getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a+getIndices = indexReader++-------------------------------------------------------------------------------+-- Subarrays+-------------------------------------------------------------------------------++-- XXX We can also get immutable slices.++-- | /O(1)/ Slice an array in constant time.+--+-- Unsafe: The bounds of the slice are not checked.+--+-- /Unsafe/+--+-- /Pre-release/+{-# INLINE getSliceUnsafe #-}+getSliceUnsafe :: forall a. Unbox a+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> MutArray a+    -> MutArray a+getSliceUnsafe index len (MutArray contents start e _) =+    let fp1 = INDEX_OF(start,index,a)+        end = fp1 + (len * SIZE_OF(a))+     in assert+            (index >= 0 && len >= 0 && end <= e)+            -- Note: In a slice we always use bound = end so that the slice+            -- user cannot overwrite elements beyond the end of the slice.+            (MutArray contents fp1 end end)++-- | /O(1)/ Slice an array in constant time. Throws an error if the slice+-- extends out of the array bounds.+--+-- /Pre-release/+{-# INLINE getSlice #-}+getSlice :: forall a. Unbox a =>+       Int -- ^ from index+    -> Int -- ^ length of the slice+    -> MutArray a+    -> MutArray a+getSlice index len (MutArray contents start e _) =+    let fp1 = INDEX_OF(start,index,a)+        end = fp1 + (len * SIZE_OF(a))+     in if index >= 0 && len >= 0 && end <= e+        -- Note: In a slice we always use bound = end so that the slice user+        -- cannot overwrite elements beyond the end of the slice.+        then MutArray contents fp1 end end+        else error+                $ "getSlice: invalid slice, index "+                ++ show index ++ " length " ++ show len++-------------------------------------------------------------------------------+-- In-place mutation algorithms+-------------------------------------------------------------------------------++-- XXX consider the bulk update/accumulation/permutation APIs from vector.++-- | You may not need to reverse an array because you can consume it in reverse+-- using 'readerRev'. To reverse large arrays you can read in reverse and write+-- to another array. However, in-place reverse can be useful to take adavantage+-- of cache locality and when you do not want to allocate additional memory.+--+{-# INLINE reverse #-}+reverse :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m ()+reverse MutArray{..} = liftIO $ do+    let l = arrStart+        h = INDEX_PREV(arrEnd,a)+     in swap l h++    where++    swap l h = do+        when (l < h) $ do+            swapArrayByteIndices (Proxy :: Proxy a) arrContents l h+            swap (INDEX_NEXT(l,a)) (INDEX_PREV(h,a))++-- | Generate the next permutation of the sequence, returns False if this is+-- the last permutation.+--+-- /Unimplemented/+{-# INLINE permute #-}+permute :: MutArray a -> m Bool+permute = undefined++-- | Partition an array into two halves using a partitioning predicate. The+-- first half retains values where the predicate is 'False' and the second half+-- retains values where the predicate is 'True'.+--+-- /Pre-release/+{-# INLINE partitionBy #-}+partitionBy :: forall m a. (MonadIO m, Unbox a)+    => (a -> Bool) -> MutArray a -> m (MutArray a, MutArray a)+partitionBy f arr@MutArray{..} = liftIO $ do+    if arrStart >= arrEnd+    then return (arr, arr)+    else do+        ptr <- go arrStart (INDEX_PREV(arrEnd,a))+        let pl = MutArray arrContents arrStart ptr ptr+            pr = MutArray arrContents ptr arrEnd arrEnd+        return (pl, pr)++    where++    -- Invariant low < high on entry, and on return as well+    moveHigh low high = do+        h <- peekAt high arrContents+        if f h+        then+            -- Correctly classified, continue the loop+            let high1 = INDEX_PREV(high,a)+             in if low == high1+                then return Nothing+                else moveHigh low high1+        else return (Just (high, h)) -- incorrectly classified++    -- Keep a low pointer starting at the start of the array (first partition)+    -- and a high pointer starting at the end of the array (second partition).+    -- Keep incrementing the low ptr and decrementing the high ptr until both+    -- are wrongly classified, at that point swap the two and continue until+    -- the two pointer cross each other.+    --+    -- Invariants when entering this loop:+    -- low <= high+    -- Both low and high are valid locations within the array+    go low high = do+        l <- peekAt low arrContents+        if f l+        then+            -- low is wrongly classified+            if low == high+            then return low+            else do -- low < high+                r <- moveHigh low high+                case r of+                    Nothing -> return low+                    Just (high1, h) -> do -- low < high1+                        pokeAt low arrContents h+                        pokeAt high1 arrContents l+                        let low1 = INDEX_NEXT(low,a)+                            high2 = INDEX_PREV(high1,a)+                        if low1 <= high2+                        then go low1 high2+                        else return low1 -- low1 > high2++        else do+            -- low is correctly classified+            let low1 = INDEX_NEXT(low,a)+            if low == high+            then return low1+            else go low1 high++-- | Shuffle corresponding elements from two arrays using a shuffle function.+-- If the shuffle function returns 'False' then do nothing otherwise swap the+-- elements. This can be used in a bottom up fold to shuffle or reorder the+-- elements.+--+-- /Unimplemented/+{-# INLINE shuffleBy #-}+shuffleBy :: (a -> a -> m Bool) -> MutArray a -> MutArray a -> m ()+shuffleBy = undefined++-- XXX we can also make the folds partial by stopping at a certain level.+--+-- | @divideBy level partition array@  performs a top down hierarchical+-- recursive partitioning fold of items in the container using the given+-- function as the partition function.  Level indicates the level in the tree+-- where the fold would stop.+--+-- This performs a quick sort if the partition function is+-- 'partitionBy (< pivot)'.+--+-- /Unimplemented/+{-# INLINABLE divideBy #-}+divideBy ::+    Int -> (MutArray a -> m (MutArray a, MutArray a)) -> MutArray a -> m ()+divideBy = undefined++-- | @mergeBy level merge array@ performs a pairwise bottom up fold recursively+-- merging the pairs using the supplied merge function. Level indicates the+-- level in the tree where the fold would stop.+--+-- This performs a random shuffle if the merge function is random.  If we+-- stop at level 0 and repeatedly apply the function then we can do a bubble+-- sort.+--+-- /Unimplemented/+mergeBy :: Int -> (MutArray a -> MutArray a -> m ()) -> MutArray a -> m ()+mergeBy = undefined++-------------------------------------------------------------------------------+-- Size+-------------------------------------------------------------------------------++-- | /O(1)/ Get the byte length of the array.+--+{-# INLINE byteLength #-}+byteLength :: MutArray a -> Int+byteLength MutArray{..} =+    let len = arrEnd - arrStart+    in assert (len >= 0) len++-- Note: try to avoid the use of length in performance sensitive internal+-- routines as it involves a costly 'div' operation. Instead use the end ptr+-- in the array to check the bounds etc.+--+-- | /O(1)/ Get the length of the array i.e. the number of elements in the+-- array.+--+-- Note that 'byteLength' is less expensive than this operation, as 'length'+-- involves a costly division operation.+--+{-# INLINE length #-}+length :: forall a. Unbox a => MutArray a -> Int+length arr =+    let elemSize = SIZE_OF(a)+        blen = byteLength arr+     in assert (blen `mod` elemSize == 0) (blen `div` elemSize)++-- | Get the total capacity of an array. An array may have space reserved+-- beyond the current used length of the array.+--+-- /Pre-release/+{-# INLINE byteCapacity #-}+byteCapacity :: MutArray a -> Int+byteCapacity MutArray{..} =+    let len = arrBound - arrStart+    in assert (len >= 0) len++-- | The remaining capacity in the array for appending more elements without+-- reallocation.+--+-- /Pre-release/+{-# INLINE bytesFree #-}+bytesFree :: MutArray a -> Int+bytesFree MutArray{..} =+    let n = arrBound - arrEnd+    in assert (n >= 0) n++-------------------------------------------------------------------------------+-- Streams of arrays - Creation+-------------------------------------------------------------------------------++data GroupState s contents start end bound+    = GroupStart s+    | GroupBuffer s contents start end bound+    | GroupYield+        contents start end bound (GroupState s contents start end bound)+    | GroupFinish++{-# INLINE_NORMAL chunksOfAs #-}+chunksOfAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> D.Stream m a -> D.Stream m (MutArray a)+chunksOfAs ps n (D.Stream step state) =+    D.Stream step' (GroupStart state)++    where++    {-# INLINE_LATE step' #-}+    step' _ (GroupStart st) = do+        when (n <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "+                    ++ "the size of arrays [" ++ show n+                    ++ "] must be a natural number"+        (MutArray contents start end bound :: MutArray a) <- newAs ps n+        return $ D.Skip (GroupBuffer st contents start end bound)++    step' gst (GroupBuffer st contents start end bound) = do+        r <- step (adaptState gst) st+        case r of+            D.Yield x s -> do+                liftIO $ pokeAt end contents  x+                let end1 = INDEX_NEXT(end,a)+                return $+                    if end1 >= bound+                    then D.Skip+                            (GroupYield+                                contents start end1 bound (GroupStart s))+                    else D.Skip (GroupBuffer s contents start end1 bound)+            D.Skip s ->+                return $ D.Skip (GroupBuffer s contents start end bound)+            D.Stop ->+                return+                    $ D.Skip (GroupYield contents start end bound GroupFinish)++    step' _ (GroupYield contents start end bound next) =+        return $ D.Yield (MutArray contents start end bound) next++    step' _ GroupFinish = return D.Stop++-- | @chunksOf n stream@ groups the elements in the input stream into arrays of+-- @n@ elements each.+--+-- Same as the following but may be more efficient:+--+-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)+--+-- /Pre-release/+{-# INLINE_NORMAL chunksOf #-}+chunksOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> D.Stream m (MutArray a)+-- XXX the idiomatic implementation leads to large regression in the D.reverse'+-- benchmark. It seems it has difficulty producing optimized code when+-- converting to StreamK. Investigate GHC optimizations.+-- chunksOf n = D.foldMany (createOf n)+chunksOf = chunksOfAs Unpinned++-- | Like 'chunksOf' but creates pinned arrays.+{-# INLINE_NORMAL pinnedChunksOf #-}+pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> D.Stream m (MutArray a)+-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)+pinnedChunksOf = chunksOfAs Pinned++-- | When we are buffering a stream of unknown size into an array we do not+-- know how much space to pre-allocate. So we start with the min size and emit+-- the array then keep on doubling the size every time. Thus we do not need to+-- guess the optimum chunk size.+--+-- We can incorporate this in chunksOfAs if the additional size parameter does+-- not impact perf.+--+{-# INLINE _chunksOfRange #-}+_chunksOfRange :: -- (MonadIO m, Unbox a) =>+    PinnedState -> Int -> Int -> D.Stream m a -> D.Stream m (MutArray a)+_chunksOfRange _ps _low _hi = undefined++-- XXX buffer to a list instead?+-- | Buffer the stream into arrays in memory.+{-# INLINE arrayStreamKFromStreamDAs #-}+arrayStreamKFromStreamDAs :: forall m a. (MonadIO m, Unbox a) =>+    PinnedState -> D.Stream m a -> m (StreamK m (MutArray a))+arrayStreamKFromStreamDAs ps =+    let n = allocBytesToElemCount (undefined :: a) defaultChunkSize+     in D.foldr K.cons K.nil . chunksOfAs ps n++-------------------------------------------------------------------------------+-- Streams of arrays - Flattening+-------------------------------------------------------------------------------++data FlattenState s contents a =+      OuterLoop s+    | InnerLoop s contents !Int !Int++{-# INLINE_NORMAL concatWith #-}+concatWith :: forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a+concatWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (OuterLoop st) = do+        r <- step (adaptState gst) st+        return $ case r of+            D.Yield MutArray{..} s ->+                D.Skip (InnerLoop s arrContents arrStart arrEnd)+            D.Skip s -> D.Skip (OuterLoop s)+            D.Stop -> D.Stop++    step' _ (InnerLoop st _ p end) | assert (p <= end) (p == end) =+        return $ D.Skip $ OuterLoop st++    step' _ (InnerLoop st contents p end) = do+        x <- liftio $ peekAt p contents+        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)++-- | Use the "reader" unfold instead.+--+-- @concat = unfoldMany reader@+--+-- We can try this if there are any fusion issues in the unfold.+--+{-# INLINE_NORMAL concat #-}+concat :: forall m a. (MonadIO m, Unbox a)+    => D.Stream m (MutArray a) -> D.Stream m a+concat = concatWith liftIO++{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}+{-# INLINE flattenArrays #-}+flattenArrays :: forall m a. (MonadIO m, Unbox a)+    => D.Stream m (MutArray a) -> D.Stream m a+flattenArrays = concat++{-# INLINE_NORMAL concatRevWith #-}+concatRevWith :: forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a+concatRevWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (OuterLoop st) = do+        r <- step (adaptState gst) st+        return $ case r of+            D.Yield MutArray{..} s ->+                let p = INDEX_PREV(arrEnd,a)+                 in D.Skip (InnerLoop s arrContents p arrStart)+            D.Skip s -> D.Skip (OuterLoop s)+            D.Stop -> D.Stop++    step' _ (InnerLoop st _ p start) | p < start =+        return $ D.Skip $ OuterLoop st++    step' _ (InnerLoop st contents p start) = do+        x <- liftio $ peekAt p contents+        let cur = INDEX_PREV(p,a)+        return $ D.Yield x (InnerLoop st contents cur start)++-- | Use the "readerRev" unfold instead.+--+-- @concat = unfoldMany readerRev@+--+-- We can try this if there are any fusion issues in the unfold.+--+{-# INLINE_NORMAL concatRev #-}+concatRev :: forall m a. (MonadIO m, Unbox a)+    => D.Stream m (MutArray a) -> D.Stream m a+concatRev = concatRevWith liftIO++{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}+{-# INLINE flattenArraysRev #-}+flattenArraysRev :: forall m a. (MonadIO m, Unbox a)+    => D.Stream m (MutArray a) -> D.Stream m a+flattenArraysRev = concatRev++-------------------------------------------------------------------------------+-- Unfolds+-------------------------------------------------------------------------------++data ArrayUnsafe a = ArrayUnsafe+    {-# UNPACK #-} !MutByteArray   -- contents+    {-# UNPACK #-} !Int                -- index 1+    {-# UNPACK #-} !Int                -- index 2++toArrayUnsafe :: MutArray a -> ArrayUnsafe a+toArrayUnsafe (MutArray contents start end _) = ArrayUnsafe contents start end++fromArrayUnsafe ::+#ifdef DEVBUILD+    Unbox a =>+#endif+    ArrayUnsafe a -> MutArray a+fromArrayUnsafe (ArrayUnsafe contents start end) =+         MutArray contents start end end++{-# INLINE_NORMAL producerWith #-}+producerWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> Producer m (MutArray a) a+producerWith liftio = Producer step (return . toArrayUnsafe) extract+    where++    {-# INLINE_LATE step #-}+    step (ArrayUnsafe _ cur end)+        | assert (cur <= end) (cur == end) = return D.Stop+    step (ArrayUnsafe contents cur end) = do+            -- When we use a purely lazy Monad like Identity, we need to force a+            -- few actions for correctness and execution order sanity. We want+            -- the peek to occur right here and not lazily at some later point+            -- because we want the peek to be ordered with respect to the touch.+            !x <- liftio $ peekAt cur contents+            return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)++    extract = return . fromArrayUnsafe++-- | Resumable unfold of an array.+--+{-# INLINE_NORMAL producer #-}+producer :: forall m a. (MonadIO m, Unbox a) => Producer m (MutArray a) a+producer = producerWith liftIO++-- | Unfold an array into a stream.+--+{-# INLINE_NORMAL reader #-}+reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a+reader = Producer.simplify producer++{-# INLINE_NORMAL readerRevWith #-}+readerRevWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> Unfold m (MutArray a) a+readerRevWith liftio = Unfold step inject+    where++    inject (MutArray contents start end _) =+        let p = INDEX_PREV(end,a)+         in return $ ArrayUnsafe contents start p++    {-# INLINE_LATE step #-}+    step (ArrayUnsafe _ start p) | p < start = return D.Stop+    step (ArrayUnsafe contents start p) = do+        !x <- liftio $ peekAt p contents+        return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))++-- | Unfold an array into a stream in reverse order.+--+{-# INLINE_NORMAL readerRev #-}+readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a+readerRev = readerRevWith liftIO++-------------------------------------------------------------------------------+-- to Lists and streams+-------------------------------------------------------------------------------++{-+-- Use foldr/build fusion to fuse with list consumers+-- This can be useful when using the IsList instance+{-# INLINE_LATE toListFB #-}+toListFB :: forall a b. Unbox a => (a -> b -> b) -> b -> MutArray a -> b+toListFB c n MutArray{..} = go arrStart+    where++    go p | assert (p <= arrEnd) (p == arrEnd) = n+    go p =+        -- unsafeInlineIO allows us to run this in Identity monad for pure+        -- toList/foldr case which makes them much faster due to not+        -- accumulating the list and fusing better with the pure consumers.+        --+        -- This should be safe as the array contents are guaranteed to be+        -- evaluated/written to before we peek at them.+        -- XXX+        let !x = unsafeInlineIO $ do+                    r <- peekAt arrContents p+                    return r+        in c x (go (PTR_NEXT(p,a)))+-}++-- XXX Monadic foldr/build fusion?+-- Reference: https://www.researchgate.net/publication/220676509_Monadic_augment_and_generalised_short_cut_fusion++-- | Convert a 'MutArray' into a list.+--+{-# INLINE toList #-}+toList :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m [a]+toList MutArray{..} = liftIO $ go arrStart+    where++    go p | assert (p <= arrEnd) (p == arrEnd) = return []+    go p = do+        x <- peekAt p arrContents+        (:) x <$> go (INDEX_NEXT(p,a))++{-# INLINE_NORMAL toStreamWith #-}+toStreamWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a+toStreamWith liftio MutArray{..} = D.Stream step arrStart++    where++    {-# INLINE_LATE step #-}+    step _ p | assert (p <= arrEnd) (p == arrEnd) = return D.Stop+    step _ p = liftio $ do+        r <- peekAt p arrContents+        return $ D.Yield r (INDEX_NEXT(p,a))++-- | Convert a 'MutArray' into a stream.+--+-- >>> read = Stream.unfold MutArray.reader+--+{-# INLINE_NORMAL read #-}+read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a+read = toStreamWith liftIO++{-# INLINE toStreamKWith #-}+toStreamKWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a+toStreamKWith liftio MutArray{..} = go arrStart++    where++    go p | assert (p <= arrEnd) (p == arrEnd) = K.nil+         | otherwise =+        let elemM = peekAt p arrContents+        in liftio elemM `K.consM` go (INDEX_NEXT(p,a))++{-# INLINE toStreamK #-}+toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a+toStreamK = toStreamKWith liftIO++{-# INLINE_NORMAL toStreamRevWith #-}+toStreamRevWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a+toStreamRevWith liftio MutArray{..} =+    let p = INDEX_PREV(arrEnd,a)+    in D.Stream step p++    where++    {-# INLINE_LATE step #-}+    step _ p | p < arrStart = return D.Stop+    step _ p = liftio $ do+        r <- peekAt p arrContents+        return $ D.Yield r (INDEX_PREV(p,a))++-- | Convert a 'MutArray' into a stream in reverse order.+--+-- >>> readRev = Stream.unfold MutArray.readerRev+--+{-# INLINE_NORMAL readRev #-}+readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a+readRev = toStreamRevWith liftIO++{-# INLINE toStreamKRevWith #-}+toStreamKRevWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a+toStreamKRevWith liftio MutArray {..} =+    let p = INDEX_PREV(arrEnd,a)+    in go p++    where++    go p | p < arrStart = K.nil+         | otherwise =+        let elemM = peekAt p arrContents+        in liftio elemM `K.consM` go (INDEX_PREV(p,a))++{-# INLINE toStreamKRev #-}+toStreamKRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a+toStreamKRev = toStreamKRevWith liftIO++-------------------------------------------------------------------------------+-- Folding+-------------------------------------------------------------------------------++-- XXX Need something like "MutArray m a" enforcing monadic action to avoid the+-- possibility of such APIs.+--+-- | Strict left fold of an array.+{-# INLINE_NORMAL foldl' #-}+foldl' :: (MonadIO m, Unbox a) => (b -> a -> b) -> b -> MutArray a -> m b+foldl' f z arr = D.foldl' f z $ read arr++-- | Right fold of an array.+{-# INLINE_NORMAL foldr #-}+foldr :: (MonadIO m, Unbox a) => (a -> b -> b) -> b -> MutArray a -> m b+foldr f z arr = D.foldr f z $ read arr++-------------------------------------------------------------------------------+-- Folds+-------------------------------------------------------------------------------++-- Note: Arrays may be allocated with a specific alignment at the beginning of+-- the array. If you need to maintain that alignment on reallocations then you+-- can resize the array manually before append, using an aligned resize+-- operation.++-- XXX Keep the bound intact to not lose any free space? Perf impact?++-- | @unsafeAppendN n arr@ appends up to @n@ input items to the supplied+-- array.+--+-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory+-- corruption or segfault.+--+-- Any free space left in the array after appending @n@ elements is lost.+--+-- /Internal/+{-# INLINE_NORMAL unsafeAppendN #-}+unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>+       Int+    -> m (MutArray a)+    -> Fold m a (MutArray a)+unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial++    where++    initial = do+        assert (n >= 0) (return ())+        arr@(MutArray _ _ end bound) <- action+        let free = bound - end+            needed = n * SIZE_OF(a)+        -- XXX We can also reallocate if the array has too much free space,+        -- otherwise we lose that space.+        arr1 <-+            if free < needed+            then noinline reallocWith "unsafeAppendN" (+ needed) needed arr+            else return arr+        return $ toArrayUnsafe arr1++    step (ArrayUnsafe contents start end) x = do+        liftIO $ pokeAt end contents x+        return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))++{-# DEPRECATED writeAppendNUnsafe "Please use unsafeAppendN instead." #-}+{-# INLINE writeAppendNUnsafe #-}+writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>+       Int+    -> m (MutArray a)+    -> Fold m a (MutArray a)+writeAppendNUnsafe = unsafeAppendN++-- | Append @n@ elements to an existing array. Any free space left in the array+-- after appending @n@ elements is lost.+--+-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)+--+{-# INLINE_NORMAL appendN #-}+appendN :: forall m a. (MonadIO m, Unbox a) =>+    Int -> m (MutArray a) -> Fold m a (MutArray a)+appendN n initial = FL.take n (unsafeAppendN n initial)++-- XXX Deprecate in major+-- {-# DEPRECATED writeAppendN "Please use appendN instead." #-}+{-# INLINE writeAppendN #-}+writeAppendN :: forall m a. (MonadIO m, Unbox a) =>+    Int -> m (MutArray a) -> Fold m a (MutArray a)+writeAppendN = appendN++-- | @appendWith realloc action@ mutates the array generated by @action@ to+-- append the input stream. If there is no reserved space available in the+-- array it is reallocated to a size in bytes  determined by @realloc oldSize@,+-- where @oldSize@ is the current size of the array in bytes.+--+-- Note that the returned array may be a mutated version of original array.+--+-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)+--+-- /Pre-release/+{-# INLINE appendWith #-}+appendWith :: forall m a. (MonadIO m, Unbox a) =>+    (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)+appendWith sizer = FL.foldlM' (snocWith sizer)++{-# DEPRECATED writeAppendWith "Please use appendWith instead." #-}+{-# INLINE writeAppendWith #-}+writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>+    (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)+writeAppendWith = appendWith++-- | @append action@ mutates the array generated by @action@ to append the+-- input stream. If there is no reserved space available in the array it is+-- reallocated to double the size.+--+-- Note that the returned array may be a mutated version of original array.+--+-- >>> append = MutArray.appendWith (* 2)+--+{-# INLINE append #-}+append :: forall m a. (MonadIO m, Unbox a) =>+    m (MutArray a) -> Fold m a (MutArray a)+append = appendWith (* 2)++-- XXX Deprecate in major+-- {-# DEPRECATED writeAppend "Please use append instead." #-}+{-# INLINE writeAppend #-}+writeAppend :: forall m a. (MonadIO m, Unbox a) =>+    m (MutArray a) -> Fold m a (MutArray a)+writeAppend = append++-- XXX We can carry bound as well in the state to make sure we do not lose the+-- remaining capacity. Need to check perf impact.+--+-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@+-- function as argument.+--+-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n+--+-- /Pre-release/+{-# INLINE_NORMAL unsafeCreateOfWith #-}+unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial++    where++    initial = toArrayUnsafe <$> alloc (max n 0)++    step (ArrayUnsafe contents start end) x = do+        liftIO $ pokeAt end contents x+        return+          $ ArrayUnsafe contents start (INDEX_NEXT(end,a))++{-# DEPRECATED writeNWithUnsafe "Please use unsafeCreateOfWith instead." #-}+{-# INLINE writeNWithUnsafe #-}+writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeNWithUnsafe = unsafeCreateOfWith++{-# INLINE_NORMAL writeNUnsafeAs #-}+writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> Fold m a (MutArray a)+writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)++-- | Like 'createOf' but does not check the array bounds when writing. The fold+-- driver must not call the step function more than 'n' times otherwise it will+-- corrupt the memory and crash. This function exists mainly because any+-- conditional in the step function blocks fusion causing 10x performance+-- slowdown.+--+-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf+--+{-# INLINE_NORMAL unsafeCreateOf #-}+unsafeCreateOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+unsafeCreateOf = writeNUnsafeAs Unpinned++{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}+{-# INLINE writeNUnsafe #-}+writeNUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+writeNUnsafe = unsafeCreateOf++-- | Like 'unsafeCreateOf' but creates a pinned array.+{-# INLINE_NORMAL unsafePinnedCreateOf #-}+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+unsafePinnedCreateOf = writeNUnsafeAs Pinned++{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}+{-# INLINE pinnedWriteNUnsafe #-}+pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+pinnedWriteNUnsafe = unsafePinnedCreateOf++-- | @createOfWith alloc n@ folds a maximum of @n@ elements into an array+-- allocated using the @alloc@ function.+--+-- >>> createOfWith alloc n = Fold.take n (MutArray.unsafeCreateOfWith alloc n)+-- >>> createOfWith alloc n = MutArray.appendN (alloc n) n+--+{-# INLINE_NORMAL createOfWith #-}+createOfWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+createOfWith alloc n = FL.take n (unsafeCreateOfWith alloc n)++-- {-# DEPRECATED writeNWith "Please use createOfWith instead." #-}+{-# INLINE writeNWith #-}+writeNWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeNWith = createOfWith++{-# INLINE_NORMAL writeNAs #-}+writeNAs ::+       forall m a. (MonadIO m, Unbox a)+    => PinnedState+    -> Int+    -> Fold m a (MutArray a)+writeNAs ps = createOfWith (newAs ps)++-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an+-- 'MutArray'.+--+-- >>> createOf = MutArray.createOfWith MutArray.new+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)+-- >>> createOf n = MutArray.appendN n (MutArray.emptyOf n)+--+{-# INLINE_NORMAL createOf #-}+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+createOf = writeNAs Unpinned++-- XXX Deprecate in major+-- {-# DEPRECATED writeN "Please use createOf instead." #-}+{-# INLINE writeN #-}+writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+writeN = createOf++-- | Like 'createOf' but creates a pinned array.+{-# INLINE_NORMAL pinnedCreateOf #-}+pinnedCreateOf ::+       forall m a. (MonadIO m, Unbox a)+    => Int+    -> Fold m a (MutArray a)+pinnedCreateOf = writeNAs Pinned++{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}+{-# INLINE pinnedWriteN #-}+pinnedWriteN ::+       forall m a. (MonadIO m, Unbox a)+    => Int+    -> Fold m a (MutArray a)+pinnedWriteN = pinnedCreateOf++-- | Like unsafeCreateOfWith but writes the array in reverse order.+--+-- /Internal/+{-# INLINE_NORMAL writeRevNWithUnsafe #-}+writeRevNWithUnsafe :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial++    where++    toArrayUnsafeRev (MutArray contents _ _ bound) =+         ArrayUnsafe contents bound bound++    initial = toArrayUnsafeRev <$> alloc (max n 0)++    step (ArrayUnsafe contents start end) x = do+        let ptr = INDEX_PREV(start,a)+        liftIO $ pokeAt ptr contents x+        return+          $ ArrayUnsafe contents ptr end++-- | Like createOfWith but writes the array in reverse order.+--+-- /Internal/+{-# INLINE_NORMAL writeRevNWith #-}+writeRevNWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)++-- | Like 'createOf' but writes the array in reverse order.+--+-- /Pre-release/+{-# INLINE_NORMAL revCreateOf #-}+revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+revCreateOf = writeRevNWith new++{-# DEPRECATED writeRevN "Please use revCreateOf instead." #-}+{-# INLINE writeRevN #-}+writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+writeRevN = revCreateOf++-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the+-- input stream to a 'MutArray' aligned to the given size.+--+-- >>> pinnedWriteNAligned align = MutArray.createOfWith (MutArray.pinnedNewAligned align)+-- >>> pinnedWriteNAligned align n = MutArray.appendN n (MutArray.pinnedNewAligned align n)+--+-- /Pre-release/+--+{-# INLINE_NORMAL pinnedWriteNAligned #-}+pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)+    => Int -> Int -> Fold m a (MutArray a)+pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)++-- XXX Buffer to a list instead?++-- | Buffer a stream into a stream of arrays.+--+-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK+--+-- Breaking an array into an array stream  can be useful to consume a large+-- array sequentially such that memory of the array is released incrementatlly.+--+-- See also: 'arrayStreamKFromStreamD'.+--+-- /Unimplemented/+--+{-# INLINE_NORMAL buildChunks #-}+buildChunks :: (MonadIO m, Unbox a) =>+    Int -> Fold m a (StreamK n (MutArray a))+buildChunks n = FL.many (createOf n) FL.toStreamK++{-# DEPRECATED writeChunks "Please use buildChunks instead." #-}+{-# INLINE writeChunks #-}+writeChunks :: (MonadIO m, Unbox a) =>+    Int -> Fold m a (StreamK n (MutArray a))+writeChunks = buildChunks++{-# INLINE_NORMAL writeWithAs #-}+writeWithAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> Fold m a (MutArray a)+-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)+writeWithAs ps elemCount =+    FL.rmapM extract $ FL.foldlM' step initial++    where++    initial = do+        when (elemCount < 0) $ error "createWith: elemCount is negative"+        newAs ps elemCount++    step arr@(MutArray _ start end bound) x+        | INDEX_NEXT(end,a) > bound = do+        let oldSize = end - start+            newSize = max (oldSize * 2) 1+        arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr+        snocUnsafe arr1 x+    step arr x = snocUnsafe arr x++    extract = liftIO . rightSize++-- XXX Compare createWith with fromStreamD which uses an array of streams+-- implementation. We can write this using buildChunks above if that is faster.+-- If createWith is faster then we should use that to implement+-- fromStreamD.+--+-- XXX The realloc based implementation needs to make one extra copy if we use+-- shrinkToFit.  On the other hand, the stream of arrays implementation may+-- buffer the array chunk pointers in memory but it does not have to shrink as+-- we know the exact size in the end. However, memory copying does not seem to+-- be as expensive as the allocations. Therefore, we need to reduce the number+-- of allocations instead. Also, the size of allocations matters, right sizing+-- an allocation even at the cost of copying sems to help.  Should be measured+-- on a big stream with heavy calls to toArray to see the effect.+--+-- XXX check if GHC's memory allocator is efficient enough. We can try the C+-- malloc to compare against.++-- | @createWith minCount@ folds the whole input to a single array. The array+-- starts at a size big enough to hold minCount elements, the size is doubled+-- every time the array needs to be grown.+--+-- /Caution! Do not use this on infinite streams./+--+-- >>> f n = MutArray.appendWith (* 2) (MutArray.emptyOf n)+-- >>> createWith n = Fold.rmapM MutArray.rightSize (f n)+-- >>> createWith n = Fold.rmapM MutArray.fromChunksK (MutArray.buildChunks n)+--+-- /Pre-release/+{-# INLINE_NORMAL createWith #-}+createWith :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+-- createWith n = FL.rmapM rightSize $ appendWith (* 2) (emptyOf n)+createWith = writeWithAs Unpinned++{-# DEPRECATED writeWith "Please use createWith instead." #-}+{-# INLINE writeWith #-}+writeWith :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+writeWith = createWith++-- | Fold the whole input to a single array.+--+-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes+-- rounded up to the element size.+--+-- /Caution! Do not use this on infinite streams./+--+{-# INLINE create #-}+create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+create = createWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)++-- XXX Deprecate in major+-- {-# DEPRECATED write "Please use create instead." #-}+{-# INLINE write #-}+write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+write = create++-- | Like 'create' but creates a pinned array.+{-# INLINE pinnedCreate #-}+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+pinnedCreate =+    writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)++{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}+{-# INLINE pinnedWrite #-}+pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+pinnedWrite = pinnedCreate++-------------------------------------------------------------------------------+-- construct from streams, known size+-------------------------------------------------------------------------------++{-# INLINE_NORMAL fromStreamDNAs #-}+fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> D.Stream m a -> m (MutArray a)+fromStreamDNAs ps limit str = do+    (arr :: MutArray a) <- newAs ps limit+    end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str+    return $ arr {arrEnd = end}++    where++    fwrite arrContents ptr x = do+        liftIO $ pokeAt ptr arrContents  x+        return $ INDEX_NEXT(ptr,a)++-- | Use the 'createOf' fold instead.+--+-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)+--+{-# INLINE_NORMAL fromStreamN #-}+fromStreamN :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> m (MutArray a)+-- fromStreamDN n = D.fold (createOf n)+fromStreamN = fromStreamDNAs Unpinned++{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}+{-# INLINE fromStreamDN #-}+fromStreamDN :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> m (MutArray a)+fromStreamDN = fromStreamN++-- | Create a 'MutArray' from the first N elements of a list. The array is+-- allocated to size N, if the list terminates before N elements then the+-- array may hold less than N elements.+--+{-# INLINABLE fromListN #-}+fromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+fromListN n xs = fromStreamDN n $ D.fromList xs++-- | Like 'fromListN' but creates a pinned array.+{-# INLINABLE pinnedFromListN #-}+pinnedFromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs++-- | Like fromListN but writes the array in reverse order.+--+-- /Pre-release/+{-# INLINE fromListRevN #-}+fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs++-- | Convert a pure stream in Identity monad to a mutable array.+{-# INLINABLE fromPureStreamN #-}+fromPureStreamN :: (MonadIO m, Unbox a) =>+    Int -> Stream Identity a -> m (MutArray a)+fromPureStreamN n xs =+    D.fold (createOf n) $ D.morphInner (return . runIdentity) xs++-- | Convert a pure stream in Identity monad to a mutable array.+{-# INLINABLE fromPureStream #-}+fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)+fromPureStream xs =+    D.fold create $ D.morphInner (return . runIdentity) xs++{-# INLINABLE fromPtrN #-}+fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)+fromPtrN len addr = do+    -- memcpy is better than stream copy when the size is known.+    -- XXX We can implement a stream copy in a similar way by streaming Word64+    -- first and then remaining Word8.+    arr <- new len+    _ <- unsafeAsPtr arr+            (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))+    return (arr {arrEnd = len})++{-# INLINABLE fromByteStr# #-}+fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)+fromByteStr# addr = do+    -- It is better to count the size first and allocate exact space.+    -- Also, memcpy is better than stream copy when the size is known.+    -- C strlen compares 4 bytes at a time, so is better than the stream+    -- version. https://github.com/bminor/glibc/blob/master/string/strlen.c+    -- XXX We can possibly use a stream of Word64 to do the same.+    -- fromByteStr# addr = fromPureStream (D.fromByteStr# addr)+    len <- liftIO $ c_strlen (Ptr addr)+    let lenInt = fromIntegral len+    arr <- new lenInt+    _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)+    return (arr {arrEnd = lenInt})++-------------------------------------------------------------------------------+-- convert a stream of arrays to a single array by reallocating and copying+-------------------------------------------------------------------------------++-- XXX Both of these implementations of splicing seem to perform equally well.+-- We need to perform benchmarks over a range of sizes though.++-- | Also see 'fromChunksK'.+{-# INLINE fromChunksRealloced #-}+fromChunksRealloced :: forall m a. (MonadIO m, Unbox a)+    => Stream m (MutArray a) -> m (MutArray a)+fromChunksRealloced s = do+    res <- D.uncons s+    case res of+        Just (a, strm) -> do+            arr <- D.foldlM' spliceExp (pure a) strm+            -- Reallocation is exponential so there may be 50% empty space in+            -- worst case. One more reallocation to reclaim the space.+            rightSize arr+        Nothing -> pure nil++-------------------------------------------------------------------------------+-- convert a stream of arrays to a single array by buffering arrays first+-------------------------------------------------------------------------------++{-# INLINE arrayStreamKLength #-}+arrayStreamKLength :: (Monad m, Unbox a) => StreamK m (MutArray a) -> m Int+arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)++-- | Convert an array stream to an array. Note that this requires peak memory+-- that is double the size of the array stream.+--+{-# INLINE fromChunkskAs #-}+fromChunkskAs :: (Unbox a, MonadIO m) =>+    PinnedState -> StreamK m (MutArray a) -> m (MutArray a)+fromChunkskAs ps as = do+    len <- arrayStreamKLength as+    arr <- newAs ps len+    -- XXX is StreamK fold faster or StreamD fold?+    K.foldlM' spliceUnsafe (pure arr) as+    -- fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as++-- XXX Need to compare this with fromChunks and fromChunkList and keep the+-- fastest or simplest one if all are equally fast.++-- | Convert an array stream to an array. Note that this requires peak memory+-- that is double the size of the array stream.+--+-- Also see 'fromChunksRealloced'.+--+{-# INLINE fromChunksK #-}+fromChunksK :: (Unbox a, MonadIO m) =>+    StreamK m (MutArray a) -> m (MutArray a)+fromChunksK = fromChunkskAs Unpinned++{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}+{-# INLINE fromArrayStreamK #-}+fromArrayStreamK :: (Unbox a, MonadIO m) =>+    StreamK m (MutArray a) -> m (MutArray a)+fromArrayStreamK = fromChunksK++{-# INLINE fromStreamDAs #-}+fromStreamDAs ::+       (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)+fromStreamDAs ps m =+    arrayStreamKFromStreamDAs Unpinned m >>= fromChunkskAs ps++-- | Create an 'Array' from a stream. This is useful when we want to create a+-- single array from a stream of unknown size. 'createOf' is at least twice+-- as efficient when the size is already known.+--+-- Note that if the input stream is too large memory allocation for the array+-- may fail.  When the stream size is not known, `chunksOf` followed by+-- processing of indvidual arrays in the resulting stream should be preferred.+--+-- /Pre-release/+{-# INLINE fromStream #-}+fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)+fromStream = fromStreamDAs Unpinned++-- fromStream (Stream m) = P.fold create m+-- CAUTION: a very large number (millions) of arrays can degrade performance+-- due to GC overhead because we need to buffer the arrays before we flatten+-- all the arrays.+--+-- XXX Compare if this is faster or "fold create".+--+-- | We could take the approach of doubling the memory allocation on each+-- overflow. This would result in more or less the same amount of copying as in+-- the chunking approach. However, if we have to shrink in the end then it may+-- result in an extra copy of the entire data.+--+-- >>> fromStreamD = StreamD.fold MutArray.create+--+{-# INLINE fromStreamD #-}+{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}+fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)+fromStreamD = fromStream++-- | Create a 'MutArray' from a list. The list must be of finite size.+--+{-# INLINE fromList #-}+fromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+fromList xs = fromStreamD $ D.fromList xs++-- | Like 'fromList' but creates a pinned array.+{-# INLINE pinnedFromList #-}+pinnedFromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+pinnedFromList xs = fromStreamDAs Pinned $ D.fromList xs++-- XXX We are materializing the whole list first for getting the length. Check+-- if the 'fromList' like chunked implementation would fare better.++-- | Like 'fromList' but writes the contents of the list in reverse order.+{-# INLINE fromListRev #-}+fromListRev :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+fromListRev xs = fromListRevN (Prelude.length xs) xs++-------------------------------------------------------------------------------+-- Cloning+-------------------------------------------------------------------------------++-- Arrays are aligned on 64-bit boundaries. The fastest way to copy an array is+-- to unsafeCast it to Word64, read it, write it to Word64 array and unsafeCast+-- it again. We can use SIMD read/write as well.++{-# INLINE cloneAs #-}+cloneAs ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => PinnedState -> MutArray a -> m (MutArray a)+cloneAs ps src =+    do+        let startSrc = arrStart src+            srcLen = arrEnd src - startSrc+        newArrContents <-+            Unboxed.cloneSliceUnsafeAs ps startSrc srcLen (arrContents src)+        return $ MutArray newArrContents 0 srcLen srcLen++{-# INLINE clone #-}+clone ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => MutArray a -> m (MutArray a)+clone = cloneAs Unpinned++{-# INLINE pinnedClone #-}+pinnedClone ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => MutArray a -> m (MutArray a)+pinnedClone = cloneAs Pinned++-------------------------------------------------------------------------------+-- Combining+-------------------------------------------------------------------------------++-- | Copy two arrays into a newly allocated array. If the first array is pinned+-- the spliced array is also pinned.+{-# INLINE spliceCopy #-}+spliceCopy :: forall m a. MonadIO m =>+#ifdef DEVBUILD+    Unbox a =>+#endif+    MutArray a -> MutArray a -> m (MutArray a)+spliceCopy arr1 arr2 = do+    let start1 = arrStart arr1+        start2 = arrStart arr2+        len1 = arrEnd arr1 - start1+        len2 = arrEnd arr2 - start2+    let len = len1 + len2+    newArrContents <-+        if Unboxed.isPinned (arrContents arr1)+        then liftIO $ Unboxed.pinnedNew len+        else liftIO $ Unboxed.new len+    putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1+    putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2+    return $ MutArray newArrContents 0 len len++-- | Really really unsafe, appends the second array into the first array. If+-- the first array does not have enough space it may cause silent data+-- corruption or if you are lucky a segfault.+{-# INLINE spliceUnsafe #-}+spliceUnsafe :: MonadIO m =>+    MutArray a -> MutArray a -> m (MutArray a)+spliceUnsafe dst src =+    do+         let startSrc = arrStart src+             srcLen = arrEnd src - startSrc+             endDst = arrEnd dst+         assertM(endDst + srcLen <= arrBound dst)+         putSliceUnsafe+             (arrContents src) startSrc (arrContents dst) endDst srcLen+         return $ dst {arrEnd = endDst + srcLen}++-- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no+-- reserved space available in @dst@ it is reallocated to a size determined by+-- the @sizer dstBytes srcBytes@ function, where @dstBytes@ is the size of the+-- first array and @srcBytes@ is the size of the second array, in bytes.+--+-- Note that the returned array may be a mutated version of first array.+--+-- /Pre-release/+{-# INLINE spliceWith #-}+spliceWith :: forall m a. (MonadIO m, Unbox a) =>+    (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)+spliceWith sizer dst@(MutArray _ start end bound) src = do+{-+    let f = appendWith (`sizer` byteLength src) (return dst)+     in D.fold f (toStreamD src)+-}+    assert (end <= bound) (return ())+    let srcBytes = arrEnd src - arrStart src++    dst1 <-+        if end + srcBytes >= bound+        then do+            let dstBytes = end - start+                newSizeInBytes = sizer dstBytes srcBytes+            when (newSizeInBytes < dstBytes + srcBytes)+                $ error+                    $ "splice: newSize is less than the total size "+                    ++ "of arrays being appended. Please check the "+                    ++ "sizer function passed."+            realloc newSizeInBytes dst+        else return dst+    spliceUnsafe dst1 src++-- | The first array is mutated to append the second array. If there is no+-- reserved space available in the first array a new allocation of exact+-- required size is done.+--+-- Note that the returned array may be a mutated version of first array.+--+-- >>> splice = MutArray.spliceWith (+)+--+-- If the original array is pinned the spliced array is also pinned.+--+-- /Pre-release/+{-# INLINE splice #-}+splice :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)+splice = spliceWith (+)++-- | Like 'append' but the growth of the array is exponential. Whenever a new+-- allocation is required the previous array size is at least doubled.+--+-- This is useful to reduce allocations when folding many arrays together.+--+-- Note that the returned array may be a mutated version of first array.+--+-- >>> spliceExp = MutArray.spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))+--+-- /Pre-release/+{-# INLINE spliceExp #-}+spliceExp :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)+spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))++-------------------------------------------------------------------------------+-- Splitting+-------------------------------------------------------------------------------++-- | Generate a stream of array slices using a predicate. The array element+-- matching the predicate is dropped.+--+-- /Pre-release/+{-# INLINE splitOn #-}+splitOn :: (MonadIO m, Unbox a) =>+    (a -> Bool) -> MutArray a -> Stream m (MutArray a)+splitOn predicate arr =+    fmap (\(i, len) -> getSliceUnsafe i len arr)+        $ D.indexOnSuffix predicate (read arr)++-- | Drops the separator byte+{-# INLINE breakOn #-}+breakOn :: MonadIO m+    => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do+    -- XXX We do not need memchr here, we can use a Haskell equivalent.+    -- Need efficient stream based primitives that work on Word64.+    loc <- c_memchr p sep (fromIntegral $ byteLength arr)+    let sepIndex = loc `minusPtr` p+    return $+        if loc == nullPtr+        then (arr, Nothing)+        else+            ( MutArray+                { arrContents = arrContents+                , arrStart = arrStart+                , arrEnd = arrStart + sepIndex -- exclude the separator+                , arrBound = arrStart + sepIndex+                }+            , Just $ MutArray+                    { arrContents = arrContents+                    , arrStart = arrStart + (sepIndex + 1)+                    , arrEnd = arrEnd+                    , arrBound = arrBound+                    }+            )++-- | Like 'splitAt' but does not check whether the index is valid.+--+{-# INLINE unsafeSplitAt #-}+unsafeSplitAt :: forall a. Unbox a =>+    Int -> MutArray a -> (MutArray a, MutArray a)+unsafeSplitAt i MutArray{..} =+    let off = i * SIZE_OF(a)+        p = arrStart + off+     in ( MutArray+         { arrContents = arrContents+         , arrStart = arrStart+         , arrEnd = p+         , arrBound = p+         }+        , MutArray+          { arrContents = arrContents+          , arrStart = p+          , arrEnd = arrEnd+          , arrBound = arrBound+          }+        )++-- | Create two slices of an array without copying the original array. The+-- specified index @i@ is the first index of the second slice.+--+{-# INLINE splitAt #-}+splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)+splitAt i arr =+    let maxIndex = length arr - 1+    in  if i < 0+        then error "sliceAt: negative array index"+        else if i > maxIndex+             then error $ "sliceAt: specified array index " ++ show i+                        ++ " is beyond the maximum index " ++ show maxIndex+             else unsafeSplitAt i arr++-------------------------------------------------------------------------------+-- Casting+-------------------------------------------------------------------------------++-- | Cast an array having elements of type @a@ into an array having elements of+-- type @b@. The array size must be a multiple of the size of type @b@+-- otherwise accessing the last element of the array may result into a crash or+-- a random value.+--+-- /Pre-release/+--+castUnsafe ::+#ifdef DEVBUILD+    Unbox b =>+#endif+    MutArray a -> MutArray b+castUnsafe (MutArray contents start end bound) =+    MutArray contents start end bound++-- | Cast an @MutArray a@ into an @MutArray Word8@.+--+asBytes :: MutArray a -> MutArray Word8+asBytes = castUnsafe++-- | Cast an array having elements of type @a@ into an array having elements of+-- type @b@. The length of the array should be a multiple of the size of the+-- target element otherwise 'Nothing' is returned.+--+cast :: forall a b. Unbox b => MutArray a -> Maybe (MutArray b)+cast arr =+    let len = byteLength arr+        r = len `mod` SIZE_OF(b)+     in if r /= 0+        then Nothing+        else Just $ castUnsafe arr++-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted+-- pointer to the FFI call. For unsafe calls we do not need to pin the array.+-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in+-- before the pointer is wrapped.+--+-- From the GHC manual:+--+-- GHC, since version 8.4, guarantees that garbage collection will never occur+-- during an unsafe call, even in the bytecode interpreter, and further+-- guarantees that unsafe calls will be performed in the calling thread. Making+-- it safe to pass heap-allocated objects to unsafe functions.++-- XXX Should we just name it asPtr, the unsafety is implicit for any pointer+-- operations. And we are safe from Haskell perspective because we will be+-- pinning the memory.+--+-- XXX we cannot pass the length of the ptr here as in some cases it may not be+-- available e.g. a null terminated C string. However, we can create another+-- flavor of the API e.g. asPtrN.++-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an+-- array as a pointer to some operating system call or to a "safe" FFI call.+--+-- If the array is not pinned it is copied to pinned memory before passing it+-- to the monadic action.+--+-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised+-- that the programmer keeps this in mind and creates a pinned array+-- opportunistically before this operation occurs, to avoid the cost of a copy+-- if possible.+--+-- /Unsafe/ because of direct pointer operations. The user must ensure that+-- they are writing within the legal bounds of the array.+--+-- /Pre-release/+--+{-# INLINE unsafePinnedAsPtr #-}+unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+unsafePinnedAsPtr arr f =+    Unboxed.unsafePinnedAsPtr+        (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))++{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}+{-# INLINE asPtrUnsafe #-}+asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+asPtrUnsafe = unsafePinnedAsPtr++{-# INLINE unsafeAsPtr #-}+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+unsafeAsPtr arr f =+    Unboxed.unsafeAsPtr+        (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))++-------------------------------------------------------------------------------+-- Equality+-------------------------------------------------------------------------------++-- | Byte compare two arrays. Compare the length of the arrays. If the length+-- is equal, compare the lexicographical ordering of two underlying byte arrays+-- otherwise return the result of length comparison.+--+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of+-- different sizes may leave some memory uninitialized which can make byte+-- comparison unreliable.+--+-- /Pre-release/+{-# INLINE byteCmp #-}+byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering+byteCmp arr1 arr2 = do+    let marr1 = getMutableByteArray# (arrContents arr1)+        marr2 = getMutableByteArray# (arrContents arr2)+        !(I# st1#) = arrStart arr1+        !(I# st2#) = arrStart arr2+        !(I# len#) = byteLength arr1+    case compare (byteLength arr1) (byteLength arr2) of+        EQ -> do+            r <- liftIO $ IO $ \s# ->+                     let res =+                             I#+                                 (compareByteArrays#+                                      (unsafeCoerce# marr1)+                                      st1#+                                      (unsafeCoerce# marr2)+                                      st2#+                                      len#)+                      in (# s#, res #)+            return $ compare r 0+        x -> return x++{-# INLINE cmp #-}+{-# DEPRECATED cmp "Please use byteCmp instead." #-}+cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering+cmp = byteCmp++-- | Byte equality of two arrays.+--+-- >>> byteEq arr1 arr2 = (==) EQ $ MArray.byteCmp arr1 arr2+--+-- /Unsafe/: See 'byteCmp'.+{-# INLINE byteEq #-}+byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool+byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2++-------------------------------------------------------------------------------+-- Compact+-------------------------------------------------------------------------------++-- Note: LE versions avoid an extra copy compared to GE. LE parser trades+-- backtracking one array in lieu of avoiding a copy. However, LE and GE both+-- can leave some memory unused. They can split the last array to fit it+-- exactly in the space.++{-# INLINE_NORMAL pCompactLeAs #-}+pCompactLeAs ::+       forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)+pCompactLeAs ps maxElems = Parser step initial extract++    where++    maxBytes = maxElems * SIZE_OF(a)++    functionName = "Streamly.Internal.Data.MutArray.pCompactLE"++    initial =+        return+            $ if maxElems <= 0+              then error+                       $ functionName+                       ++ ": the size of arrays ["+                       ++ show maxElems ++ "] must be a natural number"+              else Parser.IPartial Nothing++    step Nothing arr =+        return+            $ let len = byteLength arr+               in if len >= maxBytes+                  then Parser.Done 0 arr+                  else Parser.Partial 0 (Just arr)+    -- XXX Split the last array to use the space more compactly.+    step (Just buf) arr =+        let len = byteLength buf + byteLength arr+         in if len > maxBytes+            then return $ Parser.Done 1 buf+            else do+                buf1 <-+                    if byteCapacity buf < maxBytes+                    then liftIO $ reallocExplicitAs+                            ps (SIZE_OF(a)) maxBytes buf+                    else return buf+                buf2 <- spliceUnsafe buf1 arr+                return $ Parser.Partial 0 (Just buf2)++    extract Nothing = return $ Parser.Done 0 nil+    extract (Just buf) = return $ Parser.Done 0 buf++-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream+-- only if the combined size would be less than or equal to @maxElems@+-- elements. Note that it won't split an array if the original array is already+-- larger than maxElems.+--+-- @maxElems@ must be greater than 0.+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+--+-- /Internal/+{-# INLINE pCompactLE #-}+pCompactLE ::+       forall m a. (MonadIO m, Unbox a)+    => Int -> Parser (MutArray a) m (MutArray a)+pCompactLE = pCompactLeAs Unpinned++-- | Pinned version of 'pCompactLE'.+{-# INLINE pPinnedCompactLE #-}+pPinnedCompactLE ::+       forall m a. (MonadIO m, Unbox a)+    => Int -> Parser (MutArray a) m (MutArray a)+pPinnedCompactLE = pCompactLeAs Pinned++data SpliceState s arr+    = SpliceInitial s+    | SpliceBuffering s arr+    | SpliceYielding arr (SpliceState s arr)+    | SpliceFinish++-- This mutates the first array (if it has space) to append values from the+-- second one. This would work for immutable arrays as well because an+-- immutable array never has additional space so a new array is allocated+-- instead of mutating it.++{-# INLINE_NORMAL compactLeAs #-}+compactLeAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)+compactLeAs ps maxElems (D.Stream step state) =+    D.Stream step' (SpliceInitial state)++    where++    maxBytes = maxElems * SIZE_OF(a)++    functionName = "Streamly.Internal.Data.MutArray.rCompactLE"++    {-# INLINE_LATE step' #-}+    step' gst (SpliceInitial st) = do+        when (maxElems <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ functionName ++ ": the size of arrays [" ++ show maxElems+                ++ "] must be a natural number"+        r <- step gst st+        case r of+            D.Yield arr s -> return $+                let len = byteLength arr+                 in if len >= maxBytes+                    then D.Skip (SpliceYielding arr (SpliceInitial s))+                    else D.Skip (SpliceBuffering s arr)+            D.Skip s -> return $ D.Skip (SpliceInitial s)+            D.Stop -> return D.Stop++    -- XXX Split the last array to use the space more compactly.+    step' gst (SpliceBuffering st buf) = do+        r <- step gst st+        case r of+            D.Yield arr s -> do+                let len = byteLength buf + byteLength arr+                if len > maxBytes+                then return $+                    D.Skip (SpliceYielding buf (SpliceBuffering s arr))+                else do+                    buf1 <- if byteCapacity buf < maxBytes+                            then liftIO $ reallocExplicitAs+                                    ps (SIZE_OF(a)) maxBytes buf+                            else return buf+                    buf2 <- spliceUnsafe buf1 arr+                    return $ D.Skip (SpliceBuffering s buf2)+            D.Skip s -> return $ D.Skip (SpliceBuffering s buf)+            D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)++    step' _ SpliceFinish = return D.Stop++    step' _ (SpliceYielding arr next) = return $ D.Yield arr next+++{-# INLINE_NORMAL fCompactGeAs #-}+fCompactGeAs ::+       forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)+fCompactGeAs ps minElems = Fold step initial extract extract++    where++    minBytes = minElems * SIZE_OF(a)++    functionName = "Streamly.Internal.Data.MutArray.fCompactGE"++    initial =+        return+            $ if minElems < 0+              then error+                       $ functionName+                       ++ ": the size of arrays ["+                       ++ show minElems ++ "] must be a natural number"+              else FL.Partial Nothing++    step Nothing arr =+        return+            $ let len = byteLength arr+               in if len >= minBytes+                  then FL.Done arr+                  else FL.Partial (Just arr)+    -- XXX Buffer arrays as a list to avoid copy and reallocations+    step (Just buf) arr = do+        let len = byteLength buf + byteLength arr+        buf1 <-+            if byteCapacity buf < len+            then liftIO $ reallocExplicitAs+                    ps (SIZE_OF(a)) (max minBytes len) buf+            else return buf+        buf2 <- spliceUnsafe buf1 arr+        if len >= minBytes+        then return $ FL.Done buf2+        else return $ FL.Partial (Just buf2)++    extract Nothing = return nil+    extract (Just buf) = return buf++-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream+-- until the size becomes greater than or equal to @minElems@.+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE fCompactGE #-}+fCompactGE ::+       forall m a. (MonadIO m, Unbox a)+    => Int -> FL.Fold m (MutArray a) (MutArray a)+fCompactGE = fCompactGeAs Unpinned++-- | Pinned version of 'fCompactGE'.+{-# INLINE fPinnedCompactGE #-}+fPinnedCompactGE ::+       forall m a. (MonadIO m, Unbox a)+    => Int -> FL.Fold m (MutArray a) (MutArray a)+fPinnedCompactGE = fCompactGeAs Pinned++{-# INLINE_NORMAL lCompactGeAs #-}+lCompactGeAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+-- The fold version turns out to be a little bit slower.+-- lCompactGeAs ps n = FL.many (fCompactGeAs ps n)+lCompactGeAs ps minElems (Fold step1 initial1 _ final1) =+    Fold step initial extract final++    where++    minBytes = minElems * SIZE_OF(a)++    functionName = "Streamly.Internal.Data.MutArray.lCompactGE"++    initial = do+        when (minElems <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ functionName ++ ": the size of arrays ["+                ++ show minElems ++ "] must be a natural number"++        r <- initial1+        return $ first (Tuple' Nothing) r++    {-# INLINE runInner #-}+    runInner len acc buf =+            if len >= minBytes+            then do+                r <- step1 acc buf+                case r of+                    FL.Done _ -> return $ FL.Done ()+                    FL.Partial s -> do+                        _ <- final1 s+                        res <- initial1+                        return $ first (Tuple' Nothing) res+            else return $ FL.Partial $ Tuple' (Just buf) acc++    step (Tuple' Nothing r1) arr =+         runInner (byteLength arr) r1 arr++    -- XXX Buffer arrays as a list to avoid copy and reallocations+    step (Tuple' (Just buf) r1) arr = do+        let len = byteLength buf + byteLength arr+        buf1 <- if byteCapacity buf < len+                then liftIO $ reallocExplicitAs+                        ps (SIZE_OF(a)) (max minBytes len) buf+                else return buf+        buf2 <- spliceUnsafe buf1 arr+        runInner len r1 buf2++    -- XXX Several folds do extract >=> final, therefore, we need to make final+    -- return "m b" rather than using extract post it if we want extract to be+    -- partial.+    --+    -- extract forces the pending buffer to be sent to the fold which is not+    -- what we want.+    extract _ = error "lCompactGE: not designed for scanning"++    final (Tuple' Nothing r1) = final1 r1+    final (Tuple' (Just buf) r1) = do+        r <- step1 r1 buf+        case r of+            FL.Partial rr -> final1 rr+            FL.Done _ -> return ()++-- | Like 'compactGE' but for transforming folds instead of stream.+--+-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)+--+-- Generates unpinned arrays irrespective of the pinning status of input+-- arrays.+{-# INLINE lCompactGE #-}+lCompactGE :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+lCompactGE = lCompactGeAs Unpinned++-- | Pinned version of 'lCompactGE'.+{-# INLINE lPinnedCompactGE #-}+lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+lPinnedCompactGE = lCompactGeAs Pinned++-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until+-- the size becomes greater than or equal to @n@.+--+-- >>> compactGE n = Stream.foldMany (MutArray.fCompactGE n)+--+{-# INLINE compactGE #-}+compactGE ::+       (MonadIO m, Unbox a)+    => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+compactGE n = D.foldMany (fCompactGE n)++-- | 'compactEQ n' coalesces adajacent arrays in the input stream to+-- arrays of exact size @n@.+--+-- /Unimplemented/+{-# INLINE compactEQ #-}+compactEQ :: -- (MonadIO m, Unbox a) =>+    Int -> Stream m (MutArray a) -> Stream m (MutArray a)+compactEQ _n = undefined -- D.parseManyD (pCompactEQ n)++-------------------------------------------------------------------------------+-- In-place mutation algorithms -------------------------------------------------------------------------------  -- | Strip elements which match with predicate from both ends.
src/Streamly/Internal/Data/MutByteArray/Type.hs view
@@ -13,7 +13,7 @@     -- ** MutByteArray       MutByteArray(..)     , MutableByteArray-    , getMutableByteArray#+    , getMutableByteArray# -- XXX getMutByteArray#      -- ** Pinning     , PinnedState(..)@@ -22,19 +22,24 @@     , unpin      -- ** Allocation-    , nil-    , newBytesAs+    , empty+    , newBytesAs -- XXX should be removed     , new     , pinnedNew-    , pinnedNewAlignedBytes+    , pinnedNewAlignedBytes -- XXX should be removed      -- ** Access-    , sizeOfMutableByteArray+    , sizeOfMutableByteArray -- XXX length     , putSliceUnsafe     , cloneSliceUnsafeAs     , cloneSliceUnsafe     , pinnedCloneSliceUnsafe+    , unsafePinnedAsPtr+    , unsafeAsPtr++    -- ** Deprecated     , asPtrUnsafe+    , nil     ) where  import Control.Monad.IO.Class (MonadIO(..))@@ -53,7 +58,7 @@  data PinnedState     = Pinned-    | Unpinned+    | Unpinned deriving (Show, Eq)  -- XXX can use UnliftedNewtypes @@ -82,6 +87,9 @@ touch (MutByteArray contents) =     IO $ \s -> case touch# contents s of s' -> (# s', () #) +-- XXX Some functions in this module are "IO" and others are "m", we need to+-- make it consistent.+ -- XXX We can provide another API for "unsafe" FFI calls passing an unlifted -- pointer to the FFI call. For unsafe calls we do not need to pin the array. -- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in@@ -110,9 +118,9 @@ -- -- /Pre-release/ ---{-# INLINE asPtrUnsafe #-}-asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b-asPtrUnsafe arr f = do+{-# INLINE unsafePinnedAsPtr #-}+unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+unsafePinnedAsPtr arr f = do   contents <- liftIO $ pin arr   let !ptr = Ptr (byteArrayContents#                      (unsafeCoerce# (getMutableByteArray# contents)))@@ -120,14 +128,35 @@   liftIO $ touch contents   return r +{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}+{-# INLINE asPtrUnsafe #-}+asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+asPtrUnsafe = unsafePinnedAsPtr++-- | For use with unsafe FFI functions. Does not force pin the array memory.+{-# INLINE unsafeAsPtr #-}+unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+unsafeAsPtr arr f = do+  let !ptr = Ptr (byteArrayContents#+                     (unsafeCoerce# (getMutableByteArray# arr)))+  r <- f ptr+  liftIO $ touch arr+  return r+ -------------------------------------------------------------------------------- -- Creation -------------------------------------------------------------------------------- -{-# NOINLINE nil #-}+{-# NOINLINE empty #-}+empty :: MutByteArray+empty = unsafePerformIO $ new 0++{-# DEPRECATED nil "Please use empty instead" #-} nil :: MutByteArray-nil = unsafePerformIO $ new 0+nil = empty +-- XXX add "newRounded" to round up the large size to the next page boundary+-- and return the allocated size. {-# INLINE new #-} new :: Int -> IO MutByteArray new nbytes | nbytes < 0 =@@ -141,7 +170,7 @@ {-# INLINE pinnedNew #-} pinnedNew :: Int -> IO MutByteArray pinnedNew nbytes | nbytes < 0 =-  errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"+  errorWithoutStackTrace "pinnedNew: size must be >= 0" pinnedNew (I# nbytes) = IO $ \s ->     case newPinnedByteArray# nbytes s of         (# s', mbarr# #) ->
src/Streamly/Internal/Data/Stream/Generate.hs view
@@ -154,6 +154,8 @@ nil :: Applicative m => Stream m a nil = Stream (\_ _ -> pure Stop) () +infixr 5 `cons`+ -- XXX implement in terms of consM? -- cons x = consM (return x) -- From an implementation perspective, StreamK.cons translates into a
src/Streamly/Internal/Data/Stream/Nesting.hs view
@@ -149,7 +149,6 @@     , wordsBy     , splitOnSeq     , splitOnSuffixSeq-    , sliceOnSuffix      -- XXX Implement these as folds or parsers instead.     , splitOnSuffixSeqAny@@ -182,12 +181,9 @@ import GHC.Types (SPEC(..))  import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Fold.Step (Step(..)) import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Internal.Data.Parser (ParseError(..))-import Streamly.Internal.Data.Refold.Type (Refold(..)) import Streamly.Internal.Data.SVar.Type (adaptState)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..)) import Streamly.Internal.Data.Unbox (Unbox, sizeOf) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) @@ -1294,77 +1290,6 @@     stepOuter _ (FIterYield a next) = return $ Yield a next     stepOuter _ FIterStop = return Stop -{-# ANN type CIterState Fuse #-}-data CIterState s f fs b-    = CIterInit s f-    | CIterConsume s fs-    | CIterYield b (CIterState s f fs b)-    | CIterStop---- | Like 'foldIterateM' but using the 'Refold' type instead. This could be--- much more efficient due to stream fusion.------ /Internal/-{-# INLINE_NORMAL refoldIterateM #-}-refoldIterateM ::-       Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b-refoldIterateM (Refold fstep finject fextract) initial (Stream step state) =-    Stream stepOuter (CIterInit state initial)--    where--    {-# INLINE iterStep #-}-    iterStep st action = do-        res <- action-        return-            $ Skip-            $ case res of-                  FL.Partial fs -> CIterConsume st fs-                  FL.Done fb -> CIterYield fb $ CIterInit st (return fb)--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ (CIterInit st action) = do-        iterStep st (action >>= finject)-    stepOuter gst (CIterConsume st fs) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> iterStep s (fstep fs x)-            Skip s -> return $ Skip $ CIterConsume s fs-            Stop -> do-                b <- fextract fs-                return $ Skip $ CIterYield b CIterStop-    stepOuter _ (CIterYield a next) = return $ Yield a next-    stepOuter _ CIterStop = return Stop---- "n" elements at the end are dropped by the fold.-{-# INLINE sliceBy #-}-sliceBy :: Monad m => Fold m a Int -> Int -> Refold m (Int, Int) a (Int, Int)-sliceBy (Fold step1 initial1 extract1 _final) n = Refold step inject extract--    where--    inject (i, len) = do-        r <- initial1-        return $ case r of-            Partial s -> Partial $ Tuple' (i + len + n) s-            Done l -> Done (i, l)--    step (Tuple' i s) x = do-        r <- step1 s x-        return $ case r of-            Partial s1 -> Partial $ Tuple' i s1-            Done len -> Done (i, len)--    extract (Tuple' i s) = (i,) <$> extract1 s--{-# INLINE sliceOnSuffix #-}-sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)-sliceOnSuffix predicate =-    -- Scan the stream with the given refold-    refoldIterateM-        (sliceBy (FL.takeEndBy_ predicate FL.length) 1)-        (return (-1, 0))- ------------------------------------------------------------------------------ -- Parsing ------------------------------------------------------------------------------@@ -3065,12 +2990,13 @@ -- | Performs infix separator style splitting. {-# INLINE_NORMAL splitInnerBySuffix #-} splitInnerBySuffix-    :: (Monad m, Eq (f a), Monoid (f a))-    => (f a -> m (f a, Maybe (f a)))  -- splitter+    :: Monad m+    => (f a -> Bool)                  -- isEmpty?+    -> (f a -> m (f a, Maybe (f a)))  -- splitter     -> (f a -> f a -> m (f a))        -- joiner     -> Stream m (f a)     -> Stream m (f a)-splitInnerBySuffix splitter joiner (Stream step1 state1) =+splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =     Stream step (SplitInitial state1)      where@@ -3097,10 +3023,11 @@                     Nothing -> Skip (SplitBuffering s buf')                     Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))             Skip s -> return $ Skip (SplitBuffering s buf)-            Stop -> return $-                if buf == mempty-                then Stop-                else Skip (SplitYielding buf SplitFinishing)+            Stop ->+                return $+                    if isEmpty buf+                    then Stop+                    else Skip (SplitYielding buf SplitFinishing)      step _ (SplitSplitting st buf) = do         (x1, mx2) <- splitter buf
src/Streamly/Internal/Data/Stream/Transform.hs view
@@ -1537,7 +1537,7 @@ {-# INLINE reverseUnbox #-} reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a reverseUnbox =-    A.flattenArraysRev -- unfoldMany A.readRev+    A.concatRev -- unfoldMany A.readerRev         . fromStreamK         . K.reverse         . toStreamK
src/Streamly/Internal/Data/Stream/Type.hs view
@@ -123,14 +123,21 @@     , foldManyPost     , groupsOf     , refoldMany+    , refoldIterateM      -- * Fold Iterate     , reduceIterateBfs     , foldIterateBfs +    -- * Splitting+    , indexOnSuffix+     -- * Multi-stream folds     , eqBy     , cmpBy++    -- * Deprecated+    , sliceOnSuffix     ) where @@ -162,6 +169,7 @@ import Streamly.Internal.Data.Refold.Type (Refold(..)) import Streamly.Internal.Data.Stream.Step (Step (..)) import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..))  import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)@@ -212,6 +220,9 @@ nilM :: Applicative m => m b -> Stream m a nilM m = Stream (\_ _ -> m $> Stop) () ++infixr 5 `consM`+ -- | Like 'cons' but fuses an effect instead of a pure value. {-# INLINE_NORMAL consM #-} consM :: Applicative m => m a -> Stream m a -> Stream m a@@ -1970,6 +1981,90 @@                 return $ Skip (FoldManyYield b FoldManyDone)     step' _ (FoldManyYield b next) = return $ Yield b next     step' _ FoldManyDone = return Stop++{-# ANN type CIterState Fuse #-}+data CIterState s f fs b+    = CIterInit s f+    | CIterConsume s fs+    | CIterYield b (CIterState s f fs b)+    | CIterStop++-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be+-- much more efficient due to stream fusion.+--+-- /Internal/+{-# INLINE_NORMAL refoldIterateM #-}+refoldIterateM ::+       Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b+refoldIterateM (Refold fstep finject fextract) initial (Stream step state) =+    Stream stepOuter (CIterInit state initial)++    where++    {-# INLINE iterStep #-}+    iterStep st action = do+        res <- action+        return+            $ Skip+            $ case res of+                  FL.Partial fs -> CIterConsume st fs+                  FL.Done fb -> CIterYield fb $ CIterInit st (return fb)++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ (CIterInit st action) = do+        iterStep st (action >>= finject)+    stepOuter gst (CIterConsume st fs) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> iterStep s (fstep fs x)+            Skip s -> return $ Skip $ CIterConsume s fs+            Stop -> do+                b <- fextract fs+                return $ Skip $ CIterYield b CIterStop+    stepOuter _ (CIterYield a next) = return $ Yield a next+    stepOuter _ CIterStop = return Stop++-- | The refold @indexerBy f n@ takes an (index, len) tuple as initial input,+-- and returns @(index + len + n, b)@ as output where @b@ is the output of the+-- fold.+{-# INLINE indexerBy #-}+indexerBy :: Monad m =>+    Fold m a Int -> Int -> Refold m (Int, Int) a (Int, Int)+indexerBy (Fold step1 initial1 extract1 _final) n =+    Refold step inject extract++    where++    inject (i, len) = do+        r <- initial1+        return $ case r of+            FL.Partial s -> FL.Partial $ Tuple' (i + len + n) s+            FL.Done l -> FL.Done (i, l)++    step (Tuple' i s) x = do+        r <- step1 s x+        return $ case r of+            FL.Partial s1 -> FL.Partial $ Tuple' i s1+            FL.Done len -> FL.Done (i, len)++    extract (Tuple' i s) = (i,) <$> extract1 s++-- | Like 'splitOnSuffix' but generates a stream of (index, len) tuples marking+-- the places where the predicate matches in the stream.+--+-- /Pre-release/+{-# INLINE indexOnSuffix #-}+indexOnSuffix :: Monad m =>+    (a -> Bool) -> Stream m a -> Stream m (Int, Int)+indexOnSuffix predicate =+    -- Scan the stream with the given refold+    refoldIterateM+        (indexerBy (FL.takeEndBy_ predicate FL.length) 1)+        (return (-1, 0))++{-# DEPRECATED sliceOnSuffix "Please use indexOnSuffix instead." #-}+sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+sliceOnSuffix = indexOnSuffix  ------------------------------------------------------------------------------ -- Stream with a cross product style monad instance
src/Streamly/Internal/Data/StreamK.hs view
@@ -37,8 +37,6 @@     -- * Elimination     -- ** General Folds     , foldr1-    , foldlM'-    , foldlMx'     , fold     , foldBreak     , foldEither@@ -306,19 +304,6 @@             yieldk a r = fmap (step p) (go a r)          in foldStream defState yieldk single stp m1 --- XXX replace the recursive "go" with explicit continuations.--- | Like 'foldx', but with a monadic step function.-{-# INLINABLE foldlMx' #-}-foldlMx' :: Monad m-    => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b-foldlMx' step begin done = go begin-    where-    go !acc m1 =-        let stop = acc >>= done-            single a = acc >>= \b -> step b a >>= done-            yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r-         in foldStream defState yieldk single stop m1- -- | Fold a stream using the supplied left 'Fold' and reducing the resulting -- expression strictly at each step. The behavior is similar to 'foldl''. A -- 'Fold' can terminate early without consuming the full stream. See the@@ -469,11 +454,6 @@                     FL.Partial fs1 -> go1 SPEC fs1 s             Stream.Skip s -> go1 SPEC fs s             Stream.Stop -> return $ Left fs---- | Like 'foldl'' but with a monadic step function.-{-# INLINE foldlM' #-}-foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b-foldlM' step begin = foldlMx' step begin return  ------------------------------------------------------------------------------ -- Specialized folds
src/Streamly/Internal/Data/StreamK/Type.hs view
@@ -84,6 +84,8 @@     -- ** Strict Left Folds     , Streamly.Internal.Data.StreamK.Type.foldl'     , foldlx'+    , foldlMx'+    , foldlM'      -- ** Lazy Right Folds     , Streamly.Internal.Data.StreamK.Type.foldr@@ -816,6 +818,24 @@ {-# INLINE foldl' #-} foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b foldl' step begin = foldlx' step begin id++-- XXX replace the recursive "go" with explicit continuations.+-- | Like 'foldx', but with a monadic step function.+{-# INLINABLE foldlMx' #-}+foldlMx' :: Monad m+    => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b+foldlMx' step begin done = go begin+    where+    go !acc m1 =+        let stop = acc >>= done+            single a = acc >>= \b -> step b a >>= done+            yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r+         in foldStream defState yieldk single stop m1++-- | Like 'foldl'' but with a monadic step function.+{-# INLINE foldlM' #-}+foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b+foldlM' step begin = foldlMx' step begin return  ------------------------------------------------------------------------------ -- Specialized folds
src/Streamly/Internal/FileSystem/File.hs view
@@ -103,15 +103,14 @@ import qualified System.IO as SIO  import Streamly.Data.Fold (groupsOf, drain)-import Streamly.Internal.Data.Array.Type (Array(..), pinnedWriteNUnsafe)+import Streamly.Internal.Data.Array.Type (Array(..)) import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Data.Stream (Stream)-import Streamly.Internal.Data.Unbox (Unbox) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) -- import Streamly.String (encodeUtf8, decodeUtf8, foldLines) import Streamly.Internal.System.IO (defaultChunkSize) -import qualified Streamly.Data.Array as A+import qualified Streamly.Internal.Data.Array as A import qualified Streamly.Data.Stream as S import qualified Streamly.Data.Unfold as UF import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)@@ -339,10 +338,6 @@ reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8 reader = UF.many A.reader (usingFile FH.chunkReader) -{-# INLINE concatChunks #-}-concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a-concatChunks = S.unfoldMany A.reader- -- | Generate a stream of bytes from a file specified by path. The stream ends -- when EOF is encountered. File is locked using multiple reader and single -- writer locking mode.@@ -351,7 +346,7 @@ -- {-# INLINE read #-} read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8-read file = concatChunks $ withFile file ReadMode FH.readChunks+read file = A.concat $ withFile file ReadMode FH.readChunks  {-# DEPRECATED toBytes "Please use 'read' instead"  #-} {-# INLINE toBytes #-}@@ -467,7 +462,7 @@ writeWith :: (MonadIO m, MonadCatch m)     => Int -> FilePath -> Fold m Word8 () writeWith n path =-    groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)+    groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)  {-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead"  #-} {-# INLINE writeWithBufferOf #-}
src/Streamly/Internal/FileSystem/Handle.hs view
@@ -40,8 +40,8 @@      -- * Streams     , read-    , readWith-    , readChunksWith+    , readWith       -- readConcatChunksOf+    , readChunksWith -- readChunksOf     , readChunks      -- * Unfolds@@ -59,10 +59,10 @@     -- , writeUtf8ByLines     -- , writeByFrames     -- , writeLines-    , writeWith+    , writeWith -- writeChunksOf     , writeChunks-    , writeChunksWith-    , writeMaybesWith+    , writeChunksWith -- writeCompactChunksOf+    , writeMaybesWith -- writeCompactJustsOf      -- * Refolds     , writer@@ -131,16 +131,13 @@ import Streamly.Internal.Data.Refold.Type (Refold(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Streamly.Internal.Data.Array.Type-       (Array(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)+       (Array(..), unsafeFreezeWithShrink, byteLength) import Streamly.Internal.Data.Stream.Type (Stream)-import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf) -- import Streamly.String (encodeUtf8, decodeUtf8, foldLines) import Streamly.Internal.System.IO (defaultChunkSize)  import qualified Streamly.Data.Fold as FL-import qualified Streamly.Data.Array as A-import qualified Streamly.Internal.Data.Array.Type as A-import qualified Streamly.Internal.Data.Array.Stream as AS+import qualified Streamly.Internal.Data.Array as A import qualified Streamly.Internal.Data.MutArray.Type as MArray import qualified Streamly.Internal.Data.Refold.Type as Refold import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)@@ -169,6 +166,8 @@ -- Array IO (Input) ------------------------------------------------------------------------------- +-- XXX add an API that compacts the arrays to an exact size.+ -- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If -- no data is available on the handle it blocks until at least one byte becomes -- available. If any data is available then it immediately returns that data@@ -178,9 +177,9 @@ {-# INLINABLE getChunk #-} getChunk :: MonadIO m => Int -> Handle -> m (Array Word8) getChunk size h = liftIO $ do-    arr <- MArray.pinnedNewBytes size+    arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size     -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))-    MArray.asPtrUnsafe arr $ \p -> do+    MArray.unsafePinnedAsPtr arr $ \p -> do         n <- hGetBufSome h p size         -- XXX shrink only if the diff is significant         return $@@ -338,10 +337,6 @@ readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8 readWithBufferOf = readerWith -{-# INLINE concatChunks #-}-concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a-concatChunks = S.unfoldMany A.reader- -- | @readWith bufsize handle@ reads a byte stream from a file -- handle, reads are performed in chunks of up to @bufsize@. --@@ -350,7 +345,7 @@ -- /Pre-release/ {-# INLINE readWith #-} readWith :: MonadIO m => Int -> Handle -> Stream m Word8-readWith size h = concatChunks $ readChunksWith size h+readWith size h = A.concat $ readChunksWith size h  -- | Unfolds a file handle into a byte stream. IO requests to the device are -- performed in sizes of@@ -369,7 +364,7 @@ -- /Pre-release/ {-# INLINE read #-} read :: MonadIO m => Handle -> Stream m Word8-read = concatChunks . readChunks+read = A.concat . readChunks  ------------------------------------------------------------------------------- -- Writing@@ -384,12 +379,12 @@ {-# INLINABLE putChunk #-} putChunk :: MonadIO m => Handle -> Array a -> m () putChunk _ arr | byteLength arr == 0 = return ()-putChunk h arr = A.asPtrUnsafe arr $ \ptr ->+putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->     liftIO $ hPutBuf h ptr aLen      where -    -- XXX We should have the length passed by asPtrUnsafe itself.+    -- XXX We should have the length passed by unsafePinnedAsPtr itself.     aLen = A.byteLength arr  -------------------------------------------------------------------------------@@ -421,7 +416,7 @@ {-# INLINE putChunksWith #-} putChunksWith :: (MonadIO m, Unbox a)     => Int -> Handle -> Stream m (Array a) -> m ()-putChunksWith n h xs = putChunks h $ AS.compact n xs+putChunksWith n h xs = putChunks h $ A.compactLE n xs  -- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m @@ -461,8 +456,6 @@ chunkWriter :: MonadIO m => Refold m Handle (Array a) () chunkWriter = Refold.drainBy putChunk --- XXX lpackArraysChunksOf should be written idiomatically- -- | @writeChunksWith bufsize handle@ writes a stream of arrays -- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@. -- We never split an array, if a single array is bigger than the specified size@@ -472,7 +465,7 @@ {-# INLINE writeChunksWith #-} writeChunksWith :: (MonadIO m, Unbox a)     => Int -> Handle -> Fold m (Array a) ()-writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)+writeChunksWith n h = A.lCompactGE n (writeChunks h)  -- | Same as 'writeChunksWith' --@@ -496,11 +489,11 @@ -- Bytes in the input stream are collected into a buffer until we have a chunk -- of @reqSize@ and then written to the IO device. ----- >>> writeWith n h = Fold.groupsOf n (Array.writeNUnsafe n) (Handle.writeChunks h)+-- >>> writeWith n h = Fold.groupsOf n (Array.unsafeCreateOf n) (Handle.writeChunks h) -- {-# INLINE writeWith #-} writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()-writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)  -- | Same as 'writeWith' --@@ -518,7 +511,7 @@ writeMaybesWith :: (MonadIO m )     => Int -> Handle -> Fold m (Maybe Word8) () writeMaybesWith n h =-    let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n+    let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n         writeOnNothing = FL.takeEndBy_ isNothing writeNJusts     in FL.many writeOnNothing (writeChunks h) @@ -528,7 +521,7 @@ {-# INLINE writerWith #-} writerWith :: MonadIO m => Int -> Refold m Handle Word8 () writerWith n =-    FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter+    FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter  -- | Write a byte stream to a file handle. Accumulates the input in chunks of -- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
+ src/Streamly/Internal/FileSystem/Path.hs view
@@ -0,0 +1,515 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module      : Streamly.Internal.FileSystem.Path+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--+-- Well typed and flexible file systems paths, preserving the OS and filesystem+-- encoding.+--+-- You can choose the level of type safety you want. 'Path' is the basic path+-- type which can represent a file, directory, absolute or relative path with+-- no restrictions. Depending on how much type safety you want you can choose+-- appropriate type wrappers to wrap 'Path'. @File Path@ mandates the path to+-- be a file whereas @Abs (File Path)@ mandates it to be an absolute path+-- representing a file.+--+-- You can upgrade or downgrade the safety. Whenever a less restrictive path+-- type is converted to a more restrctive path type the conversion involves+-- checks and it may fail. However, a more restrictive path type can be freely+-- converted to a less restrictive one.+--+-- See the @streamly-filepath@ package for interworking with the 'OsPath' type.+-- The 'Path' type can be  converted to and from 'OsPath' type at zero cost+-- since the underlying representation of both is the same.++-- Conventions: A trailing separator on a path indicates that it is a directory.+-- However, the absence of a trailing separator does not convey any+-- information, it could either be a directory or a file.++-- You may also find the 'str' quasiquoter from "Streamly.Unicode.String" to be+-- useful in creating paths.+--+module Streamly.Internal.FileSystem.Path+    (+    -- * Path Types+      Path (..)+    , File+    , Dir+    , Abs+    , Rel++    -- * Conversions+    , IsPath (..)+    , adaptPath++    -- * Construction+    , fromChunk+    , fromChunkUnsafe+    , fromString+    , fromChars++    -- * Statically Verified Literals+    -- quasiquoters+    , path+    , abs+    , rel+    , dir+    , file+    , absdir+    , reldir+    , absfile+    , relfile++    -- * Statically Verified Strings+    -- TH macros+    , mkPath+    , mkAbs+    , mkRel+    , mkDir+    , mkFile+    , mkAbsDir+    , mkRelDir+    , mkAbsFile+    , mkRelFile++    -- * Elimination+    , toChunk+    , toString+    , toChars++    -- * Operations+    -- Do we need to export the separator functions? They are not essential if+    -- operations to split and combine paths are provided. If someone wants to+    -- work on paths at low level then they know what they are.+    , primarySeparator+    , isSeparator+    , extendPath+    , extendDir+    )+where++import Control.Exception (Exception)+import Control.Monad.Catch (MonadThrow(..))+import Data.Char (chr)+import Data.Functor.Identity (Identity(..))+import Data.Word (Word8)+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)+import Data.Word (Word16)+#endif+import Language.Haskell.TH (Q, Exp)+import Language.Haskell.TH.Quote (QuasiQuoter)+import Streamly.Internal.Data.Array (Array)+import Streamly.Internal.Data.Stream (Stream)+import System.IO.Unsafe (unsafePerformIO)++import qualified Streamly.Internal.Data.Array as Array+import qualified Streamly.Internal.Data.Fold as Fold+import qualified Streamly.Internal.Data.MutArray as MutArray+import qualified Streamly.Internal.Data.Stream as Stream+import qualified Streamly.Internal.Unicode.Stream as Unicode++import Prelude hiding (abs)++#if defined(mingw32_HOST_OS) || defined(__MINGW32__)+#define WORD_TYPE Word16+#define SEPARATOR 92+#else+#define WORD_TYPE Word8+#define SEPARATOR 47+#endif++------------------------------------------------------------------------------+-- Exceptions+------------------------------------------------------------------------------++-- | Exceptions thrown by path operations.+data PathException =+    InvalidPath String+  | InvalidAbsPath String+  | InvalidRelPath String+  | InvalidFilePath String+  | InvalidDirPath String+    deriving (Show,Eq)++instance Exception PathException++-- XXX Path must not contain null char on Posix. System calls consider the path+-- as null terminated.+-- XXX Maintain the Array with null termination because Unix system calls+-- require a null terminated string, also they return null terminated strings+-- as paths. Implementation of path append will have to handle the null+-- termination. Or we can choose to always copy the array when using it in+-- system calls.++-- XXX The eq instance needs to make sure that the paths are equivalent. If we+-- normalize the paths we can do a byte comparison. However, on windows paths+-- are case insensitive but the case is preserved, therefore, we cannot+-- normalize and need to do case insensitive comparison.++------------------------------------------------------------------------------+-- Types+------------------------------------------------------------------------------++-- | A type representing file system paths for directories or files.+newtype Path = Path (Array WORD_TYPE) -- deriving Eq++-- Show instance prints raw bytes without any decoding for rountdtripping.+-- Should we print this as a string instead, may be useful for ascii chars but+-- utf8 encoded chars may be unprintable. Better use toString if you want to+-- pretty print the path.+{-+instance Show Path where+    show (Path x) = show x+-}++-- XXX Do we need a type for file or dir Name as names cannot have the+-- separator char and there may be other restrictions on names? For example,+-- length restriction.  A file name cannot be "." or "..". We can use the types+-- "File Name" and "Dir Name" to represent names. Also, file systems may put+-- limits on names. Can have an IsName type class with members Name, (File+-- Name), (Dir Name).++-- | A type representing a file path.+newtype File a = File a++-- | A type representing a directory path.+newtype Dir a = Dir a++-- | A type representing absolute paths.+newtype Abs a = Abs a++-- | A type representing relative paths.+newtype Rel a = Rel a++------------------------------------------------------------------------------+-- Conversions+------------------------------------------------------------------------------++-- | A member of 'IsPath' knows how to convert to and from the 'Path' type.+class IsPath a where+    -- | Like 'fromPath' but does not check the properties of 'Path'. Provides+    -- performance and simplicity when we know that the properties of the path+    -- are already verified, for example, when we get the path from the file+    -- system or the OS APIs.+    fromPathUnsafe :: Path -> a++    -- | Convert a raw 'Path' to other forms of well-typed paths. It may fail+    -- if the path does not satisfy the properties of the target type.+    --+    -- Path components may have limits.+    -- Total path length may have a limit.+    fromPath :: MonadThrow m => Path -> m a++    -- | Convert a well-typed path to a raw 'Path'. Never fails.+    toPath :: a -> Path++instance IsPath Path where+    fromPathUnsafe = id+    fromPath = pure+    toPath = id++instance IsPath (File Path) where+    fromPathUnsafe p = File p+    fromPath p = pure (File p)+    toPath (File p) = p++instance IsPath (Dir Path) where+    fromPathUnsafe p = Dir p+    fromPath p = pure (Dir p)+    toPath (Dir p) = p++instance IsPath (Abs Path) where+    fromPathUnsafe p = Abs p+    fromPath p = pure (Abs p)+    toPath (Abs p) = p++instance IsPath (Rel Path) where+    fromPathUnsafe p = Rel p+    fromPath p = pure (Rel p)+    toPath (Rel p) = p++instance IsPath (Abs (File Path)) where+    fromPathUnsafe p = Abs (File p)+    fromPath p = pure (Abs (File p))+    toPath (Abs (File p)) = p++instance IsPath (Abs (Dir Path)) where+    fromPathUnsafe p = Abs (Dir p)+    fromPath p = pure (Abs (Dir p))+    toPath (Abs (Dir p)) = p++instance IsPath (Rel (File Path)) where+    fromPathUnsafe p = Rel (File p)+    fromPath p = pure (Rel (File p))+    toPath (Rel (File p)) = p++instance IsPath (Rel (Dir Path)) where+    fromPathUnsafe p = Rel (Dir p)+    fromPath p = pure (Rel (Dir p))+    toPath (Rel (Dir p)) = p++-- XXX Use rewrite rules to eliminate intermediate conversions for better+-- efficiency.++-- | Convert a path type to another path type. This operation may fail with a+-- 'PathException' when converting a less restrictive path type to a more+-- restrictive one.+adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b+adaptPath p = fromPath $ toPath p++------------------------------------------------------------------------------+-- Construction+------------------------------------------------------------------------------++-- A chunk is essentially an untyped Array i.e. Array Word8.  We can either use+-- the term ByteArray for that or just Chunk. The latter is shorter and we have+-- been using it consistently in streamly.++-- | /Unsafe/: On Posix, a path cannot contain null characters. On Windows, the+-- array passed must be a multiple of 2 bytes as the underlying representation+-- uses 'Word16'.+{-# INLINE fromChunkUnsafe #-}+fromChunkUnsafe :: Array Word8 -> Path+fromChunkUnsafe arr = Path (Array.castUnsafe arr)++-- | On Posix it may fail if the byte array contains null characters. On+-- Windows the array passed must be a multiple of 2 bytes as the underlying+-- representation uses 'Word16'.+--+-- Throws 'InvalidPath'.+fromChunk :: MonadThrow m => Array Word8 -> m Path+fromChunk arr =+    case Array.cast arr of+        Nothing ->+            -- XXX Windows only message.+            throwM+                $ InvalidPath+                $ "Encoded path length " ++ show (Array.byteLength arr)+                    ++ " is not a multiple of 16-bit."+        Just x -> pure (Path x)++-- | Convert 'Path' to an array of bytes.+toChunk :: Path -> Array Word8+toChunk (Path arr) = Array.asBytes arr++-- | Encode a Unicode char stream to 'Path' using strict UTF-8 encoding on+-- Posix. On Posix it may fail if the stream contains null characters.+-- TBD: Use UTF16LE on Windows.+fromChars :: MonadThrow m => Stream Identity Char -> m Path+fromChars s =+    let n = runIdentity $ Stream.fold Fold.length s+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)+     in pure $ Path (Array.fromPureStreamN n (Unicode.encodeUtf16le' s))+#else+     in pure $ Path (Array.fromPureStreamN n (Unicode.encodeUtf8' s))+#endif++-- | Decode the path to a stream of Unicode chars using strict UTF-8 decoding+-- on Posix.+-- TBD: Use UTF16LE on Windows.+toChars :: Monad m => Path -> Stream m Char+toChars (Path arr) =+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)+    Unicode.decodeUtf16le' $ Array.read arr+#else+    Unicode.decodeUtf8' $ Array.read arr+#endif++-- | Encode a Unicode string to 'Path' using strict UTF-8 encoding on Posix.+-- On Posix it may fail if the stream contains null characters.+-- TBD: Use UTF16LE on Windows.+fromString :: MonadThrow m => [Char] -> m Path+fromString = fromChars . Stream.fromList++-- | Decode the path to a Unicode string using strict UTF-8 decoding on Posix.+-- TBD: Use UTF16LE on Windows.+toString :: Path -> [Char]+toString = runIdentity . Stream.toList . toChars++------------------------------------------------------------------------------+-- Statically Verified Literals+------------------------------------------------------------------------------++-- XXX Build these on top of the str quasiquoter so that we get the+-- interpolation for free.++-- | Generates a 'Path' type from an interpolated string literal.+--+-- /Unimplemented/+path :: QuasiQuoter+path = undefined++-- | Generates an @Abs Path@ type from an interpolated string literal.+--+-- /Unimplemented/+abs :: QuasiQuoter+abs = undefined++-- | Generates an @Rel Path@ type from an interpolated string literal.+--+-- /Unimplemented/+rel :: QuasiQuoter+rel = undefined++-- | Generates an @Dir Path@ type from an interpolated string literal.+--+-- /Unimplemented/+dir :: QuasiQuoter+dir = undefined++-- | Generates an @File Path@ type from an interpolated string literal.+--+-- /Unimplemented/+file :: QuasiQuoter+file = undefined++-- | Generates an @Abs (Dir Path)@ type from an interpolated string literal.+--+-- /Unimplemented/+absdir :: QuasiQuoter+absdir = undefined++-- | Generates an @Rel (Dir Path)@ type from an interpolated string literal.+--+-- /Unimplemented/+reldir :: QuasiQuoter+reldir = undefined++-- | Generates an @Abs (File Path)@ type from an interpolated string literal.+--+-- /Unimplemented/+absfile :: QuasiQuoter+absfile = undefined++-- | Generates an @Rel (File Path)@ type from an interpolated string literal.+--+-- /Unimplemented/+relfile :: QuasiQuoter+relfile = undefined++------------------------------------------------------------------------------+-- Statically Verified Strings+------------------------------------------------------------------------------++-- | Generates a 'Path' type.+--+-- /Unimplemented/+mkPath :: String -> Q Exp+mkPath = undefined++-- | Generates an @Abs Path@ type.+--+-- /Unimplemented/+mkAbs :: String -> Q Exp+mkAbs = undefined++-- | Generates an @Rel Path@ type.+--+-- /Unimplemented/+mkRel :: String -> Q Exp+mkRel = undefined++-- | Generates an @Dir Path@ type.+--+-- /Unimplemented/+mkDir :: String -> Q Exp+mkDir = undefined++-- | Generates an @File Path@ type.+--+-- /Unimplemented/+mkFile :: String -> Q Exp+mkFile = undefined++-- | Generates an @Abs (Dir Path)@ type.+--+-- /Unimplemented/+mkAbsDir :: String -> Q Exp+mkAbsDir = undefined++-- | Generates an @Rel (Dir Path)@ type.+--+-- /Unimplemented/+mkRelDir :: String -> Q Exp+mkRelDir = undefined++-- | Generates an @Abs (File Path)@ type.+--+-- /Unimplemented/+mkAbsFile :: String -> Q Exp+mkAbsFile = undefined++-- | Generates an @Rel (File Path)@ type.+--+-- /Unimplemented/+mkRelFile :: String -> Q Exp+mkRelFile = undefined++------------------------------------------------------------------------------+-- Operations+------------------------------------------------------------------------------++separatorWord :: WORD_TYPE+separatorWord = SEPARATOR++-- Portable definition for exporting.++-- | Primary path separator character, @/@ on Posix and @\\@ on Windows.+-- Windows supports @/@ too as a separator. Please use 'isSeparator' for+-- testing if a char is a separator char.+primarySeparator :: Char+primarySeparator = chr (SEPARATOR)++-- | On Posix only @/@ is a path separator but in windows it could be either+-- @/@ or @\\@.+isSeparator :: Char -> Bool+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)+isSeparator c = (c == '/') || (c == '\\')+#else+isSeparator = (== '/')+#endif++-- If we append an absolute path it may fail with an error if the 'Path'+-- implementation stores absolute path information (a leading separator char).+-- However, the implementation may choose to store the path as a list of+-- components in which case we cannot distinguish an absolute path from+-- relative.++-- | Like 'extendDir' but for the less restrictive 'Path' type which will always+-- create a syntactically valid 'Path' type but it may not be semantically valid+-- because we may append an absolute path or we may append to a file path.+-- The onus lies on the user to ensure that the first path is not a file and+-- the second path is not absolute.+extendPath :: Path -> Path -> Path+extendPath (Path a) (Path b) =+    let len = Array.byteLength a + 1 + Array.byteLength b+        -- XXX Check the leading separator or drive identifier. However,+        -- checking the drive letter may add an additional overhead (can it be+        -- arbitrarily long?), if it is significant we may want to have a+        -- separate combinePathChecked API for that.+        --+        -- Also, do not add a separator char if the first path has a trailing+        -- separator.+        newArr = unsafePerformIO $ do+            arr <- MutArray.new len+            arr1 <- MutArray.spliceUnsafe arr (Array.unsafeThaw a)+            arr2 <- MutArray.snocUnsafe arr1 separatorWord+            arr3 <- MutArray.spliceUnsafe arr2 (Array.unsafeThaw b)+            return (Array.unsafeFreeze arr3)+        in Path newArr++-- The only safety we need for paths is: (1) The first path can only be a Dir+-- type path, and (2) second path can only be a Rel path.+-- Can this be coerced to create unsafe versions?++-- | Extend a directory path by appending a relative path to it. This is the+-- equivalent to the @</>@ operator from the @filepath@ package.+{-# INLINE extendDir #-}+extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) =>+    (a (Dir Path)) -> Rel b -> a b+extendDir a (Rel b) =+    fromPathUnsafe $ extendPath (toPath a) (toPath b)
src/Streamly/Internal/Unicode/Stream.hs view
@@ -28,6 +28,7 @@     , decodeUtf8     , decodeUtf8'     , decodeUtf8_+    , decodeUtf16le'      -- ** Resumable UTF-8 Decoding     , DecodeError(..)@@ -55,6 +56,7 @@     , encodeUtf8'     , encodeUtf8_     , encodeStrings+    , encodeUtf16le'     {-     -- * Operations on character strings     , strip -- (dropAround isSpace)@@ -97,7 +99,7 @@ #if MIN_VERSION_base(4,17,0) import Data.Char (generalCategory, GeneralCategory(Space)) #endif-import Data.Word (Word8)+import Data.Word (Word8, Word16) import Foreign.Marshal.Alloc (mallocBytes) import Foreign.Storable (Storable(..)) #ifndef __GHCJS__@@ -109,7 +111,7 @@ import GHC.Ptr (Ptr (..), plusPtr) import System.IO.Unsafe (unsafePerformIO) import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.MutArray.Type (MutByteArray)+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray) import Streamly.Internal.Data.Fold (Fold) import Streamly.Internal.Data.Parser (Parser) import Streamly.Internal.Data.Stream (Stream)@@ -677,6 +679,19 @@ decodeUtf8Lax = decodeUtf8  -------------------------------------------------------------------------------+-- Decoding Utf16+-------------------------------------------------------------------------------++-- | Decode a UTF-16 little endian encoded bytestream to a stream of Unicode+-- characters. The function throws an error if an invalid codepoint is+-- encountered.+--+-- /Unimplemented/+{-# INLINE decodeUtf16le' #-}+decodeUtf16le' :: Stream m Word16 -> Stream m Char+decodeUtf16le' = undefined++------------------------------------------------------------------------------- -- Decoding Array Streams ------------------------------------------------------------------------------- @@ -945,6 +960,18 @@ {-# INLINE encodeUtf8Lax #-} encodeUtf8Lax :: Monad m => Stream m Char -> Stream m Word8 encodeUtf8Lax = encodeUtf8++-------------------------------------------------------------------------------+-- Encoding to Utf16+-------------------------------------------------------------------------------++-- | Encode a stream of Unicode characters to a UTF-16 little endian encoded+-- bytestream.+--+-- /Unimplemented/+{-# INLINE encodeUtf16le' #-}+encodeUtf16le' :: Stream m Char -> Stream m Word16+encodeUtf16le' = undefined  ------------------------------------------------------------------------------- -- Decoding string literals
streamly-core.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.2 name:               streamly-core-version:            0.2.1+version:            0.2.2 synopsis:           Streaming, parsers, arrays, serialization and more description:   For upgrading to streamly-0.9.0+ please read the@@ -116,6 +116,7 @@     docs/*.md     docs/ApiChangelogs/0.1.0.txt     docs/ApiChangelogs/0.1.0-0.2.0.txt+    docs/ApiChangelogs/0.2.0-0.2.2.txt  source-repository head     type: git@@ -372,6 +373,7 @@                      , Streamly.Internal.Unicode.Array                       -- Filesystem/IO+                     , Streamly.Internal.FileSystem.Path                      , Streamly.Internal.FileSystem.Handle                      , Streamly.Internal.FileSystem.File                      , Streamly.Internal.FileSystem.Dir