packages feed

byteslice 0.2.13.0 → 0.2.13.1

raw patch · 26 files changed

+2023/−1687 lines, 26 filessetup-changednew-uploaderPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for byteslice +## 0.2.13.1 -- 2024-02-01++* Update package metadata.+ ## 0.2.13.0 -- 2024-01-12  * Add `replicate` and `replicateByte` to `Data.Bytes.Chunks`.@@ -64,7 +68,7 @@ ## 0.2.5.0 -- 2021-01-22  * Add `Data.Bytes.Chunks.concatByteString`.-* Expose `pinnedToByteString` to end users. +* Expose `pinnedToByteString` to end users.  ## 0.2.4.0 -- 2020-10-15 
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
bench/Main.hs view
@@ -1,4 +1,4 @@-import Gauge.Main (defaultMain, bench, whnf, bgroup)+import Gauge.Main (bench, bgroup, defaultMain, whnf)  import Data.Bytes.Types import Data.List (permutations)@@ -10,19 +10,21 @@ naiveMconcat = foldr mappend mempty  main :: IO ()-main = defaultMain-  [ bench "mconcat" $ whnf mconcat mconcatBytes-  , bench "naiveMconcat" $ whnf naiveMconcat mconcatBytes-  , bgroup "replace"-    [ bench "the-dog-and-the-shadow" (whnf replaceMeat theDogAndTheShadow)+main =+  defaultMain+    [ bench "mconcat" $ whnf mconcat mconcatBytes+    , bench "naiveMconcat" $ whnf naiveMconcat mconcatBytes+    , bgroup+        "replace"+        [ bench "the-dog-and-the-shadow" (whnf replaceMeat theDogAndTheShadow)+        ]     ]-  ]  mconcatBytes :: [Bytes]-mconcatBytes = fmap Ascii.fromString $ permutations ['a'..'g']+mconcatBytes = fmap Ascii.fromString $ permutations ['a' .. 'g']  replaceMeat :: Bytes -> Bytes-{-# noinline replaceMeat #-}+{-# NOINLINE replaceMeat #-} replaceMeat x = Bytes.replace meat gruel x  meat :: Bytes@@ -32,14 +34,16 @@ gruel = Ascii.fromString "gruel"  theDogAndTheShadow :: Bytes-theDogAndTheShadow = Ascii.fromString $ concat-  [ "It happened that a Dog had got a piece of meat and was "-  , "carrying it home in his mouth to eat it in peace. "-  , "Now on his way home he had to cross a plank lying across "-  , "a running brook. As he crossed, he looked down and saw his "-  , "own shadow reflected in the water beneath. Thinking it was "-  , "another dog with another piece of meat, he made up his mind "-  , "to have that also. So he made a snap at the shadow in the water, "-  , "but as he opened his mouth the piece of meat fell out, dropped "-  , "into the water and was never seen more."-  ]+theDogAndTheShadow =+  Ascii.fromString $+    concat+      [ "It happened that a Dog had got a piece of meat and was "+      , "carrying it home in his mouth to eat it in peace. "+      , "Now on his way home he had to cross a plank lying across "+      , "a running brook. As he crossed, he looked down and saw his "+      , "own shadow reflected in the water beneath. Thinking it was "+      , "another dog with another piece of meat, he made up his mind "+      , "to have that also. So he made a snap at the shadow in the water, "+      , "but as he opened his mouth the piece of meat fell out, dropped "+      , "into the water and was never seen more."+      ]
byteslice.cabal view
@@ -1,25 +1,26 @@-cabal-version: 2.4-name: byteslice-version: 0.2.13.0-synopsis: Slicing managed and unmanaged memory+cabal-version:   2.4+name:            byteslice+version:         0.2.13.1+synopsis:        Slicing managed and unmanaged memory description:   This library provides types that allow the user to talk about a slice of   a ByteArray or a MutableByteArray. It also offers UnmanagedBytes, which   is kind of like a slice into unmanaged memory. However, it is just an   address and a length.-homepage: https://github.com/andrewthad/byteslice-bug-reports: https://github.com/andrewthad/byteslice/issues-license: BSD-3-Clause-license-file: LICENSE-author: Andrew Martin-maintainer: andrew.thaddeus@gmail.com-copyright: 2020 Andrew Martin-category: Data-extra-source-files: CHANGELOG.md +homepage:        https://github.com/byteverse/byteslice+bug-reports:     https://github.com/byteverse/byteslice/issues+license:         BSD-3-Clause+license-file:    LICENSE+author:          Andrew Martin+maintainer:      amartin@layer3com.com+copyright:       2020 Andrew Martin+category:        Data+extra-doc-files: CHANGELOG.md+ flag avoid-rawmemchr-    default: True-    description: Avoid using rawmemchr which is non-portable GNU libc only+  default:     True+  description: Avoid using rawmemchr which is non-portable GNU libc only  library   exposed-modules:@@ -35,70 +36,84 @@     Data.Bytes.Text.Utf8     Data.Bytes.Text.Windows1252     Data.Bytes.Types+   other-modules:-    Data.Bytes.Search+    Cstrlen     Data.Bytes.Byte-    Data.Bytes.Pure-    Data.Bytes.IO     Data.Bytes.Internal.Show+    Data.Bytes.IO+    Data.Bytes.Pure+    Data.Bytes.Search     Reps-    Cstrlen+   build-depends:-    , base >=4.14 && <5-    , bytestring >=0.10.8 && <0.13-    , natural-arithmetic >=0.1.4-    , primitive >=0.7.4 && <0.10-    , primitive-addr >=0.1 && <0.2-    , primitive-unlifted >=0.1.2 && <2.2-    , run-st >=0.1.1 && <0.2-    , text >=1.2.5-    , text-short >=0.1.3 && <0.2-    , tuples >=0.1 && <0.2-    , vector >=0.12 && <0.14-  hs-source-dirs: src-  ghc-options: -Wall -O2-  if impl(ghc>=9.2)+    , base                >=4.14   && <5+    , bytestring          >=0.10.8 && <0.13+    , natural-arithmetic  >=0.1.4+    , primitive           >=0.7.4  && <0.10+    , primitive-addr      >=0.1    && <0.2+    , primitive-unlifted  >=0.1.2  && <2.2+    , run-st              >=0.1.1  && <0.2+    , text                >=1.2.5+    , text-short          >=0.1.3  && <0.2+    , tuples              >=0.1    && <0.2+    , vector              >=0.12   && <0.14++  hs-source-dirs:   src+  ghc-options:      -Wall -O2++  if impl(ghc >=9.2)     hs-source-dirs: src-new-reps+   else     hs-source-dirs: src-old-reps-  if impl(ghc>=9.0)++  if impl(ghc >=9.0)     hs-source-dirs: src-ghc-cstrlen+   else     hs-source-dirs: src-no-ghc-cstrlen+   default-language: Haskell2010-  include-dirs: include-  includes: bs_custom.h+  include-dirs:     include+  includes:         bs_custom.h   install-includes: bs_custom.h-  c-sources: cbits/bs_custom.c+  c-sources:        cbits/bs_custom.c+   if flag(avoid-rawmemchr)     cc-options: -DAVOID_RAWMEMCHR=1  test-suite test   default-language: Haskell2010-  type: exitcode-stdio-1.0-  hs-source-dirs: test-  main-is: Main.hs-  ghc-options: -Wall -O2+  type:             exitcode-stdio-1.0+  hs-source-dirs:   test+  main-is:          Main.hs+  ghc-options:      -Wall -O2   build-depends:-    , base >=4.11.1 && <5+    , base                >=4.11.1 && <5     , byteslice     , bytestring     , primitive-    , quickcheck-classes >=0.6.4+    , quickcheck-classes  >=0.6.4     , tasty     , tasty-hunit     , tasty-quickcheck+    , text                >=1.2     , transformers-    , text >=1.2  benchmark bench-  type: exitcode-stdio-1.0+  type:             exitcode-stdio-1.0   build-depends:     , base     , byteslice     , gauge     , primitive-  ghc-options: -Wall -O2++  ghc-options:      -Wall -O2   default-language: Haskell2010-  hs-source-dirs: bench-  main-is: Main.hs+  hs-source-dirs:   bench+  main-is:          Main.hs++source-repository head+  type:     git+  location: git://github.com/byteverse/byteslice.git
src-ghc-cstrlen/Cstrlen.hs view
@@ -1,4 +1,4 @@-{-# language MagicHash #-}+{-# LANGUAGE MagicHash #-}  module Cstrlen   ( cstringLength#
src-new-reps/Reps.hs view
@@ -1,16 +1,16 @@-{-# language DataKinds #-}-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language MagicHash #-}-{-# language UnboxedTuples #-}-{-# language UnliftedNewtypes #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UnliftedNewtypes #-}  module Reps-  ( Bytes#(..)+  ( Bytes# (..)   , word8ToWord#   ) where -import GHC.Exts (ByteArray#,Int#,RuntimeRep(..),Levity(Unlifted),TYPE,word8ToWord#)+import GHC.Exts (ByteArray#, Int#, Levity (Unlifted), RuntimeRep (..), TYPE, word8ToWord#) -newtype Bytes# :: TYPE ('TupleRep '[ 'BoxedRep 'Unlifted,'IntRep,'IntRep]) where+newtype Bytes# :: TYPE ('TupleRep '[ 'BoxedRep 'Unlifted, 'IntRep, 'IntRep]) where   Bytes# :: (# ByteArray#, Int#, Int# #) -> Bytes#
src-no-ghc-cstrlen/Cstrlen.hs view
@@ -1,12 +1,12 @@-{-# language MagicHash #-}-{-# language TypeFamilies #-}-{-# language UnliftedFFITypes #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnliftedFFITypes #-}  module Cstrlen   ( cstringLength#   ) where -import GHC.Exts (Addr#,Int#)+import GHC.Exts (Addr#, Int#)  foreign import ccall unsafe "strlen" c_strlen :: Addr# -> Int# 
src-old-reps/Reps.hs view
@@ -1,22 +1,22 @@-{-# language GADTSyntax #-}-{-# language KindSignatures #-}-{-# language MagicHash #-}-{-# language TypeInType #-}-{-# language UnboxedTuples #-}-{-# language UnliftedNewtypes #-}+{-# LANGUAGE GADTSyntax #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeInType #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UnliftedNewtypes #-}  module Reps-  ( Bytes#(..)+  ( Bytes# (..)   , word8ToWord#   ) where -import GHC.Exts (ByteArray#,Int#,Word#,RuntimeRep(..),TYPE)+import GHC.Exts (ByteArray#, Int#, RuntimeRep (..), TYPE, Word#) -newtype Bytes# :: TYPE ('TupleRep '[ 'UnliftedRep,'IntRep,'IntRep]) where+newtype Bytes# :: TYPE ('TupleRep '[ 'UnliftedRep, 'IntRep, 'IntRep]) where   Bytes# :: (# ByteArray#, Int#, Int# #) -> Bytes#  -- In GHC 9.2, the lifted Word8 type started being backed by the -- unlifted Word8# instead of by Word#. This is a compatibility hack. word8ToWord# :: Word# -> Word#-{-# inline word8ToWord# #-}+{-# INLINE word8ToWord# #-} word8ToWord# w = w
src/Data/Bytes.hs view
@@ -1,70 +1,84 @@-{-# language BangPatterns #-}-{-# language BlockArguments #-}-{-# language DataKinds #-}-{-# language DuplicateRecordFields #-}-{-# language KindSignatures #-}-{-# language MagicHash #-}-{-# language NamedFieldPuns #-}-{-# language RankNTypes #-}-{-# language TupleSections #-}-{-# language TypeApplications #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE UnboxedTuples #-} --- | If you are interested in sub-arrays of 'ByteArray's (e.g. writing a binary--- search), it would be grossly inefficient to make a copy of the sub-array. On--- the other hand, it'd be really annoying to track limit indices by hand.------ This module defines the 'Bytes' type which exposes a standard array interface--- for a sub-arrays without copying and without manual index manipulation. ----- For mutable arrays, see 'Data.Bytes.Mutable'.+{- | If you are interested in sub-arrays of 'ByteArray's (e.g. writing a binary+search), it would be grossly inefficient to make a copy of the sub-array. On+the other hand, it'd be really annoying to track limit indices by hand.++This module defines the 'Bytes' type which exposes a standard array interface+for a sub-arrays without copying and without manual index manipulation. --+For mutable arrays, see 'Data.Bytes.Mutable'.+-} module Data.Bytes   ( -- * Types     Bytes+     -- * Constants   , Pure.empty   , Pure.emptyPinned   , Pure.emptyPinnedU+     -- * Properties   , Pure.null   , Pure.length+     -- * Decompose   , uncons   , unsnoc+     -- * Predicates   , any   , all+     -- * Create+     -- ** Sliced   , singleton   , doubleton   , tripleton   , Pure.replicate+     -- ** Unsliced   , singletonU   , doubletonU   , tripletonU   , Pure.replicateU+     -- * Filtering   , takeWhile   , dropWhile   , takeWhileEnd   , dropWhileEnd+     -- * Traversals   , Pure.map   , Pure.mapU+     -- * Folds   , Pure.foldl   , Pure.foldl'   , Pure.foldr   , Pure.foldr'+     -- * Folds with Indices   , Pure.ifoldl'+     -- * Monadic Folds   , Pure.foldlM   , Pure.foldrM+     -- * Common Folds   , elem+     -- * Splitting+     -- ** Unlimited   , Byte.split   , Byte.splitU@@ -72,26 +86,33 @@   , Byte.splitInitU   , Byte.splitNonEmpty   , Byte.splitStream+     -- ** Fixed from Beginning   , Byte.split1   , splitTetragram1   , Byte.split2   , Byte.split3   , Byte.split4+     -- ** Fixed from End   , Byte.splitEnd1+     -- * Combining   , intercalate   , intercalateByte2   , concatArray   , concatArrayU+     -- * Searching   , replace   , findIndices   , findTetragramIndex+     -- * Counting   , Byte.count+     -- * Prefix and Suffix+     -- ** Byte Sequence   , isPrefixOf   , isSuffixOf@@ -101,12 +122,16 @@   , stripSuffix   , stripOptionalSuffix   , longestCommonPrefix+     -- ** C Strings   , stripCStringPrefix+     -- ** Single Byte   , isBytePrefixOf   , isByteSuffixOf+     -- * Equality+     -- ** Fixed Characters   , equalsLatin1   , equalsLatin2@@ -120,22 +145,28 @@   , equalsLatin10   , equalsLatin11   , equalsLatin12+     -- ** C Strings   , equalsCString+     -- * Hashing   , Pure.fnv1a32   , Pure.fnv1a64+     -- * Unsafe Slicing   , Pure.unsafeTake   , Pure.unsafeDrop   , Pure.unsafeIndex   , Pure.unsafeHead+     -- * Copying   , Pure.unsafeCopy+     -- * Pointers   , Pure.pin   , Pure.contents   , touch+     -- * Conversion   , Pure.toByteArray   , Pure.toByteArrayClone@@ -156,38 +187,38 @@   , toShortByteString   , toShortByteStringClone   , toLowerAsciiByteArrayClone+     -- * I\/O with Handles   , BIO.hGet   , readFile   , BIO.hPut+     -- * Unlifted Types   , lift   , unlift+     -- * Length Indexed   , withLength   , withLengthU   ) where -import Prelude hiding (length,takeWhile,dropWhile,null,foldl,foldr,elem,replicate,any,all,readFile,map)+import Prelude hiding (all, any, dropWhile, elem, foldl, foldr, length, map, null, readFile, replicate, takeWhile) -import Control.Monad.Primitive (PrimMonad,primitive_,unsafeIOToPrim)+import Control.Monad.Primitive (PrimMonad, primitive_, unsafeIOToPrim) import Control.Monad.ST.Run (runByteArrayST) import Cstrlen (cstringLength#)-import Data.Bits (unsafeShiftL,(.|.))-import Data.ByteString.Short.Internal (ShortByteString(SBS))-import Data.Bytes.Pure (length,fromByteArray,foldr,unsafeDrop)-import Data.Bytes.Pure (unsafeIndex,toShortByteString)-import Data.Bytes.Types (Bytes(Bytes,array,offset),BytesN(BytesN))-import Data.Bytes.Types (ByteArrayN(ByteArrayN))-import Data.Primitive (Array,ByteArray(ByteArray))+import Data.Bits (unsafeShiftL, (.|.))+import Data.ByteString.Short.Internal (ShortByteString (SBS))+import Data.Bytes.Pure (foldr, fromByteArray, length, toShortByteString, unsafeDrop, unsafeIndex)+import Data.Bytes.Search (findIndices, isInfixOf, replace)+import Data.Bytes.Types (ByteArrayN (ByteArrayN), Bytes (Bytes, array, offset), BytesN (BytesN))+import Data.Primitive (Array, ByteArray (ByteArray)) import Data.Text.Short (ShortText) import Foreign.C.String (CString)-import Foreign.Ptr (Ptr,plusPtr,castPtr)-import GHC.Exts (Addr#,Word#,Int#)-import GHC.Exts (Int(I#),Ptr(Ptr))-import GHC.Word (Word8(W8#),Word32)-import Reps (Bytes#(..),word8ToWord#)-import Data.Bytes.Search (findIndices,replace,isInfixOf)+import Foreign.Ptr (Ptr, castPtr, plusPtr)+import GHC.Exts (Addr#, Int (I#), Int#, Ptr (Ptr), Word#)+import GHC.Word (Word32, Word8 (W8#))+import Reps (Bytes# (..), word8ToWord#)  import qualified Arithmetic.Nat as Nat import qualified Arithmetic.Types as Arithmetic@@ -207,38 +238,43 @@ import qualified GHC.Exts as Exts import qualified GHC.TypeNats as GHC --- | Extract the head and tail of the 'Bytes', returning 'Nothing' if--- it is empty.+{- | Extract the head and tail of the 'Bytes', returning 'Nothing' if+it is empty.+-} uncons :: Bytes -> Maybe (Word8, Bytes)-{-# inline uncons #-}+{-# INLINE uncons #-} uncons b = case length b of   0 -> Nothing   _ -> Just (unsafeIndex b 0, unsafeDrop 1 b) --- | Extract the @init@ and @last@ of the 'Bytes', returning 'Nothing' if--- it is empty.+{- | Extract the @init@ and @last@ of the 'Bytes', returning 'Nothing' if+it is empty.+-} unsnoc :: Bytes -> Maybe (Bytes, Word8)-{-# inline unsnoc #-}+{-# INLINE unsnoc #-} unsnoc b@(Bytes arr off len) = case len of   0 -> Nothing-  _ -> let !len' = len - 1 in-    Just (Bytes arr off len', unsafeIndex b len')+  _ ->+    let !len' = len - 1+     in Just (Bytes arr off len', unsafeIndex b len') --- | Does the byte sequence begin with the given byte? False if the--- byte sequence is empty.+{- | Does the byte sequence begin with the given byte? False if the+byte sequence is empty.+-} isBytePrefixOf :: Word8 -> Bytes -> Bool-{-# inline isBytePrefixOf #-}+{-# INLINE isBytePrefixOf #-} isBytePrefixOf w b = case length b of   0 -> False   _ -> unsafeIndex b 0 == w --- | Does the byte sequence end with the given byte? False if the--- byte sequence is empty.+{- | Does the byte sequence end with the given byte? False if the+byte sequence is empty.+-} isByteSuffixOf :: Word8 -> Bytes -> Bool isByteSuffixOf w b = case len of   0 -> False   _ -> unsafeIndex b (len - 1) == w-  where+ where   len = length b  -- | Is the first argument a prefix of the second argument?@@ -246,7 +282,7 @@ isPrefixOf (Bytes a aOff aLen) (Bytes b bOff bLen) =   -- For prefix and suffix testing, we do not use   -- the sameByteArray optimization that we use in-  -- the Eq instance. Prefix and suffix testing seldom +  -- the Eq instance. Prefix and suffix testing seldom   -- compares a byte array with the same in-memory   -- byte array.   if aLen <= bLen@@ -263,33 +299,33 @@ -- | Find the longest string which is a prefix of both arguments. longestCommonPrefix :: Bytes -> Bytes -> Bytes longestCommonPrefix a b = loop 0-  where+ where   loop :: Int -> Bytes   loop !into     | into < maxLen-      && unsafeIndex a into == unsafeIndex b into-      = loop (into + 1)+        && unsafeIndex a into == unsafeIndex b into =+        loop (into + 1)     | otherwise = Pure.unsafeTake into a   maxLen = min (length a) (length b)  -- | Create a byte sequence with one byte. singleton :: Word8 -> Bytes-{-# inline singleton #-}+{-# INLINE singleton #-} singleton !a = Bytes (singletonU a) 0 1  -- | Create a byte sequence with two bytes. doubleton :: Word8 -> Word8 -> Bytes-{-# inline doubleton #-}+{-# INLINE doubleton #-} doubleton !a !b = Bytes (doubletonU a b) 0 2  -- | Create a byte sequence with three bytes. tripleton :: Word8 -> Word8 -> Word8 -> Bytes-{-# inline tripleton #-}+{-# INLINE tripleton #-} tripleton !a !b !c = Bytes (tripletonU a b c) 0 3  -- | Create an unsliced byte sequence with one byte. singletonU :: Word8 -> ByteArray-{-# inline singletonU #-}+{-# INLINE singletonU #-} singletonU !a = runByteArrayST do   arr <- PM.newByteArray 1   PM.writeByteArray arr 0 a@@ -297,7 +333,7 @@  -- | Create an unsliced byte sequence with two bytes. doubletonU :: Word8 -> Word8 -> ByteArray-{-# inline doubletonU #-}+{-# INLINE doubletonU #-} doubletonU !a !b = runByteArrayST do   arr <- PM.newByteArray 2   PM.writeByteArray arr 0 a@@ -306,7 +342,7 @@  -- | Create an unsliced byte sequence with three bytes. tripletonU :: Word8 -> Word8 -> Word8 -> ByteArray-{-# inline tripletonU #-}+{-# INLINE tripletonU #-} tripletonU !a !b !c = runByteArrayST do   arr <- PM.newByteArray 3   PM.writeByteArray arr 0 a@@ -314,35 +350,43 @@   PM.writeByteArray arr 2 c   PM.unsafeFreezeByteArray arr --- | /O(n)/ Return the suffix of the second string if its prefix--- matches the entire first string.+{- | /O(n)/ Return the suffix of the second string if its prefix+matches the entire first string.+-} stripPrefix :: Bytes -> Bytes -> Maybe Bytes-stripPrefix !pre !str = if pre `isPrefixOf` str-  then Just (Bytes (array str) (offset str + length pre) (length str - length pre))-  else Nothing+stripPrefix !pre !str =+  if pre `isPrefixOf` str+    then Just (Bytes (array str) (offset str + length pre) (length str - length pre))+    else Nothing --- | /O(n)/ Return the suffix of the second string if its prefix--- matches the entire first string. Otherwise, return the second--- string unchanged.+{- | /O(n)/ Return the suffix of the second string if its prefix+matches the entire first string. Otherwise, return the second+string unchanged.+-} stripOptionalPrefix :: Bytes -> Bytes -> Bytes-stripOptionalPrefix !pre !str = if pre `isPrefixOf` str-  then Bytes (array str) (offset str + length pre) (length str - length pre)-  else str+stripOptionalPrefix !pre !str =+  if pre `isPrefixOf` str+    then Bytes (array str) (offset str + length pre) (length str - length pre)+    else str --- | /O(n)/ Return the prefix of the second string if its suffix--- matches the entire first string.+{- | /O(n)/ Return the prefix of the second string if its suffix+matches the entire first string.+-} stripSuffix :: Bytes -> Bytes -> Maybe Bytes-stripSuffix !suf !str = if suf `isSuffixOf` str-  then Just (Bytes (array str) (offset str) (length str - length suf))-  else Nothing+stripSuffix !suf !str =+  if suf `isSuffixOf` str+    then Just (Bytes (array str) (offset str) (length str - length suf))+    else Nothing --- | /O(n)/ Return the prefix of the second string if its suffix--- matches the entire first string. Otherwise, return the second--- string unchanged.+{- | /O(n)/ Return the prefix of the second string if its suffix+matches the entire first string. Otherwise, return the second+string unchanged.+-} stripOptionalSuffix :: Bytes -> Bytes -> Bytes-stripOptionalSuffix !suf !str = if suf `isSuffixOf` str-  then Bytes (array str) (offset str) (length str - length suf)-  else str+stripOptionalSuffix !suf !str =+  if suf `isSuffixOf` str+    then Bytes (array str) (offset str) (length str - length suf)+    else str  -- | Is the byte a member of the byte sequence? elem :: Word8 -> Bytes -> Bool@@ -351,31 +395,33 @@   _ -> False  elemLoop :: Int# -> Word# -> Bytes -> Int#-elemLoop !r !w (Bytes arr@(ByteArray arr# ) off@(I# off# ) len) = case len of+elemLoop !r !w (Bytes arr@(ByteArray arr#) off@(I# off#) len) = case len of   0 -> r-  _ -> elemLoop (Exts.orI# r (Exts.eqWord# w (word8ToWord# (Exts.indexWord8Array# arr# off# )) )) w (Bytes arr (off + 1) (len - 1))+  _ -> elemLoop (Exts.orI# r (Exts.eqWord# w (word8ToWord# (Exts.indexWord8Array# arr# off#)))) w (Bytes arr (off + 1) (len - 1))  -- | Take bytes while the predicate is true. takeWhile :: (Word8 -> Bool) -> Bytes -> Bytes-{-# inline takeWhile #-}+{-# INLINE takeWhile #-} takeWhile k b = Pure.unsafeTake (countWhile k b) b  -- | Drop bytes while the predicate is true. dropWhile :: (Word8 -> Bool) -> Bytes -> Bytes-{-# inline dropWhile #-}+{-# INLINE dropWhile #-} dropWhile k b = Pure.unsafeDrop (countWhile k b) b --- | /O(n)/ 'dropWhileEnd' @p@ @b@ returns the prefix remaining after--- dropping characters that satisfy the predicate @p@ from the end of--- @t@.+{- | /O(n)/ 'dropWhileEnd' @p@ @b@ returns the prefix remaining after+dropping characters that satisfy the predicate @p@ from the end of+@t@.+-} dropWhileEnd :: (Word8 -> Bool) -> Bytes -> Bytes-{-# inline dropWhileEnd #-}+{-# INLINE dropWhileEnd #-} dropWhileEnd k !b = Pure.unsafeTake (length b - countWhileEnd k b) b --- | /O(n)/ 'takeWhileEnd' @p@ @b@ returns the longest suffix of--- elements that satisfy predicate @p@.+{- | /O(n)/ 'takeWhileEnd' @p@ @b@ returns the longest suffix of+elements that satisfy predicate @p@.+-} takeWhileEnd :: (Word8 -> Bool) -> Bytes -> Bytes-{-# inline takeWhileEnd #-}+{-# INLINE takeWhileEnd #-} takeWhileEnd k !b =   let n = countWhileEnd k b    in Bytes (array b) (offset b + length b - n) n@@ -385,36 +431,44 @@ -- match the predicate, this will return the length originally -- provided. countWhile :: (Word8 -> Bool) -> Bytes -> Int-{-# inline countWhile #-}-countWhile k (Bytes arr off0 len0) = go off0 len0 0 where-  go !off !len !n = if len > 0-    then if k (PM.indexByteArray arr off)-      then go (off + 1) (len - 1) (n + 1)+{-# INLINE countWhile #-}+countWhile k (Bytes arr off0 len0) = go off0 len0 0+ where+  go !off !len !n =+    if len > 0+      then+        if k (PM.indexByteArray arr off)+          then go (off + 1) (len - 1) (n + 1)+          else n       else n-    else n  -- Internal. Variant of countWhile that starts from the end -- of the string instead of the beginning. countWhileEnd :: (Word8 -> Bool) -> Bytes -> Int-{-# inline countWhileEnd #-}-countWhileEnd k (Bytes arr off0 len0) = go (off0 + len0 - 1) (len0 - 1) 0 where-  go !off !len !n = if len >= 0-    then if k (PM.indexByteArray arr off)-      then go (off - 1) (len - 1) (n + 1)+{-# INLINE countWhileEnd #-}+countWhileEnd k (Bytes arr off0 len0) = go (off0 + len0 - 1) (len0 - 1) 0+ where+  go !off !len !n =+    if len >= 0+      then+        if k (PM.indexByteArray arr off)+          then go (off - 1) (len - 1) (n + 1)+          else n       else n-    else n --- | Convert a 'String' consisting of only characters in the ASCII block--- to a byte sequence. Any character with a codepoint above @U+007F@ is--- replaced by @U+0000@.+{- | Convert a 'String' consisting of only characters in the ASCII block+to a byte sequence. Any character with a codepoint above @U+007F@ is+replaced by @U+0000@.+-} fromAsciiString :: String -> Bytes {-# DEPRECATED fromAsciiString "use Data.Bytes.Text.Ascii.fromString instead" #-} {-# INLINE fromAsciiString #-} fromAsciiString = Ascii.fromString --- | Convert a 'String' consisting of only characters representable--- by ISO-8859-1. These are encoded with ISO-8859-1. Any character--- with a codepoint above @U+00FF@ is replaced by an unspecified byte.+{- | Convert a 'String' consisting of only characters representable+by ISO-8859-1. These are encoded with ISO-8859-1. Any character+with a codepoint above @U+00FF@ is replaced by an unspecified byte.+-} fromLatinString :: String -> Bytes {-# DEPRECATED fromLatinString "use Data.Bytes.Text.Latin1.fromString instead" #-} {-# INLINE fromLatinString #-}@@ -428,14 +482,20 @@  -- | Copy a primitive string literal into managed memory. fromCString# :: Addr# -> Bytes-fromCString# a = Bytes-  ( runByteArrayST $ do-      dst@(PM.MutableByteArray dst# ) <- PM.newByteArray len-      PM.copyPtrToMutablePrimArray-        (PM.MutablePrimArray dst# ) 0 (Ptr a :: Ptr Word8) len-      PM.unsafeFreezeByteArray dst-  ) 0 len-  where+fromCString# a =+  Bytes+    ( runByteArrayST $ do+        dst@(PM.MutableByteArray dst#) <- PM.newByteArray len+        PM.copyPtrToMutablePrimArray+          (PM.MutablePrimArray dst#)+          0+          (Ptr a :: Ptr Word8)+          len+        PM.unsafeFreezeByteArray dst+    )+    0+    len+ where   len = I# (cstringLength# a)  compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering@@ -443,107 +503,122 @@ compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#) =   compare (I# (Exts.compareByteArrays# ba1# off1# ba2# off2# n#)) 0 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a singleton whose element matches the character?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a singleton whose element matches the character?+-} equalsLatin1 :: Char -> Bytes -> Bool {-# DEPRECATED equalsLatin1 "use Data.Bytes.Text.Latin1.equals1 instead" #-} {-# INLINE equalsLatin1 #-} equalsLatin1 = Latin1.equals1 ---- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a doubleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a doubleton whose elements match the characters?+-} equalsLatin2 :: Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin2 "use Data.Bytes.Text.Latin1.equals2 instead" #-} {-# INLINE equalsLatin2 #-} equalsLatin2 = Latin1.equals2 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a tripleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a tripleton whose elements match the characters?+-} equalsLatin3 :: Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin3 "use Data.Bytes.Text.Latin1.equals3 instead" #-} {-# INLINE equalsLatin3 #-} equalsLatin3 = Latin1.equals3 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a quadrupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a quadrupleton whose elements match the characters?+-} equalsLatin4 :: Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin4 "use Data.Bytes.Text.Latin1.equals4 instead" #-} {-# INLINE equalsLatin4 #-} equalsLatin4 = Latin1.equals4 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a quintupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a quintupleton whose elements match the characters?+-} equalsLatin5 :: Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin5 "use Data.Bytes.Text.Latin1.equals5 instead" #-} {-# INLINE equalsLatin5 #-} equalsLatin5 = Latin1.equals5 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a sextupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a sextupleton whose elements match the characters?+-} equalsLatin6 :: Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin6 "use Data.Bytes.Text.Latin1.equals6 instead" #-} {-# INLINE equalsLatin6 #-} equalsLatin6 = Latin1.equals6 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a septupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a septupleton whose elements match the characters?+-} equalsLatin7 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin7 "use Data.Bytes.Text.Latin1.equals7 instead" #-} {-# INLINE equalsLatin7 #-} equalsLatin7 = Latin1.equals7 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- an octupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+an octupleton whose elements match the characters?+-} equalsLatin8 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin8 "use Data.Bytes.Text.Latin1.equals8 instead" #-} {-# INLINE equalsLatin8 #-} equalsLatin8 = Latin1.equals8 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 9-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 9-tuple whose elements match the characters?+-} equalsLatin9 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin9 "use Data.Bytes.Text.Latin1.equals9 instead" #-} {-# INLINE equalsLatin9 #-} equalsLatin9 = Latin1.equals9 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 10-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 10-tuple whose elements match the characters?+-} equalsLatin10 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin10 "use Data.Bytes.Text.Latin1.equals10 instead" #-} {-# INLINE equalsLatin10 #-} equalsLatin10 = Latin1.equals10 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 11-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 11-tuple whose elements match the characters?+-} equalsLatin11 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin11 "use Data.Bytes.Text.Latin1.equals11 instead" #-} {-# INLINE equalsLatin11 #-} equalsLatin11 = Latin1.equals11 --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 12-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 12-tuple whose elements match the characters?+-} equalsLatin12 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool {-# DEPRECATED equalsLatin12 "use Data.Bytes.Text.Latin1.equals12 instead" #-} {-# INLINE equalsLatin12 #-} equalsLatin12 = Latin1.equals12 --- | Is the byte sequence equal to the @NUL@-terminated C String?--- The C string must be a constant.+{- | Is the byte sequence equal to the @NUL@-terminated C String?+The C string must be a constant.+-} equalsCString :: CString -> Bytes -> Bool-{-# inline equalsCString #-}-equalsCString !ptr0 (Bytes arr off0 len0) = go (castPtr ptr0 :: Ptr Word8) off0 len0 where+{-# INLINE equalsCString #-}+equalsCString !ptr0 (Bytes arr off0 len0) = go (castPtr ptr0 :: Ptr Word8) off0 len0+ where   go !ptr !off !len = case len of     0 -> PM.indexOffPtr ptr 0 == (0 :: Word8)     _ -> case PM.indexOffPtr ptr 0 of       0 -> False       c -> c == PM.indexByteArray arr off && go (plusPtr ptr 1) (off + 1) (len - 1) --- | /O(n)/ Variant of 'stripPrefix' that takes a @NUL@-terminated C String--- as the prefix to test for.+{- | /O(n)/ Variant of 'stripPrefix' that takes a @NUL@-terminated C String+as the prefix to test for.+-} stripCStringPrefix :: CString -> Bytes -> Maybe Bytes-{-# inline stripCStringPrefix #-}-stripCStringPrefix !ptr0 (Bytes arr off0 len0) = go (castPtr ptr0 :: Ptr Word8) off0 len0 where+{-# INLINE stripCStringPrefix #-}+stripCStringPrefix !ptr0 (Bytes arr off0 len0) = go (castPtr ptr0 :: Ptr Word8) off0 len0+ where   go !ptr !off !len = case PM.indexOffPtr ptr 0 of     0 -> Just (Bytes arr off len)     c -> case len of@@ -552,194 +627,211 @@         True -> go (plusPtr ptr 1) (off + 1) (len - 1)         False -> Nothing --- | Touch the byte array backing the byte sequence. This sometimes needed--- after calling 'Pure.contents' so that the @ByteArray@ does not get garbage--- collected.-touch :: PrimMonad m => Bytes -> m ()-touch (Bytes (ByteArray arr) _ _) = unsafeIOToPrim-  (primitive_ (\s -> Exts.touch# arr s))+{- | Touch the byte array backing the byte sequence. This sometimes needed+after calling 'Pure.contents' so that the @ByteArray@ does not get garbage+collected.+-}+touch :: (PrimMonad m) => Bytes -> m ()+touch (Bytes (ByteArray arr) _ _) =+  unsafeIOToPrim+    (primitive_ (\s -> Exts.touch# arr s))  -- | Read an entire file strictly into a 'Bytes'. readFile :: FilePath -> IO Bytes readFile f = Chunks.concat <$> Chunks.readFile f --- | /O(n)/ The intercalate function takes a separator 'Bytes' and a list of--- 'Bytes' and concatenates the list elements by interspersing the separator--- between each element.+{- | /O(n)/ The intercalate function takes a separator 'Bytes' and a list of+'Bytes' and concatenates the list elements by interspersing the separator+between each element.+-} intercalate ::-     Bytes -- ^ Separator (interspersed into the list)-  -> [Bytes] -- ^ List-  -> Bytes+  -- | Separator (interspersed into the list)+  Bytes ->+  -- | List+  [Bytes] ->+  Bytes intercalate !_ [] = mempty intercalate !_ [x] = x intercalate (Bytes sarr soff slen) (Bytes arr0 off0 len0 : bs) = Bytes r 0 fullLen-  where+ where   !fullLen = List.foldl' (\acc (Bytes _ _ len) -> acc + len + slen) 0 bs + len0   r = runByteArrayST $ do     marr <- PM.newByteArray fullLen     PM.copyByteArray marr 0 arr0 off0 len0-    !_ <- F.foldlM-      (\ !currLen (Bytes arr off len) -> do-        PM.copyByteArray marr currLen sarr soff slen-        PM.copyByteArray marr (currLen + slen) arr off len-        pure (currLen + len + slen)-      ) len0 bs+    !_ <-+      F.foldlM+        ( \ !currLen (Bytes arr off len) -> do+            PM.copyByteArray marr currLen sarr soff slen+            PM.copyByteArray marr (currLen + slen) arr off len+            pure (currLen + len + slen)+        )+        len0+        bs     PM.unsafeFreezeByteArray marr --- | Specialization of 'intercalate' where the separator is a single byte and--- there are exactly two byte sequences that are being concatenated.+{- | Specialization of 'intercalate' where the separator is a single byte and+there are exactly two byte sequences that are being concatenated.+-} intercalateByte2 ::-     Word8 -- ^ Separator-  -> Bytes -- ^ First byte sequence-  -> Bytes -- ^ Second byte sequence-  -> Bytes-intercalateByte2 !sep !a !b = Bytes-  { Types.array = runByteArrayST $ do-      dst <- PM.newByteArray len-      Pure.unsafeCopy dst 0 a-      PM.writeByteArray dst (length a) sep-      Pure.unsafeCopy dst (length a + 1) b-      PM.unsafeFreezeByteArray dst-  , Types.length = len-  , Types.offset = 0-  }-  where len = length a + length b + 1+  -- | Separator+  Word8 ->+  -- | First byte sequence+  Bytes ->+  -- | Second byte sequence+  Bytes ->+  Bytes+intercalateByte2 !sep !a !b =+  Bytes+    { Types.array = runByteArrayST $ do+        dst <- PM.newByteArray len+        Pure.unsafeCopy dst 0 a+        PM.writeByteArray dst (length a) sep+        Pure.unsafeCopy dst (length a + 1) b+        PM.unsafeFreezeByteArray dst+    , Types.length = len+    , Types.offset = 0+    }+ where+  len = length a + length b + 1  -- | /O(n)/ Returns true if any byte in the sequence satisfies the predicate. any :: (Word8 -> Bool) -> Bytes -> Bool-{-# inline any #-}+{-# INLINE any #-} any f = foldr (\b r -> f b || r) False  -- | /O(n)/ Returns true if all bytes in the sequence satisfy the predicate. all :: (Word8 -> Bool) -> Bytes -> Bool-{-# inline all #-}+{-# INLINE all #-} all f = foldr (\b r -> f b && r) True --- | Variant of 'toShortByteString' that unconditionally makes a copy of--- the array backing the sliced 'Bytes' even if the original array--- could be reused. Prefer 'toShortByteString'.+{- | Variant of 'toShortByteString' that unconditionally makes a copy of+the array backing the sliced 'Bytes' even if the original array+could be reused. Prefer 'toShortByteString'.+-} toShortByteStringClone :: Bytes -> ShortByteString-{-# inline toShortByteStringClone #-}+{-# INLINE toShortByteStringClone #-} toShortByteStringClone !b = case Pure.toByteArrayClone b of   PM.ByteArray x -> SBS x  -- | /O(1)/ Create 'Bytes' from a 'ShortByteString'. fromShortByteString :: ShortByteString -> Bytes-{-# inline fromShortByteString #-}+{-# INLINE fromShortByteString #-} fromShortByteString (SBS x) = fromByteArray (ByteArray x) --- | /O(1)/ Create 'Bytes' from a 'ShortText'. This encodes the text as UTF-8.--- It is a no-op.+{- | /O(1)/ Create 'Bytes' from a 'ShortText'. This encodes the text as UTF-8.+It is a no-op.+-} fromShortText :: ShortText -> Bytes-{-# inline fromShortText #-}+{-# INLINE fromShortText #-} fromShortText t = case TS.toShortByteString t of   SBS x -> fromByteArray (ByteArray x) --- | /O(n)/ Interpreting the bytes an ASCII-encoded characters, convert--- the string to lowercase. This adds @0x20@ to bytes in the range--- @[0x41,0x5A]@ and leaves all other bytes alone. Unconditionally--- copies the bytes.+{- | /O(n)/ Interpreting the bytes an ASCII-encoded characters, convert+the string to lowercase. This adds @0x20@ to bytes in the range+@[0x41,0x5A]@ and leaves all other bytes alone. Unconditionally+copies the bytes.+-} toLowerAsciiByteArrayClone :: Bytes -> ByteArray {-# DEPRECATED toLowerAsciiByteArrayClone "use Data.Bytes/Text/AsciiExt.toLowerU" #-} {-# INLINE toLowerAsciiByteArrayClone #-} toLowerAsciiByteArrayClone = AsciiExt.toLowerU  lift :: Bytes# -> Bytes-{-# inline lift #-}+{-# INLINE lift #-} lift (Bytes# (# arr, off, len #)) = Bytes (ByteArray arr) (I# off) (I# len)  unlift :: Bytes -> Bytes#-{-# inline unlift #-}+{-# INLINE unlift #-} unlift (Bytes (ByteArray arr) (I# off) (I# len)) =   Bytes# (# arr, off, len #)  concatArrayU :: Array Bytes -> ByteArray-{-# noinline concatArrayU #-}+{-# NOINLINE concatArrayU #-} concatArrayU !xs = runByteArrayST $ do   let !arrLen = PM.sizeofArray xs   let !totalByteLen = F.foldl' (\acc b -> length b + acc) 0 xs   dst <- PM.newByteArray totalByteLen-  let go !ix !dstOff = if ix < arrLen-        then do-          x <- PM.indexArrayM xs ix-          Pure.unsafeCopy dst dstOff x-          go (ix + 1) (dstOff + length x)-        else PM.unsafeFreezeByteArray dst+  let go !ix !dstOff =+        if ix < arrLen+          then do+            x <- PM.indexArrayM xs ix+            Pure.unsafeCopy dst dstOff x+            go (ix + 1) (dstOff + length x)+          else PM.unsafeFreezeByteArray dst   go 0 0  concatArray :: Array Bytes -> Bytes-{-# inline concatArray #-}+{-# INLINE concatArray #-} concatArray !xs = Pure.fromByteArray (concatArrayU xs) --- | Convert 'Bytes' to 'BytesN', exposing the length in a type-safe--- way in the callback.+{- | Convert 'Bytes' to 'BytesN', exposing the length in a type-safe+way in the callback.+-} withLength ::-     Bytes-  -> (forall (n :: GHC.Nat). Arithmetic.Nat n -> BytesN n -> a)-  -> a-{-# inline withLength #-}-withLength Bytes{array,offset,length=len} f = Nat.with-  len-  (\n -> f n BytesN{array,offset})+  Bytes ->+  (forall (n :: GHC.Nat). Arithmetic.Nat n -> BytesN n -> a) ->+  a+{-# INLINE withLength #-}+withLength Bytes {array, offset, length = len} f =+  Nat.with+    len+    (\n -> f n BytesN {array, offset})  withLengthU ::-     ByteArray-  -> (forall (n :: GHC.Nat). Arithmetic.Nat n -> ByteArrayN n -> a)-  -> a-{-# inline withLengthU #-}-withLengthU !arr f = Nat.with-  (PM.sizeofByteArray arr)-  (\n -> f n (ByteArrayN arr))+  ByteArray ->+  (forall (n :: GHC.Nat). Arithmetic.Nat n -> ByteArrayN n -> a) ->+  a+{-# INLINE withLengthU #-}+withLengthU !arr f =+  Nat.with+    (PM.sizeofByteArray arr)+    (\n -> f n (ByteArrayN arr))  findTetragramIndex ::-     Word8-  -> Word8-  -> Word8-  -> Word8-  -> Bytes-  -> Maybe Int-findTetragramIndex !w0 !w1 !w2 !w3 (Bytes arr off len) = if len < 4-  then Nothing-  else-    let !target = -          unsafeShiftL (fromIntegral w0 :: Word32) 24-          .|.-          unsafeShiftL (fromIntegral w1 :: Word32) 16-          .|.-          unsafeShiftL (fromIntegral w2 :: Word32) 8-          .|.-          unsafeShiftL (fromIntegral w3 :: Word32) 0-        !end = off + len-        go !ix !acc = if acc == target-          then-            let n = ix - off-             in Just (n - 4)-          else if ix < end-            then-              let !w = PM.indexByteArray arr ix :: Word8-                  acc' =-                    (fromIntegral w :: Word32)-                    .|.-                    unsafeShiftL acc 8-               in go (ix + 1) acc'-            else Nothing-        !acc0 =-          unsafeShiftL (fromIntegral (PM.indexByteArray arr 0 :: Word8) :: Word32) 24-          .|.-          unsafeShiftL (fromIntegral (PM.indexByteArray arr 1 :: Word8) :: Word32) 16-          .|.-          unsafeShiftL (fromIntegral (PM.indexByteArray arr 2 :: Word8) :: Word32) 8-          .|.-          unsafeShiftL (fromIntegral (PM.indexByteArray arr 3 :: Word8) :: Word32) 0-     in go 4 acc0+  Word8 ->+  Word8 ->+  Word8 ->+  Word8 ->+  Bytes ->+  Maybe Int+findTetragramIndex !w0 !w1 !w2 !w3 (Bytes arr off len) =+  if len < 4+    then Nothing+    else+      let !target =+            unsafeShiftL (fromIntegral w0 :: Word32) 24+              .|. unsafeShiftL (fromIntegral w1 :: Word32) 16+              .|. unsafeShiftL (fromIntegral w2 :: Word32) 8+              .|. unsafeShiftL (fromIntegral w3 :: Word32) 0+          !end = off + len+          go !ix !acc =+            if acc == target+              then+                let n = ix - off+                 in Just (n - 4)+              else+                if ix < end+                  then+                    let !w = PM.indexByteArray arr ix :: Word8+                        acc' =+                          (fromIntegral w :: Word32)+                            .|. unsafeShiftL acc 8+                     in go (ix + 1) acc'+                  else Nothing+          !acc0 =+            unsafeShiftL (fromIntegral (PM.indexByteArray arr 0 :: Word8) :: Word32) 24+              .|. unsafeShiftL (fromIntegral (PM.indexByteArray arr 1 :: Word8) :: Word32) 16+              .|. unsafeShiftL (fromIntegral (PM.indexByteArray arr 2 :: Word8) :: Word32) 8+              .|. unsafeShiftL (fromIntegral (PM.indexByteArray arr 3 :: Word8) :: Word32) 0+       in go 4 acc0  splitTetragram1 ::-     Word8-  -> Word8-  -> Word8-  -> Word8-  -> Bytes-  -> Maybe (Bytes,Bytes)+  Word8 ->+  Word8 ->+  Word8 ->+  Word8 ->+  Bytes ->+  Maybe (Bytes, Bytes) splitTetragram1 !w0 !w1 !w2 !w3 !b = case findTetragramIndex w0 w1 w2 w3 b of   Nothing -> Nothing   Just n -> Just (Pure.unsafeTake n b, Pure.unsafeDrop (n + 4) b)
src/Data/Bytes/Byte.hs view
@@ -1,11 +1,9 @@-{-# language BangPatterns #-}-{-# language BlockArguments #-}-{-# language MagicHash #-}-{-# language NamedFieldPuns #-}-{-# language ScopedTypeVariables #-}-{-# language TypeApplications #-}-{-# language UnliftedFFITypes #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnliftedFFITypes #-}  -- This internal module has functions for splitting strings -- on a particular byte and for counting occurences of that@@ -24,7 +22,7 @@   , split3   , split4   , splitEnd1-    -- Used by other internal modules+  -- Used by other internal modules   , elemIndexLoop#   ) where @@ -32,14 +30,14 @@  import Control.Monad.ST (runST) import Control.Monad.ST.Run (runPrimArrayST)-import Data.Bytes.Types (Bytes(..))-import Data.List.NonEmpty (NonEmpty((:|)))-import Data.Primitive (PrimArray(..),MutablePrimArray(..),ByteArray(..))+import Data.Bytes.Types (Bytes (..))+import Data.List.NonEmpty (NonEmpty ((:|)))+import Data.Primitive (ByteArray (..), MutablePrimArray (..), PrimArray (..)) import Data.Primitive.Unlifted.Array (UnliftedArray)-import Data.Tuple.Types (IntPair(IntPair))-import Data.Vector.Fusion.Stream.Monadic (Stream(Stream),Step(Yield,Done))+import Data.Tuple.Types (IntPair (IntPair))+import Data.Vector.Fusion.Stream.Monadic (Step (Done, Yield), Stream (Stream)) import Data.Word (Word8)-import GHC.Exts (ByteArray#,MutableByteArray#,Int#,Int(I#))+import GHC.Exts (ByteArray#, Int (I#), Int#, MutableByteArray#) import GHC.IO (unsafeIOToST)  import qualified Data.Primitive as PM@@ -48,23 +46,25 @@  -- | Count the number of times the byte appears in the sequence. count :: Word8 -> Bytes -> Int-count !b (Bytes{array=ByteArray arr,offset,length}) =+count !b (Bytes {array = ByteArray arr, offset, length}) =   count_ba arr offset length b --- | Variant of 'split' that returns an array of unsliced byte sequences.--- Unlike 'split', this is not a good producer for list fusion. (It does--- not return a list, so it could not be.) Prefer 'split' if the result--- is going to be consumed exactly once by a good consumer. Prefer 'splitU'--- if the result of the split is going to be around for a while and--- inspected multiple times.+{- | Variant of 'split' that returns an array of unsliced byte sequences.+Unlike 'split', this is not a good producer for list fusion. (It does+not return a list, so it could not be.) Prefer 'split' if the result+is going to be consumed exactly once by a good consumer. Prefer 'splitU'+if the result of the split is going to be around for a while and+inspected multiple times.+-} splitU :: Word8 -> Bytes -> UnliftedArray ByteArray splitU !w !bs =   let !lens = splitLengthsAlt w bs       !lensSz = PM.sizeofPrimArray lens    in splitCommonU lens lensSz bs --- | Variant of 'splitU' that drops the trailing element. See 'splitInit'--- for an explanation of why this may be useful.+{- | Variant of 'splitU' that drops the trailing element. See 'splitInit'+for an explanation of why this may be useful.+-} splitInitU :: Word8 -> Bytes -> UnliftedArray ByteArray splitInitU !w !bs =   let !lens = splitLengthsAlt w bs@@ -73,103 +73,117 @@  -- Internal function splitCommonU ::-     PrimArray Int -- array of segment lengths-  -> Int -- number of lengths to consider-  -> Bytes-  -> UnliftedArray ByteArray-splitCommonU !lens !lensSz Bytes{array,offset=arrIx0} = runST do+  PrimArray Int -> -- array of segment lengths+  Int -> -- number of lengths to consider+  Bytes ->+  UnliftedArray ByteArray+splitCommonU !lens !lensSz Bytes {array, offset = arrIx0} = runST do   dst <- PM.unsafeNewUnliftedArray lensSz-  let go !lenIx !arrIx = if lenIx < lensSz-        then do-          let !len = PM.indexPrimArray lens lenIx-          buf <- PM.newByteArray len-          PM.copyByteArray buf 0 array arrIx len-          buf' <- PM.unsafeFreezeByteArray buf-          PM.writeUnliftedArray dst lenIx buf'-          go (lenIx + 1) (arrIx + len + 1)-        else pure ()+  let go !lenIx !arrIx =+        if lenIx < lensSz+          then do+            let !len = PM.indexPrimArray lens lenIx+            buf <- PM.newByteArray len+            PM.copyByteArray buf 0 array arrIx len+            buf' <- PM.unsafeFreezeByteArray buf+            PM.writeUnliftedArray dst lenIx buf'+            go (lenIx + 1) (arrIx + len + 1)+          else pure ()   go 0 arrIx0   PM.unsafeFreezeUnliftedArray dst-   --- | Break a byte sequence into pieces separated by the byte argument,--- consuming the delimiter. This function is a good producer for list--- fusion. It is common to immidiately consume the results of @split@--- with @foldl'@, @traverse_@, @foldlM@, and being a good producer helps--- in this situation.------ Note: this function differs from its counterpart in @bytestring@.--- If the byte sequence is empty, this returns a singleton list with--- the empty byte sequence.+{- | Break a byte sequence into pieces separated by the byte argument,+consuming the delimiter. This function is a good producer for list+fusion. It is common to immidiately consume the results of @split@+with @foldl'@, @traverse_@, @foldlM@, and being a good producer helps+in this situation.++Note: this function differs from its counterpart in @bytestring@.+If the byte sequence is empty, this returns a singleton list with+the empty byte sequence.+-} split :: Word8 -> Bytes -> [Bytes]-{-# inline split #-}-split !w !bs@Bytes{array,offset=arrIx0} = Exts.build-  (\g x0 ->-    let go !lenIx !arrIx = if lenIx < lensSz-          then let !len = PM.indexPrimArray lens lenIx in -            g (Bytes array arrIx len) (go (lenIx + 1) (arrIx + len + 1))-          else x0-     in go 0 arrIx0-  )-  where+{-# INLINE split #-}+split !w !bs@Bytes {array, offset = arrIx0} =+  Exts.build+    ( \g x0 ->+        let go !lenIx !arrIx =+              if lenIx < lensSz+                then+                  let !len = PM.indexPrimArray lens lenIx+                   in g (Bytes array arrIx len) (go (lenIx + 1) (arrIx + len + 1))+                else x0+         in go 0 arrIx0+    )+ where   !lens = splitLengthsAlt w bs   !lensSz = PM.sizeofPrimArray lens --- | Variant of 'split' that intended for use with stream fusion rather--- than @build@-@foldr@ fusion.-splitStream :: forall m. Applicative m => Word8 -> Bytes -> Stream m Bytes-{-# inline [1] splitStream #-}-splitStream !w !bs@Bytes{array,offset=arrIx0} = Stream step (IntPair 0 arrIx0)-  where+{- | Variant of 'split' that intended for use with stream fusion rather+than @build@-@foldr@ fusion.+-}+splitStream :: forall m. (Applicative m) => Word8 -> Bytes -> Stream m Bytes+{-# INLINE [1] splitStream #-}+splitStream !w !bs@Bytes {array, offset = arrIx0} = Stream step (IntPair 0 arrIx0)+ where   !lens = splitLengthsAlt w bs   !lensSz = PM.sizeofPrimArray lens-  {-# inline [0] step #-}+  {-# INLINE [0] step #-}   step :: IntPair -> m (Step IntPair Bytes)-  step (IntPair lenIx arrIx) = if lenIx < lensSz-    then do-      let !len = PM.indexPrimArray lens lenIx-          !element = Bytes array arrIx len-          !acc = IntPair (lenIx + 1) (arrIx + len + 1)-      pure (Yield element acc)-    else pure Done+  step (IntPair lenIx arrIx) =+    if lenIx < lensSz+      then do+        let !len = PM.indexPrimArray lens lenIx+            !element = Bytes array arrIx len+            !acc = IntPair (lenIx + 1) (arrIx + len + 1)+        pure (Yield element acc)+      else pure Done --- | Variant of 'split' that returns the result as a 'NonEmpty'--- instead of @[]@. This is also eligible for stream fusion.+{- | Variant of 'split' that returns the result as a 'NonEmpty'+instead of @[]@. This is also eligible for stream fusion.+-} splitNonEmpty :: Word8 -> Bytes -> NonEmpty Bytes-{-# inline splitNonEmpty #-}-splitNonEmpty !w !bs@Bytes{array,offset=arrIx0} =-  Bytes array arrIx0 len0 :| Exts.build-  (\g x0 ->-    let go !lenIx !arrIx = if lenIx < lensSz-          then let !len = PM.indexPrimArray lens lenIx in -            g (Bytes array arrIx len) (go (lenIx + 1) (arrIx + len + 1))-          else x0-     in go 1 (1 + (arrIx0 + len0))-  )-  where+{-# INLINE splitNonEmpty #-}+splitNonEmpty !w !bs@Bytes {array, offset = arrIx0} =+  Bytes array arrIx0 len0+    :| Exts.build+      ( \g x0 ->+          let go !lenIx !arrIx =+                if lenIx < lensSz+                  then+                    let !len = PM.indexPrimArray lens lenIx+                     in g (Bytes array arrIx len) (go (lenIx + 1) (arrIx + len + 1))+                  else x0+           in go 1 (1 + (arrIx0 + len0))+      )+ where   !lens = splitLengthsAlt w bs   !lensSz = PM.sizeofPrimArray lens   !len0 = PM.indexPrimArray lens 0 :: Int --- | Variant of 'split' that drops the trailing element. This behaves--- correctly even if the byte sequence is empty. This is a good producer--- for list fusion. This is useful when splitting a text file--- into lines.--- <https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_392 POSIX>--- mandates that text files end with a newline, so the list resulting--- from 'split' always has an empty byte sequence as its last element.--- With 'splitInit', that unwanted element is discarded.+{- | Variant of 'split' that drops the trailing element. This behaves+correctly even if the byte sequence is empty. This is a good producer+for list fusion. This is useful when splitting a text file+into lines.+<https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_392 POSIX>+mandates that text files end with a newline, so the list resulting+from 'split' always has an empty byte sequence as its last element.+With 'splitInit', that unwanted element is discarded.+-} splitInit :: Word8 -> Bytes -> [Bytes]-{-# inline splitInit #-}-splitInit !w !bs@Bytes{array,offset=arrIx0} = Exts.build-  (\g x0 ->-    let go !lenIx !arrIx = if lenIx < lensSz-          then let !len = PM.indexPrimArray lens lenIx in -            g (Bytes array arrIx len) (go (lenIx + 1) (arrIx + len + 1))-          else x0-     in go 0 arrIx0-  )-  where+{-# INLINE splitInit #-}+splitInit !w !bs@Bytes {array, offset = arrIx0} =+  Exts.build+    ( \g x0 ->+        let go !lenIx !arrIx =+              if lenIx < lensSz+                then+                  let !len = PM.indexPrimArray lens lenIx+                   in g (Bytes array arrIx len) (go (lenIx + 1) (arrIx + len + 1))+                else x0+         in go 0 arrIx0+    )+ where   -- Remember, the resulting array from splitLengthsAlt always has   -- a length of at least one.   !lens = splitLengthsAlt w bs@@ -179,99 +193,118 @@ -- it does not treat the empty byte sequences specially. The result -- for that byte sequence is a singleton array with the element zero. splitLengthsAlt :: Word8 -> Bytes -> PrimArray Int-splitLengthsAlt b Bytes{array=ByteArray arr#,offset=off,length=len} = runPrimArrayST do+splitLengthsAlt b Bytes {array = ByteArray arr#, offset = off, length = len} = runPrimArrayST do   let !n = count_ba arr# off len b-  dst@(MutablePrimArray dst# ) :: MutablePrimArray s Int <- PM.newPrimArray (n + 1)+  dst@(MutablePrimArray dst#) :: MutablePrimArray s Int <- PM.newPrimArray (n + 1)   total <- unsafeIOToST (memchr_ba_many arr# off len dst# n b)   PM.writePrimArray dst n (len - total)   PM.unsafeFreezePrimArray dst -foreign import ccall unsafe "bs_custom.h memchr_ba_many" memchr_ba_many-  :: ByteArray# -> Int -> Int -> MutableByteArray# s -> Int -> Word8 -> IO Int+foreign import ccall unsafe "bs_custom.h memchr_ba_many"+  memchr_ba_many ::+    ByteArray# -> Int -> Int -> MutableByteArray# s -> Int -> Word8 -> IO Int -foreign import ccall unsafe "bs_custom.h count_ba" count_ba-  :: ByteArray# -> Int -> Int -> Word8 -> Int+foreign import ccall unsafe "bs_custom.h count_ba"+  count_ba ::+    ByteArray# -> Int -> Int -> Word8 -> Int --- | Split a byte sequence on the first occurrence of the target--- byte. The target is removed from the result. For example:------ >>> split1 0xA [0x1,0x2,0xA,0xB]--- Just ([0x1,0x2],[0xB])-split1 :: Word8 -> Bytes -> Maybe (Bytes,Bytes)-{-# inline split1 #-}+{- | Split a byte sequence on the first occurrence of the target+byte. The target is removed from the result. For example:++>>> split1 0xA [0x1,0x2,0xA,0xB]+Just ([0x1,0x2],[0xB])+-}+split1 :: Word8 -> Bytes -> Maybe (Bytes, Bytes)+{-# INLINE split1 #-} split1 w b@(Bytes arr off len) = case elemIndexLoop# w b of   (-1#) -> Nothing-  i# -> let i = I# i# in-    Just (Bytes arr off (i - off), Bytes arr (i + 1) (len - (1 + i - off)))+  i# ->+    let i = I# i#+     in Just (Bytes arr off (i - off), Bytes arr (i + 1) (len - (1 + i - off))) --- | Split a byte sequence on the first and second occurrences--- of the target byte. The target is removed from the result.--- For example:------ >>> split2 0xA [0x1,0x2,0xA,0xB,0xA,0xA,0xA]--- Just ([0x1,0x2],[0xB],[0xA,0xA])-split2 :: Word8 -> Bytes -> Maybe (Bytes,Bytes,Bytes)-{-# inline split2 #-}+{- | Split a byte sequence on the first and second occurrences+of the target byte. The target is removed from the result.+For example:++>>> split2 0xA [0x1,0x2,0xA,0xB,0xA,0xA,0xA]+Just ([0x1,0x2],[0xB],[0xA,0xA])+-}+split2 :: Word8 -> Bytes -> Maybe (Bytes, Bytes, Bytes)+{-# INLINE split2 #-} split2 w b@(Bytes arr off len) = case elemIndexLoop# w b of   (-1#) -> Nothing-  i# -> let i = I# i# in-    case elemIndexLoop# w (Bytes arr (i + 1) (len - (1 + i - off))) of-      (-1#) -> Nothing-      j# -> let j = I# j# in Just-        ( Bytes arr off (i - off)-        , Bytes arr (i + 1) (j - (i + 1))-        , Bytes arr (j + 1) (len - (1 + j - off))-        )+  i# ->+    let i = I# i#+     in case elemIndexLoop# w (Bytes arr (i + 1) (len - (1 + i - off))) of+          (-1#) -> Nothing+          j# ->+            let j = I# j#+             in Just+                  ( Bytes arr off (i - off)+                  , Bytes arr (i + 1) (j - (i + 1))+                  , Bytes arr (j + 1) (len - (1 + j - off))+                  ) --- | Split a byte sequence on the first, second, and third occurrences--- of the target byte. The target is removed from the result.--- For example:------ >>> split3 0xA [0x1,0x2,0xA,0xB,0xA,0xA,0xA]--- Just ([0x1,0x2],[0xB],[],[0xA])-split3 :: Word8 -> Bytes -> Maybe (Bytes,Bytes,Bytes,Bytes)-{-# inline split3 #-}+{- | Split a byte sequence on the first, second, and third occurrences+of the target byte. The target is removed from the result.+For example:++>>> split3 0xA [0x1,0x2,0xA,0xB,0xA,0xA,0xA]+Just ([0x1,0x2],[0xB],[],[0xA])+-}+split3 :: Word8 -> Bytes -> Maybe (Bytes, Bytes, Bytes, Bytes)+{-# INLINE split3 #-} split3 w b@(Bytes arr off len) = case elemIndexLoop# w b of   (-1#) -> Nothing-  i# -> let i = I# i# in-    case elemIndexLoop# w (Bytes arr (i + 1) (len - (1 + i - off))) of-      (-1#) -> Nothing-      j# -> let j = I# j# in-        case elemIndexLoop# w (Bytes arr (j + 1) (len - (1 + j - off))) of+  i# ->+    let i = I# i#+     in case elemIndexLoop# w (Bytes arr (i + 1) (len - (1 + i - off))) of           (-1#) -> Nothing-          k# -> let k = I# k# in Just-            ( Bytes arr off (i - off)-            , Bytes arr (i + 1) (j - (i + 1))-            , Bytes arr (j + 1) (k - (j + 1))-            , Bytes arr (k + 1) (len - (1 + k - off))-            )+          j# ->+            let j = I# j#+             in case elemIndexLoop# w (Bytes arr (j + 1) (len - (1 + j - off))) of+                  (-1#) -> Nothing+                  k# ->+                    let k = I# k#+                     in Just+                          ( Bytes arr off (i - off)+                          , Bytes arr (i + 1) (j - (i + 1))+                          , Bytes arr (j + 1) (k - (j + 1))+                          , Bytes arr (k + 1) (len - (1 + k - off))+                          ) --- | Split a byte sequence on the first, second, third, and fourth--- occurrences of the target byte. The target is removed from the result.--- For example:------ >>> split4 0xA [0x1,0x2,0xA,0xB,0xA,0xA,0xA]--- Just ([0x1,0x2],[0xB],[],[],[])-split4 :: Word8 -> Bytes -> Maybe (Bytes,Bytes,Bytes,Bytes,Bytes)-{-# inline split4 #-}+{- | Split a byte sequence on the first, second, third, and fourth+occurrences of the target byte. The target is removed from the result.+For example:++>>> split4 0xA [0x1,0x2,0xA,0xB,0xA,0xA,0xA]+Just ([0x1,0x2],[0xB],[],[],[])+-}+split4 :: Word8 -> Bytes -> Maybe (Bytes, Bytes, Bytes, Bytes, Bytes)+{-# INLINE split4 #-} split4 w b@(Bytes arr off len) = case elemIndexLoop# w b of   (-1#) -> Nothing-  i# -> let i = I# i# in-    case elemIndexLoop# w (Bytes arr (i + 1) (len - (1 + i - off))) of-      (-1#) -> Nothing-      j# -> let j = I# j# in-        case elemIndexLoop# w (Bytes arr (j + 1) (len - (1 + j - off))) of+  i# ->+    let i = I# i#+     in case elemIndexLoop# w (Bytes arr (i + 1) (len - (1 + i - off))) of           (-1#) -> Nothing-          k# -> let k = I# k# in-            case elemIndexLoop# w (Bytes arr (k + 1) (len - (1 + k - off))) of-              (-1#) -> Nothing-              m# -> let m = I# m# in Just-                ( Bytes arr off (i - off)-                , Bytes arr (i + 1) (j - (i + 1))-                , Bytes arr (j + 1) (k - (j + 1))-                , Bytes arr (k + 1) (m - (k + 1))-                , Bytes arr (m + 1) (len - (1 + m - off))-                )+          j# ->+            let j = I# j#+             in case elemIndexLoop# w (Bytes arr (j + 1) (len - (1 + j - off))) of+                  (-1#) -> Nothing+                  k# ->+                    let k = I# k#+                     in case elemIndexLoop# w (Bytes arr (k + 1) (len - (1 + k - off))) of+                          (-1#) -> Nothing+                          m# ->+                            let m = I# m#+                             in Just+                                  ( Bytes arr off (i - off)+                                  , Bytes arr (i + 1) (j - (i + 1))+                                  , Bytes arr (j + 1) (k - (j + 1))+                                  , Bytes arr (k + 1) (m - (k + 1))+                                  , Bytes arr (m + 1) (len - (1 + m - off))+                                  )  -- This returns the offset into the byte array. This is not an index -- that will mean anything to the end user, so it cannot be returned@@ -280,30 +313,35 @@ -- Exported for use in other internal modules because it is needed in -- Data.Bytes.Search. elemIndexLoop# :: Word8 -> Bytes -> Int#-{-# inline elemIndexLoop# #-}-elemIndexLoop# !w (Bytes arr off@(I# off# ) len) = case len of+{-# INLINE elemIndexLoop# #-}+elemIndexLoop# !w (Bytes arr off@(I# off#) len) = case len of   0 -> (-1#)-  _ -> if PM.indexByteArray arr off == w-    then off#-    else elemIndexLoop# w (Bytes arr (off + 1) (len - 1))+  _ ->+    if PM.indexByteArray arr off == w+      then off#+      else elemIndexLoop# w (Bytes arr (off + 1) (len - 1))  -- Variant of elemIndexLoop# that starts at the end. Similarly, returns -- negative one if the element is not found. elemIndexLoopBackwards# :: Word8 -> ByteArray -> Int -> Int -> Int#-elemIndexLoopBackwards# !w !arr !start !pos@(I# pos#) = if pos < start-  then (-1#)-  else if PM.indexByteArray arr pos == w-    then pos#-    else elemIndexLoopBackwards# w arr start (pos - 1)+elemIndexLoopBackwards# !w !arr !start !pos@(I# pos#) =+  if pos < start+    then (-1#)+    else+      if PM.indexByteArray arr pos == w+        then pos#+        else elemIndexLoopBackwards# w arr start (pos - 1) --- | Split a byte sequence on the last occurrence of the target--- byte. The target is removed from the result. For example:------ >>> split1 0xA [0x1,0x2,0xA,0xB,0xA,0xC]--- Just ([0x1,0x2,0xA,0xB],[0xC])-splitEnd1 :: Word8 -> Bytes -> Maybe (Bytes,Bytes)-{-# inline splitEnd1 #-}+{- | Split a byte sequence on the last occurrence of the target+byte. The target is removed from the result. For example:++>>> split1 0xA [0x1,0x2,0xA,0xB,0xA,0xC]+Just ([0x1,0x2,0xA,0xB],[0xC])+-}+splitEnd1 :: Word8 -> Bytes -> Maybe (Bytes, Bytes)+{-# INLINE splitEnd1 #-} splitEnd1 !w (Bytes arr off len) = case elemIndexLoopBackwards# w arr off (off + len - 1) of   (-1#) -> Nothing-  i# -> let i = I# i# in-    Just (Bytes arr off (i - off), Bytes arr (i + 1) (len - (1 + i - off)))+  i# ->+    let i = I# i#+     in Just (Bytes arr off (i - off), Bytes arr (i + 1) (len - (1 + i - off)))
src/Data/Bytes/Chunks.hs view
@@ -1,25 +1,28 @@-{-# language BangPatterns #-}-{-# language DerivingStrategies #-}-{-# language DuplicateRecordFields #-}-{-# language MagicHash #-}-{-# language NamedFieldPuns #-}-{-# language RankNTypes #-}-{-# language TypeApplications #-}-{-# language TypeFamilies #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-} --- | Chunks of bytes. This is useful as a target for a builder--- or as a way to read a large amount of whose size is unknown--- in advance. Structurally, this type is similar to--- @Data.ByteString.Lazy.ByteString@. However, the type in this--- module is strict in its spine. Additionally, none of the--- @Handle@ functions perform lazy I\/O.+{- | Chunks of bytes. This is useful as a target for a builder+or as a way to read a large amount of whose size is unknown+in advance. Structurally, this type is similar to+@Data.ByteString.Lazy.ByteString@. However, the type in this+module is strict in its spine. Additionally, none of the+@Handle@ functions perform lazy I\/O.+-} module Data.Bytes.Chunks   ( -- * Types-    Chunks(..)+    Chunks (..)+     -- * Properties   , length   , null+     -- * Manipulate   , cons   , concat@@ -31,18 +34,24 @@   , reverseOnto   , replicate   , replicateByte+     -- * Folds   , foldl'+     -- * Splitting   , split+     -- * Hashing   , fnv1a32   , fnv1a64+     -- * Create   , fromBytes   , fromByteArray+     -- * Copy to buffer   , unsafeCopy+     -- * I\/O with Handles   , hGetContents   , readFile@@ -50,19 +59,18 @@   , writeFile   ) where -import Prelude hiding (Foldable(..),concat,reverse,readFile,writeFile,replicate)+import Prelude hiding (Foldable (..), concat, readFile, replicate, reverse, writeFile) -import Control.Exception (IOException,catch)+import Control.Exception (IOException, catch) import Control.Monad.ST.Run (runIntByteArrayST) import Data.Bits (xor) import Data.ByteString (ByteString)-import Data.Bytes.Types (Bytes(Bytes))-import Data.Primitive (ByteArray(..),MutableByteArray(..))-import Data.Word (Word8,Word32,Word64)-import GHC.Exts (ByteArray#,MutableByteArray#)-import GHC.Exts (Int#,State#,Int(I#),(+#))-import GHC.ST (ST(..))-import System.IO (Handle,hFileSize,IOMode(ReadMode,WriteMode),withBinaryFile)+import Data.Bytes.Types (Bytes (Bytes))+import Data.Primitive (ByteArray (..), MutableByteArray (..))+import Data.Word (Word32, Word64, Word8)+import GHC.Exts (ByteArray#, Int (I#), Int#, MutableByteArray#, State#, (+#))+import GHC.ST (ST (..))+import System.IO (Handle, IOMode (ReadMode, WriteMode), hFileSize, withBinaryFile)  import qualified Data.Bytes.Byte as Byte import qualified Data.Bytes.IO as IO@@ -94,48 +102,52 @@  -- | Add a byte sequence to the beginning. cons :: Bytes -> Chunks -> Chunks-{-# inline cons #-}+{-# INLINE cons #-} cons = ChunksCons --- | Repeat the byte sequence over and over. Returns empty chunks when given--- a negative repetition count.+{- | Repeat the byte sequence over and over. Returns empty chunks when given+a negative repetition count.+-} replicate ::-     Bytes-  -> Int -- ^ Number of times to repeat the sequence.-  -> Chunks+  Bytes ->+  -- | Number of times to repeat the sequence.+  Int ->+  Chunks replicate !b@(Bytes _ _ len) !n   | n <= 0 = ChunksNil   | len == 0 = ChunksNil   | otherwise = go n ChunksNil-  where+ where   -- Implementation note: We do not have to reverse the chunks at the end.   go i !acc = case i of     0 -> acc     _ -> go (i - 1) (ChunksCons b acc) --- | Repeat the byte over and over. This builds a single byte array that--- is at most 64KiB and shares that across every @ChunksCons@ cell.------ An as example, creating a 2GiB chunks this way would use 64KiB for the--- byte array, and there would be the additional overhead of the 2^15--- @ChunksCons@ data constructors. On a 64-bit platform, @ChunksCons@--- takes 40 bytes, so the total memory consumption would be--- @2^16 + 40 * 2^15@, which is roughly 1.37MB. The same reasoning--- shows that it takes about 83.95MB to represent a 128GiB chunks.------ The size of the shared payload is an implementation detail. Do not--- rely on this function producing 64KiB chunks. The implementation might--- one day change to something smarter that minimizes the memory footprint--- for very large chunks.+{- | Repeat the byte over and over. This builds a single byte array that+is at most 64KiB and shares that across every @ChunksCons@ cell.++An as example, creating a 2GiB chunks this way would use 64KiB for the+byte array, and there would be the additional overhead of the 2^15+@ChunksCons@ data constructors. On a 64-bit platform, @ChunksCons@+takes 40 bytes, so the total memory consumption would be+@2^16 + 40 * 2^15@, which is roughly 1.37MB. The same reasoning+shows that it takes about 83.95MB to represent a 128GiB chunks.++The size of the shared payload is an implementation detail. Do not+rely on this function producing 64KiB chunks. The implementation might+one day change to something smarter that minimizes the memory footprint+for very large chunks.+-} replicateByte ::-     Word8-  -> Int -- ^ Number of times to replicate the byte-  -> Chunks+  Word8 ->+  -- | Number of times to replicate the byte+  Int ->+  Chunks replicateByte !w !n   | n <= 0 = ChunksNil   | n < 65536 = ChunksCons (Bytes.replicate n w) ChunksNil   | otherwise = go (Bytes.replicateU 65536 w) n ChunksNil-  where+ where   go !shared !remaining !acc     | remaining == 0 = acc     | remaining < 65536 = ChunksCons (Bytes shared 0 remaining) acc@@ -143,14 +155,16 @@  -- | Are there any bytes in the chunked byte sequences? null :: Chunks -> Bool-null = go where+null = go+ where   go ChunksNil = True   go (ChunksCons (Bytes _ _ len) xs) = case len of     0 -> go xs     _ -> False --- | Variant of 'concat' that ensure that the resulting byte--- sequence is pinned memory.+{- | Variant of 'concat' that ensure that the resulting byte+sequence is pinned memory.+-} concatPinned :: Chunks -> Bytes concatPinned x = case x of   ChunksNil -> Bytes.emptyPinned@@ -197,15 +211,17 @@ concatPinnedFollowing2 = internalConcatFollowing2 PM.newPinnedByteArray  internalConcatFollowing2 ::-     (forall s. Int -> ST s (MutableByteArray s))-  -> Bytes-  -> Bytes-  -> Chunks-  -> (# Int#, ByteArray# #)-{-# inline internalConcatFollowing2 #-}-internalConcatFollowing2 allocate-  (Bytes{array=c,offset=coff,length=szc}) -  (Bytes{array=d,offset=doff,length=szd}) ds =+  (forall s. Int -> ST s (MutableByteArray s)) ->+  Bytes ->+  Bytes ->+  Chunks ->+  (# Int#, ByteArray# #)+{-# INLINE internalConcatFollowing2 #-}+internalConcatFollowing2+  allocate+  (Bytes {array = c, offset = coff, length = szc})+  (Bytes {array = d, offset = doff, length = szd})+  ds =     let !(I# x, ByteArray y) = runIntByteArrayST $ do           let !szboth = szc + szd               !len = chunksLengthGo szboth ds@@ -215,7 +231,7 @@           -- Note: len2 will always be the same as len.           !len2 <- unsafeCopy dst szboth ds           result <- PM.unsafeFreezeByteArray dst-          pure (len2,result)+          pure (len2, result)      in (# x, y #)  -- | The total number of bytes in all the chunks.@@ -224,26 +240,32 @@  chunksLengthGo :: Int -> Chunks -> Int chunksLengthGo !n ChunksNil = n-chunksLengthGo !n (ChunksCons (Bytes{B.length=len}) cs) =+chunksLengthGo !n (ChunksCons (Bytes {B.length = len}) cs) =   chunksLengthGo (n + len) cs --- | Copy the contents of the chunks into a mutable array.--- Precondition: The destination must have enough space to--- house the contents. This is not checked.+{- | Copy the contents of the chunks into a mutable array.+Precondition: The destination must have enough space to+house the contents. This is not checked.+-} unsafeCopy ::-     MutableByteArray s -- ^ Destination-  -> Int -- ^ Destination offset-  -> Chunks -- ^ Source-  -> ST s Int -- ^ Returns the next index into the destination after the payload-{-# inline unsafeCopy #-}-unsafeCopy (MutableByteArray dst) (I# off) cs = ST-  (\s0 -> case copy# dst off cs s0 of-    (# s1, nextOff #) -> (# s1, I# nextOff #)-  )+  -- | Destination+  MutableByteArray s ->+  -- | Destination offset+  Int ->+  -- | Source+  Chunks ->+  -- | Returns the next index into the destination after the payload+  ST s Int+{-# INLINE unsafeCopy #-}+unsafeCopy (MutableByteArray dst) (I# off) cs =+  ST+    ( \s0 -> case copy# dst off cs s0 of+        (# s1, nextOff #) -> (# s1, I# nextOff #)+    )  copy# :: MutableByteArray# s -> Int# -> Chunks -> State# s -> (# State# s, Int# #) copy# _ off ChunksNil s0 = (# s0, off #)-copy# marr off (ChunksCons (Bytes{B.array,B.offset,B.length=len}) cs) s0 =+copy# marr off (ChunksCons (Bytes {B.array, B.offset, B.length = len}) cs) s0 =   case Exts.copyByteArray# (unBa array) (unI offset) marr off (unI len) s0 of     s1 -> copy# marr (off +# unI len) cs s1 @@ -251,20 +273,21 @@ reverse :: Chunks -> Chunks reverse = reverseOnto ChunksNil --- | Variant of 'reverse' that allows the caller to provide--- an initial list of chunks that the reversed chunks will--- be pushed onto.+{- | Variant of 'reverse' that allows the caller to provide+an initial list of chunks that the reversed chunks will+be pushed onto.+-} reverseOnto :: Chunks -> Chunks -> Chunks reverseOnto !x ChunksNil = x reverseOnto !x (ChunksCons y ys) =   reverseOnto (ChunksCons y x) ys  unI :: Int -> Int#-{-# inline unI #-}+{-# INLINE unI #-} unI (I# i) = i  unBa :: ByteArray -> ByteArray#-{-# inline unBa #-}+{-# INLINE unBa #-} unBa (ByteArray x) = x  -- | Read a handle's entire contents strictly into chunks.@@ -281,10 +304,11 @@     else hGetContentsCommon r h  hGetContentsCommon ::-     Chunks -- reversed chunks-  -> Handle-  -> IO Chunks-hGetContentsCommon !acc0 !h = go acc0 where+  Chunks -> -- reversed chunks+  Handle ->+  IO Chunks+hGetContentsCommon !acc0 !h = go acc0+ where   go !acc = do     c <- IO.hGet h chunkSize     let !r = ChunksCons c acc@@ -292,9 +316,10 @@       then go r       else pure $! reverse r --- | Read an entire file strictly into chunks. If reading from a--- regular file, this makes an effort read the file into a single--- chunk.+{- | Read an entire file strictly into chunks. If reading from a+regular file, this makes an effort read the file into a single+chunk.+-} readFile :: FilePath -> IO Chunks readFile f = withBinaryFile f ReadMode $ \h -> do   -- Implementation copied from bytestring.@@ -303,14 +328,15 @@   filesz <- catch (hFileSize h) useZeroIfNotRegularFile   let hint = (fromIntegral filesz `max` 255) + 1   hGetContentsHint hint h+ where   -- Our initial size is one bigger than the file size so that in the   -- typical case we will read the whole file in one go and not have   -- to allocate any more chunks. We'll still do the right thing if the   -- file size is 0 or is changed before we do the read.-  where-    useZeroIfNotRegularFile :: IOException -> IO Integer-    useZeroIfNotRegularFile _ = return 0 +  useZeroIfNotRegularFile :: IOException -> IO Integer+  useZeroIfNotRegularFile _ = return 0+ chunkSize :: Int chunkSize = 16384 - 16 @@ -324,63 +350,77 @@  -- | Left fold over all bytes in the chunks, strict in the accumulator. foldl' :: (a -> Word8 -> a) -> a -> Chunks -> a-{-# inline foldl' #-}-foldl' g = go where+{-# INLINE foldl' #-}+foldl' g = go+ where   go !a ChunksNil = a   go !a (ChunksCons c cs) = go (Bytes.foldl' g a c) cs  -- | Hash byte sequence with 32-bit variant of FNV-1a. fnv1a32 :: Chunks -> Word32-fnv1a32 !b = foldl'-  (\acc w -> (fromIntegral @Word8 @Word32 w `xor` acc) * 0x01000193-  ) 0x811c9dc5 b+fnv1a32 !b =+  foldl'+    ( \acc w -> (fromIntegral @Word8 @Word32 w `xor` acc) * 0x01000193+    )+    0x811c9dc5+    b  -- | Hash byte sequence with 64-bit variant of FNV-1a. fnv1a64 :: Chunks -> Word64-fnv1a64 !b = foldl'-  (\acc w -> (fromIntegral @Word8 @Word64 w `xor` acc) * 0x00000100000001B3-  ) 0xcbf29ce484222325 b+fnv1a64 !b =+  foldl'+    ( \acc w -> (fromIntegral @Word8 @Word64 w `xor` acc) * 0x00000100000001B3+    )+    0xcbf29ce484222325+    b --- | Outputs 'Chunks' to the specified 'Handle'. This is implemented--- with 'IO.hPut'.+{- | Outputs 'Chunks' to the specified 'Handle'. This is implemented+with 'IO.hPut'.+-} hPut :: Handle -> Chunks -> IO ()-hPut h = go where+hPut h = go+ where   go ChunksNil = pure ()   go (ChunksCons c cs) = IO.hPut h c *> go cs --- | Write 'Chunks' to a file, replacing the previous contents of--- the file.+{- | Write 'Chunks' to a file, replacing the previous contents of+the file.+-} writeFile :: FilePath -> Chunks -> IO () writeFile path cs = withBinaryFile path WriteMode (\h -> hPut h cs) --- | Break chunks of bytes into contiguous pieces separated by the--- byte argument. This is a good producer for list fusion. For this--- function to perform well, each chunk should contain multiple separators.--- Any piece that spans multiple chunks must be copied.+{- | Break chunks of bytes into contiguous pieces separated by the+byte argument. This is a good producer for list fusion. For this+function to perform well, each chunk should contain multiple separators.+Any piece that spans multiple chunks must be copied.+-} split :: Word8 -> Chunks -> [Bytes]-{-# inline split #-}-split !w !cs0 = Exts.build-  (\g x0 ->-    -- It is possible to optimize for the common case where a-    -- piece does not span multiple chunks. However, such an-    -- optimization would actually cause this to tail call in-    -- two places rather than one and may actually adversely-    -- affect performance. It hasn't been benchmarked.-    let go !cs = case splitOnto ChunksNil w cs of-          (hd,tl) -> let !x = concat (reverse hd) in-            case tl of-              ChunksNil -> x0-              _ -> g x (go tl)-     in go cs0-  )+{-# INLINE split #-}+split !w !cs0 =+  Exts.build+    ( \g x0 ->+        -- It is possible to optimize for the common case where a+        -- piece does not span multiple chunks. However, such an+        -- optimization would actually cause this to tail call in+        -- two places rather than one and may actually adversely+        -- affect performance. It hasn't been benchmarked.+        let go !cs = case splitOnto ChunksNil w cs of+              (hd, tl) ->+                let !x = concat (reverse hd)+                 in case tl of+                      ChunksNil -> x0+                      _ -> g x (go tl)+         in go cs0+    ) -splitOnto :: Chunks -> Word8 -> Chunks -> (Chunks,Chunks)-{-# inline splitOnto #-}-splitOnto !acc0 !w !cs0 = go acc0 cs0 where-  go !acc ChunksNil = (acc,ChunksNil)+splitOnto :: Chunks -> Word8 -> Chunks -> (Chunks, Chunks)+{-# INLINE splitOnto #-}+splitOnto !acc0 !w !cs0 = go acc0 cs0+ where+  go !acc ChunksNil = (acc, ChunksNil)   go !acc (ChunksCons b bs) = case Byte.split1 w b of     Nothing -> go (ChunksCons b acc) bs-    Just (hd,tl) ->+    Just (hd, tl) ->       let !r1 = ChunksCons hd acc           !r2 = ChunksCons tl bs-       in (r1,r2)+       in (r1, r2)
src/Data/Bytes/Encode/BigEndian.hs view
@@ -1,5 +1,5 @@-{-# language BangPatterns #-}-{-# language TypeApplications #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeApplications #-}  module Data.Bytes.Encode.BigEndian   ( word16@@ -13,17 +13,17 @@ import Control.Monad.ST.Run (runByteArrayST) import Data.Bits (unsafeShiftR) import Data.Bytes.Types (Bytes)-import Data.Int (Int16,Int32,Int64)+import Data.Int (Int16, Int32, Int64) import Data.Primitive (ByteArray)-import Data.Word (Word8,Word16,Word32,Word64)+import Data.Word (Word16, Word32, Word64, Word8)  import qualified Data.Bytes.Pure as Pure import qualified Data.Primitive as PM  -- | Encode a 32-bit signed integer as 4 bytes. int32 :: Int32 -> Bytes-{-# inline int32 #-}-int32 = word32 . fromIntegral @Int32 @Word32 +{-# INLINE int32 #-}+int32 = word32 . fromIntegral @Int32 @Word32  -- | Encode a 32-bit unsigned integer as 4 bytes. word32 :: Word32 -> Bytes@@ -40,8 +40,8 @@  -- | Encode a 16-bit signed integer as 4 bytes. int16 :: Int16 -> Bytes-{-# inline int16 #-}-int16 = word16 . fromIntegral @Int16 @Word16 +{-# INLINE int16 #-}+int16 = word16 . fromIntegral @Int16 @Word16  -- | Encode a 16-bit unsigned integer as 4 bytes. word16 :: Word16 -> Bytes@@ -56,8 +56,8 @@  -- | Encode a 16-bit signed integer as 4 bytes. int64 :: Int64 -> Bytes-{-# inline int64 #-}-int64 = word64 . fromIntegral @Int64 @Word64 +{-# INLINE int64 #-}+int64 = word64 . fromIntegral @Int64 @Word64  -- | Encode a 16-bit unsigned integer as 4 bytes. word64 :: Word64 -> Bytes
src/Data/Bytes/Encode/LittleEndian.hs view
@@ -1,5 +1,5 @@-{-# language BangPatterns #-}-{-# language TypeApplications #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeApplications #-}  module Data.Bytes.Encode.LittleEndian   ( word16@@ -13,17 +13,17 @@ import Control.Monad.ST.Run (runByteArrayST) import Data.Bits (unsafeShiftR) import Data.Bytes.Types (Bytes)-import Data.Int (Int16,Int32,Int64)+import Data.Int (Int16, Int32, Int64) import Data.Primitive (ByteArray)-import Data.Word (Word8,Word16,Word32,Word64)+import Data.Word (Word16, Word32, Word64, Word8)  import qualified Data.Bytes.Pure as Pure import qualified Data.Primitive as PM  -- | Encode a 32-bit signed integer as 4 bytes. int32 :: Int32 -> Bytes-{-# inline int32 #-}-int32 = word32 . fromIntegral @Int32 @Word32 +{-# INLINE int32 #-}+int32 = word32 . fromIntegral @Int32 @Word32  -- | Encode a 32-bit unsigned integer as 4 bytes. word32 :: Word32 -> Bytes@@ -40,8 +40,8 @@  -- | Encode a 16-bit signed integer as 4 bytes. int16 :: Int16 -> Bytes-{-# inline int16 #-}-int16 = word16 . fromIntegral @Int16 @Word16 +{-# INLINE int16 #-}+int16 = word16 . fromIntegral @Int16 @Word16  -- | Encode a 16-bit unsigned integer as 4 bytes. word16 :: Word16 -> Bytes@@ -56,8 +56,8 @@  -- | Encode a 16-bit signed integer as 4 bytes. int64 :: Int64 -> Bytes-{-# inline int64 #-}-int64 = word64 . fromIntegral @Int64 @Word64 +{-# INLINE int64 #-}+int64 = word64 . fromIntegral @Int64 @Word64  -- | Encode a 16-bit unsigned integer as 4 bytes. word64 :: Word64 -> Bytes
src/Data/Bytes/IO.hs view
@@ -1,33 +1,33 @@-{-# language BangPatterns #-}-{-# language BlockArguments #-}-{-# language MagicHash #-}-{-# language NamedFieldPuns #-}-{-# language TypeApplications #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UnboxedTuples #-}  module Data.Bytes.IO   ( hGet   , hPut   ) where -import Data.Primitive (MutableByteArray,ByteArray(..))+import Data.Bytes.Pure (contents, pin)+import Data.Bytes.Types (Bytes (Bytes))+import Data.Primitive (ByteArray (..), MutableByteArray)+import qualified Data.Primitive as PM import Data.Word (Word8)-import Data.Bytes.Types (Bytes(Bytes))-import Data.Bytes.Pure (pin,contents)-import System.IO (Handle) import Foreign.Ptr (Ptr)-import GHC.IO (IO(IO))-import qualified System.IO as IO import qualified GHC.Exts as Exts-import qualified Data.Primitive as PM+import GHC.IO (IO (IO))+import System.IO (Handle)+import qualified System.IO as IO --- | Read 'Bytes' directly from the specified 'Handle'. The resulting--- 'Bytes' are pinned. This is implemented with 'IO.hGetBuf'.+{- | Read 'Bytes' directly from the specified 'Handle'. The resulting+'Bytes' are pinned. This is implemented with 'IO.hGetBuf'.+-} hGet :: Handle -> Int -> IO Bytes hGet h i = createPinnedAndTrim i (\p -> IO.hGetBuf h p i) --- | Outputs 'Bytes' to the specified 'Handle'. This is implemented--- with 'IO.hPutBuf'.+{- | Outputs 'Bytes' to the specified 'Handle'. This is implemented+with 'IO.hPutBuf'.+-} hPut :: Handle -> Bytes -> IO () hPut h b0 = do   let b1@(Bytes arr _ len) = pin b0@@ -36,7 +36,7 @@  -- Only used internally. createPinnedAndTrim :: Int -> (Ptr Word8 -> IO Int) -> IO Bytes-{-# inline createPinnedAndTrim #-}+{-# INLINE createPinnedAndTrim #-} createPinnedAndTrim maxSz f = do   arr@(PM.MutableByteArray arr#) <- PM.newPinnedByteArray maxSz   sz <- f (PM.mutableByteArrayContents arr)
src/Data/Bytes/Internal.hs view
@@ -1,25 +1,24 @@-{-# language BangPatterns #-}-{-# language DataKinds #-}-{-# language MagicHash #-}-{-# language TypeFamilies #-}-{-# language DuplicateRecordFields #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeFamilies #-}  -- This needs to be in its own module to prevent a cyclic dependency -- between UnliftedBytes and Data.Bytes.Types module Data.Bytes.Internal-  ( Bytes(..)+  ( Bytes (..)   ) where  import Control.Monad.ST (runST) import Control.Monad.ST.Run (runByteArrayST)-import Data.Primitive (ByteArray(..))-import Data.Word (Word8)-import GHC.Exts (Int(I#),unsafeCoerce#,sameMutableByteArray#)-import GHC.Exts (isTrue#,compareByteArrays#,IsList(..)) import Data.Bytes.Internal.Show (showsSlice)+import Data.Primitive (ByteArray (..))+import Data.Word (Word8)+import GHC.Exts (Int (I#), IsList (..), compareByteArrays#, isTrue#, sameMutableByteArray#, unsafeCoerce#) -import qualified Data.List as L import qualified Data.Foldable as F+import qualified Data.List as L import qualified Data.Primitive as PM  -- | A slice of a 'ByteArray'.@@ -36,9 +35,10 @@   toList (Bytes arr off len) = toListLoop off len arr  toListLoop :: Int -> Int -> ByteArray -> [Word8]-toListLoop !off !len !arr = if len > 0-  then PM.indexByteArray arr off : toListLoop (off + 1) (len - 1) arr-  else []+toListLoop !off !len !arr =+  if len > 0+    then PM.indexByteArray arr off : toListLoop (off + 1) (len - 1) arr+    else []  instance Show Bytes where   showsPrec _ (Bytes arr off len) s = showsSlice arr off len s@@ -69,15 +69,18 @@   mconcat [] = mempty   mconcat [x] = x   mconcat bs = Bytes r 0 fullLen-    where+   where     !fullLen = L.foldl' (\acc (Bytes _ _ len) -> acc + len) 0 bs     r = runByteArrayST $ do       marr <- PM.newByteArray fullLen-      !_ <- F.foldlM-        (\ !currLen (Bytes arr off len) -> do-          PM.copyByteArray marr currLen arr off len-          pure (currLen + len)-        ) 0 bs+      !_ <-+        F.foldlM+          ( \ !currLen (Bytes arr off len) -> do+              PM.copyByteArray marr currLen arr off len+              pure (currLen + len)+          )+          0+          bs       PM.unsafeFreezeByteArray marr  compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering@@ -89,4 +92,3 @@ {-# INLINE sameByteArray #-} sameByteArray (ByteArray ba1#) (ByteArray ba2#) =   isTrue# (sameMutableByteArray# (unsafeCoerce# ba1#) (unsafeCoerce# ba2#))-
src/Data/Bytes/Internal/Show.hs view
@@ -1,10 +1,10 @@-{-# language BangPatterns #-}+{-# LANGUAGE BangPatterns #-}  module Data.Bytes.Internal.Show   ( showsSlice   ) where -import Data.Bits ((.&.),unsafeShiftR)+import Data.Bits (unsafeShiftR, (.&.)) import Data.Char (ord) import Data.Primitive (ByteArray) import Data.Word (Word8)@@ -13,23 +13,27 @@ import qualified Data.Primitive as PM  showsSlice :: ByteArray -> Int -> Int -> String -> String-showsSlice arr off len s = if len == 0-  then showString "[]" s-  else showString "[0x"-     $ showHexDigitsWord8 (PM.indexByteArray arr off)-     $ showHexLoop (off + 1) (len - 1) arr-     $ showChar ']'-     $ s+showsSlice arr off len s =+  if len == 0+    then showString "[]" s+    else+      showString "[0x" $+        showHexDigitsWord8 (PM.indexByteArray arr off) $+          showHexLoop (off + 1) (len - 1) arr $+            showChar ']' $+              s  showHexLoop :: Int -> Int -> ByteArray -> String -> String-showHexLoop !ix !len !arr s = if len > 0-  then ',':'0':'x':showHexDigitsWord8 (PM.indexByteArray arr ix) (showHexLoop (ix + 1) (len - 1) arr s)-  else s+showHexLoop !ix !len !arr s =+  if len > 0+    then ',' : '0' : 'x' : showHexDigitsWord8 (PM.indexByteArray arr ix) (showHexLoop (ix + 1) (len - 1) arr s)+    else s  showHexDigitsWord8 :: Word8 -> String -> String showHexDigitsWord8 !w s = word4ToChar (unsafeShiftR w 4) : word4ToChar (0x0F .&. w) : s  word4ToChar :: Word8 -> Char-word4ToChar w = if w < 10-  then unsafeChr (ord '0' + fromIntegral w)-  else unsafeChr (ord 'a' + (fromIntegral w) - 10)+word4ToChar w =+  if w < 10+    then unsafeChr (ord '0' + fromIntegral w)+    else unsafeChr (ord 'a' + (fromIntegral w) - 10)
src/Data/Bytes/Mutable.hs view
@@ -1,76 +1,87 @@-{-# language BangPatterns #-}-{-# language LambdaCase #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE LambdaCase #-} --- | If you are interested in sub-arrays of 'MutableByteArray's (e.g. writing--- quicksort), it would be grossly inefficient to make a copy of the sub-array.--- On the other hand, it'd be really annoying to track limit indices by hand.------ This module defines the 'MutableBytes' type which exposes a standard array--- interface for a sub-arrays without copying and without manual index--- manipulation. For immutable arrays, see 'Data.Bytes'.+{- | If you are interested in sub-arrays of 'MutableByteArray's (e.g. writing+quicksort), it would be grossly inefficient to make a copy of the sub-array.+On the other hand, it'd be really annoying to track limit indices by hand.++This module defines the 'MutableBytes' type which exposes a standard array+interface for a sub-arrays without copying and without manual index+manipulation. For immutable arrays, see 'Data.Bytes'.+-} module Data.Bytes.Mutable   ( -- * Types     MutableBytes+     -- * Filtering   , takeWhile   , dropWhile+     -- * Unsafe Slicing   , unsafeTake   , unsafeDrop+     -- * Conversion   , fromMutableByteArray   ) where -import Prelude hiding (takeWhile,dropWhile)+import Prelude hiding (dropWhile, takeWhile) -import Data.Bytes.Types (MutableBytes(MutableBytes))+import Control.Monad.Primitive (PrimMonad, PrimState)+import Data.Bytes.Types (MutableBytes (MutableBytes)) import Data.Primitive (MutableByteArray) import Data.Word (Word8)-import Control.Monad.Primitive (PrimMonad,PrimState)  import qualified Data.Primitive as PM --- | Take bytes while the predicate is true, aliasing the--- argument array.-takeWhile :: PrimMonad m-  => (Word8 -> m Bool)-  -> MutableBytes (PrimState m)-  -> m (MutableBytes (PrimState m))-{-# inline takeWhile #-}+{- | Take bytes while the predicate is true, aliasing the+argument array.+-}+takeWhile ::+  (PrimMonad m) =>+  (Word8 -> m Bool) ->+  MutableBytes (PrimState m) ->+  m (MutableBytes (PrimState m))+{-# INLINE takeWhile #-} takeWhile k b = do   n <- countWhile k b   pure (unsafeTake n b) --- | Drop bytes while the predicate is true, aliasing the--- argument array.-dropWhile :: PrimMonad m-  => (Word8 -> m Bool)-  -> MutableBytes (PrimState m)-  -> m (MutableBytes (PrimState m))-{-# inline dropWhile #-}+{- | Drop bytes while the predicate is true, aliasing the+argument array.+-}+dropWhile ::+  (PrimMonad m) =>+  (Word8 -> m Bool) ->+  MutableBytes (PrimState m) ->+  m (MutableBytes (PrimState m))+{-# INLINE dropWhile #-} dropWhile k b = do   n <- countWhile k b   pure (unsafeDrop n b)  -- | Take the first @n@ bytes from the argument, aliasing it. unsafeTake :: Int -> MutableBytes s -> MutableBytes s-{-# inline unsafeTake #-}+{-# INLINE unsafeTake #-} unsafeTake n (MutableBytes arr off _) =   MutableBytes arr off n --- | Drop the first @n@ bytes from the argument, aliasing it.--- The new length will be @len - n@.+{- | Drop the first @n@ bytes from the argument, aliasing it.+The new length will be @len - n@.+-} unsafeDrop :: Int -> MutableBytes s -> MutableBytes s-{-# inline unsafeDrop #-}+{-# INLINE unsafeDrop #-} unsafeDrop n (MutableBytes arr off len) =   MutableBytes arr (off + n) (len - n) --- | Create a slice of 'MutableBytes' that spans the entire--- argument array. This aliases the argument.-fromMutableByteArray :: PrimMonad m-  => MutableByteArray (PrimState m)-  -> m (MutableBytes (PrimState m))-{-# inline fromMutableByteArray #-}+{- | Create a slice of 'MutableBytes' that spans the entire+argument array. This aliases the argument.+-}+fromMutableByteArray ::+  (PrimMonad m) =>+  MutableByteArray (PrimState m) ->+  m (MutableBytes (PrimState m))+{-# INLINE fromMutableByteArray #-} fromMutableByteArray mba = do   sz <- PM.getSizeofMutableByteArray mba   pure (MutableBytes mba 0 sz)@@ -79,14 +90,18 @@ -- predicate until the first non-match occurs. If all bytes -- match the predicate, this will return the length originally -- provided.-countWhile :: PrimMonad m-  => (Word8 -> m Bool)-  -> MutableBytes (PrimState m)-  -> m Int-{-# inline countWhile #-}-countWhile k (MutableBytes arr off0 len0) = go off0 len0 0 where-  go !off !len !n = if len > 0-    then (k =<< PM.readByteArray arr off) >>= \case-      True -> go (off + 1) (len - 1) (n + 1)-      False -> pure n-    else pure n+countWhile ::+  (PrimMonad m) =>+  (Word8 -> m Bool) ->+  MutableBytes (PrimState m) ->+  m Int+{-# INLINE countWhile #-}+countWhile k (MutableBytes arr off0 len0) = go off0 len0 0+ where+  go !off !len !n =+    if len > 0+      then+        (k =<< PM.readByteArray arr off) >>= \case+          True -> go (off + 1) (len - 1) (n + 1)+          False -> pure n+      else pure n
src/Data/Bytes/Pure.hs view
@@ -1,9 +1,7 @@-{-# language BangPatterns #-}-{-# language BlockArguments #-}-{-# language MagicHash #-}-{-# language NamedFieldPuns #-}-{-# language TypeApplications #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeApplications #-}  module Data.Bytes.Pure   ( empty@@ -44,17 +42,17 @@   , replicateU   ) where -import Prelude hiding (Foldable(..),map,replicate)+import Prelude hiding (Foldable (..), map, replicate) -import Control.Monad.Primitive (PrimState,PrimMonad)+import Control.Monad.Primitive (PrimMonad, PrimState) import Control.Monad.ST.Run (runByteArrayST) import Data.Bits (xor)-import Data.Bytes.Types (Bytes(Bytes)) import Data.ByteString (ByteString)-import Data.ByteString.Short.Internal (ShortByteString(SBS))-import Data.Primitive (ByteArray(ByteArray),MutableByteArray,PrimArray(PrimArray))-import Data.Word (Word64,Word32,Word8)-import Foreign.Ptr (Ptr,plusPtr)+import Data.ByteString.Short.Internal (ShortByteString (SBS))+import Data.Bytes.Types (Bytes (Bytes))+import Data.Primitive (ByteArray (ByteArray), MutableByteArray, PrimArray (PrimArray))+import Data.Word (Word32, Word64, Word8)+import Foreign.Ptr (Ptr, plusPtr) import GHC.IO (unsafeIOToST)  import qualified Data.ByteString as ByteString@@ -72,110 +70,133 @@  -- | The empty pinned byte sequence. emptyPinned :: Bytes-emptyPinned = Bytes-  ( runByteArrayST-    (PM.newPinnedByteArray 0 >>= PM.unsafeFreezeByteArray)-  ) 0 0+emptyPinned =+  Bytes+    ( runByteArrayST+        (PM.newPinnedByteArray 0 >>= PM.unsafeFreezeByteArray)+    )+    0+    0  -- | The empty pinned byte sequence. emptyPinnedU :: ByteArray-emptyPinnedU = runByteArrayST-  (PM.newPinnedByteArray 0 >>= PM.unsafeFreezeByteArray)+emptyPinnedU =+  runByteArrayST+    (PM.newPinnedByteArray 0 >>= PM.unsafeFreezeByteArray) --- | Yields a pinned byte sequence whose contents are identical to those--- of the original byte sequence. If the @ByteArray@ backing the argument--- was already pinned, this simply aliases the argument and does not perform--- any copying.+{- | Yields a pinned byte sequence whose contents are identical to those+of the original byte sequence. If the @ByteArray@ backing the argument+was already pinned, this simply aliases the argument and does not perform+any copying.+-} pin :: Bytes -> Bytes pin b@(Bytes arr _ len) = case PM.isByteArrayPinned arr of   True -> b-  False -> Bytes-    ( runByteArrayST do-        dst <- PM.newPinnedByteArray len-        unsafeCopy dst 0 b-        PM.unsafeFreezeByteArray dst-    ) 0 len+  False ->+    Bytes+      ( runByteArrayST do+          dst <- PM.newPinnedByteArray len+          unsafeCopy dst 0 b+          PM.unsafeFreezeByteArray dst+      )+      0+      len --- | Convert the sliced 'Bytes' to an unsliced 'ByteArray'. This--- reuses the array backing the sliced 'Bytes' if the slicing metadata--- implies that all of the bytes are used. Otherwise, it makes a copy.+{- | Convert the sliced 'Bytes' to an unsliced 'ByteArray'. This+reuses the array backing the sliced 'Bytes' if the slicing metadata+implies that all of the bytes are used. Otherwise, it makes a copy.+-} toByteArray :: Bytes -> ByteArray-{-# inline toByteArray #-}+{-# INLINE toByteArray #-} toByteArray b@(Bytes arr off len)   | off == 0, PM.sizeofByteArray arr == len = arr   | otherwise = toByteArrayClone b --- | Variant of 'toByteArray' that unconditionally makes a copy of--- the array backing the sliced 'Bytes' even if the original array--- could be reused. Prefer 'toByteArray'.+{- | Variant of 'toByteArray' that unconditionally makes a copy of+the array backing the sliced 'Bytes' even if the original array+could be reused. Prefer 'toByteArray'.+-} toByteArrayClone :: Bytes -> ByteArray-{-# inline toByteArrayClone #-}+{-# INLINE toByteArrayClone #-} toByteArrayClone (Bytes arr off len) = runByteArrayST $ do   m <- PM.newByteArray len   PM.copyByteArray m 0 arr off len   PM.unsafeFreezeByteArray m --- | Copy the byte sequence into a mutable buffer. The buffer must have--- enough space to accomodate the byte sequence, but this this is not--- checked.-unsafeCopy :: PrimMonad m-  => MutableByteArray (PrimState m) -- ^ Destination-  -> Int -- ^ Destination Offset-  -> Bytes -- ^ Source-  -> m ()-{-# inline unsafeCopy #-}+{- | Copy the byte sequence into a mutable buffer. The buffer must have+enough space to accomodate the byte sequence, but this this is not+checked.+-}+unsafeCopy ::+  (PrimMonad m) =>+  -- | Destination+  MutableByteArray (PrimState m) ->+  -- | Destination Offset+  Int ->+  -- | Source+  Bytes ->+  m ()+{-# INLINE unsafeCopy #-} unsafeCopy dst dstIx (Bytes src srcIx len) =   PM.copyByteArray dst dstIx src srcIx len  -- | Create a slice of 'Bytes' that spans the entire argument array. fromByteArray :: ByteArray -> Bytes-{-# inline fromByteArray #-}+{-# INLINE fromByteArray #-} fromByteArray b = Bytes b 0 (PM.sizeofByteArray b)  -- | Create a slice of 'Bytes' that spans the entire 'PrimArray' of 8-bit words. fromPrimArray :: PrimArray Word8 -> Bytes-{-# inline fromPrimArray #-}+{-# INLINE fromPrimArray #-} fromPrimArray p@(PrimArray b) = Bytes (ByteArray b) 0 (PM.sizeofPrimArray p)  -- | The length of a slice of bytes. length :: Bytes -> Int-{-# inline length #-}+{-# INLINE length #-} length (Bytes _ _ len) = len  -- | Hash byte sequence with 32-bit variant of FNV-1a. fnv1a32 :: Bytes -> Word32-fnv1a32 !b = foldl'-  (\acc w -> (fromIntegral @Word8 @Word32 w `xor` acc) * 0x01000193-  ) 0x811c9dc5 b+fnv1a32 !b =+  foldl'+    ( \acc w -> (fromIntegral @Word8 @Word32 w `xor` acc) * 0x01000193+    )+    0x811c9dc5+    b  -- | Hash byte sequence with 64-bit variant of FNV-1a. fnv1a64 :: Bytes -> Word64-fnv1a64 !b = foldl'-  (\acc w -> (fromIntegral @Word8 @Word64 w `xor` acc) * 0x00000100000001B3-  ) 0xcbf29ce484222325 b+fnv1a64 !b =+  foldl'+    ( \acc w -> (fromIntegral @Word8 @Word64 w `xor` acc) * 0x00000100000001B3+    )+    0xcbf29ce484222325+    b  -- | Left fold over bytes, non-strict in the accumulator. foldl :: (a -> Word8 -> a) -> a -> Bytes -> a-{-# inline foldl #-}+{-# INLINE foldl #-} foldl f a0 (Bytes arr off0 len0) =-  go (off0 + len0 - 1) (len0 - 1) -  where+  go (off0 + len0 - 1) (len0 - 1)+ where   go !off !ix = case ix of     (-1) -> a0     _ -> f (go (off - 1) (ix - 1)) (PM.indexByteArray arr off)  -- | Left fold over bytes, strict in the accumulator. foldl' :: (a -> Word8 -> a) -> a -> Bytes -> a-{-# inline foldl' #-}-foldl' f a0 (Bytes arr off0 len0) = go a0 off0 len0 where+{-# INLINE foldl' #-}+foldl' f a0 (Bytes arr off0 len0) = go a0 off0 len0+ where   go !a !off !len = case len of     0 -> a     _ -> go (f a (PM.indexByteArray arr off)) (off + 1) (len - 1)  -- | Left monadic fold over bytes, non-strict in the accumulator.-foldlM :: Monad m => (a -> Word8 -> m a) -> a -> Bytes -> m a-{-# inline foldlM #-}-foldlM f a0 (Bytes arr off0 len0) = go a0 off0 len0 where+foldlM :: (Monad m) => (a -> Word8 -> m a) -> a -> Bytes -> m a+{-# INLINE foldlM #-}+foldlM f a0 (Bytes arr off0 len0) = go a0 off0 len0+ where   go a !off !len = case len of     0 -> pure a     _ -> do@@ -184,94 +205,106 @@  -- | Right fold over bytes, non-strict in the accumulator. foldr :: (Word8 -> a -> a) -> a -> Bytes -> a-{-# inline foldr #-}-foldr f a0 (Bytes arr off0 len0) = go off0 len0 where+{-# INLINE foldr #-}+foldr f a0 (Bytes arr off0 len0) = go off0 len0+ where   go !off !len = case len of     0 -> a0     _ -> f (PM.indexByteArray arr off) (go (off + 1) (len - 1)) --- | Left fold over bytes, strict in the accumulator. The reduction function--- is applied to each element along with its index.+{- | Left fold over bytes, strict in the accumulator. The reduction function+is applied to each element along with its index.+-} ifoldl' :: (a -> Int -> Word8 -> a) -> a -> Bytes -> a-{-# inline ifoldl' #-}-ifoldl' f a0 (Bytes arr off0 len0) = go a0 0 off0 len0 where+{-# INLINE ifoldl' #-}+ifoldl' f a0 (Bytes arr off0 len0) = go a0 0 off0 len0+ where   go !a !ix !off !len = case len of     0 -> a     _ -> go (f a ix (PM.indexByteArray arr off)) (ix + 1) (off + 1) (len - 1)  -- | Right fold over bytes, strict in the accumulator. foldr' :: (Word8 -> a -> a) -> a -> Bytes -> a-{-# inline foldr' #-}+{-# INLINE foldr' #-} foldr' f a0 (Bytes arr off0 len0) =-  go a0 (off0 + len0 - 1) (len0 - 1) -  where+  go a0 (off0 + len0 - 1) (len0 - 1)+ where   go !a !off !ix = case ix of     (-1) -> a     _ -> go (f (PM.indexByteArray arr off) a) (off - 1) (ix - 1)  -- | Right monadic fold over bytes, non-strict in the accumulator.-foldrM :: Monad m => (Word8 -> a -> m a) -> a -> Bytes -> m a-{-# inline foldrM #-}+foldrM :: (Monad m) => (Word8 -> a -> m a) -> a -> Bytes -> m a+{-# INLINE foldrM #-} foldrM f a0 (Bytes arr off0 len0) =-  go a0 (off0 + len0 - 1) (len0 - 1) -  where+  go a0 (off0 + len0 - 1) (len0 - 1)+ where   go !a !off !ix = case ix of     (-1) -> pure a     _ -> do       a' <- f (PM.indexByteArray arr off) a       go a' (off - 1) (ix - 1) --- | Yields a pointer to the beginning of the byte sequence. It is only safe--- to call this on a 'Bytes' backed by a pinned @ByteArray@.+{- | Yields a pointer to the beginning of the byte sequence. It is only safe+to call this on a 'Bytes' backed by a pinned @ByteArray@.+-} contents :: Bytes -> Ptr Word8-{-# inline contents #-}+{-# INLINE contents #-} contents (Bytes arr off _) = plusPtr (PM.byteArrayContents arr) off --- | Convert the sliced 'Bytes' to an unsliced 'ByteArray'. This--- reuses the array backing the sliced 'Bytes' if the slicing metadata--- implies that all of the bytes are used and they are already pinned.--- Otherwise, it makes a copy.+{- | Convert the sliced 'Bytes' to an unsliced 'ByteArray'. This+reuses the array backing the sliced 'Bytes' if the slicing metadata+implies that all of the bytes are used and they are already pinned.+Otherwise, it makes a copy.+-} toPinnedByteArray :: Bytes -> ByteArray-{-# inline toPinnedByteArray #-}+{-# INLINE toPinnedByteArray #-} toPinnedByteArray b@(Bytes arr off len)   | off == 0, PM.sizeofByteArray arr == len, PM.isByteArrayPinned arr = arr   | otherwise = toPinnedByteArrayClone b --- | Variant of 'toPinnedByteArray' that unconditionally makes a copy of--- the array backing the sliced 'Bytes' even if the original array--- could be reused. Prefer 'toPinnedByteArray'.+{- | Variant of 'toPinnedByteArray' that unconditionally makes a copy of+the array backing the sliced 'Bytes' even if the original array+could be reused. Prefer 'toPinnedByteArray'.+-} toPinnedByteArrayClone :: Bytes -> ByteArray toPinnedByteArrayClone (Bytes arr off len) = runByteArrayST $ do   m <- PM.newPinnedByteArray len   PM.copyByteArray m 0 arr off len   PM.unsafeFreezeByteArray m --- | /O(n)/ when unpinned, /O(1)/ when pinned. Create a 'ByteString' from--- a byte sequence. This only copies the byte sequence if it is not pinned.+{- | /O(n)/ when unpinned, /O(1)/ when pinned. Create a 'ByteString' from+a byte sequence. This only copies the byte sequence if it is not pinned.+-} toByteString :: Bytes -> ByteString toByteString !b = pinnedToByteString (pin b) --- | Convert a pinned 'Bytes' to a 'ByteString'--- /O(1)/ Precondition: bytes are pinned. Behavior is undefined otherwise.+{- | Convert a pinned 'Bytes' to a 'ByteString'+/O(1)/ Precondition: bytes are pinned. Behavior is undefined otherwise.+-} pinnedToByteString :: Bytes -> ByteString pinnedToByteString (Bytes y@(PM.ByteArray x) off len) =   ByteString.PS-    (ForeignPtr.ForeignPtr-      (case plusPtr (PM.byteArrayContents y) off of {Exts.Ptr p -> p})-      (ForeignPtr.PlainPtr (Exts.unsafeCoerce# x))+    ( ForeignPtr.ForeignPtr+        (case plusPtr (PM.byteArrayContents y) off of Exts.Ptr p -> p)+        (ForeignPtr.PlainPtr (Exts.unsafeCoerce# x))     )-    0 len+    0+    len  -- | /O(n)/ Copy a 'ByteString' to a byte sequence. fromByteString :: ByteString -> Bytes-fromByteString !b = Bytes-  ( runByteArrayST $ unsafeIOToST $ do -      dst@(PM.MutableByteArray dst# ) <- PM.newByteArray len-      ByteString.unsafeUseAsCString b $ \src -> do-        PM.copyPtrToMutablePrimArray (PM.MutablePrimArray dst# ) 0 src len-      PM.unsafeFreezeByteArray dst-  ) 0 len-  where+fromByteString !b =+  Bytes+    ( runByteArrayST $ unsafeIOToST $ do+        dst@(PM.MutableByteArray dst#) <- PM.newByteArray len+        ByteString.unsafeUseAsCString b $ \src -> do+          PM.copyPtrToMutablePrimArray (PM.MutablePrimArray dst#) 0 src len+        PM.unsafeFreezeByteArray dst+    )+    0+    len+ where   !len = ByteString.length b  -- | /O(n)/ Copy a lazy bytestring to a byte sequence.@@ -281,78 +314,86 @@   n64 ->     let n = fromIntegral n64 :: Int      in Bytes-        (runByteArrayST $ unsafeIOToST $ do-          dst@(PM.MutableByteArray dst# ) <- PM.newByteArray n-          let loop chunks !ix = case chunks of-                LBS.Empty -> PM.unsafeFreezeByteArray dst-                LBS.Chunk c cs -> do-                  let !len = ByteString.length c-                  ByteString.unsafeUseAsCString c $ \src -> do-                    PM.copyPtrToMutablePrimArray (PM.MutablePrimArray dst# ) ix src len-                  loop cs (ix + len)-          loop x 0-        ) 0 n+          ( runByteArrayST $ unsafeIOToST $ do+              dst@(PM.MutableByteArray dst#) <- PM.newByteArray n+              let loop chunks !ix = case chunks of+                    LBS.Empty -> PM.unsafeFreezeByteArray dst+                    LBS.Chunk c cs -> do+                      let !len = ByteString.length c+                      ByteString.unsafeUseAsCString c $ \src -> do+                        PM.copyPtrToMutablePrimArray (PM.MutablePrimArray dst#) ix src len+                      loop cs (ix + len)+              loop x 0+          )+          0+          n  -- | Drop the first @n@ bytes from the argument. Precondition: @n ≤ len@ unsafeDrop :: Int -> Bytes -> Bytes-{-# inline unsafeDrop #-}+{-# INLINE unsafeDrop #-} unsafeDrop n (Bytes arr off len) =   Bytes arr (off + n) (len - n)  -- | Variant of 'map' that returns unsliced byte sequence. mapU :: (Word8 -> Word8) -> Bytes -> ByteArray-{-# inline mapU #-}+{-# INLINE mapU #-} mapU f (Bytes array ix0 len) = runByteArrayST do   dst <- PM.newByteArray len-  let go !srcIx !dstIx = if dstIx < len-        then do-          let w = PM.indexByteArray array srcIx :: Word8-          PM.writeByteArray dst dstIx (f w)-          go (srcIx + 1) (dstIx + 1)-        else PM.unsafeFreezeByteArray dst+  let go !srcIx !dstIx =+        if dstIx < len+          then do+            let w = PM.indexByteArray array srcIx :: Word8+            PM.writeByteArray dst dstIx (f w)+            go (srcIx + 1) (dstIx + 1)+          else PM.unsafeFreezeByteArray dst   go ix0 0 --- | Map over bytes in a sequence. The result has the same length as--- the argument. +{- | Map over bytes in a sequence. The result has the same length as+the argument.+-} map :: (Word8 -> Word8) -> Bytes -> Bytes-{-# inline map #-}+{-# INLINE map #-} map f !b = Bytes (mapU f b) 0 (length b)  -- | Is the byte sequence empty? null :: Bytes -> Bool-{-# inline null #-}+{-# INLINE null #-} null (Bytes _ _ len) = len == 0  -- | Take the first @n@ bytes from the argument. Precondition: @n ≤ len@ unsafeTake :: Int -> Bytes -> Bytes-{-# inline unsafeTake #-}+{-# INLINE unsafeTake #-} unsafeTake n (Bytes arr off _) =   Bytes arr off n --- | Index into the byte sequence at the given position. This index--- must be less than the length.+{- | Index into the byte sequence at the given position. This index+must be less than the length.+-} unsafeIndex :: Bytes -> Int -> Word8-{-# inline unsafeIndex #-}+{-# INLINE unsafeIndex #-} unsafeIndex (Bytes arr off _) ix = PM.indexByteArray arr (off + ix)  -- | Access the first byte. The given 'Bytes' must be non-empty.-{-# inline unsafeHead #-}+{-# INLINE unsafeHead #-} unsafeHead :: Bytes -> Word8 unsafeHead bs = unsafeIndex bs 0 --- | Convert the sliced 'Bytes' to an unsliced 'ShortByteString'. This--- reuses the array backing the sliced 'Bytes' if the slicing metadata--- implies that all of the bytes are used. Otherwise, it makes a copy.+{- | Convert the sliced 'Bytes' to an unsliced 'ShortByteString'. This+reuses the array backing the sliced 'Bytes' if the slicing metadata+implies that all of the bytes are used. Otherwise, it makes a copy.+-} toShortByteString :: Bytes -> ShortByteString-{-# inline toShortByteString #-}+{-# INLINE toShortByteString #-} toShortByteString !b = case toByteArray b of   PM.ByteArray x -> SBS x  -- | Replicate a byte @n@ times. replicate ::-     Int -- ^ Desired length @n@-  -> Word8 -- ^ Byte to replicate-  -> Bytes+  -- | Desired length @n@+  Int ->+  -- | Byte to replicate+  Word8 ->+  Bytes replicate !n !w = Bytes (replicateU n w) 0 n  -- | Variant of 'replicate' that returns a unsliced byte array.
src/Data/Bytes/Search.hs view
@@ -1,12 +1,9 @@-{-# language BangPatterns #-}-{-# language BlockArguments #-}-{-# language DuplicateRecordFields #-}-{-# language MagicHash #-}-{-# language NamedFieldPuns #-}-{-# language RankNTypes #-}-{-# language TupleSections #-}-{-# language TypeApplications #-}-{-# language UnboxedTuples #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}  -- This is broken out into a separate module to make it easier -- to dump core and investigate performance issues.@@ -16,14 +13,14 @@   , isInfixOf   ) where -import Prelude hiding (length,takeWhile,dropWhile,null,foldl,foldr,elem,replicate,any,all,readFile,map)+import Prelude hiding (all, any, dropWhile, elem, foldl, foldr, length, map, null, readFile, replicate, takeWhile) -import Control.Monad.ST.Run (runByteArrayST,runPrimArrayST)-import Data.Bits((.&.),(.|.),shiftL,finiteBitSize)-import Data.Bytes.Pure (length,unsafeIndex,unsafeHead)-import Data.Bytes.Types (Bytes(Bytes,array,offset))-import Data.Primitive (ByteArray,PrimArray)-import GHC.Exts (Int(I#))+import Control.Monad.ST.Run (runByteArrayST, runPrimArrayST)+import Data.Bits (finiteBitSize, shiftL, (.&.), (.|.))+import Data.Bytes.Pure (length, unsafeHead, unsafeIndex)+import Data.Bytes.Types (Bytes (Bytes, array, offset))+import Data.Primitive (ByteArray, PrimArray)+import GHC.Exts (Int (I#)) import GHC.Word (Word32)  import qualified Data.Bytes.Byte as Byte@@ -38,27 +35,36 @@ -- sure that there is no unnecessary boxing of Int, Word32, or Bytes -- going on. Here are some other things that have not been done: --+ -- * The hash is currently a Word32. It would be better to use either+ --   Word or Word64 for this. We would need for hashKey to be different.+ -- * In several places, we track an index into a Bytes. This index gets+ --   repeatedly added to the base offset as we loop over the bytes. We --   could instead track the true offset instead of repeatedly --   recalculating it. --- | Replace every non-overlapping occurrence of @needle@ in--- @haystack@ with @replacement@.+{- | Replace every non-overlapping occurrence of @needle@ in+@haystack@ with @replacement@.+-} replace ::-     Bytes -- ^ needle, must not be empty-  -> Bytes -- ^ replacement-  -> Bytes -- ^ haystack-  -> Bytes-{-# noinline replace #-}+  -- | needle, must not be empty+  Bytes ->+  -- | replacement+  Bytes ->+  -- | haystack+  Bytes ->+  Bytes+{-# NOINLINE replace #-} -- Implementation note: there is a lot of room to improve the performance -- of this function.-replace !needle !replacement !haystack@Bytes{array=haystackArray,offset=haystackIndex,length=haystackLength}+replace !needle !replacement !haystack@Bytes {array = haystackArray, offset = haystackIndex, length = haystackLength}   | Pure.length needle == 0 = errorWithoutStackTrace "Data.Bytes.replace: needle of length zero"   | Pure.length haystack == 0 = Pure.empty-  | Pure.length needle == 1, Pure.length replacement == 1 =+  | Pure.length needle == 1+  , Pure.length replacement == 1 =       let !needle0 = unsafeIndex needle 0           !replacement0 = unsafeIndex replacement 0        in Pure.map (\w -> if w == needle0 then replacement0 else w) haystack@@ -67,75 +73,93 @@           !ixs = findIndicesKarpRabin 0 hp needle haystackArray haystackIndex haystackLength        in Pure.fromByteArray (replaceIndices ixs replacement (Pure.length needle) haystackArray haystackIndex haystackLength) - -- This is an internal function because it deals explicitly with -- an offset into a byte array. -- -- Example:+ -- * haystack len: 39+ -- * ixs: 7, 19, 33+ -- * patLen: 5+ -- * replacment: foo (len 3)+ -- We want to perform these copies:+ -- * src[0,7] -> dst[0,7]+ -- * foo -> dst[7,3]+ -- * src[12,7] -> dst[10,7]+ -- * foo -> dst[17,3]+ -- * src[24,9] -> dst[20,9]+ -- * foo -> dst[29,3]+ -- * src[38,1] -> dst[32,1] replaceIndices :: PrimArray Int -> Bytes -> Int -> ByteArray -> Int -> Int -> ByteArray replaceIndices !ixs !replacement !patLen !haystack !ix0 !len0 = runByteArrayST $ do   let !ixsLen = PM.sizeofPrimArray ixs   let !delta = Pure.length replacement - patLen   dst <- PM.newByteArray (len0 + ixsLen * delta)-  let applyReplacement !ixIx !prevSrcIx = if ixIx < ixsLen-        then do-          let !srcMatchIx = PM.indexPrimArray ixs ixIx-          let !offset = ixIx * delta-          let !dstIx = srcMatchIx + offset - ix0-          Pure.unsafeCopy dst (prevSrcIx + offset - ix0)-            Bytes{array=haystack,offset=prevSrcIx,length=srcMatchIx - prevSrcIx}-          Pure.unsafeCopy dst dstIx replacement-          applyReplacement (ixIx + 1) (srcMatchIx + patLen)-        else do -          let !offset = ixIx * delta-          Pure.unsafeCopy dst (prevSrcIx + offset - ix0)-            Bytes{array=haystack,offset=prevSrcIx,length=(len0 + ix0) - prevSrcIx}-          PM.unsafeFreezeByteArray dst+  let applyReplacement !ixIx !prevSrcIx =+        if ixIx < ixsLen+          then do+            let !srcMatchIx = PM.indexPrimArray ixs ixIx+            let !offset = ixIx * delta+            let !dstIx = srcMatchIx + offset - ix0+            Pure.unsafeCopy+              dst+              (prevSrcIx + offset - ix0)+              Bytes {array = haystack, offset = prevSrcIx, length = srcMatchIx - prevSrcIx}+            Pure.unsafeCopy dst dstIx replacement+            applyReplacement (ixIx + 1) (srcMatchIx + patLen)+          else do+            let !offset = ixIx * delta+            Pure.unsafeCopy+              dst+              (prevSrcIx + offset - ix0)+              Bytes {array = haystack, offset = prevSrcIx, length = (len0 + ix0) - prevSrcIx}+            PM.unsafeFreezeByteArray dst   applyReplacement 0 ix0  -- | Find locations of non-overlapping instances of @needle@ within @haystack@. findIndices ::-     Bytes -- ^ needle-  -> Bytes -- ^ haystack-  -> PrimArray Int-findIndices needle Bytes{array,offset=off,length=len}+  -- | needle+  Bytes ->+  -- | haystack+  Bytes ->+  PrimArray Int+findIndices needle Bytes {array, offset = off, length = len}   | needleLen == 0 = errorWithoutStackTrace "Data.Bytes.findIndices: needle with length zero"   | len == 0 = mempty-  | otherwise = +  | otherwise =       let !hp = rollingHash needle        in findIndicesKarpRabin (negate off) hp needle array off len-  where+ where   needleLen = Pure.length needle  -- Precondition: Haystack has non-zero length -- Precondition: Pattern has non-zero length--- Uses karp rabin to search. +-- Uses karp rabin to search. -- Easy opportunity to improve implementation. Instead of having karpRabin -- return two slices, we could have it just return a single index. findIndicesKarpRabin ::-     Int -- Output index modifier. Set to negated initial index to make slicing invisible in results.-  -> Word32 -- Hash to search for (must agree with pattern)-  -> Bytes -- Pattern to search for-  -> ByteArray-  -> Int -- initial index-  -> Int -- length-  -> PrimArray Int+  Int -> -- Output index modifier. Set to negated initial index to make slicing invisible in results.+  Word32 -> -- Hash to search for (must agree with pattern)+  Bytes -> -- Pattern to search for+  ByteArray ->+  Int -> -- initial index+  Int -> -- length+  PrimArray Int findIndicesKarpRabin !ixModifier !hp !pat !haystack !ix0 !len0 = runPrimArrayST $ do   let dstLen = 1 + quot len0 (Pure.length pat)   dst <- PM.newPrimArray dstLen-  let go !ix !len !ixIx = case karpRabin hp pat Bytes{array=haystack,offset=ix,length=len} of+  let go !ix !len !ixIx = case karpRabin hp pat Bytes {array = haystack, offset = ix, length = len} of         (-1) -> do           PM.shrinkMutablePrimArray dst ixIx           PM.unsafeFreezePrimArray dst@@ -150,46 +174,50 @@ -- Output: Negative one means match not found. Other negative -- numbers should not occur. Zero may occur. Positive number -- means the number of bytes to skip to make it past the match.-breakSubstring :: Bytes -- ^ String to search for-               -> Bytes -- ^ String to search in-               -> Int+breakSubstring ::+  -- | String to search for+  Bytes ->+  -- | String to search in+  Bytes ->+  Int breakSubstring !pat !haystack@(Bytes _ off0 _) =   case lp of     0 -> 0     1 -> case Byte.elemIndexLoop# (unsafeHead pat) haystack of       (-1#) -> (-1)       off -> 1 + (I# off) - off0-    _ -> if lp * 8 <= finiteBitSize (0 :: Word)-      then shift haystack-      else karpRabin (rollingHash pat) pat haystack-  where-  lp                = length pat+    _ ->+      if lp * 8 <= finiteBitSize (0 :: Word)+        then shift haystack+        else karpRabin (rollingHash pat) pat haystack+ where+  lp = length pat   {-# INLINE shift #-}   shift :: Bytes -> Int   shift !src-      | length src < lp = (-1)-      | otherwise       = search (intoWord $ Pure.unsafeTake lp src) lp-    where+    | length src < lp = (-1)+    | otherwise = search (intoWord $ Pure.unsafeTake lp src) lp+   where     intoWord :: Bytes -> Word     intoWord = Pure.foldl' (\w b -> (w `shiftL` 8) .|. fromIntegral b) 0-    wp   = intoWord pat+    wp = intoWord pat     mask = (1 `shiftL` (8 * lp)) - 1     search :: Word -> Int -> Int     search !w !i-        | w == wp         = i-        | length src <= i = (-1)-        | otherwise       = search w' (i + 1)-      where-      b  = fromIntegral (Pure.unsafeIndex src i)+      | w == wp = i+      | length src <= i = (-1)+      | otherwise = search w' (i + 1)+     where+      b = fromIntegral (Pure.unsafeIndex src i)       w' = mask .&. ((w `shiftL` 8) .|. b)  -- Only used for karp rabin rollingHash :: Bytes -> Word32-{-# inline rollingHash #-}+{-# INLINE rollingHash #-} rollingHash = Pure.foldl' (\h b -> h * hashKey + fromIntegral b) 0 -hashKey :: Word32 -{-# inline hashKey #-}+hashKey :: Word32+{-# INLINE hashKey #-} hashKey = 2891336453  -- Precondition: Length of bytes is greater than or equal to 1.@@ -199,9 +227,9 @@ -- means the number of bytes to skip to make it past the match. karpRabin :: Word32 -> Bytes -> Bytes -> Int karpRabin !hp !pat !src-    | length src < lp = (-1)-    | otherwise = search (rollingHash $ Pure.unsafeTake lp src) lp-  where+  | length src < lp = (-1)+  | otherwise = search (rollingHash $ Pure.unsafeTake lp src) lp+ where   lp :: Int   !lp = Pure.length pat   m :: Word32@@ -209,26 +237,30 @@   get :: Int -> Word32   get !ix = fromIntegral (Pure.unsafeIndex src ix)   search !hs !i-      | hp == hs && eqBytesNoShortCut pat (Pure.unsafeTake lp (Pure.unsafeDrop (i - lp) src)) = i-      | length src <= i                    = (-1)-      | otherwise                          = search hs' (i + 1)-    where-    hs' = hs * hashKey +-          get i --          m * get (i - lp)+    | hp == hs && eqBytesNoShortCut pat (Pure.unsafeTake lp (Pure.unsafeDrop (i - lp) src)) = i+    | length src <= i = (-1)+    | otherwise = search hs' (i + 1)+   where+    hs' =+      hs * hashKey+        + get i+        - m * get (i - lp) --- | Is the first argument an infix of the second argument?--- --- Uses the Rabin-Karp algorithm: expected time @O(n+m)@, worst-case @O(nm)@.-isInfixOf :: Bytes -- ^ String to search for-          -> Bytes -- ^ String to search in-          -> Bool-isInfixOf p s = Pure.null p || breakSubstring p s >= 0+{- | Is the first argument an infix of the second argument? +Uses the Rabin-Karp algorithm: expected time @O(n+m)@, worst-case @O(nm)@.+-}+isInfixOf ::+  -- | String to search for+  Bytes ->+  -- | String to search in+  Bytes ->+  Bool+isInfixOf p s = Pure.null p || breakSubstring p s >= 0  -- Precondition: both arguments have the same length -- Skips the pointer equality check and the length check. eqBytesNoShortCut :: Bytes -> Bytes -> Bool-{-# inline eqBytesNoShortCut #-}+{-# INLINE eqBytesNoShortCut #-} eqBytesNoShortCut (Bytes arr1 off1 len1) (Bytes arr2 off2 _) =   PM.compareByteArrays arr1 off1 arr2 off2 len1 == EQ
src/Data/Bytes/Text/Ascii.hs view
@@ -4,12 +4,13 @@ {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE TypeApplications #-} --- | This module treats 'Bytes' data as holding ASCII text. Providing bytes--- outside the ASCII range (@U+0000@ -- @U+007F@) may cause a failure or--- unspecified results, but such bytes will never be inspected.------ For functions that can operate on ASCII-compatible encodings, see--- 'Data.Bytes.Text.AsciiExt'.+{- | This module treats 'Bytes' data as holding ASCII text. Providing bytes+outside the ASCII range (@U+0000@ -- @U+007F@) may cause a failure or+unspecified results, but such bytes will never be inspected.++For functions that can operate on ASCII-compatible encodings, see+'Data.Bytes.Text.AsciiExt'.+-} module Data.Bytes.Text.Ascii   ( fromString   , decodeDecWord@@ -22,16 +23,16 @@   ) where  import Data.Bits ((.&.))-import Data.ByteString.Short.Internal (ShortByteString(SBS))+import Data.ByteString.Short.Internal (ShortByteString (SBS)) import Data.Bytes.Text.Latin1 (decodeDecWord)-import Data.Bytes.Types (Bytes(Bytes))+import Data.Bytes.Types (Bytes (Bytes)) import Data.Char (ord) import Data.Primitive (ByteArray) import Data.Text (Text) import Data.Text.Short (ShortText) import Data.Word (Word8) import Foreign.C.String (CString)-import Foreign.Ptr (Ptr,plusPtr,castPtr)+import Foreign.Ptr (Ptr, castPtr, plusPtr)  import qualified Data.Bytes.Pure as Bytes import qualified Data.Primitive as PM@@ -41,26 +42,28 @@ import qualified Data.Text.Short.Unsafe as TS import qualified GHC.Exts as Exts --- | Convert a 'String' consisting of only characters in the ASCII block--- to a byte sequence. Any character with a codepoint above @U+007F@ is--- replaced by @U+0000@.+{- | Convert a 'String' consisting of only characters in the ASCII block+to a byte sequence. Any character with a codepoint above @U+007F@ is+replaced by @U+0000@.+-} fromString :: String -> Bytes-fromString = Bytes.fromByteArray-  . Exts.fromList-  . map (\c -> let i = ord c in if i < 128 then fromIntegral @Int @Word8 i else 0)+fromString =+  Bytes.fromByteArray+    . Exts.fromList+    . map (\c -> let i = ord c in if i < 128 then fromIntegral @Int @Word8 i else 0)  -- TODO presumably also fromText and fromShortText  toShortText :: Bytes -> Maybe ShortText-{-# inline toShortText #-}+{-# INLINE toShortText #-} toShortText !b = case Bytes.foldr (\w acc -> w < 128 && acc) True b of   True -> Just (TS.fromShortByteStringUnsafe (Bytes.toShortByteString b))   False -> Nothing  toShortTextU :: ByteArray -> Maybe ShortText-{-# inline toShortTextU #-}+{-# INLINE toShortTextU #-} toShortTextU !b = case Bytes.foldr (\w acc -> w < 128 && acc) True (Bytes.fromByteArray b) of-  True -> Just (TS.fromShortByteStringUnsafe (case b of {PM.ByteArray x -> SBS x}))+  True -> Just (TS.fromShortByteStringUnsafe (case b of PM.ByteArray x -> SBS x))   False -> Nothing  #if MIN_VERSION_text(2,0,0)@@ -75,16 +78,17 @@   False -> Nothing #endif --- | Is the byte sequence equal to the @NUL@-terminated C String?--- The C string must be a constant.+{- | Is the byte sequence equal to the @NUL@-terminated C String?+The C string must be a constant.+-} equalsCStringCaseInsensitive :: CString -> Bytes -> Bool-{-# inline equalsCStringCaseInsensitive #-}-equalsCStringCaseInsensitive !ptr0 (Bytes arr off0 len0) = go (castPtr ptr0 :: Ptr Word8) off0 len0 where+{-# INLINE equalsCStringCaseInsensitive #-}+equalsCStringCaseInsensitive !ptr0 (Bytes arr off0 len0) = go (castPtr ptr0 :: Ptr Word8) off0 len0+ where   go !ptr !off !len = case len of     0 -> PM.indexOffPtr ptr 0 == (0 :: Word8)     _ -> case PM.indexOffPtr ptr 0 of       0 -> False       c ->         (c .&. 0b1101_1111) == (PM.indexByteArray arr off .&. 0b1101_1111)-        &&-        go (plusPtr ptr 1) (off + 1) (len - 1)+          && go (plusPtr ptr 1) (off + 1) (len - 1)
src/Data/Bytes/Text/AsciiExt.hs view
@@ -2,33 +2,36 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} --- | This module contains functions which operate on supersets of 'Bytes' containing ASCII-encoded text.--- That is, none of the functions here inspect bytes with a value greater than 127, and do not fail due to the presence of such bytes.- -- For functions that can fail for bytes outside the ASCII range, see -- 'Data.Bytes.Ascii'. For functions that can inspect bytes outside ASCII, see -- any of the modules for ASCII-compatible encodings (e.g. 'Data.Bytes.Utf8', -- 'Data.Bytes.Latin1', and so on).++{- | This module contains functions which operate on supersets of 'Bytes' containing ASCII-encoded text.+That is, none of the functions here inspect bytes with a value greater than 127, and do not fail due to the presence of such bytes.+-} module Data.Bytes.Text.AsciiExt   ( -- * Line-Oriented IO     hFoldLines   , hForLines_-  -- ** Standard Handles++    -- ** Standard Handles   , forLines_   , foldLines-  -- * Text Manipulation++    -- * Text Manipulation   , toLowerU   ) where  import Control.Monad.ST (ST) import Control.Monad.ST.Run (runByteArrayST)-import Data.Bytes.Types (Bytes(..))+import Data.Bytes.Types (Bytes (..)) import Data.Primitive (ByteArray) import Data.Word (Word8) import System.IO (Handle, hIsEOF, stdin) -import qualified Data.Bytes.Pure as Bytes import qualified Data.ByteString.Char8 as BC8+import qualified Data.Bytes.Pure as Bytes import qualified Data.Primitive as PM  -- | `hForLines_` over `stdin`@@ -41,56 +44,63 @@ {-# INLINEABLE foldLines #-} foldLines = hFoldLines stdin --- | Perform an action on each line of the input, discarding results.--- To maintain a running state, see 'hFoldLines'.------ Lines are extracted with with 'BC8.hGetLine', which does not document its--- detection algorithm. As of writing (bytestring v0.11.1.0), lines are--- delimited by a single @\n@ character (UNIX-style, as all things should be).+{- | Perform an action on each line of the input, discarding results.+To maintain a running state, see 'hFoldLines'.++Lines are extracted with with 'BC8.hGetLine', which does not document its+detection algorithm. As of writing (bytestring v0.11.1.0), lines are+delimited by a single @\n@ character (UNIX-style, as all things should be).+-} hForLines_ :: Handle -> (Bytes -> IO a) -> IO () hForLines_ h body = loop-  where-  loop = hIsEOF h >>= \case-    False -> do-      line <- Bytes.fromByteString <$> BC8.hGetLine h-      _ <- body line-      loop-    True -> pure ()+ where+  loop =+    hIsEOF h >>= \case+      False -> do+        line <- Bytes.fromByteString <$> BC8.hGetLine h+        _ <- body line+        loop+      True -> pure () --- | Perform an action on each line of the input, threading state through the computation.--- If you do not need to keep a state, see `hForLines_`.------ Lines are extracted with with 'BC8.hGetLine', which does not document its--- detection algorithm. As of writing (bytestring v0.11.1.0), lines are--- delimited by a single @\n@ character (UNIX-style, as all things should be).+{- | Perform an action on each line of the input, threading state through the computation.+If you do not need to keep a state, see `hForLines_`.++Lines are extracted with with 'BC8.hGetLine', which does not document its+detection algorithm. As of writing (bytestring v0.11.1.0), lines are+delimited by a single @\n@ character (UNIX-style, as all things should be).+-} hFoldLines :: Handle -> a -> (a -> Bytes -> IO a) -> IO a hFoldLines h z body = loop z-  where-  loop !x = hIsEOF h >>= \case-    False -> do-      line <- Bytes.fromByteString <$> BC8.hGetLine h-      x' <- body x line-      loop x'-    True -> pure x+ where+  loop !x =+    hIsEOF h >>= \case+      False -> do+        line <- Bytes.fromByteString <$> BC8.hGetLine h+        x' <- body x line+        loop x'+      True -> pure x --- | /O(n)/ Convert ASCII letters to lowercase. This adds @0x20@ to bytes in the--- range @[0x41,0x5A]@ (@A-Z@ ⇒ @a-z@) and leaves all other bytes alone.--- Unconditionally copies the bytes.+{- | /O(n)/ Convert ASCII letters to lowercase. This adds @0x20@ to bytes in the+range @[0x41,0x5A]@ (@A-Z@ ⇒ @a-z@) and leaves all other bytes alone.+Unconditionally copies the bytes.+-} toLowerU :: Bytes -> ByteArray toLowerU (Bytes src off0 len0) =   runByteArrayST action-  where+ where   action :: forall s. ST s ByteArray   action = do     dst <- PM.newByteArray len0-    let go !off !ix !len = if len == 0-          then pure ()-          else do-            let w = PM.indexByteArray src off :: Word8-                w' = if w >= 0x41 && w <= 0x5A-                  then w + 32-                  else w-            PM.writeByteArray dst ix w'-            go (off + 1) (ix + 1) (len - 1)+    let go !off !ix !len =+          if len == 0+            then pure ()+            else do+              let w = PM.indexByteArray src off :: Word8+                  w' =+                    if w >= 0x41 && w <= 0x5A+                      then w + 32+                      else w+              PM.writeByteArray dst ix w'+              go (off + 1) (ix + 1) (len - 1)     go off0 0 len0     PM.unsafeFreezeByteArray dst
src/Data/Bytes/Text/Latin1.hs view
@@ -1,28 +1,29 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UnboxedSums #-} {-# LANGUAGE UnboxedTuples #-} --- | This module treats 'Bytes' data as holding text encoded in ISO-8859-1. This--- encoding can only encode codepoints strictly below @U+0100@, but this allows--- each codepoint to be placed directly into a single byte. This range consists--- of Unicode Basic Latin, Latin-1 Supplement and C0+C1 Controls, which includes--- ASCII.------ Strictly, ISO-8859-1 is not to be confused with ISO/IEC 8859-1 (which was the--- default encoding for webpages before HTML5). ISO/IEC 8859-1 lacks encodings--- for the C0 and C1 control characters. --- --- With HTML5, the default encoding of webpages was changed to Windows-1252,--- which is _not_ compatible with ISO-8859-1. Windows-1252 uses the C1 Control--- range (@U+0080@ -- @U+009F@) mostly to encode a variety of printable--- characters. For this encoding, see 'Data.Bytes.Text.Windows1252'.+{- | This module treats 'Bytes' data as holding text encoded in ISO-8859-1. This+encoding can only encode codepoints strictly below @U+0100@, but this allows+each codepoint to be placed directly into a single byte. This range consists+of Unicode Basic Latin, Latin-1 Supplement and C0+C1 Controls, which includes+ASCII.++Strictly, ISO-8859-1 is not to be confused with ISO/IEC 8859-1 (which was the+default encoding for webpages before HTML5). ISO/IEC 8859-1 lacks encodings+for the C0 and C1 control characters.++With HTML5, the default encoding of webpages was changed to Windows-1252,+which is _not_ compatible with ISO-8859-1. Windows-1252 uses the C1 Control+range (@U+0080@ -- @U+009F@) mostly to encode a variety of printable+characters. For this encoding, see 'Data.Bytes.Text.Windows1252'.+-} module Data.Bytes.Text.Latin1   ( toString   , fromString   , decodeDecWord-  -- * Specialized Comparisons++    -- * Specialized Comparisons   , equals1   , equals2   , equals3@@ -42,21 +43,20 @@  import Prelude hiding (length) -import Data.Bytes.Types (Bytes(..))-import Data.Char (ord,chr)-import Data.Primitive (ByteArray(ByteArray))+import Data.Bytes.Types (Bytes (..))+import Data.Char (chr, ord)+import Data.Primitive (ByteArray (ByteArray)) import Data.Word (Word8)-import GHC.Exts (Int(I#),Char(C#),or#,ltWord#,int2Word#)-import GHC.Exts (Word(W#),Word#)+import GHC.Exts (Char (C#), Int (I#), Word (W#), Word#, int2Word#, ltWord#, or#) -import qualified Data.Primitive as PM import qualified Data.Bytes.Pure as Bytes+import qualified Data.Primitive as PM import qualified GHC.Exts as Exts ---- | Convert a 'String' consisting of only characters representable--- by ISO-8859-1. These are encoded with ISO-8859-1. Any character--- with a codepoint above @U+00FF@ is replaced by an unspecified byte.+{- | Convert a 'String' consisting of only characters representable+by ISO-8859-1. These are encoded with ISO-8859-1. Any character+with a codepoint above @U+00FF@ is replaced by an unspecified byte.+-} fromString :: String -> Bytes fromString =   Bytes.fromByteArray . Exts.fromList . map (fromIntegral @Int @Word8 . ord)@@ -68,263 +68,296 @@  -- TODO presumably also fromText and fromShortText ---- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a singleton whose element matches the character?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a singleton whose element matches the character?+-} equals1 :: Char -> Bytes -> Bool {-# INLINE equals1 #-} equals1 !c0 (Bytes arr off len) = case len of   1 -> c0 == indexCharArray arr off   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a doubleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a doubleton whose elements match the characters?+-} equals2 :: Char -> Char -> Bytes -> Bool equals2 !c0 !c1 (Bytes arr off len) = case len of-  2 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1)+  2 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a tripleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a tripleton whose elements match the characters?+-} equals3 :: Char -> Char -> Char -> Bytes -> Bool equals3 !c0 !c1 !c2 (Bytes arr off len) = case len of-  3 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2)+  3 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a quadrupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a quadrupleton whose elements match the characters?+-} equals4 :: Char -> Char -> Char -> Char -> Bytes -> Bool equals4 !c0 !c1 !c2 !c3 (Bytes arr off len) = case len of-  4 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2) &&-       c3 == indexCharArray arr (off + 3)+  4 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a quintupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a quintupleton whose elements match the characters?+-} equals5 :: Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals5 !c0 !c1 !c2 !c3 !c4 (Bytes arr off len) = case len of-  5 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2) &&-       c3 == indexCharArray arr (off + 3) &&-       c4 == indexCharArray arr (off + 4)+  5 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a sextupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a sextupleton whose elements match the characters?+-} equals6 :: Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals6 !c0 !c1 !c2 !c3 !c4 !c5 (Bytes arr off len) = case len of-  6 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2) &&-       c3 == indexCharArray arr (off + 3) &&-       c4 == indexCharArray arr (off + 4) &&-       c5 == indexCharArray arr (off + 5)+  6 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a septupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a septupleton whose elements match the characters?+-} equals7 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals7 !c0 !c1 !c2 !c3 !c4 !c5 !c6 (Bytes arr off len) = case len of-  7 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2) &&-       c3 == indexCharArray arr (off + 3) &&-       c4 == indexCharArray arr (off + 4) &&-       c5 == indexCharArray arr (off + 5) &&-       c6 == indexCharArray arr (off + 6)+  7 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- an octupleton whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+an octupleton whose elements match the characters?+-} equals8 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals8 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 (Bytes arr off len) = case len of-  8 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2) &&-       c3 == indexCharArray arr (off + 3) &&-       c4 == indexCharArray arr (off + 4) &&-       c5 == indexCharArray arr (off + 5) &&-       c6 == indexCharArray arr (off + 6) &&-       c7 == indexCharArray arr (off + 7)+  8 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 9-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 9-tuple whose elements match the characters?+-} equals9 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals9 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 (Bytes arr off len) = case len of-  9 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1) &&-       c2 == indexCharArray arr (off + 2) &&-       c3 == indexCharArray arr (off + 3) &&-       c4 == indexCharArray arr (off + 4) &&-       c5 == indexCharArray arr (off + 5) &&-       c6 == indexCharArray arr (off + 6) &&-       c7 == indexCharArray arr (off + 7) &&-       c8 == indexCharArray arr (off + 8)+  9 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 10-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 10-tuple whose elements match the characters?+-} equals10 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals10 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 (Bytes arr off len) = case len of-  10 -> c0 == indexCharArray arr off &&-        c1 == indexCharArray arr (off + 1) &&-        c2 == indexCharArray arr (off + 2) &&-        c3 == indexCharArray arr (off + 3) &&-        c4 == indexCharArray arr (off + 4) &&-        c5 == indexCharArray arr (off + 5) &&-        c6 == indexCharArray arr (off + 6) &&-        c7 == indexCharArray arr (off + 7) &&-        c8 == indexCharArray arr (off + 8) &&-        c9 == indexCharArray arr (off + 9)+  10 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)+      && c9 == indexCharArray arr (off + 9)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 11-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 11-tuple whose elements match the characters?+-} equals11 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals11 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 (Bytes arr off len) = case len of-  11 -> c0 == indexCharArray arr off &&-        c1 == indexCharArray arr (off + 1) &&-        c2 == indexCharArray arr (off + 2) &&-        c3 == indexCharArray arr (off + 3) &&-        c4 == indexCharArray arr (off + 4) &&-        c5 == indexCharArray arr (off + 5) &&-        c6 == indexCharArray arr (off + 6) &&-        c7 == indexCharArray arr (off + 7) &&-        c8 == indexCharArray arr (off + 8) &&-        c9 == indexCharArray arr (off + 9) &&-        c10 == indexCharArray arr (off + 10)+  11 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)+      && c9 == indexCharArray arr (off + 9)+      && c10 == indexCharArray arr (off + 10)   _ -> False --- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,--- a 12-tuple whose elements match the characters?+{- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text,+a 12-tuple whose elements match the characters?+-} equals12 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals12 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 (Bytes arr off len) = case len of-  12 -> c0 == indexCharArray arr off &&-        c1 == indexCharArray arr (off + 1) &&-        c2 == indexCharArray arr (off + 2) &&-        c3 == indexCharArray arr (off + 3) &&-        c4 == indexCharArray arr (off + 4) &&-        c5 == indexCharArray arr (off + 5) &&-        c6 == indexCharArray arr (off + 6) &&-        c7 == indexCharArray arr (off + 7) &&-        c8 == indexCharArray arr (off + 8) &&-        c9 == indexCharArray arr (off + 9) &&-        c10 == indexCharArray arr (off + 10) &&-        c11 == indexCharArray arr (off + 11)+  12 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)+      && c9 == indexCharArray arr (off + 9)+      && c10 == indexCharArray arr (off + 10)+      && c11 == indexCharArray arr (off + 11)   _ -> False  equals13 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals13 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 !c12 (Bytes arr off len) = case len of-  13 -> c0 == indexCharArray arr off &&-        c1 == indexCharArray arr (off + 1) &&-        c2 == indexCharArray arr (off + 2) &&-        c3 == indexCharArray arr (off + 3) &&-        c4 == indexCharArray arr (off + 4) &&-        c5 == indexCharArray arr (off + 5) &&-        c6 == indexCharArray arr (off + 6) &&-        c7 == indexCharArray arr (off + 7) &&-        c8 == indexCharArray arr (off + 8) &&-        c9 == indexCharArray arr (off + 9) &&-        c10 == indexCharArray arr (off + 10) &&-        c11 == indexCharArray arr (off + 11) &&-        c12 == indexCharArray arr (off + 12)+  13 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)+      && c9 == indexCharArray arr (off + 9)+      && c10 == indexCharArray arr (off + 10)+      && c11 == indexCharArray arr (off + 11)+      && c12 == indexCharArray arr (off + 12)   _ -> False  equals14 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals14 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 !c12 !c13 (Bytes arr off len) = case len of-  14 -> c0 == indexCharArray arr off &&-        c1 == indexCharArray arr (off + 1) &&-        c2 == indexCharArray arr (off + 2) &&-        c3 == indexCharArray arr (off + 3) &&-        c4 == indexCharArray arr (off + 4) &&-        c5 == indexCharArray arr (off + 5) &&-        c6 == indexCharArray arr (off + 6) &&-        c7 == indexCharArray arr (off + 7) &&-        c8 == indexCharArray arr (off + 8) &&-        c9 == indexCharArray arr (off + 9) &&-        c10 == indexCharArray arr (off + 10) &&-        c11 == indexCharArray arr (off + 11) &&-        c12 == indexCharArray arr (off + 12) &&-        c13 == indexCharArray arr (off + 13)+  14 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)+      && c9 == indexCharArray arr (off + 9)+      && c10 == indexCharArray arr (off + 10)+      && c11 == indexCharArray arr (off + 11)+      && c12 == indexCharArray arr (off + 12)+      && c13 == indexCharArray arr (off + 13)   _ -> False  equals15 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool equals15 !c0 !c1 !c2 !c3 !c4 !c5 !c6 !c7 !c8 !c9 !c10 !c11 !c12 !c13 !c14 (Bytes arr off len) = case len of-  15 -> c0 == indexCharArray arr off &&-        c1 == indexCharArray arr (off + 1) &&-        c2 == indexCharArray arr (off + 2) &&-        c3 == indexCharArray arr (off + 3) &&-        c4 == indexCharArray arr (off + 4) &&-        c5 == indexCharArray arr (off + 5) &&-        c6 == indexCharArray arr (off + 6) &&-        c7 == indexCharArray arr (off + 7) &&-        c8 == indexCharArray arr (off + 8) &&-        c9 == indexCharArray arr (off + 9) &&-        c10 == indexCharArray arr (off + 10) &&-        c11 == indexCharArray arr (off + 11) &&-        c12 == indexCharArray arr (off + 12) &&-        c13 == indexCharArray arr (off + 13) &&-        c14 == indexCharArray arr (off + 14)+  15 ->+    c0 == indexCharArray arr off+      && c1 == indexCharArray arr (off + 1)+      && c2 == indexCharArray arr (off + 2)+      && c3 == indexCharArray arr (off + 3)+      && c4 == indexCharArray arr (off + 4)+      && c5 == indexCharArray arr (off + 5)+      && c6 == indexCharArray arr (off + 6)+      && c7 == indexCharArray arr (off + 7)+      && c8 == indexCharArray arr (off + 8)+      && c9 == indexCharArray arr (off + 9)+      && c10 == indexCharArray arr (off + 10)+      && c11 == indexCharArray arr (off + 11)+      && c12 == indexCharArray arr (off + 12)+      && c13 == indexCharArray arr (off + 13)+      && c14 == indexCharArray arr (off + 14)   _ -> False  indexCharArray :: ByteArray -> Int -> Char indexCharArray (ByteArray arr) (I# off) = C# (Exts.indexCharArray# arr off) --- | Decode machine-sized word from decimal representation. Returns--- Nothing on overflow. Allows any number of leading zeros. Trailing--- non-digit bytes cause Nothing to be returned.+{- | Decode machine-sized word from decimal representation. Returns+Nothing on overflow. Allows any number of leading zeros. Trailing+non-digit bytes cause Nothing to be returned.+-} decodeDecWord :: Bytes -> Maybe Word-{-# inline decodeDecWord #-}+{-# INLINE decodeDecWord #-} decodeDecWord !b = case decWordStart b of   (# (# #) | #) -> Nothing   (# | w #) -> Just (W# w)  decWordStart ::-     Bytes -- Chunk-  -> (# (# #) | Word# #)-{-# noinline decWordStart #-}-decWordStart !chunk0 = if length chunk0 > 0-  then-    let !w = fromIntegral @Word8 @Word-          (PM.indexByteArray (array chunk0) (offset chunk0)) - 48-     in if w < 10-          then decWordMore w (Bytes.unsafeDrop 1 chunk0)-          else (# (# #) | #)-  else (# (# #) | #)-  where-  decWordMore ::-       Word -- Accumulator-    -> Bytes -- Chunk-    -> (# (# #) | Word# #)-  decWordMore !acc !chunk = let len = length chunk in case len of-    0 -> (# | unW (fromIntegral acc) #)-    _ ->-      let !w = fromIntegral @Word8 @Word-            (PM.indexByteArray (array chunk) (offset chunk)) - 48+  Bytes -> -- Chunk+  (# (# #) | Word# #)+{-# NOINLINE decWordStart #-}+decWordStart !chunk0 =+  if length chunk0 > 0+    then+      let !w =+            fromIntegral @Word8 @Word+              (PM.indexByteArray (array chunk0) (offset chunk0))+              - 48        in if w < 10-            then-              let (overflow,acc') = unsignedPushBase10 acc w-               in if overflow-                 then (# (# #) | #)-                 else decWordMore acc' (Bytes.unsafeDrop 1 chunk)+            then decWordMore w (Bytes.unsafeDrop 1 chunk0)             else (# (# #) | #)+    else (# (# #) | #)+ where+  decWordMore ::+    Word -> -- Accumulator+    Bytes -> -- Chunk+    (# (# #) | Word# #)+  decWordMore !acc !chunk =+    let len = length chunk+     in case len of+          0 -> (# | unW (fromIntegral acc) #)+          _ ->+            let !w =+                  fromIntegral @Word8 @Word+                    (PM.indexByteArray (array chunk) (offset chunk))+                    - 48+             in if w < 10+                  then+                    let (overflow, acc') = unsignedPushBase10 acc w+                     in if overflow+                          then (# (# #) | #)+                          else decWordMore acc' (Bytes.unsafeDrop 1 chunk)+                  else (# (# #) | #) -unsignedPushBase10 :: Word -> Word -> (Bool,Word)-{-# inline unsignedPushBase10 #-}-unsignedPushBase10 (W# a) (W# b) = +unsignedPushBase10 :: Word -> Word -> (Bool, Word)+{-# INLINE unsignedPushBase10 #-}+unsignedPushBase10 (W# a) (W# b) =   let !(# ca, r0 #) = Exts.timesWord2# a 10##       !r1 = Exts.plusWord# r0 b       !cb = int2Word# (ltWord# r1 r0)       !c = ca `or#` cb-   in (case c of { 0## -> False; _ -> True }, W# r1)+   in (case c of 0## -> False; _ -> True, W# r1)  unW :: Word -> Word#-{-# inline unW #-}+{-# INLINE unW #-} unW (W# w) = w
src/Data/Bytes/Text/Utf8.hs view
@@ -1,5 +1,5 @@-{-# language CPP #-}-{-# language BangPatterns #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}  -- | Convert 'Bytes' to and from 'Text' and 'ShortText'. module Data.Bytes.Text.Utf8@@ -9,34 +9,36 @@   , fromText #endif #if MIN_VERSION_text(2,1,0)-  , toText+  -- , toText #endif   ) where -import Data.Bytes.Types (Bytes(Bytes))-import Data.Text.Short (ShortText)+import Data.Bytes.Types (Bytes (Bytes))+import Data.Primitive (ByteArray (ByteArray)) import Data.Text (Text)-import Data.Primitive (ByteArray(ByteArray))+import Data.Text.Short (ShortText) -import qualified Data.Text.Short as TS import qualified Data.Bytes as Bytes-import qualified Data.Text.Internal as I import qualified Data.Text.Array as A+import qualified Data.Text.Internal as I+import qualified Data.Text.Short as TS  #if MIN_VERSION_text(2,1,0) import qualified Data.Text.Internal.Validate #endif --- | Encode 'ShortText' using UTF-8. Since 'ShortText' is backed by a UTF-8--- byte sequence, this does not perform a copy.+{- | Encode 'ShortText' using UTF-8. Since 'ShortText' is backed by a UTF-8+byte sequence, this does not perform a copy.+-} fromShortText :: ShortText -> Bytes-{-# inline fromShortText #-}+{-# INLINE fromShortText #-} fromShortText = Bytes.fromShortByteString . TS.toShortByteString --- | Attempt to interpret the byte sequence as UTF-8 encoded text. Returns--- 'Nothing' if the bytes are not UTF-8 encoded text.+{- | Attempt to interpret the byte sequence as UTF-8 encoded text. Returns+'Nothing' if the bytes are not UTF-8 encoded text.+-} toShortText :: Bytes -> Maybe ShortText-{-# inline toShortText #-}+{-# INLINE toShortText #-} toShortText !b = TS.fromShortByteString (Bytes.toShortByteString b)  #if MIN_VERSION_text(2,0,0)
src/Data/Bytes/Text/Windows1252.hs view
@@ -1,3 +1,4 @@--- | Placeholder module in case there is demand for treating 'Bytes' as--- Windows-1252-encoded text+{- | Placeholder module in case there is demand for treating 'Bytes' as+Windows-1252-encoded text+-} module Data.Bytes.Text.Windows1252 () where
src/Data/Bytes/Types.hs view
@@ -1,50 +1,51 @@-{-# language BangPatterns #-}-{-# language DataKinds #-}-{-# language PolyKinds #-}-{-# language MagicHash #-}-{-# language TypeFamilies #-}-{-# language DuplicateRecordFields #-}-{-# language ScopedTypeVariables #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}  module Data.Bytes.Types-  ( Bytes(..)-  , Bytes#(..)-  , MutableBytes(..)-  , UnmanagedBytes(..)-  , BytesN(..)-  , ByteArrayN(..)+  ( Bytes (..)+  , Bytes# (..)+  , MutableBytes (..)+  , UnmanagedBytes (..)+  , BytesN (..)+  , ByteArrayN (..)   ) where -import Data.Bytes.Internal (Bytes(..))-import Data.Primitive (ByteArray(..),MutableByteArray(..))-import Data.Primitive.Addr (Addr)-import GHC.TypeNats (Nat,KnownNat,natVal)-import Reps (Bytes#(..))+import Data.Bytes.Internal (Bytes (..)) import Data.Bytes.Internal.Show (showsSlice)-import Data.Proxy (Proxy(Proxy))+import Data.Primitive (ByteArray (..), MutableByteArray (..))+import Data.Primitive.Addr (Addr)+import Data.Proxy (Proxy (Proxy)) import GHC.Natural (naturalToInteger)+import GHC.TypeNats (KnownNat, Nat, natVal)+import Reps (Bytes# (..)) --- | A slice of a 'ByteArray' whose compile-time-known length is represented--- by a phantom type variable. Consumers of this data constructor must be--- careful to preserve the expected invariant.+{- | A slice of a 'ByteArray' whose compile-time-known length is represented+by a phantom type variable. Consumers of this data constructor must be+careful to preserve the expected invariant.+-} data BytesN (n :: Nat) = BytesN   { array :: {-# UNPACK #-} !ByteArray   , offset :: {-# UNPACK #-} !Int   } -instance KnownNat n => Show (BytesN n) where+instance (KnownNat n) => Show (BytesN n) where   showsPrec _ (BytesN arr off) s =     let len = fromInteger (naturalToInteger (natVal (Proxy :: Proxy n)))      in showsSlice arr off len s --- | A 'ByteArray' whose compile-time-known length is represented--- by a phantom type variable. Consumers of this data constructor must be--- careful to preserve the expected invariant.+{- | A 'ByteArray' whose compile-time-known length is represented+by a phantom type variable. Consumers of this data constructor must be+careful to preserve the expected invariant.+-} newtype ByteArrayN (n :: Nat) = ByteArrayN   { array :: ByteArray   } -instance KnownNat n => Show (ByteArrayN n) where+instance (KnownNat n) => Show (ByteArrayN n) where   showsPrec _ (ByteArrayN arr) s =     let len = fromInteger (naturalToInteger (natVal (Proxy :: Proxy n)))      in showsSlice arr 0 len s
test/Main.hs view
@@ -1,37 +1,40 @@-{-# language BangPatterns #-}-{-# language MultiWayIf #-}-{-# language NumDecimals #-}-{-# language OverloadedStrings #-}-{-# language ScopedTypeVariables #-}-{-# language TypeApplications #-}-+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -import Data.Bytes.Chunks (Chunks(ChunksNil,ChunksCons))-import Data.Bytes.Types (Bytes(Bytes))+import Control.Monad.Trans.Writer (Writer, tell)+import Data.Bytes.Chunks (Chunks (ChunksCons, ChunksNil))+import Data.Bytes.Types (Bytes (Bytes)) import Data.Char (ord) import Data.Primitive (ByteArray)-import Data.Proxy (Proxy(..))-import Data.Word (Word8)-import Test.Tasty (defaultMain,testGroup,TestTree)-import Test.Tasty.HUnit ((@=?),testCase)-import Test.Tasty.QuickCheck ((===),testProperty,property,Discard(Discard))-import Test.Tasty.QuickCheck ((==>),Arbitrary)-import Test.Tasty.QuickCheck (ASCIIString(ASCIIString))-import Control.Monad.Trans.Writer (Writer,tell)+import Data.Proxy (Proxy (..)) import Data.Text (Text)+import Data.Word (Word8)+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.HUnit (testCase, (@=?))+import Test.Tasty.QuickCheck+  ( ASCIIString (ASCIIString)+  , Arbitrary+  , Discard (Discard)+  , property+  , testProperty+  , (===)+  , (==>)+  ) -import qualified Data.Text as Text-import qualified Data.Text.Encoding as Text.Encoding import qualified Data.ByteString as ByteString import qualified Data.Bytes as Bytes+import qualified Data.Bytes.Chunks as Chunks import qualified Data.Bytes.Text.Ascii as Ascii-import qualified Data.Bytes.Text.Latin1 as Latin1 import qualified Data.Bytes.Text.AsciiExt as AsciiExt-import qualified Data.Bytes.Chunks as Chunks+import qualified Data.Bytes.Text.Latin1 as Latin1 import qualified Data.Foldable as Foldable import qualified Data.List as List import qualified Data.Primitive as PM+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text.Encoding import qualified GHC.Exts as Exts import qualified Test.QuickCheck.Classes as QCC import qualified Test.Tasty.HUnit as THU@@ -41,303 +44,296 @@ main = defaultMain tests  tests :: TestTree-tests = testGroup "Bytes"-  [ lawsToTest (QCC.eqLaws (Proxy :: Proxy Bytes))-  , lawsToTest (QCC.ordLaws (Proxy :: Proxy Bytes))-  , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy Bytes))-  , lawsToTest (QCC.monoidLaws (Proxy :: Proxy Bytes))-  , testGroup "toLowerAsciiByteArrayClone"-    [ testCase "A" $ THU.assertBool "" $-        Bytes.isPrefixOf (bytes "hey") (bytes "hey man")-    ]-  , testGroup "isPrefixOf"-    [ testCase "A" $-        AsciiExt.toLowerU (bytes "FooBar")-        @=?-        pack "foobar"-    , testCase "B" $ THU.assertBool "" $-        not (Bytes.isPrefixOf (bytes "an") (bytes "hey man"))-    ]-  , testGroup "isSuffixOf"-    [ testCase "A" $ THU.assertBool "" $-        Bytes.isSuffixOf (bytes "an") (bytes "hey man")-    , testCase "B" $ THU.assertBool "" $-        not (Bytes.isSuffixOf (bytes "h") (bytes "hey man"))-    ]-  , testGroup "isInfixOf"-    [ testCase "small pattern A" $ THU.assertBool "" $-        Bytes.isInfixOf (bytes "y m") (bytes "hey man")-    , testCase "small pattern B" $ THU.assertBool "" $-        Bytes.isInfixOf (bytes "h") (bytes "hey man")-    , testCase "small pattern C" $ THU.assertBool "" $-        Bytes.isInfixOf (bytes "an") (bytes "hey man")-    , testCase "small pattern D" $ THU.assertBool "" $-        not (Bytes.isInfixOf (bytes "Y M") (bytes "hey man"))-    , testCase "large pattern A" $ THU.assertBool "" $-        Bytes.isInfixOf (bytes "Hello, hello!") (bytes "I say hello. Hello, hello! I don't know why you say goodbye, I say hello!")-    , testCase "large pattern D" $ THU.assertBool "" $-        not (Bytes.isInfixOf (bytes "Hello, hello!") (bytes "I don't know why you say goodbye, I say hello!"))-    , testCase "edge: empty pattern" $ THU.assertBool "" $-        Bytes.isInfixOf (bytes "") (bytes "hello hello!")-    , testCase "edge: empty string" $ THU.assertBool "" $-        not (Bytes.isInfixOf (bytes "hello hello!") (bytes ""))-    ]-  , testGroup "findIndices"-    [ testCase "A" $-        Exts.fromList [4]-        @=?-        Bytes.findIndices (Bytes.fromByteArray (pack "greatest showman")) (Bytes.fromByteArray (pack "the greatest showman of all"))-    , testCase "B" $-        Exts.fromList [4]-        @=?-        Bytes.findIndices (bytes "greatest showman") (bytes "the greatest showman of all")-    , testCase "C" $-        Exts.fromList [0,1,2,3,4,5]-        @=?-        Bytes.findIndices (bytes "a") (bytes "aaaaaa")-    , testCase "D" $-        Exts.fromList [0,2,4]-        @=?-        Bytes.findIndices (bytes "aa") (bytes "aaaaaaa")-    ]-  , testGroup "replace-spec"-    [ testCase "A" $-        Bytes.empty-        @=?-        Bytes.replace (Bytes.fromByteArray (pack "hello")) (Bytes.fromByteArray (pack "world")) Bytes.empty-    , testCase "B" $-        bytes "xzybbcbbc"-        @=?-        Bytes.replace (bytes "a") (bytes "b") (bytes "xzyabcabc")-    , testCase "C" $-        bytes "my favorite month is March!"-        @=?-        Bytes.replace (bytes "November") (bytes "March") (bytes "my favorite month is November!")-    , testCase "D" $-        bytes "Saturn, Saturn, Mars, Saturn"-        @=?-        Bytes.replace (bytes "Jupiter") (bytes "Saturn") (bytes "Jupiter, Jupiter, Mars, Jupiter")-    ]-  , testGroup "stripOptionalSuffix"-    [ testCase "A" $-        Ascii.fromString "hey m"-        @=?-        Bytes.stripOptionalSuffix (bytes "an") (bytes "hey man")-    , testCase "B" $-        Ascii.fromString "hey man"-        @=?-        Bytes.stripOptionalSuffix (bytes "h") (bytes "hey man")-    ]-  , testGroup "longestCommonPrefix"-    [ testProperty "finds prefix" $ \(pre :: Bytes) (a :: Bytes) (b :: Bytes) ->-        if | Just (wa,_) <- Bytes.uncons a, Just (wb,_) <- Bytes.uncons b, wa /= wb ->-               Bytes.longestCommonPrefix (pre <> a) (pre <> b) === pre-           | otherwise -> property Discard-    , testProperty "finds no prefix" $ \(a :: Bytes) (b :: Bytes) ->-        if | Just (wa,_) <- Bytes.uncons a, Just (wb,_) <- Bytes.uncons b, wa /= wb ->-               Bytes.longestCommonPrefix a b === mempty-           | otherwise -> property Discard-    ]-  , testGroup "dropWhileEnd"-    [ testCase "A" $-        Ascii.fromString "aabbcc"-        @=?-        Bytes.dropWhileEnd (== c2w 'b') (bytes "aabbccbb")-    ]-  , testGroup "takeWhileEnd"-    [ testCase "A" $-        Ascii.fromString "bb"-        @=?-        Bytes.takeWhileEnd (== c2w 'b') (bytes "aabbccbb")-    , testCase "B" $-        Ascii.fromString ""-        @=?-        Bytes.takeWhileEnd (/= c2w '\n') (bytes "aabbccbb\n")-    , testCase "C" $-        slicedPack [0x1,0x2,0x3]-        @=?-        Bytes.takeWhileEnd (/= 0x0) (slicedPack [0x1,0x0,0x1,0x2,0x3])-    ]-  , testProperty "decodeDecWord" $ \(w :: Word) ->-      Just w-      ===-      Latin1.decodeDecWord (Bytes.unsafeDrop 1 (Exts.fromList (0xFF : (Exts.toList (Latin1.fromString (show w))))))-  , testProperty "elem" $ \(x :: Word8) (xs :: [Word8]) ->-      List.elem x xs-      ===-      Bytes.elem x (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "foldl" $ \(x :: Word8) (xs :: [Word8]) ->-      List.foldl (-) 0 xs-      ===-      Bytes.foldl (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "foldlM" $ \(x :: Word8) (xs :: [Word8]) ->-      let f acc y = (tell [x] >> pure (acc - y)) :: Writer [Word8] Word8 in-      Foldable.foldlM f 0 xs-      ===-      Bytes.foldlM f 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "foldrM" $ \(x :: Word8) (xs :: [Word8]) ->-      let f acc y = (tell [x] >> pure (acc - y)) :: Writer [Word8] Word8 in-      Foldable.foldrM f 0 xs-      ===-      Bytes.foldrM f 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "foldl'" $ \(x :: Word8) (xs :: [Word8]) ->-      List.foldl' (-) 0 xs-      ===-      Bytes.foldl' (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "foldr" $ \(x :: Word8) (xs :: [Word8]) ->-      Foldable.foldr (-) 0 xs-      ===-      Bytes.foldr (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "foldr'" $ \(x :: Word8) (xs :: [Word8]) ->-      Foldable.foldr' (-) 0 xs-      ===-      Bytes.foldr' (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))-  , testProperty "count" $ \(x :: Word8) (xs :: [Word8]) ->-      ByteString.count x (ByteString.pack xs)-      ===-      Bytes.count x (slicedPack xs)-  , testProperty "split" $ \(x :: Word8) (xs :: [Word8]) ->-      not (List.null xs)-      ==>-      ByteString.split x (ByteString.pack xs)-      ===-      map bytesToByteString (Bytes.split x (slicedPack xs))-  , testProperty "intercalate" $ \(x :: Bytes) (xs :: [Bytes]) ->-      mconcat (List.intersperse x xs)-      ===-      Bytes.intercalate x xs-  , testProperty "concatArray" $ \(xs :: [Bytes]) ->-      mconcat xs-      ===-      Bytes.concatArray (Exts.fromList xs)-  , testProperty "splitNonEmpty" $ \(x :: Word8) (xs :: [Word8]) ->-      Bytes.split x (slicedPack xs)-      ===-      Foldable.toList (Bytes.splitNonEmpty x (slicedPack xs))-  , testProperty "splitInit" $ \(x :: Word8) (xs :: [Word8]) -> case xs of-      [] -> Bytes.splitInit x (slicedPack xs) === []-      _ -> -        List.init (ByteString.split x (ByteString.pack xs))-        ===-        map bytesToByteString (Bytes.splitInit x (slicedPack xs))-  , testProperty "splitU" $ \(x :: Word8) (xs :: [Word8]) ->-      not (List.null xs)-      ==>-      Bytes.split x (slicedPack xs)-      ===-      map Bytes.fromByteArray (Exts.toList (Bytes.splitU x (slicedPack xs)))-  , testCase "splitInit-A" $-      [Ascii.fromString "hello", Ascii.fromString "world"]-      @=?-      (Bytes.splitInit 0x0A (Ascii.fromString "hello\nworld\n"))-  , testCase "splitInit-B" $-      [Ascii.fromString "hello", Ascii.fromString "world"]-      @=?-      (Bytes.splitInit 0x0A (Ascii.fromString "hello\nworld\nthere"))-  , testProperty "replace" $ \(ws :: ASCIIString) (xs :: ASCIIString) (ys :: ASCIIString) (zs :: ASCIIString) ->-      let ws' = asciiStringToBytes ws-          xs' = asciiStringToBytes xs-          ys' = asciiStringToBytes ys-          zs' = asciiStringToBytes zs-          ws'' = asciiStringToText ws-          xs'' = asciiStringToText xs-          ys'' = asciiStringToText ys-          zs'' = asciiStringToText zs-       in case ws of-            ASCIIString [] -> property Discard-            _ ->-              Bytes.replace ws' xs' (ys' <> ws' <> zs')-              ===-              Bytes.fromByteString (Text.Encoding.encodeUtf8 (Text.replace ws'' xs'' (ys'' <> ws'' <> zs'')))-  , testProperty "splitEnd1" $ \(x :: Word8) (xs :: [Word8]) ->-      case ByteString.split x (ByteString.pack xs) of-        [] -> Bytes.splitEnd1 x (slicedPack xs) === Nothing-        [_] -> Bytes.splitEnd1 x (slicedPack xs) === Nothing-        [y1,z1] -> case Bytes.splitEnd1 x (slicedPack xs) of-          Nothing -> property False-          Just (y2,z2) -> (y1,z1) === (bytesToByteString y2, bytesToByteString z2)-        _ -> property Discard-  , testProperty "split1" $ \(x :: Word8) (xs :: [Word8]) ->-      case ByteString.split x (ByteString.pack xs) of-        [] -> Bytes.split1 x (slicedPack xs) === Nothing-        [_] -> Bytes.split1 x (slicedPack xs) === Nothing-        [y1,z1] -> case Bytes.split1 x (slicedPack xs) of-          Nothing -> property False-          Just (y2,z2) -> (y1,z1) === (bytesToByteString y2, bytesToByteString z2)-        _ -> property Discard-  , testProperty "splitTetragram1A" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (w3 :: Word8) (xs :: [Word8]) ->-      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF && w3 /= 0xEF)-      ==>-      case Bytes.splitTetragram1 w0 w1 w2 w3 (slicedPack (0xEF : w0 : w1 : w2 : w3 : xs)) of-        Nothing -> property False-        Just (pre,post) ->-          (pre,post) === (Exts.fromList [0xEF], Exts.fromList xs) -  , testProperty "splitTetragram1B" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (w3 :: Word8) (xs :: [Word8]) ->-      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF && w3 /= 0xEF)-      ==>-      case Bytes.splitTetragram1 w0 w1 w2 w3 (slicedPack (0xEF : 0xEF : 0xEF : 0xEF : w0 : w1 : w2 : w3 : xs)) of-        Nothing -> property False-        Just (pre,post) ->-          (pre,post) === (Exts.fromList (List.replicate 4 0xEF), Exts.fromList xs) -  , testProperty "splitTetragram1C" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (xs :: [Word8]) ->-      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF)-      ==>-      case Bytes.splitTetragram1 w0 w1 w2 0xEF (slicedPack (xs ++ [w0, w1, w2, 0xEF])) of-        Nothing -> property False-        Just (pre,post) ->-          (pre,post) === (Exts.fromList xs, mempty)-  , testProperty "splitTetragram1D" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (xs :: [Word8]) ->-      (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF)-      ==>-      case Bytes.splitTetragram1 w0 w1 w2 0xEF (slicedPack (xs ++ [w0, w1, w2, 0xEF, 0xEF])) of-        Nothing -> property False-        Just (pre,post) ->-          (pre,post) === (Exts.fromList xs, Exts.fromList [0xEF])-  , testProperty "split2" $ \(xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8]) ->-      (all (/=0xEF) xs && all (/=0xEF) ys && all (/=0xEF) zs)-      ==>-      case Bytes.split2 0xEF (slicedPack (xs ++ [0xEF] ++ ys ++ [0xEF] ++ zs)) of-        Just r -> r === (slicedPack xs, slicedPack ys, slicedPack zs)-        Nothing -> property False-  , testProperty "split3" $ \(ws :: [Word8]) (xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8])->-      (all (/=0xEF) ws && all (/=0xEF) xs && all (/=0xEF) ys && all (/=0xEF) zs)-      ==>-      case Bytes.split3 0xEF (slicedPack (ws ++ [0xEF] ++ xs ++ [0xEF] ++ ys ++ [0xEF] ++ zs)) of-        Just r -> r === (slicedPack ws, slicedPack xs, slicedPack ys, slicedPack zs)-        Nothing -> property False-  , testProperty "split4" $ \(ws :: [Word8]) (xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8]) (ps :: [Word8]) ->-      (all (/=0xEF) ws && all (/=0xEF) xs && all (/=0xEF) ys && all (/=0xEF) zs && all (/=0xEF) ps)-      ==>-      case Bytes.split4 0xEF (slicedPack (ws ++ [0xEF] ++ xs ++ [0xEF] ++ ys ++ [0xEF] ++ zs ++ [0xEF] ++ ps)) of-        Just r -> r === (slicedPack ws, slicedPack xs, slicedPack ys, slicedPack zs, slicedPack ps)-        Nothing -> property False-  , testGroup "FNV-1a"-    [ testGroup "32-bit"-      [ testCase "empty" (Bytes.fnv1a32 Bytes.empty @=? 0x811c9dc5)-      , testCase "a" (Bytes.fnv1a32 (bytes "a") @=? 0xe40c292c)-      , testCase "foobar" (Bytes.fnv1a32 (bytes "foobar") @=? 0xbf9cf968)-      ]-    , testGroup "64-bit"-      [ testCase "empty" (Bytes.fnv1a64 Bytes.empty @=? 0xcbf29ce484222325)-      , testCase "a" (Bytes.fnv1a64 (bytes "a") @=? 0xaf63dc4c8601ec8c)-      , testCase "foobar" (Bytes.fnv1a64 (bytes "foobar") @=? 0x85944171f73967e8)-      , testCase "google.com" (Bytes.fnv1a64 (bytes "google.com") @=? 0xe1a2c1ae38dcdf45)-      ]-    ]-  , testGroup "Chunks"-    [ lawsToTest (QCC.eqLaws (Proxy :: Proxy Chunks))-    , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy Chunks))-    , lawsToTest (QCC.monoidLaws (Proxy :: Proxy Chunks))-    , testGroup "concatenation"-      [ testProperty "concat=concatU" $ \(c :: Chunks) ->-          Chunks.concat c === Bytes.fromByteArray (Chunks.concatU c)-      , testProperty "concat-singleton" $ \(b :: Bytes) ->-          Chunks.concat (ChunksCons b ChunksNil) === b-      , testProperty "concatU-singleton" $ \(b :: Bytes) ->-          Chunks.concatU (ChunksCons b ChunksNil) === Bytes.toByteArray b-      ]+tests =+  testGroup+    "Bytes"+    [ lawsToTest (QCC.eqLaws (Proxy :: Proxy Bytes))+    , lawsToTest (QCC.ordLaws (Proxy :: Proxy Bytes))+    , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy Bytes))+    , lawsToTest (QCC.monoidLaws (Proxy :: Proxy Bytes))+    , testGroup+        "toLowerAsciiByteArrayClone"+        [ testCase "A" $+            THU.assertBool "" $+              Bytes.isPrefixOf (bytes "hey") (bytes "hey man")+        ]+    , testGroup+        "isPrefixOf"+        [ testCase "A" $+            AsciiExt.toLowerU (bytes "FooBar")+              @=? pack "foobar"+        , testCase "B" $+            THU.assertBool "" $+              not (Bytes.isPrefixOf (bytes "an") (bytes "hey man"))+        ]+    , testGroup+        "isSuffixOf"+        [ testCase "A" $+            THU.assertBool "" $+              Bytes.isSuffixOf (bytes "an") (bytes "hey man")+        , testCase "B" $+            THU.assertBool "" $+              not (Bytes.isSuffixOf (bytes "h") (bytes "hey man"))+        ]+    , testGroup+        "isInfixOf"+        [ testCase "small pattern A" $+            THU.assertBool "" $+              Bytes.isInfixOf (bytes "y m") (bytes "hey man")+        , testCase "small pattern B" $+            THU.assertBool "" $+              Bytes.isInfixOf (bytes "h") (bytes "hey man")+        , testCase "small pattern C" $+            THU.assertBool "" $+              Bytes.isInfixOf (bytes "an") (bytes "hey man")+        , testCase "small pattern D" $+            THU.assertBool "" $+              not (Bytes.isInfixOf (bytes "Y M") (bytes "hey man"))+        , testCase "large pattern A" $+            THU.assertBool "" $+              Bytes.isInfixOf (bytes "Hello, hello!") (bytes "I say hello. Hello, hello! I don't know why you say goodbye, I say hello!")+        , testCase "large pattern D" $+            THU.assertBool "" $+              not (Bytes.isInfixOf (bytes "Hello, hello!") (bytes "I don't know why you say goodbye, I say hello!"))+        , testCase "edge: empty pattern" $+            THU.assertBool "" $+              Bytes.isInfixOf (bytes "") (bytes "hello hello!")+        , testCase "edge: empty string" $+            THU.assertBool "" $+              not (Bytes.isInfixOf (bytes "hello hello!") (bytes ""))+        ]+    , testGroup+        "findIndices"+        [ testCase "A" $+            Exts.fromList [4]+              @=? Bytes.findIndices (Bytes.fromByteArray (pack "greatest showman")) (Bytes.fromByteArray (pack "the greatest showman of all"))+        , testCase "B" $+            Exts.fromList [4]+              @=? Bytes.findIndices (bytes "greatest showman") (bytes "the greatest showman of all")+        , testCase "C" $+            Exts.fromList [0, 1, 2, 3, 4, 5]+              @=? Bytes.findIndices (bytes "a") (bytes "aaaaaa")+        , testCase "D" $+            Exts.fromList [0, 2, 4]+              @=? Bytes.findIndices (bytes "aa") (bytes "aaaaaaa")+        ]+    , testGroup+        "replace-spec"+        [ testCase "A" $+            Bytes.empty+              @=? Bytes.replace (Bytes.fromByteArray (pack "hello")) (Bytes.fromByteArray (pack "world")) Bytes.empty+        , testCase "B" $+            bytes "xzybbcbbc"+              @=? Bytes.replace (bytes "a") (bytes "b") (bytes "xzyabcabc")+        , testCase "C" $+            bytes "my favorite month is March!"+              @=? Bytes.replace (bytes "November") (bytes "March") (bytes "my favorite month is November!")+        , testCase "D" $+            bytes "Saturn, Saturn, Mars, Saturn"+              @=? Bytes.replace (bytes "Jupiter") (bytes "Saturn") (bytes "Jupiter, Jupiter, Mars, Jupiter")+        ]+    , testGroup+        "stripOptionalSuffix"+        [ testCase "A" $+            Ascii.fromString "hey m"+              @=? Bytes.stripOptionalSuffix (bytes "an") (bytes "hey man")+        , testCase "B" $+            Ascii.fromString "hey man"+              @=? Bytes.stripOptionalSuffix (bytes "h") (bytes "hey man")+        ]+    , testGroup+        "longestCommonPrefix"+        [ testProperty "finds prefix" $ \(pre :: Bytes) (a :: Bytes) (b :: Bytes) ->+            if+              | Just (wa, _) <- Bytes.uncons a+              , Just (wb, _) <- Bytes.uncons b+              , wa /= wb ->+                  Bytes.longestCommonPrefix (pre <> a) (pre <> b) === pre+              | otherwise -> property Discard+        , testProperty "finds no prefix" $ \(a :: Bytes) (b :: Bytes) ->+            if+              | Just (wa, _) <- Bytes.uncons a+              , Just (wb, _) <- Bytes.uncons b+              , wa /= wb ->+                  Bytes.longestCommonPrefix a b === mempty+              | otherwise -> property Discard+        ]+    , testGroup+        "dropWhileEnd"+        [ testCase "A" $+            Ascii.fromString "aabbcc"+              @=? Bytes.dropWhileEnd (== c2w 'b') (bytes "aabbccbb")+        ]+    , testGroup+        "takeWhileEnd"+        [ testCase "A" $+            Ascii.fromString "bb"+              @=? Bytes.takeWhileEnd (== c2w 'b') (bytes "aabbccbb")+        , testCase "B" $+            Ascii.fromString ""+              @=? Bytes.takeWhileEnd (/= c2w '\n') (bytes "aabbccbb\n")+        , testCase "C" $+            slicedPack [0x1, 0x2, 0x3]+              @=? Bytes.takeWhileEnd (/= 0x0) (slicedPack [0x1, 0x0, 0x1, 0x2, 0x3])+        ]+    , testProperty "decodeDecWord" $ \(w :: Word) ->+        Just w+          === Latin1.decodeDecWord (Bytes.unsafeDrop 1 (Exts.fromList (0xFF : (Exts.toList (Latin1.fromString (show w))))))+    , testProperty "elem" $ \(x :: Word8) (xs :: [Word8]) ->+        List.elem x xs+          === Bytes.elem x (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "foldl" $ \(x :: Word8) (xs :: [Word8]) ->+        List.foldl (-) 0 xs+          === Bytes.foldl (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "foldlM" $ \(x :: Word8) (xs :: [Word8]) ->+        let f acc y = (tell [x] >> pure (acc - y)) :: Writer [Word8] Word8+         in Foldable.foldlM f 0 xs+              === Bytes.foldlM f 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "foldrM" $ \(x :: Word8) (xs :: [Word8]) ->+        let f acc y = (tell [x] >> pure (acc - y)) :: Writer [Word8] Word8+         in Foldable.foldrM f 0 xs+              === Bytes.foldrM f 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "foldl'" $ \(x :: Word8) (xs :: [Word8]) ->+        List.foldl' (-) 0 xs+          === Bytes.foldl' (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "foldr" $ \(x :: Word8) (xs :: [Word8]) ->+        Foldable.foldr (-) 0 xs+          === Bytes.foldr (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "foldr'" $ \(x :: Word8) (xs :: [Word8]) ->+        Foldable.foldr' (-) 0 xs+          === Bytes.foldr' (-) 0 (Bytes.unsafeDrop 1 (Exts.fromList (x : xs)))+    , testProperty "count" $ \(x :: Word8) (xs :: [Word8]) ->+        ByteString.count x (ByteString.pack xs)+          === Bytes.count x (slicedPack xs)+    , testProperty "split" $ \(x :: Word8) (xs :: [Word8]) ->+        not (List.null xs)+          ==> ByteString.split x (ByteString.pack xs)+          === map bytesToByteString (Bytes.split x (slicedPack xs))+    , testProperty "intercalate" $ \(x :: Bytes) (xs :: [Bytes]) ->+        mconcat (List.intersperse x xs)+          === Bytes.intercalate x xs+    , testProperty "concatArray" $ \(xs :: [Bytes]) ->+        mconcat xs+          === Bytes.concatArray (Exts.fromList xs)+    , testProperty "splitNonEmpty" $ \(x :: Word8) (xs :: [Word8]) ->+        Bytes.split x (slicedPack xs)+          === Foldable.toList (Bytes.splitNonEmpty x (slicedPack xs))+    , testProperty "splitInit" $ \(x :: Word8) (xs :: [Word8]) -> case xs of+        [] -> Bytes.splitInit x (slicedPack xs) === []+        _ ->+          List.init (ByteString.split x (ByteString.pack xs))+            === map bytesToByteString (Bytes.splitInit x (slicedPack xs))+    , testProperty "splitU" $ \(x :: Word8) (xs :: [Word8]) ->+        not (List.null xs)+          ==> Bytes.split x (slicedPack xs)+          === map Bytes.fromByteArray (Exts.toList (Bytes.splitU x (slicedPack xs)))+    , testCase "splitInit-A" $+        [Ascii.fromString "hello", Ascii.fromString "world"]+          @=? (Bytes.splitInit 0x0A (Ascii.fromString "hello\nworld\n"))+    , testCase "splitInit-B" $+        [Ascii.fromString "hello", Ascii.fromString "world"]+          @=? (Bytes.splitInit 0x0A (Ascii.fromString "hello\nworld\nthere"))+    , testProperty "replace" $ \(ws :: ASCIIString) (xs :: ASCIIString) (ys :: ASCIIString) (zs :: ASCIIString) ->+        let ws' = asciiStringToBytes ws+            xs' = asciiStringToBytes xs+            ys' = asciiStringToBytes ys+            zs' = asciiStringToBytes zs+            ws'' = asciiStringToText ws+            xs'' = asciiStringToText xs+            ys'' = asciiStringToText ys+            zs'' = asciiStringToText zs+         in case ws of+              ASCIIString [] -> property Discard+              _ ->+                Bytes.replace ws' xs' (ys' <> ws' <> zs')+                  === Bytes.fromByteString (Text.Encoding.encodeUtf8 (Text.replace ws'' xs'' (ys'' <> ws'' <> zs'')))+    , testProperty "splitEnd1" $ \(x :: Word8) (xs :: [Word8]) ->+        case ByteString.split x (ByteString.pack xs) of+          [] -> Bytes.splitEnd1 x (slicedPack xs) === Nothing+          [_] -> Bytes.splitEnd1 x (slicedPack xs) === Nothing+          [y1, z1] -> case Bytes.splitEnd1 x (slicedPack xs) of+            Nothing -> property False+            Just (y2, z2) -> (y1, z1) === (bytesToByteString y2, bytesToByteString z2)+          _ -> property Discard+    , testProperty "split1" $ \(x :: Word8) (xs :: [Word8]) ->+        case ByteString.split x (ByteString.pack xs) of+          [] -> Bytes.split1 x (slicedPack xs) === Nothing+          [_] -> Bytes.split1 x (slicedPack xs) === Nothing+          [y1, z1] -> case Bytes.split1 x (slicedPack xs) of+            Nothing -> property False+            Just (y2, z2) -> (y1, z1) === (bytesToByteString y2, bytesToByteString z2)+          _ -> property Discard+    , testProperty "splitTetragram1A" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (w3 :: Word8) (xs :: [Word8]) ->+        (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF && w3 /= 0xEF)+          ==> case Bytes.splitTetragram1 w0 w1 w2 w3 (slicedPack (0xEF : w0 : w1 : w2 : w3 : xs)) of+            Nothing -> property False+            Just (pre, post) ->+              (pre, post) === (Exts.fromList [0xEF], Exts.fromList xs)+    , testProperty "splitTetragram1B" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (w3 :: Word8) (xs :: [Word8]) ->+        (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF && w3 /= 0xEF)+          ==> case Bytes.splitTetragram1 w0 w1 w2 w3 (slicedPack (0xEF : 0xEF : 0xEF : 0xEF : w0 : w1 : w2 : w3 : xs)) of+            Nothing -> property False+            Just (pre, post) ->+              (pre, post) === (Exts.fromList (List.replicate 4 0xEF), Exts.fromList xs)+    , testProperty "splitTetragram1C" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (xs :: [Word8]) ->+        (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF)+          ==> case Bytes.splitTetragram1 w0 w1 w2 0xEF (slicedPack (xs ++ [w0, w1, w2, 0xEF])) of+            Nothing -> property False+            Just (pre, post) ->+              (pre, post) === (Exts.fromList xs, mempty)+    , testProperty "splitTetragram1D" $ \(w0 :: Word8) (w1 :: Word8) (w2 :: Word8) (xs :: [Word8]) ->+        (w0 /= 0xEF && w1 /= 0xEF && w2 /= 0xEF)+          ==> case Bytes.splitTetragram1 w0 w1 w2 0xEF (slicedPack (xs ++ [w0, w1, w2, 0xEF, 0xEF])) of+            Nothing -> property False+            Just (pre, post) ->+              (pre, post) === (Exts.fromList xs, Exts.fromList [0xEF])+    , testProperty "split2" $ \(xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8]) ->+        (all (/= 0xEF) xs && all (/= 0xEF) ys && all (/= 0xEF) zs)+          ==> case Bytes.split2 0xEF (slicedPack (xs ++ [0xEF] ++ ys ++ [0xEF] ++ zs)) of+            Just r -> r === (slicedPack xs, slicedPack ys, slicedPack zs)+            Nothing -> property False+    , testProperty "split3" $ \(ws :: [Word8]) (xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8]) ->+        (all (/= 0xEF) ws && all (/= 0xEF) xs && all (/= 0xEF) ys && all (/= 0xEF) zs)+          ==> case Bytes.split3 0xEF (slicedPack (ws ++ [0xEF] ++ xs ++ [0xEF] ++ ys ++ [0xEF] ++ zs)) of+            Just r -> r === (slicedPack ws, slicedPack xs, slicedPack ys, slicedPack zs)+            Nothing -> property False+    , testProperty "split4" $ \(ws :: [Word8]) (xs :: [Word8]) (ys :: [Word8]) (zs :: [Word8]) (ps :: [Word8]) ->+        (all (/= 0xEF) ws && all (/= 0xEF) xs && all (/= 0xEF) ys && all (/= 0xEF) zs && all (/= 0xEF) ps)+          ==> case Bytes.split4 0xEF (slicedPack (ws ++ [0xEF] ++ xs ++ [0xEF] ++ ys ++ [0xEF] ++ zs ++ [0xEF] ++ ps)) of+            Just r -> r === (slicedPack ws, slicedPack xs, slicedPack ys, slicedPack zs, slicedPack ps)+            Nothing -> property False+    , testGroup+        "FNV-1a"+        [ testGroup+            "32-bit"+            [ testCase "empty" (Bytes.fnv1a32 Bytes.empty @=? 0x811c9dc5)+            , testCase "a" (Bytes.fnv1a32 (bytes "a") @=? 0xe40c292c)+            , testCase "foobar" (Bytes.fnv1a32 (bytes "foobar") @=? 0xbf9cf968)+            ]+        , testGroup+            "64-bit"+            [ testCase "empty" (Bytes.fnv1a64 Bytes.empty @=? 0xcbf29ce484222325)+            , testCase "a" (Bytes.fnv1a64 (bytes "a") @=? 0xaf63dc4c8601ec8c)+            , testCase "foobar" (Bytes.fnv1a64 (bytes "foobar") @=? 0x85944171f73967e8)+            , testCase "google.com" (Bytes.fnv1a64 (bytes "google.com") @=? 0xe1a2c1ae38dcdf45)+            ]+        ]+    , testGroup+        "Chunks"+        [ lawsToTest (QCC.eqLaws (Proxy :: Proxy Chunks))+        , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy Chunks))+        , lawsToTest (QCC.monoidLaws (Proxy :: Proxy Chunks))+        , testGroup+            "concatenation"+            [ testProperty "concat=concatU" $ \(c :: Chunks) ->+                Chunks.concat c === Bytes.fromByteArray (Chunks.concatU c)+            , testProperty "concat-singleton" $ \(b :: Bytes) ->+                Chunks.concat (ChunksCons b ChunksNil) === b+            , testProperty "concatU-singleton" $ \(b :: Bytes) ->+                Chunks.concatU (ChunksCons b ChunksNil) === Bytes.toByteArray b+            ]+        ]     ]-  ]  bytes :: String -> Bytes bytes s = let b = pack ('x' : s) in Bytes b 1 (PM.sizeofByteArray b - 1)@@ -359,8 +355,8 @@ instance Arbitrary Bytes where   arbitrary = do     xs :: [Word8] <- TQC.arbitrary-    front <- TQC.choose (0,2)-    back <- TQC.choose (0,2)+    front <- TQC.choose (0, 2)+    back <- TQC.choose (0, 2)     let frontPad = replicate front (254 :: Word8)     let backPad = replicate back (254 :: Word8)     let raw = Exts.fromList (frontPad ++ xs ++ backPad)@@ -369,13 +365,17 @@ instance Arbitrary Chunks where   arbitrary = do     xs :: [[Word8]] <- TQC.arbitrary-    let ys = map-          (\x -> Exts.fromList ([255] ++ x ++ [255]))-          xs-        zs = foldr-          (\b cs ->-            ChunksCons (Bytes b 1 (PM.sizeofByteArray b - 2)) cs-          ) ChunksNil ys+    let ys =+          map+            (\x -> Exts.fromList ([255] ++ x ++ [255]))+            xs+        zs =+          foldr+            ( \b cs ->+                ChunksCons (Bytes b 1 (PM.sizeofByteArray b - 2)) cs+            )+            ChunksNil+            ys     pure zs  lawsToTest :: QCC.Laws -> TestTree