text-builder-linear-0.1.2: src/Data/Text/Builder/Linear/Buffer.hs
-- |
-- Copyright: (c) 2022 Andrew Lelechenko
-- (c) 2023 Pierre Le Marre
-- Licence: BSD3
-- Maintainer: Andrew Lelechenko <andrew.lelechenko@gmail.com>
--
-- 'Buffer' for strict 'Text', based on linear types.
module Data.Text.Builder.Linear.Buffer (
-- * Type
Buffer,
-- * Basic interface
runBuffer,
runBufferBS,
dupBuffer,
consumeBuffer,
eraseBuffer,
foldlIntoBuffer,
newEmptyBuffer,
(><),
-- * Single character
(|>.),
(.<|),
-- * Multiple characters
-- ** Character replication
prependChars,
appendChars,
-- ** Text
(|>),
(<|),
(|>…),
(…<|),
-- ** Raw 'Addr#'
(|>#),
( #<| ), -- NOTE: extra spaces required because of -XUnboxedTuples
(<|#),
-- * Padding
justifyLeft,
justifyRight,
center,
-- * Number formatting
-- ** Decimal
(|>$),
($<|),
-- ** Hexadecimal
-- *** Lower-case
(|>&),
(&<|),
-- *** Upper-case and padding
-- $custom_hexadecimal
-- ** Double
(|>%),
(%<|),
) where
import Data.Text.Array qualified as A
import Data.Text.Internal (Text (..))
import GHC.Exts (Addr#, Int (..), Ptr (..), cstringLength#, setByteArray#)
import GHC.ST (ST (..))
import Data.Text.Builder.Linear.Char
import Data.Text.Builder.Linear.Core
import Data.Text.Builder.Linear.Dec
import Data.Text.Builder.Linear.Double
import Data.Text.Builder.Linear.Hex
-- | Append 'Text' suffix to a 'Buffer' by mutating it.
-- If a suffix is statically known, consider using '(|>#)' for optimal performance.
--
-- >>> :set -XOverloadedStrings -XLinearTypes
-- >>> runBuffer (\b -> b |> "foo" |> "bar")
-- "foobar"
(|>) ∷ Buffer ⊸ Text → Buffer
infixl 6 |>
buffer |> (Text src srcOff srcLen) =
appendExact
srcLen
(\dst dstOff → A.copyI srcLen dst dstOff src srcOff)
buffer
-- | Prepend 'Text' prefix to a 'Buffer' by mutating it.
-- If a prefix is statically known, consider using '(#<|)' for optimal performance.
--
-- >>> :set -XOverloadedStrings -XLinearTypes
-- >>> runBuffer (\b -> "foo" <| "bar" <| b)
-- "foobar"
(<|) ∷ Text → Buffer ⊸ Buffer
infixr 6 <|
Text src srcOff srcLen <| buffer =
prependExact
srcLen
(\dst dstOff → A.copyI srcLen dst dstOff src srcOff)
buffer
-- | Append a null-terminated UTF-8 string
-- to a 'Buffer' by mutating it. E. g.,
--
-- >>> :set -XOverloadedStrings -XLinearTypes -XMagicHash
-- >>> runBuffer (\b -> b |># "foo"# |># "bar"#)
-- "foobar"
--
-- The literal string must not contain zero bytes @\\0@ and must be a valid UTF-8,
-- these conditions are not checked.
(|>#) ∷ Buffer ⊸ Addr# → Buffer
infixl 6 |>#
buffer |># addr# =
appendExact
srcLen
(\dst dstOff → A.copyFromPointer dst dstOff (Ptr addr#) srcLen)
buffer
where
srcLen = I# (cstringLength# addr#)
-- | Prepend a null-terminated UTF-8 string
-- to a 'Buffer' by mutating it. E. g.,
--
-- >>> :set -XOverloadedStrings -XLinearTypes -XMagicHash
-- >>> runBuffer (\b -> "foo"# #<| "bar"# #<| b)
-- "foobar"
--
-- The literal string must not contain zero bytes @\\0@ and must be a valid UTF-8,
-- these conditions are not checked.
--
-- /Note:/ When the syntactic extensions @UnboxedTuples@ or @UnboxedSums@ are
-- enabled, extra spaces are required when using parentheses: i.e. use @( '#<|' )@
-- instead of @('#<|')@. See the GHC User Guide chapter
-- “[Unboxed types and primitive operations](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/primitives.html#unboxed-tuples)”
-- for further information.
( #<| ) ∷ Addr# → Buffer ⊸ Buffer
infixr 6 #<|, <|#
addr# #<| buffer =
prependExact
srcLen
(\dst dstOff → A.copyFromPointer dst dstOff (Ptr addr#) srcLen)
buffer
where
srcLen = I# (cstringLength# addr#)
-- | Alias for @'(#<|)'@.
{-# DEPRECATED (<|#) "Use '(#<|)' instead" #-}
(<|#) ∷ Addr# → Buffer ⊸ Buffer
(<|#) = ( #<| ) -- NOTE: extra spaces required because of -XUnboxedTuples
{-# INLINE (<|#) #-}
-- | Append given number of spaces.
(|>…) ∷ Buffer ⊸ Word → Buffer
infixr 6 |>…
buf |>… 0 = buf
buffer |>… (fromIntegral → spaces@(I# spaces#)) =
appendExact
spaces
( \(A.MutableByteArray dst#) (I# dstOff#) →
ST
( \s# →
(# setByteArray# dst# dstOff# spaces# 32# s#, () #)
)
)
buffer
-- | Prepend given number of spaces.
(…<|) ∷ Word → Buffer ⊸ Buffer
infixr 6 …<|
0 …<| buf = buf
(fromIntegral → spaces@(I# spaces#)) …<| buffer =
prependExact
spaces
( \(A.MutableByteArray dst#) (I# dstOff#) →
ST
( \s# →
(# setByteArray# dst# dstOff# spaces# 32# s#, () #)
)
)
buffer
-- | This is just a normal 'Data.List.foldl'', but with a linear arrow
-- and unlifted accumulator.
foldlIntoBuffer ∷ ∀ a. (Buffer ⊸ a → Buffer) → Buffer ⊸ [a] → Buffer
foldlIntoBuffer f = go
where
go ∷ Buffer ⊸ [a] → Buffer
go !acc [] = acc
go !acc (x : xs) = go (f acc x) xs
-- $custom_hexadecimal
--
-- Note that no /upper/ case hexadecimal formatting is provided. This package
-- provides a minimal API with utility functions only for common cases. For
-- other use cases, please adapt the code of this package, e.g. as shown in
-- the [Unicode code point example](https://github.com/Bodigrim/linear-builder/examples/src/Examples/Unicode.hs).