diff --git a/rio/ChangeLog.md b/rio/ChangeLog.md index 03e966b..cf87855 100644 --- a/rio/ChangeLog.md +++ b/rio/ChangeLog.md @@ -1,5 +1,9 @@ # Changelog for rio +## 0.1.13.0 + +* Move vector `slice` functions to `Partial` modules and add `RIO.Vector.sliceMaybe` function. + ## 0.1.12.0 * Add `logFormat` and `setLogFormat` for `LogOptions`. diff --git a/rio/src/RIO/Vector.hs b/rio/src/RIO/Vector.hs index 51cc045..efa952b 100644 --- a/rio/src/RIO/Vector.hs +++ b/rio/src/RIO/Vector.hs @@ -20,7 +20,7 @@ module RIO.Vector , (Data.Vector.Generic.!?) -- ** Extracting subvectors - , Data.Vector.Generic.slice + , sliceMaybe , Data.Vector.Generic.take , Data.Vector.Generic.drop , Data.Vector.Generic.splitAt @@ -257,3 +257,20 @@ module RIO.Vector ) where import qualified Data.Vector.Generic +import Data.Vector.Generic (Vector) +import Control.Monad (guard) + +-- | /O(1)/ Yield a slice of the vector without copying it. If the vector +-- cannot satisfy the specificed slice 'Nothing' is returned. +-- +-- @since 0.1.13.0 +sliceMaybe :: Vector v a + => Int -- ^ @i@ starting index + -> Int -- ^ @n@ length + -> v a + -> Maybe (v a) +sliceMaybe i n v = do + guard $ i >= 0 + guard $ n >= 0 + guard $ Data.Vector.Generic.length v - n >= i + pure $ Data.Vector.Generic.slice i n v diff --git a/rio/src/RIO/Vector/Boxed.hs b/rio/src/RIO/Vector/Boxed.hs index d2ff45a..09ddbf3 100644 --- a/rio/src/RIO/Vector/Boxed.hs +++ b/rio/src/RIO/Vector/Boxed.hs @@ -21,7 +21,7 @@ module RIO.Vector.Boxed , (Data.Vector.!?) -- ** Extracting subvectors - , Data.Vector.slice + , V.sliceMaybe , Data.Vector.take , Data.Vector.drop , Data.Vector.splitAt @@ -222,3 +222,4 @@ module RIO.Vector.Boxed ) where import qualified Data.Vector +import qualified RIO.Vector as V diff --git a/rio/src/RIO/Vector/Boxed/Partial.hs b/rio/src/RIO/Vector/Boxed/Partial.hs index c3663a5..18e9d5e 100644 --- a/rio/src/RIO/Vector/Boxed/Partial.hs +++ b/rio/src/RIO/Vector/Boxed/Partial.hs @@ -17,6 +17,7 @@ module RIO.Vector.Boxed.Partial -- ** Extracting subvectors , Data.Vector.init , Data.Vector.tail + , RIO.Vector.Partial.slice -- Pending -- * Modifying vectors -- ** Bulk updates @@ -62,3 +63,4 @@ module RIO.Vector.Boxed.Partial ) where import qualified Data.Vector +import qualified RIO.Vector.Partial diff --git a/rio/src/RIO/Vector/Partial.hs b/rio/src/RIO/Vector/Partial.hs index 11f7df7..241e3d0 100644 --- a/rio/src/RIO/Vector/Partial.hs +++ b/rio/src/RIO/Vector/Partial.hs @@ -17,6 +17,7 @@ module RIO.Vector.Partial -- ** Extracting subvectors , Data.Vector.Generic.init , Data.Vector.Generic.tail + , slice -- Pending -- * Modifying vectors -- ** Bulk updates @@ -62,3 +63,23 @@ module RIO.Vector.Partial ) where import qualified Data.Vector.Generic + +-- | /O(1)/ Yield a slice of the vector without copying it. The vector must +-- contain at least @i+n@ elements. +slice :: Data.Vector.Generic.Vector v a + => Int -- ^ @i@ starting index + -> Int -- ^ @n@ length + -> v a + -> v a +slice i n v = if i > 0 && n > 0 && i + n < 0 -- `i+n` overflows + -- Special case handling for cases when `i+n` overflows. This is + -- required due to . + -- Once that GHC issue is closed this function can be replaced by + -- `Data.Vector.Generic.slice`. + -- (Negative overflow is not an issue as an `Date.Vector.Generic.slice` + -- throws an exception is thrown for negative arguments.) + then error $ "slice: invalid slice (" + ++ show i ++ "," + ++ show n ++ "," + ++ show (Data.Vector.Generic.length v) ++ ")" + else Data.Vector.Generic.slice i n v diff --git a/rio/src/RIO/Vector/Storable.hs b/rio/src/RIO/Vector/Storable.hs index cb2a708..00733c3 100644 --- a/rio/src/RIO/Vector/Storable.hs +++ b/rio/src/RIO/Vector/Storable.hs @@ -22,7 +22,7 @@ module RIO.Vector.Storable , (Data.Vector.Storable.!?) -- ** Extracting subvectors - , Data.Vector.Storable.slice + , V.sliceMaybe , Data.Vector.Storable.take , Data.Vector.Storable.drop , Data.Vector.Storable.splitAt @@ -188,3 +188,4 @@ module RIO.Vector.Storable ) where import qualified Data.Vector.Storable +import qualified RIO.Vector as V diff --git a/rio/src/RIO/Vector/Storable/Partial.hs b/rio/src/RIO/Vector/Storable/Partial.hs index 3e30163..5a48082 100644 --- a/rio/src/RIO/Vector/Storable/Partial.hs +++ b/rio/src/RIO/Vector/Storable/Partial.hs @@ -17,6 +17,7 @@ module RIO.Vector.Storable.Partial -- ** Extracting subvectors , Data.Vector.Storable.init , Data.Vector.Storable.tail + , RIO.Vector.Partial.slice -- Pending -- * Modifying vectors -- ** Bulk updates @@ -60,3 +61,4 @@ module RIO.Vector.Storable.Partial ) where import qualified Data.Vector.Storable +import qualified RIO.Vector.Partial diff --git a/rio/src/RIO/Vector/Unboxed.hs b/rio/src/RIO/Vector/Unboxed.hs index 9307110..829cf6e 100644 --- a/rio/src/RIO/Vector/Unboxed.hs +++ b/rio/src/RIO/Vector/Unboxed.hs @@ -22,7 +22,7 @@ module RIO.Vector.Unboxed , (Data.Vector.Unboxed.!?) -- ** Extracting subvectors - , Data.Vector.Unboxed.slice + , V.sliceMaybe , Data.Vector.Unboxed.take , Data.Vector.Unboxed.drop , Data.Vector.Unboxed.splitAt @@ -211,3 +211,4 @@ module RIO.Vector.Unboxed ) where import qualified Data.Vector.Unboxed +import qualified RIO.Vector as V diff --git a/rio/src/RIO/Vector/Unboxed/Partial.hs b/rio/src/RIO/Vector/Unboxed/Partial.hs index 86a0a8e..b98f367 100644 --- a/rio/src/RIO/Vector/Unboxed/Partial.hs +++ b/rio/src/RIO/Vector/Unboxed/Partial.hs @@ -17,6 +17,7 @@ module RIO.Vector.Unboxed.Partial -- ** Extracting subvectors , Data.Vector.Unboxed.init , Data.Vector.Unboxed.tail + , RIO.Vector.Partial.slice -- Pending -- * Modifying vectors -- ** Bulk updates @@ -62,3 +63,4 @@ module RIO.Vector.Unboxed.Partial ) where import qualified Data.Vector.Unboxed +import qualified RIO.Vector.Partial diff --git a/rio/test/RIO/VectorSpec.hs b/rio/test/RIO/VectorSpec.hs new file mode 100644 index 0000000..c492b4c --- /dev/null +++ b/rio/test/RIO/VectorSpec.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE NoImplicitPrelude #-} +module RIO.VectorSpec where + +import Test.Hspec +import Test.Hspec.QuickCheck (prop) +import qualified Test.QuickCheck as QC +import RIO +import qualified RIO.Vector as V +import qualified RIO.Vector.Partial as V' + +spec :: Spec +spec = + describe "sliceMaybe" $ do + prop "is consistent with `slice` (pathological cases)" $ + \(QC.Large i) (QC.Large n) v + -> sliceTest i n (V.fromList v) + -- The next property is a subset of the previous one but with + -- significantly greater likelyhood of having "realistic" + -- arguments to `slice`. + prop "is consistent with `slice` (more realistic cases)" $ + \(QC.NonNegative i) (QC.NonNegative n) (QC.NonEmpty v) + -> sliceTest i n (V.fromList v) + where + sliceTest :: Int -> Int -> Vector Char -> QC.Property + sliceTest i n v = QC.withMaxSuccess 1000 $ case V.sliceMaybe i n v of + Just v' -> V'.slice i n v `shouldBe` v' + Nothing -> evaluate (V'.slice i n v) `shouldThrow` anyException