Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

instance IsList Builder #672

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
BebeSparkelSparkel marked this conversation as resolved.
Show resolved Hide resolved
--
-- @
-- {-\# 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
Loading