on master help sign in
Polish UTF-8 bytestring builder support

- adjust function names to 'encodeUtf8Builder' and 'encodeUtf8BuilderEscaped' - expose the same conversion to builders for both lazy and strict text - ensure 'Escaped' versions are inlined to allow specialization for specific escaping primitives - fix some Haddock references - add Haddock comment about bytestring >= 0.10.4.0 dependency - remove stream-to-builder encoding functions. There is no direct use case for them and they require too much knowledge about the internals to be used correctly.

commit3e915b239afc99289eaf62b558b598c088ce6f4a authorSimon Meier <simon.meier@erudify.com> dateFri 3 Jan 2014, 16:59 (13 years ago) parent0f3435b
2 files changed+55−60
Data/Text/Encoding.hs+38−54 Data/Text/Lazy/Encoding.hs+17−6
Data/Text/Encoding.hs+38−54
@@ -56,37 +56,44 @@ module Data.Text.Encoding
5656 , encodeUtf32BE
5757
5858#if MIN_VERSION_bytestring(0,10,4)
59− -- * Generic encoding of Text
60− -- , encodeStreamWithB
61− -- , encodeTextWithB
62− -- , encodeUtf8Builder
63− , encodeUtf8Escaped
59+ -- * Encoding Text using ByteString Builders
60+ -- | /Note/ that these functions are only available if built against
61+ -- @bytestring >= 0.10.4.0@.
62+ , encodeUtf8Builder
63+ , encodeUtf8BuilderEscaped
6464#endif
6565 ) where
6666
6767import Control.Exception (evaluate, try)
68+
6869#if __GLASGOW_HASKELL__ >= 702
6970import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
7071#else
7172import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
7273#endif
74+
7375import Control.Monad.ST (runST)
7476import Data.Bits ((.&.))
7577import Data.ByteString as B
76−import Data.ByteString.Internal as B
78+import Data.ByteString.Internal as B hiding (c2w)
79+
7780#if MIN_VERSION_bytestring(0,10,4)
81+import Data.Monoid ((<>))
7882import qualified Data.ByteString.Builder as B
7983import qualified Data.ByteString.Builder.Internal as B
8084import qualified Data.ByteString.Builder.Prim as BP
8185import qualified Data.ByteString.Builder.Prim.Internal as BP
8286import qualified Data.ByteString.Lazy as BL
87+import Data.Text.Internal.Unsafe.Shift (shiftR)
88+#else
89+import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
8390#endif
91+
8492import Data.Text ()
8593import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
8694import Data.Text.Internal (Text(..), safe, textP)
8795import Data.Text.Internal.Private (runText)
8896import Data.Text.Internal.Unsafe.Char (ord, unsafeWrite)
89−import Data.Text.Internal.Unsafe.Shift (shiftL, shiftR)
9097import Data.Word (Word8, Word32)
9198import Foreign.C.Types (CSize)
9299import Foreign.ForeignPtr (withForeignPtr)
@@ -308,26 +315,36 @@ decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDec
308315encodeUtf8 :: Text -> ByteString
309316#if MIN_VERSION_bytestring(0,10,4)
310317
311−encodeUtf8 =
312− BL.toStrict . B.toLazyByteString
313− . encodeUtf8Escaped (BP.liftFixedToBounded BP.word8)
318+encodeUtf8 = BL.toStrict . B.toLazyByteString . encodeUtf8Builder
319+
320+-- | Encode text to a ByteString 'B.Builder' using UTF-8 encoding.
321+encodeUtf8Builder :: Text -> B.Builder
322+encodeUtf8Builder = encodeUtf8BuilderEscaped (BP.liftFixedToBounded BP.word8)
314323
315324-- | Encode text using UTF-8 encoding and escape the ASCII characters using
316−-- a 'BP.PrimBounded'.
317−encodeUtf8Escaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
318−encodeUtf8Escaped be (Text arr off len) =
319− B.builder step
325+-- a 'BP.BoundedPrim'.
326+--
327+-- Use this function is to implement efficient encoders for text-based formats
328+-- like JSON or HTML.
329+{-# INLINE encodeUtf8BuilderEscaped #-}
330+-- TODO: Extend documentation with references to source code in @blaze-html@
331+-- or @aeson@ that uses this function.
332+encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
333+encodeUtf8BuilderEscaped be =
334+ -- manual eta-expansion to ensure inlining works as expected
335+ \txt -> B.builder (mkBuildstep txt)
320336 where
321− bound = max 4 $ BP.sizeBound be
322− iend = off + len
323− step !k =
337+ bound = max 4 $ BP.sizeBound be
338+
339+ mkBuildstep (Text arr off len) !k =
324340 outerLoop off
325341 where
342+ iend = off + len
343+
326344 outerLoop !i0 !br@(B.BufferRange op0 ope)
327− | i0 >= iend = k br
328− | op0 `plusPtr` bound < ope =
329− goPartial (i0 + min outRemaining inpRemaining)
330− | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
345+ | i0 >= iend = k br
346+ | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
347+ | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
331348 where
332349 outRemaining = (ope `minusPtr` op0) `div` bound
333350 inpRemaining = iend - i0
@@ -492,36 +509,3 @@ foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_stat
492509
493510foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
494511 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
495−
496−
497−{-
498−-- | Encode all elements of a 'F.Stream' using a 'B.BoundedEncoding'.
499−{-# INLINE encodeStreamWithB #-}
500−encodeStreamWithB :: B.BoundedEncoding a -> F.Stream a -> B.Builder
501−encodeStreamWithB be =
502− \(F.Stream next s0 _) -> B.builder $ step next s0
503− where
504− bound = B.sizeBound be
505− step next s0 k (B.BufferRange op0 ope0) =
506− go s0 op0
507− where
508− go s !op = case next s of
509− F.Done -> k (B.BufferRange op ope0)
510− F.Skip s' -> go s' op
511− F.Yield x s'
512− | op `plusPtr` bound <= ope0 -> B.runB be x op >>= go s'
513− | otherwise ->
514− return $ B.bufferFull bound op (step next s k)
515−
516−
517−-- |
518−-- | /Subject to fusion./
519−-- Encode all 'Char's of a 'T.Text' using a 'B.BoundedEncoding'.
520−{-# INLINE encodeTextWithB #-}
521−encodeTextWithB :: B.BoundedEncoding Char -> Text -> B.Builder
522−encodeTextWithB be = encodeStreamWithB be . F.stream
523−
524−-- | Encode text using UTF-8 encoding.
525−encodeUtf8Builder :: Text -> B.Builder
526−encodeUtf8Builder = encodeUtf8Escaped (B.fromF B.word8)
527−-}
Data/Text/Lazy/Encoding.hs+17−6
@@ -45,6 +45,12 @@ module Data.Text.Lazy.Encoding
4545 , encodeUtf16BE
4646 , encodeUtf32LE
4747 , encodeUtf32BE
48+
49+#if MIN_VERSION_bytestring(0,10,4)
50+ -- * Encoding Text using ByteString Builders
51+ , encodeUtf8Builder
52+ , encodeUtf8BuilderEscaped
53+#endif
4854 ) where
4955
5056import Control.Exception (evaluate, try)
@@ -55,6 +61,7 @@ import qualified Data.ByteString.Lazy as B
5561import qualified Data.ByteString.Lazy.Internal as B
5662import qualified Data.ByteString.Unsafe as B
5763#if MIN_VERSION_bytestring(0,10,4)
64+import Data.Word (Word8)
5865import Data.Monoid (mempty, (<>))
5966import qualified Data.ByteString.Builder as B
6067import qualified Data.ByteString.Builder.Prim as BP
@@ -141,12 +148,16 @@ decodeUtf8' bs = unsafeDupablePerformIO $ do
141148
142149encodeUtf8 :: Text -> B.ByteString
143150#if MIN_VERSION_bytestring(0,10,4)
144−encodeUtf8 =
145− B.toLazyByteString . go
146− where
147− go Empty = mempty
148− go (Chunk c cs) =
149− TE.encodeUtf8Escaped (BP.liftFixedToBounded BP.word8) c <> go cs
151+encodeUtf8 = B.toLazyByteString . encodeUtf8Builder
152+
153+encodeUtf8Builder :: Text -> B.Builder
154+encodeUtf8Builder = foldrChunks (\c b -> TE.encodeUtf8Builder c <> b) mempty
155+
156+{-# INLINE encodeUtf8BuilderEscaped #-}
157+encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
158+encodeUtf8BuilderEscaped prim =
159+ foldrChunks (\c b -> TE.encodeUtf8BuilderEscaped prim c <> b) mempty
160+
150161#else
151162encodeUtf8 (Chunk c cs) = B.Chunk (TE.encodeUtf8 c) (encodeUtf8 cs)
152163encodeUtf8 Empty = B.Empty