array-builder-0.1.4.1: src/Data/Builder.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE UnboxedTuples #-}
module Data.Builder
( -- * Builder
Builder (..)
, cons
, singleton
, doubleton
, tripleton
-- * Run
, run
) where
import Compat (unsafeShrinkAndFreeze#)
import Data.Chunks (Chunks (ChunksCons, ChunksNil))
import Data.Primitive (SmallArray (SmallArray))
import GHC.Exts (Int#, SmallMutableArray#, State#, newSmallArray#, runRW#, unsafeFreezeSmallArray#, writeSmallArray#, (*#), (+#), (-#), (>#))
import qualified Data.Chunks as C
-- | Builder for an array of boxed elements.
newtype Builder a
= Builder
-- The chunks being built up are in reverse order.
-- Consequently, functions that run a builder must
-- reverse the chunks at the end.
( forall s.
SmallMutableArray# s a ->
Int# ->
Int# ->
Chunks a ->
State# s ->
(# State# s, SmallMutableArray# s a, Int#, Int#, Chunks a #)
)
run :: Builder a -> Chunks a
run (Builder f) = case runRW#
-- The initial size of 16 elements is chosen somewhat
-- arbitrarily. It is more than enough to saturate a
-- cache line.
( \s0 -> case newSmallArray# 16# errorThunk s0 of
(# s1, marr0 #) -> case f marr0 0# 16# ChunksNil s1 of
(# s2, marr, off, _, cs #) ->
-- Recall that freezeSmallArray copies a slice.
-- If resize functions ever become available for
-- SmallArray, we should use that instead.
case unsafeShrinkAndFreeze# marr off s2 of
(# s3, arr #) ->
let !r =
C.reverseOnto
(ChunksCons (SmallArray arr) ChunksNil)
cs
in (# s3, r #)
) of
(# _, cs #) -> cs
errorThunk :: a
{-# NOINLINE errorThunk #-}
errorThunk = error "array-builder:Data.Builder: error"
instance Monoid (Builder a) where
{-# INLINE mempty #-}
mempty =
Builder
( \marr0 off0 len0 cs0 s0 ->
(# s0, marr0, off0, len0, cs0 #)
)
instance Semigroup (Builder a) where
{-# INLINE (<>) #-}
Builder f <> Builder g =
Builder
( \marr0 off0 len0 cs0 s0 -> case f marr0 off0 len0 cs0 s0 of
(# s1, marr1, off1, len1, cs1 #) ->
g marr1 off1 len1 cs1 s1
)
cons :: a -> Builder a -> Builder a
{-# INLINE cons #-}
cons a b = singleton a <> b
-- | A builder with one element.
singleton :: a -> Builder a
{-# NOINLINE singleton #-}
singleton a =
Builder
( \marr off len cs s0 -> case len ># 0# of
1# -> case writeSmallArray# marr off a s0 of
s1 -> (# s1, marr, off +# 1#, len -# 1#, cs #)
_ -> case unsafeFreezeSmallArray# marr s0 of
(# s1, arr #) ->
let !lenNew = nextLength off
in -- Since we feed the element to newSmallArray#, we do not
-- need to write it to the 0 index.
case newSmallArray# lenNew a s1 of
(# s2, marrNew #) ->
let !csNew = ChunksCons (SmallArray arr) cs
in (# s2, marrNew, 1#, lenNew -# 1#, csNew #)
)
{- | A builder with two elements.
@since 0.1.1.0
-}
doubleton :: a -> a -> Builder a
{-# NOINLINE doubleton #-}
doubleton a b =
Builder
( \marr off len cs s0 -> case len ># 1# of
1# -> case writeSmallArray# marr off a s0 of
s1 -> case writeSmallArray# marr (off +# 1#) b s1 of
s2 -> (# s2, marr, off +# 2#, len -# 2#, cs #)
_ -> case unsafeShrinkAndFreeze# marr off s0 of
(# s1, arr #) ->
let !lenNew = nextLength off
in -- Since we feed the element to newSmallArray#, we do not
-- need to write element a to the 0 index.
case newSmallArray# lenNew a s1 of
(# s2, marrNew #) -> case writeSmallArray# marrNew 1# b s2 of
s3 ->
let !csNew = ChunksCons (SmallArray arr) cs
in (# s3, marrNew, 2#, lenNew -# 2#, csNew #)
)
{- | A builder with three elements.
@since 0.1.1.0
-}
tripleton :: a -> a -> a -> Builder a
{-# NOINLINE tripleton #-}
tripleton a b c =
Builder
( \marr off len cs s0 -> case len ># 1# of
1# -> case writeSmallArray# marr off a s0 of
s1 -> case writeSmallArray# marr (off +# 1#) b s1 of
s2 -> case writeSmallArray# marr (off +# 2#) c s2 of
s3 -> (# s3, marr, off +# 3#, len -# 3#, cs #)
_ -> case unsafeShrinkAndFreeze# marr off s0 of
(# s1, arr #) ->
let !lenNew = nextLength off
in -- Since we feed the element to newSmallArray#, we do not
-- need to write element a to the 0 index.
case newSmallArray# lenNew a s1 of
(# s2, marrNew #) -> case writeSmallArray# marrNew 1# b s2 of
s3 -> case writeSmallArray# marrNew 2# c s3 of
s4 ->
let !csNew = ChunksCons (SmallArray arr) cs
in (# s4, marrNew, 3#, lenNew -# 3#, csNew #)
)
nextLength :: Int# -> Int#
{-# INLINE nextLength #-}
nextLength i = i *# 2#