byteslice 0.1.2.0 → 0.1.3.0
raw patch · 5 files changed
+156/−6 lines, 5 filesdep +byteslicedep +tastydep +tasty-hunitdep ~basedep ~primitivePVP ok
version bump matches the API change (PVP)
Dependencies added: byteslice, tasty, tasty-hunit, tasty-quickcheck
Dependency ranges changed: base, primitive
API changes (from Hackage documentation)
+ Data.Bytes: foldl :: (a -> Word8 -> a) -> a -> Bytes -> a
+ Data.Bytes: foldl' :: (a -> Word8 -> a) -> a -> Bytes -> a
+ Data.Bytes: foldr :: (Word8 -> a -> a) -> a -> Bytes -> a
+ Data.Bytes: foldr' :: (Word8 -> a -> a) -> a -> Bytes -> a
+ Data.Bytes: isPrefixOf :: Bytes -> Bytes -> Bool
+ Data.Bytes: isSuffixOf :: Bytes -> Bytes -> Bool
Files
- CHANGELOG.md +5/−0
- byteslice.cabal +16/−2
- src/Data/Bytes.hs +71/−2
- src/Data/Bytes/Types.hs +0/−2
- test/Main.hs +64/−0
CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for byteslice +## 0.1.3.0 -- 2019-09-15++* Add `isPrefixOf` and `isSuffixOf`.+* Add `foldl`, `foldr`, `foldl'`, and `foldr'`.+ ## 0.1.2.0 -- 2019-08-21 * Add `Data.Bytes.Mutable` module.
byteslice.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: byteslice-version: 0.1.2.0+version: 0.1.3.0 synopsis: Slicing managed and unmanaged memory description: This library provides types that allow the user to talk about a slice of@@ -23,10 +23,24 @@ Data.Bytes.Mutable Data.Bytes.Types build-depends:- , base >=4.11.1.0 && <5+ , base >=4.11.1 && <5 , primitive >=0.7 && <0.8 , primitive-addr >=0.1 && <0.2 , run-st >=0.1 && <0.2 hs-source-dirs: src ghc-options: -Wall -O2 default-language: Haskell2010++test-suite test+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ ghc-options: -Wall -O2+ build-depends:+ , base >=4.11.1 && <5+ , byteslice+ , primitive+ , tasty-hunit+ , tasty+ , tasty-quickcheck
src/Data/Bytes.hs view
@@ -1,4 +1,5 @@ {-# language BangPatterns #-}+{-# language MagicHash #-} {-# language TypeApplications #-} module Data.Bytes@@ -10,6 +11,14 @@ -- * Filtering , takeWhile , dropWhile+ -- * Folds+ , foldl+ , foldl'+ , foldr+ , foldr'+ -- * Equality+ , isPrefixOf+ , isSuffixOf -- * Unsafe Slicing , unsafeTake , unsafeDrop@@ -20,13 +29,14 @@ , fromByteArray ) where -import Prelude hiding (length,takeWhile,dropWhile,null)+import Prelude hiding (length,takeWhile,dropWhile,null,foldl,foldr) import Data.Bytes.Types (Bytes(Bytes))-import Data.Primitive (ByteArray)+import Data.Primitive (ByteArray(ByteArray)) import Data.Word (Word8) import Data.Char (ord) import Control.Monad.ST.Run (runByteArrayST)+import GHC.Exts (Int(I#)) import qualified Data.Primitive as PM import qualified GHC.Exts as Exts@@ -39,6 +49,25 @@ length :: Bytes -> Int length (Bytes _ _ len) = len +-- | Is the first argument a prefix of the second argument?+isPrefixOf :: Bytes -> Bytes -> Bool+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 + -- compares a byte array with the same in-memory+ -- byte array.+ if aLen <= bLen+ then compareByteArrays a aOff b bOff aLen == EQ+ else False++-- | Is the first argument a suffix of the second argument?+isSuffixOf :: Bytes -> Bytes -> Bool+isSuffixOf (Bytes a aOff aLen) (Bytes b bOff bLen) =+ if aLen <= bLen+ then compareByteArrays a aOff b (bOff + bLen - aLen) aLen == EQ+ else False+ -- | Take bytes while the predicate is true. takeWhile :: (Word8 -> Bool) -> Bytes -> Bytes {-# inline takeWhile #-}@@ -74,6 +103,42 @@ 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.+foldl' :: (a -> Word8 -> a) -> a -> Bytes -> a+{-# 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)++-- | 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 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.@@ -101,3 +166,7 @@ fromByteArray :: ByteArray -> Bytes fromByteArray b = Bytes b 0 (PM.sizeofByteArray b) +compareByteArrays :: ByteArray -> Int -> ByteArray -> Int -> Int -> Ordering+{-# INLINE compareByteArrays #-}+compareByteArrays (ByteArray ba1#) (I# off1#) (ByteArray ba2#) (I# off2#) (I# n#) =+ compare (I# (Exts.compareByteArrays# ba1# off1# ba2# off2# n#)) 0
src/Data/Bytes/Types.hs view
@@ -76,14 +76,12 @@ else unsafeChr (ord 'a' + (fromIntegral w) - 10) instance Eq Bytes where- {-# INLINE (==) #-} 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- {-# INLINE compare #-} compare (Bytes arr1 off1 len1) (Bytes arr2 off2 len2) | sameByteArray arr1 arr2 && off1 == off2 && len1 == len2 = EQ | otherwise = compareByteArrays arr1 off1 arr2 off2 len1
+ test/Main.hs view
@@ -0,0 +1,64 @@+{-# language BangPatterns #-}+{-# language MultiWayIf #-}+{-# language NumDecimals #-}+{-# language OverloadedStrings #-}+{-# language ScopedTypeVariables #-}+{-# language TypeApplications #-}++import Data.Primitive (ByteArray)+import Data.Word (Word8)+import Data.Char (ord)+import Data.Bytes.Types (Bytes(Bytes))+import Test.Tasty (defaultMain,testGroup,TestTree)+import Test.Tasty.HUnit ((@=?),testCase)+import Test.Tasty.QuickCheck ((===),testProperty)++import qualified Data.Bytes as Bytes+import qualified Data.Foldable as Foldable+import qualified Data.List as List+import qualified Data.Primitive as PM+import qualified GHC.Exts as Exts+import qualified Test.Tasty.HUnit as THU+import qualified Test.Tasty.QuickCheck as QC++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Bytes"+ [ testGroup "isPrefixOf"+ [ testCase "A" $ THU.assertBool "" $+ Bytes.isPrefixOf (bytes "hey") (bytes "hey man")+ , 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"))+ ]+ , testProperty "foldl" $ \(x :: Word8) (xs :: [Word8]) ->+ List.foldl (-) 0 xs+ ===+ Bytes.foldl (-) 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)))+ ]++bytes :: String -> Bytes+bytes s = let b = pack ('x' : s) in Bytes b 1 (PM.sizeofByteArray b - 1)++pack :: String -> ByteArray+pack = Exts.fromList . map (fromIntegral @Int @Word8 . ord)+