content: -- | 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 #-}
  