{-# 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)