1- {-# LANGUAGE BangPatterns #-}
21{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
32{-# OPTIONS_HADDOCK prune #-}
43{-# LANGUAGE Trustworthy #-}
54
5+ {-# LANGUAGE BangPatterns #-}
6+ {-# LANGUAGE TypeApplications #-}
7+ {-# LANGUAGE ScopedTypeVariables #-}
8+
69-- |
710-- Module : Data.ByteString.Lazy
811-- Copyright : (c) Don Stewart 2006
@@ -237,9 +240,9 @@ import qualified Data.ByteString as P (ByteString) -- type name only
237240import qualified Data.ByteString as S -- S for strict (hmm...)
238241import qualified Data.ByteString.Internal.Type as S
239242import qualified Data.ByteString.Unsafe as S
240- import qualified Data.ByteString.Lazy.Internal.Deque as D
241243import Data.ByteString.Lazy.Internal
242244
245+ import Control.Exception (assert )
243246import Control.Monad (mplus )
244247import Data.Word (Word8 )
245248import Data.Int (Int64 )
@@ -790,15 +793,75 @@ take i cs0 = take' i cs0
790793--
791794-- @since 0.11.2.0
792795takeEnd :: Int64 -> ByteString -> ByteString
793- takeEnd i _ | i <= 0 = Empty
794- takeEnd i cs0 = takeEnd' i cs0
795- where takeEnd' 0 _ = Empty
796- takeEnd' n cs =
797- snd $ foldrChunks takeTuple (n,Empty ) cs
798- takeTuple _ (0 , cs) = (0 , cs)
799- takeTuple c (n, cs)
800- | n > fromIntegral (S. length c) = (n - fromIntegral (S. length c), Chunk c cs)
801- | otherwise = (0 , Chunk (S. takeEnd (fromIntegral n) c) cs)
796+ takeEnd i bs
797+ | i <= 0 = Empty
798+ | otherwise = splitAtEndFold (\ _ res -> res) id i bs
799+
800+ -- | Helper function for implementing 'takeEnd' and 'dropEnd'
801+ splitAtEndFold
802+ :: forall result
803+ . (S. StrictByteString -> result -> result )
804+ -- ^ What to do when one chunk of output is ready
805+ -- (The StrictByteString will not be empty.)
806+ -> (ByteString -> result )
807+ -- ^ What to do when the split-point is reached
808+ -> Int64
809+ -- ^ Number of bytes to leave at the end (must be strictly positive)
810+ -> ByteString -- ^ Input ByteString
811+ -> result
812+ {-# INLINE splitAtEndFold #-}
813+ splitAtEndFold step end len bs0 = assert (len > 0 ) $ case bs0 of
814+ Empty -> end Empty
815+ Chunk c t -> goR len c t t
816+ where
817+ -- Idea: Keep two references into the input ByteString:
818+ -- "toSplit" tracks the current split point,
819+ -- "toScan" tracks the yet-unprocessed tail.
820+ -- When they are closer than "len" bytes apart, process more input. ("goR")
821+ -- When they are at least "len" bytes apart, produce more output. ("goL")
822+ -- We always have that "toScan" is a suffix of "toSplit",
823+ -- and "toSplit" is a suffix of the original input (bs0).
824+ goR :: Int64 -> S. StrictByteString -> ByteString -> ByteString -> result
825+ goR ! undershoot nextOutput@ (S. BS noFp noLen) toSplit toScan =
826+ assert (undershoot > 0 ) $
827+ -- INVARIANT: length toSplit == length toScan + len - undershoot
828+ -- (not 'assert'ed because that would break our laziness properties)
829+ case toScan of
830+ Empty
831+ | undershoot >= intToInt64 noLen
832+ -> end (Chunk nextOutput toSplit)
833+ | undershootW <- fromIntegral @ Int64 @ Int undershoot
834+ -- conversion Int64->Int is OK because 0 < undershoot < noLen
835+ , splitIndex <- noLen - undershootW
836+ , beforeSplit <- S. BS noFp splitIndex
837+ , afterSplit <- S. BS (noFp `S.plusForeignPtr` splitIndex) undershootW
838+ -> step beforeSplit $ end (Chunk afterSplit toSplit)
839+
840+ Chunk (S. BS _ cLen) newBsR
841+ | cLen64 <- intToInt64 cLen
842+ , undershoot > cLen64
843+ -> goR (undershoot - cLen64) nextOutput toSplit newBsR
844+ | undershootW <- fromIntegral @ Int64 @ Int undershoot
845+ -> step nextOutput $ goL (cLen - undershootW) toSplit newBsR
846+
847+ goL :: Int -> ByteString -> ByteString -> result
848+ goL ! overshoot toSplit toScan =
849+ assert (overshoot >= 0 ) $
850+ -- INVARIANT: length toSplit == length toScan + len + intToInt64 overshoot
851+ -- (not 'assert'ed because that would break our laziness properties)
852+ case toSplit of
853+ Empty -> splitAtEndFoldInvariantFailed
854+ Chunk c@ (S. BS _ cLen) newBsL
855+ | overshoot >= cLen
856+ -> step c $ goL (overshoot - cLen) newBsL toScan
857+ | otherwise
858+ -> goR (intToInt64 $ cLen - overshoot) c newBsL toScan
859+
860+ splitAtEndFoldInvariantFailed :: a
861+ -- See Note [Float error calls out of INLINABLE things] in D.B.Internal.Type
862+ splitAtEndFoldInvariantFailed =
863+ moduleError " splitAtEndFold"
864+ " internal error: toSplit not longer than toScan"
802865
803866-- | /O(n\/c)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@
804867-- elements, or 'empty' if @n > 'length' xs@.
@@ -824,44 +887,9 @@ drop i cs0 = drop' i cs0
824887--
825888-- @since 0.11.2.0
826889dropEnd :: Int64 -> ByteString -> ByteString
827- dropEnd i p | i <= 0 = p
828- dropEnd i p = go D. empty p
829- where go :: D. Deque -> ByteString -> ByteString
830- go deque (Chunk c cs)
831- | D. byteLength deque < i = go (D. snoc c deque) cs
832- | otherwise =
833- let (output, deque') = getOutput empty (D. snoc c deque)
834- in foldrChunks Chunk (go deque' cs) output
835- go deque Empty = fromDeque $ dropEndBytes deque i
836-
837- len c = fromIntegral (S. length c)
838-
839- -- get a `ByteString` from all the front chunks of the accumulating deque
840- -- for which we know they won't be dropped
841- getOutput :: ByteString -> D. Deque -> (ByteString , D. Deque )
842- getOutput out deque = case D. popFront deque of
843- Nothing -> (reverseChunks out, deque)
844- Just (x, deque') | D. byteLength deque' >= i ->
845- getOutput (Chunk x out) deque'
846- _ -> (reverseChunks out, deque)
847-
848- -- reverse a `ByteString`s chunks, keeping all internal `S.StrictByteString`s
849- -- unchanged
850- reverseChunks = foldlChunks (flip Chunk ) empty
851-
852- -- drop n elements from the rear of the accumulating `deque`
853- dropEndBytes :: D. Deque -> Int64 -> D. Deque
854- dropEndBytes deque n = case D. popRear deque of
855- Nothing -> deque
856- Just (deque', x) | len x <= n -> dropEndBytes deque' (n - len x)
857- | otherwise ->
858- D. snoc (S. dropEnd (fromIntegral n) x) deque'
859-
860- -- build a lazy ByteString from an accumulating `deque`
861- fromDeque :: D. Deque -> ByteString
862- fromDeque deque =
863- List. foldr chunk Empty (D. front deque) `append`
864- List. foldl' (flip chunk) Empty (D. rear deque)
890+ dropEnd i p
891+ | i <= 0 = p
892+ | otherwise = splitAtEndFold Chunk (const Empty ) i p
865893
866894-- | /O(n\/c)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@.
867895splitAt :: Int64 -> ByteString -> (ByteString , ByteString )
@@ -1688,6 +1716,9 @@ revNonEmptyChunks = List.foldl' (flip Chunk) Empty
16881716revChunks :: [P. ByteString ] -> ByteString
16891717revChunks = List. foldl' (flip chunk) Empty
16901718
1719+ intToInt64 :: Int -> Int64
1720+ intToInt64 = fromIntegral @ Int @ Int64
1721+
16911722-- $IOChunk
16921723--
16931724-- ⚠ Using lazy I\/O functions like 'readFile' or 'hGetContents'
0 commit comments