From 57173e0e128d8faf9865d6eeeb0fc7af3502b750 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Fri, 29 Jul 2022 15:27:37 -0400 Subject: [PATCH 01/13] Avoid 'withForeignPtr' via 'createf' and variants --- Data/ByteString.hs | 117 ++++++++------- Data/ByteString/Builder/Internal.hs | 4 +- Data/ByteString/Char8.hs | 10 +- Data/ByteString/Internal.hs | 222 +++++++++++++++++++--------- Data/ByteString/Lazy.hs | 8 +- Data/ByteString/Lazy/Internal.hs | 10 +- 6 files changed, 230 insertions(+), 141 deletions(-) diff --git a/Data/ByteString.hs b/Data/ByteString.hs index bcab21170..093c22b31 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -267,7 +267,6 @@ import GHC.IO (unsafePerformIO, unsafeDupablePerformIO) import GHC.Foreign (newCStringLen, peekCStringLen) import GHC.Stack.Types (HasCallStack) import Data.Char (ord) -import Foreign.Marshal.Utils (copyBytes) import GHC.Base (build) import GHC.Word hiding (Word8) @@ -375,16 +374,16 @@ infixl 5 `snoc` -- | /O(n)/ 'cons' is analogous to (:) for lists, but of different -- complexity, as it requires making a copy. cons :: Word8 -> ByteString -> ByteString -cons c (BS x l) = unsafeCreate (l+1) $ \p -> unsafeWithForeignPtr x $ \f -> do - poke p c - memcpy (p `plusPtr` 1) f l +cons c (BS x l) = unsafeCreatef (l+1) $ \p -> do + pokefp p c + memcpyf (p `plusForeignPtr` 1) x l {-# INLINE cons #-} -- | /O(n)/ Append a byte to the end of a 'ByteString' snoc :: ByteString -> Word8 -> ByteString -snoc (BS x l) c = unsafeCreate (l+1) $ \p -> unsafeWithForeignPtr x $ \f -> do - memcpy p f l - poke (p `plusPtr` l) c +snoc (BS x l) c = unsafeCreatef (l+1) $ \p -> do + memcpyf p x l + pokefp (p `plusForeignPtr` l) c {-# INLINE snoc #-} -- | /O(1)/ Extract the first element of a ByteString, which must be non-empty. @@ -459,8 +458,7 @@ append = mappend -- | /O(n)/ 'map' @f xs@ is the ByteString obtained by applying @f@ to each -- element of @xs@. map :: (Word8 -> Word8) -> ByteString -> ByteString -map f (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \srcPtr -> - create len $ \dstPtr -> m srcPtr dstPtr +map f (BS srcPtr len) = unsafeCreatef len $ \dstPtr -> m srcPtr dstPtr where m !p1 !p2 = map_ 0 where @@ -468,15 +466,17 @@ map f (BS fp len) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \srcPtr - map_ !n | n >= len = return () | otherwise = do - x <- peekByteOff p1 n - pokeByteOff p2 n (f x) + x <- peekfpByteOff p1 n + pokefpByteOff p2 n (f x) map_ (n+1) {-# INLINE map #-} -- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. reverse :: ByteString -> ByteString -reverse (BS x l) = unsafeCreate l $ \p -> unsafeWithForeignPtr x $ \f -> - c_reverse p f (fromIntegral l) +reverse (BS x l) = unsafeCreatef l $ \fp -> + unsafeWithForeignPtr fp $ \p -> + unsafeWithForeignPtr x $ \f -> + c_reverse p f (fromIntegral l) -- | /O(n)/ The 'intersperse' function takes a 'Word8' and a -- 'ByteString' and \`intersperses\' that byte between the elements of @@ -485,8 +485,10 @@ reverse (BS x l) = unsafeCreate l $ \p -> unsafeWithForeignPtr x $ \f -> intersperse :: Word8 -> ByteString -> ByteString intersperse c ps@(BS x l) | length ps < 2 = ps - | otherwise = unsafeCreate (2*l-1) $ \p -> unsafeWithForeignPtr x $ \f -> - c_intersperse p f (fromIntegral l) c + | otherwise = unsafeCreatef (2*l-1) $ \fp -> + unsafeWithForeignPtr fp $ \p -> + unsafeWithForeignPtr x $ \f -> + c_intersperse p f (fromIntegral l) c -- | The 'transpose' function transposes the rows and columns of its -- 'ByteString' argument. @@ -765,21 +767,20 @@ scanl -- ^ input of length n -> ByteString -- ^ output of length n+1 -scanl f v = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> +scanl f v = \(BS a len) -> unsafeCreatef (len+1) $ \q -> do -- see fold inlining - create (len+1) $ \q -> do - poke q v + pokefp q v let go src dst = scanl_ v 0 where scanl_ !z !n | n >= len = return () | otherwise = do - x <- peekByteOff src n + x <- peekfpByteOff src n let z' = f z x - pokeByteOff dst n z' + pokefpByteOff dst n z' scanl_ z' (n+1) - go a (q `plusPtr` 1) + go a (q `plusForeignPtr` 1) {-# INLINE scanl #-} -- | 'scanl1' is a variant of 'scanl' that has no starting value argument. @@ -810,19 +811,18 @@ scanr -- ^ input of length n -> ByteString -- ^ output of length n+1 -scanr f v = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> +scanr f v = \(BS a len) -> unsafeCreatef (len+1) $ \b -> do -- see fold inlining - create (len+1) $ \b -> do - poke (b `plusPtr` len) v + pokefpByteOff b len v let go p q = scanr_ v (len-1) where scanr_ !z !n | n < 0 = return () | otherwise = do - x <- peekByteOff p n + x <- peekfpByteOff p n let z' = f x z - pokeByteOff q n z' + pokefpByteOff q n z' scanr_ z' (n-1) go a b {-# INLINE scanr #-} @@ -846,7 +846,8 @@ scanr1 f ps = case unsnoc ps of replicate :: Int -> Word8 -> ByteString replicate w c | w <= 0 = empty - | otherwise = unsafeCreate w $ \ptr -> + | otherwise = unsafeCreatef w $ \fptr -> + unsafeWithForeignPtr fptr $ \ptr -> void $ memset ptr c (fromIntegral w) {-# INLINE replicate #-} @@ -882,7 +883,7 @@ unfoldr f = concat . unfoldChunk 32 64 unfoldrN :: Int -> (a -> Maybe (Word8, a)) -> a -> (ByteString, Maybe a) unfoldrN i f x0 | i < 0 = (empty, Just x0) - | otherwise = unsafePerformIO $ createAndTrim' i $ \p -> go p x0 0 + | otherwise = unsafePerformIO $ createfAndTrim' i $ \p -> go p x0 0 where go !p !x !n = go' x n where @@ -890,7 +891,7 @@ unfoldrN i f x0 | n' == i = return (0, n', Just x') | otherwise = case f x' of Nothing -> return (0, n', Nothing) - Just (w,x'') -> do pokeByteOff p n' w + Just (w,x'') -> do pokefpByteOff p n' w go' x'' (n'+1) {-# INLINE unfoldrN #-} @@ -1213,19 +1214,16 @@ groupBy k xs = case uncons xs of intercalate :: ByteString -> [ByteString] -> ByteString intercalate _ [] = mempty intercalate _ [x] = x -- This branch exists for laziness, not speed -intercalate (BS fSepPtr sepLen) (BS fhPtr hLen : t) = - unsafeCreate totalLen $ \dstPtr0 -> - unsafeWithForeignPtr fSepPtr $ \sepPtr -> do - unsafeWithForeignPtr fhPtr $ \hPtr -> - memcpy dstPtr0 hPtr hLen +intercalate (BS sepPtr sepLen) (BS hPtr hLen : t) = + unsafeCreatef totalLen $ \dstPtr0 -> do + memcpyf dstPtr0 hPtr hLen let go _ [] = pure () - go dstPtr (BS fChunkPtr chunkLen : chunks) = do - memcpy dstPtr sepPtr sepLen - let destPtr' = dstPtr `plusPtr` sepLen - unsafeWithForeignPtr fChunkPtr $ \chunkPtr -> - memcpy destPtr' chunkPtr chunkLen - go (destPtr' `plusPtr` chunkLen) chunks - go (dstPtr0 `plusPtr` hLen) t + go dstPtr (BS chunkPtr chunkLen : chunks) = do + memcpyf dstPtr sepPtr sepLen + let destPtr' = dstPtr `plusForeignPtr` sepLen + memcpyf destPtr' chunkPtr chunkLen + go (destPtr' `plusForeignPtr` chunkLen) chunks + go (dstPtr0 `plusForeignPtr` hLen) t where totalLen = List.foldl' (\acc chunk -> acc +! sepLen +! length chunk) hLen t (+!) = checkedAdd "intercalate" @@ -1387,7 +1385,9 @@ filter k = \ps@(BS x l) -> if null ps then ps else - unsafePerformIO $ createAndTrim l $ \pOut -> unsafeWithForeignPtr x $ \pIn -> do + unsafePerformIO $ createfAndTrim l $ \fpOut -> + unsafeWithForeignPtr fpOut $ \pOut -> + unsafeWithForeignPtr x $ \pIn -> do let go' pf pt = go pf pt where @@ -1666,7 +1666,7 @@ packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteStri packZipWith f (BS fp l) (BS fq m) = unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> unsafeWithForeignPtr fq $ \b -> - create len $ go a b + createf len $ \dest -> unsafeWithForeignPtr dest $ go a b where go p1 p2 = zipWith_ 0 where @@ -1709,10 +1709,10 @@ tails p | null p = [empty] sort :: ByteString -> ByteString sort (BS input l) -- qsort outperforms counting sort for small arrays - | l <= 20 = unsafeCreate l $ \ptr -> unsafeWithForeignPtr input $ \inp -> do - memcpy ptr inp l - c_sort ptr (fromIntegral l) - | otherwise = unsafeCreate l $ \p -> allocaArray 256 $ \arr -> do + | l <= 20 = unsafeCreatef l $ \destFP -> do + memcpyf destFP input l + unsafeWithForeignPtr destFP $ \dest -> c_sort dest (fromIntegral l) + | otherwise = unsafeCreatef l $ \p -> allocaArray 256 $ \arr -> do _ <- memset (castPtr arr) 0 (256 * fromIntegral (sizeOf (undefined :: CSize))) unsafeWithForeignPtr input (\x -> countOccurrences arr x l) @@ -1721,7 +1721,7 @@ sort (BS input l) go i !ptr = do n <- peekElemOff arr i when (n /= 0) $ void $ memset ptr (fromIntegral i) n go (i + 1) (ptr `plusPtr` fromIntegral n) - go 0 p + unsafeWithForeignPtr p (go 0) where -- Count the number of occurrences of each byte. -- Used by 'sort' @@ -1771,8 +1771,8 @@ packCString cstr = do -- The @ByteString@ is a normal Haskell value and will be managed on the -- Haskell heap. packCStringLen :: CStringLen -> IO ByteString -packCStringLen (cstr, len) | len >= 0 = create len $ \p -> - memcpy p (castPtr cstr) len +packCStringLen (cstr, len) | len >= 0 = createf len $ \fp -> + unsafeWithForeignPtr fp $ \p -> memcpy p (castPtr cstr) len packCStringLen (_, len) = moduleErrorIO "packCStringLen" ("negative length: " ++ show len) @@ -1785,8 +1785,7 @@ packCStringLen (_, len) = -- is needed in the rest of the program. -- copy :: ByteString -> ByteString -copy (BS x l) = unsafeCreate l $ \p -> unsafeWithForeignPtr x $ \f -> - memcpy p f l +copy (BS x l) = unsafeCreatef l $ \p -> memcpyf p x l -- --------------------------------------------------------------------- -- Line IO @@ -1845,8 +1844,9 @@ hGetLine h = mkPS :: RawBuffer Word8 -> Int -> Int -> IO ByteString mkPS buf start end = - create len $ \p -> - withRawBuffer buf $ \pbuf -> copyBytes p (pbuf `plusPtr` start) len + createf len $ \fp -> + unsafeWithForeignPtr fp $ \p -> + withRawBuffer buf $ \pbuf -> memcpy p (pbuf `plusPtr` start) len where len = end - start @@ -1899,7 +1899,8 @@ putStr = hPut stdout -- hGet :: Handle -> Int -> IO ByteString hGet h i - | i > 0 = createAndTrim i $ \p -> hGetBuf h p i + | i > 0 = createfAndTrim i $ \fp -> + unsafeWithForeignPtr fp $ \p -> hGetBuf h p i | i == 0 = return empty | otherwise = illegalBufferSize h "hGet" i @@ -1913,7 +1914,8 @@ hGet h i -- hGetNonBlocking :: Handle -> Int -> IO ByteString hGetNonBlocking h i - | i > 0 = createAndTrim i $ \p -> hGetBufNonBlocking h p i + | i > 0 = createfAndTrim i $ \fp -> + unsafeWithForeignPtr fp $ \p -> hGetBufNonBlocking h p i | i == 0 = return empty | otherwise = illegalBufferSize h "hGetNonBlocking" i @@ -1924,7 +1926,8 @@ hGetNonBlocking h i -- hGetSome :: Handle -> Int -> IO ByteString hGetSome hh i - | i > 0 = createAndTrim i $ \p -> hGetBufSome hh p i + | i > 0 = createfAndTrim i $ \fp -> + unsafeWithForeignPtr fp $ \p -> hGetBufSome hh p i | i == 0 = return empty | otherwise = illegalBufferSize hh "hGetSome" i diff --git a/Data/ByteString/Builder/Internal.hs b/Data/ByteString/Builder/Internal.hs index 800476154..0fdf6fa75 100644 --- a/Data/ByteString/Builder/Internal.hs +++ b/Data/ByteString/Builder/Internal.hs @@ -1099,8 +1099,8 @@ buildStepToCIOS (AllocationStrategy nextBuffer bufSize trim) = wrapChunk !op' mkCIOS | chunkSize == 0 = mkCIOS True | trim chunkSize size = do - bs <- S.create chunkSize $ \pbuf' -> - copyBytes pbuf' pbuf chunkSize + bs <- S.createf chunkSize $ \fpbuf' -> + S.memcpyf fpbuf' fpbuf chunkSize -- FIXME: We could reuse the trimmed buffer here. return $ Yield1 bs (mkCIOS False) | otherwise = diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs index 9b06468d3..7ed19351f 100644 --- a/Data/ByteString/Char8.hs +++ b/Data/ByteString/Char8.hs @@ -975,11 +975,11 @@ unlines = \li -> let (+!) = checkedAdd "Char8.unlines" go [] _ = pure () - go (BS srcFP len : srcs) dest = do - unsafeWithForeignPtr srcFP $ \src -> memcpy dest src len - pokeElemOff dest len (c2w '\n') - go srcs $ dest `plusPtr` (len + 1) - in unsafeCreate totLen (go li) + go (BS src len : srcs) dest = do + memcpyf dest src len + pokefpByteOff dest len (c2w '\n') + go srcs $ dest `plusForeignPtr` (len + 1) + in unsafeCreatef totLen (go li) -- | 'words' breaks a ByteString up into a list of words, which -- were delimited by Chars representing white space. diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index 5fc8222a7..e1102f3aa 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -48,6 +48,14 @@ module Data.ByteString.Internal ( -- * Low level imperative construction empty, + createf, + createfUptoN, + createfUptoN', + createfAndTrim, + createfAndTrim', + unsafeCreatef, + unsafeCreatefUptoN, + unsafeCreatefUptoN', create, createUptoN, createUptoN', @@ -66,6 +74,12 @@ module Data.ByteString.Internal ( -- * Utilities nullForeignPtr, + peekfp, + pokefp, + peekfpByteOff, + pokefpByteOff, + minusForeignPtr, + memcpyf, SizeOverflowException, overflowError, checkedAdd, @@ -105,7 +119,7 @@ import qualified Data.List as List import Control.Monad (void) import Foreign.ForeignPtr (ForeignPtr, withForeignPtr) -import Foreign.Ptr (Ptr, FunPtr, plusPtr, minusPtr) +import Foreign.Ptr (Ptr, FunPtr, plusPtr) import Foreign.Storable (Storable(..)) import Foreign.C.Types (CInt(..), CSize(..)) import Foreign.C.String (CString) @@ -132,7 +146,7 @@ import Data.Data (Data(..), mkNoRepType) import GHC.Base (nullAddr#,realWorld#,unsafeChr) import GHC.Exts (IsList(..)) import GHC.CString (unpackCString#) -import GHC.Prim (Addr#) +import GHC.Exts (Addr#, minusAddr#) #define TIMES_INT_2_AVAILABLE MIN_VERSION_ghc_prim(0,7,0) #if TIMES_INT_2_AVAILABLE @@ -171,6 +185,8 @@ import GHC.Types (Int (..)) #if MIN_VERSION_base(4,15,0) import GHC.ForeignPtr (unsafeWithForeignPtr) +#else +import GHC.ForeignPtr (withForeignPtr) #endif import qualified Language.Haskell.TH.Lib as TH @@ -203,6 +219,24 @@ plusForeignPtr (ForeignPtr addr guts) (I# offset) = ForeignPtr (plusAddr# addr o #-} #endif +minusForeignPtr :: ForeignPtr a -> ForeignPtr b -> Int +minusForeignPtr (ForeignPtr addr1 _) (ForeignPtr addr2 _) + = I# (minusAddr# addr1 addr2) + +peekfp :: Storable a => ForeignPtr a -> IO a +peekfp fp = unsafeWithForeignPtr fp peek + +pokefp :: Storable a => ForeignPtr a -> a -> IO () +pokefp fp val = unsafeWithForeignPtr fp $ \p -> poke p val + +peekfpByteOff :: Storable a => ForeignPtr a -> Int -> IO a +peekfpByteOff fp off = unsafeWithForeignPtr fp $ \p -> + peekByteOff p off + +pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () +pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> + pokeByteOff p off val + -- ----------------------------------------------------------------------------- -- | A space-efficient representation of a 'Word8' vector, supporting many @@ -340,17 +374,17 @@ packChars cs = unsafePackLenChars (List.length cs) cs unsafePackLenBytes :: Int -> [Word8] -> ByteString unsafePackLenBytes len xs0 = - unsafeCreate len $ \p -> go p xs0 + unsafeCreatef len $ \p -> go p xs0 where go !_ [] = return () - go !p (x:xs) = poke p x >> go (p `plusPtr` 1) xs + go !p (x:xs) = pokefp p x >> go (p `plusForeignPtr` 1) xs unsafePackLenChars :: Int -> [Char] -> ByteString unsafePackLenChars len cs0 = - unsafeCreate len $ \p -> go p cs0 + unsafeCreatef len $ \p -> go p cs0 where go !_ [] = return () - go !p (c:cs) = poke p (c2w c) >> go (p `plusPtr` 1) cs + go !p (c:cs) = pokefp p (c2w c) >> go (p `plusForeignPtr` 1) cs -- | /O(n)/ Pack a null-terminated sequence of bytes, pointed to by an @@ -435,20 +469,20 @@ unsafePackLenLiteral len addr# = packUptoLenBytes :: Int -> [Word8] -> (ByteString, [Word8]) packUptoLenBytes len xs0 = - unsafeCreateUptoN' len $ \p0 -> - let p_end = plusPtr p0 len - go !p [] = return (p `minusPtr` p0, []) + unsafeCreatefUptoN' len $ \p0 -> + let p_end = plusForeignPtr p0 len + go !p [] = return (p `minusForeignPtr` p0, []) go !p xs | p == p_end = return (len, xs) - go !p (x:xs) = poke p x >> go (p `plusPtr` 1) xs + go !p (x:xs) = pokefp p x >> go (p `plusForeignPtr` 1) xs in go p0 xs0 packUptoLenChars :: Int -> [Char] -> (ByteString, [Char]) packUptoLenChars len cs0 = - unsafeCreateUptoN' len $ \p0 -> - let p_end = plusPtr p0 len - go !p [] = return (p `minusPtr` p0, []) + unsafeCreatefUptoN' len $ \p0 -> + let p_end = plusForeignPtr p0 len + go !p [] = return (p `minusForeignPtr` p0, []) go !p cs | p == p_end = return (len, cs) - go !p (c:cs) = poke p (c2w c) >> go (p `plusPtr` 1) cs + go !p (c:cs) = pokefp p (c2w c) >> go (p `plusForeignPtr` 1) cs in go p0 cs0 -- Unpacking bytestrings into lists efficiently is a tradeoff: on the one hand @@ -556,10 +590,88 @@ toForeignPtr0 :: ByteString -> (ForeignPtr Word8, Int) -- ^ (ptr, length) toForeignPtr0 (BS ps l) = (ps, l) {-# INLINE toForeignPtr0 #-} +-- | A way of creating ByteStrings outside the IO monad. The @Int@ +-- argument gives the final size of the ByteString. +unsafeCreatef :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString +unsafeCreatef l f = unsafeDupablePerformIO (createf l f) +{-# INLINE unsafeCreatef #-} + +-- | Like 'unsafeCreatef' but instead of giving the final size of the +-- ByteString, it is just an upper bound. The inner action returns +-- the actual size. Unlike 'createfAndTrim' the ByteString is not +-- reallocated if the final size is less than the estimated size. +unsafeCreatefUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString +unsafeCreatefUptoN l f = unsafeDupablePerformIO (createfUptoN l f) +{-# INLINE unsafeCreatefUptoN #-} + +unsafeCreatefUptoN' + :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> (ByteString, a) +unsafeCreatefUptoN' l f = unsafeDupablePerformIO (createfUptoN' l f) +{-# INLINE unsafeCreatefUptoN' #-} + +-- | Create ByteString of size @l@ and use action @f@ to fill its contents. +createf :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString +createf l action = do + fp <- mallocByteString l + action fp + return $! BS fp l +{-# INLINE createf #-} + +-- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' +-- starting at the given 'Ptr' and returns the actual utilized length, +-- @`createfUptoN'` l f@ returns the filled 'ByteString'. +createfUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createfUptoN l action = do + fp <- mallocByteString l + l' <- action fp + assert (l' <= l) $ return $! BS fp l' +{-# INLINE createfUptoN #-} + +-- | Like 'createfUptoN', but also returns an additional value created by the +-- action. +createfUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) +createfUptoN' l action = do + fp <- mallocByteString l + (l', res) <- action fp + assert (l' <= l) $ return (BS fp l', res) +{-# INLINE createfUptoN' #-} + +-- | Given the maximum size needed and a function to make the contents +-- of a ByteString, createfAndTrim makes the 'ByteString'. The generating +-- function is required to return the actual final size (<= the maximum +-- size), and the resulting byte array is reallocated to this size. +-- +-- createfAndTrim is the main mechanism for creating custom, efficient +-- ByteString functions, using Haskell or C functions to fill the space. +-- +createfAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createfAndTrim l action = do + fp <- mallocByteString l + l' <- action fp + if assert (0 <= l' && l' <= l) $ l' >= l + then return $! BS fp l + else createf l' $ \fp' -> memcpyf fp' fp l' +{-# INLINE createfAndTrim #-} + +createfAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) +createfAndTrim' l action = do + fp <- mallocByteString l + (off, l', res) <- action fp + if assert (0 <= l' && l' <= l) $ l' >= l + then return (BS fp l, res) + else do ps <- createf l' $ \fp' -> + memcpyf fp' (fp `plusForeignPtr` off) l' + return (ps, res) +{-# INLINE createfAndTrim' #-} + + +wrapAction :: (Ptr Word8 -> IO res) -> ForeignPtr Word8 -> IO res +wrapAction = flip withForeignPtr + -- | A way of creating ByteStrings outside the IO monad. The @Int@ -- argument gives the final size of the ByteString. unsafeCreate :: Int -> (Ptr Word8 -> IO ()) -> ByteString -unsafeCreate l f = unsafeDupablePerformIO (create l f) +unsafeCreate l f = unsafeCreatef l (wrapAction f) {-# INLINE unsafeCreate #-} -- | Like 'unsafeCreate' but instead of giving the final size of the @@ -567,32 +679,24 @@ unsafeCreate l f = unsafeDupablePerformIO (create l f) -- the actual size. Unlike 'createAndTrim' the ByteString is not -- reallocated if the final size is less than the estimated size. unsafeCreateUptoN :: Int -> (Ptr Word8 -> IO Int) -> ByteString -unsafeCreateUptoN l f = unsafeDupablePerformIO (createUptoN l f) +unsafeCreateUptoN l f = unsafeCreatefUptoN l (wrapAction f) {-# INLINE unsafeCreateUptoN #-} -- | @since 0.10.12.0 unsafeCreateUptoN' :: Int -> (Ptr Word8 -> IO (Int, a)) -> (ByteString, a) -unsafeCreateUptoN' l f = unsafeDupablePerformIO (createUptoN' l f) +unsafeCreateUptoN' l f = unsafeCreatefUptoN' l (wrapAction f) {-# INLINE unsafeCreateUptoN' #-} -- | Create ByteString of size @l@ and use action @f@ to fill its contents. create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString -create l action = do - fp <- mallocByteString l - -- Cannot use unsafeWithForeignPtr, because action can diverge - withForeignPtr fp $ \p -> action p - return $! BS fp l +create l action = createf l (wrapAction action) {-# INLINE create #-} -- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' -- starting at the given 'Ptr' and returns the actual utilized length, -- @`createUptoN'` l f@ returns the filled 'ByteString'. createUptoN :: Int -> (Ptr Word8 -> IO Int) -> IO ByteString -createUptoN l action = do - fp <- mallocByteString l - -- Cannot use unsafeWithForeignPtr, because action can diverge - l' <- withForeignPtr fp $ \p -> action p - assert (l' <= l) $ return $! BS fp l' +createUptoN l action = createfUptoN l (wrapAction action) {-# INLINE createUptoN #-} -- | Like 'createUptoN', but also returns an additional value created by the @@ -600,11 +704,7 @@ createUptoN l action = do -- -- @since 0.10.12.0 createUptoN' :: Int -> (Ptr Word8 -> IO (Int, a)) -> IO (ByteString, a) -createUptoN' l action = do - fp <- mallocByteString l - -- Cannot use unsafeWithForeignPtr, because action can diverge - (l', res) <- withForeignPtr fp $ \p -> action p - assert (l' <= l) $ return (BS fp l', res) +createUptoN' l action = createfUptoN' l (wrapAction action) {-# INLINE createUptoN' #-} -- | Given the maximum size needed and a function to make the contents @@ -612,33 +712,15 @@ createUptoN' l action = do -- function is required to return the actual final size (<= the maximum -- size), and the resulting byte array is reallocated to this size. -- --- createAndTrim is the main mechanism for creating custom, efficient --- ByteString functions, using Haskell or C functions to fill the space. --- createAndTrim :: Int -> (Ptr Word8 -> IO Int) -> IO ByteString -createAndTrim l action = do - fp <- mallocByteString l - -- Cannot use unsafeWithForeignPtr, because action can diverge - withForeignPtr fp $ \p -> do - l' <- action p - if assert (l' <= l) $ l' >= l - then return $! BS fp l - else create l' $ \p' -> memcpy p' p l' +createAndTrim l action = createfAndTrim l (wrapAction action) {-# INLINE createAndTrim #-} createAndTrim' :: Int -> (Ptr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) -createAndTrim' l action = do - fp <- mallocByteString l - -- Cannot use unsafeWithForeignPtr, because action can diverge - withForeignPtr fp $ \p -> do - (off, l', res) <- action p - if assert (l' <= l) $ l' >= l - then return (BS fp l, res) - else do ps <- create l' $ \p' -> - memcpy p' (p `plusPtr` off) l' - return (ps, res) +createAndTrim' l action = createfAndTrim' l (wrapAction action) {-# INLINE createAndTrim' #-} + -- | Wrapper of 'Foreign.ForeignPtr.mallocForeignPtrBytes' with faster implementation for GHC -- mallocByteString :: Int -> IO (ForeignPtr a) @@ -678,10 +760,10 @@ append :: ByteString -> ByteString -> ByteString append (BS _ 0) b = b append a (BS _ 0) = a append (BS fp1 len1) (BS fp2 len2) = - unsafeCreate (checkedAdd "append" len1 len2) $ \destptr1 -> do - let destptr2 = destptr1 `plusPtr` len1 - unsafeWithForeignPtr fp1 $ \p1 -> memcpy destptr1 p1 len1 - unsafeWithForeignPtr fp2 $ \p2 -> memcpy destptr2 p2 len2 + unsafeCreatef (checkedAdd "append" len1 len2) $ \destptr1 -> do + let destptr2 = destptr1 `plusForeignPtr` len1 + memcpyf destptr1 fp1 len1 + memcpyf destptr2 fp2 len2 concat :: [ByteString] -> ByteString concat = \bss0 -> goLen0 bss0 bss0 @@ -717,14 +799,14 @@ concat = \bss0 -> goLen0 bss0 bss0 goLen bss0 !total (BS _ len:bss) = goLen bss0 total' bss where total' = checkedAdd "concat" total len goLen bss0 total [] = - unsafeCreate total $ \ptr -> goCopy bss0 ptr + unsafeCreatef total $ \ptr -> goCopy bss0 ptr -- Copy the data goCopy [] !_ = return () goCopy (BS _ 0 :bss) !ptr = goCopy bss ptr goCopy (BS fp len:bss) !ptr = do - unsafeWithForeignPtr fp $ \p -> memcpy ptr p len - goCopy bss (ptr `plusPtr` len) + memcpyf ptr fp len + goCopy bss (ptr `plusForeignPtr` len) {-# NOINLINE concat #-} {-# RULES @@ -767,24 +849,24 @@ stimesNonNegativeInt n (BS fp len) | n == 0 = empty | n == 1 = BS fp len | len == 0 = empty - | len == 1 = unsafeCreate n $ \destptr -> + | len == 1 = unsafeCreatef n $ \destfptr -> unsafeWithForeignPtr fp $ \p -> do byte <- peek p - void $ memset destptr byte (fromIntegral n) - | otherwise = unsafeCreate size $ \destptr -> - unsafeWithForeignPtr fp $ \p -> do - memcpy destptr p len + void $ unsafeWithForeignPtr destfptr $ \destptr -> + memset destptr byte (fromIntegral n) + | otherwise = unsafeCreatef size $ \destptr -> do + memcpyf destptr fp len fillFrom destptr len where size = checkedMultiply "stimes" n len halfSize = (size - 1) `div` 2 -- subtraction and division won't overflow - fillFrom :: Ptr Word8 -> Int -> IO () + fillFrom :: ForeignPtr Word8 -> Int -> IO () fillFrom destptr copied | copied <= halfSize = do - memcpy (destptr `plusPtr` copied) destptr copied + memcpyf (destptr `plusForeignPtr` copied) destptr copied fillFrom destptr (copied * 2) - | otherwise = memcpy (destptr `plusPtr` copied) destptr (size - copied) + | otherwise = memcpyf (destptr `plusForeignPtr` copied) destptr (size - copied) ------------------------------------------------------------------------ @@ -938,6 +1020,10 @@ foreign import ccall unsafe "string.h memcpy" c_memcpy memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () memcpy p q s = void $ c_memcpy p q (fromIntegral s) +memcpyf :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () +memcpyf fp fq s = unsafeWithForeignPtr fp $ \p -> + unsafeWithForeignPtr fq $ \q -> memcpy p q s + {- foreign import ccall unsafe "string.h memmove" c_memmove :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8) diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs index f046d61e5..316147111 100644 --- a/Data/ByteString/Lazy.hs +++ b/Data/ByteString/Lazy.hs @@ -434,9 +434,11 @@ intersperse w (Chunk c cs) = Chunk (S.intersperse w c) (foldrChunks (Chunk . intersperse') Empty cs) where intersperse' :: P.ByteString -> P.ByteString intersperse' (S.BS fp l) = - S.unsafeCreate (2*l) $ \p' -> S.unsafeWithForeignPtr fp $ \p -> do - poke p' w - S.c_intersperse (p' `plusPtr` 1) p (fromIntegral l) w + S.unsafeCreatef (2*l) $ \fp' -> + S.unsafeWithForeignPtr fp' $ \p' -> + S.unsafeWithForeignPtr fp $ \p -> do + poke p' w + S.c_intersperse (p' `plusPtr` 1) p (fromIntegral l) w -- | The 'transpose' function transposes the rows and columns of its -- 'ByteString' argument. diff --git a/Data/ByteString/Lazy/Internal.hs b/Data/ByteString/Lazy/Internal.hs index 63dc3f670..1fd7bbf28 100644 --- a/Data/ByteString/Lazy/Internal.hs +++ b/Data/ByteString/Lazy/Internal.hs @@ -52,7 +52,6 @@ import Prelude hiding (concat) import qualified Data.ByteString.Internal as S import Data.Word (Word8) -import Foreign.Ptr (plusPtr) import Foreign.Storable (Storable(sizeOf)) #if MIN_VERSION_base(4,13,0) @@ -313,14 +312,13 @@ toStrict = \cs -> goLen0 cs cs goLen cs0 !total (Chunk (S.BS _ cl) cs) = goLen cs0 (S.checkedAdd "Lazy.toStrict" total cl) cs goLen cs0 total Empty = - S.unsafeCreate total $ \ptr -> goCopy cs0 ptr + S.unsafeCreatef total $ \ptr -> goCopy cs0 ptr -- Copy the data goCopy Empty !_ = return () goCopy (Chunk (S.BS _ 0 ) cs) !ptr = goCopy cs ptr - goCopy (Chunk (S.BS fp len) cs) !ptr = - S.unsafeWithForeignPtr fp $ \p -> do - S.memcpy ptr p len - goCopy cs (ptr `plusPtr` len) + goCopy (Chunk (S.BS fp len) cs) !ptr = do + S.memcpyf ptr fp len + goCopy cs (ptr `S.plusForeignPtr` len) -- See the comment on Data.ByteString.Internal.concat for some background on -- this implementation. From 5c2e72d12da41185267456799ad6a70433e451d8 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Fri, 29 Jul 2022 19:30:31 -0400 Subject: [PATCH 02/13] remove stupid/wrong temporary cpp --- Data/ByteString/Internal.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index e1102f3aa..275652450 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -185,8 +185,6 @@ import GHC.Types (Int (..)) #if MIN_VERSION_base(4,15,0) import GHC.ForeignPtr (unsafeWithForeignPtr) -#else -import GHC.ForeignPtr (withForeignPtr) #endif import qualified Language.Haskell.TH.Lib as TH From 6462e4f4ad684fa1f1e61ae282a1ff769ebfb50a Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sat, 6 Aug 2022 20:06:52 -0400 Subject: [PATCH 03/13] Move new utilities into D.B.Internal.Utils --- Data/ByteString.hs | 1 + Data/ByteString/Builder/Internal.hs | 1 + Data/ByteString/Char8.hs | 1 + Data/ByteString/Internal.hs | 153 +---------------------- Data/ByteString/Internal.hs-boot | 11 ++ Data/ByteString/Internal/Utils.hs | 180 ++++++++++++++++++++++++++++ Data/ByteString/Lazy.hs | 1 + Data/ByteString/Lazy/Internal.hs | 1 + bytestring.cabal | 1 + 9 files changed, 201 insertions(+), 149 deletions(-) create mode 100644 Data/ByteString/Internal.hs-boot create mode 100644 Data/ByteString/Internal/Utils.hs diff --git a/Data/ByteString.hs b/Data/ByteString.hs index 093c22b31..3ffff1465 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -231,6 +231,7 @@ import Prelude hiding (reverse,head,tail,last,init,null import Data.Bits (finiteBitSize, shiftL, (.|.), (.&.)) import Data.ByteString.Internal +import Data.ByteString.Internal.Utils import Data.ByteString.Lazy.Internal (fromStrict, toStrict) import Data.ByteString.Unsafe diff --git a/Data/ByteString/Builder/Internal.hs b/Data/ByteString/Builder/Internal.hs index 0fdf6fa75..ede5199e4 100644 --- a/Data/ByteString/Builder/Internal.hs +++ b/Data/ByteString/Builder/Internal.hs @@ -134,6 +134,7 @@ import Data.Semigroup (Semigroup((<>))) import qualified Data.ByteString as S import qualified Data.ByteString.Internal as S +import qualified Data.ByteString.Internal.Utils as S import qualified Data.ByteString.Lazy.Internal as L import qualified Data.ByteString.Short.Internal as Sh diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs index 7ed19351f..e2b8e1b33 100644 --- a/Data/ByteString/Char8.hs +++ b/Data/ByteString/Char8.hs @@ -276,6 +276,7 @@ import Data.ByteString (null,length,tail,init,append ) import Data.ByteString.Internal +import Data.ByteString.Internal.Utils import Data.ByteString.ReadInt import Data.ByteString.ReadNat diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index 275652450..8c197b6c1 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -48,14 +48,6 @@ module Data.ByteString.Internal ( -- * Low level imperative construction empty, - createf, - createfUptoN, - createfUptoN', - createfAndTrim, - createfAndTrim', - unsafeCreatef, - unsafeCreatefUptoN, - unsafeCreatefUptoN', create, createUptoN, createUptoN', @@ -74,12 +66,6 @@ module Data.ByteString.Internal ( -- * Utilities nullForeignPtr, - peekfp, - pokefp, - peekfpByteOff, - pokefpByteOff, - minusForeignPtr, - memcpyf, SizeOverflowException, overflowError, checkedAdd, @@ -113,6 +99,7 @@ module Data.ByteString.Internal ( unsafeWithForeignPtr ) where +import Data.ByteString.Internal.Utils import Prelude hiding (concat, null) import qualified Data.List as List @@ -146,7 +133,7 @@ import Data.Data (Data(..), mkNoRepType) import GHC.Base (nullAddr#,realWorld#,unsafeChr) import GHC.Exts (IsList(..)) import GHC.CString (unpackCString#) -import GHC.Exts (Addr#, minusAddr#) +import GHC.Exts (Addr#) #define TIMES_INT_2_AVAILABLE MIN_VERSION_ghc_prim(0,7,0) #if TIMES_INT_2_AVAILABLE @@ -161,18 +148,12 @@ import GHC.Prim ( timesWord2# import Data.Bits (finiteBitSize) #endif -import GHC.IO (IO(IO),unsafeDupablePerformIO) +import GHC.IO (IO(IO)) import GHC.ForeignPtr (ForeignPtr(ForeignPtr) #if __GLASGOW_HASKELL__ < 900 , newForeignPtr_ #endif - , mallocPlainForeignPtrBytes) - -#if MIN_VERSION_base(4,10,0) -import GHC.ForeignPtr (plusForeignPtr) -#else -import GHC.Prim (plusAddr#) -#endif + ) #if __GLASGOW_HASKELL__ >= 811 import GHC.CString (cstringLength#) @@ -198,42 +179,6 @@ unsafeWithForeignPtr = withForeignPtr -- CFILES stuff is Hugs only {-# CFILES cbits/fpstring.c #-} -#if !MIN_VERSION_base(4,10,0) --- |Advances the given address by the given offset in bytes. --- --- The new 'ForeignPtr' shares the finalizer of the original, --- equivalent from a finalization standpoint to just creating another --- reference to the original. That is, the finalizer will not be --- called before the new 'ForeignPtr' is unreachable, nor will it be --- called an additional time due to this call, and the finalizer will --- be called with the same address that it would have had this call --- not happened, *not* the new address. -plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b -plusForeignPtr (ForeignPtr addr guts) (I# offset) = ForeignPtr (plusAddr# addr offset) guts -{-# INLINE [0] plusForeignPtr #-} -{-# RULES -"ByteString plusForeignPtr/0" forall fp . - plusForeignPtr fp 0 = fp - #-} -#endif - -minusForeignPtr :: ForeignPtr a -> ForeignPtr b -> Int -minusForeignPtr (ForeignPtr addr1 _) (ForeignPtr addr2 _) - = I# (minusAddr# addr1 addr2) - -peekfp :: Storable a => ForeignPtr a -> IO a -peekfp fp = unsafeWithForeignPtr fp peek - -pokefp :: Storable a => ForeignPtr a -> a -> IO () -pokefp fp val = unsafeWithForeignPtr fp $ \p -> poke p val - -peekfpByteOff :: Storable a => ForeignPtr a -> Int -> IO a -peekfpByteOff fp off = unsafeWithForeignPtr fp $ \p -> - peekByteOff p off - -pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () -pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> - pokeByteOff p off val -- ----------------------------------------------------------------------------- @@ -588,80 +533,6 @@ toForeignPtr0 :: ByteString -> (ForeignPtr Word8, Int) -- ^ (ptr, length) toForeignPtr0 (BS ps l) = (ps, l) {-# INLINE toForeignPtr0 #-} --- | A way of creating ByteStrings outside the IO monad. The @Int@ --- argument gives the final size of the ByteString. -unsafeCreatef :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString -unsafeCreatef l f = unsafeDupablePerformIO (createf l f) -{-# INLINE unsafeCreatef #-} - --- | Like 'unsafeCreatef' but instead of giving the final size of the --- ByteString, it is just an upper bound. The inner action returns --- the actual size. Unlike 'createfAndTrim' the ByteString is not --- reallocated if the final size is less than the estimated size. -unsafeCreatefUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString -unsafeCreatefUptoN l f = unsafeDupablePerformIO (createfUptoN l f) -{-# INLINE unsafeCreatefUptoN #-} - -unsafeCreatefUptoN' - :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> (ByteString, a) -unsafeCreatefUptoN' l f = unsafeDupablePerformIO (createfUptoN' l f) -{-# INLINE unsafeCreatefUptoN' #-} - --- | Create ByteString of size @l@ and use action @f@ to fill its contents. -createf :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString -createf l action = do - fp <- mallocByteString l - action fp - return $! BS fp l -{-# INLINE createf #-} - --- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' --- starting at the given 'Ptr' and returns the actual utilized length, --- @`createfUptoN'` l f@ returns the filled 'ByteString'. -createfUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString -createfUptoN l action = do - fp <- mallocByteString l - l' <- action fp - assert (l' <= l) $ return $! BS fp l' -{-# INLINE createfUptoN #-} - --- | Like 'createfUptoN', but also returns an additional value created by the --- action. -createfUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) -createfUptoN' l action = do - fp <- mallocByteString l - (l', res) <- action fp - assert (l' <= l) $ return (BS fp l', res) -{-# INLINE createfUptoN' #-} - --- | Given the maximum size needed and a function to make the contents --- of a ByteString, createfAndTrim makes the 'ByteString'. The generating --- function is required to return the actual final size (<= the maximum --- size), and the resulting byte array is reallocated to this size. --- --- createfAndTrim is the main mechanism for creating custom, efficient --- ByteString functions, using Haskell or C functions to fill the space. --- -createfAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString -createfAndTrim l action = do - fp <- mallocByteString l - l' <- action fp - if assert (0 <= l' && l' <= l) $ l' >= l - then return $! BS fp l - else createf l' $ \fp' -> memcpyf fp' fp l' -{-# INLINE createfAndTrim #-} - -createfAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) -createfAndTrim' l action = do - fp <- mallocByteString l - (off, l', res) <- action fp - if assert (0 <= l' && l' <= l) $ l' >= l - then return (BS fp l, res) - else do ps <- createf l' $ \fp' -> - memcpyf fp' (fp `plusForeignPtr` off) l' - return (ps, res) -{-# INLINE createfAndTrim' #-} - wrapAction :: (Ptr Word8 -> IO res) -> ForeignPtr Word8 -> IO res wrapAction = flip withForeignPtr @@ -718,13 +589,6 @@ createAndTrim' :: Int -> (Ptr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) createAndTrim' l action = createfAndTrim' l (wrapAction action) {-# INLINE createAndTrim' #-} - --- | Wrapper of 'Foreign.ForeignPtr.mallocForeignPtrBytes' with faster implementation for GHC --- -mallocByteString :: Int -> IO (ForeignPtr a) -mallocByteString = mallocPlainForeignPtrBytes -{-# INLINE mallocByteString #-} - ------------------------------------------------------------------------ -- Implementations for Eq, Ord and Monoid instances @@ -1012,15 +876,6 @@ foreign import ccall unsafe "string.h memcmp" c_memcmp memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO CInt memcmp p q s = c_memcmp p q (fromIntegral s) -foreign import ccall unsafe "string.h memcpy" c_memcpy - :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8) - -memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () -memcpy p q s = void $ c_memcpy p q (fromIntegral s) - -memcpyf :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () -memcpyf fp fq s = unsafeWithForeignPtr fp $ \p -> - unsafeWithForeignPtr fq $ \q -> memcpy p q s {- foreign import ccall unsafe "string.h memmove" c_memmove diff --git a/Data/ByteString/Internal.hs-boot b/Data/ByteString/Internal.hs-boot new file mode 100644 index 000000000..d537ecf90 --- /dev/null +++ b/Data/ByteString/Internal.hs-boot @@ -0,0 +1,11 @@ +-- allow Data.ByteString.Internal.Utils to use the BS constructor + +module Data.ByteString.Internal ( + ByteString (BS) +) where + +import Foreign.ForeignPtr (ForeignPtr) +import Data.Word (Word8) + +data ByteString = BS {-# UNPACK #-} !(ForeignPtr Word8) -- payload + {-# UNPACK #-} !Int -- length diff --git a/Data/ByteString/Internal/Utils.hs b/Data/ByteString/Internal/Utils.hs new file mode 100644 index 000000000..1105b8004 --- /dev/null +++ b/Data/ByteString/Internal/Utils.hs @@ -0,0 +1,180 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE MagicHash #-} + +module Data.ByteString.Internal.Utils ( + peekfp, + pokefp, + peekfpByteOff, + pokefpByteOff, + plusForeignPtr, + minusForeignPtr, + memcpyf, + + createf, + createfUptoN, + createfUptoN', + createfAndTrim, + createfAndTrim', + unsafeCreatef, + unsafeCreatefUptoN, + unsafeCreatefUptoN', + mallocByteString, + memcpy, +) where + +import {-# SOURCE #-} Data.ByteString.Internal + +import Control.Exception (assert) +import Control.Monad (void) +import Data.Word (Word8) +import Foreign.C.Types (CSize(..)) +import Foreign.Ptr (Ptr) +import Foreign.Storable (Storable(..)) +import GHC.Exts (Int(I#), minusAddr#) +import GHC.ForeignPtr + ( ForeignPtr(ForeignPtr) + , unsafeWithForeignPtr + , mallocPlainForeignPtrBytes + ) +import GHC.IO (unsafeDupablePerformIO) + +#if MIN_VERSION_base(4,10,0) +import GHC.ForeignPtr (plusForeignPtr) +#else +import GHC.Exts (plusAddr#) +#endif + + +-- Utilities for working with ForeignPtr + +#if !MIN_VERSION_base(4,10,0) +-- |Advances the given address by the given offset in bytes. +-- +-- The new 'ForeignPtr' shares the finalizer of the original, +-- equivalent from a finalization standpoint to just creating another +-- reference to the original. That is, the finalizer will not be +-- called before the new 'ForeignPtr' is unreachable, nor will it be +-- called an additional time due to this call, and the finalizer will +-- be called with the same address that it would have had this call +-- not happened, *not* the new address. +plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b +plusForeignPtr (ForeignPtr addr guts) (I# offset) = ForeignPtr (plusAddr# addr offset) guts +{-# INLINE [0] plusForeignPtr #-} +{-# RULES +"ByteString plusForeignPtr/0" forall fp . + plusForeignPtr fp 0 = fp + #-} +#endif + +minusForeignPtr :: ForeignPtr a -> ForeignPtr b -> Int +minusForeignPtr (ForeignPtr addr1 _) (ForeignPtr addr2 _) + = I# (minusAddr# addr1 addr2) + +peekfp :: Storable a => ForeignPtr a -> IO a +peekfp fp = unsafeWithForeignPtr fp peek + +pokefp :: Storable a => ForeignPtr a -> a -> IO () +pokefp fp val = unsafeWithForeignPtr fp $ \p -> poke p val + +peekfpByteOff :: Storable a => ForeignPtr a -> Int -> IO a +peekfpByteOff fp off = unsafeWithForeignPtr fp $ \p -> + peekByteOff p off + +pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () +pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> + pokeByteOff p off val + + + +-- Utilities for ByteString creation + +-- | Wrapper of 'Foreign.ForeignPtr.mallocForeignPtrBytes' with faster implementation for GHC +-- +mallocByteString :: Int -> IO (ForeignPtr a) +mallocByteString = mallocPlainForeignPtrBytes +{-# INLINE mallocByteString #-} + +-- | A way of creating ByteStrings outside the IO monad. The @Int@ +-- argument gives the final size of the ByteString. +unsafeCreatef :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString +unsafeCreatef l f = unsafeDupablePerformIO (createf l f) +{-# INLINE unsafeCreatef #-} + +-- | Like 'unsafeCreatef' but instead of giving the final size of the +-- ByteString, it is just an upper bound. The inner action returns +-- the actual size. Unlike 'createfAndTrim' the ByteString is not +-- reallocated if the final size is less than the estimated size. +unsafeCreatefUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString +unsafeCreatefUptoN l f = unsafeDupablePerformIO (createfUptoN l f) +{-# INLINE unsafeCreatefUptoN #-} + +unsafeCreatefUptoN' + :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> (ByteString, a) +unsafeCreatefUptoN' l f = unsafeDupablePerformIO (createfUptoN' l f) +{-# INLINE unsafeCreatefUptoN' #-} + +-- | Create ByteString of size @l@ and use action @f@ to fill its contents. +createf :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString +createf l action = do + fp <- mallocByteString l + action fp + return $! BS fp l +{-# INLINE createf #-} + +-- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' +-- starting at the given 'Ptr' and returns the actual utilized length, +-- @`createfUptoN'` l f@ returns the filled 'ByteString'. +createfUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createfUptoN l action = do + fp <- mallocByteString l + l' <- action fp + assert (l' <= l) $ return $! BS fp l' +{-# INLINE createfUptoN #-} + +-- | Like 'createfUptoN', but also returns an additional value created by the +-- action. +createfUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) +createfUptoN' l action = do + fp <- mallocByteString l + (l', res) <- action fp + assert (l' <= l) $ return (BS fp l', res) +{-# INLINE createfUptoN' #-} + +-- | Given the maximum size needed and a function to make the contents +-- of a ByteString, createfAndTrim makes the 'ByteString'. The generating +-- function is required to return the actual final size (<= the maximum +-- size), and the resulting byte array is reallocated to this size. +-- +-- createfAndTrim is the main mechanism for creating custom, efficient +-- ByteString functions, using Haskell or C functions to fill the space. +-- +createfAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createfAndTrim l action = do + fp <- mallocByteString l + l' <- action fp + if assert (0 <= l' && l' <= l) $ l' >= l + then return $! BS fp l + else createf l' $ \fp' -> memcpyf fp' fp l' +{-# INLINE createfAndTrim #-} + +createfAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) +createfAndTrim' l action = do + fp <- mallocByteString l + (off, l', res) <- action fp + if assert (0 <= l' && l' <= l) $ l' >= l + then return (BS fp l, res) + else do ps <- createf l' $ \fp' -> + memcpyf fp' (fp `plusForeignPtr` off) l' + return (ps, res) +{-# INLINE createfAndTrim' #-} + + +foreign import ccall unsafe "string.h memcpy" c_memcpy + :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8) + +memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () +memcpy p q s = void $ c_memcpy p q (fromIntegral s) + +memcpyf :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () +memcpyf fp fq s = unsafeWithForeignPtr fp $ \p -> + unsafeWithForeignPtr fq $ \q -> memcpy p q s diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs index 316147111..a78a694c3 100644 --- a/Data/ByteString/Lazy.hs +++ b/Data/ByteString/Lazy.hs @@ -232,6 +232,7 @@ import qualified Data.Bifunctor as BF import qualified Data.ByteString as P (ByteString) -- type name only import qualified Data.ByteString as S -- S for strict (hmm...) import qualified Data.ByteString.Internal as S +import qualified Data.ByteString.Internal.Utils as S import qualified Data.ByteString.Unsafe as S import qualified Data.ByteString.Lazy.Internal.Deque as D import Data.ByteString.Lazy.Internal diff --git a/Data/ByteString/Lazy/Internal.hs b/Data/ByteString/Lazy/Internal.hs index 1fd7bbf28..ab2000061 100644 --- a/Data/ByteString/Lazy/Internal.hs +++ b/Data/ByteString/Lazy/Internal.hs @@ -50,6 +50,7 @@ module Data.ByteString.Lazy.Internal ( import Prelude hiding (concat) import qualified Data.ByteString.Internal as S +import qualified Data.ByteString.Internal.Utils as S import Data.Word (Word8) import Foreign.Storable (Storable(sizeOf)) diff --git a/bytestring.cabal b/bytestring.cabal index 009aab131..fef8ddf03 100644 --- a/bytestring.cabal +++ b/bytestring.cabal @@ -99,6 +99,7 @@ library Data.ByteString.Builder.RealFloat.D2S Data.ByteString.Builder.RealFloat.Internal Data.ByteString.Builder.RealFloat.TableGenerator + Data.ByteString.Internal.Utils Data.ByteString.Lazy.Internal.Deque Data.ByteString.Lazy.ReadInt Data.ByteString.Lazy.ReadNat From 6df394ea8222beb0f74961f7c3d83a301154417f Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sat, 6 Aug 2022 20:17:59 -0400 Subject: [PATCH 04/13] Move unsafeWithForeignPtr shim into Utils --- Data/ByteString/Internal.hs | 9 --------- Data/ByteString/Internal/Utils.hs | 7 +++++++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index 8c197b6c1..1a4d4cf1c 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -164,18 +164,9 @@ import GHC.Ptr (Ptr(..)) import GHC.Types (Int (..)) -#if MIN_VERSION_base(4,15,0) -import GHC.ForeignPtr (unsafeWithForeignPtr) -#endif - import qualified Language.Haskell.TH.Lib as TH import qualified Language.Haskell.TH.Syntax as TH -#if !MIN_VERSION_base(4,15,0) -unsafeWithForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b -unsafeWithForeignPtr = withForeignPtr -#endif - -- CFILES stuff is Hugs only {-# CFILES cbits/fpstring.c #-} diff --git a/Data/ByteString/Internal/Utils.hs b/Data/ByteString/Internal/Utils.hs index 1105b8004..e641be20a 100644 --- a/Data/ByteString/Internal/Utils.hs +++ b/Data/ByteString/Internal/Utils.hs @@ -9,6 +9,7 @@ module Data.ByteString.Internal.Utils ( plusForeignPtr, minusForeignPtr, memcpyf, + unsafeWithForeignPtr, createf, createfUptoN, @@ -33,7 +34,9 @@ import Foreign.Storable (Storable(..)) import GHC.Exts (Int(I#), minusAddr#) import GHC.ForeignPtr ( ForeignPtr(ForeignPtr) +#if MIN_VERSION_base(4,15,0) , unsafeWithForeignPtr +#endif , mallocPlainForeignPtrBytes ) import GHC.IO (unsafeDupablePerformIO) @@ -84,6 +87,10 @@ pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> pokeByteOff p off val +#if !MIN_VERSION_base(4,15,0) +unsafeWithForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b +unsafeWithForeignPtr = withForeignPtr +#endif -- Utilities for ByteString creation From b6b4de6d2fd40dbd4282ce9b54d110359463064f Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sat, 6 Aug 2022 20:23:38 -0400 Subject: [PATCH 05/13] Fix imports for unsafeWithForeignPtr shim The error messages from ghc-8.0 weren't red, so I didn't notice them. Silly stuff. --- Data/ByteString/Internal/Utils.hs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Data/ByteString/Internal/Utils.hs b/Data/ByteString/Internal/Utils.hs index e641be20a..4706f2d09 100644 --- a/Data/ByteString/Internal/Utils.hs +++ b/Data/ByteString/Internal/Utils.hs @@ -32,13 +32,14 @@ import Foreign.C.Types (CSize(..)) import Foreign.Ptr (Ptr) import Foreign.Storable (Storable(..)) import GHC.Exts (Int(I#), minusAddr#) -import GHC.ForeignPtr - ( ForeignPtr(ForeignPtr) +import GHC.ForeignPtr (ForeignPtr(ForeignPtr), mallocPlainForeignPtrBytes) + #if MIN_VERSION_base(4,15,0) - , unsafeWithForeignPtr +import GHC.ForeignPtr (unsafeWithForeignPtr) +#else +import Foreign.ForeignPtr (withForeignPtr) #endif - , mallocPlainForeignPtrBytes - ) + import GHC.IO (unsafeDupablePerformIO) #if MIN_VERSION_base(4,10,0) From 4d59766f9429ac6eb61105894339f1b1b4b8c698 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sat, 6 Aug 2022 21:21:31 -0400 Subject: [PATCH 06/13] Clean up potentially-sketchy unsafeWithForeignPtr uses --- Data/ByteString.hs | 125 +++++++++++++++++------------------- Data/ByteString/Internal.hs | 10 +-- 2 files changed, 65 insertions(+), 70 deletions(-) diff --git a/Data/ByteString.hs b/Data/ByteString.hs index 3ffff1465..b7e3d2ca4 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -536,13 +536,13 @@ foldl' f v = \(BS fp len) -> let g ptr = go v ptr where - end = ptr `plusPtr` len + end = ptr `plusForeignPtr` len -- tail recursive; traverses array left to right go !z !p | p == end = return z - | otherwise = do x <- peek p - go (f z x) (p `plusPtr` 1) + | otherwise = do x <- peekfp p + go (f z x) (p `plusForeignPtr` 1) in - accursedUnutterablePerformIO $ unsafeWithForeignPtr fp g + accursedUnutterablePerformIO $ g fp {-# INLINE foldl' #-} -- | 'foldr', applied to a binary operator, a starting value @@ -570,15 +570,15 @@ foldr' :: (Word8 -> a -> a) -> a -> ByteString -> a foldr' k v = \(BS fp len) -> -- see fold inlining let - g ptr = go v (end `plusPtr` len) + g ptr = go v (end `plusForeignPtr` len) where - end = ptr `plusPtr` (-1) + end = ptr `plusForeignPtr` (-1) -- tail recursive; traverses array right to left go !z !p | p == end = return z - | otherwise = do x <- peek p - go (k x z) (p `plusPtr` (-1)) + | otherwise = do x <- peekfp p + go (k x z) (p `plusForeignPtr` (-1)) in - accursedUnutterablePerformIO $ unsafeWithForeignPtr fp g + accursedUnutterablePerformIO $ g fp {-# INLINE foldr' #-} @@ -633,15 +633,15 @@ concatMap f = concat . foldr ((:) . f) [] -- any element of the 'ByteString' satisfies the predicate. any :: (Word8 -> Bool) -> ByteString -> Bool any _ (BS _ 0) = False -any f (BS x len) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g +any f (BS x len) = accursedUnutterablePerformIO $ g x where g ptr = go ptr where - end = ptr `plusPtr` len + end = ptr `plusForeignPtr` len go !p | p == end = return False - | otherwise = do c <- peek p + | otherwise = do c <- peekfp p if f c then return True - else go (p `plusPtr` 1) + else go (p `plusForeignPtr` 1) {-# INLINE [1] any #-} {-# RULES @@ -662,15 +662,15 @@ anyByte c (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -- if all elements of the 'ByteString' satisfy the predicate. all :: (Word8 -> Bool) -> ByteString -> Bool all _ (BS _ 0) = True -all f (BS x len) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g +all f (BS x len) = accursedUnutterablePerformIO $ g x where g ptr = go ptr where - end = ptr `plusPtr` len + end = ptr `plusForeignPtr` len go !p | p == end = return True -- end of list - | otherwise = do c <- peek p + | otherwise = do c <- peekfp p if f c - then go (p `plusPtr` 1) + then go (p `plusForeignPtr` 1) else return False {-# INLINE [1] all #-} @@ -708,7 +708,7 @@ minimum xs@(BS x l) -- passing an accumulating parameter from left to right, and returning a -- final value of this accumulator together with the new ByteString. mapAccumL :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString) -mapAccumL f acc = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> do +mapAccumL f acc = \(BS a len) -> unsafeDupablePerformIO $ do -- see fold inlining gp <- mallocByteString len let @@ -717,11 +717,11 @@ mapAccumL f acc = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr mapAccumL_ !s !n | n >= len = return s | otherwise = do - x <- peekByteOff src n + x <- peekfpByteOff src n let (s', y) = f s x - pokeByteOff dst n y + pokefpByteOff dst n y mapAccumL_ s' (n+1) - acc' <- unsafeWithForeignPtr gp (go a) + acc' <- go a gp return (acc', BS gp len) {-# INLINE mapAccumL #-} @@ -730,7 +730,7 @@ mapAccumL f acc = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr -- passing an accumulating parameter from right to left, and returning a -- final value of this accumulator together with the new ByteString. mapAccumR :: (acc -> Word8 -> (acc, Word8)) -> acc -> ByteString -> (acc, ByteString) -mapAccumR f acc = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr fp $ \a -> do +mapAccumR f acc = \(BS a len) -> unsafeDupablePerformIO $ do -- see fold inlining gp <- mallocByteString len let @@ -738,11 +738,11 @@ mapAccumR f acc = \(BS fp len) -> unsafeDupablePerformIO $ unsafeWithForeignPtr where mapAccumR_ !s (-1) = return s mapAccumR_ !s !n = do - x <- peekByteOff src n + x <- peekfpByteOff src n let (s', y) = f s x - pokeByteOff dst n y + pokefpByteOff dst n y mapAccumR_ s' (n-1) - acc' <- unsafeWithForeignPtr gp (go a) + acc' <- go a gp return (acc', BS gp len) {-# INLINE mapAccumR #-} @@ -1132,10 +1132,9 @@ splitWith _ (BS _ 0) = [] splitWith predicate (BS fp len) = splitWith0 0 len fp where splitWith0 !off' !len' !fp' = accursedUnutterablePerformIO $ - unsafeWithForeignPtr fp $ \p -> - splitLoop p 0 off' len' fp' + splitLoop fp 0 off' len' fp' - splitLoop :: Ptr Word8 + splitLoop :: ForeignPtr Word8 -> Int -> Int -> Int -> ForeignPtr Word8 -> IO [ByteString] @@ -1144,7 +1143,7 @@ splitWith predicate (BS fp len) = splitWith0 0 len fp go idx' | idx' >= len' = return [BS (plusForeignPtr fp' off') idx'] | otherwise = do - w <- peekElemOff p (off'+idx') + w <- peekfpByteOff p (off'+idx') if predicate w then return (BS (plusForeignPtr fp' off') idx' : splitWith0 (off'+idx'+1) (len'-idx'-1) fp') @@ -1316,12 +1315,12 @@ count w (BS x m) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> -- returns the index of the first element in the ByteString -- satisfying the predicate. findIndex :: (Word8 -> Bool) -> ByteString -> Maybe Int -findIndex k (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g +findIndex k (BS x l) = accursedUnutterablePerformIO $ g x where g !ptr = go 0 where go !n | n >= l = return Nothing - | otherwise = do w <- peek $ ptr `plusPtr` n + | otherwise = do w <- peekfp $ ptr `plusForeignPtr` n if k w then return (Just n) else go (n+1) @@ -1333,12 +1332,12 @@ findIndex k (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g -- -- @since 0.10.12.0 findIndexEnd :: (Word8 -> Bool) -> ByteString -> Maybe Int -findIndexEnd k (BS x l) = accursedUnutterablePerformIO $ unsafeWithForeignPtr x g +findIndexEnd k (BS x l) = accursedUnutterablePerformIO $ g x where g !ptr = go (l-1) where go !n | n < 0 = return Nothing - | otherwise = do w <- peekByteOff ptr n + | otherwise = do w <- peekfpByteOff ptr n if k w then return (Just n) else go (n-1) @@ -1381,26 +1380,25 @@ notElem c ps = not (c `elem` ps) -- returns a ByteString containing those characters that satisfy the -- predicate. filter :: (Word8 -> Bool) -> ByteString -> ByteString -filter k = \ps@(BS x l) -> +filter k = \ps@(BS pIn l) -> -- see fold inlining. if null ps then ps else - unsafePerformIO $ createfAndTrim l $ \fpOut -> - unsafeWithForeignPtr fpOut $ \pOut -> - unsafeWithForeignPtr x $ \pIn -> do + unsafeDupablePerformIO $ createfAndTrim l $ \pOut -> do let go' pf pt = go pf pt where - end = pf `plusPtr` l + end = pf `plusForeignPtr` l go !f !t | f == end = return t | otherwise = do - w <- peek f + w <- peekfp f if k w - then poke t w >> go (f `plusPtr` 1) (t `plusPtr` 1) - else go (f `plusPtr` 1) t + then pokefp t w + >> go (f `plusForeignPtr` 1) (t `plusForeignPtr` 1) + else go (f `plusForeignPtr` 1) t t <- go' pIn pOut - return $! t `minusPtr` pOut -- actual length + return $! t `minusForeignPtr` pOut -- actual length {-# INLINE filter #-} {- @@ -1446,34 +1444,33 @@ find f p = case findIndex f p of -- partition :: (Word8 -> Bool) -> ByteString -> (ByteString, ByteString) partition f s = unsafeDupablePerformIO $ - do fp' <- mallocByteString len - unsafeWithForeignPtr fp' $ \p -> - do let end = p `plusPtr` (len - 1) + do p <- mallocByteString len + let end = p `plusForeignPtr` (len - 1) mid <- sep 0 p end rev mid end - let i = mid `minusPtr` p - return (BS fp' i, - BS (plusForeignPtr fp' i) (len - i)) + let i = mid `minusForeignPtr` p + return (BS p i, + BS (p `plusForeignPtr` i) (len - i)) where len = length s - incr = (`plusPtr` 1) - decr = (`plusPtr` (-1)) + incr = (`plusForeignPtr` 1) + decr = (`plusForeignPtr` (-1)) sep !i !p1 !p2 | i == len = return p1 - | f w = do poke p1 w + | f w = do pokefp p1 w sep (i + 1) (incr p1) p2 - | otherwise = do poke p2 w + | otherwise = do pokefp p2 w sep (i + 1) p1 (decr p2) where w = s `unsafeIndex` i - rev !p1 !p2 + rev !p1 !p2 -- fixme: surely there are faster ways to do this | p1 >= p2 = return () - | otherwise = do a <- peek p1 - b <- peek p2 - poke p1 b - poke p2 a + | otherwise = do a <- peekfp p1 + b <- peekfp p2 + pokefp p1 b + pokefp p2 a rev (incr p1) (decr p2) -- -------------------------------------------------------------------- @@ -1664,20 +1661,18 @@ zipWith f ps qs = case uncons ps of -- -- @since 0.11.1.0 packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString -packZipWith f (BS fp l) (BS fq m) = unsafeDupablePerformIO $ - unsafeWithForeignPtr fp $ \a -> - unsafeWithForeignPtr fq $ \b -> - createf len $ \dest -> unsafeWithForeignPtr dest $ go a b +packZipWith f (BS a l) (BS b m) = unsafeDupablePerformIO $ + createf len $ go a b where go p1 p2 = zipWith_ 0 where - zipWith_ :: Int -> Ptr Word8 -> IO () + zipWith_ :: Int -> ForeignPtr Word8 -> IO () zipWith_ !n !r | n >= len = return () | otherwise = do - x <- peekByteOff p1 n - y <- peekByteOff p2 n - pokeByteOff r n (f x y) + x <- peekfpByteOff p1 n + y <- peekfpByteOff p2 n + pokefpByteOff r n (f x y) zipWith_ (n+1) r len = min l m diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index 1a4d4cf1c..f97b2732c 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -279,12 +279,12 @@ instance TH.Lift ByteString where -- of the string if no element is found, rather than Nothing. findIndexOrLength :: (Word8 -> Bool) -> ByteString -> Int findIndexOrLength k (BS x l) = - accursedUnutterablePerformIO $ unsafeWithForeignPtr x g + accursedUnutterablePerformIO $ g x where g ptr = go 0 where go !n | n >= l = return l - | otherwise = do w <- peek $ ptr `plusPtr` n + | otherwise = do w <- peekfp $ ptr `plusForeignPtr` n if k w then return n else go (n+1) @@ -527,6 +527,7 @@ toForeignPtr0 (BS ps l) = (ps, l) wrapAction :: (Ptr Word8 -> IO res) -> ForeignPtr Word8 -> IO res wrapAction = flip withForeignPtr + -- Cannot use unsafeWithForeignPtr, because action can diverge -- | A way of creating ByteStrings outside the IO monad. The @Int@ -- argument gives the final size of the ByteString. @@ -702,9 +703,8 @@ stimesNonNegativeInt n (BS fp len) | n == 0 = empty | n == 1 = BS fp len | len == 0 = empty - | len == 1 = unsafeCreatef n $ \destfptr -> - unsafeWithForeignPtr fp $ \p -> do - byte <- peek p + | len == 1 = unsafeCreatef n $ \destfptr -> do + byte <- peekfp fp void $ unsafeWithForeignPtr destfptr $ \destptr -> memset destptr byte (fromIntegral n) | otherwise = unsafeCreatef size $ \destptr -> do From f933ccf73ec4de1c6619c77688493a4acc7efa08 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sat, 6 Aug 2022 21:46:34 -0400 Subject: [PATCH 07/13] Revert documentation change to createAndTrim --- Data/ByteString/Internal.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index f97b2732c..39d6dca3e 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -573,6 +573,9 @@ createUptoN' l action = createfUptoN' l (wrapAction action) -- function is required to return the actual final size (<= the maximum -- size), and the resulting byte array is reallocated to this size. -- +-- createAndTrim is the main mechanism for creating custom, efficient +-- ByteString functions, using Haskell or C functions to fill the space. +-- createAndTrim :: Int -> (Ptr Word8 -> IO Int) -> IO ByteString createAndTrim l action = createfAndTrim l (wrapAction action) {-# INLINE createAndTrim #-} From 0d4f106a76a6dfbc35aa1581368f3c9ddab26c25 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sun, 7 Aug 2022 16:36:42 -0400 Subject: [PATCH 08/13] Revert "Fix imports for unsafeWithForeignPtr shim" This reverts commit b6b4de6d2fd40dbd4282ce9b54d110359463064f. Revert "Move unsafeWithForeignPtr shim into Utils" This reverts commit 6df394ea8222beb0f74961f7c3d83a301154417f. Revert "Move new utilities into D.B.Internal.Utils" This reverts commit 6462e4f4ad684fa1f1e61ae282a1ff769ebfb50a. --- Data/ByteString.hs | 1 - Data/ByteString/Builder/Internal.hs | 1 - Data/ByteString/Char8.hs | 1 - Data/ByteString/Internal.hs | 162 +++++++++++++++++++++++- Data/ByteString/Internal.hs-boot | 11 -- Data/ByteString/Internal/Utils.hs | 188 ---------------------------- Data/ByteString/Lazy.hs | 1 - Data/ByteString/Lazy/Internal.hs | 1 - bytestring.cabal | 1 - 9 files changed, 158 insertions(+), 209 deletions(-) delete mode 100644 Data/ByteString/Internal.hs-boot delete mode 100644 Data/ByteString/Internal/Utils.hs diff --git a/Data/ByteString.hs b/Data/ByteString.hs index b7e3d2ca4..d8a6acdc9 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -231,7 +231,6 @@ import Prelude hiding (reverse,head,tail,last,init,null import Data.Bits (finiteBitSize, shiftL, (.|.), (.&.)) import Data.ByteString.Internal -import Data.ByteString.Internal.Utils import Data.ByteString.Lazy.Internal (fromStrict, toStrict) import Data.ByteString.Unsafe diff --git a/Data/ByteString/Builder/Internal.hs b/Data/ByteString/Builder/Internal.hs index ede5199e4..0fdf6fa75 100644 --- a/Data/ByteString/Builder/Internal.hs +++ b/Data/ByteString/Builder/Internal.hs @@ -134,7 +134,6 @@ import Data.Semigroup (Semigroup((<>))) import qualified Data.ByteString as S import qualified Data.ByteString.Internal as S -import qualified Data.ByteString.Internal.Utils as S import qualified Data.ByteString.Lazy.Internal as L import qualified Data.ByteString.Short.Internal as Sh diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs index e2b8e1b33..7ed19351f 100644 --- a/Data/ByteString/Char8.hs +++ b/Data/ByteString/Char8.hs @@ -276,7 +276,6 @@ import Data.ByteString (null,length,tail,init,append ) import Data.ByteString.Internal -import Data.ByteString.Internal.Utils import Data.ByteString.ReadInt import Data.ByteString.ReadNat diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index 39d6dca3e..b69e8e7c4 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -48,6 +48,14 @@ module Data.ByteString.Internal ( -- * Low level imperative construction empty, + createf, + createfUptoN, + createfUptoN', + createfAndTrim, + createfAndTrim', + unsafeCreatef, + unsafeCreatefUptoN, + unsafeCreatefUptoN', create, createUptoN, createUptoN', @@ -66,6 +74,12 @@ module Data.ByteString.Internal ( -- * Utilities nullForeignPtr, + peekfp, + pokefp, + peekfpByteOff, + pokefpByteOff, + minusForeignPtr, + memcpyf, SizeOverflowException, overflowError, checkedAdd, @@ -99,7 +113,6 @@ module Data.ByteString.Internal ( unsafeWithForeignPtr ) where -import Data.ByteString.Internal.Utils import Prelude hiding (concat, null) import qualified Data.List as List @@ -133,7 +146,7 @@ import Data.Data (Data(..), mkNoRepType) import GHC.Base (nullAddr#,realWorld#,unsafeChr) import GHC.Exts (IsList(..)) import GHC.CString (unpackCString#) -import GHC.Exts (Addr#) +import GHC.Exts (Addr#, minusAddr#) #define TIMES_INT_2_AVAILABLE MIN_VERSION_ghc_prim(0,7,0) #if TIMES_INT_2_AVAILABLE @@ -148,12 +161,18 @@ import GHC.Prim ( timesWord2# import Data.Bits (finiteBitSize) #endif -import GHC.IO (IO(IO)) +import GHC.IO (IO(IO),unsafeDupablePerformIO) import GHC.ForeignPtr (ForeignPtr(ForeignPtr) #if __GLASGOW_HASKELL__ < 900 , newForeignPtr_ #endif - ) + , mallocPlainForeignPtrBytes) + +#if MIN_VERSION_base(4,10,0) +import GHC.ForeignPtr (plusForeignPtr) +#else +import GHC.Prim (plusAddr#) +#endif #if __GLASGOW_HASKELL__ >= 811 import GHC.CString (cstringLength#) @@ -164,12 +183,57 @@ import GHC.Ptr (Ptr(..)) import GHC.Types (Int (..)) +#if MIN_VERSION_base(4,15,0) +import GHC.ForeignPtr (unsafeWithForeignPtr) +#endif + import qualified Language.Haskell.TH.Lib as TH import qualified Language.Haskell.TH.Syntax as TH +#if !MIN_VERSION_base(4,15,0) +unsafeWithForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b +unsafeWithForeignPtr = withForeignPtr +#endif + -- CFILES stuff is Hugs only {-# CFILES cbits/fpstring.c #-} +#if !MIN_VERSION_base(4,10,0) +-- |Advances the given address by the given offset in bytes. +-- +-- The new 'ForeignPtr' shares the finalizer of the original, +-- equivalent from a finalization standpoint to just creating another +-- reference to the original. That is, the finalizer will not be +-- called before the new 'ForeignPtr' is unreachable, nor will it be +-- called an additional time due to this call, and the finalizer will +-- be called with the same address that it would have had this call +-- not happened, *not* the new address. +plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b +plusForeignPtr (ForeignPtr addr guts) (I# offset) = ForeignPtr (plusAddr# addr offset) guts +{-# INLINE [0] plusForeignPtr #-} +{-# RULES +"ByteString plusForeignPtr/0" forall fp . + plusForeignPtr fp 0 = fp + #-} +#endif + +minusForeignPtr :: ForeignPtr a -> ForeignPtr b -> Int +minusForeignPtr (ForeignPtr addr1 _) (ForeignPtr addr2 _) + = I# (minusAddr# addr1 addr2) + +peekfp :: Storable a => ForeignPtr a -> IO a +peekfp fp = unsafeWithForeignPtr fp peek + +pokefp :: Storable a => ForeignPtr a -> a -> IO () +pokefp fp val = unsafeWithForeignPtr fp $ \p -> poke p val + +peekfpByteOff :: Storable a => ForeignPtr a -> Int -> IO a +peekfpByteOff fp off = unsafeWithForeignPtr fp $ \p -> + peekByteOff p off + +pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () +pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> + pokeByteOff p off val -- ----------------------------------------------------------------------------- @@ -524,6 +588,80 @@ toForeignPtr0 :: ByteString -> (ForeignPtr Word8, Int) -- ^ (ptr, length) toForeignPtr0 (BS ps l) = (ps, l) {-# INLINE toForeignPtr0 #-} +-- | A way of creating ByteStrings outside the IO monad. The @Int@ +-- argument gives the final size of the ByteString. +unsafeCreatef :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString +unsafeCreatef l f = unsafeDupablePerformIO (createf l f) +{-# INLINE unsafeCreatef #-} + +-- | Like 'unsafeCreatef' but instead of giving the final size of the +-- ByteString, it is just an upper bound. The inner action returns +-- the actual size. Unlike 'createfAndTrim' the ByteString is not +-- reallocated if the final size is less than the estimated size. +unsafeCreatefUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString +unsafeCreatefUptoN l f = unsafeDupablePerformIO (createfUptoN l f) +{-# INLINE unsafeCreatefUptoN #-} + +unsafeCreatefUptoN' + :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> (ByteString, a) +unsafeCreatefUptoN' l f = unsafeDupablePerformIO (createfUptoN' l f) +{-# INLINE unsafeCreatefUptoN' #-} + +-- | Create ByteString of size @l@ and use action @f@ to fill its contents. +createf :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString +createf l action = do + fp <- mallocByteString l + action fp + return $! BS fp l +{-# INLINE createf #-} + +-- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' +-- starting at the given 'Ptr' and returns the actual utilized length, +-- @`createfUptoN'` l f@ returns the filled 'ByteString'. +createfUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createfUptoN l action = do + fp <- mallocByteString l + l' <- action fp + assert (l' <= l) $ return $! BS fp l' +{-# INLINE createfUptoN #-} + +-- | Like 'createfUptoN', but also returns an additional value created by the +-- action. +createfUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) +createfUptoN' l action = do + fp <- mallocByteString l + (l', res) <- action fp + assert (l' <= l) $ return (BS fp l', res) +{-# INLINE createfUptoN' #-} + +-- | Given the maximum size needed and a function to make the contents +-- of a ByteString, createfAndTrim makes the 'ByteString'. The generating +-- function is required to return the actual final size (<= the maximum +-- size), and the resulting byte array is reallocated to this size. +-- +-- createfAndTrim is the main mechanism for creating custom, efficient +-- ByteString functions, using Haskell or C functions to fill the space. +-- +createfAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createfAndTrim l action = do + fp <- mallocByteString l + l' <- action fp + if assert (0 <= l' && l' <= l) $ l' >= l + then return $! BS fp l + else createf l' $ \fp' -> memcpyf fp' fp l' +{-# INLINE createfAndTrim #-} + +createfAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) +createfAndTrim' l action = do + fp <- mallocByteString l + (off, l', res) <- action fp + if assert (0 <= l' && l' <= l) $ l' >= l + then return (BS fp l, res) + else do ps <- createf l' $ \fp' -> + memcpyf fp' (fp `plusForeignPtr` off) l' + return (ps, res) +{-# INLINE createfAndTrim' #-} + wrapAction :: (Ptr Word8 -> IO res) -> ForeignPtr Word8 -> IO res wrapAction = flip withForeignPtr @@ -584,6 +722,13 @@ createAndTrim' :: Int -> (Ptr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) createAndTrim' l action = createfAndTrim' l (wrapAction action) {-# INLINE createAndTrim' #-} + +-- | Wrapper of 'Foreign.ForeignPtr.mallocForeignPtrBytes' with faster implementation for GHC +-- +mallocByteString :: Int -> IO (ForeignPtr a) +mallocByteString = mallocPlainForeignPtrBytes +{-# INLINE mallocByteString #-} + ------------------------------------------------------------------------ -- Implementations for Eq, Ord and Monoid instances @@ -870,6 +1015,15 @@ foreign import ccall unsafe "string.h memcmp" c_memcmp memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO CInt memcmp p q s = c_memcmp p q (fromIntegral s) +foreign import ccall unsafe "string.h memcpy" c_memcpy + :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8) + +memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () +memcpy p q s = void $ c_memcpy p q (fromIntegral s) + +memcpyf :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () +memcpyf fp fq s = unsafeWithForeignPtr fp $ \p -> + unsafeWithForeignPtr fq $ \q -> memcpy p q s {- foreign import ccall unsafe "string.h memmove" c_memmove diff --git a/Data/ByteString/Internal.hs-boot b/Data/ByteString/Internal.hs-boot deleted file mode 100644 index d537ecf90..000000000 --- a/Data/ByteString/Internal.hs-boot +++ /dev/null @@ -1,11 +0,0 @@ --- allow Data.ByteString.Internal.Utils to use the BS constructor - -module Data.ByteString.Internal ( - ByteString (BS) -) where - -import Foreign.ForeignPtr (ForeignPtr) -import Data.Word (Word8) - -data ByteString = BS {-# UNPACK #-} !(ForeignPtr Word8) -- payload - {-# UNPACK #-} !Int -- length diff --git a/Data/ByteString/Internal/Utils.hs b/Data/ByteString/Internal/Utils.hs deleted file mode 100644 index 4706f2d09..000000000 --- a/Data/ByteString/Internal/Utils.hs +++ /dev/null @@ -1,188 +0,0 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE MagicHash #-} - -module Data.ByteString.Internal.Utils ( - peekfp, - pokefp, - peekfpByteOff, - pokefpByteOff, - plusForeignPtr, - minusForeignPtr, - memcpyf, - unsafeWithForeignPtr, - - createf, - createfUptoN, - createfUptoN', - createfAndTrim, - createfAndTrim', - unsafeCreatef, - unsafeCreatefUptoN, - unsafeCreatefUptoN', - mallocByteString, - memcpy, -) where - -import {-# SOURCE #-} Data.ByteString.Internal - -import Control.Exception (assert) -import Control.Monad (void) -import Data.Word (Word8) -import Foreign.C.Types (CSize(..)) -import Foreign.Ptr (Ptr) -import Foreign.Storable (Storable(..)) -import GHC.Exts (Int(I#), minusAddr#) -import GHC.ForeignPtr (ForeignPtr(ForeignPtr), mallocPlainForeignPtrBytes) - -#if MIN_VERSION_base(4,15,0) -import GHC.ForeignPtr (unsafeWithForeignPtr) -#else -import Foreign.ForeignPtr (withForeignPtr) -#endif - -import GHC.IO (unsafeDupablePerformIO) - -#if MIN_VERSION_base(4,10,0) -import GHC.ForeignPtr (plusForeignPtr) -#else -import GHC.Exts (plusAddr#) -#endif - - --- Utilities for working with ForeignPtr - -#if !MIN_VERSION_base(4,10,0) --- |Advances the given address by the given offset in bytes. --- --- The new 'ForeignPtr' shares the finalizer of the original, --- equivalent from a finalization standpoint to just creating another --- reference to the original. That is, the finalizer will not be --- called before the new 'ForeignPtr' is unreachable, nor will it be --- called an additional time due to this call, and the finalizer will --- be called with the same address that it would have had this call --- not happened, *not* the new address. -plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b -plusForeignPtr (ForeignPtr addr guts) (I# offset) = ForeignPtr (plusAddr# addr offset) guts -{-# INLINE [0] plusForeignPtr #-} -{-# RULES -"ByteString plusForeignPtr/0" forall fp . - plusForeignPtr fp 0 = fp - #-} -#endif - -minusForeignPtr :: ForeignPtr a -> ForeignPtr b -> Int -minusForeignPtr (ForeignPtr addr1 _) (ForeignPtr addr2 _) - = I# (minusAddr# addr1 addr2) - -peekfp :: Storable a => ForeignPtr a -> IO a -peekfp fp = unsafeWithForeignPtr fp peek - -pokefp :: Storable a => ForeignPtr a -> a -> IO () -pokefp fp val = unsafeWithForeignPtr fp $ \p -> poke p val - -peekfpByteOff :: Storable a => ForeignPtr a -> Int -> IO a -peekfpByteOff fp off = unsafeWithForeignPtr fp $ \p -> - peekByteOff p off - -pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () -pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> - pokeByteOff p off val - -#if !MIN_VERSION_base(4,15,0) -unsafeWithForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b -unsafeWithForeignPtr = withForeignPtr -#endif - - --- Utilities for ByteString creation - --- | Wrapper of 'Foreign.ForeignPtr.mallocForeignPtrBytes' with faster implementation for GHC --- -mallocByteString :: Int -> IO (ForeignPtr a) -mallocByteString = mallocPlainForeignPtrBytes -{-# INLINE mallocByteString #-} - --- | A way of creating ByteStrings outside the IO monad. The @Int@ --- argument gives the final size of the ByteString. -unsafeCreatef :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString -unsafeCreatef l f = unsafeDupablePerformIO (createf l f) -{-# INLINE unsafeCreatef #-} - --- | Like 'unsafeCreatef' but instead of giving the final size of the --- ByteString, it is just an upper bound. The inner action returns --- the actual size. Unlike 'createfAndTrim' the ByteString is not --- reallocated if the final size is less than the estimated size. -unsafeCreatefUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString -unsafeCreatefUptoN l f = unsafeDupablePerformIO (createfUptoN l f) -{-# INLINE unsafeCreatefUptoN #-} - -unsafeCreatefUptoN' - :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> (ByteString, a) -unsafeCreatefUptoN' l f = unsafeDupablePerformIO (createfUptoN' l f) -{-# INLINE unsafeCreatefUptoN' #-} - --- | Create ByteString of size @l@ and use action @f@ to fill its contents. -createf :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString -createf l action = do - fp <- mallocByteString l - action fp - return $! BS fp l -{-# INLINE createf #-} - --- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' --- starting at the given 'Ptr' and returns the actual utilized length, --- @`createfUptoN'` l f@ returns the filled 'ByteString'. -createfUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString -createfUptoN l action = do - fp <- mallocByteString l - l' <- action fp - assert (l' <= l) $ return $! BS fp l' -{-# INLINE createfUptoN #-} - --- | Like 'createfUptoN', but also returns an additional value created by the --- action. -createfUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) -createfUptoN' l action = do - fp <- mallocByteString l - (l', res) <- action fp - assert (l' <= l) $ return (BS fp l', res) -{-# INLINE createfUptoN' #-} - --- | Given the maximum size needed and a function to make the contents --- of a ByteString, createfAndTrim makes the 'ByteString'. The generating --- function is required to return the actual final size (<= the maximum --- size), and the resulting byte array is reallocated to this size. --- --- createfAndTrim is the main mechanism for creating custom, efficient --- ByteString functions, using Haskell or C functions to fill the space. --- -createfAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString -createfAndTrim l action = do - fp <- mallocByteString l - l' <- action fp - if assert (0 <= l' && l' <= l) $ l' >= l - then return $! BS fp l - else createf l' $ \fp' -> memcpyf fp' fp l' -{-# INLINE createfAndTrim #-} - -createfAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) -createfAndTrim' l action = do - fp <- mallocByteString l - (off, l', res) <- action fp - if assert (0 <= l' && l' <= l) $ l' >= l - then return (BS fp l, res) - else do ps <- createf l' $ \fp' -> - memcpyf fp' (fp `plusForeignPtr` off) l' - return (ps, res) -{-# INLINE createfAndTrim' #-} - - -foreign import ccall unsafe "string.h memcpy" c_memcpy - :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8) - -memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () -memcpy p q s = void $ c_memcpy p q (fromIntegral s) - -memcpyf :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () -memcpyf fp fq s = unsafeWithForeignPtr fp $ \p -> - unsafeWithForeignPtr fq $ \q -> memcpy p q s diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs index a78a694c3..316147111 100644 --- a/Data/ByteString/Lazy.hs +++ b/Data/ByteString/Lazy.hs @@ -232,7 +232,6 @@ import qualified Data.Bifunctor as BF import qualified Data.ByteString as P (ByteString) -- type name only import qualified Data.ByteString as S -- S for strict (hmm...) import qualified Data.ByteString.Internal as S -import qualified Data.ByteString.Internal.Utils as S import qualified Data.ByteString.Unsafe as S import qualified Data.ByteString.Lazy.Internal.Deque as D import Data.ByteString.Lazy.Internal diff --git a/Data/ByteString/Lazy/Internal.hs b/Data/ByteString/Lazy/Internal.hs index ab2000061..1fd7bbf28 100644 --- a/Data/ByteString/Lazy/Internal.hs +++ b/Data/ByteString/Lazy/Internal.hs @@ -50,7 +50,6 @@ module Data.ByteString.Lazy.Internal ( import Prelude hiding (concat) import qualified Data.ByteString.Internal as S -import qualified Data.ByteString.Internal.Utils as S import Data.Word (Word8) import Foreign.Storable (Storable(sizeOf)) diff --git a/bytestring.cabal b/bytestring.cabal index fef8ddf03..009aab131 100644 --- a/bytestring.cabal +++ b/bytestring.cabal @@ -99,7 +99,6 @@ library Data.ByteString.Builder.RealFloat.D2S Data.ByteString.Builder.RealFloat.Internal Data.ByteString.Builder.RealFloat.TableGenerator - Data.ByteString.Internal.Utils Data.ByteString.Lazy.Internal.Deque Data.ByteString.Lazy.ReadInt Data.ByteString.Lazy.ReadNat From 698faec9094ca4b11ff3673d7b4d8afd97065bad Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sun, 7 Aug 2022 16:37:28 -0400 Subject: [PATCH 09/13] Rename ForeignPtr helpers to use "Fp" suffix --- Data/ByteString.hs | 118 +++++++++---------- Data/ByteString/Builder/Internal.hs | 4 +- Data/ByteString/Char8.hs | 6 +- Data/ByteString/Internal.hs | 170 ++++++++++++++-------------- Data/ByteString/Lazy.hs | 2 +- Data/ByteString/Lazy/Internal.hs | 4 +- 6 files changed, 152 insertions(+), 152 deletions(-) diff --git a/Data/ByteString.hs b/Data/ByteString.hs index d8a6acdc9..aecec7974 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -374,16 +374,16 @@ infixl 5 `snoc` -- | /O(n)/ 'cons' is analogous to (:) for lists, but of different -- complexity, as it requires making a copy. cons :: Word8 -> ByteString -> ByteString -cons c (BS x l) = unsafeCreatef (l+1) $ \p -> do - pokefp p c - memcpyf (p `plusForeignPtr` 1) x l +cons c (BS x l) = unsafeCreateFp (l+1) $ \p -> do + pokeFp p c + memcpyFp (p `plusForeignPtr` 1) x l {-# INLINE cons #-} -- | /O(n)/ Append a byte to the end of a 'ByteString' snoc :: ByteString -> Word8 -> ByteString -snoc (BS x l) c = unsafeCreatef (l+1) $ \p -> do - memcpyf p x l - pokefp (p `plusForeignPtr` l) c +snoc (BS x l) c = unsafeCreateFp (l+1) $ \p -> do + memcpyFp p x l + pokeFp (p `plusForeignPtr` l) c {-# INLINE snoc #-} -- | /O(1)/ Extract the first element of a ByteString, which must be non-empty. @@ -458,7 +458,7 @@ append = mappend -- | /O(n)/ 'map' @f xs@ is the ByteString obtained by applying @f@ to each -- element of @xs@. map :: (Word8 -> Word8) -> ByteString -> ByteString -map f (BS srcPtr len) = unsafeCreatef len $ \dstPtr -> m srcPtr dstPtr +map f (BS srcPtr len) = unsafeCreateFp len $ \dstPtr -> m srcPtr dstPtr where m !p1 !p2 = map_ 0 where @@ -466,14 +466,14 @@ map f (BS srcPtr len) = unsafeCreatef len $ \dstPtr -> m srcPtr dstPtr map_ !n | n >= len = return () | otherwise = do - x <- peekfpByteOff p1 n - pokefpByteOff p2 n (f x) + x <- peekFpByteOff p1 n + pokeFpByteOff p2 n (f x) map_ (n+1) {-# INLINE map #-} -- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. reverse :: ByteString -> ByteString -reverse (BS x l) = unsafeCreatef l $ \fp -> +reverse (BS x l) = unsafeCreateFp l $ \fp -> unsafeWithForeignPtr fp $ \p -> unsafeWithForeignPtr x $ \f -> c_reverse p f (fromIntegral l) @@ -485,7 +485,7 @@ reverse (BS x l) = unsafeCreatef l $ \fp -> intersperse :: Word8 -> ByteString -> ByteString intersperse c ps@(BS x l) | length ps < 2 = ps - | otherwise = unsafeCreatef (2*l-1) $ \fp -> + | otherwise = unsafeCreateFp (2*l-1) $ \fp -> unsafeWithForeignPtr fp $ \p -> unsafeWithForeignPtr x $ \f -> c_intersperse p f (fromIntegral l) c @@ -538,7 +538,7 @@ foldl' f v = \(BS fp len) -> end = ptr `plusForeignPtr` len -- tail recursive; traverses array left to right go !z !p | p == end = return z - | otherwise = do x <- peekfp p + | otherwise = do x <- peekFp p go (f z x) (p `plusForeignPtr` 1) in accursedUnutterablePerformIO $ g fp @@ -574,7 +574,7 @@ foldr' k v = \(BS fp len) -> end = ptr `plusForeignPtr` (-1) -- tail recursive; traverses array right to left go !z !p | p == end = return z - | otherwise = do x <- peekfp p + | otherwise = do x <- peekFp p go (k x z) (p `plusForeignPtr` (-1)) in accursedUnutterablePerformIO $ g fp @@ -638,7 +638,7 @@ any f (BS x len) = accursedUnutterablePerformIO $ g x where end = ptr `plusForeignPtr` len go !p | p == end = return False - | otherwise = do c <- peekfp p + | otherwise = do c <- peekFp p if f c then return True else go (p `plusForeignPtr` 1) {-# INLINE [1] any #-} @@ -667,7 +667,7 @@ all f (BS x len) = accursedUnutterablePerformIO $ g x where end = ptr `plusForeignPtr` len go !p | p == end = return True -- end of list - | otherwise = do c <- peekfp p + | otherwise = do c <- peekFp p if f c then go (p `plusForeignPtr` 1) else return False @@ -716,9 +716,9 @@ mapAccumL f acc = \(BS a len) -> unsafeDupablePerformIO $ do mapAccumL_ !s !n | n >= len = return s | otherwise = do - x <- peekfpByteOff src n + x <- peekFpByteOff src n let (s', y) = f s x - pokefpByteOff dst n y + pokeFpByteOff dst n y mapAccumL_ s' (n+1) acc' <- go a gp return (acc', BS gp len) @@ -737,9 +737,9 @@ mapAccumR f acc = \(BS a len) -> unsafeDupablePerformIO $ do where mapAccumR_ !s (-1) = return s mapAccumR_ !s !n = do - x <- peekfpByteOff src n + x <- peekFpByteOff src n let (s', y) = f s x - pokefpByteOff dst n y + pokeFpByteOff dst n y mapAccumR_ s' (n-1) acc' <- go a gp return (acc', BS gp len) @@ -767,18 +767,18 @@ scanl -- ^ input of length n -> ByteString -- ^ output of length n+1 -scanl f v = \(BS a len) -> unsafeCreatef (len+1) $ \q -> do +scanl f v = \(BS a len) -> unsafeCreateFp (len+1) $ \q -> do -- see fold inlining - pokefp q v + pokeFp q v let go src dst = scanl_ v 0 where scanl_ !z !n | n >= len = return () | otherwise = do - x <- peekfpByteOff src n + x <- peekFpByteOff src n let z' = f z x - pokefpByteOff dst n z' + pokeFpByteOff dst n z' scanl_ z' (n+1) go a (q `plusForeignPtr` 1) {-# INLINE scanl #-} @@ -811,18 +811,18 @@ scanr -- ^ input of length n -> ByteString -- ^ output of length n+1 -scanr f v = \(BS a len) -> unsafeCreatef (len+1) $ \b -> do +scanr f v = \(BS a len) -> unsafeCreateFp (len+1) $ \b -> do -- see fold inlining - pokefpByteOff b len v + pokeFpByteOff b len v let go p q = scanr_ v (len-1) where scanr_ !z !n | n < 0 = return () | otherwise = do - x <- peekfpByteOff p n + x <- peekFpByteOff p n let z' = f x z - pokefpByteOff q n z' + pokeFpByteOff q n z' scanr_ z' (n-1) go a b {-# INLINE scanr #-} @@ -846,7 +846,7 @@ scanr1 f ps = case unsnoc ps of replicate :: Int -> Word8 -> ByteString replicate w c | w <= 0 = empty - | otherwise = unsafeCreatef w $ \fptr -> + | otherwise = unsafeCreateFp w $ \fptr -> unsafeWithForeignPtr fptr $ \ptr -> void $ memset ptr c (fromIntegral w) {-# INLINE replicate #-} @@ -883,7 +883,7 @@ unfoldr f = concat . unfoldChunk 32 64 unfoldrN :: Int -> (a -> Maybe (Word8, a)) -> a -> (ByteString, Maybe a) unfoldrN i f x0 | i < 0 = (empty, Just x0) - | otherwise = unsafePerformIO $ createfAndTrim' i $ \p -> go p x0 0 + | otherwise = unsafePerformIO $ createFpAndTrim' i $ \p -> go p x0 0 where go !p !x !n = go' x n where @@ -891,7 +891,7 @@ unfoldrN i f x0 | n' == i = return (0, n', Just x') | otherwise = case f x' of Nothing -> return (0, n', Nothing) - Just (w,x'') -> do pokefpByteOff p n' w + Just (w,x'') -> do pokeFpByteOff p n' w go' x'' (n'+1) {-# INLINE unfoldrN #-} @@ -1142,7 +1142,7 @@ splitWith predicate (BS fp len) = splitWith0 0 len fp go idx' | idx' >= len' = return [BS (plusForeignPtr fp' off') idx'] | otherwise = do - w <- peekfpByteOff p (off'+idx') + w <- peekFpByteOff p (off'+idx') if predicate w then return (BS (plusForeignPtr fp' off') idx' : splitWith0 (off'+idx'+1) (len'-idx'-1) fp') @@ -1214,13 +1214,13 @@ intercalate :: ByteString -> [ByteString] -> ByteString intercalate _ [] = mempty intercalate _ [x] = x -- This branch exists for laziness, not speed intercalate (BS sepPtr sepLen) (BS hPtr hLen : t) = - unsafeCreatef totalLen $ \dstPtr0 -> do - memcpyf dstPtr0 hPtr hLen + unsafeCreateFp totalLen $ \dstPtr0 -> do + memcpyFp dstPtr0 hPtr hLen let go _ [] = pure () go dstPtr (BS chunkPtr chunkLen : chunks) = do - memcpyf dstPtr sepPtr sepLen + memcpyFp dstPtr sepPtr sepLen let destPtr' = dstPtr `plusForeignPtr` sepLen - memcpyf destPtr' chunkPtr chunkLen + memcpyFp destPtr' chunkPtr chunkLen go (destPtr' `plusForeignPtr` chunkLen) chunks go (dstPtr0 `plusForeignPtr` hLen) t where @@ -1319,7 +1319,7 @@ findIndex k (BS x l) = accursedUnutterablePerformIO $ g x g !ptr = go 0 where go !n | n >= l = return Nothing - | otherwise = do w <- peekfp $ ptr `plusForeignPtr` n + | otherwise = do w <- peekFp $ ptr `plusForeignPtr` n if k w then return (Just n) else go (n+1) @@ -1336,7 +1336,7 @@ findIndexEnd k (BS x l) = accursedUnutterablePerformIO $ g x g !ptr = go (l-1) where go !n | n < 0 = return Nothing - | otherwise = do w <- peekfpByteOff ptr n + | otherwise = do w <- peekFpByteOff ptr n if k w then return (Just n) else go (n-1) @@ -1384,16 +1384,16 @@ filter k = \ps@(BS pIn l) -> if null ps then ps else - unsafeDupablePerformIO $ createfAndTrim l $ \pOut -> do + unsafeDupablePerformIO $ createFpAndTrim l $ \pOut -> do let go' pf pt = go pf pt where end = pf `plusForeignPtr` l go !f !t | f == end = return t | otherwise = do - w <- peekfp f + w <- peekFp f if k w - then pokefp t w + then pokeFp t w >> go (f `plusForeignPtr` 1) (t `plusForeignPtr` 1) else go (f `plusForeignPtr` 1) t t <- go' pIn pOut @@ -1457,19 +1457,19 @@ partition f s = unsafeDupablePerformIO $ sep !i !p1 !p2 | i == len = return p1 - | f w = do pokefp p1 w + | f w = do pokeFp p1 w sep (i + 1) (incr p1) p2 - | otherwise = do pokefp p2 w + | otherwise = do pokeFp p2 w sep (i + 1) p1 (decr p2) where w = s `unsafeIndex` i rev !p1 !p2 -- fixme: surely there are faster ways to do this | p1 >= p2 = return () - | otherwise = do a <- peekfp p1 - b <- peekfp p2 - pokefp p1 b - pokefp p2 a + | otherwise = do a <- peekFp p1 + b <- peekFp p2 + pokeFp p1 b + pokeFp p2 a rev (incr p1) (decr p2) -- -------------------------------------------------------------------- @@ -1661,7 +1661,7 @@ zipWith f ps qs = case uncons ps of -- @since 0.11.1.0 packZipWith :: (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> ByteString packZipWith f (BS a l) (BS b m) = unsafeDupablePerformIO $ - createf len $ go a b + createFp len $ go a b where go p1 p2 = zipWith_ 0 where @@ -1669,9 +1669,9 @@ packZipWith f (BS a l) (BS b m) = unsafeDupablePerformIO $ zipWith_ !n !r | n >= len = return () | otherwise = do - x <- peekfpByteOff p1 n - y <- peekfpByteOff p2 n - pokefpByteOff r n (f x y) + x <- peekFpByteOff p1 n + y <- peekFpByteOff p2 n + pokeFpByteOff r n (f x y) zipWith_ (n+1) r len = min l m @@ -1704,10 +1704,10 @@ tails p | null p = [empty] sort :: ByteString -> ByteString sort (BS input l) -- qsort outperforms counting sort for small arrays - | l <= 20 = unsafeCreatef l $ \destFP -> do - memcpyf destFP input l + | l <= 20 = unsafeCreateFp l $ \destFP -> do + memcpyFp destFP input l unsafeWithForeignPtr destFP $ \dest -> c_sort dest (fromIntegral l) - | otherwise = unsafeCreatef l $ \p -> allocaArray 256 $ \arr -> do + | otherwise = unsafeCreateFp l $ \p -> allocaArray 256 $ \arr -> do _ <- memset (castPtr arr) 0 (256 * fromIntegral (sizeOf (undefined :: CSize))) unsafeWithForeignPtr input (\x -> countOccurrences arr x l) @@ -1766,7 +1766,7 @@ packCString cstr = do -- The @ByteString@ is a normal Haskell value and will be managed on the -- Haskell heap. packCStringLen :: CStringLen -> IO ByteString -packCStringLen (cstr, len) | len >= 0 = createf len $ \fp -> +packCStringLen (cstr, len) | len >= 0 = createFp len $ \fp -> unsafeWithForeignPtr fp $ \p -> memcpy p (castPtr cstr) len packCStringLen (_, len) = moduleErrorIO "packCStringLen" ("negative length: " ++ show len) @@ -1780,7 +1780,7 @@ packCStringLen (_, len) = -- is needed in the rest of the program. -- copy :: ByteString -> ByteString -copy (BS x l) = unsafeCreatef l $ \p -> memcpyf p x l +copy (BS x l) = unsafeCreateFp l $ \p -> memcpyFp p x l -- --------------------------------------------------------------------- -- Line IO @@ -1839,7 +1839,7 @@ hGetLine h = mkPS :: RawBuffer Word8 -> Int -> Int -> IO ByteString mkPS buf start end = - createf len $ \fp -> + createFp len $ \fp -> unsafeWithForeignPtr fp $ \p -> withRawBuffer buf $ \pbuf -> memcpy p (pbuf `plusPtr` start) len where @@ -1894,7 +1894,7 @@ putStr = hPut stdout -- hGet :: Handle -> Int -> IO ByteString hGet h i - | i > 0 = createfAndTrim i $ \fp -> + | i > 0 = createFpAndTrim i $ \fp -> unsafeWithForeignPtr fp $ \p -> hGetBuf h p i | i == 0 = return empty | otherwise = illegalBufferSize h "hGet" i @@ -1909,7 +1909,7 @@ hGet h i -- hGetNonBlocking :: Handle -> Int -> IO ByteString hGetNonBlocking h i - | i > 0 = createfAndTrim i $ \fp -> + | i > 0 = createFpAndTrim i $ \fp -> unsafeWithForeignPtr fp $ \p -> hGetBufNonBlocking h p i | i == 0 = return empty | otherwise = illegalBufferSize h "hGetNonBlocking" i @@ -1921,7 +1921,7 @@ hGetNonBlocking h i -- hGetSome :: Handle -> Int -> IO ByteString hGetSome hh i - | i > 0 = createfAndTrim i $ \fp -> + | i > 0 = createFpAndTrim i $ \fp -> unsafeWithForeignPtr fp $ \p -> hGetBufSome hh p i | i == 0 = return empty | otherwise = illegalBufferSize hh "hGetSome" i diff --git a/Data/ByteString/Builder/Internal.hs b/Data/ByteString/Builder/Internal.hs index 0fdf6fa75..c7beeb1f3 100644 --- a/Data/ByteString/Builder/Internal.hs +++ b/Data/ByteString/Builder/Internal.hs @@ -1099,8 +1099,8 @@ buildStepToCIOS (AllocationStrategy nextBuffer bufSize trim) = wrapChunk !op' mkCIOS | chunkSize == 0 = mkCIOS True | trim chunkSize size = do - bs <- S.createf chunkSize $ \fpbuf' -> - S.memcpyf fpbuf' fpbuf chunkSize + bs <- S.createFp chunkSize $ \fpbuf' -> + S.memcpyFp fpbuf' fpbuf chunkSize -- FIXME: We could reuse the trimmed buffer here. return $ Yield1 bs (mkCIOS False) | otherwise = diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs index 7ed19351f..a4c4eaff6 100644 --- a/Data/ByteString/Char8.hs +++ b/Data/ByteString/Char8.hs @@ -976,10 +976,10 @@ unlines = \li -> let go [] _ = pure () go (BS src len : srcs) dest = do - memcpyf dest src len - pokefpByteOff dest len (c2w '\n') + memcpyFp dest src len + pokeFpByteOff dest len (c2w '\n') go srcs $ dest `plusForeignPtr` (len + 1) - in unsafeCreatef totLen (go li) + in unsafeCreateFp totLen (go li) -- | 'words' breaks a ByteString up into a list of words, which -- were delimited by Chars representing white space. diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs index b69e8e7c4..e3f04f21f 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal.hs @@ -48,14 +48,14 @@ module Data.ByteString.Internal ( -- * Low level imperative construction empty, - createf, - createfUptoN, - createfUptoN', - createfAndTrim, - createfAndTrim', - unsafeCreatef, - unsafeCreatefUptoN, - unsafeCreatefUptoN', + createFp, + createFpUptoN, + createFpUptoN', + createFpAndTrim, + createFpAndTrim', + unsafeCreateFp, + unsafeCreateFpUptoN, + unsafeCreateFpUptoN', create, createUptoN, createUptoN', @@ -74,12 +74,12 @@ module Data.ByteString.Internal ( -- * Utilities nullForeignPtr, - peekfp, - pokefp, - peekfpByteOff, - pokefpByteOff, + peekFp, + pokeFp, + peekFpByteOff, + pokeFpByteOff, minusForeignPtr, - memcpyf, + memcpyFp, SizeOverflowException, overflowError, checkedAdd, @@ -221,18 +221,18 @@ minusForeignPtr :: ForeignPtr a -> ForeignPtr b -> Int minusForeignPtr (ForeignPtr addr1 _) (ForeignPtr addr2 _) = I# (minusAddr# addr1 addr2) -peekfp :: Storable a => ForeignPtr a -> IO a -peekfp fp = unsafeWithForeignPtr fp peek +peekFp :: Storable a => ForeignPtr a -> IO a +peekFp fp = unsafeWithForeignPtr fp peek -pokefp :: Storable a => ForeignPtr a -> a -> IO () -pokefp fp val = unsafeWithForeignPtr fp $ \p -> poke p val +pokeFp :: Storable a => ForeignPtr a -> a -> IO () +pokeFp fp val = unsafeWithForeignPtr fp $ \p -> poke p val -peekfpByteOff :: Storable a => ForeignPtr a -> Int -> IO a -peekfpByteOff fp off = unsafeWithForeignPtr fp $ \p -> +peekFpByteOff :: Storable a => ForeignPtr a -> Int -> IO a +peekFpByteOff fp off = unsafeWithForeignPtr fp $ \p -> peekByteOff p off -pokefpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () -pokefpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> +pokeFpByteOff :: Storable a => ForeignPtr b -> Int -> a -> IO () +pokeFpByteOff fp off val = unsafeWithForeignPtr fp $ \p -> pokeByteOff p off val -- ----------------------------------------------------------------------------- @@ -348,7 +348,7 @@ findIndexOrLength k (BS x l) = g ptr = go 0 where go !n | n >= l = return l - | otherwise = do w <- peekfp $ ptr `plusForeignPtr` n + | otherwise = do w <- peekFp $ ptr `plusForeignPtr` n if k w then return n else go (n+1) @@ -372,17 +372,17 @@ packChars cs = unsafePackLenChars (List.length cs) cs unsafePackLenBytes :: Int -> [Word8] -> ByteString unsafePackLenBytes len xs0 = - unsafeCreatef len $ \p -> go p xs0 + unsafeCreateFp len $ \p -> go p xs0 where go !_ [] = return () - go !p (x:xs) = pokefp p x >> go (p `plusForeignPtr` 1) xs + go !p (x:xs) = pokeFp p x >> go (p `plusForeignPtr` 1) xs unsafePackLenChars :: Int -> [Char] -> ByteString unsafePackLenChars len cs0 = - unsafeCreatef len $ \p -> go p cs0 + unsafeCreateFp len $ \p -> go p cs0 where go !_ [] = return () - go !p (c:cs) = pokefp p (c2w c) >> go (p `plusForeignPtr` 1) cs + go !p (c:cs) = pokeFp p (c2w c) >> go (p `plusForeignPtr` 1) cs -- | /O(n)/ Pack a null-terminated sequence of bytes, pointed to by an @@ -467,20 +467,20 @@ unsafePackLenLiteral len addr# = packUptoLenBytes :: Int -> [Word8] -> (ByteString, [Word8]) packUptoLenBytes len xs0 = - unsafeCreatefUptoN' len $ \p0 -> + unsafeCreateFpUptoN' len $ \p0 -> let p_end = plusForeignPtr p0 len go !p [] = return (p `minusForeignPtr` p0, []) go !p xs | p == p_end = return (len, xs) - go !p (x:xs) = pokefp p x >> go (p `plusForeignPtr` 1) xs + go !p (x:xs) = pokeFp p x >> go (p `plusForeignPtr` 1) xs in go p0 xs0 packUptoLenChars :: Int -> [Char] -> (ByteString, [Char]) packUptoLenChars len cs0 = - unsafeCreatefUptoN' len $ \p0 -> + unsafeCreateFpUptoN' len $ \p0 -> let p_end = plusForeignPtr p0 len go !p [] = return (p `minusForeignPtr` p0, []) go !p cs | p == p_end = return (len, cs) - go !p (c:cs) = pokefp p (c2w c) >> go (p `plusForeignPtr` 1) cs + go !p (c:cs) = pokeFp p (c2w c) >> go (p `plusForeignPtr` 1) cs in go p0 cs0 -- Unpacking bytestrings into lists efficiently is a tradeoff: on the one hand @@ -590,77 +590,77 @@ toForeignPtr0 (BS ps l) = (ps, l) -- | A way of creating ByteStrings outside the IO monad. The @Int@ -- argument gives the final size of the ByteString. -unsafeCreatef :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString -unsafeCreatef l f = unsafeDupablePerformIO (createf l f) -{-# INLINE unsafeCreatef #-} +unsafeCreateFp :: Int -> (ForeignPtr Word8 -> IO ()) -> ByteString +unsafeCreateFp l f = unsafeDupablePerformIO (createFp l f) +{-# INLINE unsafeCreateFp #-} --- | Like 'unsafeCreatef' but instead of giving the final size of the +-- | Like 'unsafeCreateFp' but instead of giving the final size of the -- ByteString, it is just an upper bound. The inner action returns --- the actual size. Unlike 'createfAndTrim' the ByteString is not +-- the actual size. Unlike 'createFpAndTrim' the ByteString is not -- reallocated if the final size is less than the estimated size. -unsafeCreatefUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString -unsafeCreatefUptoN l f = unsafeDupablePerformIO (createfUptoN l f) -{-# INLINE unsafeCreatefUptoN #-} +unsafeCreateFpUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> ByteString +unsafeCreateFpUptoN l f = unsafeDupablePerformIO (createFpUptoN l f) +{-# INLINE unsafeCreateFpUptoN #-} -unsafeCreatefUptoN' +unsafeCreateFpUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> (ByteString, a) -unsafeCreatefUptoN' l f = unsafeDupablePerformIO (createfUptoN' l f) -{-# INLINE unsafeCreatefUptoN' #-} +unsafeCreateFpUptoN' l f = unsafeDupablePerformIO (createFpUptoN' l f) +{-# INLINE unsafeCreateFpUptoN' #-} -- | Create ByteString of size @l@ and use action @f@ to fill its contents. -createf :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString -createf l action = do +createFp :: Int -> (ForeignPtr Word8 -> IO ()) -> IO ByteString +createFp l action = do fp <- mallocByteString l action fp return $! BS fp l -{-# INLINE createf #-} +{-# INLINE createFp #-} -- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' -- starting at the given 'Ptr' and returns the actual utilized length, --- @`createfUptoN'` l f@ returns the filled 'ByteString'. -createfUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString -createfUptoN l action = do +-- @`createFpUptoN'` l f@ returns the filled 'ByteString'. +createFpUptoN :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createFpUptoN l action = do fp <- mallocByteString l l' <- action fp assert (l' <= l) $ return $! BS fp l' -{-# INLINE createfUptoN #-} +{-# INLINE createFpUptoN #-} --- | Like 'createfUptoN', but also returns an additional value created by the +-- | Like 'createFpUptoN', but also returns an additional value created by the -- action. -createfUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) -createfUptoN' l action = do +createFpUptoN' :: Int -> (ForeignPtr Word8 -> IO (Int, a)) -> IO (ByteString, a) +createFpUptoN' l action = do fp <- mallocByteString l (l', res) <- action fp assert (l' <= l) $ return (BS fp l', res) -{-# INLINE createfUptoN' #-} +{-# INLINE createFpUptoN' #-} -- | Given the maximum size needed and a function to make the contents --- of a ByteString, createfAndTrim makes the 'ByteString'. The generating +-- of a ByteString, createFpAndTrim makes the 'ByteString'. The generating -- function is required to return the actual final size (<= the maximum -- size), and the resulting byte array is reallocated to this size. -- --- createfAndTrim is the main mechanism for creating custom, efficient +-- createFpAndTrim is the main mechanism for creating custom, efficient -- ByteString functions, using Haskell or C functions to fill the space. -- -createfAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString -createfAndTrim l action = do +createFpAndTrim :: Int -> (ForeignPtr Word8 -> IO Int) -> IO ByteString +createFpAndTrim l action = do fp <- mallocByteString l l' <- action fp if assert (0 <= l' && l' <= l) $ l' >= l then return $! BS fp l - else createf l' $ \fp' -> memcpyf fp' fp l' -{-# INLINE createfAndTrim #-} + else createFp l' $ \fp' -> memcpyFp fp' fp l' +{-# INLINE createFpAndTrim #-} -createfAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) -createfAndTrim' l action = do +createFpAndTrim' :: Int -> (ForeignPtr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) +createFpAndTrim' l action = do fp <- mallocByteString l (off, l', res) <- action fp if assert (0 <= l' && l' <= l) $ l' >= l then return (BS fp l, res) - else do ps <- createf l' $ \fp' -> - memcpyf fp' (fp `plusForeignPtr` off) l' + else do ps <- createFp l' $ \fp' -> + memcpyFp fp' (fp `plusForeignPtr` off) l' return (ps, res) -{-# INLINE createfAndTrim' #-} +{-# INLINE createFpAndTrim' #-} wrapAction :: (Ptr Word8 -> IO res) -> ForeignPtr Word8 -> IO res @@ -670,7 +670,7 @@ wrapAction = flip withForeignPtr -- | A way of creating ByteStrings outside the IO monad. The @Int@ -- argument gives the final size of the ByteString. unsafeCreate :: Int -> (Ptr Word8 -> IO ()) -> ByteString -unsafeCreate l f = unsafeCreatef l (wrapAction f) +unsafeCreate l f = unsafeCreateFp l (wrapAction f) {-# INLINE unsafeCreate #-} -- | Like 'unsafeCreate' but instead of giving the final size of the @@ -678,24 +678,24 @@ unsafeCreate l f = unsafeCreatef l (wrapAction f) -- the actual size. Unlike 'createAndTrim' the ByteString is not -- reallocated if the final size is less than the estimated size. unsafeCreateUptoN :: Int -> (Ptr Word8 -> IO Int) -> ByteString -unsafeCreateUptoN l f = unsafeCreatefUptoN l (wrapAction f) +unsafeCreateUptoN l f = unsafeCreateFpUptoN l (wrapAction f) {-# INLINE unsafeCreateUptoN #-} -- | @since 0.10.12.0 unsafeCreateUptoN' :: Int -> (Ptr Word8 -> IO (Int, a)) -> (ByteString, a) -unsafeCreateUptoN' l f = unsafeCreatefUptoN' l (wrapAction f) +unsafeCreateUptoN' l f = unsafeCreateFpUptoN' l (wrapAction f) {-# INLINE unsafeCreateUptoN' #-} -- | Create ByteString of size @l@ and use action @f@ to fill its contents. create :: Int -> (Ptr Word8 -> IO ()) -> IO ByteString -create l action = createf l (wrapAction action) +create l action = createFp l (wrapAction action) {-# INLINE create #-} -- | Given a maximum size @l@ and an action @f@ that fills the 'ByteString' -- starting at the given 'Ptr' and returns the actual utilized length, -- @`createUptoN'` l f@ returns the filled 'ByteString'. createUptoN :: Int -> (Ptr Word8 -> IO Int) -> IO ByteString -createUptoN l action = createfUptoN l (wrapAction action) +createUptoN l action = createFpUptoN l (wrapAction action) {-# INLINE createUptoN #-} -- | Like 'createUptoN', but also returns an additional value created by the @@ -703,7 +703,7 @@ createUptoN l action = createfUptoN l (wrapAction action) -- -- @since 0.10.12.0 createUptoN' :: Int -> (Ptr Word8 -> IO (Int, a)) -> IO (ByteString, a) -createUptoN' l action = createfUptoN' l (wrapAction action) +createUptoN' l action = createFpUptoN' l (wrapAction action) {-# INLINE createUptoN' #-} -- | Given the maximum size needed and a function to make the contents @@ -715,11 +715,11 @@ createUptoN' l action = createfUptoN' l (wrapAction action) -- ByteString functions, using Haskell or C functions to fill the space. -- createAndTrim :: Int -> (Ptr Word8 -> IO Int) -> IO ByteString -createAndTrim l action = createfAndTrim l (wrapAction action) +createAndTrim l action = createFpAndTrim l (wrapAction action) {-# INLINE createAndTrim #-} createAndTrim' :: Int -> (Ptr Word8 -> IO (Int, Int, a)) -> IO (ByteString, a) -createAndTrim' l action = createfAndTrim' l (wrapAction action) +createAndTrim' l action = createFpAndTrim' l (wrapAction action) {-# INLINE createAndTrim' #-} @@ -762,10 +762,10 @@ append :: ByteString -> ByteString -> ByteString append (BS _ 0) b = b append a (BS _ 0) = a append (BS fp1 len1) (BS fp2 len2) = - unsafeCreatef (checkedAdd "append" len1 len2) $ \destptr1 -> do + unsafeCreateFp (checkedAdd "append" len1 len2) $ \destptr1 -> do let destptr2 = destptr1 `plusForeignPtr` len1 - memcpyf destptr1 fp1 len1 - memcpyf destptr2 fp2 len2 + memcpyFp destptr1 fp1 len1 + memcpyFp destptr2 fp2 len2 concat :: [ByteString] -> ByteString concat = \bss0 -> goLen0 bss0 bss0 @@ -801,13 +801,13 @@ concat = \bss0 -> goLen0 bss0 bss0 goLen bss0 !total (BS _ len:bss) = goLen bss0 total' bss where total' = checkedAdd "concat" total len goLen bss0 total [] = - unsafeCreatef total $ \ptr -> goCopy bss0 ptr + unsafeCreateFp total $ \ptr -> goCopy bss0 ptr -- Copy the data goCopy [] !_ = return () goCopy (BS _ 0 :bss) !ptr = goCopy bss ptr goCopy (BS fp len:bss) !ptr = do - memcpyf ptr fp len + memcpyFp ptr fp len goCopy bss (ptr `plusForeignPtr` len) {-# NOINLINE concat #-} @@ -851,12 +851,12 @@ stimesNonNegativeInt n (BS fp len) | n == 0 = empty | n == 1 = BS fp len | len == 0 = empty - | len == 1 = unsafeCreatef n $ \destfptr -> do - byte <- peekfp fp + | len == 1 = unsafeCreateFp n $ \destfptr -> do + byte <- peekFp fp void $ unsafeWithForeignPtr destfptr $ \destptr -> memset destptr byte (fromIntegral n) - | otherwise = unsafeCreatef size $ \destptr -> do - memcpyf destptr fp len + | otherwise = unsafeCreateFp size $ \destptr -> do + memcpyFp destptr fp len fillFrom destptr len where size = checkedMultiply "stimes" n len @@ -865,9 +865,9 @@ stimesNonNegativeInt n (BS fp len) fillFrom :: ForeignPtr Word8 -> Int -> IO () fillFrom destptr copied | copied <= halfSize = do - memcpyf (destptr `plusForeignPtr` copied) destptr copied + memcpyFp (destptr `plusForeignPtr` copied) destptr copied fillFrom destptr (copied * 2) - | otherwise = memcpyf (destptr `plusForeignPtr` copied) destptr (size - copied) + | otherwise = memcpyFp (destptr `plusForeignPtr` copied) destptr (size - copied) ------------------------------------------------------------------------ @@ -1021,9 +1021,9 @@ foreign import ccall unsafe "string.h memcpy" c_memcpy memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO () memcpy p q s = void $ c_memcpy p q (fromIntegral s) -memcpyf :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () -memcpyf fp fq s = unsafeWithForeignPtr fp $ \p -> - unsafeWithForeignPtr fq $ \q -> memcpy p q s +memcpyFp :: ForeignPtr Word8 -> ForeignPtr Word8 -> Int -> IO () +memcpyFp fp fq s = unsafeWithForeignPtr fp $ \p -> + unsafeWithForeignPtr fq $ \q -> memcpy p q s {- foreign import ccall unsafe "string.h memmove" c_memmove diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs index 316147111..bdf53a84c 100644 --- a/Data/ByteString/Lazy.hs +++ b/Data/ByteString/Lazy.hs @@ -434,7 +434,7 @@ intersperse w (Chunk c cs) = Chunk (S.intersperse w c) (foldrChunks (Chunk . intersperse') Empty cs) where intersperse' :: P.ByteString -> P.ByteString intersperse' (S.BS fp l) = - S.unsafeCreatef (2*l) $ \fp' -> + S.unsafeCreateFp (2*l) $ \fp' -> S.unsafeWithForeignPtr fp' $ \p' -> S.unsafeWithForeignPtr fp $ \p -> do poke p' w diff --git a/Data/ByteString/Lazy/Internal.hs b/Data/ByteString/Lazy/Internal.hs index 1fd7bbf28..9ecf1ec1f 100644 --- a/Data/ByteString/Lazy/Internal.hs +++ b/Data/ByteString/Lazy/Internal.hs @@ -312,13 +312,13 @@ toStrict = \cs -> goLen0 cs cs goLen cs0 !total (Chunk (S.BS _ cl) cs) = goLen cs0 (S.checkedAdd "Lazy.toStrict" total cl) cs goLen cs0 total Empty = - S.unsafeCreatef total $ \ptr -> goCopy cs0 ptr + S.unsafeCreateFp total $ \ptr -> goCopy cs0 ptr -- Copy the data goCopy Empty !_ = return () goCopy (Chunk (S.BS _ 0 ) cs) !ptr = goCopy cs ptr goCopy (Chunk (S.BS fp len) cs) !ptr = do - S.memcpyf ptr fp len + S.memcpyFp ptr fp len goCopy cs (ptr `S.plusForeignPtr` len) -- See the comment on Data.ByteString.Internal.concat for some background on -- this implementation. From 299e7ac7f31db84780eb0bc8f2929e3a3f00537a Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sun, 7 Aug 2022 16:50:43 -0400 Subject: [PATCH 10/13] [NONBUILDABLE] rename Data.ByteString.Internal to Data.ByteString.Internal.Type. The re-export-only compatibility module to replace it at Data.ByteString.Internal is intentionally deleted from this commit, to allow for a more reviewable diff. --- Data/ByteString.hs | 2 +- Data/ByteString/Builder/Internal.hs | 2 +- Data/ByteString/Char8.hs | 2 +- Data/ByteString/{Internal.hs => Internal/Type.hs} | 13 ++++--------- Data/ByteString/Lazy.hs | 2 +- Data/ByteString/Lazy/Internal.hs | 2 +- bytestring.cabal | 1 + 7 files changed, 10 insertions(+), 14 deletions(-) rename Data/ByteString/{Internal.hs => Internal/Type.hs} (98%) diff --git a/Data/ByteString.hs b/Data/ByteString.hs index aecec7974..bf914d013 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -230,7 +230,7 @@ import Prelude hiding (reverse,head,tail,last,init,null import Data.Bits (finiteBitSize, shiftL, (.|.), (.&.)) -import Data.ByteString.Internal +import Data.ByteString.Internal.Type import Data.ByteString.Lazy.Internal (fromStrict, toStrict) import Data.ByteString.Unsafe diff --git a/Data/ByteString/Builder/Internal.hs b/Data/ByteString/Builder/Internal.hs index c7beeb1f3..a05beead9 100644 --- a/Data/ByteString/Builder/Internal.hs +++ b/Data/ByteString/Builder/Internal.hs @@ -133,7 +133,7 @@ import Data.Semigroup (Semigroup((<>))) #endif import qualified Data.ByteString as S -import qualified Data.ByteString.Internal as S +import qualified Data.ByteString.Internal.Type as S import qualified Data.ByteString.Lazy.Internal as L import qualified Data.ByteString.Short.Internal as Sh diff --git a/Data/ByteString/Char8.hs b/Data/ByteString/Char8.hs index a4c4eaff6..1aa70e82f 100644 --- a/Data/ByteString/Char8.hs +++ b/Data/ByteString/Char8.hs @@ -275,7 +275,7 @@ import Data.ByteString (null,length,tail,init,append ,useAsCString,useAsCStringLen ) -import Data.ByteString.Internal +import Data.ByteString.Internal.Type import Data.ByteString.ReadInt import Data.ByteString.ReadNat diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal/Type.hs similarity index 98% rename from Data/ByteString/Internal.hs rename to Data/ByteString/Internal/Type.hs index e3f04f21f..a563ad54c 100644 --- a/Data/ByteString/Internal.hs +++ b/Data/ByteString/Internal/Type.hs @@ -9,7 +9,7 @@ {-# OPTIONS_HADDOCK not-home #-} -- | --- Module : Data.ByteString.Internal +-- Module : Data.ByteString.Internal.Type -- Copyright : (c) Don Stewart 2006-2008 -- (c) Duncan Coutts 2006-2012 -- License : BSD-style @@ -17,15 +17,10 @@ -- Stability : unstable -- Portability : non-portable -- --- A module containing semi-public 'ByteString' internals. This exposes the --- 'ByteString' representation and low level construction functions. As such --- all the functions in this module are unsafe. The API is also not stable. +-- A module containing private 'ByteString' internals. +-- For now, it contains exactly what was once in Data.ByteString.Internal. -- --- Where possible application should instead use the functions from the normal --- public interface modules, such as "Data.ByteString.Unsafe". Packages that --- extend the ByteString system at a low level will need to use this module. --- -module Data.ByteString.Internal ( +module Data.ByteString.Internal.Type ( -- * The @ByteString@ type and representation ByteString diff --git a/Data/ByteString/Lazy.hs b/Data/ByteString/Lazy.hs index bdf53a84c..d9ae76acc 100644 --- a/Data/ByteString/Lazy.hs +++ b/Data/ByteString/Lazy.hs @@ -231,7 +231,7 @@ import qualified Data.List as List import qualified Data.Bifunctor as BF import qualified Data.ByteString as P (ByteString) -- type name only import qualified Data.ByteString as S -- S for strict (hmm...) -import qualified Data.ByteString.Internal as S +import qualified Data.ByteString.Internal.Type as S import qualified Data.ByteString.Unsafe as S import qualified Data.ByteString.Lazy.Internal.Deque as D import Data.ByteString.Lazy.Internal diff --git a/Data/ByteString/Lazy/Internal.hs b/Data/ByteString/Lazy/Internal.hs index 9ecf1ec1f..5962e5ae8 100644 --- a/Data/ByteString/Lazy/Internal.hs +++ b/Data/ByteString/Lazy/Internal.hs @@ -49,7 +49,7 @@ module Data.ByteString.Lazy.Internal ( import Prelude hiding (concat) -import qualified Data.ByteString.Internal as S +import qualified Data.ByteString.Internal.Type as S import Data.Word (Word8) import Foreign.Storable (Storable(sizeOf)) diff --git a/bytestring.cabal b/bytestring.cabal index 009aab131..025a6bd81 100644 --- a/bytestring.cabal +++ b/bytestring.cabal @@ -99,6 +99,7 @@ library Data.ByteString.Builder.RealFloat.D2S Data.ByteString.Builder.RealFloat.Internal Data.ByteString.Builder.RealFloat.TableGenerator + Data.ByteString.Internal.Type Data.ByteString.Lazy.Internal.Deque Data.ByteString.Lazy.ReadInt Data.ByteString.Lazy.ReadNat From 15cbde9a816ff1e8d7476bc8438de6266356e689 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sun, 7 Aug 2022 16:53:02 -0400 Subject: [PATCH 11/13] Add compatibility stub "Data.ByteString.Internal" --- Data/ByteString/Internal.hs | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Data/ByteString/Internal.hs diff --git a/Data/ByteString/Internal.hs b/Data/ByteString/Internal.hs new file mode 100644 index 000000000..b4481a833 --- /dev/null +++ b/Data/ByteString/Internal.hs @@ -0,0 +1,94 @@ +{-# OPTIONS_HADDOCK not-home #-} + +-- | +-- Module : Data.ByteString.Internal +-- Copyright : (c) Don Stewart 2006-2008 +-- (c) Duncan Coutts 2006-2012 +-- License : BSD-style +-- Maintainer : dons00@gmail.com, duncan@community.haskell.org +-- Stability : unstable +-- Portability : non-portable +-- +-- A module containing semi-public 'ByteString' internals. This exposes the +-- 'ByteString' representation and low level construction functions. As such +-- all the functions in this module are unsafe. The API is also not stable. +-- +-- Where possible application should instead use the functions from the normal +-- public interface modules, such as "Data.ByteString.Unsafe". Packages that +-- extend the ByteString system at a low level will need to use this module. +-- +module Data.ByteString.Internal ( + + -- * The @ByteString@ type and representation + ByteString + ( BS + , PS -- backwards compatibility shim + ), + + StrictByteString, + + -- * Internal indexing + findIndexOrLength, + + -- * Conversion with lists: packing and unpacking + packBytes, packUptoLenBytes, unsafePackLenBytes, + packChars, packUptoLenChars, unsafePackLenChars, + unpackBytes, unpackAppendBytesLazy, unpackAppendBytesStrict, + unpackChars, unpackAppendCharsLazy, unpackAppendCharsStrict, + unsafePackAddress, unsafePackLenAddress, + unsafePackLiteral, unsafePackLenLiteral, + + -- * Low level imperative construction + empty, + create, + createUptoN, + createUptoN', + createAndTrim, + createAndTrim', + unsafeCreate, + unsafeCreateUptoN, + unsafeCreateUptoN', + mallocByteString, + + -- * Conversion to and from ForeignPtrs + fromForeignPtr, + toForeignPtr, + fromForeignPtr0, + toForeignPtr0, + + -- * Utilities + nullForeignPtr, + SizeOverflowException, + overflowError, + checkedAdd, + checkedMultiply, + + -- * Standard C Functions + c_strlen, + c_free_finalizer, + + memchr, + memcmp, + memcpy, + memset, + + -- * cbits functions + c_reverse, + c_intersperse, + c_maximum, + c_minimum, + c_count, + c_sort, + + -- * Chars + w2c, c2w, isSpaceWord8, isSpaceChar8, + + -- * Deprecated and unmentionable + accursedUnutterablePerformIO, + + -- * Exported compatibility shim + plusForeignPtr, + unsafeWithForeignPtr + ) where + +import Data.ByteString.Internal.Type From eee138cbac956f85de82b0dcaed65d8350155d3a Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Sun, 14 Aug 2022 08:46:57 -0400 Subject: [PATCH 12/13] Replace Data.ByteString.Internal.Type's description --- Data/ByteString/Internal/Type.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Data/ByteString/Internal/Type.hs b/Data/ByteString/Internal/Type.hs index a563ad54c..d80daa48b 100644 --- a/Data/ByteString/Internal/Type.hs +++ b/Data/ByteString/Internal/Type.hs @@ -17,8 +17,8 @@ -- Stability : unstable -- Portability : non-portable -- --- A module containing private 'ByteString' internals. --- For now, it contains exactly what was once in Data.ByteString.Internal. +-- The 'ByteString' type, its instances, and whatever related +-- utilities the bytestring developers see fit to use internally. -- module Data.ByteString.Internal.Type ( From b498c4d3e46a17b4c8560e002b2fa162fd2c7f68 Mon Sep 17 00:00:00 2001 From: Matthew Craven Date: Thu, 25 Aug 2022 22:11:38 -0400 Subject: [PATCH 13/13] Remove the use of withRawBuffer, too. It has the same performance issues as does withForeignPtr. --- Data/ByteString.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Data/ByteString.hs b/Data/ByteString.hs index bf914d013..dfb015d89 100644 --- a/Data/ByteString.hs +++ b/Data/ByteString.hs @@ -1839,9 +1839,7 @@ hGetLine h = mkPS :: RawBuffer Word8 -> Int -> Int -> IO ByteString mkPS buf start end = - createFp len $ \fp -> - unsafeWithForeignPtr fp $ \p -> - withRawBuffer buf $ \pbuf -> memcpy p (pbuf `plusPtr` start) len + createFp len $ \fp -> memcpyFp fp (buf `plusForeignPtr` start) len where len = end - start