on a3196b3 help sign in
streamly/src/Streamly/String.hs
178 lines 6.0 kB history raw
1-- |
2-- Module : Streamly.String
3-- Copyright : (c) 2018 Composewell Technologies
4--
5-- License : BSD3
6-- Maintainer : harendra.kumar@gmail.com
7-- Stability : experimental
8-- Portability : GHC
9--
10-- Stream of 'Char' are the equivalent of Haskell Strings but without any
11-- performance issues that are usually associated with the 'String' type.
12-- Haskell lists are not suitable for storage in GC memory. We use streams for
13-- processing and arrays for storing. We can use the usual stream processing
14-- routines on these 'Char' streams. Stream processing is very efficient due to
15-- stream fusion. If strings are to be stored or buffered in memory, they can
16-- be encoded to 'Word8' arrays using the encoding routines provided in the
17-- module. Therefore, a separate type for text representation is not required.
19-- The 'String' type in this module is just a synonym for the type @List Char@.
20-- It provides better performance compared to the standard Haskell @String@
21-- type and can be used almost as a drop-in replacement, especially when used
22-- with @OverloadedStrings@ extension, with little differences.
23--
24-- See "Streamly.List", <src/docs/streamly-vs-lists.md> for more details and
25-- <src/test/PureStreams.hs> for comprehensive usage examples.
27-- TBD write a note on performance comparison with text, bytestrings, lists and
28-- vector.
29--
30module Streamly.String
31 (
32 -- String
34 -- * Encoding and Decoding
35 encodeChar8
36 , encodeChar8Unchecked
37 , decodeChar8
39 , encodeUtf8
40 , decodeUtf8
41{-
42 -- * Unicode aware operations
43 , toCaseFold
44 , toLower
45 , toUpper
46 , toTitle
48 -- * Operations on character strings
49 , strip -- (dropAround isSpace)
50 , stripEnd-}
51 , stripStart
52 , foldLines
53 , foldWords
54 , lines
55 , words{-
56 , unlines
57 , unwords-}
58 )
59where
61import Control.Monad.IO.Class (MonadIO)
62import Data.Char (isSpace, ord)
63import Data.Word (Word8)
64import GHC.Base (unsafeChr)
65import Streamly (IsStream)
66import Prelude hiding (String, lines, words, unlines, unwords)
67import Streamly.Fold (Fold)
68import Streamly.Mem.Array (Array, toArray)
70import qualified Streamly.Prelude as S
71import qualified Streamly.Fold as FL
72import qualified Streamly.Streams.StreamD as D
74-- type String = List Char
76-------------------------------------------------------------------------------
77-- Encoding/Decoding Characters
78-------------------------------------------------------------------------------
80-- decodeWith :: TextEncoding -> t m Word8 -> t m Char
81-- decodeWith = undefined
83-------------------------------------------------------------------------------
84-- Encoding/Decoding Unicode Characters
85-------------------------------------------------------------------------------
87-- | Decode a stream of bytes to Unicode characters by mapping each byte to a
88-- corresponding Unicode 'Char' in 0-255 range.
89{-# INLINE decodeChar8 #-}
90decodeChar8 :: (IsStream t, Monad m) => t m Word8 -> t m Char
91decodeChar8 = S.map (unsafeChr . fromIntegral)
93-- | Encode a stream of Unicode characters to bytes by mapping each character
94-- to a byte in 0-255 range. Throws an error if the input stream contains
95-- characters beyond 255.
96{-# INLINE encodeChar8 #-}
97encodeChar8 :: (IsStream t, Monad m) => t m Char -> t m Word8
98encodeChar8 = S.map convert
99 where
100 convert c =
101 let codepoint = ord c
102 in if codepoint > 255
103 then error $ "Streamly.String.encodeChar8 invalid \
104 \input char codepoint " ++ show codepoint
105 else fromIntegral codepoint
107-- | Like 'encodeChar8' but silently truncates and maps input characters beyond
108-- 255 to (incorrect) chars in 0-255 range. No error or exception is thrown
109-- when such truncation occurs.
110{-# INLINE encodeChar8Unchecked #-}
111encodeChar8Unchecked :: (IsStream t, Monad m) => t m Char -> t m Word8
112encodeChar8Unchecked = S.map (fromIntegral . ord)
114-- | Decode a UTF-8 encoded bytestream to a stream of Unicode characters.
115{-# INLINE decodeUtf8 #-}
116decodeUtf8 :: (Monad m, IsStream t) => t m Word8 -> t m Char
117decodeUtf8 = D.fromStreamD . D.decodeUtf8 . D.toStreamD
119{-# INLINE encodeUtf8 #-}
120-- | Encode a stream of Unicode characters to a UTF-8 encoded bytestream.
121encodeUtf8 :: (Monad m, IsStream t) => t m Char -> t m Word8
122encodeUtf8 = D.fromStreamD . D.encodeUtf8 . D.toStreamD
124{-
125-------------------------------------------------------------------------------
126-- Unicode aware operations on strings
127-------------------------------------------------------------------------------
129toCaseFold :: IsStream t => t m Char -> t m Char
130toCaseFold = undefined
132toLower :: IsStream t => t m Char -> t m Char
133toLower = undefined
135toUpper :: IsStream t => t m Char -> t m Char
136toUpper = undefined
138toTitle :: IsStream t => t m Char -> t m Char
139toTitle = undefined
141-------------------------------------------------------------------------------
142-- Utility operations on strings
143-------------------------------------------------------------------------------
145strip :: IsStream t => t m Char -> t m Char
146strip = undefined
148stripEnd :: IsStream t => t m Char -> t m Char
149stripEnd = undefined
150-}
152{-# INLINE stripStart #-}
153stripStart :: (Monad m, IsStream t) => t m Char -> t m Char
154stripStart = S.dropWhile isSpace
156{-# INLINE foldLines #-}
157foldLines :: (Monad m, IsStream t) => t m Char -> Fold m Char b -> t m b
158foldLines = flip (FL.splitSuffixBy (== '\n'))
160{-# INLINE foldWords #-}
161foldWords :: (Monad m, IsStream t) => t m Char -> Fold m Char b -> t m b
162foldWords = flip (FL.wordsBy isSpace)
164{-# INLINE lines #-}
165lines :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
166lines = FL.splitSuffixBy (== '\n') toArray
168{-# INLINE words #-}
169words :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
170words = FL.wordsBy isSpace toArray
172{-
173unlines :: IsStream t => t m (Array Char) -> t m Char
174unlines = undefined
176unwords :: IsStream t => t m (Array Char) -> t m Char
177unwords = undefined
178-}