Merge branch 'master' into feature-new-bytestring-builder
[packages/text.git] / Data / Text / Encoding.hs
1 {-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving, MagicHash,
2 UnliftedFFITypes #-}
3 #if __GLASGOW_HASKELL__ >= 702
4 {-# LANGUAGE Trustworthy #-}
5 #endif
6 -- |
7 -- Module : Data.Text.Encoding
8 -- Copyright : (c) 2009, 2010, 2011 Bryan O'Sullivan,
9 -- (c) 2009 Duncan Coutts,
10 -- (c) 2008, 2009 Tom Harper
11 --
12 -- License : BSD-style
13 -- Maintainer : bos@serpentine.com, rtomharper@googlemail.com,
14 -- duncan@haskell.org
15 -- Stability : experimental
16 -- Portability : portable
17 --
18 -- Functions for converting 'Text' values to and from 'ByteString',
19 -- using several standard encodings.
20 --
21 -- To gain access to a much larger family of encodings, use the
22 -- @text-icu@ package: <http://hackage.haskell.org/package/text-icu>
23
24 module Data.Text.Encoding
25 (
26 -- * Decoding ByteStrings to Text
27 -- $strict
28 decodeASCII
29 , decodeLatin1
30 , decodeUtf8
31 , decodeUtf16LE
32 , decodeUtf16BE
33 , decodeUtf32LE
34 , decodeUtf32BE
35
36 -- ** Catchable failure
37 , decodeUtf8'
38
39 -- ** Controllable error handling
40 , decodeUtf8With
41 , decodeUtf16LEWith
42 , decodeUtf16BEWith
43 , decodeUtf32LEWith
44 , decodeUtf32BEWith
45
46 -- ** Stream oriented decoding
47 -- $stream
48 , streamDecodeUtf8
49 , streamDecodeUtf8With
50 , Decoding(..)
51
52 -- * Encoding Text to ByteStrings
53 , encodeUtf8
54 , encodeUtf16LE
55 , encodeUtf16BE
56 , encodeUtf32LE
57 , encodeUtf32BE
58
59 -- * Generic encoding of Text
60 -- , encodeStreamWithB
61 -- , encodeTextWithB
62 -- , encodeUtf8Builder
63 , encodeUtf8Escaped
64 ) where
65
66 import Control.Exception (evaluate, try)
67 #if __GLASGOW_HASKELL__ >= 702
68 import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
69 #else
70 import Control.Monad.ST (unsafeIOToST, unsafeSTToIO)
71 #endif
72 import Control.Monad.ST (runST)
73 import Data.Bits ((.&.))
74 import Data.ByteString as B
75 import Data.ByteString.Internal as B
76 import qualified Data.ByteString.Lazy as BL
77 import qualified Data.ByteString.Builder as B
78 import qualified Data.ByteString.Builder.Internal as B
79 import qualified Data.ByteString.Builder.Prim.Internal as BP
80 import qualified Data.ByteString.Builder.Prim as BP
81 import Data.Text ()
82 import Data.Text.Encoding.Error (OnDecodeError, UnicodeException, strictDecode)
83 import Data.Text.Internal (Text(..), safe, textP)
84 import Data.Text.Private (runText)
85 import Data.Text.UnsafeChar (ord, unsafeWrite)
86 import Data.Text.UnsafeShift (shiftL, shiftR)
87 import Data.Word (Word8, Word32)
88 import Foreign.C.Types (CSize)
89 import Foreign.ForeignPtr (withForeignPtr)
90 import Foreign.Marshal.Utils (with)
91 import Foreign.Ptr (Ptr, minusPtr, nullPtr, plusPtr)
92 import Foreign.Storable (Storable, peek, poke)
93 import GHC.Base (MutableByteArray#)
94 import qualified Data.Text.Array as A
95 import qualified Data.Text.Encoding.Fusion as E
96 import qualified Data.Text.Encoding.Utf16 as U16
97 import qualified Data.Text.Fusion as F
98 import Data.Text.Unsafe (unsafeDupablePerformIO)
99
100 #include "text_cbits.h"
101
102 -- $strict
103 --
104 -- All of the single-parameter functions for decoding bytestrings
105 -- encoded in one of the Unicode Transformation Formats (UTF) operate
106 -- in a /strict/ mode: each will throw an exception if given invalid
107 -- input.
108 --
109 -- Each function has a variant, whose name is suffixed with -'With',
110 -- that gives greater control over the handling of decoding errors.
111 -- For instance, 'decodeUtf8' will throw an exception, but
112 -- 'decodeUtf8With' allows the programmer to determine what to do on a
113 -- decoding error.
114
115 -- | /Deprecated/. Decode a 'ByteString' containing 7-bit ASCII
116 -- encoded text.
117 --
118 -- This function is deprecated. Use 'decodeLatin1' instead.
119 decodeASCII :: ByteString -> Text
120 decodeASCII = decodeUtf8
121 {-# DEPRECATED decodeASCII "Use decodeUtf8 instead" #-}
122
123 -- | Decode a 'ByteString' containing Latin-1 (aka ISO-8859-1) encoded text.
124 --
125 -- 'decodeLatin1' is semantically equivalent to
126 -- @Data.Text.pack . Data.ByteString.Char8.unpack@
127 decodeLatin1 :: ByteString -> Text
128 decodeLatin1 (PS fp off len) = textP a 0 len
129 where
130 a = A.run (A.new len >>= unsafeIOToST . go)
131 go dest = withForeignPtr fp $ \ptr -> do
132 c_decode_latin1 (A.maBA dest) (ptr `plusPtr` off) (ptr `plusPtr` (off+len))
133 return dest
134
135 -- | Decode a 'ByteString' containing UTF-8 encoded text.
136 decodeUtf8With :: OnDecodeError -> ByteString -> Text
137 decodeUtf8With onErr (PS fp off len) = runText $ \done -> do
138 let go dest = withForeignPtr fp $ \ptr ->
139 with (0::CSize) $ \destOffPtr -> do
140 let end = ptr `plusPtr` (off + len)
141 loop curPtr = do
142 curPtr' <- c_decode_utf8 (A.maBA dest) destOffPtr curPtr end
143 if curPtr' == end
144 then do
145 n <- peek destOffPtr
146 unsafeSTToIO (done dest (fromIntegral n))
147 else do
148 x <- peek curPtr'
149 case onErr desc (Just x) of
150 Nothing -> loop $ curPtr' `plusPtr` 1
151 Just c -> do
152 destOff <- peek destOffPtr
153 w <- unsafeSTToIO $
154 unsafeWrite dest (fromIntegral destOff) (safe c)
155 poke destOffPtr (destOff + fromIntegral w)
156 loop $ curPtr' `plusPtr` 1
157 loop (ptr `plusPtr` off)
158 (unsafeIOToST . go) =<< A.new len
159 where
160 desc = "Data.Text.Encoding.decodeUtf8: Invalid UTF-8 stream"
161 {- INLINE[0] decodeUtf8With #-}
162
163 -- $stream
164 --
165 -- The 'streamDecodeUtf8' and 'streamDecodeUtf8With' functions accept
166 -- a 'ByteString' that represents a possibly incomplete input (e.g. a
167 -- packet from a network stream) that may not end on a UTF-8 boundary.
168 --
169 -- The first element of the result is the maximal chunk of 'Text' that
170 -- can be decoded from the given input. The second is a function which
171 -- accepts another 'ByteString'. That string will be assumed to
172 -- directly follow the string that was passed as input to the original
173 -- function, and it will in turn be decoded.
174 --
175 -- To help understand the use of these functions, consider the Unicode
176 -- string @\"hi &#9731;\"@. If encoded as UTF-8, this becomes @\"hi
177 -- \\xe2\\x98\\x83\"@; the final @\'&#9731;\'@ is encoded as 3 bytes.
178 --
179 -- Now suppose that we receive this encoded string as 3 packets that
180 -- are split up on untidy boundaries: @[\"hi \\xe2\", \"\\x98\",
181 -- \"\\x83\"]@. We cannot decode the entire Unicode string until we
182 -- have received all three packets, but we would like to make progress
183 -- as we receive each one.
184 --
185 -- @
186 -- let 'Some' t0 f0 = 'streamDecodeUtf8' \"hi \\xe2\"
187 -- t0 == \"hi \" :: 'Text'
188 -- @
189 --
190 -- We use the continuation @f0@ to decode our second packet.
191 --
192 -- @
193 -- let 'Some' t1 f1 = f0 \"\\x98\"
194 -- t1 == \"\"
195 -- @
196 --
197 -- We could not give @f0@ enough input to decode anything, so it
198 -- returned an empty string. Once we feed our second continuation @f1@
199 -- the last byte of input, it will make progress.
200 --
201 -- @
202 -- let 'Some' t2 f2 = f1 \"\\x83\"
203 -- t2 == \"&#9731;\"
204 -- @
205 --
206 -- If given invalid input, an exception will be thrown by the function
207 -- or continuation where it is encountered.
208
209 -- | A stream oriented decoding result.
210 data Decoding = Some Text ByteString (ByteString -> Decoding)
211
212 instance Show Decoding where
213 showsPrec d (Some t bs _) = showParen (d > prec) $
214 showString "Some " . showsPrec prec' t .
215 showChar ' ' . showsPrec prec' bs .
216 showString " _"
217 where prec = 10; prec' = prec + 1
218
219 newtype CodePoint = CodePoint Word32 deriving (Eq, Show, Num, Storable)
220 newtype DecoderState = DecoderState Word32 deriving (Eq, Show, Num, Storable)
221
222 -- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8
223 -- encoded text that is known to be valid.
224 --
225 -- If the input contains any invalid UTF-8 data, an exception will be
226 -- thrown (either by this function or a continuation) that cannot be
227 -- caught in pure code. For more control over the handling of invalid
228 -- data, use 'streamDecodeUtf8With'.
229 streamDecodeUtf8 :: ByteString -> Decoding
230 streamDecodeUtf8 = streamDecodeUtf8With strictDecode
231
232 -- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8
233 -- encoded text.
234 streamDecodeUtf8With :: OnDecodeError -> ByteString -> Decoding
235 streamDecodeUtf8With onErr = decodeChunk 0 0
236 where
237 -- We create a slightly larger than necessary buffer to accommodate a
238 -- potential surrogate pair started in the last buffer
239 decodeChunk :: CodePoint -> DecoderState -> ByteString -> Decoding
240 decodeChunk codepoint0 state0 bs@(PS fp off len) =
241 runST $ (unsafeIOToST . decodeChunkToBuffer) =<< A.new (len+1)
242 where
243 decodeChunkToBuffer :: A.MArray s -> IO Decoding
244 decodeChunkToBuffer dest = withForeignPtr fp $ \ptr ->
245 with (0::CSize) $ \destOffPtr ->
246 with codepoint0 $ \codepointPtr ->
247 with state0 $ \statePtr ->
248 with nullPtr $ \curPtrPtr ->
249 let end = ptr `plusPtr` (off + len)
250 loop curPtr = do
251 poke curPtrPtr curPtr
252 curPtr' <- c_decode_utf8_with_state (A.maBA dest) destOffPtr
253 curPtrPtr end codepointPtr statePtr
254 state <- peek statePtr
255 case state of
256 UTF8_REJECT -> do
257 -- We encountered an encoding error
258 x <- peek curPtr'
259 case onErr desc (Just x) of
260 Nothing -> loop $ curPtr' `plusPtr` 1
261 Just c -> do
262 destOff <- peek destOffPtr
263 w <- unsafeSTToIO $
264 unsafeWrite dest (fromIntegral destOff) (safe c)
265 poke destOffPtr (destOff + fromIntegral w)
266 poke statePtr 0
267 loop $ curPtr' `plusPtr` 1
268
269 _ -> do
270 -- We encountered the end of the buffer while decoding
271 n <- peek destOffPtr
272 codepoint <- peek codepointPtr
273 chunkText <- unsafeSTToIO $ do
274 arr <- A.unsafeFreeze dest
275 return $! textP arr 0 (fromIntegral n)
276 lastPtr <- peek curPtrPtr
277 let left = lastPtr `minusPtr` curPtr
278 return $ Some chunkText (B.drop left bs)
279 (decodeChunk codepoint state)
280 in loop (ptr `plusPtr` off)
281 desc = "Data.Text.Encoding.streamDecodeUtf8With: Invalid UTF-8 stream"
282
283 -- | Decode a 'ByteString' containing UTF-8 encoded text that is known
284 -- to be valid.
285 --
286 -- If the input contains any invalid UTF-8 data, an exception will be
287 -- thrown that cannot be caught in pure code. For more control over
288 -- the handling of invalid data, use 'decodeUtf8'' or
289 -- 'decodeUtf8With'.
290 decodeUtf8 :: ByteString -> Text
291 decodeUtf8 = decodeUtf8With strictDecode
292 {-# INLINE[0] decodeUtf8 #-}
293 {-# RULES "STREAM stream/decodeUtf8 fusion" [1]
294 forall bs. F.stream (decodeUtf8 bs) = E.streamUtf8 strictDecode bs #-}
295
296 -- | Decode a 'ByteString' containing UTF-8 encoded text.
297 --
298 -- If the input contains any invalid UTF-8 data, the relevant
299 -- exception will be returned, otherwise the decoded text.
300 decodeUtf8' :: ByteString -> Either UnicodeException Text
301 decodeUtf8' = unsafeDupablePerformIO . try . evaluate . decodeUtf8With strictDecode
302 {-# INLINE decodeUtf8' #-}
303
304 -- | Encode text using UTF-8 encoding.
305 encodeUtf8 :: Text -> ByteString
306 encodeUtf8 =
307 BL.toStrict . B.toLazyByteString
308 . encodeUtf8Escaped (BP.liftFixedToBounded BP.word8)
309
310 {-
311 encodeUtf8 (Text arr off len) = unsafeDupablePerformIO $ do
312 let size0 = max len 4
313 mallocByteString size0 >>= start size0 off 0
314 where
315 start size n0 m0 fp = withForeignPtr fp $ loop n0 m0
316 where
317 loop n1 m1 ptr = go n1 m1
318 where
319 offLen = off + len
320 go !n !m
321 | n == offLen = return (PS fp 0 m)
322 | otherwise = do
323 let poke8 k v = poke (ptr `plusPtr` k) (fromIntegral v :: Word8)
324 ensure k act
325 | size-m >= k = act
326 | otherwise = {-# SCC "resizeUtf8/ensure" #-} do
327 let newSize = size `shiftL` 1
328 fp' <- mallocByteString newSize
329 withForeignPtr fp' $ \ptr' ->
330 memcpy ptr' ptr (fromIntegral m)
331 start newSize n m fp'
332 {-# INLINE ensure #-}
333 case A.unsafeIndex arr n of
334 w| w <= 0x7F -> ensure 1 $ do
335 poke (ptr `plusPtr` m) (fromIntegral w :: Word8)
336 -- A single ASCII octet is likely to start a run of
337 -- them. We see better performance when we
338 -- special-case this assumption.
339 let end = ptr `plusPtr` size
340 ascii !t !u
341 | t == offLen || u == end || v >= 0x80 =
342 go t (u `minusPtr` ptr)
343 | otherwise = do
344 poke u (fromIntegral v :: Word8)
345 ascii (t+1) (u `plusPtr` 1)
346 where v = A.unsafeIndex arr t
347 ascii (n+1) (ptr `plusPtr` (m+1))
348 | w <= 0x7FF -> ensure 2 $ do
349 poke8 m $ (w `shiftR` 6) + 0xC0
350 poke8 (m+1) $ (w .&. 0x3f) + 0x80
351 go (n+1) (m+2)
352 | 0xD800 <= w && w <= 0xDBFF -> ensure 4 $ do
353 let c = ord $ U16.chr2 w (A.unsafeIndex arr (n+1))
354 poke8 m $ (c `shiftR` 18) + 0xF0
355 poke8 (m+1) $ ((c `shiftR` 12) .&. 0x3F) + 0x80
356 poke8 (m+2) $ ((c `shiftR` 6) .&. 0x3F) + 0x80
357 poke8 (m+3) $ (c .&. 0x3F) + 0x80
358 go (n+2) (m+4)
359 | otherwise -> ensure 3 $ do
360 poke8 m $ (w `shiftR` 12) + 0xE0
361 poke8 (m+1) $ ((w `shiftR` 6) .&. 0x3F) + 0x80
362 poke8 (m+2) $ (w .&. 0x3F) + 0x80
363 go (n+1) (m+3)
364 -}
365
366 -- | Encode text using UTF-8 encoding and escape the ASCII characters using
367 -- a 'BP.PrimBounded'.
368 encodeUtf8Escaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
369 encodeUtf8Escaped be (Text arr off len) =
370 B.builder step
371 where
372 bound = max 4 $ BP.sizeBound be
373 iend = off + len
374 step !k =
375 outerLoop off
376 where
377 outerLoop !i0 !br@(B.BufferRange op0 ope)
378 | i0 >= iend = k br
379 | op0 `plusPtr` bound < ope =
380 goPartial (i0 + min outRemaining inpRemaining)
381 | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
382 where
383 outRemaining = (ope `minusPtr` op0) `div` bound
384 inpRemaining = iend - i0
385
386 goPartial !iendTmp = go i0 op0
387 where
388 go !i !op
389 | i < iendTmp = case A.unsafeIndex arr i of
390 w | w <= 0x7F -> do
391 BP.runB be (fromIntegral w) op >>= go (i + 1)
392 | w <= 0x7FF -> do
393 poke8 0 $ (w `shiftR` 6) + 0xC0
394 poke8 1 $ (w .&. 0x3f) + 0x80
395 go (i + 1) (op `plusPtr` 2)
396 | 0xD800 <= w && w <= 0xDBFF -> do
397 let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
398 poke8 0 $ (c `shiftR` 18) + 0xF0
399 poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
400 poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
401 poke8 3 $ (c .&. 0x3F) + 0x80
402 go (i + 2) (op `plusPtr` 4)
403 | otherwise -> do
404 poke8 0 $ (w `shiftR` 12) + 0xE0
405 poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
406 poke8 2 $ (w .&. 0x3F) + 0x80
407 go (i + 1) (op `plusPtr` 3)
408 | otherwise =
409 outerLoop i (B.BufferRange op ope)
410 where
411 poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
412
413
414 -- | Decode text from little endian UTF-16 encoding.
415 decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text
416 decodeUtf16LEWith onErr bs = F.unstream (E.streamUtf16LE onErr bs)
417 {-# INLINE decodeUtf16LEWith #-}
418
419 -- | Decode text from little endian UTF-16 encoding.
420 --
421 -- If the input contains any invalid little endian UTF-16 data, an
422 -- exception will be thrown. For more control over the handling of
423 -- invalid data, use 'decodeUtf16LEWith'.
424 decodeUtf16LE :: ByteString -> Text
425 decodeUtf16LE = decodeUtf16LEWith strictDecode
426 {-# INLINE decodeUtf16LE #-}
427
428 -- | Decode text from big endian UTF-16 encoding.
429 decodeUtf16BEWith :: OnDecodeError -> ByteString -> Text
430 decodeUtf16BEWith onErr bs = F.unstream (E.streamUtf16BE onErr bs)
431 {-# INLINE decodeUtf16BEWith #-}
432
433 -- | Decode text from big endian UTF-16 encoding.
434 --
435 -- If the input contains any invalid big endian UTF-16 data, an
436 -- exception will be thrown. For more control over the handling of
437 -- invalid data, use 'decodeUtf16BEWith'.
438 decodeUtf16BE :: ByteString -> Text
439 decodeUtf16BE = decodeUtf16BEWith strictDecode
440 {-# INLINE decodeUtf16BE #-}
441
442 -- | Encode text using little endian UTF-16 encoding.
443 encodeUtf16LE :: Text -> ByteString
444 encodeUtf16LE txt = E.unstream (E.restreamUtf16LE (F.stream txt))
445 {-# INLINE encodeUtf16LE #-}
446
447 -- | Encode text using big endian UTF-16 encoding.
448 encodeUtf16BE :: Text -> ByteString
449 encodeUtf16BE txt = E.unstream (E.restreamUtf16BE (F.stream txt))
450 {-# INLINE encodeUtf16BE #-}
451
452 -- | Decode text from little endian UTF-32 encoding.
453 decodeUtf32LEWith :: OnDecodeError -> ByteString -> Text
454 decodeUtf32LEWith onErr bs = F.unstream (E.streamUtf32LE onErr bs)
455 {-# INLINE decodeUtf32LEWith #-}
456
457 -- | Decode text from little endian UTF-32 encoding.
458 --
459 -- If the input contains any invalid little endian UTF-32 data, an
460 -- exception will be thrown. For more control over the handling of
461 -- invalid data, use 'decodeUtf32LEWith'.
462 decodeUtf32LE :: ByteString -> Text
463 decodeUtf32LE = decodeUtf32LEWith strictDecode
464 {-# INLINE decodeUtf32LE #-}
465
466 -- | Decode text from big endian UTF-32 encoding.
467 decodeUtf32BEWith :: OnDecodeError -> ByteString -> Text
468 decodeUtf32BEWith onErr bs = F.unstream (E.streamUtf32BE onErr bs)
469 {-# INLINE decodeUtf32BEWith #-}
470
471 -- | Decode text from big endian UTF-32 encoding.
472 --
473 -- If the input contains any invalid big endian UTF-32 data, an
474 -- exception will be thrown. For more control over the handling of
475 -- invalid data, use 'decodeUtf32BEWith'.
476 decodeUtf32BE :: ByteString -> Text
477 decodeUtf32BE = decodeUtf32BEWith strictDecode
478 {-# INLINE decodeUtf32BE #-}
479
480 -- | Encode text using little endian UTF-32 encoding.
481 encodeUtf32LE :: Text -> ByteString
482 encodeUtf32LE txt = E.unstream (E.restreamUtf32LE (F.stream txt))
483 {-# INLINE encodeUtf32LE #-}
484
485 -- | Encode text using big endian UTF-32 encoding.
486 encodeUtf32BE :: Text -> ByteString
487 encodeUtf32BE txt = E.unstream (E.restreamUtf32BE (F.stream txt))
488 {-# INLINE encodeUtf32BE #-}
489
490 foreign import ccall unsafe "_hs_text_decode_utf8" c_decode_utf8
491 :: MutableByteArray# s -> Ptr CSize
492 -> Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8)
493
494 foreign import ccall unsafe "_hs_text_decode_utf8_state" c_decode_utf8_with_state
495 :: MutableByteArray# s -> Ptr CSize
496 -> Ptr (Ptr Word8) -> Ptr Word8
497 -> Ptr CodePoint -> Ptr DecoderState -> IO (Ptr Word8)
498
499 foreign import ccall unsafe "_hs_text_decode_latin1" c_decode_latin1
500 :: MutableByteArray# s -> Ptr Word8 -> Ptr Word8 -> IO ()
501
502
503 {-
504 -- | Encode all elements of a 'F.Stream' using a 'B.BoundedEncoding'.
505 {-# INLINE encodeStreamWithB #-}
506 encodeStreamWithB :: B.BoundedEncoding a -> F.Stream a -> B.Builder
507 encodeStreamWithB be =
508 \(F.Stream next s0 _) -> B.builder $ step next s0
509 where
510 bound = B.sizeBound be
511 step next s0 k (B.BufferRange op0 ope0) =
512 go s0 op0
513 where
514 go s !op = case next s of
515 F.Done -> k (B.BufferRange op ope0)
516 F.Skip s' -> go s' op
517 F.Yield x s'
518 | op `plusPtr` bound <= ope0 -> B.runB be x op >>= go s'
519 | otherwise ->
520 return $ B.bufferFull bound op (step next s k)
521
522
523 -- |
524 -- | /Subject to fusion./
525 -- Encode all 'Char's of a 'T.Text' using a 'B.BoundedEncoding'.
526 {-# INLINE encodeTextWithB #-}
527 encodeTextWithB :: B.BoundedEncoding Char -> Text -> B.Builder
528 encodeTextWithB be = encodeStreamWithB be . F.stream
529
530 -- | Encode text using UTF-8 encoding.
531 encodeUtf8Builder :: Text -> B.Builder
532 encodeUtf8Builder = encodeUtf8Escaped (B.fromF B.word8)
533 -}
534
535