{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Zenacy.HTML.Internal.Buffer
( Buffer(..)
, bufferNew
, bufferCapacity
, bufferSize
, bufferReset
, bufferAppend
, bufferApply
, bufferTake
, bufferContains
, bufferPack
, bufferString
) where
import Zenacy.HTML.Internal.BS
import Control.Monad.ST
( ST
)
import Data.STRef
( STRef
, newSTRef
, readSTRef
, writeSTRef
)
import qualified Data.DList as D
( empty
, snoc
, toList
)
import qualified Data.Vector.Unboxed as U
( freeze
, slice
, toList
)
import Data.Vector.Unboxed.Mutable
( MVector
)
import qualified Data.Vector.Unboxed.Mutable as U
( new
, length
, read
, write
, grow
)
import Data.Word
( Word8
)
data Buffer s = Buffer
{ bfCntl :: MVector s Int
, bfData :: MVector s Word8
}
bufferNew :: ST s (STRef s (Buffer s))
bufferNew = do
c <- U.new 1
d <- U.new 100
r <- newSTRef (Buffer c d)
bufferReset r
pure r
bufferCapacity :: STRef s (Buffer s) -> ST s (Int, Int)
bufferCapacity r = do
Buffer{..} <- readSTRef r
pure (U.length bfCntl, U.length bfData)
bufferSize :: STRef s (Buffer s) -> ST s Int
bufferSize r = do
Buffer{..} <- readSTRef r
U.read bfCntl 0
bufferReset :: STRef s (Buffer s) -> ST s ()
bufferReset r = do
Buffer{..} <- readSTRef r
U.write bfCntl 0 0
bufferAppend :: Word8 -> STRef s (Buffer s) -> ST s ()
bufferAppend word r = do
Buffer{..} <- readSTRef r
i <- U.read bfCntl 0
d <- if i + 1 < U.length bfData
then pure bfData
else do
v <- U.grow bfData $ U.length bfData
writeSTRef r $ Buffer bfCntl v
pure v
U.write d i word
U.write bfCntl 0 (i + 1)
bufferApply :: (Word8 -> ST s ()) -> STRef s (Buffer s) -> ST s ()
bufferApply f r = do
Buffer{..} <- readSTRef r
n <- U.read bfCntl 0
let go i
| i < n =
U.read bfData i >>= f >> go (i + 1)
| otherwise =
pure ()
go 0
bufferTake :: Int -> STRef s (Buffer s) -> ST s [Word8]
bufferTake x r = do
Buffer{..} <- readSTRef r
n <- min x <$> U.read bfCntl 0
let go i y
| i < n = do
a <- U.read bfData i
go (i + 1) $ D.snoc y a
| otherwise =
pure $ D.toList y
go 0 D.empty
bufferContains :: [Word8] -> STRef s (Buffer s) -> ST s Bool
bufferContains x r = do
n <- bufferSize r
if n /= length x
then pure False
else do
a <- bufferTake n r
pure $ x == a
bufferPack :: STRef s (Buffer s) -> ST s BS
bufferPack r = do
Buffer{..} <- readSTRef r
n <- U.read bfCntl 0
bufferString bfData n
bufferString :: MVector s Word8 -> Int -> ST s BS
bufferString v n =
U.freeze v >>= pure . bsPack . U.toList . U.slice 0 n