{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

{- | Row-hash kernels with a parallel driver, feeding grouping and the join
build/probe. Each row's hash depends only on its own bytes, so hashing disjoint
ranges in parallel is race-free and bit-identical to the sequential pass.
-}
module DataFrame.Internal.RowHash (
    computeRowHashesIO,
    hashRowRange,
    parRowHashThreshold,
) where

import Control.Concurrent (forkIO, getNumCapabilities)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import Control.Exception (SomeException, throwIO, try)
import qualified Data.Text as T
import Data.Type.Equality (TestEquality (..), type (:~:) (Refl))
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
import System.IO.Unsafe (unsafePerformIO)
import Type.Reflection (typeRep)

import DataFrame.Internal.Column (
    Bitmap,
    Column (..),
    bitmapTestBit,
    materializeMerged,
 )
import DataFrame.Internal.Hash (
    fnvOffset,
    mixBytes,
    mixDouble,
    mixInt,
    mixShow,
    mixText,
    nullSalt,
 )
import DataFrame.Internal.PackedText (
    PackedTextData (..),
    offAt,
    packedSlice,
 )
import DataFrame.Internal.Types (
    SBool (..),
    sFloating,
    sIntegral,
 )

{- | At least this many rows make the fork/coordination overhead of the parallel
hash worth it. Below it the sequential single range is used. Matches the
grouping/join parallel thresholds so the whole pipeline switches together.
-}
parRowHashThreshold :: Int
parRowHashThreshold :: Int
parRowHashThreshold = Int
200000

capabilities :: Int
capabilities :: Int
capabilities = IO Int -> Int
forall a. IO a -> a
unsafePerformIO IO Int
getNumCapabilities
{-# NOINLINE capabilities #-}

{- | Compute the per-row key hash over the selected key columns of an @n@-row
frame. Forks one worker per capability over disjoint row ranges when the row
count justifies it, else hashes the single full range; output is capability-independent.
-}
computeRowHashesIO :: Int -> [Column] -> IO (VU.Vector Int)
computeRowHashesIO :: Int -> [Column] -> IO (Vector Int)
computeRowHashesIO Int
n [Column]
selected = do
    IOVector Int
mv <- Int -> IO (MVector (PrimState IO) Int)
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
Int -> m (MVector (PrimState m) a)
VUM.unsafeNew (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
n)
    let runRange :: Int -> Int -> IO ()
runRange Int
lo Int
hi = IOVector Int -> Int -> Int -> [Column] -> IO ()
hashRowRange IOVector Int
mv Int
lo Int
hi [Column]
selected
    if Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
parRowHashThreshold Bool -> Bool -> Bool
&& Int
capabilities Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
1
        then do
            let !caps :: Int
caps = Int
capabilities
                !per :: Int
per = (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
caps Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
caps
                spawn :: Int -> IO (MVar (Either SomeException ()))
spawn Int
w = do
                    MVar (Either SomeException ())
var <- IO (MVar (Either SomeException ()))
forall a. IO (MVar a)
newEmptyMVar
                    let !lo :: Int
lo = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
n (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
per)
                        !hi :: Int
hi = Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
n (Int
lo Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
per)
                    ThreadId
_ <- IO () -> IO ThreadId
forkIO (IO () -> IO (Either SomeException ())
forall e a. Exception e => IO a -> IO (Either e a)
try (Int -> Int -> IO ()
runRange Int
lo Int
hi) IO (Either SomeException ())
-> (Either SomeException () -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= MVar (Either SomeException ()) -> Either SomeException () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Either SomeException ())
var)
                    MVar (Either SomeException ())
-> IO (MVar (Either SomeException ()))
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure MVar (Either SomeException ())
var
            [MVar (Either SomeException ())]
vars <- (Int -> IO (MVar (Either SomeException ())))
-> [Int] -> IO [MVar (Either SomeException ())]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Int -> IO (MVar (Either SomeException ()))
spawn [Int
0 .. Int
caps Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
            [Either SomeException ()]
rs <- (MVar (Either SomeException ()) -> IO (Either SomeException ()))
-> [MVar (Either SomeException ())] -> IO [Either SomeException ()]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM MVar (Either SomeException ()) -> IO (Either SomeException ())
forall a. MVar a -> IO a
takeMVar [MVar (Either SomeException ())]
vars
            (Either SomeException () -> IO ())
-> [Either SomeException ()] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ((SomeException -> IO ())
-> (() -> IO ()) -> Either SomeException () -> IO ()
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall e a. Exception e => e -> IO a
throwIO @SomeException) () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure) [Either SomeException ()]
rs
        else Int -> Int -> IO ()
runRange Int
0 Int
n
    MVector (PrimState IO) Int -> IO (Vector Int)
forall a (m :: * -> *).
(Unbox a, PrimMonad m) =>
MVector (PrimState m) a -> m (Vector a)
VU.unsafeFreeze (Int -> Int -> IOVector Int -> IOVector Int
forall a s. Unbox a => Int -> Int -> MVector s a -> MVector s a
VUM.slice Int
0 Int
n IOVector Int
mv)

{- | Mix every selected column over the row range @[lo, hi)@ into @mv@, seeding
each slot with 'fnvOffset'. Must match the sequential grouping hash byte-for-byte
so grouping and joins bucket identically.
-}
hashRowRange :: VUM.IOVector Int -> Int -> Int -> [Column] -> IO ()
hashRowRange :: IOVector Int -> Int -> Int -> [Column] -> IO ()
hashRowRange IOVector Int
mv Int
lo Int
hi [Column]
cols = do
    IOVector Int -> Int -> Int -> IO ()
seedRange IOVector Int
mv Int
lo Int
hi
    (Column -> IO ()) -> [Column] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (IOVector Int -> Int -> Int -> Column -> IO ()
mixColumnRange IOVector Int
mv Int
lo Int
hi) [Column]
cols

seedRange :: VUM.IOVector Int -> Int -> Int -> IO ()
seedRange :: IOVector Int -> Int -> Int -> IO ()
seedRange IOVector Int
mv Int
lo Int
hi = Int -> IO ()
go Int
lo
  where
    go :: Int -> IO ()
go !Int
i
        | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
hi = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        | Bool
otherwise = MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
mv Int
i Int
fnvOffset IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int -> IO ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)

{- | Fold one column's values over @[lo, hi)@ into the running hashes. The branch
structure mirrors the sequential grouping hash: typed unboxed fast paths, then a
'mixShow' fallback, with the null bitmap mixing 'nullSalt'.
-}
mixColumnRange :: VUM.IOVector Int -> Int -> Int -> Column -> IO ()
mixColumnRange :: IOVector Int -> Int -> Int -> Column -> IO ()
mixColumnRange IOVector Int
mv Int
lo Int
hi = \case
    c :: Column
c@(MergedColumn Column
_ Column
_) -> IOVector Int -> Int -> Int -> Column -> IO ()
mixColumnRange IOVector Int
mv Int
lo Int
hi (Column -> Column
materializeMerged Column
c)
    UnboxedColumn Maybe Bitmap
ubm (Vector a
v :: VU.Vector a) ->
        case TypeRep a -> TypeRep Int -> Maybe (a :~: Int)
forall a b. TypeRep a -> TypeRep b -> Maybe (a :~: b)
forall {k} (f :: k -> *) (a :: k) (b :: k).
TestEquality f =>
f a -> f b -> Maybe (a :~: b)
testEquality (forall a. Typeable a => TypeRep a
forall {k} (a :: k). Typeable a => TypeRep a
typeRep @a) (forall a. Typeable a => TypeRep a
forall {k} (a :: k). Typeable a => TypeRep a
typeRep @Int) of
            Just a :~: Int
Refl -> IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> Int -> Int)
-> Vector Int
-> IO ()
forall a.
Unbox a =>
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
unboxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
ubm Int -> Int -> Int
mixInt Vector a
Vector Int
v
            Maybe (a :~: Int)
Nothing ->
                case TypeRep a -> TypeRep Double -> Maybe (a :~: Double)
forall a b. TypeRep a -> TypeRep b -> Maybe (a :~: b)
forall {k} (f :: k -> *) (a :: k) (b :: k).
TestEquality f =>
f a -> f b -> Maybe (a :~: b)
testEquality (forall a. Typeable a => TypeRep a
forall {k} (a :: k). Typeable a => TypeRep a
typeRep @a) (forall a. Typeable a => TypeRep a
forall {k} (a :: k). Typeable a => TypeRep a
typeRep @Double) of
                    Just a :~: Double
Refl -> IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> Double -> Int)
-> Vector Double
-> IO ()
forall a.
Unbox a =>
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
unboxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
ubm Int -> Double -> Int
mixDouble Vector a
Vector Double
v
                    Maybe (a :~: Double)
