packages feed

bytebuild 0.3.8.0 → 0.3.9.0

raw patch · 8 files changed

+361/−13 lines, 8 filesdep +haskell-src-metadep +template-haskelldep ~byteslicedep ~text-shortPVP ok

version bump matches the API change (PVP)

Dependencies added: haskell-src-meta, template-haskell

Dependency ranges changed: byteslice, text-short

API changes (from Hackage documentation)

+ Data.Bytes.Builder: cstring# :: Addr# -> Builder
+ Data.Bytes.Builder.Bounded.Class: class ToBoundedBuilder a where {
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder (Data.Bytes.Builder.Bounded.Unsafe.Builder n)
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Int.Int16
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Int.Int32
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Int.Int64
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Int.Int8
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Types.Int
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Types.Word
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Word.Word16
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Word.Word32
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Word.Word64
+ Data.Bytes.Builder.Bounded.Class: instance Data.Bytes.Builder.Bounded.Class.ToBoundedBuilder GHC.Word.Word8
+ Data.Bytes.Builder.Bounded.Class: toBuilder :: ToBoundedBuilder a => a -> Builder (BoundedBuilderLength a)
+ Data.Bytes.Builder.Bounded.Class: type family BoundedBuilderLength a :: Nat;
+ Data.Bytes.Builder.Bounded.Class: }
+ Data.Bytes.Builder.Class: class ToBuilder a
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder Data.ByteString.Short.Internal.ShortByteString
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder Data.Bytes.Builder.Unsafe.Builder
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder Data.Bytes.Internal.Bytes
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder Data.Primitive.ByteArray.ByteArray
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder Data.Text.Short.Internal.ShortText
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Base.String
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Int.Int16
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Int.Int32
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Int.Int64
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Int.Int8
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Types.Double
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Types.Int
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Types.Word
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Word.Word16
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Word.Word32
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Word.Word64
+ Data.Bytes.Builder.Class: instance Data.Bytes.Builder.Class.ToBuilder GHC.Word.Word8
+ Data.Bytes.Builder.Class: toBuilder :: ToBuilder a => a -> Builder
+ Data.Bytes.Builder.Template: bldr :: QuasiQuoter

Files

