lsm-tree-1.0.0.0: src-core/Database/LSMTree/Internal/Vector/Growing.hs
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
{-# OPTIONS_HADDOCK not-home #-}
{- HLINT ignore "Avoid restricted alias" -}
-- | Vectors with support for appending elements.
module Database.LSMTree.Internal.Vector.Growing
(
GrowingVector (GrowingVector),
new,
append,
freeze,
readMaybeLast
)
where
import Prelude hiding (init, last, length, read)
import Control.Monad (when)
import Control.Monad.ST.Strict (ST)
import Data.Primitive.PrimVar (PrimVar, newPrimVar, readPrimVar,
writePrimVar)
import Data.STRef.Strict (STRef, newSTRef, readSTRef, writeSTRef)
import Data.Vector (Vector)
import qualified Data.Vector as Mutable (freeze)
import Data.Vector.Mutable (MVector)
import qualified Data.Vector.Mutable as Mutable (grow, length, new, read, set,
slice, take)
{-|
A vector with support for appending elements.
Internally, the elements of a growing vector are stored in a buffer. This
buffer is automatically enlarged whenever this is needed for storing
additional elements. On each such enlargement, the size of the buffer is
multiplied by a power of 2, whose exponent is chosen just big enough to make
the final buffer size at least as high as the new vector length.
Note that, while buffer sizes and vector lengths are represented as 'Int'
values internally, the above-described buffer enlargement scheme has the
consequence that the largest possible buffer size and thus the largest
possible vector length may not be the maximum 'Int' value. That said, they
are always greater than half the maximum 'Int' value, which should be enough
for all practical purposes.
-}
data GrowingVector s a = GrowingVector
!(STRef s (MVector s a)) -- Reference to the buffer
!(PrimVar s Int) -- Reference to the length
-- | Creates a new, initially empty, vector.
new :: Int -- ^ Initial buffer size
-> ST s (GrowingVector s a) -- ^ Construction of the vector
new illegalInitialBufferSize | illegalInitialBufferSize <= 0
= error "Initial buffer size not positive"
new initialBufferSize
= do
buffer <- Mutable.new initialBufferSize
bufferRef <- newSTRef $! buffer
lengthRef <- newPrimVar 0
pure (GrowingVector bufferRef lengthRef)
{-|
Appends a value a certain number of times to a vector. If a negative number
is provided as the count, the vector is not changed.
-}
append :: forall s a . GrowingVector s a -> Int -> a -> ST s ()
append _ pseudoCount _ | pseudoCount <= 0
= pure ()
append (GrowingVector bufferRef lengthRef) count val
= do
length <- readPrimVar lengthRef
makeRoom
buffer' <- readSTRef bufferRef
Mutable.set (Mutable.slice length count buffer') $! val
where
makeRoom :: ST s ()
makeRoom = do
length <- readPrimVar lengthRef
when (count > maxBound - length) (error "New length too large")
buffer <- readSTRef bufferRef
let
bufferSize :: Int
!bufferSize = Mutable.length buffer
length' :: Int
!length' = length + count
when (bufferSize < length') $ do
let
higherBufferSizes :: [Int]
higherBufferSizes = tail (init ++ [last]) where
init :: [Int]
last :: Int
(init, last : _) = span (<= maxBound `div` 2) $
iterate (* 2) bufferSize
{-NOTE:
In order to prevent overflow, we have to start with the
current buffer size here, although we know that it is
not sufficient.
-}
sufficientBufferSizes :: [Int]
sufficientBufferSizes = dropWhile (< length') higherBufferSizes
case sufficientBufferSizes of
[]
-> error "No sufficient buffer size available"
bufferSize' : _
-> Mutable.grow buffer (bufferSize' - bufferSize) >>=
(writeSTRef bufferRef $!)
writePrimVar lengthRef length'
-- | Turns a growing vector into an ordinary vector.
freeze :: GrowingVector s a -> ST s (Vector a)
freeze (GrowingVector bufferRef lengthRef) = do
buffer <- readSTRef bufferRef
length <- readPrimVar lengthRef
Mutable.freeze (Mutable.take length buffer)
-- | Reads the last element of a growing vector if it exists.
readMaybeLast :: GrowingVector s a -> ST s (Maybe a)
readMaybeLast (GrowingVector bufferRef lengthRef) = do
length <- readPrimVar lengthRef
if length == 0
then pure Nothing
else do
buffer <- readSTRef bufferRef
Just <$> Mutable.read buffer (pred length)