packages feed

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 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)+