packages feed

bytebuild 0.3.14.0 → 0.3.15.0

raw patch · 4 files changed

+109/−47 lines, 4 filesdep ~basedep ~bytestringdep ~haskell-src-metaPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, bytestring, haskell-src-meta, tasty

API changes (from Hackage documentation)

+ Data.Bytes.Builder.Unsafe: pasteUtf8TextJson# :: ByteArray# -> Int# -> Int# -> MutableByteArray# s -> Int# -> State# s -> (# State# s, Int# #)

Files

CHANGELOG.md view
@@ -5,6 +5,12 @@ `small-bytearray-builder` is now just a compatibility shim to ease the migration process. +## 0.3.15.0 -- 2024-01-05++* Add `Data.Bytes.Builder.Unsafe.pasteUtf8TextJson#` for users who need+  to perform JSON string encoding without using a builder.+* Add `Data.Bytes.Builder.textJsonString` when building with text 2.0++ ## 0.3.14.0 -- 2023-07-20  * Add `runOntoLength`.
bytebuild.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: bytebuild-version: 0.3.14.0+version: 0.3.15.0 synopsis: Build byte arrays description:   This is similar to the builder facilities provided by@@ -49,10 +49,10 @@   reexported-modules:     Data.Bytes.Chunks   build-depends:-    , base >=4.12.0.0 && <4.19+    , base >=4.12.0.0 && <4.20     , byteslice >=0.2.6 && <0.3-    , bytestring >=0.10.8.2 && <0.12-    , haskell-src-meta >=0.8+    , bytestring >=0.10.8.2 && <0.13+    , haskell-src-meta >=0.8.13     , integer-logarithms >=1.0.3 && <1.1     , natural-arithmetic >=0.1 && <0.3     , primitive-offset >=0.2 && <0.3@@ -68,10 +68,10 @@     if impl(ghc >= 8.10)       hs-source-dirs: src-9.0   if flag(checked)-    build-depends: primitive-checked >= 0.7 && <0.9+    build-depends: primitive-checked >= 0.7 && <0.10     hs-source-dirs: src-checked   else-    build-depends: primitive >= 0.7 && <0.9+    build-depends: primitive >= 0.7 && <0.10     hs-source-dirs: src-unchecked   ghc-options: -Wall -O2   hs-source-dirs: src@@ -99,7 +99,7 @@     , quickcheck-classes >=0.6.4     , quickcheck-instances >=0.3.22     , text-short-    , tasty >=1.2.3 && <1.5+    , tasty >=1.2.3 && <1.6     , tasty-hunit >=0.10.0.2 && <0.11     , tasty-quickcheck >=0.10.1 && <0.11     , text >=1.2 && <2.2
src/Data/Bytes/Builder.hs view
@@ -32,6 +32,7 @@   , shortByteString #if MIN_VERSION_text(2,0,0)   , textUtf8+  , textJsonString #endif   , shortTextUtf8   , shortTextJsonString@@ -159,10 +160,10 @@ import Data.Bytes.Builder.Unsafe (commitsOntoChunks) import Data.Bytes.Builder.Unsafe (reverseCommitsOntoChunks) import Data.Bytes.Builder.Unsafe (stringUtf8,cstring,fromEffect)+import Data.Bytes.Builder.Unsafe (pasteUtf8TextJson#) import Data.Bytes.Chunks (Chunks(ChunksCons,ChunksNil)) import Data.Bytes.Types (Bytes(Bytes),MutableBytes(MutableBytes)) import Data.ByteString.Short.Internal (ShortByteString(SBS))-import Data.Char (ord) import Data.Foldable (foldlM) import Data.Int (Int64,Int32,Int16,Int8) import Data.Primitive (ByteArray(..),MutableByteArray(..),PrimArray(..))@@ -173,7 +174,7 @@ import Foreign.C.String (CStringLen) import GHC.ByteOrder (ByteOrder(BigEndian,LittleEndian),targetByteOrder) import GHC.Exts (MutableByteArray#,Addr#,(*#),oneShot)-import GHC.Exts (Int(I#),Char(C#),Int#,State#,ByteArray#,(>=#))+import GHC.Exts (Int(I#),Int#,State#,ByteArray#,(>=#)) import GHC.Exts (RealWorld,(+#),(-#),(<#)) import GHC.Integer.Logarithms.Compat (integerLog2#) import GHC.IO (IO(IO),stToIO)@@ -795,38 +796,18 @@ -- byte sequence is UTF-8 encoded text. slicedUtf8TextJson :: ByteArray# -> Int# -> Int# -> Builder {-# noinline slicedUtf8TextJson #-}-slicedUtf8TextJson !src# !soff0# !slen0# = fromFunction reqLen $ \dst doff0 -> do-  PM.writeByteArray dst doff0 (c2w '"')-  let go !soff !slen !doff = if slen > 0-        then case indexChar8Array (ByteArray src#) soff of-          '\\' -> write2 dst doff '\\' '\\' *> go (soff + 1) (slen - 1) (doff + 2)-          '\"' -> write2 dst doff '\\' '\"' *> go (soff + 1) (slen - 1) (doff + 2)-          c -> if c >= '\x20'-            then PM.writeByteArray dst doff (c2w c) *> go (soff + 1) (slen - 1) (doff + 1)-            else case c of-              '\n' -> write2 dst doff '\\' 'n' *> go (soff + 1) (slen - 1) (doff + 2)-              '\r' -> write2 dst doff '\\' 'r' *> go (soff + 1) (slen - 1) (doff + 2)-              '\t' -> write2 dst doff '\\' 't' *> go (soff + 1) (slen - 1) (doff + 2)-              _ -> do-                write2 dst doff '\\' 'u'-                doff' <- UnsafeBounded.pasteST-                  (Bounded.word16PaddedUpperHex (fromIntegral (c2w c)))-                  dst (doff + 2)-                go (soff + 1) (slen - 1) doff'-        else pure doff-  doffRes <- go (I# soff0#) (I# slen0#) (doff0 + 1)-  PM.writeByteArray dst doffRes (c2w '"')-  pure (doffRes + 1)+slicedUtf8TextJson !src# !soff0# !slen0# = fromFunction# reqLen#+  ( \dst# doff0# s0# -> pasteUtf8TextJson# src# soff0# slen0# dst# doff0# s0# )   where-  slen0 = I# slen0#   -- We multiply by 6 because, in the worst case, everything might be in the   -- unprintable ASCII range. The plus 2 is for the quotes on the ends.-  reqLen = (6 * slen0) + 2+  !reqLen# = (6# *# slen0# ) +# 2#  -- | Constructor for 'Builder' that works on a function with lifted -- arguments instead of unlifted ones. This is just as unsafe as the -- actual constructor. fromFunction :: Int -> (forall s. MutableByteArray s -> Int -> ST s Int) -> Builder+{-# inline fromFunction #-} fromFunction (I# req) f = Builder $ \buf0 off0 len0 cs0 s0 ->   let !(# s1, buf1, off1, len1, cs1 #) = case len0 >=# req of         1# -> (# s0, buf0, off0, len0, cs0 #)@@ -837,11 +818,17 @@    in case unST (f (MutableByteArray buf1) (I# off1)) s1 of         (# s2, I# off2 #) -> (# s2, buf1, off2, len1 -# (off2 -# off1), cs1 #) --- Internal. Write two characters in the ASCII plane to a byte array.-write2 :: MutableByteArray s -> Int -> Char -> Char -> ST s ()-write2 marr ix a b = do-  PM.writeByteArray marr ix (c2w a)-  PM.writeByteArray marr (ix + 1) (c2w b)+fromFunction# :: Int# -> (forall s. MutableByteArray# s -> Int# -> State# s -> (# State# s, Int# #) ) -> Builder+{-# inline fromFunction# #-}+fromFunction# req f = Builder $ \buf0 off0 len0 cs0 s0 ->+  let !(# s1, buf1, off1, len1, cs1 #) = case len0 >=# req of+        1# -> (# s0, buf0, off0, len0, cs0 #)+        _ -> let !(I# lenX) = max 4080 (I# req) in+          case Exts.newByteArray# lenX s0 of+            (# sX, bufX #) ->+              (# sX, bufX, 0#, lenX, Mutable buf0 off0 cs0 #)+   in case f buf1 off1 s1 of+        (# s2, off2 #) -> (# s2, buf1, off2, len1 -# (off2 -# off1), cs1 #)  -- | Create a builder from text. The text will be UTF-8 encoded. shortTextUtf8 :: ShortText -> Builder@@ -864,11 +851,18 @@ -- * @\\_"_\/ ==\> "\\\\_\\"_\/"@ (escapes backslashes and quotes) -- * @hello\<ESC\>world ==> "hello\\u001Bworld"@ (where @\<ESC\>@ is code point 0x1B) shortTextJsonString :: ShortText -> Builder+{-# inline shortTextJsonString #-} shortTextJsonString a =   let !(ByteArray ba) = shortTextToByteArray a       !(I# len) = PM.sizeofByteArray (ByteArray ba)    in slicedUtf8TextJson ba 0# len +#if MIN_VERSION_text(2,0,0)+textJsonString :: Text -> Builder+{-# inline textJsonString #-}+textJsonString (I.Text (A.ByteArray ba) (I# off) (I# len)) = slicedUtf8TextJson ba off len+#endif+ -- | Encodes an unsigned 64-bit integer as decimal. -- This encoding never starts with a zero unless the -- argument was zero.@@ -1204,23 +1198,20 @@ shortTextToByteArray x = case TS.toShortByteString x of   SBS a -> ByteArray a -indexChar8Array :: ByteArray -> Int -> Char-indexChar8Array (ByteArray b) (I# i) = C# (Exts.indexCharArray# b i)--c2w :: Char -> Word8-c2w = fromIntegral . ord- -- | Encode a signed machine-sized integer with LEB-128. This uses -- zig-zag encoding. intLEB128 :: Int -> Builder+{-# inline intLEB128 #-} intLEB128 = wordLEB128 . toZigzagNative  -- | Encode a 32-bit signed integer with LEB-128. This uses zig-zag encoding. int32LEB128 :: Int32 -> Builder+{-# inline int32LEB128 #-} int32LEB128 = word32LEB128 . toZigzag32  -- | Encode a 64-bit signed integer with LEB-128. This uses zig-zag encoding. int64LEB128 :: Int64 -> Builder+{-# inline int64LEB128 #-} int64LEB128 = word64LEB128 . toZigzag64  -- | Encode a machine-sized word with LEB-128.
src/Data/Bytes/Builder/Unsafe.hs view
@@ -33,22 +33,25 @@     -- @Data.Bytes.Builder@ instead.   , stringUtf8   , cstring+    -- * Pasting with Preconditions+  , pasteUtf8TextJson#   ) where  import Control.Monad.Primitive (primitive_) import Data.Bytes.Chunks (Chunks(ChunksCons)) import Data.Bytes.Types (Bytes(Bytes))+import Data.Char (ord) import Data.Primitive (MutableByteArray(..),ByteArray(..))+import Data.Word (Word8) import Foreign.C.String (CString) import GHC.Base (unpackCString#,unpackCStringUtf8#)-import GHC.Exts ((-#),(+#),(>#),(>=#))+import GHC.Exts ((-#),(+#),(>#),(>=#),Char(C#)) import GHC.Exts (Addr#,ByteArray#,MutableByteArray#,Int(I#),Ptr(Ptr)) import GHC.Exts (RealWorld,IsString,Int#,State#)-import GHC.ST (ST(ST)) import GHC.IO (stToIO)+import GHC.ST (ST(ST))  import qualified Compat as C- import qualified Data.Bytes.Builder.Bounded as Bounded import qualified Data.Bytes.Builder.Bounded.Unsafe as UnsafeBounded import qualified Data.Primitive as PM@@ -311,3 +314,65 @@   case Exts.sameMutableByteArray# target buf of     1# -> n +# len     _ -> commitDistance target (n +# len) cs++-- | Encode (UTF-8 encoded) text as a JSON string, wrapping it in double quotes.+-- This escapes all characters with code points below @0x20@.+--+-- * Precondition: The slice of the byte argument is UTF-8 encoded text.+-- * Precondition: There is enough space in the buffer for the result+--   to be written to. A simple way to ensure enough space is to allocate+--   @6N + 2@ bytes, where N is the length of the argument. However, the+--   caller may use clever heuristics to find a lower upper bound.+-- * Result: The next offset in the destination buffer+pasteUtf8TextJson# ::+     ByteArray# -- ^ source+  -> Int# -- ^ source offset+  -> Int# -- ^ source length+  -> MutableByteArray# s -- ^ destination buffer+  -> Int# -- ^ offset into destination buffer+  -> State# s -- ^ state token+  -> (# State# s, Int# #) -- returns next destination offset+{-# noinline pasteUtf8TextJson# #-}+pasteUtf8TextJson# src# soff0# slen0# dst# doff0# s0# =+  let ST f = do+        let dst = MutableByteArray dst#+        let doff0 = I# doff0#+        PM.writeByteArray dst doff0 (c2w '"')+        let go !soff !slen !doff = if slen > 0+              then case indexChar8Array (ByteArray src#) soff of+                '\\' -> write2 dst doff '\\' '\\' *> go (soff + 1) (slen - 1) (doff + 2)+                '\"' -> write2 dst doff '\\' '\"' *> go (soff + 1) (slen - 1) (doff + 2)+                c -> if c >= '\x20'+                  then PM.writeByteArray dst doff (c2w c) *> go (soff + 1) (slen - 1) (doff + 1)+                  else case c of+                    '\n' -> write2 dst doff '\\' 'n' *> go (soff + 1) (slen - 1) (doff + 2)+                    '\r' -> write2 dst doff '\\' 'r' *> go (soff + 1) (slen - 1) (doff + 2)+                    '\t' -> write2 dst doff '\\' 't' *> go (soff + 1) (slen - 1) (doff + 2)+                    '\b' -> write2 dst doff '\\' 'b' *> go (soff + 1) (slen - 1) (doff + 2)+                    '\f' -> write2 dst doff '\\' 'f' *> go (soff + 1) (slen - 1) (doff + 2)+                    _ -> do+                      write2 dst doff '\\' 'u'+                      doff' <- UnsafeBounded.pasteST+                        (Bounded.word16PaddedUpperHex (fromIntegral (c2w c)))+                        dst (doff + 2)+                      go (soff + 1) (slen - 1) doff'+              else pure doff+        doffRes <- go (I# soff0#) (I# slen0#) (doff0 + 1)+        PM.writeByteArray dst doffRes (c2w '"')+        pure (doffRes + 1)+      !(# !s1, I# dstFinal #) = f s0#+   in (# s1, dstFinal #)++c2w :: Char -> Word8+{-# inline c2w #-}+c2w = fromIntegral . ord++-- Internal. Write two characters in the ASCII plane to a byte array.+write2 :: MutableByteArray s -> Int -> Char -> Char -> ST s ()+write2 marr ix a b = do+  PM.writeByteArray marr ix (c2w a)+  PM.writeByteArray marr (ix + 1) (c2w b)++indexChar8Array :: ByteArray -> Int -> Char+{-# inline indexChar8Array #-}+indexChar8Array (ByteArray b) (I# i) = C# (Exts.indexCharArray# b i)