bytezap-1.0.0: src/Bytezap/Poke/Derived.hs
module Bytezap.Poke.Derived where
import Bytezap.Poke
import Data.ByteString.Short qualified as SBS
import Data.Text.Internal qualified as T
--import Data.Array.Byte qualified as A
-- ^ text-2.1 and above
import Data.Text.Array qualified as A
import Data.Word
import GHC.Int
import Data.Char ( ord )
import Data.Bits ( shiftR, (.&.) )
import GHC.Exts ( sizeofByteArray# )
-- | Poke a 'SBS.ShortByteString'.
shortByteString :: SBS.ShortByteString -> Poke s
shortByteString (SBS.SBS ba#) = byteArray# ba# 0# (sizeofByteArray# ba#)
-- | Poke a 'T.Text'.
text :: T.Text -> Poke s
text (T.Text (A.ByteArray ba#) (I# os#) (I# len#)) = byteArray# ba# os# len#
-- | Poke a 'Char'.
--
-- Adapted from utf8-string.
char :: Char -> Poke s
char = go . ord
where
w8 :: Word8 -> Poke s
w8 = prim
go oc
| oc <= 0x7f = w8 $ fromIntegral oc
| oc <= 0x7ff = w8 (fromIntegral (0xc0 + (oc `shiftR` 6)))
<> w8 (fromIntegral (0x80 + oc .&. 0x3f))
| oc <= 0xffff = w8 (fromIntegral (0xe0 + (oc `shiftR` 12)))
<> w8 (fromIntegral (0x80 + ((oc `shiftR` 6) .&. 0x3f)))
<> w8 (fromIntegral (0x80 + oc .&. 0x3f))
| otherwise = w8 (fromIntegral (0xf0 + (oc `shiftR` 18)))
<> w8 (fromIntegral (0x80 + ((oc `shiftR` 12) .&. 0x3f)))
<> w8 (fromIntegral (0x80 + ((oc `shiftR` 6) .&. 0x3f)))
<> w8 (fromIntegral (0x80 + oc .&. 0x3f))
-- v TODO maybe not needed any more thanks to removing Pokeable class
-- | @unsafePokeIndexed pokeAt off n@ performs @n@ indexed pokes starting from
-- @off@.
--
-- Does not check bounds. Largely intended for bytewise pokes where some work
-- needs to be performed for each byte (e.g. escaping text and poking inline).
unsafePokeIndexed :: (Int -> Poke s) -> Int -> Int -> Poke s
-- no tail recursive option since it'd require binding a ptr, which we can't do
-- due to TYPE rr.
-- if you simply expand the monoidal ops here, you get the tail call ver. but
-- like, will GHC be able to do that?
-- hoping INLINE is good enough. but maybe need to check strictness.
unsafePokeIndexed pokeAt off n = go off
where
go i | i >= iend = mempty
| otherwise = pokeAt i <> go (i+1)
iend = off + n