{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}

module Buf (Buf (..), SizedStr, SizedBuilder) where

import Data.Char (intToDigit)
import qualified Data.DList as D
import Data.Kind (Type)
import Data.String
import qualified Data.Text as S
import Data.Text.Lazy (Text)
import qualified Data.Text.Lazy as L
import qualified Data.Text.Lazy.Builder as T
import qualified Data.Text.Lazy.Builder.Int as T

newtype Sized a = Sized {forall a. Sized a -> (a, Int)
unSized :: (a, Int)} deriving (Int -> Sized a -> ShowS
forall a. Show a => Int -> Sized a -> ShowS
forall a. Show a => [Sized a] -> ShowS
forall a. Show a => Sized a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Sized a] -> ShowS
$cshowList :: forall a. Show a => [Sized a] -> ShowS
show :: Sized a -> String
$cshow :: forall a. Show a => Sized a -> String
showsPrec :: Int -> Sized a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Sized a -> ShowS
Show, Sized a -> Sized a -> Bool
Sized a -> Sized a -> Ordering
Sized a -> Sized a -> Sized a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall {a}. Ord a => Eq (Sized a)
forall a. Ord a => Sized a -> Sized a -> Bool
forall a. Ord a => Sized a -> Sized a -> Ordering
forall a. Ord a => Sized a -> Sized a -> Sized a
min :: Sized a -> Sized a -> Sized a
$cmin :: forall a. Ord a => Sized a -> Sized a -> Sized a
max :: Sized a -> Sized a -> Sized a
$cmax :: forall a. Ord a => Sized a -> Sized a -> Sized a
>= :: Sized a -> Sized a -> Bool
$c>= :: forall a. Ord a => Sized a -> Sized a -> Bool
> :: Sized a -> Sized a -> Bool
$c> :: forall a. Ord a => Sized a -> Sized a -> Bool
<= :: Sized a -> Sized a -> Bool
$c<= :: forall a. Ord a => Sized a -> Sized a -> Bool
< :: Sized a -> Sized a -> Bool
$c< :: forall a. Ord a => Sized a -> Sized a -> Bool
compare :: Sized a -> Sized a -> Ordering
$ccompare :: forall a. Ord a => Sized a -> Sized a -> Ordering
Ord, Sized a -> Sized a -> Bool
forall a. Eq a => Sized a -> Sized a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Sized a -> Sized a -> Bool
$c/= :: forall a. Eq a => Sized a -> Sized a -> Bool
== :: Sized a -> Sized a -> Bool
$c== :: forall a. Eq a => Sized a -> Sized a -> Bool
Eq)

type SizedStr = Sized (D.DList Char)
type SizedBuilder = Sized T.Builder

instance (IsString a) => IsString (Sized a) where
  fromString :: String -> Sized a
fromString String
s = forall a. (a, Int) -> Sized a
Sized (forall a. IsString a => String -> a
fromString String
s, forall (t :: * -> *) a. Foldable t => t a -> Int
length String
s)

instance (Semigroup a) => Semigroup (Sized a) where
  Sized (a
a, Int
b) <> :: Sized a -> Sized a -> Sized a
<> Sized (a
c, Int
d) = forall a. (a, Int) -> Sized a
Sized (a
a forall a. Semigroup a => a -> a -> a
<> a
c, Int
b forall a. Num a => a -> a -> a
+ Int
d)
  {-# INLINE (<>) #-}

instance (Monoid a) => Monoid (Sized a) where
  mempty :: Sized a
mempty = forall a. (a, Int) -> Sized a
Sized (forall a. Monoid a => a
mempty, Int
0)
  mappend :: Sized a -> Sized a -> Sized a
mappend = forall a. Semigroup a => a -> a -> a
(<>)
  {-# INLINE mappend #-}

class (Monoid a) => Buf a where
  type Output a :: Type

  str :: String -> a

  sText :: S.Text -> a
  sText = forall a. Buf a => String -> a
str forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
S.unpack
  lText :: L.Text -> a
  lText = forall a. Buf a => String -> a
str forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
L.unpack

  singleton :: Char -> a
  digit :: Int -> a
  digit = forall a. Buf a => Char -> a
singleton forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Char
intToDigit
  {-# INLINE digit #-}

  cons :: Char -> a -> a
  cons Char
c a
s = forall a. Buf a => Char -> a
singleton Char
c forall a. Semigroup a => a -> a -> a
<> a
s
  {-# INLINE cons #-}

  repeatN :: Int -> Char -> a
  repeatN Int
n = forall a. Buf a => String -> a
str forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Int -> a -> [a]
replicate Int
n

  size :: a -> Int

  finalize :: a -> Output a

instance Buf SizedStr where
  type Output SizedStr = String
  str :: String -> SizedStr
str String
a = forall a. (a, Int) -> Sized a
Sized (forall a. [a] -> DList a
D.fromList String
a, forall (t :: * -> *) a. Foldable t => t a -> Int
length String
a)
  singleton :: Char -> SizedStr
singleton Char
c = forall a. (a, Int) -> Sized a
Sized (forall a. a -> DList a
D.singleton Char
c, Int
1)
  finalize :: SizedStr -> Output SizedStr
finalize = forall a. DList a -> [a]
D.toList forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Sized a -> (a, Int)
unSized
  cons :: Char -> SizedStr -> SizedStr
cons Char
c (Sized (DList Char
r, Int
m)) = forall a. (a, Int) -> Sized a
Sized (forall a. a -> DList a -> DList a
D.cons Char
c DList Char
r, Int
m forall a. Num a => a -> a -> a
+ Int
1)
  repeatN :: Int -> Char -> SizedStr
repeatN Int
n Char
c = forall a. (a, Int) -> Sized a
Sized (forall a. Int -> a -> DList a
D.replicate Int
n Char
c, Int
n)
  size :: SizedStr -> Int
size = forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Sized a -> (a, Int)
unSized

instance Buf SizedBuilder where
  type Output SizedBuilder = Text
  str :: String -> SizedBuilder
str String
a = forall a. (a, Int) -> Sized a
Sized (forall a. IsString a => String -> a
fromString String
a, forall (t :: * -> *) a. Foldable t => t a -> Int
length String
a)
  sText :: Text -> SizedBuilder
sText Text
a = forall a. (a, Int) -> Sized a
Sized (Text -> Builder
T.fromText Text
a, Text -> Int
S.length Text
a)
  lText :: Text -> SizedBuilder
lText Text
a = forall a. (a, Int) -> Sized a
Sized (Text -> Builder
T.fromLazyText Text
a, forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Int64
L.length Text
a))
  singleton :: Char -> SizedBuilder
singleton Char
c = forall a. (a, Int) -> Sized a
Sized (Char -> Builder
T.singleton Char
c, Int
1)
  digit :: Int -> SizedBuilder
digit Int
c = forall a. (a, Int) -> Sized a
Sized (forall a. Integral a => a -> Builder
T.hexadecimal Int
c, Int
1)
  finalize :: SizedBuilder -> Output SizedBuilder
finalize = Builder -> Text
T.toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Sized a -> (a, Int)
unSized
  size :: SizedBuilder -> Int
size = forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Sized a -> (a, Int)
unSized