packages feed

array-builder 0.1.1.0 → 0.1.2.0

raw patch · 7 files changed

+295/−31 lines, 7 filesdep +bytebuilddep +byteslicedep +text-shortdep ~primitivePVP ok

version bump matches the API change (PVP)

Dependencies added: bytebuild, byteslice, text-short

Dependency ranges changed: primitive

API changes (from Hackage documentation)

+ Data.Builder.Catenable: Append :: !Builder a -> !Builder a -> Builder a
+ Data.Builder.Catenable: Cons :: a -> !Builder a -> Builder a
+ Data.Builder.Catenable: Empty :: Builder a
+ Data.Builder.Catenable: Snoc :: !Builder a -> a -> Builder a
+ Data.Builder.Catenable: data Builder a
+ Data.Builder.Catenable: infixl 5 :>
+ Data.Builder.Catenable: infixr 5 :<
+ Data.Builder.Catenable: instance GHC.Base.Monoid (Data.Builder.Catenable.Builder a)
+ Data.Builder.Catenable: instance GHC.Base.Semigroup (Data.Builder.Catenable.Builder a)
+ Data.Builder.Catenable: instance GHC.Exts.IsList (Data.Builder.Catenable.Builder a)
+ Data.Builder.Catenable: pattern (:>) :: Builder a -> a -> Builder a
+ Data.Builder.Catenable: run :: Builder a -> Chunks a
+ Data.Builder.Catenable.Bytes: Append :: !Builder -> !Builder -> Builder
+ Data.Builder.Catenable.Bytes: Cons :: {-# UNPACK #-} !Bytes -> !Builder -> Builder
+ Data.Builder.Catenable.Bytes: Empty :: Builder
+ Data.Builder.Catenable.Bytes: Snoc :: !Builder -> {-# UNPACK #-} !Bytes -> Builder
+ Data.Builder.Catenable.Bytes: data Builder
+ Data.Builder.Catenable.Bytes: infixl 5 :>
+ Data.Builder.Catenable.Bytes: infixr 5 :<
+ Data.Builder.Catenable.Bytes: instance GHC.Base.Monoid Data.Builder.Catenable.Bytes.Builder
+ Data.Builder.Catenable.Bytes: instance GHC.Base.Semigroup Data.Builder.Catenable.Bytes.Builder
+ Data.Builder.Catenable.Bytes: pattern (:>) :: Builder -> Bytes -> Builder
+ Data.Builder.Catenable.Bytes: run :: Builder -> Chunks
+ Data.Builder.Catenable.Text: Append :: !Builder -> !Builder -> Builder
+ Data.Builder.Catenable.Text: Cons :: !ShortText -> !Builder -> Builder
+ Data.Builder.Catenable.Text: Empty :: Builder
+ Data.Builder.Catenable.Text: Snoc :: !Builder -> !ShortText -> Builder
+ Data.Builder.Catenable.Text: data Builder
+ Data.Builder.Catenable.Text: infixl 5 :>
+ Data.Builder.Catenable.Text: infixr 5 :<
+ Data.Builder.Catenable.Text: instance Data.String.IsString Data.Builder.Catenable.Text.Builder
+ Data.Builder.Catenable.Text: instance GHC.Base.Monoid Data.Builder.Catenable.Text.Builder
+ Data.Builder.Catenable.Text: instance GHC.Base.Semigroup Data.Builder.Catenable.Text.Builder
+ Data.Builder.Catenable.Text: pattern (:>) :: Builder -> ShortText -> Builder
+ Data.Builder.Catenable.Text: run :: Builder -> Chunks

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for array-builder +## 0.1.2.0 -- 2022-08-09++* Add `Data.Builder.Catenable` module.+* Add `Data.Builder.Catenable.Bytes` module.+* Add `Data.Builder.Catenable.Text` module.+ ## 0.1.1.0 -- 2020-11-18  * Add `new1`.
array-builder.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: array-builder-version: 0.1.1.0+version: 0.1.2.0 synopsis: Builders for arrays homepage: https://github.com/andrewthad/array-builder bug-reports: https://github.com/andrewthad/array-builder/issues@@ -15,14 +15,20 @@ library   exposed-modules:     Data.Builder+    Data.Builder.Catenable+    Data.Builder.Catenable.Bytes+    Data.Builder.Catenable.Text     Data.Builder.ST   other-modules:     Compat   build-depends:     , array-chunks >=0.1 && <0.2     , base >=4.12 && <5+    , bytebuild >=0.3.5+    , byteslice >=0.2.7     , primitive >=0.6.4 && <0.8     , run-st >=0.1 && <0.2+    , text-short >=0.1.3   hs-source-dirs: src   if impl(ghc >= 8.9)     hs-source-dirs: src-post-8.9
+ src/Data/Builder/Catenable.hs view
@@ -0,0 +1,95 @@+{-# language BangPatterns #-}+{-# language PatternSynonyms #-}+{-# language TypeFamilies #-}++-- | Builder with cheap concatenation. Like the builder type from+-- @Data.Builder.ST@, this builder can be stored somewhere and this used+-- again later. However, this builder type has several advantages:+--+-- * Supports both cons and snoc (@Data.Builder.ST@ only supports snoc)+-- * No linear-use restriction+-- * Extremely cheap concatenation (not supported by @Data.Builder.ST@ at all)+--+-- In exchange for all of these, this implementation trades performance.+-- Performance is degraded for two reasons:+--+-- * Evaluation of the builder is deferred, and the evaluation requires walking+--   a tree of nodes.+-- * This builder stores individual elements rather than chunks. There is+--   no fundamental reason for this. It is possible to store a SmallArray+--   in each Cons and Snoc instead, but this makes the implementation a+--   little more simple.+--+-- One reason to prefer this module instead of @Data.Builder.ST@ is that+-- this module lets the user works with builder in a more monoidal style+-- rather than a stateful style. Consider a data type with several fields+-- that is being converted to a builder. Here, @Data.Builder.ST@+-- would require that @Builder@ appear as both an argument and an result for+-- each field\'s encode function. The linearly-used builder must be threaded+-- through by hand or by clever use of @StateT@. With @Data.Builder.Catenable@,+-- the encode functions only need return the builder.+module Data.Builder.Catenable+  ( -- * Type+    Builder(..)+    -- * Convenient infix operators+  , pattern (:<)+  , pattern (:>)+    -- * Run+  , run+  ) where++import Control.Monad.ST (ST,runST)+import Data.Chunks (Chunks)+import Data.Foldable (foldl')+import GHC.Exts (IsList(..))++import qualified Data.Builder.ST as STB+import qualified Data.Chunks as Chunks++infixr 5 :<+infixl 5 :>++data Builder a+  = Empty+  | Cons a !(Builder a)+  | Snoc !(Builder a) a+  | Append !(Builder a) !(Builder a)++instance Monoid (Builder a) where+  {-# inline mempty #-}+  mempty = Empty++instance Semigroup (Builder a) where+  {-# inline (<>) #-}+  (<>) = Append++instance IsList (Builder a) where+  type Item (Builder a) = a+  toList = toList . Chunks.concat . run+  fromList = foldl' (\acc x -> acc :> x) Empty++pattern (:<) :: a -> Builder a -> Builder a+pattern (:<) x y = Cons x y++pattern (:>) :: Builder a -> a -> Builder a+pattern (:>) x y = Snoc x y++run :: Builder a -> Chunks a+{-# noinline run #-}+run b = runST $ do+  bldr0 <- STB.new+  bldr1 <- pushCatenable bldr0 b+  STB.freeze bldr1++pushCatenable :: STB.Builder s a -> Builder a -> ST s (STB.Builder s a)+pushCatenable !bldr0 b = case b of+  Empty -> pure bldr0+  Cons x b1 -> do+    bldr1 <- STB.push x bldr0+    pushCatenable bldr1 b1+  Snoc b1 x -> do+    bldr1 <- pushCatenable bldr0 b1+    STB.push x bldr1+  Append x y -> do+    bldr1 <- pushCatenable bldr0 x+    pushCatenable bldr1 y
+ src/Data/Builder/Catenable/Bytes.hs view
@@ -0,0 +1,65 @@+{-# language BangPatterns #-}+{-# language PatternSynonyms #-}++-- | @Data.Builder.Bytes@ specialized to @Bytes@.+module Data.Builder.Catenable.Bytes+  ( -- * Type+    Builder(..)+    -- * Convenient infix operators+  , pattern (:<)+  , pattern (:>)+    -- * Run+  , run+  ) where++import Control.Monad.ST (ST,runST)+import Data.Bytes (Bytes)+import Data.Bytes.Chunks (Chunks(ChunksNil))++import qualified Data.Bytes.Builder as BB+import qualified Data.Bytes.Builder.Unsafe as BBU++infixr 5 :<+infixl 5 :>++data Builder+  = Empty+  | Cons {-# UNPACK #-} !Bytes !Builder+  | Snoc !Builder {-# UNPACK #-} !Bytes+  | Append !Builder !Builder++instance Monoid Builder where+  {-# inline mempty #-}+  mempty = Empty++instance Semigroup Builder where+  {-# inline (<>) #-}+  (<>) = Append++pattern (:<) :: Bytes -> Builder -> Builder+pattern (:<) x y = Cons x y++pattern (:>) :: Builder -> Bytes -> Builder+pattern (:>) x y = Snoc x y++run :: Builder -> Chunks+{-# noinline run #-}+run b = runST $ do+  bldr0 <- BBU.newBuilderState 128+  bldr1 <- pushCatenable bldr0 b+  BBU.reverseCommitsOntoChunks ChunksNil (BBU.closeBuilderState bldr1)++pushCatenable :: BBU.BuilderState s -> Builder -> ST s (BBU.BuilderState s)+pushCatenable !bldr0 b = case b of+  Empty -> pure bldr0+  Cons x b1 -> do+    bldr1 <- BBU.pasteST (BB.bytes x) bldr0+    pushCatenable bldr1 b1+  Snoc b1 x -> do+    bldr1 <- pushCatenable bldr0 b1+    BBU.pasteST (BB.bytes x) bldr1+  Append x y -> do+    bldr1 <- pushCatenable bldr0 x+    pushCatenable bldr1 y++
+ src/Data/Builder/Catenable/Text.hs view
@@ -0,0 +1,75 @@+{-# language BangPatterns #-}+{-# language PatternSynonyms #-}++-- | @Data.Builder.Catenable@ specialized to @ShortText@.+module Data.Builder.Catenable.Text+  ( -- * Type+    Builder(..)+    -- * Convenient infix operators+  , pattern (:<)+  , pattern (:>)+    -- * Run+  , run+  ) where++import Control.Monad.ST (ST,runST)+import Data.Text.Short (ShortText)+import Data.Bytes.Chunks (Chunks(ChunksNil))+import Data.String (IsString(fromString))++import qualified Data.Text.Short as TS+import qualified Data.Bytes.Builder as BB+import qualified Data.Bytes.Builder.Unsafe as BBU++infixr 5 :<+infixl 5 :>++data Builder+  = Empty+  | Cons !ShortText !Builder+  | Snoc !Builder !ShortText+  | Append !Builder !Builder++-- | Note: The choice of appending to the left side of @Empty@ instead+-- of the right side of arbitrary. Under ordinary use, this difference+-- cannot be observed by the user.+instance IsString Builder where+  fromString t = Cons (TS.pack t) Empty++instance Monoid Builder where+  {-# inline mempty #-}+  mempty = Empty++instance Semigroup Builder where+  {-# inline (<>) #-}+  (<>) = Append++pattern (:<) :: ShortText -> Builder -> Builder+pattern (:<) x y = Cons x y++pattern (:>) :: Builder -> ShortText -> Builder+pattern (:>) x y = Snoc x y++-- | The result is chunks, but this is guaranteed to be UTF-8 encoded+-- text, so if needed, you can flatten out the chunks and convert back+-- to @ShortText@.+run :: Builder -> Chunks+{-# noinline run #-}+run b = runST $ do+  bldr0 <- BBU.newBuilderState 128+  bldr1 <- pushCatenable bldr0 b+  BBU.reverseCommitsOntoChunks ChunksNil (BBU.closeBuilderState bldr1)++pushCatenable :: BBU.BuilderState s -> Builder -> ST s (BBU.BuilderState s)+pushCatenable !bldr0 b = case b of+  Empty -> pure bldr0+  Cons x b1 -> do+    bldr1 <- BBU.pasteST (BB.shortTextUtf8 x) bldr0+    pushCatenable bldr1 b1+  Snoc b1 x -> do+    bldr1 <- pushCatenable bldr0 b1+    BBU.pasteST (BB.shortTextUtf8 x) bldr1+  Append x y -> do+    bldr1 <- pushCatenable bldr0 x+    pushCatenable bldr1 y+
src/Data/Builder/ST.hs view
@@ -5,7 +5,7 @@   , new   , new1   , push-  , freeze +  , freeze   ) where  import Compat (unsafeShrinkAndFreeze)
test/Main.hs view
@@ -1,4 +1,5 @@ {-# language BangPatterns #-}+{-# language PatternSynonyms #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-} @@ -6,6 +7,9 @@ import Test.Tasty (defaultMain,testGroup,TestTree) import Test.Tasty.HUnit ((@=?)) import Data.Semigroup (stimes)+import Data.Builder.Catenable (pattern (:<), pattern (:>))++import qualified Data.Builder.Catenable as Cat import qualified Data.List as L import qualified Data.Foldable as F import qualified Test.Tasty.HUnit as THU@@ -15,37 +19,50 @@  tests :: TestTree tests = testGroup "Tests"-  [ THU.testCase "A" $ "ABCDEF" @=?-    ( F.toList $ run-      (  singleton 'A'-      <> singleton 'B'-      <> singleton 'C'-      <> singleton 'D'-      <> singleton 'E'-      <> singleton 'F'+  [ testGroup "Data.Builder"+    [ THU.testCase "A" $ "ABCDEF" @=?+      ( F.toList $ run+        (  singleton 'A'+        <> singleton 'B'+        <> singleton 'C'+        <> singleton 'D'+        <> singleton 'E'+        <> singleton 'F'+        )       )-    )-  , THU.testCase "B" $ "ABCCCCCCCCCCCCCCCD" @=?-    ( F.toList $ run-      (  singleton 'A'-      <> singleton 'B'-      <> stimes (15 :: Int) (singleton 'C')-      <> singleton 'D'+    , THU.testCase "B" $ "ABCCCCCCCCCCCCCCCD" @=?+      ( F.toList $ run+        (  singleton 'A'+        <> singleton 'B'+        <> stimes (15 :: Int) (singleton 'C')+        <> singleton 'D'+        )       )-    )-  , THU.testCase "C" $ (L.replicate 500 'X') @=?-    (F.toList $ run (stimes (500 :: Int) (singleton 'X')))-  , THU.testCase "D" $ "ACDCDCDCDCDCDCDCDCDX" @=?-    ( F.toList $ run-      (  singleton 'A'-      <> stimes (9 :: Int) (doubleton 'C' 'D')-      <> singleton 'X'+    , THU.testCase "C" $ (L.replicate 500 'X') @=?+      (F.toList $ run (stimes (500 :: Int) (singleton 'X')))+    , THU.testCase "D" $ "ACDCDCDCDCDCDCDCDCDX" @=?+      ( F.toList $ run+        (  singleton 'A'+        <> stimes (9 :: Int) (doubleton 'C' 'D')+        <> singleton 'X'+        )       )-    )-  , THU.testCase "E" $ "ABCABCABCABCABCABCABCX" @=?-    ( F.toList $ run-      (  stimes (7 :: Int) (tripleton 'A' 'B' 'C')-      <> singleton 'X'+    , THU.testCase "E" $ "ABCABCABCABCABCABCABCX" @=?+      ( F.toList $ run+        (  stimes (7 :: Int) (tripleton 'A' 'B' 'C')+        <> singleton 'X'+        )       )-    )+    ]+  , testGroup "Data.Builder.Catenable"+    [ THU.testCase "A" $ "ABCDEF" @=?+      ( F.toList $ Cat.run+        ( ('A' :< 'B' :< 'C' :< mempty)+          <>+          (mempty :> 'D' :> 'E' :> 'F')+        )+      )+    , THU.testCase "B" $ "DEF" @=?+      (F.toList $ Cat.run (mempty :> 'D' :> 'E' :> 'F'))+    ]   ]