1
- {-# LANGUAGE BangPatterns #-}
2
1
{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
3
2
{-# OPTIONS_HADDOCK prune #-}
4
3
{-# LANGUAGE Trustworthy #-}
5
4
5
+ {-# LANGUAGE BangPatterns #-}
6
+ {-# LANGUAGE TypeApplications #-}
7
+ {-# LANGUAGE ScopedTypeVariables #-}
8
+
6
9
-- |
7
10
-- Module : Data.ByteString.Lazy
8
11
-- Copyright : (c) Don Stewart 2006
@@ -237,9 +240,9 @@ import qualified Data.ByteString as P (ByteString) -- type name only
237
240
import qualified Data.ByteString as S -- S for strict (hmm...)
238
241
import qualified Data.ByteString.Internal.Type as S
239
242
import qualified Data.ByteString.Unsafe as S
240
- import qualified Data.ByteString.Lazy.Internal.Deque as D
241
243
import Data.ByteString.Lazy.Internal
242
244
245
+ import Control.Exception (assert )
243
246
import Control.Monad (mplus )
244
247
import Data.Word (Word8 )
245
248
import Data.Int (Int64 )
@@ -790,15 +793,75 @@ take i cs0 = take' i cs0
790
793
--
791
794
-- @since 0.11.2.0
792
795
takeEnd :: 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"
802
865
803
866
-- | /O(n\/c)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@
804
867
-- elements, or 'empty' if @n > 'length' xs@.
@@ -824,44 +887,9 @@ drop i cs0 = drop' i cs0
824
887
--
825
888
-- @since 0.11.2.0
826
889
dropEnd :: 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
865
893
866
894
-- | /O(n\/c)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@.
867
895
splitAt :: Int64 -> ByteString -> (ByteString , ByteString )
@@ -1688,6 +1716,9 @@ revNonEmptyChunks = List.foldl' (flip Chunk) Empty
1688
1716
revChunks :: [P. ByteString ] -> ByteString
1689
1717
revChunks = List. foldl' (flip chunk) Empty
1690
1718
1719
+ intToInt64 :: Int -> Int64
1720
+ intToInt64 = fromIntegral @ Int @ Int64
1721
+
1691
1722
-- $IOChunk
1692
1723
--
1693
1724
-- ⚠ Using lazy I\/O functions like 'readFile' or 'hGetContents'
0 commit comments