groan-0.1.0.0: Data/Vector/Groan.hs
{-# LANGUAGE Strict #-}
module Data.Vector.Groan (
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,
pushAllBack,
pushFoldableBack,
-- * Conversion
freeze,
unsafeFreeze,
unsafeFreezeVector,
) where
import Control.Monad (forM_)
import Control.Monad.Primitive
import Control.Monad.ST.Strict
import Control.Monad.Trans.State.Strict
import Data.Primitive.Array
import Data.Primitive.Contiguous qualified as C
import Data.Primitive.MutVar
import Data.Vector (Vector)
import Data.Vector qualified as V
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 :: {-# UNPACK #-} !(MutableArray s a)
}
{-# INLINE run #-}
run :: Int -> (forall s. Buffer s a -> ST s ()) -> Vector a
run initialCapacity fn = runST do
buffer <- new initialCapacity
fn buffer
unsafeFreezeVector buffer
{-# INLINE runPrim #-}
runPrim :: (PrimMonad m) => Int -> (Buffer (PrimState m) a -> m ()) -> m (Vector a)
runPrim initialCapacity fn = do
buffer <- new initialCapacity
fn buffer
unsafeFreezeVector buffer
-- | Create a buffer with an initial (unfilled) capacity
{-# INLINE new #-}
new :: forall a m. (PrimMonad m) => Int -> m (Buffer (PrimState m) a)
new initialCapacity = do
buffer <- newArray initialCapacity uninitialisedElement
Buffer <$> newMutVar (BufferRep 0 buffer)
-- | Create a buffer with n initial elements
{-# INLINE replicate #-}
replicate :: (PrimMonad m) => Int -> a -> m (Buffer (PrimState m) a)
replicate size initialElem = do
buffer <- newArray size initialElem
Buffer <$> newMutVar (BufferRep size buffer)
-- | Create a buffer from an array, copying its elements
{-# INLINE thaw #-}
thaw :: (PrimMonad m) => Array a -> m (Buffer (PrimState m) a)
thaw input = do
buffer <- thawArray input 0 (sizeofArray input)
Buffer <$> newMutVar (BufferRep (sizeofArray input) buffer)
{-# INLINE read #-}
-- | Read an item from a buffer
read :: (PrimMonad m) => Buffer (PrimState m) a -> Int -> m a
read (Buffer ref) i = do
BufferRep len buf <- readMutVar ref
if i < len
then readArray 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 :: (PrimMonad m) => Buffer (PrimState m) a -> Int -> a -> m ()
write (Buffer ref) i a = do
BufferRep len buf <- readMutVar ref
if i < len
then writeArray 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 :: (PrimMonad m) => Buffer (PrimState m) a -> a -> m Int
pushBack (Buffer ref) a = do
BufferRep len dest <- readMutVar ref
let capacity = sizeofMutableArray dest
if len < capacity
then do
writeArray dest len a
writeMutVar ref (BufferRep (len + 1) dest)
else do
next <- newArray (grow (capacity + 1)) uninitialisedElement
copyMutableArray next 0 dest 0 (sizeofMutableArray dest)
writeArray next len a
writeMutVar ref (BufferRep (len + 1) next)
pure (len + 1)
{-# INLINE pushFoldableBack #-}
-- | Push multiple elements to the end of the buffer, returning the new length
pushFoldableBack :: (Foldable f, PrimMonad m) => Buffer (PrimState m) a -> f a -> m Int
pushFoldableBack b@(Buffer ref) f
| null f = size b
pushFoldableBack (Buffer ref) f = do
BufferRep {length = len, buffer = dest} <- readMutVar ref
let capacity = sizeofMutableArray dest
let nextLen = len + Prelude.length f
if nextLen < capacity
then do
-- just write
writtenLen <- (`execStateT` len) do
forM_ f \x -> StateT \i -> do
writeArray dest i x
pure ((), i + 1)
-- assert (writtenLen == nextLen)
writeMutVar ref (BufferRep writtenLen dest)
else do
-- grow then write
dest <- C.resize dest (grow nextLen)
writtenLen <- (`execStateT` len) do
forM_ f \x -> StateT \i -> do
writeArray dest i x
pure ((), i + 1)
-- assert (writtenLen == nextLen)
writeMutVar ref (BufferRep writtenLen dest)
pure nextLen
{-# INLINE pushAllBack #-}
-- | Push multiple elements to the end of the buffer
pushAllBack :: (PrimMonad m) => Buffer (PrimState m) a -> C.Sliced Array a -> m Int
pushAllBack b@(Buffer ref) arr
| C.null arr = size b
pushAllBack (Buffer ref) arr = do
BufferRep len dest <- readMutVar ref
let capacity = sizeofMutableArray dest
let nextLen = len + C.size arr
if nextLen < capacity
then do
-- just write
C.copy dest len arr
writeMutVar ref (BufferRep nextLen dest)
else do
-- grow then write
dest <- C.resize dest (grow nextLen)
C.copy dest len arr
writeMutVar ref (BufferRep nextLen dest)
pure nextLen
{-# INLINE popBack #-}
-- | Pop the last element off of the buffer
popBack :: (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 <- readArray buf i
pure (Just (el, i))
else pure Nothing
-- | Freeze the buffer by copying
{-# INLINE freeze #-}
freeze :: (PrimMonad m) => Buffer (PrimState m) a -> m (Array a)
freeze (Buffer ref) = do
BufferRep len buf <- readMutVar ref
C.freeze (C.sliceMut buf 0 len)
-- | Freeze the buffer without copying
{-# INLINE unsafeFreeze #-}
unsafeFreeze :: (PrimMonad m) => Buffer (PrimState m) a -> m (C.Sliced C.Array a)
unsafeFreeze (Buffer ref) = do
BufferRep len buf <- readMutVar ref
frz <- C.unsafeFreeze buf
pure (C.slice frz 0 len)
-- | Freeze the buffer into a 'Vector' without copying
{-# INLINE unsafeFreezeVector #-}
unsafeFreezeVector :: (PrimMonad m) => Buffer (PrimState m) a -> m (Vector a)
unsafeFreezeVector (Buffer ref) = do
BufferRep len buf <- readMutVar ref
frz <- C.unsafeFreeze buf
pure (V.unsafeFromArraySlice frz 0 len)
--------------------------------------------------------------------------------
-- | 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 :: (PrimMonad m) => Buffer (PrimState m) a -> m Int
capacity (Buffer ref) = do
BufferRep len buf <- readMutVar ref
let capacity = sizeofMutableArray buf
return (capacity - len)
--------------------------------------------------------------------------------
{-# INLINE grow #-}
grow :: Int -> Int
grow i = i * 2
{-# NOINLINE uninitialisedElement #-}
uninitialisedElement :: a
uninitialisedElement = error "Buffer: Not initialised"