Skip to content

Commit

Permalink
instance IsList Builder (#672)
Browse files Browse the repository at this point in the history
  • Loading branch information
BebeSparkelSparkel committed Jun 20, 2024
1 parent 595847e commit d1aebfb
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 13 deletions.
9 changes: 0 additions & 9 deletions Data/ByteString/Builder.hs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ import Prelude hiding (writeFile)

import Data.ByteString.Builder.Internal
import qualified Data.ByteString.Builder.Prim as P
import qualified Data.ByteString.Lazy.Internal as L
import Data.ByteString.Builder.ASCII
import Data.ByteString.Builder.RealFloat

Expand All @@ -265,14 +264,6 @@ import Foreign
import GHC.Base (unpackCString#, unpackCStringUtf8#,
unpackFoldrCString#, build)

-- | Execute a 'Builder' and return the generated chunks as a 'L.LazyByteString'.
-- The work is performed lazy, i.e., only when a chunk of the 'L.LazyByteString'
-- is forced.
{-# NOINLINE toLazyByteString #-} -- ensure code is shared
toLazyByteString :: Builder -> L.LazyByteString
toLazyByteString = toLazyByteStringWith
(safeStrategy L.smallChunkSize L.defaultChunkSize) L.Empty

{- Not yet stable enough.
See note on 'hPut' in Data.ByteString.Builder.Internal
-}
Expand Down
27 changes: 23 additions & 4 deletions Data/ByteString/Builder/Internal.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{-# LANGUAGE Unsafe #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE NoMonoLocalBinds #-}

{-# OPTIONS_HADDOCK not-home #-}

Expand Down Expand Up @@ -99,6 +101,7 @@ module Data.ByteString.Builder.Internal (
, lazyByteString

-- ** Execution
, toLazyByteString
, toLazyByteStringWith
, AllocationStrategy
, safeStrategy
Expand Down Expand Up @@ -129,6 +132,7 @@ module Data.ByteString.Builder.Internal (

import Control.Arrow (second)
import Control.DeepSeq (NFData(..))
import GHC.Exts (IsList(..))

import Data.Semigroup (Semigroup(..))
import Data.List.NonEmpty (NonEmpty(..))
Expand Down Expand Up @@ -426,6 +430,13 @@ instance Monoid Builder where
{-# INLINE mconcat #-}
mconcat = foldr mappend mempty

-- | For long or infinite lists use 'fromList' because it uses 'LazyByteString' otherwise use 'fromListN' which uses 'StrictByteString'.
instance IsList Builder where
type Item Builder = Word8
fromList = lazyByteString . fromList
fromListN n = byteString . fromListN n
toList = toList . toLazyByteString

-- | Flush the current buffer. This introduces a chunk boundary.
{-# INLINE flush #-}
flush :: Builder
Expand Down Expand Up @@ -1052,27 +1063,35 @@ safeStrategy firstSize bufSize =
nextBuffer Nothing = newBuffer $ sanitize firstSize
nextBuffer (Just (_, minSize)) = newBuffer minSize

-- | Execute a 'Builder' and return the generated chunks as a 'L.LazyByteString'.
-- The work is performed lazy, i.e., only when a chunk of the 'L.LazyByteString'
-- is forced.
{-# NOINLINE toLazyByteString #-} -- ensure code is shared
toLazyByteString :: Builder -> L.LazyByteString
toLazyByteString = toLazyByteStringWith
(safeStrategy L.smallChunkSize L.defaultChunkSize) L.Empty

-- | /Heavy inlining./ Execute a 'Builder' with custom execution parameters.
--
-- This function is inlined despite its heavy code-size to allow fusing with
-- the allocation strategy. For example, the default 'Builder' execution
-- function 'Data.ByteString.Builder.toLazyByteString' is defined as follows.
-- function 'Data.ByteString.Builder.Internal.toLazyByteString' is defined as follows.
--
-- @
-- {-\# NOINLINE toLazyByteString \#-}
-- toLazyByteString =
-- toLazyByteStringWith ('safeStrategy' 'L.smallChunkSize' 'L.defaultChunkSize') L.empty
-- toLazyByteStringWith ('safeStrategy' 'L.smallChunkSize' 'L.defaultChunkSize') L.Empty
-- @
--
-- where @L.empty@ is the zero-length 'L.LazyByteString'.
-- where @L.Empty@ is the zero-length 'L.LazyByteString'.
--
-- In most cases, the parameters used by 'Data.ByteString.Builder.toLazyByteString' give good
-- performance. A sub-performing case of 'Data.ByteString.Builder.toLazyByteString' is executing short
-- (<128 bytes) 'Builder's. In this case, the allocation overhead for the first
-- 4kb buffer and the trimming cost dominate the cost of executing the
-- 'Builder'. You can avoid this problem using
--
-- >toLazyByteStringWith (safeStrategy 128 smallChunkSize) L.empty
-- >toLazyByteStringWith (safeStrategy 128 smallChunkSize) L.Empty
--
-- This reduces the allocation and trimming overhead, as all generated
-- 'L.LazyByteString's fit into the first buffer and there is no trimming
Expand Down
1 change: 1 addition & 0 deletions Data/ByteString/Internal/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ instance Read ByteString where
instance IsList ByteString where
type Item ByteString = Word8
fromList = packBytes
fromListN n = fst . packUptoLenBytes n
toList = unpackBytes

-- | Beware: 'fromString' truncates multi-byte characters to octets.
Expand Down

0 comments on commit d1aebfb

Please sign in to comment.