packages feed

byteslice 0.2.5.2 → 0.2.6.0

raw patch · 18 files changed

+759/−312 lines, 18 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.Bytes.Types: instance GHC.Base.Monoid Data.Bytes.Types.Bytes
- Data.Bytes.Types: instance GHC.Base.Semigroup Data.Bytes.Types.Bytes
- Data.Bytes.Types: instance GHC.Classes.Eq Data.Bytes.Types.Bytes
- Data.Bytes.Types: instance GHC.Classes.Ord Data.Bytes.Types.Bytes
- Data.Bytes.Types: instance GHC.Exts.IsList Data.Bytes.Types.Bytes
- Data.Bytes.Types: instance GHC.Show.Show Data.Bytes.Types.Bytes
+ Data.Bytes: isInfixOf :: Bytes -> Bytes -> Bool
+ Data.Bytes: lift :: Bytes# -> Bytes
+ Data.Bytes: unlift :: Bytes -> Bytes#
+ Data.Bytes.Internal: Bytes :: {-# UNPACK #-} !ByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> Bytes
+ Data.Bytes.Internal: [$sel:array:Bytes] :: Bytes -> {-# UNPACK #-} !ByteArray
+ Data.Bytes.Internal: [$sel:length:Bytes] :: Bytes -> {-# UNPACK #-} !Int
+ Data.Bytes.Internal: [$sel:offset:Bytes] :: Bytes -> {-# UNPACK #-} !Int
+ Data.Bytes.Internal: data Bytes
+ Data.Bytes.Internal: instance GHC.Base.Monoid Data.Bytes.Internal.Bytes
+ Data.Bytes.Internal: instance GHC.Base.Semigroup Data.Bytes.Internal.Bytes
+ Data.Bytes.Internal: instance GHC.Classes.Eq Data.Bytes.Internal.Bytes
+ Data.Bytes.Internal: instance GHC.Classes.Ord Data.Bytes.Internal.Bytes
+ Data.Bytes.Internal: instance GHC.Exts.IsList Data.Bytes.Internal.Bytes
+ Data.Bytes.Internal: instance GHC.Show.Show Data.Bytes.Internal.Bytes
+ Data.Bytes.Text.Ascii: fromString :: String -> Bytes
+ Data.Bytes.Text.AsciiExt: foldLines :: a -> (a -> Bytes -> IO a) -> IO a
+ Data.Bytes.Text.AsciiExt: forLines_ :: (Bytes -> IO a) -> IO ()
+ Data.Bytes.Text.AsciiExt: hFoldLines :: Handle -> a -> (a -> Bytes -> IO a) -> IO a
+ Data.Bytes.Text.AsciiExt: hForLines_ :: Handle -> (Bytes -> IO a) -> IO ()
+ Data.Bytes.Text.AsciiExt: toLowerU :: Bytes -> ByteArray
+ Data.Bytes.Text.Latin1: equals1 :: Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals10 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals11 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals12 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals2 :: Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals3 :: Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals4 :: Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals5 :: Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals6 :: Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals7 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals8 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: equals9 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool
+ Data.Bytes.Text.Latin1: fromString :: String -> Bytes
+ Data.Bytes.Text.Latin1: toString :: Bytes -> String
+ Data.Bytes.Types: ByteArrayN :: ByteArray -> ByteArrayN (n :: Nat)
+ Data.Bytes.Types: BytesN :: {-# UNPACK #-} !ByteArray -> {-# UNPACK #-} !Int -> BytesN (n :: Nat)
+ Data.Bytes.Types: [$sel:array:ByteArrayN] :: ByteArrayN (n :: Nat) -> ByteArray
+ Data.Bytes.Types: [$sel:array:BytesN] :: BytesN (n :: Nat) -> {-# UNPACK #-} !ByteArray
+ Data.Bytes.Types: [$sel:offset:BytesN] :: BytesN (n :: Nat) -> {-# UNPACK #-} !Int
+ Data.Bytes.Types: data BytesN (n :: Nat)
+ Data.Bytes.Types: newtype ByteArrayN (n :: Nat)

Files

CHANGELOG.md view
@@ -1,5 +1,14 @@ # Revision history for byteslice +## 0.2.6.0 -- 2021-09-15++* Add `BytesN` and `ByteArrayN`.+* Add `isInfixOf`.+* Add `hForLines_` and `hFoldLines`.+* Add `lift` and `unlift` for converting between `Bytes` and `Bytes#`.+* Move text-oriented functions from Data.Bytes to `Data.Bytes.Text.*`.+  Provide aliases with older names that come with deprecation warning.+ ## 0.2.5.2 -- 2021-02-23  * Correct compatibility shims.
bench/Main.hs view
@@ -2,7 +2,7 @@  import Data.Bytes.Types import Data.List (permutations)-import qualified Data.Bytes as Bytes+import qualified Data.Bytes.Text.Ascii as Ascii  naiveMconcat :: [Bytes] -> Bytes naiveMconcat = foldr mappend mempty@@ -14,4 +14,4 @@   ]  mconcatBytes :: [Bytes]-mconcatBytes = fmap Bytes.fromAsciiString $ permutations ['a'..'g']+mconcatBytes = fmap Ascii.fromString $ permutations ['a'..'g']
byteslice.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: byteslice-version: 0.2.5.2+version: 0.2.6.0 synopsis: Slicing managed and unmanaged memory description:   This library provides types that allow the user to talk about a slice of@@ -25,7 +25,13 @@   exposed-modules:     Data.Bytes     Data.Bytes.Chunks+    Data.Bytes.Internal     Data.Bytes.Mutable+    Data.Bytes.Text.Ascii+    Data.Bytes.Text.AsciiExt+    Data.Bytes.Text.Latin1+    Data.Bytes.Text.Utf8+    Data.Bytes.Text.Windows1252     Data.Bytes.Types   other-modules:     Data.Bytes.Byte
src-no-unlifted-newtypes/UnliftedBytes.hs view
@@ -5,10 +5,19 @@  module UnliftedBytes   ( Bytes#+  , lift+  , unlift   ) where +import Data.Bytes.Internal (Bytes) import GHC.TypeLits import GHC.Exts (RuntimeRep(..),TYPE)  type family Bytes# :: TYPE ('TupleRep '[ 'UnliftedRep,'IntRep,'IntRep]) where   Bytes# = TypeError ('Text "Bytes# not available before GHC 8.10")++lift :: Bytes# -> Bytes+lift _ = errorWithoutStackTrace "UnliftedBytes: lift not implemented"++unlift :: Bytes -> Bytes#+unlift _ = errorWithoutStackTrace "UnliftedBytes: unlift not implemented"
src-unlifted-newtypes/UnliftedBytes.hs view
@@ -7,10 +7,20 @@  module UnliftedBytes   ( Bytes#(..)+  , lift+  , unlift   ) where -import GHC.Exts (ByteArray#,Int#,RuntimeRep(..),TYPE)+import Data.Bytes.Internal (Bytes(Bytes))+import Data.Primitive (ByteArray(ByteArray))+import GHC.Exts (Int(I#),ByteArray#,Int#,RuntimeRep(..),TYPE)  newtype Bytes# :: TYPE ('TupleRep '[ 'UnliftedRep,'IntRep,'IntRep]) where   Bytes# :: (# ByteArray#, Int#, Int# #) -> Bytes# +lift :: Bytes# -> Bytes+lift (Bytes# (# arr, off, len #)) = Bytes (ByteArray arr) (I# off) (I# len)++unlift :: Bytes -> Bytes#+unlift (Bytes (ByteArray arr) (I# off) (I# len)) =+  Bytes# (# arr, off, len #)
src/Data/Bytes.hs view
@@ -4,9 +4,17 @@ {-# language MagicHash #-} {-# language NamedFieldPuns #-} {-# language RankNTypes #-}+{-# language TupleSections #-} {-# language TypeApplications #-} {-# 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'. module Data.Bytes   ( -- * Types     Bytes@@ -40,12 +48,12 @@   , takeWhileEnd   , dropWhileEnd     -- * Folds-  , foldl+  , Pure.foldl   , Pure.foldl'-  , foldr-  , foldr'+  , Pure.foldr+  , Pure.foldr'     -- * Folds with Indices-  , ifoldl'+  , Pure.ifoldl'     -- * Common Folds   , elem     -- * Splitting@@ -72,6 +80,7 @@     -- ** Byte Sequence   , isPrefixOf   , isSuffixOf+  , isInfixOf   , stripPrefix   , stripOptionalPrefix   , stripSuffix@@ -123,7 +132,7 @@   , fromCString#   , Pure.toByteString   , Pure.pinnedToByteString-  , fromByteString+  , Pure.fromByteString   , fromShortByteString   , toShortByteString   , toShortByteStringClone@@ -132,33 +141,36 @@   , BIO.hGet   , readFile   , BIO.hPut+    -- * Unlifted Types+  , lift+  , unlift   ) where  import Prelude hiding (length,takeWhile,dropWhile,null,foldl,foldr,elem,replicate,any,all,readFile)  import Control.Monad.Primitive (PrimMonad,primitive_,unsafeIOToPrim)-import Control.Monad.ST (ST) import Control.Monad.ST.Run (runByteArrayST)-import Data.ByteString (ByteString)-import Data.ByteString.Short.Internal (ShortByteString(SBS))-import Data.Bytes.Pure (length,fromByteArray)+import Cstrlen (cstringLength#)+import Data.Bits((.&.),(.|.),shiftL,finiteBitSize)+import Data.Bytes.Pure (length,fromByteArray,foldr) import Data.Bytes.Types (Bytes(Bytes,array,offset))-import Data.Char (ord)+import Data.ByteString.Short.Internal (ShortByteString(SBS))+import Data.Maybe (fromMaybe) import Data.Primitive (ByteArray(ByteArray)) import Foreign.C.String (CString) import Foreign.Ptr (Ptr,plusPtr,castPtr)-import GHC.Exts (Int(I#),Char(C#),Ptr(Ptr),word2Int#,chr#) import GHC.Exts (Addr#,Word#,Int#)-import GHC.IO (unsafeIOToST)-import GHC.Word (Word8(W8#))-import Cstrlen (cstringLength#)+import GHC.Exts (Int(I#),Ptr(Ptr))+import GHC.Word (Word8(W8#),Word32)+import UnliftedBytes (lift,unlift) -import qualified Data.ByteString as ByteString-import qualified Data.ByteString.Unsafe as ByteString import qualified Data.Bytes.Byte as Byte import qualified Data.Bytes.Chunks as Chunks import qualified Data.Bytes.IO as BIO import qualified Data.Bytes.Pure as Pure+import qualified Data.Bytes.Text.Ascii as Ascii+import qualified Data.Bytes.Text.AsciiExt as AsciiExt+import qualified Data.Bytes.Text.Latin1 as Latin1 import qualified Data.Bytes.Types as Types import qualified Data.Foldable as F import qualified Data.List as List@@ -224,6 +236,67 @@     then compareByteArrays a aOff b (bOff + bLen - aLen) aLen == EQ     else False +-- | 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 = null p || (not . null) (snd $ breakSubstring p s)++breakSubstring :: Bytes -- ^ String to search for+               -> Bytes -- ^ String to search in+               -> (Bytes,Bytes) -- ^ Head and tail of string broken at substring+breakSubstring pat =+  case lp of+    0 -> (mempty,)+    1 -> breakByte (unsafeHead pat)+    _ -> if lp * 8 <= finiteBitSize (0 :: Word)+             then shift+             else karpRabin+  where+  unsafeSplitAt i s = (unsafeTake i s, unsafeDrop i s)+  lp                = length pat+  {-# INLINE breakByte #-}+  breakByte b bytes = fromMaybe (mempty,bytes) $ Byte.split1 b bytes+  {-# INLINE karpRabin #-}+  karpRabin :: Bytes -> (Bytes, Bytes)+  karpRabin src+      | length src < lp = (src,mempty)+      | otherwise = search (rollingHash $ unsafeTake lp src) lp+    where+    k           = 2891336453 :: Word32+    rollingHash = Pure.foldl' (\h b -> h * k + fromIntegral b) 0+    hp          = rollingHash pat+    m           = k ^ lp+    get = fromIntegral . unsafeIndex src+    search !hs !i+        | hp == hs && pat == unsafeTake lp b = u+        | length src <= i                    = (src,mempty) -- not found+        | otherwise                          = search hs' (i + 1)+      where+      u@(_, b) = unsafeSplitAt (i - lp) src+      hs' = hs * k ++            get i -+            m * get (i - lp)+  {-# INLINE shift #-}+  shift :: Bytes -> (Bytes, Bytes)+  shift !src+      | length src < lp = (src,mempty)+      | otherwise       = search (intoWord $ unsafeTake lp src) lp+    where+    intoWord :: Bytes -> Word+    intoWord = Pure.foldl' (\w b -> (w `shiftL` 8) .|. fromIntegral b) 0+    wp   = intoWord pat+    mask = (1 `shiftL` (8 * lp)) - 1+    search !w !i+        | w == wp         = unsafeSplitAt (i - lp) src+        | length src <= i = (src, mempty)+        | otherwise       = search w' (i + 1)+      where+      b  = fromIntegral (unsafeIndex src i)+      w' = mask .&. ((w `shiftL` 8) .|. b)+ -- | Find the longest string which is a prefix of both arguments. longestCommonPrefix :: Bytes -> Bytes -> Bytes longestCommonPrefix a b = loop 0@@ -349,6 +422,11 @@ {-# 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 #-}+unsafeHead :: Bytes -> Word8+unsafeHead bs = unsafeIndex bs 0+ -- | /O(n)/ 'dropWhileEnd' @p@ @b@ returns the prefix remaining after -- dropping characters that satisfy the predicate @p@ from the end of -- @t@.@@ -400,61 +478,27 @@       else n     else n --- | Left fold over bytes, non-strict in the accumulator.-foldl :: (a -> Word8 -> a) -> a -> Bytes -> a-{-# inline foldl #-}-foldl f a0 (Bytes arr off0 len0) =-  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)---- | 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-  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.-ifoldl' :: (a -> Int -> Word8 -> a) -> a -> Bytes -> a-{-# 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' #-}-foldr' f a0 (Bytes arr off0 len0) =-  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)- -- | 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-fromAsciiString = fromByteArray-  . Exts.fromList-  . map (\c -> let i = ord c in if i < 128 then fromIntegral @Int @Word8 i else 0)+{-# DEPRECATED fromAsciiString "use Data.Bytes.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. fromLatinString :: String -> Bytes-fromLatinString =-  fromByteArray . Exts.fromList . map (fromIntegral @Int @Word8 . ord)+{-# DEPRECATED fromLatinString "use Data.Bytes.Latin1.fromString instead" #-}+{-# INLINE fromLatinString #-}+fromLatinString = Latin1.fromString  -- | Interpret a byte sequence as text encoded by ISO-8859-1. toLatinString :: Bytes -> String-toLatinString = foldr (\(W8# w) xs -> C# (chr# (word2Int# w)) : xs) []+{-# DEPRECATED toLatinString "use Data.Bytes.Latin1.toString instead" #-}+{-# INLINE toLatinString #-}+toLatinString = Latin1.toString  -- | Copy a primitive string literal into managed memory. fromCString# :: Addr# -> Bytes@@ -476,153 +520,87 @@ -- | Is the byte sequence, when interpreted as ISO-8859-1-encoded text, -- a singleton whose element matches the character? equalsLatin1 :: Char -> Bytes -> Bool-{-# inline equalsLatin1 #-}-equalsLatin1 !c0 (Bytes arr off len) = case len of-  1 -> c0 == indexCharArray arr off-  _ -> False+{-# 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? equalsLatin2 :: Char -> Char -> Bytes -> Bool-equalsLatin2 !c0 !c1 (Bytes arr off len) = case len of-  2 -> c0 == indexCharArray arr off &&-       c1 == indexCharArray arr (off + 1)-  _ -> False+{-# 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? equalsLatin3 :: Char -> Char -> Char -> Bytes -> Bool-equalsLatin3 !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)-  _ -> False+{-# 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? equalsLatin4 :: Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin4 !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)-  _ -> False+{-# 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? equalsLatin5 :: Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin5 !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)-  _ -> False+{-# 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? equalsLatin6 :: Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin6 !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)-  _ -> False+{-# 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? equalsLatin7 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin7 !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)-  _ -> False+{-# 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? equalsLatin8 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin8 !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)-  _ -> False+{-# 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? equalsLatin9 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin9 !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)-  _ -> False+{-# 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? equalsLatin10 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin10 !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)-  _ -> False+{-# 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? equalsLatin11 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin11 !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)-  _ -> False+{-# 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? equalsLatin12 :: Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Char -> Bytes -> Bool-equalsLatin12 !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)-  _ -> False+{-# 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.@@ -649,15 +627,12 @@         False -> Nothing  -- | Touch the byte array backing the byte sequence. This sometimes needed--- after calling 'contents' so that the @ByteArray@ does not get garbage+-- 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)) -indexCharArray :: ByteArray -> Int -> Char-indexCharArray (ByteArray arr) (I# off) = C# (Exts.indexCharArray# arr off)- -- | Read an entire file strictly into a 'Bytes'. readFile :: FilePath -> IO Bytes readFile f = Chunks.concat <$> Chunks.readFile f@@ -740,33 +715,6 @@ -- @[0x41,0x5A]@ and leaves all other bytes alone. Unconditionally -- copies the bytes. toLowerAsciiByteArrayClone :: Bytes -> ByteArray-toLowerAsciiByteArrayClone (Bytes src off0 len0) =-  runByteArrayST action-  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)-    go off0 0 len0-    PM.unsafeFreezeByteArray dst----- | /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-  !len = ByteString.length b+{-# DEPRECATED toLowerAsciiByteArrayClone "use Data.Bytes/Text/AsciiExt.toLowerU" #-}+{-# INLINE toLowerAsciiByteArrayClone #-}+toLowerAsciiByteArrayClone = AsciiExt.toLowerU
src/Data/Bytes/Chunks.hs view
@@ -290,7 +290,7 @@   ) 0xcbf29ce484222325 b  -- | Outputs 'Chunks' to the specified 'Handle'. This is implemented--- with 'hPutBuf'.+-- with 'IO.hPut'. hPut :: Handle -> Chunks -> IO () hPut h = go where   go ChunksNil = pure ()
src/Data/Bytes/IO.hs view
@@ -22,12 +22,12 @@ import qualified Data.Primitive as PM  -- | Read 'Bytes' directly from the specified 'Handle'. The resulting--- 'Bytes' are pinned. This is implemented with 'hGetBuf'.+-- '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 'hPutBuf'.+-- with 'IO.hPutBuf'. hPut :: Handle -> Bytes -> IO () hPut h b0 = do   let b1@(Bytes arr _ len) = pin b0@@ -52,4 +52,3 @@ touchByteArrayIO :: ByteArray -> IO () touchByteArrayIO (ByteArray x) =   IO (\s -> (# Exts.touch# x s, () #))-
+ src/Data/Bytes/Internal.hs view
@@ -0,0 +1,113 @@+{-# language BangPatterns #-}+{-# language DataKinds #-}+{-# language MagicHash #-}+{-# language TypeFamilies #-}+{-# language DuplicateRecordFields #-}++-- This needs to be in its own module to prevent a cyclic dependency+-- between UnliftedBytes and Data.Bytes.Types+module Data.Bytes.Internal+  ( Bytes(..)+  ) where++import Control.Monad.ST (runST)+import Control.Monad.ST.Run (runByteArrayST)+import Data.Bits ((.&.),unsafeShiftR)+import Data.Char (ord)+import Data.Primitive (ByteArray(..))+import Data.Word (Word8)+import GHC.Base (unsafeChr)+import GHC.Exts (Int(I#),unsafeCoerce#,sameMutableByteArray#)+import GHC.Exts (isTrue#,compareByteArrays#,IsList(..))++import qualified Data.List as L+import qualified Data.Foldable as F+import qualified Data.Primitive as PM++-- | A slice of a 'ByteArray'.+data Bytes = Bytes+  { array :: {-# UNPACK #-} !ByteArray+  , offset :: {-# UNPACK #-} !Int+  , length :: {-# UNPACK #-} !Int+  }++instance IsList Bytes where+  type Item Bytes = Word8+  fromListN n xs = Bytes (fromListN n xs) 0 n+  fromList xs = fromListN (L.length xs) xs+  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 []++instance Show Bytes where+  showsPrec _ (Bytes arr off len) s = if len == 0+    then showString "[]" s+    else showString "[0x"+       $ showHexDigits (PM.indexByteArray arr off)+       $ showLoop (off + 1) (len - 1) arr+       $ showChar ']'+       $ s++showLoop :: Int -> Int -> ByteArray -> String -> String+showLoop !ix !len !arr s = if len > 0+  then ',':'0':'x':showHexDigits (PM.indexByteArray arr ix) (showLoop (ix + 1) (len - 1) arr s)+  else s++showHexDigits :: Word8 -> String -> String+showHexDigits !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)++instance Eq Bytes where+  Bytes arr1 off1 len1 == Bytes arr2 off2 len2+    | len1 /= len2 = False+    | sameByteArray arr1 arr2 && off1 == off2 = True+    | otherwise = compareByteArrays arr1 off1 arr2 off2 len1 == EQ++instance Ord Bytes where+  compare (Bytes arr1 off1 len1) (Bytes arr2 off2 len2)+    | sameByteArray arr1 arr2 && off1 == off2 && len1 == len2 = EQ+    | otherwise = compareByteArrays arr1 off1 arr2 off2 (min len1 len2) <> compare len1 len2++instance Semigroup Bytes where+  -- TODO: Do the trick to move the data constructor to the outside+  -- of runST.+  Bytes arrA offA lenA <> Bytes arrB offB lenB = runST $ do+    marr <- PM.newByteArray (lenA + lenB)+    PM.copyByteArray marr 0 arrA offA lenA+    PM.copyByteArray marr lenA arrB offB lenB+    r <- PM.unsafeFreezeByteArray marr+    pure (Bytes r 0 (lenA + lenB))++instance Monoid Bytes where+  mempty = Bytes mempty 0 0+  mconcat [] = mempty+  mconcat [x] = x+  mconcat bs = Bytes r 0 fullLen+    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+      PM.unsafeFreezeByteArray marr++compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering+{-# INLINE compareByteArrays #-}+compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#) =+  compare (I# (compareByteArrays# ba1# off1# ba2# off2# n#)) 0++sameByteArray :: ByteArray -> ByteArray -> Bool+{-# INLINE sameByteArray #-}+sameByteArray (ByteArray ba1#) (ByteArray ba2#) =+  isTrue# (sameMutableByteArray# (unsafeCoerce# ba1#) (unsafeCoerce# ba2#))+
src/Data/Bytes/Mutable.hs view
@@ -1,6 +1,13 @@ {-# 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'. module Data.Bytes.Mutable   ( -- * Types     MutableBytes
src/Data/Bytes/Pure.hs view
@@ -18,26 +18,35 @@   , toPinnedByteArrayClone   , fromByteArray   , length+  , foldl   , foldl'+  , foldr+  , ifoldl'+  , foldr'   , fnv1a32   , fnv1a64   , toByteString   , pinnedToByteString+  , fromByteString   ) where -import Prelude hiding (length)+import Prelude hiding (length,foldl,foldr)  import Control.Monad.Primitive (PrimState,PrimMonad) import Control.Monad.ST.Run (runByteArrayST) import Data.Bits (xor)-import Data.ByteString (ByteString) import Data.Bytes.Types (Bytes(Bytes))+import Data.ByteString (ByteString) import Data.Primitive (ByteArray,MutableByteArray) import Data.Word (Word64,Word32,Word8) import Foreign.Ptr (Ptr,plusPtr)+import GHC.IO (unsafeIOToST) +import qualified Data.ByteString as ByteString import qualified Data.ByteString.Internal as ByteString+import qualified Data.ByteString.Unsafe as ByteString import qualified Data.Primitive as PM+import qualified Data.Primitive.Ptr as PM import qualified GHC.Exts as Exts import qualified GHC.ForeignPtr as ForeignPtr @@ -124,6 +133,16 @@   (\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 #-}+foldl f a0 (Bytes arr off0 len0) =+  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' #-}@@ -132,6 +151,34 @@     0 -> a     _ -> go (f a (PM.indexByteArray arr off)) (off + 1) (len - 1) +-- | 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+  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.+ifoldl' :: (a -> Int -> Word8 -> a) -> a -> Bytes -> a+{-# 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' #-}+foldr' f a0 (Bytes arr off0 len0) =+  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)++ -- | 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@@ -162,7 +209,8 @@ toByteString :: Bytes -> ByteString toByteString !b = pinnedToByteString (pin b) --- | /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@@ -171,3 +219,15 @@       (ForeignPtr.PlainPtr (Exts.unsafeCoerce# x))     )     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+  !len = ByteString.length b
+ src/Data/Bytes/Text/Ascii.hs view
@@ -0,0 +1,29 @@+{-# 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'.+module Data.Bytes.Text.Ascii+  ( fromString+  ) where++import Data.Bytes.Types (Bytes)+import Data.Char (ord)+import Data.Word (Word8)++import qualified Data.Bytes.Pure as Bytes+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@.+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)++-- TODO presumably also fromText and fromShortText
+ src/Data/Bytes/Text/AsciiExt.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE BangPatterns #-}+{-# 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).+module Data.Bytes.Text.AsciiExt+  ( -- * Line-Oriented IO+    hFoldLines+  , hForLines_+  -- ** Standard Handles+  , forLines_+  , foldLines+  -- * Text Manipulation+  , toLowerU+  ) where++import Control.Monad.ST (ST)+import Control.Monad.ST.Run (runByteArrayST)+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.Primitive as PM++-- | `hForLines_` over `stdin`+forLines_ :: (Bytes -> IO a) -> IO ()+{-# INLINEABLE forLines_ #-}+forLines_ = hForLines_ stdin++-- | `hFoldLines` over `stdin`+foldLines :: a -> (a -> Bytes -> IO a) -> IO a+{-# 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+-- dectection 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 ()++-- | 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+-- dectection 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++-- | /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+  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)+    go off0 0 len0+    PM.unsafeFreezeByteArray dst
+ src/Data/Bytes/Text/Latin1.hs view
@@ -0,0 +1,214 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE TypeApplications #-}++-- | 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+  -- * Specialized Comparisons+  , equals1+  , equals2+  , equals3+  , equals4+  , equals5+  , equals6+  , equals7+  , equals8+  , equals9+  , equals10+  , equals11+  , equals12+  ) where++import Data.Bytes.Types (Bytes(..))+import Data.Char (ord)+import Data.Primitive (ByteArray(ByteArray))+import GHC.Exts (Int(I#),Char(C#),word2Int#,chr#)+import GHC.Word (Word8(W8#))++import qualified Data.Bytes.Pure as Bytes+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.+fromString :: String -> Bytes+fromString =+  Bytes.fromByteArray . Exts.fromList . map (fromIntegral @Int @Word8 . ord)++-- | Interpret a byte sequence as text encoded by ISO-8859-1.+toString :: Bytes -> String+{-# INLINE toString #-}+toString = Bytes.foldr (\(W8# w) xs -> C# (chr# (word2Int# w)) : xs) []++-- 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?+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?+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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++-- | 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)+  _ -> False++indexCharArray :: ByteArray -> Int -> Char+indexCharArray (ByteArray arr) (I# off) = C# (Exts.indexCharArray# arr off)
+ src/Data/Bytes/Text/Utf8.hs view
@@ -0,0 +1,3 @@+-- | Placeholder module in case there is demand for treating 'Bytes' as+-- UTF8-encoded text+module Data.Bytes.Text.Utf8 () where
+ src/Data/Bytes/Text/Windows1252.hs view
@@ -0,0 +1,3 @@+-- | 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,4 +1,5 @@ {-# language BangPatterns #-}+{-# language DataKinds #-} {-# language MagicHash #-} {-# language TypeFamilies #-} {-# language DuplicateRecordFields #-}@@ -8,31 +9,31 @@   , Bytes#(..)   , MutableBytes(..)   , UnmanagedBytes(..)+  , BytesN(..)+  , ByteArrayN(..)   ) where -import Control.Monad.ST (runST)-import Control.Monad.ST.Run (runByteArrayST)-import Data.Bits ((.&.),unsafeShiftR)-import Data.Char (ord)+import Data.Bytes.Internal (Bytes(..)) import Data.Primitive (ByteArray(..),MutableByteArray(..)) import Data.Primitive.Addr (Addr)-import Data.Word (Word8)-import GHC.Base (unsafeChr)-import GHC.Exts (Int(I#),unsafeCoerce#,sameMutableByteArray#)-import GHC.Exts (isTrue#,compareByteArrays#,IsList(..))+import GHC.TypeNats (Nat) import UnliftedBytes (Bytes#(..)) -import qualified Data.List as L-import qualified Data.Foldable as F-import qualified Data.Primitive as PM---- | A slice of a 'ByteArray'.-data Bytes = 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.+data BytesN (n :: Nat) = BytesN   { array :: {-# UNPACK #-} !ByteArray   , offset :: {-# UNPACK #-} !Int-  , length :: {-# UNPACK #-} !Int   } +-- | 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+  }+ -- | A slice of a 'MutableByteArray'. data MutableBytes s = MutableBytes   { array :: {-# UNPACK #-} !(MutableByteArray s)@@ -45,83 +46,3 @@   { address :: {-# UNPACK #-} !Addr   , length :: {-# UNPACK #-} !Int   }--instance IsList Bytes where-  type Item Bytes = Word8-  fromListN n xs = Bytes (fromListN n xs) 0 n-  fromList xs = fromListN (L.length xs) xs-  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 []--instance Show Bytes where-  showsPrec _ (Bytes arr off len) s = if len == 0-    then showString "[]" s-    else showString "[0x"-       $ showHexDigits (PM.indexByteArray arr off)-       $ showLoop (off + 1) (len - 1) arr-       $ showChar ']'-       $ s--showLoop :: Int -> Int -> ByteArray -> String -> String-showLoop !ix !len !arr s = if len > 0-  then ',':'0':'x':showHexDigits (PM.indexByteArray arr ix) (showLoop (ix + 1) (len - 1) arr s)-  else s--showHexDigits :: Word8 -> String -> String-showHexDigits !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)--instance Eq Bytes where-  Bytes arr1 off1 len1 == Bytes arr2 off2 len2-    | len1 /= len2 = False-    | sameByteArray arr1 arr2 && off1 == off2 = True-    | otherwise = compareByteArrays arr1 off1 arr2 off2 len1 == EQ--instance Ord Bytes where-  compare (Bytes arr1 off1 len1) (Bytes arr2 off2 len2)-    | sameByteArray arr1 arr2 && off1 == off2 && len1 == len2 = EQ-    | otherwise = compareByteArrays arr1 off1 arr2 off2 (min len1 len2) <> compare len1 len2--instance Semigroup Bytes where-  -- TODO: Do the trick to move the data constructor to the outside-  -- of runST.-  Bytes arrA offA lenA <> Bytes arrB offB lenB = runST $ do-    marr <- PM.newByteArray (lenA + lenB)-    PM.copyByteArray marr 0 arrA offA lenA-    PM.copyByteArray marr lenA arrB offB lenB-    r <- PM.unsafeFreezeByteArray marr-    pure (Bytes r 0 (lenA + lenB))--instance Monoid Bytes where-  mempty = Bytes mempty 0 0-  mconcat [] = mempty-  mconcat [x] = x-  mconcat bs = Bytes r 0 fullLen-    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-      PM.unsafeFreezeByteArray marr--compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering-{-# INLINE compareByteArrays #-}-compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#) =-  compare (I# (compareByteArrays# ba1# off1# ba2# off2# n#)) 0--sameByteArray :: ByteArray -> ByteArray -> Bool-{-# INLINE sameByteArray #-}-sameByteArray (ByteArray ba1#) (ByteArray ba2#) =-  isTrue# (sameMutableByteArray# (unsafeCoerce# ba1#) (unsafeCoerce# ba2#))
test/Main.hs view
@@ -20,6 +20,8 @@  import qualified Data.ByteString as ByteString import qualified Data.Bytes as Bytes+import qualified Data.Bytes.Text.Ascii as Ascii+import qualified Data.Bytes.Text.AsciiExt as AsciiExt import qualified Data.Bytes.Chunks as Chunks import qualified Data.Foldable as Foldable import qualified Data.List as List@@ -44,7 +46,7 @@     ]   , testGroup "isPrefixOf"     [ testCase "A" $-        Bytes.toLowerAsciiByteArrayClone (bytes "FooBar")+        AsciiExt.toLowerU (bytes "FooBar")         @=?         pack "foobar"     , testCase "B" $ THU.assertBool "" $@@ -56,13 +58,31 @@     , 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 "stripOptionalSuffix"     [ testCase "A" $-        Bytes.fromAsciiString "hey m"+        Ascii.fromString "hey m"         @=?         Bytes.stripOptionalSuffix (bytes "an") (bytes "hey man")     , testCase "B" $-        Bytes.fromAsciiString "hey man"+        Ascii.fromString "hey man"         @=?         Bytes.stripOptionalSuffix (bytes "h") (bytes "hey man")     ]@@ -78,17 +98,17 @@     ]   , testGroup "dropWhileEnd"     [ testCase "A" $-        Bytes.fromAsciiString "aabbcc"+        Ascii.fromString "aabbcc"         @=?         Bytes.dropWhileEnd (== c2w 'b') (bytes "aabbccbb")     ]   , testGroup "takeWhileEnd"     [ testCase "A" $-        Bytes.fromAsciiString "bb"+        Ascii.fromString "bb"         @=?         Bytes.takeWhileEnd (== c2w 'b') (bytes "aabbccbb")     , testCase "B" $-        Bytes.fromAsciiString ""+        Ascii.fromString ""         @=?         Bytes.takeWhileEnd (/= c2w '\n') (bytes "aabbccbb\n")     , testCase "C" $@@ -147,13 +167,13 @@       ===       map Bytes.fromByteArray (Exts.toList (Bytes.splitU x (slicedPack xs)))   , testCase "splitInit-A" $-      [Bytes.fromAsciiString "hello", Bytes.fromAsciiString "world"]+      [Ascii.fromString "hello", Ascii.fromString "world"]       @=?-      (Bytes.splitInit 0x0A (Bytes.fromAsciiString "hello\nworld\n"))+      (Bytes.splitInit 0x0A (Ascii.fromString "hello\nworld\n"))   , testCase "splitInit-B" $-      [Bytes.fromAsciiString "hello", Bytes.fromAsciiString "world"]+      [Ascii.fromString "hello", Ascii.fromString "world"]       @=?-      (Bytes.splitInit 0x0A (Bytes.fromAsciiString "hello\nworld\nthere"))+      (Bytes.splitInit 0x0A (Ascii.fromString "hello\nworld\nthere"))   , testProperty "splitEnd1" $ \(x :: Word8) (xs :: [Word8]) ->       case ByteString.split x (ByteString.pack xs) of         [] -> Bytes.splitEnd1 x (slicedPack xs) === Nothing