Nothing ->
                        case forall a. SBoolI (IntegralTypes a) => SBool (IntegralTypes a)
sIntegral @a of
                            SBool (IntegralTypes a)
STrue ->
                                IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
forall a.
Unbox a =>
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
unboxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
ubm (\Int
h a
d -> Int -> Int -> Int
mixInt Int
h (forall a b. (Integral a, Num b) => a -> b
fromIntegral @a @Int a
d)) Vector a
v
                            SBool (IntegralTypes a)
SFalse ->
                                case forall a. SBoolI (FloatingTypes a) => SBool (FloatingTypes a)
sFloating @a of
                                    SBool (FloatingTypes a)
STrue ->
                                        IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
forall a.
Unbox a =>
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
unboxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
ubm (\Int
h a
d -> Int -> Double -> Int
mixDouble Int
h (a -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac a
d :: Double)) Vector a
v
                                    SBool (FloatingTypes a)
SFalse ->
                                        IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
forall a.
Unbox a =>
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
unboxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
ubm Int -> a -> Int
forall a. Show a => Int -> a -> Int
mixShow Vector a
v
    BoxedColumn Maybe Bitmap
bm (Vector a
v :: V.Vector a) ->
        case TypeRep a -> TypeRep Text -> Maybe (a :~: Text)
