module Data.Niagra.Builder.Internal
(
Buffer(..),
bufferLength,
unsafeWriteChar,
unsafeNewBuffer,
bufferToText,
pushBuffer,
snocVec,
appendVec
)
where
import GHC.Prim
import GHC.Exts
import GHC.ST
import GHC.Word (Word16(..))
import Data.Char
import Data.Bits ((.&.))
import Data.Text.Internal (Text(..))
import Data.Text.Array (Array(..))
import Data.Text.Internal.Unsafe.Shift (shiftR)
import Data.Sequence (Seq(..), (|>))
bufferLength :: Int
bufferLength = 128
data Buffer s = Buffer {
bufferArray :: !(MutableByteArray# s),
bufferUsedLength :: !Int
}
copyBA :: MutableByteArray# s
-> Int
-> ByteArray#
-> Int
-> Int
-> ST s ()
copyBA dest (I# doff) src (I# soff) (I# n) = ST $ \s ->
(# copyByteArray# src (uncheckedIShiftL# soff 1#)
dest (uncheckedIShiftL# doff 1#)
(uncheckedIShiftL# n 1#) s, () #)
writeWord16 :: MutableByteArray# s -> Int -> Word16 -> ST s ()
writeWord16 marr# (I# i#) (W16# w#) = ST $ \s -> (# (writeWord16Array# marr# i# w# s), () #)
shrinkBuffer :: Buffer s -> ST s ()
shrinkBuffer (Buffer a (I# l#)) = ST $ \s -> (# (shrinkMutableByteArray# a (uncheckedIShiftL# l# 1#) s), () #)
freezeBuffer :: Buffer s -> ST s Text
freezeBuffer (Buffer a l) = ST $ \s -> case unsafeFreezeByteArray# a s of
(# s', ary #) -> (# s', Text (Array ary) 0 l #)
unsafeWriteChar :: MutableByteArray# s -> Int -> Char -> ST s Int
unsafeWriteChar a i c
| n < 0x10000 = do
writeWord16 a i $ fromIntegral n
return 1
| otherwise = do
writeWord16 a i lo
writeWord16 a (i+1) hi
return 2
where n = ord c
m = n 0x10000
lo = fromIntegral $ (m `shiftR` 10) + 0xD800
hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00
unsafeNewBuffer :: ST s (Buffer s)
unsafeNewBuffer = ST $ \s -> case newByteArray# (uncheckedIShiftL# aryLen 1#) s of
(# s', a #) -> (# s', Buffer a 0 #)
where !(I# aryLen) = bufferLength * 2
bufferToText :: Buffer s -> ST s Text
bufferToText b = shrinkBuffer b >> freezeBuffer b
pushBuffer :: (Buffer s, Seq Text) -> ST s (Buffer s, Seq Text)
pushBuffer (b,xs) = do
frzn <- bufferToText b
(, xs |> frzn) <$> unsafeNewBuffer
snocVec :: Char -> (Buffer s, Seq Text) -> ST s (Buffer s, Seq Text)
snocVec c tup@(Buffer a l, xs)
| n < 0x10000 = do
if l+1 > bufferLength
then pushBuffer tup >>= snocVec c
else do
writeWord16 a l $ fromIntegral n
return (Buffer a (l+1), xs)
| otherwise = do
if l+2 > bufferLength
then pushBuffer tup >>= snocVec c
else do
writeWord16 a l lo
writeWord16 a (l+1) hi
return (Buffer a (l+2), xs)
where n = ord c
m = n 0x10000
lo = fromIntegral $ (m `shiftR` 10) + 0xD800
hi = fromIntegral $ (m .&. 0x3FF) + 0xDC00
appendVec :: Text -> (Buffer s, Seq Text) -> ST s (Buffer s, Seq Text)
appendVec (Text ta@(Array tbuf) to tl) (Buffer a l, xs) = do
copyBA a l tbuf to copyLength
if tl > remaining
then pushBuffer updatedTup >>= appendVec (Text ta (to+copyLength) (tlcopyLength))
else return updatedTup
where
updatedTup = (Buffer a (l+copyLength), xs)
remaining = bufferLength l
copyLength = minTwo remaining tl
minTwo a b | a < b = a | otherwise = b