-- | A mutable vector that grows in size. -- -- Example usage: -- -- > import qualified Data.ProtoLens.Encoding.Growing as Growing -- > import qualified Data.Vector.Unboxed as V -- > test :: IO (V.Vector Int) -- > test = do -- > v <- Growing.new -- > v' <- Growing.append v 1 -- > v'' <- Growing.append v' 2 -- > v''' <- Growing.append v'' 3 -- > unsafeFreeze v''' module Data.ProtoLens.Encoding.Growing ( Growing, new, append, unsafeFreeze, RealWorld, ) where import Control.Monad.Primitive (PrimMonad, PrimState, RealWorld) import qualified Data.Vector.Generic as V import qualified Data.Vector.Generic.Mutable as MV -- | A mutable vector which can increase in capacity. data Growing v s a = Growing {-# UNPACK #-} !Int -- The number of elements in the mutable vector -- that have already been set. !(V.Mutable v s a) -- TODOs for efficiency: -- - Try unpacking this. It's difficult as-is because -- V.Mutable is a type function. -- - MVectors support slicing, but we're not using that -- functionality, so we're passing around an extra unnecessary -- Int. -- | Create a new empty growing vector. new :: (PrimMonad m, V.Vector v a) => m (Growing v (PrimState m) a) new = Growing 0 <$> MV.new 0 -- | Unsafely convert a growing vector to an immutable one without -- copying. After this call, you may not use the growing vector -- nor any other growing vectors that were used to produce this one. unsafeFreeze :: (PrimMonad m, V.Vector v a) => Growing v (PrimState m) a -> m (v a) unsafeFreeze (Growing len m) = V.unsafeFreeze (MV.take len m) -- | Returns a new growing vector with a new element at the end. -- Note that the return value may share storage with the input value. -- Furthermore, calling @append@ twice on the same input may result -- in two vectors that share the same storage. append :: (PrimMonad m, V.Vector v a) => Growing v (PrimState m) a -> a -> m (Growing v (PrimState m) a) append (Growing len v) x | len < MV.length v = do MV.unsafeWrite v len x return $ Growing (len + 1) v | otherwise = do let len' = 2 * len + 1 v' <- MV.unsafeGrow v len' MV.unsafeWrite v' len x return $ Growing (len + 1) v' {-# INLINE append #-}