bytezap-1.0.0: src/Bytezap/Poke/Json.hs
module Bytezap.Poke.Json where
import Data.Word
import Data.Text.Internal qualified as T
import Data.Text.Array qualified as A
import Bytezap.Poke.Derived ( unsafePokeIndexed )
import Bytezap.Poke
import GHC.Exts
import Foreign.C.Types ( CChar )
import GHC.Word
escapedLength8 :: T.Text -> Int
escapedLength8 (T.Text arr off len) = go off 0
where
go !i escLen
| i >= iend = escLen
| otherwise = go (i+1) (i + escapeW8 (A.unsafeIndex arr i))
iend = off + len
-- my benchmarks go much faster without INLINE :/
escapeW8 :: Word8 -> Int
escapeW8 w | w >= 0x80 = 1 -- all multibyte chars are unescaped
| otherwise = case w of 0x5C -> 2 -- \
0x22 -> 2 -- "
0x0A -> 2 -- \n
0x0D -> 2 -- \r
0x09 -> 2 -- \t
_ -> if w < 0x20 then 6 else 1
pokeEscapedTextUnquoted :: T.Text -> Poke s
pokeEscapedTextUnquoted (T.Text arr off len) =
unsafePokeIndexed (pokeEscapeW8 . A.unsafeIndex arr) off len
-- think we have to poke bytes here still thanks to endianness >:(
pokeEscapeW8 :: Word8 -> Poke s
pokeEscapeW8 w
| w >= 0x80 = w8 w -- all multibyte chars are unescaped
| otherwise =
case w of
0x5C -> w8 0x5C <> w8 0x5C -- \
0x22 -> w8 0x5C <> w8 0x22 -- "
0x0A -> w8 0x5C <> w8 0x6E -- \n
0x0D -> w8 0x5C <> w8 0x72 -- \r
0x09 -> w8 0x5C <> w8 0x74 -- \t
_ ->
if w >= 0x20 then w8 w else w8 0x5C <> w8 0x75 <> w8 0x30 <> w8 0x30 <> w8AsciiHex w
where w8 = prim @Word8
-- note I index because I _think_ this is immutable mem! but I might be wrong
-- and very stupid!!
-- look I'm copying some very weird code from bytestring here. idk
w8AsciiHex :: Word8 -> Poke s
w8AsciiHex w = prim (W16# (indexWord16OffAddr# c_lower_hex_table# wI))
where
!(I# wI) = fromIntegral w
!(Ptr c_lower_hex_table#) = c_lower_hex_table
-- TODO lol I'm saying safe b/c I think it's immutable mem. but I might be
-- making a huge fucky wucky here xd
-- can't do Addr# here, no unlifted top-level values x)
foreign import ccall safe "&hs_bytestring_lower_hex_table"
c_lower_hex_table :: Ptr CChar