forall a b. TypeRep a -> TypeRep b -> Maybe (a :~: b)
forall {k} (f :: k -> *) (a :: k) (b :: k).
TestEquality f =>
f a -> f b -> Maybe (a :~: b)
testEquality (forall a. Typeable a => TypeRep a
forall {k} (a :: k). Typeable a => TypeRep a
typeRep @a) (forall a. Typeable a => TypeRep a
forall {k} (a :: k). Typeable a => TypeRep a
typeRep @T.Text) of
            Just a :~: Text
Refl -> IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> Text -> Int)
-> Vector Text
-> IO ()
forall a.
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
boxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
bm Int -> Text -> Int
mixText Vector a
Vector Text
v
            Maybe (a :~: Text)
Nothing -> IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
forall a.
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
boxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
bm Int -> a -> Int
forall a. Show a => Int -> a -> Int
mixShow Vector a
v
    PackedText Maybe Bitmap
bm PackedTextData
p -> IOVector Int
-> Int -> Int -> Maybe Bitmap -> PackedTextData -> IO ()
packedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
bm PackedTextData
p

{- | Mix an unboxed column's range, mixing 'nullSalt' at null slots. @INLINE@d to
specialise on the element type and mixing function per call site.
-}
unboxedRange ::
    (VU.Unbox a) =>
    VUM.IOVector Int ->
    Int ->
    Int ->
    Maybe Bitmap ->
    (Int -> a -> Int) ->
    VU.Vector a ->
    IO ()
unboxedRange :: forall a.
Unbox a =>
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
unboxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
ubm Int -> a -> Int
mix Vector a
v = Int -> IO ()
go Int
lo
  where
    go :: Int -> IO ()
go !Int
i
        | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
hi = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        | Bool
otherwise = do
            Int
h <- MVector (PrimState IO) Int -> Int -> IO Int
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m a
VUM.unsafeRead IOVector Int
MVector (PrimState IO) Int
mv Int
i
            let !h' :: Int
h' = case Maybe Bitmap
ubm of
                    Just Bitmap
bm | Bool -> Bool
not (Bitmap -> Int -> Bool
bitmapTestBit Bitmap
bm Int
i) -> Int -> Int -> Int
mixInt Int
h Int
nullSalt
                    Maybe Bitmap
_ -> Int -> a -> Int
mix Int
h (Vector a -> Int -> a
forall a. Unbox a => Vector a -> Int -> a
VU.unsafeIndex Vector a
v Int
i)
            MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
