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