byteslice 0.2.13.2 → 0.2.14.0
raw patch · 5 files changed
+219/−103 lines, 5 filesdep ~primitive-unliftednew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: primitive-unlifted
API changes (from Hackage documentation)
+ Data.Bytes.Text.AsciiExt: anyEq :: Char -> Bytes -> Bool
+ Data.Bytes.Text.AsciiExt: dropWhileEndEq :: Char -> Bytes -> Bytes
+ Data.Bytes.Text.AsciiExt: dropWhileNotEq :: Char -> Bytes -> Bytes
+ Data.Bytes.Text.AsciiExt: split1 :: Char -> Bytes -> Maybe (Bytes, Bytes)
+ Data.Bytes.Text.AsciiExt: split2 :: Char -> Bytes -> Maybe (Bytes, Bytes, Bytes)
+ Data.Bytes.Text.AsciiExt: split3 :: Char -> Bytes -> Maybe (Bytes, Bytes, Bytes, Bytes)
+ Data.Bytes.Text.AsciiExt: split4 :: Char -> Bytes -> Maybe (Bytes, Bytes, Bytes, Bytes, Bytes)
+ Data.Bytes.Text.AsciiExt: splitTetragram1 :: Char -> Char -> Char -> Char -> Bytes -> Maybe (Bytes, Bytes)
+ Data.Bytes.Text.AsciiExt: takeWhileEndNotEq :: Char -> Bytes -> Bytes
+ Data.Bytes.Text.AsciiExt: takeWhileNotEq :: Char -> Bytes -> Bytes
+ Data.Bytes.Text.Utf8: toText :: Bytes -> Maybe Text
Files
- CHANGELOG.md +7/−0
- byteslice.cabal +2/−2
- src/Data/Bytes.hs +10/−100
- src/Data/Bytes/Pure.hs +98/−1
- src/Data/Bytes/Text/AsciiExt.hs +102/−0
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for byteslice +## 0.2.14.0 -- 2024-02-26++* Add functions to `Data.Bytes.Text.AsciiExt`: `split(1|2|3|4)`,+ `splitTetragram1`, `anyEq`, `takeWhileNotEq`, `dropWhileNotEq`,+ `takeWhileEndNotEq`, dropWhileEndEq`.+* Increment upper bound on `primitive-unlifted`+ ## 0.2.13.2 -- 2024-02-06 * Restore `Data.Bytes.Text.Utf8.toText`.
byteslice.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: byteslice-version: 0.2.13.2+version: 0.2.14.0 synopsis: Slicing managed and unmanaged memory description: This library provides types that allow the user to talk about a slice of@@ -61,7 +61,7 @@ , natural-arithmetic >=0.1.4 , primitive >=0.7.4 && <0.10 , primitive-addr >=0.1 && <0.2- , primitive-unlifted >=0.1.2 && <2.2+ , primitive-unlifted >=0.1.2 && <2.3 , run-st >=0.1.1 && <0.2 , text >=1.2.5 , text-short >=0.1.3 && <0.2
src/Data/Bytes.hs view
@@ -34,8 +34,8 @@ , unsnoc -- * Predicates- , any- , all+ , Pure.any+ , Pure.all -- * Create @@ -89,7 +89,7 @@ -- ** Fixed from Beginning , Byte.split1- , splitTetragram1+ , Pure.splitTetragram1 , Byte.split2 , Byte.split3 , Byte.split4@@ -106,7 +106,7 @@ -- * Searching , replace , findIndices- , findTetragramIndex+ , Pure.findTetragramIndex -- * Counting , Byte.count@@ -207,9 +207,8 @@ 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 (foldr, fromByteArray, length, toShortByteString, unsafeDrop, unsafeIndex)+import Data.Bytes.Pure (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))@@ -217,7 +216,7 @@ import Foreign.C.String (CString) import Foreign.Ptr (Ptr, castPtr, plusPtr) import GHC.Exts (Addr#, Int (I#), Int#, Ptr (Ptr), Word#)-import GHC.Word (Word32, Word8 (W8#))+import GHC.Word (Word8 (W8#)) import Reps (Bytes# (..), word8ToWord#) import qualified Arithmetic.Nat as Nat@@ -402,12 +401,12 @@ -- | Take bytes while the predicate is true. takeWhile :: (Word8 -> Bool) -> Bytes -> Bytes {-# INLINE takeWhile #-}-takeWhile k b = Pure.unsafeTake (countWhile k b) b+takeWhile k b = Pure.unsafeTake (Pure.countWhile k b) b -- | Drop bytes while the predicate is true. dropWhile :: (Word8 -> Bool) -> Bytes -> Bytes {-# INLINE dropWhile #-}-dropWhile k b = Pure.unsafeDrop (countWhile k b) b+dropWhile k b = Pure.unsafeDrop (Pure.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@@ -415,7 +414,7 @@ -} dropWhileEnd :: (Word8 -> Bool) -> Bytes -> Bytes {-# INLINE dropWhileEnd #-}-dropWhileEnd k !b = Pure.unsafeTake (length b - countWhileEnd k b) b+dropWhileEnd k !b = Pure.unsafeTake (length b - Pure.countWhileEnd k b) b {- | /O(n)/ 'takeWhileEnd' @p@ @b@ returns the longest suffix of elements that satisfy predicate @p@.@@ -423,39 +422,9 @@ takeWhileEnd :: (Word8 -> Bool) -> Bytes -> Bytes {-# INLINE takeWhileEnd #-} takeWhileEnd k !b =- let n = countWhileEnd k b+ let n = Pure.countWhileEnd k b in Bytes (array b) (offset b + length b - n) n --- Internal. The returns the number of bytes that match the--- predicate until the first non-match occurs. If all bytes--- 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)- 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)- 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@.@@ -694,16 +663,6 @@ 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 #-}-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 #-}-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'.@@ -786,52 +745,3 @@ 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--splitTetragram1 ::- 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/Pure.hs view
@@ -40,12 +40,19 @@ , toShortByteString , replicate , replicateU+ , splitTetragram1+ , findTetragramIndex+ , countWhile+ , countWhileEnd+ , any+ , all ) where -import Prelude hiding (Foldable (..), map, replicate)+import Prelude hiding (Foldable (..), map, replicate, any, all) import Control.Monad.Primitive (PrimMonad, PrimState) import Control.Monad.ST.Run (runByteArrayST)+import Data.Bits (unsafeShiftL, (.|.)) import Data.Bits (xor) import Data.ByteString (ByteString) import Data.ByteString.Short.Internal (ShortByteString (SBS))@@ -402,3 +409,93 @@ arr <- PM.newByteArray n PM.setByteArray arr 0 n w PM.unsafeFreezeByteArray arr++splitTetragram1 ::+ 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 (unsafeTake n b, unsafeDrop (n + 4) b)++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++-- Internal. The returns the number of bytes that match the+-- predicate until the first non-match occurs. If all bytes+-- 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)+ 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)+ else n+ else n++-- | /O(n)/ Returns true if any byte in the sequence satisfies the predicate.+any :: (Word8 -> Bool) -> Bytes -> Bool+{-# 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 #-}+all f = foldr (\b r -> f b && r) True+
src/Data/Bytes/Text/AsciiExt.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeApplications #-} -- For functions that can fail for bytes outside the ASCII range, see -- 'Data.Bytes.Ascii'. For functions that can inspect bytes outside ASCII, see@@ -19,6 +20,23 @@ , forLines_ , foldLines + -- * Predicates+ , anyEq++ -- * Filtering+ , takeWhileNotEq+ , dropWhileNotEq+ , takeWhileEndNotEq+ , dropWhileEndEq++ -- * Splitting+ -- ** Fixed from Beginning+ , split1+ , splitTetragram1+ , split2+ , split3+ , split4+ -- * Text Manipulation , toLowerU ) where@@ -26,12 +44,14 @@ import Control.Monad.ST (ST) import Control.Monad.ST.Run (runByteArrayST) import Data.Bytes.Types (Bytes (..))+import Data.Char (ord) import Data.Primitive (ByteArray) import Data.Word (Word8) import System.IO (Handle, hIsEOF, stdin) import qualified Data.ByteString.Char8 as BC8 import qualified Data.Bytes.Pure as Bytes+import qualified Data.Bytes.Byte as Byte import qualified Data.Primitive as PM -- | `hForLines_` over `stdin`@@ -104,3 +124,85 @@ go (off + 1) (ix + 1) (len - 1) go off0 0 len0 PM.unsafeFreezeByteArray dst++-- | Throws an exception the 'Char' argument is non-ascii.+split1 :: Char -> Bytes -> Maybe (Bytes, Bytes)+{-# INLINE split1 #-}+split1 !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.split1: argument not in ASCII range"+ | otherwise = Byte.split1 (c2w c) b++-- | Throws an exception the 'Char' argument is non-ascii.+split2 :: Char -> Bytes -> Maybe (Bytes, Bytes, Bytes)+{-# INLINE split2 #-}+split2 !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.split2: argument not in ASCII range"+ | otherwise = Byte.split2 (c2w c) b++-- | Throws an exception the 'Char' argument is non-ascii.+split3 :: Char -> Bytes -> Maybe (Bytes, Bytes, Bytes, Bytes)+{-# INLINE split3 #-}+split3 !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.split3: argument not in ASCII range"+ | otherwise = Byte.split3 (c2w c) b++-- | Throws an exception the 'Char' argument is non-ascii.+split4 :: Char -> Bytes -> Maybe (Bytes, Bytes, Bytes, Bytes, Bytes)+{-# INLINE split4 #-}+split4 !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.split4: argument not in ASCII range"+ | otherwise = Byte.split4 (c2w c) b++-- | Throws an exception if any of the 'Char' arguments are non-ascii.+splitTetragram1 :: Char -> Char -> Char -> Char -> Bytes -> Maybe (Bytes, Bytes)+{-# inline splitTetragram1 #-}+splitTetragram1 !c0 !c1 !c2 !c3 !b+ | c0 > '\DEL' || c1 > '\DEL' || c2 > '\DEL' || c3 > '\DEL' =+ errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.splitTetragram1: one of the characters is not in ASCII range"+ | otherwise = Bytes.splitTetragram1 (c2w c0) (c2w c1) (c2w c2) (c2w c3) b++c2w :: Char -> Word8+{-# inline c2w #-}+c2w !c = fromIntegral @Int @Word8 (ord c)++-- | Throws an exception the 'Char' argument is non-ascii.+dropWhileNotEq :: Char -> Bytes -> Bytes+dropWhileNotEq !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.dropWhileNotEq: argument not in ASCII range"+ | otherwise =+ let !w = c2w c+ in Bytes.unsafeDrop (Bytes.countWhile (/= w) b) b++-- | Throws an exception the 'Char' argument is non-ascii.+takeWhileNotEq :: Char -> Bytes -> Bytes+takeWhileNotEq !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.takeWhileNotEq: argument not in ASCII range"+ | otherwise =+ let !w = c2w c+ in Bytes.unsafeTake (Bytes.countWhile (/= w) b) b+ +-- | Throws an exception the 'Char' argument is non-ascii.+takeWhileEndNotEq :: Char -> Bytes -> Bytes+takeWhileEndNotEq !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.takeWhileEndNotEq: argument not in ASCII range"+ | otherwise =+ let !w = c2w c+ !n = Bytes.countWhileEnd (/=w) b+ in Bytes (array b) (offset b + Bytes.length b - n) n++-- | Throws an exception the 'Char' argument is non-ascii.+dropWhileEndEq :: Char -> Bytes -> Bytes+dropWhileEndEq !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.dropWhileEndEq: argument not in ASCII range"+ | otherwise =+ let !w = c2w c+ !n = Bytes.countWhileEnd (==w) b+ in Bytes.unsafeTake (Bytes.length b - n) b++-- | Throws an exception the 'Char' argument is non-ascii.+anyEq :: Char -> Bytes -> Bool+anyEq !c !b+ | c > '\DEL' = errorWithoutStackTrace "Data.Bytes.Text.AsciiExt.takeWhileNotEq: argument not in ASCII range"+ | otherwise =+ let !w = c2w c+ in Bytes.any (==w) b