zenacy-html-2.0.0: src/Zenacy/HTML/Internal/Buffer.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | Defines an buffer type.
module Zenacy.HTML.Internal.Buffer
( Buffer(..)
, bufferNew
, bufferCapacity
, bufferSize
, bufferReset
, bufferAppend
, bufferApply
, bufferTake
, bufferContains
, bufferPack
, bufferString
) where
import Zenacy.HTML.Internal.BS
-- import Foreign
-- ( castPtr
-- , withForeignPtr
-- )
import Control.Monad.ST
( ST
)
import Data.STRef
( STRef
, newSTRef
, readSTRef
, writeSTRef
)
import qualified Data.DList as D
( empty
, snoc
, toList
)
-- import Data.Vector.Storable.Mutable
import qualified Data.Vector.Unboxed as U
( freeze
, slice
, toList
)
import Data.Vector.Unboxed.Mutable
( MVector
)
import qualified Data.Vector.Unboxed.Mutable as U
-- import qualified Data.Vector.Storable.Mutable as U
( new
, length
, read
, write
, grow
-- , unsafeToForeignPtr0
)
import Data.Word
( Word8
)
-- import System.IO.Unsafe
-- ( unsafePerformIO
-- )
-- | A type of buffer used to hold bytes.
data Buffer s = Buffer
{ bfCntl :: MVector s Int
, bfData :: MVector s Word8
}
-- | Makes a new buffer.
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
-- | Gets the capacity of the buffer.
bufferCapacity :: STRef s (Buffer s) -> ST s (Int, Int)
bufferCapacity r = do
Buffer{..} <- readSTRef r
pure (U.length bfCntl, U.length bfData)
-- | Gets the size of the buffer.
bufferSize :: STRef s (Buffer s) -> ST s Int
bufferSize r = do
Buffer{..} <- readSTRef r
U.read bfCntl 0
-- | Resets a buffer.
bufferReset :: STRef s (Buffer s) -> ST s ()
bufferReset r = do
Buffer{..} <- readSTRef r
U.write bfCntl 0 0
-- | Appends a word to a buffer.
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)
-- | Applies an action to each word in the buffer.
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
-- | Takes elements from the front of the buffer.
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
-- | Determines if a buffer has the specified contents.
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
-- | Packs a buffer into a byte string.
bufferPack :: STRef s (Buffer s) -> ST s BS
bufferPack r = do
Buffer{..} <- readSTRef r
n <- U.read bfCntl 0
bufferString bfData n
-- | Converts a storable vector to a byte string.
bufferString :: MVector s Word8 -> Int -> ST s BS
bufferString v n =
U.freeze v >>= pure . bsPack . U.toList . U.slice 0 n
-- pure $ unsafePerformIO $ do
-- let (f, _) = U.unsafeToForeignPtr0 v
-- withForeignPtr f $ \p ->
-- S.packCStringLen (castPtr p, n)