|
Packit |
9a2dfb |
{-# LANGUAGE BangPatterns #-}
|
|
Packit |
9a2dfb |
{-# LANGUAGE CPP #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- |
|
|
Packit |
9a2dfb |
-- Module: Data.Aeson.Encoding.Builder
|
|
Packit |
9a2dfb |
-- Copyright: (c) 2011 MailRank, Inc.
|
|
Packit |
9a2dfb |
-- (c) 2013 Simon Meier <iridcode@gmail.com>
|
|
Packit |
9a2dfb |
-- License: BSD3
|
|
Packit |
9a2dfb |
-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
|
|
Packit |
9a2dfb |
-- Stability: experimental
|
|
Packit |
9a2dfb |
-- Portability: portable
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- Efficiently serialize a JSON value using the UTF-8 encoding.
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
module Data.Aeson.Encoding.Builder
|
|
Packit |
9a2dfb |
(
|
|
Packit |
9a2dfb |
encodeToBuilder
|
|
Packit |
9a2dfb |
, null_
|
|
Packit |
9a2dfb |
, bool
|
|
Packit |
9a2dfb |
, array
|
|
Packit |
9a2dfb |
, emptyArray_
|
|
Packit |
9a2dfb |
, emptyObject_
|
|
Packit |
9a2dfb |
, object
|
|
Packit |
9a2dfb |
, text
|
|
Packit |
9a2dfb |
, string
|
|
Packit |
9a2dfb |
, unquoted
|
|
Packit |
9a2dfb |
, quote
|
|
Packit |
9a2dfb |
, scientific
|
|
Packit |
9a2dfb |
, day
|
|
Packit |
9a2dfb |
, localTime
|
|
Packit |
9a2dfb |
, utcTime
|
|
Packit |
9a2dfb |
, timeOfDay
|
|
Packit |
9a2dfb |
, zonedTime
|
|
Packit |
9a2dfb |
, ascii2
|
|
Packit |
9a2dfb |
, ascii4
|
|
Packit |
9a2dfb |
, ascii5
|
|
Packit |
9a2dfb |
) where
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
import Prelude ()
|
|
Packit |
9a2dfb |
import Prelude.Compat
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
import Data.Aeson.Internal.Time
|
|
Packit |
9a2dfb |
import Data.Aeson.Types.Internal (Value (..))
|
|
Packit |
9a2dfb |
import Data.ByteString.Builder as B
|
|
Packit |
9a2dfb |
import Data.ByteString.Builder.Prim as BP
|
|
Packit |
9a2dfb |
import Data.ByteString.Builder.Scientific (scientificBuilder)
|
|
Packit |
9a2dfb |
import Data.Char (chr, ord)
|
|
Packit |
9a2dfb |
import Data.Monoid ((<>))
|
|
Packit |
9a2dfb |
import Data.Scientific (Scientific, base10Exponent, coefficient)
|
|
Packit |
9a2dfb |
import Data.Time (UTCTime(..))
|
|
Packit |
9a2dfb |
import Data.Time.Calendar (Day(..), toGregorian)
|
|
Packit |
9a2dfb |
import Data.Time.LocalTime
|
|
Packit |
9a2dfb |
import Data.Word (Word8)
|
|
Packit |
9a2dfb |
import qualified Data.HashMap.Strict as HMS
|
|
Packit |
9a2dfb |
import qualified Data.Text as T
|
|
Packit |
9a2dfb |
import qualified Data.Vector as V
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
#if MIN_VERSION_bytestring(0,10,4)
|
|
Packit |
9a2dfb |
import Data.Text.Encoding (encodeUtf8BuilderEscaped)
|
|
Packit |
9a2dfb |
#else
|
|
Packit |
9a2dfb |
import Data.Bits ((.&.))
|
|
Packit |
9a2dfb |
import Data.Text.Internal (Text(..))
|
|
Packit |
9a2dfb |
import Data.Text.Internal.Unsafe.Shift (shiftR)
|
|
Packit |
9a2dfb |
import Foreign.Ptr (minusPtr, plusPtr)
|
|
Packit |
9a2dfb |
import Foreign.Storable (poke)
|
|
Packit |
9a2dfb |
import qualified Data.ByteString.Builder.Internal as B
|
|
Packit |
9a2dfb |
import qualified Data.ByteString.Builder.Prim.Internal as BP
|
|
Packit |
9a2dfb |
import qualified Data.Text.Array as A
|
|
Packit |
9a2dfb |
import qualified Data.Text.Internal.Encoding.Utf16 as U16
|
|
Packit |
9a2dfb |
#endif
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- Use this function if you are encoding over the wire, or need to
|
|
Packit |
9a2dfb |
-- prepend or append further bytes to the encoded JSON value.
|
|
Packit |
9a2dfb |
encodeToBuilder :: Value -> Builder
|
|
Packit |
9a2dfb |
encodeToBuilder Null = null_
|
|
Packit |
9a2dfb |
encodeToBuilder (Bool b) = bool b
|
|
Packit |
9a2dfb |
encodeToBuilder (Number n) = scientific n
|
|
Packit |
9a2dfb |
encodeToBuilder (String s) = text s
|
|
Packit |
9a2dfb |
encodeToBuilder (Array v) = array v
|
|
Packit |
9a2dfb |
encodeToBuilder (Object m) = object m
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON null.
|
|
Packit |
9a2dfb |
null_ :: Builder
|
|
Packit |
9a2dfb |
null_ = BP.primBounded (ascii4 ('n',('u',('l','l')))) ()
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON boolean.
|
|
Packit |
9a2dfb |
bool :: Bool -> Builder
|
|
Packit |
9a2dfb |
bool = BP.primBounded (BP.condB id (ascii4 ('t',('r',('u','e'))))
|
|
Packit |
9a2dfb |
(ascii5 ('f',('a',('l',('s','e'))))))
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON array.
|
|
Packit |
9a2dfb |
array :: V.Vector Value -> Builder
|
|
Packit |
9a2dfb |
array v
|
|
Packit |
9a2dfb |
| V.null v = emptyArray_
|
|
Packit |
9a2dfb |
| otherwise = B.char8 '[' <>
|
|
Packit |
9a2dfb |
encodeToBuilder (V.unsafeHead v) <>
|
|
Packit |
9a2dfb |
V.foldr withComma (B.char8 ']') (V.unsafeTail v)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
withComma a z = B.char8 ',' <> encodeToBuilder a <> z
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- Encode a JSON object.
|
|
Packit |
9a2dfb |
object :: HMS.HashMap T.Text Value -> Builder
|
|
Packit |
9a2dfb |
object m = case HMS.toList m of
|
|
Packit |
9a2dfb |
(x:xs) -> B.char8 '{' <> one x <> foldr withComma (B.char8 '}') xs
|
|
Packit |
9a2dfb |
_ -> emptyObject_
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
withComma a z = B.char8 ',' <> one a <> z
|
|
Packit |
9a2dfb |
one (k,v) = text k <> B.char8 ':' <> encodeToBuilder v
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON string.
|
|
Packit |
9a2dfb |
text :: T.Text -> Builder
|
|
Packit |
9a2dfb |
text t = B.char8 '"' <> unquoted t <> B.char8 '"'
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON string, without enclosing quotes.
|
|
Packit |
9a2dfb |
unquoted :: T.Text -> Builder
|
|
Packit |
9a2dfb |
unquoted = encodeUtf8BuilderEscaped escapeAscii
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Add quotes surrounding a builder
|
|
Packit |
9a2dfb |
quote :: Builder -> Builder
|
|
Packit |
9a2dfb |
quote b = B.char8 '"' <> b <> B.char8 '"'
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON string.
|
|
Packit |
9a2dfb |
string :: String -> Builder
|
|
Packit |
9a2dfb |
string t = B.char8 '"' <> BP.primMapListBounded go t <> B.char8 '"'
|
|
Packit |
9a2dfb |
where go = BP.condB (> '\x7f') BP.charUtf8 (c2w >$< escapeAscii)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
escapeAscii :: BP.BoundedPrim Word8
|
|
Packit |
9a2dfb |
escapeAscii =
|
|
Packit |
9a2dfb |
BP.condB (== c2w '\\' ) (ascii2 ('\\','\\')) $
|
|
Packit |
9a2dfb |
BP.condB (== c2w '\"' ) (ascii2 ('\\','"' )) $
|
|
Packit |
9a2dfb |
BP.condB (>= c2w '\x20') (BP.liftFixedToBounded BP.word8) $
|
|
Packit |
9a2dfb |
BP.condB (== c2w '\n' ) (ascii2 ('\\','n' )) $
|
|
Packit |
9a2dfb |
BP.condB (== c2w '\r' ) (ascii2 ('\\','r' )) $
|
|
Packit |
9a2dfb |
BP.condB (== c2w '\t' ) (ascii2 ('\\','t' )) $
|
|
Packit |
9a2dfb |
BP.liftFixedToBounded hexEscape -- fallback for chars < 0x20
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
hexEscape :: BP.FixedPrim Word8
|
|
Packit |
9a2dfb |
hexEscape = (\c -> ('\\', ('u', fromIntegral c))) BP.>$<
|
|
Packit |
9a2dfb |
BP.char8 >*< BP.char8 >*< BP.word16HexFixed
|
|
Packit |
9a2dfb |
{-# INLINE escapeAscii #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
c2w :: Char -> Word8
|
|
Packit |
9a2dfb |
c2w c = fromIntegral (ord c)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
-- | Encode a JSON number.
|
|
Packit |
9a2dfb |
scientific :: Scientific -> Builder
|
|
Packit |
9a2dfb |
scientific s
|
|
Packit |
9a2dfb |
| e < 0 = scientificBuilder s
|
|
Packit |
9a2dfb |
| otherwise = B.integerDec (coefficient s * 10 ^ e)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
e = base10Exponent s
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
emptyArray_ :: Builder
|
|
Packit |
9a2dfb |
emptyArray_ = BP.primBounded (ascii2 ('[',']')) ()
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
emptyObject_ :: Builder
|
|
Packit |
9a2dfb |
emptyObject_ = BP.primBounded (ascii2 ('{','}')) ()
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
ascii2 :: (Char, Char) -> BP.BoundedPrim a
|
|
Packit |
9a2dfb |
ascii2 cs = BP.liftFixedToBounded $ const cs BP.>$< BP.char7 >*< BP.char7
|
|
Packit |
9a2dfb |
{-# INLINE ascii2 #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
ascii4 :: (Char, (Char, (Char, Char))) -> BP.BoundedPrim a
|
|
Packit |
9a2dfb |
ascii4 cs = BP.liftFixedToBounded $ const cs >$<
|
|
Packit |
9a2dfb |
BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
|
|
Packit |
9a2dfb |
{-# INLINE ascii4 #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
ascii5 :: (Char, (Char, (Char, (Char, Char)))) -> BP.BoundedPrim a
|
|
Packit |
9a2dfb |
ascii5 cs = BP.liftFixedToBounded $ const cs >$<
|
|
Packit |
9a2dfb |
BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
|
|
Packit |
9a2dfb |
{-# INLINE ascii5 #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
ascii6 :: (Char, (Char, (Char, (Char, (Char, Char))))) -> BP.BoundedPrim a
|
|
Packit |
9a2dfb |
ascii6 cs = BP.liftFixedToBounded $ const cs >$<
|
|
Packit |
9a2dfb |
BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
|
|
Packit |
9a2dfb |
{-# INLINE ascii6 #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
ascii8 :: (Char, (Char, (Char, (Char, (Char, (Char, (Char, Char)))))))
|
|
Packit |
9a2dfb |
-> BP.BoundedPrim a
|
|
Packit |
9a2dfb |
ascii8 cs = BP.liftFixedToBounded $ const cs >$<
|
|
Packit |
9a2dfb |
BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*<
|
|
Packit |
9a2dfb |
BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
|
|
Packit |
9a2dfb |
{-# INLINE ascii8 #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
day :: Day -> Builder
|
|
Packit |
9a2dfb |
day dd = encodeYear yr <>
|
|
Packit |
9a2dfb |
BP.primBounded (ascii6 ('-',(mh,(ml,('-',(dh,dl)))))) ()
|
|
Packit |
9a2dfb |
where (yr,m,d) = toGregorian dd
|
|
Packit |
9a2dfb |
!(T mh ml) = twoDigits m
|
|
Packit |
9a2dfb |
!(T dh dl) = twoDigits d
|
|
Packit |
9a2dfb |
encodeYear y
|
|
Packit |
9a2dfb |
| y >= 1000 = B.integerDec y
|
|
Packit |
9a2dfb |
| y >= 0 = BP.primBounded (ascii4 (padYear y)) ()
|
|
Packit |
9a2dfb |
| y >= -999 = BP.primBounded (ascii5 ('-',padYear (- y))) ()
|
|
Packit |
9a2dfb |
| otherwise = B.integerDec y
|
|
Packit |
9a2dfb |
padYear y =
|
|
Packit |
9a2dfb |
let (ab,c) = fromIntegral y `quotRem` 10
|
|
Packit |
9a2dfb |
(a,b) = ab `quotRem` 10
|
|
Packit |
9a2dfb |
in ('0',(digit a,(digit b,digit c)))
|
|
Packit |
9a2dfb |
{-# INLINE day #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
timeOfDay :: TimeOfDay -> Builder
|
|
Packit |
9a2dfb |
timeOfDay t = timeOfDay64 (toTimeOfDay64 t)
|
|
Packit |
9a2dfb |
{-# INLINE timeOfDay #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
timeOfDay64 :: TimeOfDay64 -> Builder
|
|
Packit |
9a2dfb |
timeOfDay64 (TOD h m s)
|
|
Packit |
9a2dfb |
| frac == 0 = hhmmss -- omit subseconds if 0
|
|
Packit |
9a2dfb |
| otherwise = hhmmss <> BP.primBounded showFrac frac
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
hhmmss = BP.primBounded (ascii8 (hh,(hl,(':',(mh,(ml,(':',(sh,sl)))))))) ()
|
|
Packit |
9a2dfb |
!(T hh hl) = twoDigits h
|
|
Packit |
9a2dfb |
!(T mh ml) = twoDigits m
|
|
Packit |
9a2dfb |
!(T sh sl) = twoDigits (fromIntegral real)
|
|
Packit |
9a2dfb |
(real,frac) = s `quotRem` pico
|
|
Packit |
9a2dfb |
showFrac = (\x -> ('.', x)) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
|
|
Packit |
9a2dfb |
trunc12 = (`quotRem` micro) >$<
|
|
Packit |
9a2dfb |
BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
|
|
Packit |
9a2dfb |
digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
|
|
Packit |
9a2dfb |
trunc6 = ((`quotRem` milli) . fromIntegral) >$<
|
|
Packit |
9a2dfb |
BP.condB (\(_,y) -> y == 0) (fst >$< trunc3) (digits3 >*< trunc3)
|
|
Packit |
9a2dfb |
digits3 = (`quotRem` 10) >$< (digits2 >*< digits1)
|
|
Packit |
9a2dfb |
digits2 = (`quotRem` 10) >$< (digits1 >*< digits1)
|
|
Packit |
9a2dfb |
digits1 = BP.liftFixedToBounded (digit >$< BP.char7)
|
|
Packit |
9a2dfb |
trunc3 = BP.condB (== 0) BP.emptyB $
|
|
Packit |
9a2dfb |
(`quotRem` 100) >$< (digits1 >*< trunc2)
|
|
Packit |
9a2dfb |
trunc2 = BP.condB (== 0) BP.emptyB $
|
|
Packit |
9a2dfb |
(`quotRem` 10) >$< (digits1 >*< trunc1)
|
|
Packit |
9a2dfb |
trunc1 = BP.condB (== 0) BP.emptyB digits1
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
pico = 1000000000000 -- number of picoseconds in 1 second
|
|
Packit |
9a2dfb |
micro = 1000000 -- number of microseconds in 1 second
|
|
Packit |
9a2dfb |
milli = 1000 -- number of milliseconds in 1 second
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
timeZone :: TimeZone -> Builder
|
|
Packit |
9a2dfb |
timeZone (TimeZone off _ _)
|
|
Packit |
9a2dfb |
| off == 0 = B.char7 'Z'
|
|
Packit |
9a2dfb |
| otherwise = BP.primBounded (ascii6 (s,(hh,(hl,(':',(mh,ml)))))) ()
|
|
Packit |
9a2dfb |
where !s = if off < 0 then '-' else '+'
|
|
Packit |
9a2dfb |
!(T hh hl) = twoDigits h
|
|
Packit |
9a2dfb |
!(T mh ml) = twoDigits m
|
|
Packit |
9a2dfb |
(h,m) = abs off `quotRem` 60
|
|
Packit |
9a2dfb |
{-# INLINE timeZone #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
dayTime :: Day -> TimeOfDay64 -> Builder
|
|
Packit |
9a2dfb |
dayTime d t = day d <> B.char7 'T' <> timeOfDay64 t
|
|
Packit |
9a2dfb |
{-# INLINE dayTime #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
utcTime :: UTCTime -> B.Builder
|
|
Packit |
9a2dfb |
utcTime (UTCTime d s) = dayTime d (diffTimeOfDay64 s) <> B.char7 'Z'
|
|
Packit |
9a2dfb |
{-# INLINE utcTime #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
localTime :: LocalTime -> Builder
|
|
Packit |
9a2dfb |
localTime (LocalTime d t) = dayTime d (toTimeOfDay64 t)
|
|
Packit |
9a2dfb |
{-# INLINE localTime #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
zonedTime :: ZonedTime -> Builder
|
|
Packit |
9a2dfb |
zonedTime (ZonedTime t z) = localTime t <> timeZone z
|
|
Packit |
9a2dfb |
{-# INLINE zonedTime #-}
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
data T = T {-# UNPACK #-} !Char {-# UNPACK #-} !Char
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
twoDigits :: Int -> T
|
|
Packit |
9a2dfb |
twoDigits a = T (digit hi) (digit lo)
|
|
Packit |
9a2dfb |
where (hi,lo) = a `quotRem` 10
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
digit :: Int -> Char
|
|
Packit |
9a2dfb |
digit x = chr (x + 48)
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
#if !(MIN_VERSION_bytestring(0,10,4))
|
|
Packit |
9a2dfb |
-- | Encode text using UTF-8 encoding and escape the ASCII characters using
|
|
Packit |
9a2dfb |
-- a 'BP.BoundedPrim'.
|
|
Packit |
9a2dfb |
--
|
|
Packit |
9a2dfb |
-- Use this function is to implement efficient encoders for text-based formats
|
|
Packit |
9a2dfb |
-- like JSON or HTML.
|
|
Packit |
9a2dfb |
{-# INLINE encodeUtf8BuilderEscaped #-}
|
|
Packit |
9a2dfb |
-- TODO: Extend documentation with references to source code in @blaze-html@
|
|
Packit |
9a2dfb |
-- or @aeson@ that uses this function.
|
|
Packit |
9a2dfb |
encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
|
|
Packit |
9a2dfb |
encodeUtf8BuilderEscaped be =
|
|
Packit |
9a2dfb |
-- manual eta-expansion to ensure inlining works as expected
|
|
Packit |
9a2dfb |
\txt -> B.builder (mkBuildstep txt)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
bound = max 4 $ BP.sizeBound be
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
mkBuildstep (Text arr off len) !k =
|
|
Packit |
9a2dfb |
outerLoop off
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
iend = off + len
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
outerLoop !i0 br@(B.BufferRange op0 ope)
|
|
Packit |
9a2dfb |
| i0 >= iend = k br
|
|
Packit |
9a2dfb |
| outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
|
|
Packit |
9a2dfb |
-- TODO: Use a loop with an integrated bound's check if outRemaining
|
|
Packit |
9a2dfb |
-- is smaller than 8, as this will save on divisions.
|
|
Packit |
9a2dfb |
| otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
outRemaining = (ope `minusPtr` op0) `div` bound
|
|
Packit |
9a2dfb |
inpRemaining = iend - i0
|
|
Packit |
9a2dfb |
|
|
Packit |
9a2dfb |
goPartial !iendTmp = go i0 op0
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
go !i !op
|
|
Packit |
9a2dfb |
| i < iendTmp = case A.unsafeIndex arr i of
|
|
Packit |
9a2dfb |
w | w <= 0x7F ->
|
|
Packit |
9a2dfb |
BP.runB be (fromIntegral w) op >>= go (i + 1)
|
|
Packit |
9a2dfb |
| w <= 0x7FF -> do
|
|
Packit |
9a2dfb |
poke8 0 $ (w `shiftR` 6) + 0xC0
|
|
Packit |
9a2dfb |
poke8 1 $ (w .&. 0x3f) + 0x80
|
|
Packit |
9a2dfb |
go (i + 1) (op `plusPtr` 2)
|
|
Packit |
9a2dfb |
| 0xD800 <= w && w <= 0xDBFF -> do
|
|
Packit |
9a2dfb |
let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
|
|
Packit |
9a2dfb |
poke8 0 $ (c `shiftR` 18) + 0xF0
|
|
Packit |
9a2dfb |
poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
|
|
Packit |
9a2dfb |
poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
|
|
Packit |
9a2dfb |
poke8 3 $ (c .&. 0x3F) + 0x80
|
|
Packit |
9a2dfb |
go (i + 2) (op `plusPtr` 4)
|
|
Packit |
9a2dfb |
| otherwise -> do
|
|
Packit |
9a2dfb |
poke8 0 $ (w `shiftR` 12) + 0xE0
|
|
Packit |
9a2dfb |
poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
|
|
Packit |
9a2dfb |
poke8 2 $ (w .&. 0x3F) + 0x80
|
|
Packit |
9a2dfb |
go (i + 1) (op `plusPtr` 3)
|
|
Packit |
9a2dfb |
| otherwise =
|
|
Packit |
9a2dfb |
outerLoop i (B.BufferRange op ope)
|
|
Packit |
9a2dfb |
where
|
|
Packit |
9a2dfb |
poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
|
|
Packit |
9a2dfb |
#endif
|