-- |
-- Copyright:   (c) 2022 Andrew Lelechenko
-- Licence:     BSD3
-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
module Data.Text.Builder.Linear.Char (
  -- * Buffer
  (|>.),
  (.<|),
) where

import Data.Text.Array qualified as A
import Data.Text.Internal.Encoding.Utf8 (ord2, ord3, ord4, utf8Length)
import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
import GHC.ST (ST)

import Data.Text.Builder.Linear.Core

-- | Append 'Char' to a 'Buffer' by mutating it.
--
-- >>> :set -XLinearTypes
-- >>> runBuffer (\b -> b |>. 'q' |>. 'w')
-- "qw"
--
-- In contrast to 'Data.Text.Lazy.Builder.singleton', it's a responsibility
-- of the caller to sanitize surrogate code points with 'Data.Text.Internal.safe'.
(|>.)  Buffer  Char  Buffer

infixl 6 |>.
Buffer
buffer |>. :: Buffer %1 -> Char -> Buffer
|>. Char
ch = Int
-> (forall s. MArray s -> Int -> ST s Int) -> Buffer %1 -> Buffer
appendBounded Int
4 (\MArray s
dst Int
dstOff  forall s. MArray s -> Int -> Char -> ST s Int
unsafeWrite MArray s
dst Int
dstOff Char
ch) Buffer
buffer

-- | Prepend 'Char' to a 'Buffer' by mutating it.
--
-- >>> :set -XLinearTypes
-- >>> runBuffer (\b -> 'q' .<| 'w' .<| b)
-- "qw"
--
-- In contrast to 'Data.Text.Lazy.Builder.singleton', it's a responsibility
-- of the caller to sanitize surrogate code points with 'Data.Text.Internal.safe'.
(.<|)  Char  Buffer  Buffer

infixr 6 .<|
Char
ch .<| :: Char -> Buffer %1 -> Buffer
.<| Buffer
buffer =
  Int
-> (forall s. MArray s -> Int -> ST s Int)
-> (forall s. MArray s -> Int -> ST s Int)
-> Buffer
%1 -> Buffer
prependBounded
    Int
4
    (\MArray s
dst Int
dstOff  forall s. MArray s -> Int -> Char -> ST s Int
unsafePrependCharM MArray s
dst Int
dstOff Char
ch)
    (\MArray s
dst Int
dstOff  forall s. MArray s -> Int -> Char -> ST s Int
unsafeWrite MArray s
dst Int
dstOff Char
ch)
    Buffer
buffer

-- | Similar to 'Data.Text.Internal.Unsafe.Char.unsafeWrite',
-- but writes _before_ a given offset.
unsafePrependCharM  A.MArray s  Int  Char  ST s Int
unsafePrependCharM :: forall s. MArray s -> Int -> Char -> ST s Int
unsafePrependCharM MArray s
marr Int
off Char
c = case Char -> Int
utf8Length Char
c of
  Int
1  do
    let n0 :: Word8
n0 = forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
ord Char
c)
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
1) Word8
n0
    forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
1
  Int
2  do
    let (Word8
n0, Word8
n1) = Char -> (Word8, Word8)
ord2 Char
c
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
2) Word8
n0
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
1) Word8
n1
    forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
2
  Int
3  do
    let (Word8
n0, Word8
n1, Word8
n2) = Char -> (Word8, Word8, Word8)
ord3 Char
c
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
3) Word8
n0
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
2) Word8
n1
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
1) Word8
n2
    forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
3
  Int
_  do
    let (Word8
n0, Word8
n1, Word8
n2, Word8
n3) = Char -> (Word8, Word8, Word8, Word8)
ord4 Char
c
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
4) Word8
n0
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
3) Word8
n1
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
2) Word8
n2
    forall s. MArray s -> Int -> Word8 -> ST s ()
A.unsafeWrite MArray s
marr (Int
off forall a. Num a => a -> a -> a
- Int
1) Word8
n3
    forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
4