@@ -76,6 +76,8 @@ module Streamly.Internal.Data.MutArray.Type
7676 , fromStreamD
7777 , fromPureStreamN
7878 , fromPureStream
79+ , fromByteStr#
80+ , fromPtrN
7981
8082 -- ** Random writes
8183 , putIndex
@@ -173,6 +175,7 @@ module Streamly.Internal.Data.MutArray.Type
173175 , castUnsafe
174176 , asBytes
175177 , asPtrUnsafe
178+ , asUnpinnedPtrUnsafe
176179
177180 -- ** Folding
178181 , foldl'
@@ -255,7 +258,7 @@ import GHC.Base
255258 , copyMutableByteArray#
256259 )
257260import GHC.Base (noinline)
258−import GHC.Exts (unsafeCoerce#)
261+import GHC.Exts (unsafeCoerce#, Addr#)
259262import GHC.Ptr (Ptr(..))
260263
261264import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -283,6 +286,7 @@ import Prelude hiding
283286-- Foreign helpers
284287-------------------------------------------------------------------------------
285288
289+-- NOTE: Have to be "ccall unsafe" so that we can pass unpinned memory to these
286290foreign import ccall unsafe "string.h memcpy" c_memcpy
287291 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
288292
@@ -292,6 +296,9 @@ foreign import ccall unsafe "string.h memchr" c_memchr
292296foreign import ccall unsafe "string.h memcmp" c_memcmp
293297 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
294298
299+foreign import ccall unsafe "string.h strlen" c_strlen
300+ :: Ptr Word8 -> IO CSize
301+
295302-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
296303-- how many elements of that type will completely fit in those bytes.
297304--
@@ -2042,6 +2049,32 @@ fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
20422049fromPureStream xs =
20432050 liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
20442051
2052+{-# INLINABLE fromPtrN #-}
2053+fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
2054+fromPtrN len addr = do
2055+ -- memcpy is better than stream copy when the size is known.
2056+ -- XXX We can implement a stream copy in a similar way by streaming Word64
2057+ -- first and then remaining Word8.
2058+ arr <- new len
2059+ _ <- asUnpinnedPtrUnsafe arr
2060+ (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
2061+ return (arr {arrEnd = len})
2062+
2063+{-# INLINABLE fromByteStr# #-}
2064+fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
2065+fromByteStr# addr = do
2066+ -- It is better to count the size first and allocate exact space.
2067+ -- Also, memcpy is better than stream copy when the size is known.
2068+ -- C strlen compares 4 bytes at a time, so is better than the stream
2069+ -- version. https://github.com/bminor/glibc/blob/master/string/strlen.c
2070+ -- XXX We can possibly use a stream of Word64 to do the same.
2071+ -- fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
2072+ len <- liftIO $ c_strlen (Ptr addr)
2073+ let lenInt = fromIntegral len
2074+ arr <- new lenInt
2075+ _ <- asUnpinnedPtrUnsafe arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2076+ return (arr {arrEnd = lenInt})
2077+
20452078-------------------------------------------------------------------------------
20462079-- convert stream to a single array
20472080-------------------------------------------------------------------------------
@@ -2379,6 +2412,12 @@ asPtrUnsafe arr f =
23792412 Unboxed.asPtrUnsafe
23802413 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
23812414
2415+{-# INLINE asUnpinnedPtrUnsafe #-}
2416+asUnpinnedPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2417+asUnpinnedPtrUnsafe arr f =
2418+ Unboxed.asUnpinnedPtrUnsafe
2419+ (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2420+
23822421-------------------------------------------------------------------------------
23832422-- Equality
23842423-------------------------------------------------------------------------------