1{-# LANGUAGE BangPatterns #-}
4-- Module : Data.Text.Encoding
5-- Copyright : (c) Tom Harper 2008-2009,
6-- (c) Bryan O'Sullivan 2009,
7-- (c) Duncan Coutts 2009
10-- Maintainer : rtharper@aftereternity.co.uk, bos@serpentine.com,
12-- Stability : experimental
13-- Portability : portable
15-- Fusible 'Stream'-oriented functions for converting between 'Text'
16-- and several common encodings.
18module Data.Text.Encoding.Fusion
32 -- Restreaming is the act of converting from one 'Stream'
33 -- representation to another.
42import Control.Exception (assert)
43import Data.Bits (shiftL, shiftR, (.&.))
44import Data.ByteString as B
45import Data.ByteString.Internal (ByteString(..), mallocByteString, memcpy)
47import Data.Text.Fusion (Step(..), Stream(..))
48import Data.Text.UnsafeChar (unsafeChr, unsafeChr8, unsafeChr32)
49import Data.Word (Word8, Word16, Word32)
50import Foreign.ForeignPtr (withForeignPtr, ForeignPtr)
51import Foreign.Storable (pokeByteOff)
52import System.IO.Unsafe (unsafePerformIO)
53import qualified Data.ByteString as B
54import qualified Data.ByteString.Unsafe as B
55import qualified Data.Text.Utf16 as U16
56import qualified Data.Text.Utf32 as U32
57import qualified Data.Text.Utf8 as U8
59data T4 a b c d = T4 {-# UNPACK #-} !a {-# UNPACK #-} !b {-# UNPACK #-} !c {-# UNPACK #-} !d
61streamASCII :: ByteString -> Stream Char
62streamASCII bs = Stream next 0 l
68 | otherwise = Yield (unsafeChr8 x1) (i+1)
70 x1 = B.unsafeIndex bs i
71{-# INLINE [0] streamASCII #-}
73-- | /O(n) Convert a ByteString into a Stream Char, using the specified encoding standard.
74streamUtf8 :: ByteString -> Stream Char
75streamUtf8 bs = Stream next 0 l
81 | U8.validate1 x1 = Yield (unsafeChr8 x1) (i+1)
82 | i+1 < l && U8.validate2 x1 x2 = Yield (U8.chr2 x1 x2) (i+2)
83 | i+2 < l && U8.validate3 x1 x2 x3 = Yield (U8.chr3 x1 x2 x3) (i+3)
84 | i+3 < l && U8.validate4 x1 x2 x3 x4 = Yield (U8.chr4 x1 x2 x3 x4) (i+4)
85 | otherwise = encodingError "UTF-8"
91 idx = B.unsafeIndex bs
92{-# INLINE [0] streamUtf8 #-}
94streamUtf16LE :: ByteString -> Stream Char
95streamUtf16LE bs = Stream next 0 l
101 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
102 | i+3 < l && U16.validate2 x1 x2 = Yield (U16.chr2 x1 x2) (i+4)
103 | otherwise = encodingError "UTF-16LE"
105 x1 = (idx (i + 1) `shiftL` 8) + idx i
106 x2 = (idx (i + 3) `shiftL` 8) + idx (i + 2)
107 idx = fromIntegral . B.unsafeIndex bs :: Int -> Word16
108{-# INLINE [0] streamUtf16LE #-}
110streamUtf16BE :: ByteString -> Stream Char
111streamUtf16BE bs = Stream next 0 l
117 | i+1 < l && U16.validate1 x1 = Yield (unsafeChr x1) (i+2)
118 | i+3 < l && U16.validate2 x1 x2 = Yield (U16.chr2 x1 x2) (i+4)
119 | otherwise = encodingError "UTF16-BE"
121 x1 = (shiftL (idx i) 8) + (idx (i + 1))
122 x2 = (shiftL (idx (i + 2)) 8) + (idx (i + 3))
123 idx = fromIntegral . B.unsafeIndex bs :: Int -> Word16
124{-# INLINE [0] streamUtf16BE #-}
126streamUtf32BE :: ByteString -> Stream Char
127streamUtf32BE bs = Stream next 0 l
133 | i+3 < l && U32.validate x = Yield (unsafeChr32 x) (i+4)
134 | otherwise = encodingError "UTF-32BE"
136 x = shiftL x1 24 + shiftL x2 16 + shiftL x3 8 + x4
141 idx = fromIntegral . B.unsafeIndex bs :: Int -> Word32
142{-# INLINE [0] streamUtf32BE #-}
144streamUtf32LE :: ByteString -> Stream Char
145streamUtf32LE bs = Stream next 0 l
151 | i+3 < l && U32.validate x = Yield (unsafeChr32 x) (i+4)
152 | otherwise = encodingError "UTF-32LE"
154 x = shiftL x4 24 + shiftL x3 16 + shiftL x2 8 + x1
159 idx = fromIntegral . B.unsafeIndex bs :: Int -> Word32
160{-# INLINE [0] streamUtf32LE #-}
162restreamASCII :: Stream Char -> Stream Word8
163restreamASCII (Stream next0 s0 len) = Stream next s0 (len*2)
165 next !s = case next0 s of
168 Yield x xs -> Yield x' xs
169 where x' = fromIntegral (ord x) :: Word8
170{-# INLINE restreamASCII #-}
172data M = N | J {-# UNPACK #-} !Word8
173 deriving (Eq, Ord, Show)
175-- | /O(n)/ Convert a Stream Char into a UTF-8 encoded Stream Word8.
176restreamUtf8 :: Stream Char -> Stream Word8
177restreamUtf8 (Stream next0 s0 len) =
178 Stream next (T4 s0 N N N) (len*2)
181 next (T4 s N N N) = case next0 s of
183 Skip s' -> Skip (T4 s' N N N)
185 | n <= 0x7F -> Yield c (T4 xs N N N)
186 | n <= 0x07FF -> Yield a2 (T4 xs (J b2) N N)
187 | n <= 0xFFFF -> Yield a3 (T4 xs (J b3) (J c3) N)
188 | otherwise -> Yield a4 (T4 xs (J b4) (J c4) (J d4))
193 (a3,b3,c3) = U8.ord3 x
194 (a4,b4,c4,d4) = U8.ord4 x
195 next (T4 s (J x2) N N) = Yield x2 (T4 s N N N)
196 next (T4 s (J x2) x3 N) = Yield x2 (T4 s x3 N N)
197 next (T4 s (J x2) x3 x4) = Yield x2 (T4 s x3 x4 N)
198 next _ = internalError "restreamUtf8"
199{-# INLINE restreamUtf8 #-}
201restreamUtf16BE :: Stream Char -> Stream Word8
202restreamUtf16BE (Stream next0 s0 len) =
203 Stream next (T4 s0 N N N) (len*2)
206 next (T4 s N N N) = case next0 s of
208 Skip s' -> Skip (T4 s' N N N)
210 | n < 0x10000 -> Yield (fromIntegral $ shiftR n 8) (T4 xs (J (fromIntegral n)) N N)
211 | otherwise -> Yield c1 (T4 xs (J c2) (J c3) (J c4))
215 c1 = fromIntegral (shiftR n1 18 + 0xD8)
216 c2 = fromIntegral (shiftR n1 10)
218 c3 = fromIntegral (shiftR n2 8 + 0xDC)
220 next (T4 s (J x2) N N) = Yield x2 (T4 s N N N)
221 next (T4 s (J x2) x3 N) = Yield x2 (T4 s x3 N N)
222 next (T4 s (J x2) x3 x4) = Yield x2 (T4 s x3 x4 N)
223 next _ = internalError "restreamUtf16BE"
224{-# INLINE restreamUtf16BE #-}
226restreamUtf16LE :: Stream Char -> Stream Word8
227restreamUtf16LE (Stream next0 s0 len) =
228 Stream next (T4 s0 N N N) (len*2)
231 next (T4 s N N N) = case next0 s of
233 Skip s' -> Skip (T4 s' N N N)
235 | n < 0x10000 -> Yield (fromIntegral n) $
236 T4 xs (J (fromIntegral $ shiftR n 8)) N N
237 | otherwise -> Yield c1 $
238 T4 xs (J c2) (J c3) (J c4)
242 c2 = fromIntegral (shiftR n1 18 + 0xD8)
243 c1 = fromIntegral (shiftR n1 10)
245 c4 = fromIntegral (shiftR n2 8 + 0xDC)
247 next (T4 s (J x2) N N) = Yield x2 (T4 s N N N)
248 next (T4 s (J x2) x3 N) = Yield x2 (T4 s x3 N N)
249 next (T4 s (J x2) x3 x4) = Yield x2 (T4 s x3 x4 N)
250 next _ = internalError "restreamUtf16LE"
251{-# INLINE restreamUtf16LE #-}
253restreamUtf32BE :: Stream Char -> Stream Word8
254restreamUtf32BE (Stream next0 s0 len) =
255 Stream next (T4 (Just s0) Nothing Nothing Nothing) (len*2)
258 next (T4 (Just s) Nothing Nothing Nothing) = case next0 s of
260 Skip s' -> Skip (T4 (Just s') Nothing Nothing Nothing)
261 Yield x xs -> Yield c1 (T4 (Just xs) (Just c2) (Just c3) (Just c4))
264 c1 = fromIntegral $ shiftR n 24
265 c2 = fromIntegral $ shiftR n 16
266 c3 = fromIntegral $ shiftR n 8
268 next (T4 (Just s) (Just x2) Nothing Nothing) = Yield x2 (T4 (Just s) Nothing Nothing Nothing)
269 next (T4 (Just s) (Just x2) x3 Nothing) = Yield x2 (T4 (Just s) x3 Nothing Nothing)
270 next (T4 (Just s) (Just x2) x3 x4) = Yield x2 (T4 (Just s) x3 x4 Nothing)
271 next _ = internalError "restreamUtf32BE"
272{-# INLINE restreamUtf32BE #-}
274restreamUtf32LE :: Stream Char -> Stream Word8
275restreamUtf32LE (Stream next0 s0 len) =
276 Stream next (T4 (Just s0) Nothing Nothing Nothing) (len*2)
279 next (T4 (Just s) Nothing Nothing Nothing) = case next0 s of
281 Skip s' -> Skip (T4 (Just s') Nothing Nothing Nothing)
282 Yield x xs -> Yield c1 (T4 (Just xs) (Just c2) (Just c3) (Just c4))
285 c4 = fromIntegral $ shiftR n 24
286 c3 = fromIntegral $ shiftR n 16
287 c2 = fromIntegral $ shiftR n 8
289 next (T4 (Just s) (Just x2) Nothing Nothing) = Yield x2 (T4 (Just s) Nothing Nothing Nothing)
290 next (T4 (Just s) (Just x2) x3 Nothing) = Yield x2 (T4 (Just s) x3 Nothing Nothing)
291 next (T4 (Just s) (Just x2) x3 x4) = Yield x2 (T4 (Just s) x3 x4 Nothing)
292 next _ = internalError "restreamUtf32LE"
293{-# INLINE restreamUtf32LE #-}
296-- | /O(n)/ Convert a 'Stream' 'Word8' to a 'ByteString'.
297unstream :: Stream Word8 -> ByteString
298unstream (Stream next s0 len) = unsafePerformIO $ do
299 fp0 <- mallocByteString len
302 loop !fp !n !off !s = case next s of
303 Done -> trimUp fp n off
304 Skip s' -> loop fp n off s'
306 | n == off -> realloc fp n off s' x
308 withForeignPtr fp $ \p -> pokeByteOff p off x
310 {-# NOINLINE realloc #-}
311 realloc fp n off s x = do
314 withForeignPtr fp' $ \p -> pokeByteOff p off x
315 loop fp' n' (off+1) s
316 {-# NOINLINE trimUp #-}
317 trimUp fp _ off = return $! PS fp 0 off
318 copy0 :: ForeignPtr Word8 -> Int -> Int -> IO (ForeignPtr Word8)
319 copy0 !src !srcLen !destLen = assert (srcLen <= destLen) $ do
320 dest <- mallocByteString destLen
321 withForeignPtr src $ \src' ->
322 withForeignPtr dest $ \dest' ->
323 memcpy dest' src' (fromIntegral destLen)
326internalError :: String -> a
328 error $ "Data.Text.Encoding.Fusion." ++ func ++ ": internal error"
330encodingError :: String -> a
331encodingError encoding =
332 error $ "Data.Text.Encoding.Fusion: Bad " ++ encoding ++ " stream"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list