packages feed

text-builder-linear-0.1: src/Data/Text/Builder/Linear/Double.hs

-- |
-- Copyright:   (c) 2022 Andrew Lelechenko
-- Licence:     BSD3
-- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>

module Data.Text.Builder.Linear.Double
  ( (|>%)
  , (%<|)
  ) where

import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Builder.Internal as BBI
import qualified Data.Text.Array as A
import Data.Word (Word8)
import GHC.Exts (Ptr(..))
import GHC.ForeignPtr (touchForeignPtr, unsafeForeignPtrToPtr, unsafeWithForeignPtr, ForeignPtr)
import GHC.IO (unsafeIOToST, unsafeSTToIO, unsafeDupablePerformIO)
import GHC.Ptr (minusPtr)
import GHC.ST (ST)

import Data.Text.Builder.Linear.Core

-- | Append double.
(|>%) :: Buffer ⊸ Double → Buffer
infixl 6 |>%
buffer |>% x = appendBounded
  maxDblLen
  (\dst dstOff → unsafeAppendDouble dst dstOff x)
  buffer

-- | Prepend double
(%<|) :: Double → Buffer ⊸ Buffer
infixr 6 %<|
x %<| buffer = prependBounded
  maxDblLen
  (\dst dstOff → unsafePrependDouble dst dstOff x)
  (\dst dstOff → unsafeAppendDouble dst dstOff x)
  buffer

unsafeAppendDouble :: A.MArray s → Int → Double → ST s Int
unsafeAppendDouble dst !dstOff !x = do
  let (fp, !srcLen) = runDoubleBuilder x
  unsafeIOToST $ unsafeWithForeignPtr fp $ \(Ptr addr#) →
    unsafeSTToIO $ A.copyFromPointer dst dstOff (Ptr addr#) srcLen
  pure srcLen

unsafePrependDouble :: A.MArray s → Int → Double → ST s Int
unsafePrependDouble dst !dstOff !x = do
  let (fp, !srcLen) = runDoubleBuilder x
  unsafeIOToST $ unsafeWithForeignPtr fp $ \(Ptr addr#) →
    unsafeSTToIO $ A.copyFromPointer dst (dstOff - srcLen) (Ptr addr#) srcLen
  pure srcLen

runDoubleBuilder :: Double → (ForeignPtr Word8, Int)
runDoubleBuilder =
  unsafeDupablePerformIO . buildStepToFirstChunk . BBI.runBuilder . BB.doubleDec
{-# INLINE runDoubleBuilder #-}

buildStepToFirstChunk :: BBI.BuildStep a → IO (ForeignPtr Word8, Int)
buildStepToFirstChunk = \step → BBI.newBuffer maxDblLen >>= fill step
  where
    fill !step (BBI.Buffer fpbuf br) = do
      let doneH op' _ = pure (fpbuf, op' `minusPtr` unsafeForeignPtrToPtr fpbuf)
          fullH _ _ nextStep = BBI.newBuffer maxDblLen >>= fill nextStep
      res ← BBI.fillWithBuildStep step doneH fullH undefined br
      touchForeignPtr fpbuf
      return res

maxDblLen :: Int
maxDblLen = 24 -- length (show (-1.0000000000000004e-308 :: Double))