wireform-proto-0.2.0.0: src/Proto/Internal/GrowList.hs
{-# LANGUAGE BangPatterns #-}
{- | Pure growing accumulator for repeated fields.
Uses a difference-list (Endo-style function composition with cons)
for O(1) amortised snoc, then materialises to Vector via fromListN.
-}
module Proto.Internal.GrowList (
GrowList (..),
emptyGrowList,
snocGrowList,
growListToVector,
growListToVectorU,
growListLength,
) where
import Data.Vector qualified as V
import Data.Vector.Mutable qualified as MV
import Data.Vector.Unboxed qualified as VU
data GrowList a = GrowList
{ glBuild :: !([a] -> [a])
, glCount :: {-# UNPACK #-} !Int
}
emptyGrowList :: GrowList a
emptyGrowList = GrowList id 0
{-# INLINE emptyGrowList #-}
snocGrowList :: GrowList a -> a -> GrowList a
snocGrowList (GrowList f n) x = GrowList (f . (x :)) (n + 1)
{-# INLINE snocGrowList #-}
growListLength :: GrowList a -> Int
growListLength = glCount
{-# INLINE growListLength #-}
-- | Materialise to a boxed Vector.
growListToVector :: GrowList a -> V.Vector a
growListToVector (GrowList f n)
| n == 0 = V.empty
| otherwise = V.create $ do
let !xs = f []
mv <- MV.new n
let fill !_ [] = pure ()
fill !i (e : es) = MV.unsafeWrite mv i e >> fill (i + 1) es
fill 0 xs
pure mv
{-# INLINE growListToVector #-}
-- | Materialise to an unboxed Vector.
growListToVectorU :: VU.Unbox a => GrowList a -> VU.Vector a
growListToVectorU gl = VU.convert (growListToVector gl)
{-# INLINE growListToVectorU #-}