{-# LANGUAGE BangPatterns #-} -- | -- Module : Data.Text.Encoding -- Copyright : (c) Tom Harper 2008-2009, -- (c) Bryan O'Sullivan 2009, -- (c) Duncan Coutts 2009 -- -- License : BSD-style -- Maintainer : bos@serpentine.com, rtomharper@googlemail.com, -- duncan@haskell.org -- Stability : experimental -- Portability : portable -- -- Functions for converting 'Text' values to and from 'ByteString', -- using several standard encodings. -- -- To make use of a much larger variety of encodings, use the @text-icu@ -- package. module Data.Text.Encoding ( -- * Decoding ByteStrings to Text decodeASCII , decodeUtf8 , decodeUtf16LE , decodeUtf16BE , decodeUtf32LE , decodeUtf32BE -- ** Controllable error handling , decodeUtf8With , decodeUtf16LEWith , decodeUtf16BEWith , decodeUtf32LEWith , decodeUtf32BEWith -- * Encoding Text to ByteStrings , encodeUtf8 , encodeUtf8' , encodeUtf16LE , encodeUtf16BE , encodeUtf32LE , encodeUtf32BE ) where import Data.Bits ((.&.)) import Data.ByteString as B import Data.ByteString.Internal as B import Data.Text.Encoding.Error (OnDecodeError, strictDecode) import Data.Text.Encoding.Utf16 (chr2) import Data.Text.Internal (Text(..)) import Data.Text.UnsafeChar (ord) import Data.Text.UnsafeShift (shiftL, shiftR) import Data.Word (Word8) import Foreign.ForeignPtr (withForeignPtr) import Foreign.Ptr (plusPtr) import Foreign.Storable (poke) import System.IO.Unsafe (unsafePerformIO) import qualified Data.Text.Array as A import qualified Data.Text.Encoding.Fusion as E import qualified Data.Text.Fusion as F -- | Decode a 'ByteString' containing 7-bit ASCII encoded text. decodeASCII :: ByteString -> Text decodeASCII bs = F.unstream (E.streamASCII bs) {-# INLINE decodeASCII #-} -- | Decode a 'ByteString' containing UTF-8 encoded text. decodeUtf8With :: OnDecodeError -> ByteString -> Text decodeUtf8With onErr bs = F.unstream (E.streamUtf8 onErr bs) {-# INLINE decodeUtf8With #-} -- | Decode a 'ByteString' containing UTF-8 encoded text. decodeUtf8 :: ByteString -> Text decodeUtf8 = decodeUtf8With strictDecode {-# INLINE decodeUtf8 #-} -- | Encode text using UTF-8 encoding. encodeUtf8' :: Text -> ByteString encodeUtf8' txt = E.unstream (E.restreamUtf8 (F.stream txt)) {-# INLINE encodeUtf8' #-} -- | Encode text using UTF-8 encoding. encodeUtf8 :: Text -> ByteString encodeUtf8 (Text arr off len) = unsafePerformIO $ do let size0 = min len 4 mallocByteString size0 >>= start size0 off 0 where start size n0 m0 fp = withForeignPtr fp $ loop n0 m0 where loop n1 m1 ptr = go n1 m1 where go !n !m | n-off == len = return $! PS fp 0 m | size-m < 4 = {-# SCC "encodeUtf8/resize" #-} do let newSize = size `shiftL` 1 fp' <- mallocByteString newSize withForeignPtr fp' $ \ptr' -> memcpy ptr' ptr (fromIntegral m) start newSize n m fp' | otherwise = do let poke8 k v = poke (ptr `plusPtr` k) (fromIntegral v :: Word8) w = A.unsafeIndex arr n case undefined of _| w <= 0x7F -> do poke8 m w go (n+1) (m+1) | w <= 0x7FF -> do poke8 m $ (w `shiftR` 6) + 0xC0 poke8 (m+1) $ (w .&. 0x3f) + 0x80 go (n+1) (m+2) | 0xD800 <= w && w <= 0xDBFF -> do let c = ord $ chr2 w (A.unsafeIndex arr (n+1)) poke8 m $ (c `shiftR` 18) + 0xF0 poke8 (m+1) $ ((c `shiftR` 12) .&. 0x3F) + 0x80 poke8 (m+2) $ ((c `shiftR` 6) .&. 0x3F) + 0x80 poke8 (m+3) $ (c .&. 0x3F) + 0x80 go (n+2) (m+4) | otherwise -> do poke8 m $ (w `shiftR` 12) + 0xE0 poke8 (m+1) $ ((w `shiftR` 6) .&. 0x3F) + 0x80 poke8 (m+2) $ (w .&. 0x3F) + 0x80 go (n+1) (m+3) {- INLINE encodeUtf8 #-} -- | Decode text from little endian UTF-16 encoding. decodeUtf16LEWith :: OnDecodeError -> ByteString -> Text decodeUtf16LEWith onErr bs = F.unstream (E.streamUtf16LE onErr bs) {-# INLINE decodeUtf16LEWith #-} -- | Decode text from little endian UTF-16 encoding. decodeUtf16LE :: ByteString -> Text decodeUtf16LE = decodeUtf16LEWith strictDecode {-# INLINE decodeUtf16LE #-} -- | Decode text from big endian UTF-16 encoding. decodeUtf16BEWith :: OnDecodeError -> ByteString -> Text decodeUtf16BEWith onErr bs = F.unstream (E.streamUtf16BE onErr bs) {-# INLINE decodeUtf16BEWith #-} -- | Decode text from big endian UTF-16 encoding. decodeUtf16BE :: ByteString -> Text decodeUtf16BE = decodeUtf16BEWith strictDecode {-# INLINE decodeUtf16BE #-} -- | Encode text using little endian UTF-16 encoding. encodeUtf16LE :: Text -> ByteString encodeUtf16LE txt = E.unstream (E.restreamUtf16LE (F.stream txt)) {-# INLINE encodeUtf16LE #-} -- | Encode text using big endian UTF-16 encoding. encodeUtf16BE :: Text -> ByteString encodeUtf16BE txt = E.unstream (E.restreamUtf16BE (F.stream txt)) {-# INLINE encodeUtf16BE #-} -- | Decode text from little endian UTF-32 encoding. decodeUtf32LEWith :: OnDecodeError -> ByteString -> Text decodeUtf32LEWith onErr bs = F.unstream (E.streamUtf32LE onErr bs) {-# INLINE decodeUtf32LEWith #-} -- | Decode text from little endian UTF-32 encoding. decodeUtf32LE :: ByteString -> Text decodeUtf32LE = decodeUtf32LEWith strictDecode {-# INLINE decodeUtf32LE #-} -- | Decode text from big endian UTF-32 encoding. decodeUtf32BEWith :: OnDecodeError -> ByteString -> Text decodeUtf32BEWith onErr bs = F.unstream (E.streamUtf32BE onErr bs) {-# INLINE decodeUtf32BEWith #-} -- | Decode text from big endian UTF-32 encoding. decodeUtf32BE :: ByteString -> Text decodeUtf32BE = decodeUtf32BEWith strictDecode {-# INLINE decodeUtf32BE #-} -- | Encode text using little endian UTF-32 encoding. encodeUtf32LE :: Text -> ByteString encodeUtf32LE txt = E.unstream (E.restreamUtf32LE (F.stream txt)) {-# INLINE encodeUtf32LE #-} -- | Encode text using big endian UTF-32 encoding. encodeUtf32BE :: Text -> ByteString encodeUtf32BE txt = E.unstream (E.restreamUtf32BE (F.stream txt)) {-# INLINE encodeUtf32BE #-}