mv Int
i Int
h'
            Int -> IO ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
{-# INLINE unboxedRange #-}

boxedRange ::
    VUM.IOVector Int ->
    Int ->
    Int ->
    Maybe Bitmap ->
    (Int -> a -> Int) ->
    V.Vector a ->
    IO ()
boxedRange :: forall a.
IOVector Int
-> Int
-> Int
-> Maybe Bitmap
-> (Int -> a -> Int)
-> Vector a
-> IO ()
boxedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
bm Int -> a -> Int
mix Vector a
v = Int -> IO ()
go Int
lo
  where
    go :: Int -> IO ()
go !Int
i
        | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
hi = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
        | Bool
otherwise = do
            Int
h <- MVector (PrimState IO) Int -> Int -> IO Int
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m a
VUM.unsafeRead IOVector Int
MVector (PrimState IO) Int
mv Int
i
            let !h' :: Int
h' = case Maybe Bitmap
bm of
                    Just Bitmap
bm' | Bool -> Bool
not (Bitmap -> Int -> Bool
bitmapTestBit Bitmap
bm' Int
i) -> Int -> Int -> Int
mixInt Int
h Int
nullSalt
                    Maybe Bitmap
_ -> Int -> a -> Int
mix Int
h (Vector a -> Int -> a
forall a. Vector a -> Int -> a
V.unsafeIndex Vector a
v Int
i)
            MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
mv Int
i Int
h'
            Int -> IO ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
{-# INLINE boxedRange #-}

{- | Mix a packed-text column's range over its raw UTF-8 byte slices. The
unselected payload is the hot path (indexes the offset vector directly); a
selected payload (a gather/join result) falls back to 'packedSlice'.
-}
packedRange ::
    VUM.IOVector Int ->
    Int ->
    Int ->
    Maybe Bitmap ->
    PackedTextData ->
    IO ()
packedRange :: IOVector Int
-> Int -> Int -> Maybe Bitmap -> PackedTextData -> IO ()
packedRange IOVector Int
mv Int
lo Int
hi Maybe Bitmap
bm PackedTextData
p =
    case PackedTextData -> Maybe PackedSel
ptSel PackedTextData
p of
        Maybe PackedSel
Nothing -> Array -> PackedOffsets -> IO ()
contiguous (PackedTextData -> Array
ptBytes PackedTextData
p) (PackedTextData -> PackedOffsets
ptOffsets PackedTextData
p)
        Just PackedSel
_ -> IO ()
selected
  where
    valid :: Int -> Bool
valid Int
i = case Maybe Bitmap
bm of
        Just Bitmap
bm' -> Bitmap -> Int -> Bool
bitmapTestBit Bitmap
bm' Int
i
        Maybe Bitmap
Nothing -> Bool
True
    contiguous :: Array -> PackedOffsets -> IO ()
contiguous !Array
arr !PackedOffsets
offs = Int -> IO ()
go Int
lo
      where
        go :: Int -> IO ()
go !Int
i
            | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
hi = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
            | Bool
otherwise = do
                Int
h <- MVector (PrimState IO) Int -> Int -> IO Int
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m a
VUM.unsafeRead IOVector Int
MVector (PrimState IO) Int
mv Int
i
                let !o :: Int
o = PackedOffsets -> Int -> Int
offAt PackedOffsets
offs Int
i
                    !l :: Int
l = PackedOffsets -> Int -> Int
offAt PackedOffsets
offs (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
o
                    !h' :: Int
h' = if Int -> Bool
valid Int
i then Int -> Array -> Int -> Int -> Int
mixBytes Int
h Array
arr Int
o Int
l else Int -> Int -> Int
mixInt Int
h Int
nullSalt
                MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
mv Int
i Int
h'
                Int -> IO ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
    selected :: IO ()
selected = Int -> IO ()
go Int
lo
      where
        go :: Int -> IO ()
go !Int
i
            | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
hi = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
            | Bool
otherwise = do
                Int
h <- MVector (PrimState IO) Int -> Int -> IO Int
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> m a
VUM.unsafeRead IOVector Int
MVector (PrimState IO) Int
mv Int
i
                let !h' :: Int
h' =
                        if Int -> Bool
valid Int
i
                            then let (Array
arr, Int
o, Int
l) = PackedTextData -> Int -> (Array, Int, Int)
packedSlice PackedTextData
p Int
i in Int -> Array -> Int -> Int -> Int
mixBytes Int
h Array
arr Int
o Int
l
                            else Int -> Int -> Int
mixInt Int
h Int
nullSalt
                MVector (PrimState IO) Int -> Int -> Int -> IO ()
forall (m :: * -> *) a.
(PrimMonad m, Unbox a) =>
MVector (PrimState m) a -> Int -> a -> m ()
VUM.unsafeWrite IOVector Int
MVector (PrimState IO) Int
mv Int
i Int
h'
                Int -> IO ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
{-# INLINE packedRange #-}