on master help sign in
rename Unicode encode/decode ops
011299bHarendra Kumar
benchmark/FileIO.hs+4−4
@@ -155,12 +155,12 @@ main = do
155 , mkBench "read-word8" href $ do
155 , mkBench "read-word8" href $ do
156 Handles inh _ <- readIORef href
156 Handles inh _ <- readIORef href
157 BFS.readWord8 inh
157 BFS.readWord8 inh
158− , mkBench "read-char8" href $ do
158+ , mkBench "read-latin1" href $ do
159 Handles inh _ <- readIORef href
159 Handles inh _ <- readIORef href
160− BFS.decodeChar8 inh
160+ BFS.decodeLatin1 inh
161 , mkBench "read-utf8" href $ do
161 , mkBench "read-utf8" href $ do
162 Handles inh _ <- readIORef href
162 Handles inh _ <- readIORef href
163− BFS.decodeUtf8Lenient inh
163+ BFS.decodeUtf8Lax inh
164 ]
164 ]
165 , bgroup "copyArray"
165 , bgroup "copyArray"
166 [ mkBench "copy" href $ do
166 [ mkBench "copy" href $ do
@@ -176,7 +176,7 @@ main = do
176 ]
176 ]
177 -- This needs an ascii file, as decode just errors out.
177 -- This needs an ascii file, as decode just errors out.
178 , bgroup "decode-encode"
178 , bgroup "decode-encode"
179− [ mkBench "char8" href $ do
179+ [ mkBench "latin1" href $ do
180 Handles inh outh <- readIORef href
180 Handles inh outh <- readIORef href
181 BFS.copyCodecChar8 inh outh
181 BFS.copyCodecChar8 inh outh
182 , mkBench "utf8-arrays" href $ do
182 , mkBench "utf8-arrays" href $ do
examples/FileSinkServer.hs+2−2
@@ -24,7 +24,7 @@ main = do
24 file <- fmap head getArgs
24 file <- fmap head getArgs
25 withFile file AppendMode
25 withFile file AppendMode
26 (\src -> S.fold (FH.write src)
26 (\src -> S.fold (FH.write src)
27− $ encodeChar8Unchecked
27+ $ encodeLatin1Lax
28 $ S.concatUnfold A.read
28 $ S.concatUnfold A.read
29 $ S.concatMapWith parallel use
29 $ S.concatMapWith parallel use
30 $ S.unfold TCP.acceptOnPort 8090)
30 $ S.unfold TCP.acceptOnPort 8090)
@@ -34,5 +34,5 @@ main = do
34 use sk = S.finally (liftIO $ close sk) (recv sk)
34 use sk = S.finally (liftIO $ close sk) (recv sk)
35 recv =
35 recv =
36 S.splitWithSuffix (== '\n') A.write
36 S.splitWithSuffix (== '\n') A.write
37− . decodeChar8
37+ . decodeLatin1
38 . S.unfold NS.read
38 . S.unfold NS.read
examples/WordClassifier.hs+3−2
@@ -17,6 +17,7 @@ import qualified Data.List as List
17import qualified Data.Ord as Ord
17import qualified Data.Ord as Ord
18import Foreign.Storable (Storable(..))
18import Foreign.Storable (Storable(..))
19import qualified Streamly.Data.Unicode.Stream as Streamly
19import qualified Streamly.Data.Unicode.Stream as Streamly
20+import qualified Streamly.Internal.Data.Unicode.Stream as Streamly
20import qualified Streamly.FileSystem.Handle as FH
21import qualified Streamly.FileSystem.Handle as FH
21import qualified Streamly.Data.Fold as FL
22import qualified Streamly.Data.Fold as FL
22import qualified Streamly.Internal.Data.Fold as IFL
23import qualified Streamly.Internal.Data.Fold as IFL
@@ -56,9 +57,9 @@ main =
56 name <- fmap head getArgs
57 name <- fmap head getArgs
57 src <- openFile name ReadMode
58 src <- openFile name ReadMode
58 Streamly.unfold FH.read src -- SerialT IO Word8
59 Streamly.unfold FH.read src -- SerialT IO Word8
59− & Streamly.decodeChar8 -- SerialT IO Char
60+ & Streamly.decodeLatin1 -- SerialT IO Char
60 & Streamly.map toLower -- SerialT IO Char
61 & Streamly.map toLower -- SerialT IO Char
61− & Streamly.foldWords FL.toList -- SerialT IO String
62+ & Streamly.words FL.toList -- SerialT IO String
62 & Streamly.filter (all isAlpha)
63 & Streamly.filter (all isAlpha)
63 & Streamly.foldlM' (flip (Map.alterF alter)) Map.empty
64 & Streamly.foldlM' (flip (Map.alterF alter)) Map.empty
64 & fmap Map.toList
65 & fmap Map.toList
examples/WordCount.hs+1−1
@@ -336,7 +336,7 @@ countCharSerial (Counts l w c wasSpace) ch =
336_wc_mwl_serial :: Handle -> IO ()
336_wc_mwl_serial :: Handle -> IO ()
337_wc_mwl_serial src = print =<< (
337_wc_mwl_serial src = print =<< (
338 S.foldl' countCharSerial (Counts 0 0 0 True)
338 S.foldl' countCharSerial (Counts 0 0 0 True)
339− $ S.decodeUtf8Lenient
339+ $ S.decodeUtf8Lax
340 $ S.unfold FH.read src)
340 $ S.unfold FH.read src)
341
341
342-------------------------------------------------------------------------------
342-------------------------------------------------------------------------------
src/Streamly/Benchmark/FileIO/Stream.hs+40−39
@@ -47,10 +47,10 @@ module Streamly.Benchmark.FileIO.Stream
47 , wordsUnwordsCopy
47 , wordsUnwordsCopy
48 , wordsUnwordsCharArrayCopy
48 , wordsUnwordsCharArrayCopy
49 , readWord8
49 , readWord8
50− , decodeChar8
50+ , decodeLatin1
51 , copyCodecChar8
51 , copyCodecChar8
52 , copyCodecUtf8
52 , copyCodecUtf8
53− , decodeUtf8Lenient
53+ , decodeUtf8Lax
54 , copyCodecUtf8Lenient
54 , copyCodecUtf8Lenient
55 , chunksOf
55 , chunksOf
56 , chunksOfD
56 , chunksOfD
@@ -79,6 +79,7 @@ import qualified Streamly.Data.Fold as FL
79-- import qualified Streamly.Internal.Data.Fold as IFL
79-- import qualified Streamly.Internal.Data.Fold as IFL
80import qualified Streamly.Data.Unicode.Stream as SS
80import qualified Streamly.Data.Unicode.Stream as SS
81import qualified Streamly.Internal.Data.Unicode.Stream as IUS
81import qualified Streamly.Internal.Data.Unicode.Stream as IUS
82+import qualified Streamly.Internal.Memory.Unicode.Array as IUA
82import qualified Streamly.Internal.Data.Unfold as IUF
83import qualified Streamly.Internal.Data.Unfold as IUF
83import qualified Streamly.Internal.Prelude as IP
84import qualified Streamly.Internal.Prelude as IP
84import qualified Streamly.Streams.StreamD as D
85import qualified Streamly.Streams.StreamD as D
@@ -119,8 +120,8 @@ inspect $ 'countBytes `hasNoType` ''D.ConcatMapUState
119countLines :: Handle -> IO Int
120countLines :: Handle -> IO Int
120countLines =
121countLines =
121 S.length
122 S.length
122− . SS.foldLines FL.drain
123+ . IUS.lines FL.drain
123− . SS.decodeChar8
124+ . SS.decodeLatin1
124 . S.unfold FH.read
125 . S.unfold FH.read
125
126
126#ifdef INSPECTION
127#ifdef INSPECTION
@@ -135,8 +136,8 @@ inspect $ 'countLines `hasNoType` ''D.ConcatMapUState
135countWords :: Handle -> IO Int
136countWords :: Handle -> IO Int
136countWords =
137countWords =
137 S.length
138 S.length
138− . SS.foldWords FL.drain
139+ . IUS.words FL.drain
139− . SS.decodeChar8
140+ . SS.decodeLatin1
140 . S.unfold FH.read
141 . S.unfold FH.read
141
142
142#ifdef INSPECTION
143#ifdef INSPECTION
@@ -150,8 +151,8 @@ inspect $ hasNoTypeClasses 'countWords
150countLinesU :: Handle -> IO Int
151countLinesU :: Handle -> IO Int
151countLinesU inh =
152countLinesU inh =
152 S.length
153 S.length
153− $ SS.foldLines FL.drain
154+ $ IUS.lines FL.drain
154− $ SS.decodeChar8
155+ $ SS.decodeLatin1
155 $ S.concatUnfold A.read (IFH.toStreamArrays inh)
156 $ S.concatUnfold A.read (IFH.toStreamArrays inh)
156
157
157#ifdef INSPECTION
158#ifdef INSPECTION
@@ -304,11 +305,11 @@ inspect $ 'copy `hasNoType` ''D.ConcatMapUState
304readWord8 :: Handle -> IO ()
305readWord8 :: Handle -> IO ()
305readWord8 inh = S.drain $ S.unfold FH.read inh
306readWord8 inh = S.drain $ S.unfold FH.read inh
306
307
307−{-# INLINE decodeChar8 #-}
308+{-# INLINE decodeLatin1 #-}
308−decodeChar8 :: Handle -> IO ()
309+decodeLatin1 :: Handle -> IO ()
309−decodeChar8 inh =
310+decodeLatin1 inh =
310 S.drain
311 S.drain
311− $ SS.decodeChar8
312+ $ SS.decodeLatin1
312 $ S.unfold FH.read inh
313 $ S.unfold FH.read inh
313
314
314-- | Copy file
315-- | Copy file
@@ -316,8 +317,8 @@ decodeChar8 inh =
316copyCodecChar8 :: Handle -> Handle -> IO ()
317copyCodecChar8 :: Handle -> Handle -> IO ()
317copyCodecChar8 inh outh =
318copyCodecChar8 inh outh =
318 S.fold (FH.write outh)
319 S.fold (FH.write outh)
319− $ SS.encodeChar8
320+ $ SS.encodeLatin1
320− $ SS.decodeChar8
321+ $ SS.decodeLatin1
321 $ S.unfold FH.read inh
322 $ S.unfold FH.read inh
322
323
323#ifdef INSPECTION
324#ifdef INSPECTION
@@ -327,18 +328,18 @@ inspect $ 'copyCodecChar8 `hasNoType` ''AT.FlattenState
327inspect $ 'copyCodecChar8 `hasNoType` ''D.ConcatMapUState
328inspect $ 'copyCodecChar8 `hasNoType` ''D.ConcatMapUState
328#endif
329#endif
329
330
330−{-# INLINE decodeUtf8Lenient #-}
331+{-# INLINE decodeUtf8Lax #-}
331−decodeUtf8Lenient :: Handle -> IO ()
332+decodeUtf8Lax :: Handle -> IO ()
332−decodeUtf8Lenient inh =
333+decodeUtf8Lax inh =
333 S.drain
334 S.drain
334− $ SS.decodeUtf8Lenient
335+ $ SS.decodeUtf8Lax
335 $ S.unfold FH.read inh
336 $ S.unfold FH.read inh
336
337
337#ifdef INSPECTION
338#ifdef INSPECTION
338−inspect $ hasNoTypeClasses 'decodeUtf8Lenient
339+inspect $ hasNoTypeClasses 'decodeUtf8Lax
339−-- inspect $ 'decodeUtf8Lenient `hasNoType` ''Step
340+-- inspect $ 'decodeUtf8Lax `hasNoType` ''Step
340−-- inspect $ 'decodeUtf8Lenient `hasNoType` ''AT.FlattenState
341+-- inspect $ 'decodeUtf8Lax `hasNoType` ''AT.FlattenState
341−-- inspect $ 'decodeUtf8Lenient `hasNoType` ''D.ConcatMapUState
342+-- inspect $ 'decodeUtf8Lax `hasNoType` ''D.ConcatMapUState
342#endif
343#endif
343
344
344-- | Copy file
345-- | Copy file
@@ -363,7 +364,7 @@ copyCodecUtf8Lenient :: Handle -> Handle -> IO ()
363copyCodecUtf8Lenient inh outh =
364copyCodecUtf8Lenient inh outh =
364 S.fold (FH.write outh)
365 S.fold (FH.write outh)
365 $ SS.encodeUtf8
366 $ SS.encodeUtf8
366− $ SS.decodeUtf8Lenient
367+ $ SS.decodeUtf8Lax
367 $ S.unfold FH.read inh
368 $ S.unfold FH.read inh
368
369
369#ifdef INSPECTION
370#ifdef INSPECTION
@@ -411,10 +412,10 @@ inspect $ 'chunksOfD `hasNoType` ''D.ConcatMapUState
411linesUnlinesCopy :: Handle -> Handle -> IO ()
412linesUnlinesCopy :: Handle -> Handle -> IO ()
412linesUnlinesCopy inh outh =
413linesUnlinesCopy inh outh =
413 S.fold (FH.write outh)
414 S.fold (FH.write outh)
414− $ SS.encodeChar8
415+ $ SS.encodeLatin1
415− $ SS.unfoldLines IUF.fromList
416+ $ IUS.unlines IUF.fromList
416 $ S.splitOnSuffix (== '\n') FL.toList
417 $ S.splitOnSuffix (== '\n') FL.toList
417− $ SS.decodeChar8
418+ $ SS.decodeLatin1
418 $ S.unfold FH.read inh
419 $ S.unfold FH.read inh
419
420
420{-# INLINE linesUnlinesArrayWord8Copy #-}
421{-# INLINE linesUnlinesArrayWord8Copy #-}
@@ -431,10 +432,10 @@ linesUnlinesArrayWord8Copy inh outh =
431linesUnlinesArrayCharCopy :: Handle -> Handle -> IO ()
432linesUnlinesArrayCharCopy :: Handle -> Handle -> IO ()
432linesUnlinesArrayCharCopy inh outh =
433linesUnlinesArrayCharCopy inh outh =
433 S.fold (FH.write outh)
434 S.fold (FH.write outh)
434− $ SS.encodeChar8
435+ $ SS.encodeLatin1
435− $ IUS.unlines
436+ $ IUA.unlines
436− $ IUS.lines
437+ $ IUA.lines
437− $ SS.decodeChar8
438+ $ SS.decodeLatin1
438 $ S.unfold FH.read inh
439 $ S.unfold FH.read inh
439
440
440#ifdef INSPECTION
441#ifdef INSPECTION
@@ -451,10 +452,10 @@ inspect $ hasNoTypeClassesExcept 'linesUnlinesArrayCharCopy [''Storable]
451linesUnlinesArrayUtf8Copy :: Handle -> Handle -> IO ()
452linesUnlinesArrayUtf8Copy :: Handle -> Handle -> IO ()
452linesUnlinesArrayUtf8Copy inh outh =
453linesUnlinesArrayUtf8Copy inh outh =
453 S.fold (FH.write outh)
454 S.fold (FH.write outh)
454− $ SS.encodeChar8
455+ $ SS.encodeLatin1
455 $ IP.intercalate (A.fromList [10]) (pipe SS.decodeUtf8P A.read)
456 $ IP.intercalate (A.fromList [10]) (pipe SS.decodeUtf8P A.read)
456 $ S.splitOnSuffix (== '\n') (IFL.lmap SS.encodeUtf8 A.write)
457 $ S.splitOnSuffix (== '\n') (IFL.lmap SS.encodeUtf8 A.write)
457− $ SS.decodeChar8
458+ $ SS.decodeLatin1
458 $ S.unfold FH.read inh
459 $ S.unfold FH.read inh
459-}
460-}
460
461
@@ -500,8 +501,8 @@ inspect $ hasNoTypeClasses 'wordsUnwordsCopyWord8
500wordsUnwordsCopy :: Handle -> Handle -> IO ()
501wordsUnwordsCopy :: Handle -> Handle -> IO ()
501wordsUnwordsCopy inh outh =
502wordsUnwordsCopy inh outh =
502 S.fold (FH.write outh)
503 S.fold (FH.write outh)
503− $ SS.encodeChar8
504+ $ SS.encodeLatin1
504− $ SS.unfoldWords IUF.fromList
505+ $ IUS.unwords IUF.fromList
505 -- XXX This pipeline does not fuse with wordsBy but fuses with splitOn
506 -- XXX This pipeline does not fuse with wordsBy but fuses with splitOn
506 -- with -funfolding-use-threshold=300. With wordsBy it does not fuse
507 -- with -funfolding-use-threshold=300. With wordsBy it does not fuse
507 -- even with high limits for inlining and spec-constr ghc options. With
508 -- even with high limits for inlining and spec-constr ghc options. With
@@ -511,7 +512,7 @@ wordsUnwordsCopy inh outh =
511 -- this case could be an unknown issue, need more investigation.
512 -- this case could be an unknown issue, need more investigation.
512 $ S.wordsBy isSpace FL.toList
513 $ S.wordsBy isSpace FL.toList
513 -- -- $ S.splitOn isSpace FL.toList
514 -- -- $ S.splitOn isSpace FL.toList
514− $ SS.decodeChar8
515+ $ SS.decodeLatin1
515 $ S.unfold FH.read inh
516 $ S.unfold FH.read inh
516
517
517#ifdef INSPECTION
518#ifdef INSPECTION
@@ -525,10 +526,10 @@ wordsUnwordsCopy inh outh =
525wordsUnwordsCharArrayCopy :: Handle -> Handle -> IO ()
526wordsUnwordsCharArrayCopy :: Handle -> Handle -> IO ()
526wordsUnwordsCharArrayCopy inh outh =
527wordsUnwordsCharArrayCopy inh outh =
527 S.fold (FH.write outh)
528 S.fold (FH.write outh)
528− $ SS.encodeChar8
529+ $ SS.encodeLatin1
529− $ IUS.unwords
530+ $ IUA.unwords
530− $ IUS.words
531+ $ IUA.words
531− $ SS.decodeChar8
532+ $ SS.decodeLatin1
532 $ S.unfold FH.read inh
533 $ S.unfold FH.read inh
533
534
534lf :: Word8
535lf :: Word8
src/Streamly/Data/Unicode/Stream.hs+23−26
@@ -8,25 +8,21 @@
8-- Stability : experimental
8-- Stability : experimental
9-- Portability : GHC
9-- Portability : GHC
10--
10--
11−-- A unicode string can be represented either as a stream of 'Char' e.g.
11+-- A byte stream of Unicode text can be decoded into a stream of 'Char' and
12−-- 'SerialT' 'Identity' 'Char' or as 'Array' 'Char'. Unicode text processing
12+-- processed efficiently using regular stream processing operations. A stream
13−-- can be done efficiently by applying stream operations and folds on the
13+-- of 'Char' can be encoded into a byte stream using UTF formats and written to
14−-- stream of 'Char'. When using 'Array' 'Char' direct array operations can be
14+-- IO devices.
15−-- applied where available or the array can be read into a stream and then
15+--
16−-- processed using stream operations. Use 'Array' 'Char' when you need to store
16+-- If you have to store a unicode string in memory you can either write it as a
17−-- or buffer strings temporarily in memory. Streams in 'Identity' monad and
17+-- pure stream "SerialT Identity Char" or write the 'Char' stream as "Array
18+-- Char". If space efficiency is a concern you can UTF8 encode the 'Char'
19+-- stream and then write it as "Array Word8". "SerialT Identity Char" and
18−-- 'Array' 'Char' are instances of 'IsString' and 'IsList', therefore,
20+-- "Array Char" are instances of 'IsString' and 'IsList', therefore,
19-- 'OverloadedStrings' and 'OverloadedLists' extensions can be used for
21-- 'OverloadedStrings' and 'OverloadedLists' extensions can be used for
20-- convenience.
22-- convenience.
21--
23--
22−-- 'Array' 'Char' is usually perfectly fine to buffer short to medium or even
24+-- Some experimental APIs to conveniently process text using "Array Char"
23−-- large amounts of text in memory. Also, it is computationally efficient as
25+-- represenation can be found in "Streamly.Internal.Unicode.Array".
24−-- there is no encoding/decoding involved. We recommend using true streaming
25−-- operations to avoid buffering large amounts of data as long as possible e.g.
26−-- use `foldLines` instead of `lines`. However, if for some reason you are
27−-- buffering very large amounts of text in memory and are worried about space
28−-- efficiency you can use 'encodeUtf8' on the stream to convert it to a utf8
29−-- encoded 'Array' 'Word8'.
30--
26--
31-- Please note the following:
27-- Please note the following:
32--
28--
@@ -43,13 +39,13 @@
43module Streamly.Data.Unicode.Stream
39module Streamly.Data.Unicode.Stream
44 (
40 (
45 -- * Construction (Decoding)
41 -- * Construction (Decoding)
46− decodeChar8
42+ decodeLatin1
47 , decodeUtf8
43 , decodeUtf8
48− , decodeUtf8Lenient
44+ , decodeUtf8Lax
49
45
50 -- * Elimination (Encoding)
46 -- * Elimination (Encoding)
51− , encodeChar8
47+ , encodeLatin1
52− , encodeChar8Unchecked
48+ , encodeLatin1Lax
53 , encodeUtf8
49 , encodeUtf8
54 {-
50 {-
55 -- * Operations on character strings
51 -- * Operations on character strings
@@ -57,14 +53,15 @@ module Streamly.Data.Unicode.Stream
57 , stripEnd
53 , stripEnd
58 , stripStart
54 , stripStart
59 -}
55 -}
56+ -- Not exposing these yet as we have consider these with respect to Unicode
57+ -- segmentation routines which are yet to be implemented.
60− -- * Transformation
58+ -- -- * Transformation
61− , foldLines
59+ -- , lines
62− , foldWords
60+ -- , words
63− , unfoldLines
61+ -- , unlines
64− , unfoldWords
62+ -- , unwords
65 )
63 )
66where
64where
67
65
68import Streamly.Internal.Data.Unicode.Stream
66import Streamly.Internal.Data.Unicode.Stream
69import Prelude hiding (lines, words, unlines, unwords)
67import Prelude hiding (lines, words, unlines, unwords)
70−import qualified Streamly.Streams.StreamD as D
src/Streamly/Internal/Data/Unicode/Char.hsnew file+49
@@ -0,0 +1,49 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+-- |
3+-- Module : Streamly.Data.Internal.Unicode.Char
4+-- Copyright : (c) 2018 Composewell Technologies
5+--
6+-- License : BSD3
7+-- Maintainer : streamly@composewell.com
8+-- Stability : experimental
9+-- Portability : GHC
10+--
11+module Streamly.Internal.Data.Unicode.Char
12+ (
13+ -- * Unicode aware operations
14+ {-
15+ toCaseFold
16+ , toLower
17+ , toUpper
18+ , toTitle
19+ -}
20+ )
21+where
22+
23+-- import Streamly (IsStream)
24+
25+-------------------------------------------------------------------------------
26+-- Unicode aware operations on strings
27+-------------------------------------------------------------------------------
28+
29+{-
30+-- |
31+-- /undefined/
32+toCaseFold :: IsStream t => Char -> t m Char
33+toCaseFold = undefined
34+
35+-- |
36+-- /undefined/
37+toLower :: IsStream t => Char -> t m Char
38+toLower = undefined
39+
40+-- |
41+-- /undefined/
42+toUpper :: IsStream t => Char -> t m Char
43+toUpper = undefined
44+
45+-- |
46+-- /undefined/
47+toTitle :: IsStream t => Char -> t m Char
48+toTitle = undefined
49+-}
src/Streamly/Internal/Data/Unicode/Stream.hs+68−137
@@ -11,9 +11,9 @@
11module Streamly.Internal.Data.Unicode.Stream
11module Streamly.Internal.Data.Unicode.Stream
12 (
12 (
13 -- * Construction (Decoding)
13 -- * Construction (Decoding)
14− decodeChar8
14+ decodeLatin1
15 , decodeUtf8
15 , decodeUtf8
16− , decodeUtf8Lenient
16+ , decodeUtf8Lax
17 , D.DecodeError(..)
17 , D.DecodeError(..)
18 , D.DecodeState
18 , D.DecodeState
19 , D.CodePoint
19 , D.CodePoint
@@ -23,27 +23,16 @@ module Streamly.Internal.Data.Unicode.Stream
23 , decodeUtf8ArraysLenient
23 , decodeUtf8ArraysLenient
24
24
25 -- * Elimination (Encoding)
25 -- * Elimination (Encoding)
26− , encodeChar8
26+ , encodeLatin1
27− , encodeChar8Unchecked
27+ , encodeLatin1Lax
28 , encodeUtf8
28 , encodeUtf8
29−{-
29+ {-
30− -- * Unicode aware operations
31− , toCaseFold
32− , toLower
33− , toUpper
34− , toTitle
35−
36 -- * Operations on character strings
30 -- * Operations on character strings
37 , strip -- (dropAround isSpace)
31 , strip -- (dropAround isSpace)
38− , stripEnd-}
32+ , stripEnd
33+ -}
39 -- * Transformation
34 -- * Transformation
40 , stripStart
35 , stripStart
41− , foldLines
42− , foldWords
43− , unfoldLines
44− , unfoldWords
45−
46− -- * Streams of Strings
47 , lines
36 , lines
48 , words
37 , words
49 , unlines
38 , unlines
@@ -55,62 +44,63 @@ import Control.Monad.IO.Class (MonadIO)
55import Data.Char (ord)
44import Data.Char (ord)
56import Data.Word (Word8)
45import Data.Word (Word8)
57import GHC.Base (unsafeChr)
46import GHC.Base (unsafeChr)
58−import Streamly (IsStream, MonadAsync)
47+import Streamly (IsStream)
59import Prelude hiding (String, lines, words, unlines, unwords)
48import Prelude hiding (String, lines, words, unlines, unwords)
60import Streamly.Data.Fold (Fold)
49import Streamly.Data.Fold (Fold)
61import Streamly.Memory.Array (Array)
50import Streamly.Memory.Array (Array)
62import Streamly.Internal.Data.Unfold (Unfold)
51import Streamly.Internal.Data.Unfold (Unfold)
63
52
64import qualified Streamly.Internal.Prelude as S
53import qualified Streamly.Internal.Prelude as S
65−import qualified Streamly.Memory.Array as A
66import qualified Streamly.Streams.StreamD as D
54import qualified Streamly.Streams.StreamD as D
67
55
68−-- type String = List Char
69−
70−-------------------------------------------------------------------------------
71−-- Encoding/Decoding Characters
72−-------------------------------------------------------------------------------
73−
74−-- decodeWith :: TextEncoding -> t m Word8 -> t m Char
75−-- decodeWith = undefined
76−
77-------------------------------------------------------------------------------
56-------------------------------------------------------------------------------
78-- Encoding/Decoding Unicode Characters
57-- Encoding/Decoding Unicode Characters
79-------------------------------------------------------------------------------
58-------------------------------------------------------------------------------
80
59
81-- | Decode a stream of bytes to Unicode characters by mapping each byte to a
60-- | Decode a stream of bytes to Unicode characters by mapping each byte to a
82-- corresponding Unicode 'Char' in 0-255 range.
61-- corresponding Unicode 'Char' in 0-255 range.
83−{-# INLINE decodeChar8 #-}
62+--
63+-- /Since: 0.7.0/
64+{-# INLINE decodeLatin1 #-}
84−decodeChar8 :: (IsStream t, Monad m) => t m Word8 -> t m Char
65+decodeLatin1 :: (IsStream t, Monad m) => t m Word8 -> t m Char
85−decodeChar8 = S.map (unsafeChr . fromIntegral)
66+decodeLatin1 = S.map (unsafeChr . fromIntegral)
86
67
87-- | Encode a stream of Unicode characters to bytes by mapping each character
68-- | Encode a stream of Unicode characters to bytes by mapping each character
88-- to a byte in 0-255 range. Throws an error if the input stream contains
69-- to a byte in 0-255 range. Throws an error if the input stream contains
89-- characters beyond 255.
70-- characters beyond 255.
90−{-# INLINE encodeChar8 #-}
71+--
72+-- /Since: 0.7.0/
73+{-# INLINE encodeLatin1 #-}
91−encodeChar8 :: (IsStream t, Monad m) => t m Char -> t m Word8
74+encodeLatin1 :: (IsStream t, Monad m) => t m Char -> t m Word8
92−encodeChar8 = S.map convert
75+encodeLatin1 = S.map convert
93 where
76 where
94 convert c =
77 convert c =
95 let codepoint = ord c
78 let codepoint = ord c
96 in if codepoint > 255
79 in if codepoint > 255
97− then error $ "Streamly.String.encodeChar8 invalid \
80+ then error $ "Streamly.String.encodeLatin1 invalid \
98 \input char codepoint " ++ show codepoint
81 \input char codepoint " ++ show codepoint
99 else fromIntegral codepoint
82 else fromIntegral codepoint
100
83
101−-- | Like 'encodeChar8' but silently truncates and maps input characters beyond
84+-- | Like 'encodeLatin1' but silently truncates and maps input characters beyond
102-- 255 to (incorrect) chars in 0-255 range. No error or exception is thrown
85-- 255 to (incorrect) chars in 0-255 range. No error or exception is thrown
103-- when such truncation occurs.
86-- when such truncation occurs.
104−{-# INLINE encodeChar8Unchecked #-}
87+--
88+-- /Since: 0.7.0/
89+{-# INLINE encodeLatin1Lax #-}
105−encodeChar8Unchecked :: (IsStream t, Monad m) => t m Char -> t m Word8
90+encodeLatin1Lax :: (IsStream t, Monad m) => t m Char -> t m Word8
106−encodeChar8Unchecked = S.map (fromIntegral . ord)
91+encodeLatin1Lax = S.map (fromIntegral . ord)
107
92
108-- | Decode a UTF-8 encoded bytestream to a stream of Unicode characters.
93-- | Decode a UTF-8 encoded bytestream to a stream of Unicode characters.
109-- The incoming stream is truncated if an invalid codepoint is encountered.
94-- The incoming stream is truncated if an invalid codepoint is encountered.
95+--
96+-- /Since: 0.7.0/
110{-# INLINE decodeUtf8 #-}
97{-# INLINE decodeUtf8 #-}
111decodeUtf8 :: (Monad m, IsStream t) => t m Word8 -> t m Char
98decodeUtf8 :: (Monad m, IsStream t) => t m Word8 -> t m Char
112decodeUtf8 = D.fromStreamD . D.decodeUtf8 . D.toStreamD
99decodeUtf8 = D.fromStreamD . D.decodeUtf8 . D.toStreamD
113
100
101+-- |
102+--
103+-- /Internal/
114{-# INLINE decodeUtf8Arrays #-}
104{-# INLINE decodeUtf8Arrays #-}
115decodeUtf8Arrays :: (MonadIO m, IsStream t) => t m (Array Word8) -> t m Char
105decodeUtf8Arrays :: (MonadIO m, IsStream t) => t m (Array Word8) -> t m Char
116decodeUtf8Arrays = D.fromStreamD . D.decodeUtf8Arrays . D.toStreamD
106decodeUtf8Arrays = D.fromStreamD . D.decodeUtf8Arrays . D.toStreamD
@@ -118,15 +108,23 @@ decodeUtf8Arrays = D.fromStreamD . D.decodeUtf8Arrays . D.toStreamD
118-- | Decode a UTF-8 encoded bytestream to a stream of Unicode characters.
108-- | Decode a UTF-8 encoded bytestream to a stream of Unicode characters.
119-- Any invalid codepoint encountered is replaced with the unicode replacement
109-- Any invalid codepoint encountered is replaced with the unicode replacement
120-- character.
110-- character.
121−{-# INLINE decodeUtf8Lenient #-}
111+--
112+-- /Since: 0.7.0/
113+{-# INLINE decodeUtf8Lax #-}
122−decodeUtf8Lenient :: (Monad m, IsStream t) => t m Word8 -> t m Char
114+decodeUtf8Lax :: (Monad m, IsStream t) => t m Word8 -> t m Char
123−decodeUtf8Lenient = D.fromStreamD . D.decodeUtf8Lenient . D.toStreamD
115+decodeUtf8Lax = D.fromStreamD . D.decodeUtf8Lenient . D.toStreamD
124
116
117+-- |
118+--
119+-- /Internal/
125{-# INLINE decodeUtf8Either #-}
120{-# INLINE decodeUtf8Either #-}
126decodeUtf8Either :: (Monad m, IsStream t)
121decodeUtf8Either :: (Monad m, IsStream t)
127 => t m Word8 -> t m (Either D.DecodeError Char)
122 => t m Word8 -> t m (Either D.DecodeError Char)
128decodeUtf8Either = D.fromStreamD . D.decodeUtf8Either . D.toStreamD
123decodeUtf8Either = D.fromStreamD . D.decodeUtf8Either . D.toStreamD
129
124
125+-- |
126+--
127+-- /Internal/
130{-# INLINE resumeDecodeUtf8Either #-}
128{-# INLINE resumeDecodeUtf8Either #-}
131resumeDecodeUtf8Either
129resumeDecodeUtf8Either
132 :: (Monad m, IsStream t)
130 :: (Monad m, IsStream t)
@@ -137,6 +135,9 @@ resumeDecodeUtf8Either
137resumeDecodeUtf8Either st cp =
135resumeDecodeUtf8Either st cp =
138 D.fromStreamD . D.resumeDecodeUtf8Either st cp . D.toStreamD
136 D.fromStreamD . D.resumeDecodeUtf8Either st cp . D.toStreamD
139
137
138+-- |
139+--
140+-- /Internal/
140{-# INLINE decodeUtf8ArraysLenient #-}
141{-# INLINE decodeUtf8ArraysLenient #-}
141decodeUtf8ArraysLenient ::
142decodeUtf8ArraysLenient ::
142 (MonadIO m, IsStream t) => t m (Array Word8) -> t m Char
143 (MonadIO m, IsStream t) => t m (Array Word8) -> t m Char
@@ -144,27 +145,13 @@ decodeUtf8ArraysLenient =
144 D.fromStreamD . D.decodeUtf8ArraysLenient . D.toStreamD
145 D.fromStreamD . D.decodeUtf8ArraysLenient . D.toStreamD
145
146
146-- | Encode a stream of Unicode characters to a UTF-8 encoded bytestream.
147-- | Encode a stream of Unicode characters to a UTF-8 encoded bytestream.
148+--
149+-- /Since: 0.7.0/
147{-# INLINE encodeUtf8 #-}
150{-# INLINE encodeUtf8 #-}
148encodeUtf8 :: (Monad m, IsStream t) => t m Char -> t m Word8
151encodeUtf8 :: (Monad m, IsStream t) => t m Char -> t m Word8
149encodeUtf8 = D.fromStreamD . D.encodeUtf8 . D.toStreamD
152encodeUtf8 = D.fromStreamD . D.encodeUtf8 . D.toStreamD
150
153
151{-
154{-
152−-------------------------------------------------------------------------------
153−-- Unicode aware operations on strings
154−-------------------------------------------------------------------------------
155−
156−toCaseFold :: IsStream t => t m Char -> t m Char
157−toCaseFold = undefined
158−
159−toLower :: IsStream t => t m Char -> t m Char
160−toLower = undefined
161−
162−toUpper :: IsStream t => t m Char -> t m Char
163−toUpper = undefined
164−
165−toTitle :: IsStream t => t m Char -> t m Char
166−toTitle = undefined
167−
168-------------------------------------------------------------------------------
155-------------------------------------------------------------------------------
169-- Utility operations on strings
156-- Utility operations on strings
170-------------------------------------------------------------------------------
157-------------------------------------------------------------------------------
@@ -179,6 +166,8 @@ stripEnd = undefined
179-- | Remove leading whitespace from a string.
166-- | Remove leading whitespace from a string.
180--
167--
181-- > stripStart = S.dropWhile isSpace
168-- > stripStart = S.dropWhile isSpace
169+--
170+-- /Internal/
182{-# INLINE stripStart #-}
171{-# INLINE stripStart #-}
183stripStart :: (Monad m, IsStream t) => t m Char -> t m Char
172stripStart :: (Monad m, IsStream t) => t m Char -> t m Char
184stripStart = S.dropWhile isSpace
173stripStart = S.dropWhile isSpace
@@ -186,26 +175,15 @@ stripStart = S.dropWhile isSpace
186-- | Fold each line of the stream using the supplied 'Fold'
175-- | Fold each line of the stream using the supplied 'Fold'
187-- and stream the result.
176-- and stream the result.
188--
177--
189−-- >>> S.toList $ foldLines FL.toList (S.fromList "lines\nthis\nstring\n\n\n")
178+-- >>> S.toList $ lines FL.toList (S.fromList "lines\nthis\nstring\n\n\n")
190-- ["lines", "this", "string", "", ""]
179-- ["lines", "this", "string", "", ""]
191--
180--
192−-- > foldLines = S.splitOnSuffix (== '\n')
193−--
194−{-# INLINE foldLines #-}
195−foldLines :: (Monad m, IsStream t) => Fold m Char b -> t m Char -> t m b
196−foldLines = S.splitOnSuffix (== '\n')
181+-- > lines = S.splitOnSuffix (== '\n')
197−
198−-- | Fold each word of the stream using the supplied 'Fold'
199−-- and stream the result.
200−--
201−-- >>> S.toList $ foldWords FL.toList (S.fromList "fold these words")
202−-- ["fold", "these", "words"]
203−--
204−-- > foldWords = S.wordsBy isSpace
205--
182--
183+-- /Internal/
206−{-# INLINE foldWords #-}
184+{-# INLINE lines #-}
207−foldWords :: (Monad m, IsStream t) => Fold m Char b -> t m Char -> t m b
185+lines :: (Monad m, IsStream t) => Fold m Char b -> t m Char -> t m b
208−foldWords = S.wordsBy isSpace
186+lines = S.splitOnSuffix (== '\n')
209
187
210foreign import ccall unsafe "u_iswspace"
188foreign import ccall unsafe "u_iswspace"
211 iswspace :: Int -> Int
189 iswspace :: Int -> Int
@@ -219,79 +197,32 @@ isSpace c
219 where
197 where
220 uc = fromIntegral (ord c) :: Word
198 uc = fromIntegral (ord c) :: Word
221
199
222−-- | Break a string up into a stream of strings at newline characters.
200+-- | Fold each word of the stream using the supplied 'Fold'
223−-- The resulting strings do not contain newlines.
201+-- and stream the result.
224−--
225−-- > lines = foldLines A.write
226−--
227−-- >>> S.toList $ lines $ S.fromList "lines\nthis\nstring\n\n\n"
228−-- ["lines","this","string","",""]
229−--
230−-- If you're dealing with lines of massive length, consider using
231−-- 'foldLines' instead to avoid buffering the data in 'Array'.
232−{-# INLINE lines #-}
233−lines :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
234−lines = foldLines A.write
235−
236−-- | Break a string up into a stream of strings, which were delimited
237−-- by characters representing white space.
238--
202--
239−-- > words = foldWords A.write
203+-- >>> S.toList $ words FL.toList (S.fromList "fold these words")
204+-- ["fold", "these", "words"]
240--
205--
241−-- >>> S.toList $ words $ S.fromList "A newline\nis considered white space?"
206+-- > words = S.wordsBy isSpace
242−-- ["A", "newline", "is", "considered", "white", "space?"]
243--
207--
244−-- If you're dealing with words of massive length, consider using
208+-- /Internal/
245−-- 'foldWords' instead to avoid buffering the data in 'Array'.
246{-# INLINE words #-}
209{-# INLINE words #-}
247−words :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
210+words :: (Monad m, IsStream t) => Fold m Char b -> t m Char -> t m b
248−words = foldWords A.write
211+words = S.wordsBy isSpace
249
212
250-- | Unfold a stream to character streams using the supplied 'Unfold'
213-- | Unfold a stream to character streams using the supplied 'Unfold'
251-- and concat the results suffixing a newline character @\\n@ to each stream.
214-- and concat the results suffixing a newline character @\\n@ to each stream.
252--
215--
253−{-# INLINE unfoldLines #-}
216+-- /Internal/
254−unfoldLines :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
255−unfoldLines = S.interposeSuffix '\n'
256−
257−-- | Flattens the stream of @Array Char@, after appending a terminating
258−-- newline to each string.
259−--
260−-- 'unlines' is an inverse operation to 'lines'.
261−--
262−-- >>> S.toList $ unlines $ S.fromList ["lines", "this", "string"]
263−-- "lines\nthis\nstring\n"
264−--
265−-- > unlines = unfoldLines A.read
266−--
267−-- Note that, in general
268−--
269−-- > unlines . lines /= id
270{-# INLINE unlines #-}
217{-# INLINE unlines #-}
271−unlines :: (MonadIO m, IsStream t) => t m (Array Char) -> t m Char
218+unlines :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
272−unlines = unfoldLines A.read
219+unlines = S.interposeSuffix '\n'
273
220
274-- | Unfold the elements of a stream to character streams using the supplied
221-- | Unfold the elements of a stream to character streams using the supplied
275-- 'Unfold' and concat the results with a whitespace character infixed between
222-- 'Unfold' and concat the results with a whitespace character infixed between
276-- the streams.
223-- the streams.
277--
224--
278−{-# INLINE unfoldWords #-}
225+-- /Internal/
279−unfoldWords :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
280−unfoldWords = S.interpose ' '
281−
282−-- | Flattens the stream of @Array Char@, after appending a separating
283−-- space to each string.
284−--
285−-- 'unwords' is an inverse operation to 'words'.
286−--
287−-- >>> S.toList $ unwords $ S.fromList ["unwords", "this", "string"]
288−-- "unwords this string"
289−--
290−-- > unwords = unfoldWords A.read
291−--
292−-- Note that, in general
293−--
294−-- > unwords . words /= id
295{-# INLINE unwords #-}
226{-# INLINE unwords #-}
296−unwords :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
227+unwords :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
297−unwords = unfoldWords A.read
228+unwords = S.interpose ' '
src/Streamly/Internal/Memory/Unicode/Array.hsnew file+85
@@ -0,0 +1,85 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+-- |
3+-- Module : Streamly.Memory.Internal.Unicode.Array
4+-- Copyright : (c) 2018 Composewell Technologies
5+--
6+-- License : BSD3
7+-- Maintainer : streamly@composewell.com
8+-- Stability : experimental
9+-- Portability : GHC
10+--
11+module Streamly.Internal.Memory.Unicode.Array
12+ (
13+ -- * Streams of Strings
14+ lines
15+ , words
16+ , unlines
17+ , unwords
18+ )
19+where
20+
21+import Control.Monad.IO.Class (MonadIO)
22+import Streamly (IsStream, MonadAsync)
23+import Prelude hiding (String, lines, words, unlines, unwords)
24+import Streamly.Memory.Array (Array)
25+
26+import qualified Streamly.Internal.Data.Unicode.Stream as S
27+import qualified Streamly.Memory.Array as A
28+
29+-- | Break a string up into a stream of strings at newline characters.
30+-- The resulting strings do not contain newlines.
31+--
32+-- > lines = S.lines A.write
33+--
34+-- >>> S.toList $ lines $ S.fromList "lines\nthis\nstring\n\n\n"
35+-- ["lines","this","string","",""]
36+--
37+{-# INLINE lines #-}
38+lines :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
39+lines = S.lines A.write
40+
41+-- | Break a string up into a stream of strings, which were delimited
42+-- by characters representing white space.
43+--
44+-- > words = S.words A.write
45+--
46+-- >>> S.toList $ words $ S.fromList "A newline\nis considered white space?"
47+-- ["A", "newline", "is", "considered", "white", "space?"]
48+--
49+{-# INLINE words #-}
50+words :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
51+words = S.words A.write
52+
53+-- | Flattens the stream of @Array Char@, after appending a terminating
54+-- newline to each string.
55+--
56+-- 'unlines' is an inverse operation to 'lines'.
57+--
58+-- >>> S.toList $ unlines $ S.fromList ["lines", "this", "string"]
59+-- "lines\nthis\nstring\n"
60+--
61+-- > unlines = S.unlines A.read
62+--
63+-- Note that, in general
64+--
65+-- > unlines . lines /= id
66+{-# INLINE unlines #-}
67+unlines :: (MonadIO m, IsStream t) => t m (Array Char) -> t m Char
68+unlines = S.unlines A.read
69+
70+-- | Flattens the stream of @Array Char@, after appending a separating
71+-- space to each string.
72+--
73+-- 'unwords' is an inverse operation to 'words'.
74+--
75+-- >>> S.toList $ unwords $ S.fromList ["unwords", "this", "string"]
76+-- "unwords this string"
77+--
78+-- > unwords = S.unwords A.read
79+--
80+-- Note that, in general
81+--
82+-- > unwords . words /= id
83+{-# INLINE unwords #-}
84+unwords :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
85+unwords = S.unwords A.read
streamly.cabal+2
@@ -313,6 +313,8 @@ library
313 , Streamly.Internal.FileSystem.Handle
313 , Streamly.Internal.FileSystem.Handle
314 , Streamly.Internal.FileSystem.File
314 , Streamly.Internal.FileSystem.File
315 , Streamly.Internal.Data.Unicode.Stream
315 , Streamly.Internal.Data.Unicode.Stream
316+ , Streamly.Internal.Data.Unicode.Char
317+ , Streamly.Internal.Memory.Unicode.Array
316 , Streamly.Internal.Prelude
318 , Streamly.Internal.Prelude
317 if !impl(ghcjs)
319 if !impl(ghcjs)
318 exposed-modules:
320 exposed-modules:
test/String.hs+9−8
@@ -17,6 +17,7 @@ import qualified Streamly.Internal.Memory.ArrayStream as AS
17import qualified Streamly.Prelude as S
17import qualified Streamly.Prelude as S
18import qualified Streamly.Data.Unicode.Stream as SS
18import qualified Streamly.Data.Unicode.Stream as SS
19import qualified Streamly.Internal.Data.Unicode.Stream as IUS
19import qualified Streamly.Internal.Data.Unicode.Stream as IUS
20+import qualified Streamly.Internal.Memory.Unicode.Array as IUA
20
21
21-- Coverage build takes too long with default number of tests
22-- Coverage build takes too long with default number of tests
22{-
23{-
@@ -49,7 +50,7 @@ propDecodeEncodeIdLenient =
49 forAll genUnicode $ \list ->
50 forAll genUnicode $ \list ->
50 monadicIO $ do
51 monadicIO $ do
51 let wrds = SS.encodeUtf8 $ S.fromList list
52 let wrds = SS.encodeUtf8 $ S.fromList list
52− chrs <- S.toList $ SS.decodeUtf8Lenient wrds
53+ chrs <- S.toList $ SS.decodeUtf8Lax wrds
53 assert (chrs == list)
54 assert (chrs == list)
54
55
55propDecodeEncodeIdArrays :: Property
56propDecodeEncodeIdArrays :: Property
@@ -67,7 +68,7 @@ testLines =
67 monadicIO $ do
68 monadicIO $ do
68 xs <- S.toList
69 xs <- S.toList
69 $ S.map A.toList
70 $ S.map A.toList
70− $ IUS.lines
71+ $ IUA.lines
71 $ S.fromList list
72 $ S.fromList list
72 assert (xs == lines list)
73 assert (xs == lines list)
73
74
@@ -88,7 +89,7 @@ testWords =
88 monadicIO $ do
89 monadicIO $ do
89 xs <- S.toList
90 xs <- S.toList
90 $ S.map A.toList
91 $ S.map A.toList
91− $ IUS.words
92+ $ IUA.words
92 $ S.fromList list
93 $ S.fromList list
93 assert (xs == words list)
94 assert (xs == words list)
94
95
@@ -97,8 +98,8 @@ testUnlines =
97 forAll genUnicode $ \list ->
98 forAll genUnicode $ \list ->
98 monadicIO $ do
99 monadicIO $ do
99 xs <- S.toList
100 xs <- S.toList
100− $ IUS.unlines
101+ $ IUA.unlines
101− $ IUS.lines
102+ $ IUA.lines
102 $ S.fromList list
103 $ S.fromList list
103 assert (xs == unlines (lines list))
104 assert (xs == unlines (lines list))
104
105
@@ -108,8 +109,8 @@ testUnwords =
108 monadicIO $ do
109 monadicIO $ do
109 xs <- run
110 xs <- run
110 $ S.toList
111 $ S.toList
111− $ IUS.unwords
112+ $ IUA.unwords
112− $ IUS.words
113+ $ IUA.words
113 $ S.fromList list
114 $ S.fromList list
114 assert (xs == unwords (words list))
115 assert (xs == unwords (words list))
115
116
@@ -120,7 +121,7 @@ main = hspec
120 $ do
121 $ do
121 describe "UTF8 - Encoding / Decoding" $ do
122 describe "UTF8 - Encoding / Decoding" $ do
122 prop "decodeUtf8 . encodeUtf8 == id" $ propDecodeEncodeId
123 prop "decodeUtf8 . encodeUtf8 == id" $ propDecodeEncodeId
123− prop "decodeUtf8Lenient . encodeUtf8 == id" $ propDecodeEncodeIdLenient
124+ prop "decodeUtf8Lax . encodeUtf8 == id" $ propDecodeEncodeIdLenient
124 prop "decodeUtf8ArraysLenient . encodeUtf8 == id"
125 prop "decodeUtf8ArraysLenient . encodeUtf8 == id"
125 $ propDecodeEncodeIdArrays
126 $ propDecodeEncodeIdArrays
126 prop "Streamly.Data.String.lines == Prelude.lines" $ testLines
127 prop "Streamly.Data.String.lines == Prelude.lines" $ testLines