CHANGELOG.md view
@@ -5,10 +5,17 @@ `small-bytearray-builder` is now just a compatibility shim to ease the migration process. -## 0.3.8.0 -- 2020-??-??+## 0.3.9.0 -- 2021-11-19 +* Add `cstring#`+* Add `ToBuilder` and `ToBoundedBuilder` classes+* Add quasiquoter named `bldr` in `Data.Bytes.Builder.Template`.++## 0.3.8.0 -- 2021-06-25+ * Fix `doubleDec`, which was encoding small numbers incorrectly. * Add `runByteString` for producing `ByteString` from bounded builders.+* Add `cstring#` for producing builder from `Addr#`. * Correct the required length calculation for json string encoding.  ## 0.3.7.0 -- 2020-11-06
bytebuild.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: bytebuild-version: 0.3.8.0+version: 0.3.9.0 synopsis: Serialize to a small byte arrays description:   This is similar to the builder facilities provided by@@ -37,20 +37,25 @@ library   exposed-modules:     Data.Bytes.Builder+    Data.Bytes.Builder.Class+    Data.Bytes.Builder.Template     Data.Bytes.Builder.Unsafe     Data.Bytes.Builder.Bounded+    Data.Bytes.Builder.Bounded.Class     Data.Bytes.Builder.Bounded.Unsafe   reexported-modules:     Data.Bytes.Chunks   build-depends:     , base >=4.12.0.0 && <5-    , byteslice >=0.2.5 && <0.3+    , byteslice >=0.2.6 && <0.3     , bytestring >=0.10.8.2 && <0.11+    , haskell-src-meta >=0.8     , integer-logarithms >=1.0.3 && <1.1     , natural-arithmetic >=0.1 && <0.2     , primitive-offset >=0.2 && <0.3     , primitive-unlifted >=0.1.2 && <0.2     , run-st >=0.1 && <0.2+    , template-haskell >=2.16     , text-short >=0.1.3 && <0.2     , wide-word >=0.1.0.9 && <0.2   if flag(checked)@@ -82,6 +87,7 @@     , primitive-unlifted >=0.1.2     , quickcheck-classes >=0.6.4     , quickcheck-instances >=0.3.22+    , text-short     , tasty >=1.2.3 && <1.3     , tasty-hunit >=0.10.0.2 && <0.11     , tasty-quickcheck >=0.10.1 && <0.11
common/Word16Tree.hs view
@@ -13,6 +13,7 @@ import Data.Word (Word16) import Data.Primitive (ByteArray) import qualified Data.Bytes as Bytes+import qualified Data.Bytes.Text.Ascii  data Word16Tree   = Branch !Word16Tree !Word16Tree@@ -32,7 +33,7 @@   B.ascii ')'  expectedSmall :: ByteArray-expectedSmall = Bytes.toByteArray $ Bytes.fromAsciiString+expectedSmall = Bytes.toByteArray $ Data.Bytes.Text.Ascii.fromString   "((AB59,(1F33,2E71)),((((FA9A,247B),890C),(0F13,((55BF,7CF1),389B))),1205))"  
src/Data/Bytes/Builder.hs view
@@ -29,6 +29,7 @@   , shortTextUtf8   , shortTextJsonString   , cstring+  , cstring#   , cstringLen   , stringUtf8     -- * Encode Integral Types@@ -153,7 +154,7 @@ import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder) import GHC.Exts (Int(I#),Char(C#),Int#,State#,ByteArray#,(>=#)) import GHC.Exts (RealWorld,(+#),(-#),(<#))-import GHC.Exts ((*#))+import GHC.Exts (Addr#,(*#)) import GHC.Integer.Logarithms.Compat (integerLog2#) import GHC.IO (IO(IO),stToIO) import GHC.Natural (naturalFromInteger,naturalToInteger)@@ -390,6 +391,10 @@   )   where   !(I# newSz) = max (I# slen#) 4080++cstring# :: Addr# -> Builder+{-# inline cstring# #-}+cstring# x = cstring (Exts.Ptr x)  -- | Create a builder from a C string with explicit length. The builder -- must be executed before the C string is freed.
+ src/Data/Bytes/Builder/Bounded/Class.hs view
@@ -0,0 +1,79 @@+{-# language DataKinds #-}+{-# language TypeFamilies #-}++module Data.Bytes.Builder.Bounded.Class+  ( ToBoundedBuilder(..)+  ) where++import Data.Int+import Data.Word++import qualified Data.Bytes.Builder.Bounded as Bounded+import qualified GHC.TypeNats as GHC++-- | Variant of To that can be encoded as a builder. Human-readable encodings+-- are used when possible. For example, numbers are encoded an ascii-encoded+-- decimal characters. UTF-8 is preferred for textual types. For types+-- that represent arbitrary bytes (e.g. Bytes, ByteString), the bytes+-- are preserved.+--+-- The goal of this typeclass is to reduce the size of builders produced+-- by quasiquotation.+class ToBoundedBuilder a where+  type BoundedBuilderLength a :: GHC.Nat+  toBuilder :: a -> Bounded.Builder (BoundedBuilderLength a)++-- | Identity+instance ToBoundedBuilder (Bounded.Builder n) where+  type BoundedBuilderLength (Bounded.Builder n) = n+  toBuilder = id++-- | Uses @int64Dec@.+instance ToBoundedBuilder Int64 where+  type BoundedBuilderLength Int64 = 20+  toBuilder = Bounded.int64Dec++-- | Uses @int32Dec@.+instance ToBoundedBuilder Int32 where+  type BoundedBuilderLength Int32 = 11+  toBuilder = Bounded.int32Dec++-- | Uses @int16Dec@.+instance ToBoundedBuilder Int16 where+  type BoundedBuilderLength Int16 = 6+  toBuilder = Bounded.int16Dec++-- | Uses @int8Dec@.+instance ToBoundedBuilder Int8 where+  type BoundedBuilderLength Int8 = 4+  toBuilder = Bounded.int8Dec++-- | Uses @intDec@.+instance ToBoundedBuilder Int where+  type BoundedBuilderLength Int = 20+  toBuilder = Bounded.intDec++-- | Uses @word64Dec@.+instance ToBoundedBuilder Word64 where+  type BoundedBuilderLength Word64 = 19+  toBuilder = Bounded.word64Dec++-- | Uses @word32Dec@.+instance ToBoundedBuilder Word32 where+  type BoundedBuilderLength Word32 = 10+  toBuilder = Bounded.word32Dec++-- | Uses @word16Dec@.+instance ToBoundedBuilder Word16 where+  type BoundedBuilderLength Word16 = 5+  toBuilder = Bounded.word16Dec++-- | Uses @word8Dec@.+instance ToBoundedBuilder Word8 where+  type BoundedBuilderLength Word8 = 3+  toBuilder = Bounded.word8Dec++-- | Uses @wordDec@.+instance ToBoundedBuilder Word where+  type BoundedBuilderLength Word = 19+  toBuilder = Bounded.wordDec
+ src/Data/Bytes/Builder/Class.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}++module Data.Bytes.Builder.Class+  ( ToBuilder(..)+  ) where++import Data.Bytes (Bytes)+import Data.Bytes.Builder (Builder)+import Data.ByteString.Short (ShortByteString)+import Data.Int+import Data.Primitive.ByteArray (ByteArray)+import Data.Text.Short (ShortText)+import Data.Word++import qualified Data.Bytes.Builder as Builder++-- | Types that can be encoded as a builder. Human-readable encodings+-- are used when possible. For example, numbers are encoded an ascii-encoded+-- decimal characters. UTF-8 is preferred for textual types. For types+-- that represent arbitrary bytes (e.g. Bytes, ByteString), the bytes+-- are preserved.+--+-- The goal of this typeclass is to reduce the size of builders produced+-- by quasiquotation.+class ToBuilder a where+  toBuilder :: a -> Builder++-- | Identity+instance ToBuilder Builder where+  toBuilder = id++-- | Uses @bytes@.+instance ToBuilder Bytes where+  toBuilder = Builder.bytes++-- | Uses @byteArray@+instance ToBuilder ByteArray where+  toBuilder = Builder.byteArray++-- | Uses @shortByteString@+instance ToBuilder ShortByteString where+  toBuilder = Builder.shortByteString++-- | Uses @shortTextUtf8@.+instance ToBuilder ShortText where+  toBuilder = Builder.shortTextUtf8++-- | Uses @stringUtf8@+instance ToBuilder String where+  toBuilder = Builder.stringUtf8++-- | Uses @int64Dec@.+instance ToBuilder Int64 where+  toBuilder = Builder.int64Dec++-- | Uses @int32Dec@.+instance ToBuilder Int32 where+  toBuilder = Builder.int32Dec++-- | Uses @int16Dec@.+instance ToBuilder Int16 where+  toBuilder = Builder.int16Dec++-- | Uses @int8Dec@.+instance ToBuilder Int8 where+  toBuilder = Builder.int8Dec++-- | Uses @intDec@.+instance ToBuilder Int where+  toBuilder = Builder.intDec++-- | Uses @word64Dec@.+instance ToBuilder Word64 where+  toBuilder = Builder.word64Dec++-- | Uses @word32Dec@.+instance ToBuilder Word32 where+  toBuilder = Builder.word32Dec++-- | Uses @word16Dec@.+instance ToBuilder Word16 where+  toBuilder = Builder.word16Dec++-- | Uses @word8Dec@.+instance ToBuilder Word8 where+  toBuilder = Builder.word8Dec++-- | Uses @wordDec@.+instance ToBuilder Word where+  toBuilder = Builder.wordDec++-- | uses @doubleDec@+instance ToBuilder Double where+  toBuilder = Builder.doubleDec
+ src/Data/Bytes/Builder/Template.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++-- | Quasiquotation for byte builders.+module Data.Bytes.Builder.Template+  ( bldr+  ) where++import Control.Monad (when)+import Data.Bytes.Builder.Class (toBuilder)+import GHC.Ptr (Ptr(Ptr))+import Language.Haskell.Meta.Parse (parseExp)+import Language.Haskell.TH (Q,Exp)+import Language.Haskell.TH.Lib (integerL,stringPrimL,litE)+import Language.Haskell.TH.Quote (QuasiQuoter(..))++import qualified Data.Bytes.Builder as Builder+import qualified Data.ByteString.Short as SBS+import qualified Data.Text.Short as TS+import qualified Language.Haskell.TH as TH++-- | A quasiquoter for builders. Haskell expressions are interpolated+-- with backticks, and the @ToBuilder@ class is used to convert them+-- to builders. Several common escape sequences for whitespace and+-- control characters are recongized. Consider the following expression,+-- where the binding @partition@ has type @Word32@:+--+-- > [templ|[WARN] Partition `partition` has invalid data.\n|]+--+-- This expression has type @Builder@ and expands to:+--+-- > Builder.cstringLen (Ptr "[WARN] Partition "#, 17) <>+-- > Builder.toBuilder partition <>+-- > Builder.cstringLen (Ptr " has invalid data.\n"#, 19)+--+-- The @ToBuilder@ instance for @Word32@ uses decimal encoding, so this+-- would result in the following if @partition@ was 42 (with a newline+-- character at the end):+--+-- > [WARN] Partition 42 has invalid data.+--+-- In the future, a more sophisticated @bbldr@ variant will be added+-- that will support expressions where the maximum length of the entire+-- builder can be computed at compile time. +bldr :: QuasiQuoter+bldr = QuasiQuoter+  { quoteExp = templExp+  , quotePat = notHandled "patterns"+  , quoteType = notHandled "types"+  , quoteDec = notHandled "declarations"+  }+  where+  notHandled things _ = fail $+    things ++ "are not handled by the byte template quasiquoter"++templExp :: String -> Q Exp+templExp inp = do+  checkOverloadedStrings+  rawParts <- case parse inp of+    Left err -> fail err+    Right [] -> fail "empty template"+    Right v -> pure v+  let expParts = compile <$> rawParts+  foldl1 (\e1 e2 -> [| $e1 <> $e2 |]) expParts++checkOverloadedStrings :: Q ()+checkOverloadedStrings = do+  olEnabled <- TH.isExtEnabled TH.OverloadedStrings+  when (not olEnabled) $+    fail "Byte templates require the OverloadedStrings extension enabled."++type Template = [TemplPart]+data TemplPart+  = Literal String+  | Splice String++compile :: TemplPart -> Q Exp+compile (Literal lit) =+  let bytes = SBS.unpack . TS.toShortByteString . TS.pack $ lit+      strExp = litE . stringPrimL $ bytes+      strLen = litE . integerL . fromIntegral $ length bytes+   in [|Builder.cstringLen (Ptr $(strExp), $(strLen))|]+compile (Splice str) = case parseExp str of+  Left err -> fail err+  Right hs -> [|toBuilder $(pure hs)|]++parse :: String -> Either String Template+parse = partsLoop+  where+  partsLoop "" = do+    pure []+  partsLoop ('`':inp) = do+    (!spl, !rest) <- spliceLoop inp+    (Splice spl:) <$> partsLoop rest+  partsLoop inp = do+    (!lit, !rest) <- litLoop "" inp+    (Literal lit:) <$> partsLoop rest+  litLoop :: String -> String -> Either String (String, String)+  litLoop !acc rest@"" = pure (reverse acc, rest)+  litLoop !acc rest@('`':_) = pure (reverse acc, rest)+  litLoop !acc ('\\':next) = do+    (c, rest) <- parseEscape next+    litLoop (c:acc) rest+  litLoop !acc (c:rest) = litLoop (c:acc) rest+  spliceLoop :: String -> Either String (String, String)+  spliceLoop inp = case break (== '`') inp of+    ([], _) -> Left "internal error"+    (hs, '`':rest) -> pure (hs, rest)+    (_, _:_) -> Left "internal error"+    (_, []) -> Left "unterminated interpolation"+  parseEscape :: String -> Either String (Char, String)+  parseEscape "" = Left "incomplete escape"+  parseEscape ('\\':rest) = pure ('\\', rest)+  parseEscape ('`':rest) = pure ('`', rest)+  parseEscape ('\'':rest) = pure ('\'', rest)+  parseEscape ('\"':rest) = pure ('\"', rest)+  parseEscape ('0':rest) = pure ('\0', rest)+  parseEscape ('a':rest) = pure ('\a', rest)+  parseEscape ('b':rest) = pure ('\b', rest)+  parseEscape ('f':rest) = pure ('\f', rest)+  parseEscape ('n':rest) = pure ('\n', rest)+  parseEscape ('r':rest) = pure ('\r', rest)+  parseEscape ('t':rest) = pure ('\t', rest)+  parseEscape ('v':rest) = pure ('\v', rest)+  parseEscape (c:_) = Left $ "unrecognized escape: \\" ++ [c]
test/Main.hs view
@@ -1,8 +1,9 @@ {-# language BangPatterns #-} {-# language NumericUnderscores #-}+{-# language OverloadedStrings #-}+{-# language QuasiQuotes #-} {-# language ScopedTypeVariables #-} {-# language TypeApplications #-}-{-# language OverloadedStrings #-}  {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -11,28 +12,34 @@ import Control.Applicative (liftA2) import Control.Monad.ST (runST) import Data.Bytes.Builder+import Data.Bytes.Builder.Template (bldr) import Data.Bytes.Types (MutableBytes(MutableBytes))-import Data.Primitive (PrimArray)-import Data.Word import Data.Char (ord,chr) import Data.IORef (IORef,newIORef,readIORef,writeIORef)+import Data.Maybe (fromMaybe) import Data.Primitive (ByteArray)+import Data.Primitive (PrimArray)+import Data.Text.Short (ShortText) import Data.WideWord (Word128(Word128),Word256(Word256))+import Data.Word import Numeric.Natural (Natural)-import Test.Tasty (defaultMain,testGroup,TestTree) import Test.QuickCheck ((===),Arbitrary) import Test.QuickCheck.Instances.Natural ()-import Text.Printf (printf)+import Test.Tasty (defaultMain,testGroup,TestTree) import Test.Tasty.HUnit ((@=?))+import Text.Printf (printf)  import qualified Arithmetic.Nat as Nat import qualified Data.Bits as Bits-import qualified Data.Bytes.Builder.Bounded as Bounded import qualified Data.Bytes as Bytes+import qualified Data.Bytes.Builder as Builder+import qualified Data.Bytes.Builder.Bounded as Bounded+import qualified Data.Bytes.Chunks as Chunks+import qualified Data.Bytes.Text.Ascii as Ascii+import qualified Data.Bytes.Text.Latin1 as Latin1 import qualified Data.ByteString as ByteString import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as LB-import qualified Data.Bytes.Chunks as Chunks import qualified Data.List as L import qualified Data.Primitive as PM import qualified Data.Text as T@@ -286,6 +293,28 @@           , 0x00 : 0x09 : map c2w "listening"           ] @=? map Exts.toList (Exts.toList res)     ]+  , testGroup "bytes templates"+    [ THU.testCase "A" $ do+        let name = Just ("foo" :: ShortText)+            msgBuilder = [bldr|Hello `fromMaybe "World" name`!\n|]+            msg = Chunks.concat . Builder.run 200 $ msgBuilder+         in Ascii.fromString "Hello foo!\n" @=? msg+    , THU.testCase "B" $ do+        let one = "foo" :: ShortText+            two = "bar" :: String+            msgBuilder = [bldr|`one``two`|]+            msg = Chunks.concat . Builder.run 200 $ msgBuilder+         in Ascii.fromString "foobar" @=? msg+    , THU.testCase "C" $ do+        let msgBuilder = [bldr|a backtick for you: \`|]+            msg = Chunks.concat . Builder.run 200 $ msgBuilder+         in Ascii.fromString "a backtick for you: `" @=? msg+    , THU.testCase "D" $ do+        let i = 137 :: Int+            msgBuilder = [bldr|there are `i` lights!|]+            msg = Chunks.concat . Builder.run 200 $ msgBuilder+         in Ascii.fromString "there are 137 lights!" @=? msg+    ]   ]  bytesOntoRef ::@@ -335,7 +364,7 @@  instance Show AsciiByteArray where   show (AsciiByteArray b) = if Bytes.all (\w -> w >= 32 && w < 127) (Bytes.fromByteArray b)-    then Bytes.toLatinString (Bytes.fromByteArray b)+    then Latin1.toString (Bytes.fromByteArray b)     else show (show b)  instance Arbitrary Word128 where