packages feed

groan-0.1.0.0: Data/Vector/Groan/Unboxed.hs

{-# LANGUAGE Strict #-}

module Data.Vector.Groan.Unboxed (
  Buffer,

  -- * Running computations that allocate to a buffer
  run,
  runPrim,

  -- * Creating buffers
  new,
  replicate,
  thaw,

  -- * Reading
  read,

  -- ** Size and capacity
  size,
  capacity,

  -- * Mutable operations

  -- ** Pop
  popBack,

  -- ** Writes
  write,

  -- *** Appends
  pushBack,

  -- * Conversion
  freeze,
  unsafeFreeze,
) where

import Control.Monad (forM_)
import Control.Monad.Primitive
import Control.Monad.ST.Strict
import Control.Monad.Trans.State.Strict
import Data.Monoid (First (..))
import Data.Primitive.MutVar
import Data.Vector qualified as V
import Data.Vector.Unboxed (Unbox)
import Data.Vector.Unboxed qualified as U
import Data.Vector.Unboxed.Mutable qualified as UM
import Prelude hiding (read, replicate)

-- | Single-threaded buffer
newtype Buffer s a = Buffer (MutVar s (BufferRep s a))

data BufferRep s a = BufferRep
  { length :: {-# UNPACK #-} !Int
  , buffer :: !(UM.MVector s a)
  }

{-# INLINE run #-}
run :: (Unbox a) => Int -> a -> (forall s. Buffer s a -> ST s ()) -> U.Vector a
run initialCapacity z fn = runST do
  buffer <- new initialCapacity z
  fn buffer
  unsafeFreeze buffer

{-# INLINE runPrim #-}
runPrim :: (Unbox a, PrimMonad m) => Int -> a -> (Buffer (PrimState m) a -> m ()) -> m (U.Vector a)
runPrim initialCapacity z fn = do
  buffer <- new initialCapacity z
  fn buffer
  unsafeFreeze buffer

-- | Create a buffer with an initial (unfilled) capacity and a value for
-- uninitialised elements
{-# INLINE new #-}
new :: forall a m. (Unbox a, PrimMonad m) => Int -> a -> m (Buffer (PrimState m) a)
new initialCapacity uninitialisedElement = do
  buffer <- UM.replicate initialCapacity uninitialisedElement
  Buffer <$> newMutVar (BufferRep 0 buffer)

-- | Create a buffer with n initial elements
{-# INLINE replicate #-}
replicate :: (Unbox a, PrimMonad m) => Int -> a -> m (Buffer (PrimState m) a)
replicate size initialElem = do
  buffer <- UM.replicate size initialElem
  Buffer <$> newMutVar (BufferRep size buffer)

-- | Create a buffer from an array, copying its elements
{-# INLINE thaw #-}
thaw :: (Unbox a, PrimMonad m) => U.Vector a -> m (Buffer (PrimState m) a)
thaw input = do
  buffer <- U.thaw input
  Buffer <$> newMutVar (BufferRep (U.length input) buffer)

{-# INLINE read #-}

-- | Read an item from a buffer
read :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> Int -> m a
read (Buffer ref) i = do
  BufferRep len buf <- readMutVar ref
  if i < len
    then UM.read buf i
    else error ("index: out of range " <> show (i, len))

{-# INLINE write #-}

-- | Write an item to a buffer
--
-- The index given must be within the buffer's existing *length* - not capacity.
write :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> Int -> a -> m ()
write (Buffer ref) i a = do
  BufferRep len buf <- readMutVar ref
  if i < len
    then UM.write buf i a
    else error ("index: out of range " <> show (i, len))

{-# INLINE pushBack #-}

-- | Push an element to end of the buffer, returning the new length
pushBack :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> a -> m Int
pushBack (Buffer ref) a = do
  BufferRep len dest <- readMutVar ref
  let capacity = UM.length dest
  if len < capacity
    then do
      UM.write dest len a
      writeMutVar ref (BufferRep (len + 1) dest)
    else do
      next <- UM.replicate (grow (capacity + 1)) a
      UM.copy (UM.slice 0 len next) dest
      UM.write next len a
      writeMutVar ref (BufferRep (len + 1) next)
  pure (len + 1)

{-# INLINE popBack #-}

-- | Pop the last element off of the buffer
popBack :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> m (Maybe (a, Int))
popBack (Buffer ref) = do
  BufferRep len buf <- readMutVar ref
  let i = len - 1
  if i >= 0
    then do
      writeMutVar ref (BufferRep i buf)
      el <- UM.read buf i
      pure (Just (el, i))
    else pure Nothing

-- | Freeze the buffer by copying
{-# INLINE freeze #-}
freeze :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> m (U.Vector a)
freeze (Buffer ref) = do
  BufferRep len buf <- readMutVar ref
  U.freeze (UM.slice 0 len buf)

-- | Freeze the buffer into a 'Vector' without copying
{-# INLINE unsafeFreeze #-}
unsafeFreeze :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> m (U.Vector a)
unsafeFreeze (Buffer ref) = do
  BufferRep len buf <- readMutVar ref
  U.unsafeFreeze (UM.slice 0 len buf)

--------------------------------------------------------------------------------

-- | The current length of the buffer
{-# INLINE size #-}
size :: (PrimMonad m) => Buffer (PrimState m) a -> m Int
size (Buffer ref) = do
  BufferRep len _buf <- readMutVar ref
  pure len

-- | The current capacity (as in, remaining free space) of the buffer
{-# INLINE capacity #-}
capacity :: (Unbox a, PrimMonad m) => Buffer (PrimState m) a -> m Int
capacity (Buffer ref) = do
  BufferRep len buf <- readMutVar ref
  let capacity = UM.length buf
  pure (capacity - len)

--------------------------------------------------------------------------------

{-# INLINE grow #-}
grow :: Int -> Int
grow i = i * 2