packages feed

byteslice-0.2.15.0: src/Data/Bytes/Mutable.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}

{- | If you are interested in sub-arrays of 'MutableByteArray's (e.g. writing
quicksort), it would be grossly inefficient to make a copy of the sub-array.
On the other hand, it'd be really annoying to track limit indices by hand.

This module defines the 'MutableBytes' type which exposes a standard array
interface for a sub-arrays without copying and without manual index
manipulation. For immutable arrays, see 'Data.Bytes'.
-}
module Data.Bytes.Mutable
  ( -- * Types
    MutableBytes

    -- * Filtering
  , takeWhile
  , dropWhile

    -- * Unsafe Slicing
  , unsafeTake
  , unsafeDrop

    -- * Conversion
  , fromMutableByteArray
  ) where

import Prelude hiding (dropWhile, takeWhile)

import Control.Monad.Primitive (PrimMonad, PrimState)
import Data.Bytes.Types (MutableBytes (MutableBytes))
import Data.Primitive (MutableByteArray)
import Data.Word (Word8)

import qualified Data.Primitive as PM

{- | Take bytes while the predicate is true, aliasing the
argument array.
-}
takeWhile ::
  (PrimMonad m) =>
  (Word8 -> m Bool) ->
  MutableBytes (PrimState m) ->
  m (MutableBytes (PrimState m))
{-# INLINE takeWhile #-}
takeWhile k b = do
  n <- countWhile k b
  pure (unsafeTake n b)

{- | Drop bytes while the predicate is true, aliasing the
argument array.
-}
dropWhile ::
  (PrimMonad m) =>
  (Word8 -> m Bool) ->
  MutableBytes (PrimState m) ->
  m (MutableBytes (PrimState m))
{-# INLINE dropWhile #-}
dropWhile k b = do
  n <- countWhile k b
  pure (unsafeDrop n b)

-- | Take the first @n@ bytes from the argument, aliasing it.
unsafeTake :: Int -> MutableBytes s -> MutableBytes s
{-# INLINE unsafeTake #-}
unsafeTake n (MutableBytes arr off _) =
  MutableBytes arr off n

{- | Drop the first @n@ bytes from the argument, aliasing it.
The new length will be @len - n@.
-}
unsafeDrop :: Int -> MutableBytes s -> MutableBytes s
{-# INLINE unsafeDrop #-}
unsafeDrop n (MutableBytes arr off len) =
  MutableBytes arr (off + n) (len - n)

{- | Create a slice of 'MutableBytes' that spans the entire
argument array. This aliases the argument.
-}
fromMutableByteArray ::
  (PrimMonad m) =>
  MutableByteArray (PrimState m) ->
  m (MutableBytes (PrimState m))
{-# INLINE fromMutableByteArray #-}
fromMutableByteArray mba = do
  sz <- PM.getSizeofMutableByteArray mba
  pure (MutableBytes mba 0 sz)

-- 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 ::
  (PrimMonad m) =>
  (Word8 -> m Bool) ->
  MutableBytes (PrimState m) ->
  m Int
{-# INLINE countWhile #-}
countWhile k (MutableBytes arr off0 len0) = go off0 len0 0
 where
  go !off !len !n =
    if len > 0
      then
        (k =<< PM.readByteArray arr off) >>= \case
          True -> go (off + 1) (len - 1) (n + 1)
          False -> pure n
      else pure n