on master help sign in
Implement lazy length, and test it
1876b8cBryan O'Sullivan
Data/Text/Fusion.hs+13
@@ -39,6 +39,7 @@ module Data.Text.Fusion
39 , init
39 , init
40 , null
40 , null
41 , length
41 , length
42+ , length64
42
43
43 -- * Transformations
44 -- * Transformations
44 , map
45 , map
@@ -110,6 +111,7 @@ import qualified Data.List as L
110import GHC.Exts (Int(..), (+#))
111import GHC.Exts (Int(..), (+#))
111import Data.Bits ((.&.), shiftR)
112import Data.Bits ((.&.), shiftR)
112import Data.Char (ord)
113import Data.Char (ord)
114+import Data.Int (Int64)
113import Data.Text.Internal (Text(..))
115import Data.Text.Internal (Text(..))
114import Data.Text.UnsafeChar (unsafeChr, unsafeWrite)
116import Data.Text.UnsafeChar (unsafeChr, unsafeWrite)
115import qualified Data.Text.Array as A
117import qualified Data.Text.Array as A
@@ -313,6 +315,17 @@ length (Stream next s0 _len) = loop_length 0# s0
313 Yield _ s' -> loop_length (z# +# 1#) s'
315 Yield _ s' -> loop_length (z# +# 1#) s'
314{-# INLINE[0] length #-}
316{-# INLINE[0] length #-}
315
317
318+-- | /O(n)/ Returns the number of characters in a text.
319+length64 :: Stream Char -> Int64
320+length64 (Stream next s0 _len) = loop_length 0 s0
321+ where
322+ loop_length z s = case next s of
323+ Done -> z
324+ Skip s' -> loop_length z s'
325+ Yield _ s' -> let !z' = z + 1
326+ in loop_length z' s'
327+{-# INLINE[0] length64 #-}
328+
316-- ----------------------------------------------------------------------------
329-- ----------------------------------------------------------------------------
317-- * Stream transformations
330-- * Stream transformations
318
331
Data/Text/Lazy.hs+15−2
@@ -49,7 +49,7 @@ module Data.Text.Lazy
49 , tail
49 , tail
50 , init
50 , init
51 , null
51 , null
52− -- , length
52+ , length
53
53
54 -- * Transformations
54 -- * Transformations
55 -- , map
55 -- , map
@@ -150,8 +150,9 @@ import Prelude (Char, Bool(..), Functor(..), Int, Maybe(..), String,
150 Eq(..), Ord(..), (++),
150 Eq(..), Ord(..), (++),
151 Read(..), Show(..),
151 Read(..), Show(..),
152 (&&), (||), (+), (-), (.), ($),
152 (&&), (||), (+), (-), (.), ($),
153− not, return, otherwise)
153+ fromIntegral, not, return, otherwise)
154import qualified Prelude as P
154import qualified Prelude as P
155+import Data.Int (Int64)
155import Data.String (IsString(..))
156import Data.String (IsString(..))
156import qualified Data.Text as T
157import qualified Data.Text as T
157import qualified Data.Text.Fusion as S
158import qualified Data.Text.Fusion as S
@@ -295,6 +296,18 @@ null _ = False
295 S.null (stream t) = null t
296 S.null (stream t) = null t
296 #-}
297 #-}
297
298
299+length :: Text -> Int64
300+length = foldlChunks go 0
301+ where go l t = l + fromIntegral (T.length t)
302+{-# INLINE [1] length #-}
303+
304+{-# RULES
305+"LAZY TEXT length -> fused" [~1] forall t.
306+ length t = S.length64 (stream t)
307+"LAZY TEXT length -> unfused" [1] forall t.
308+ S.length64 (stream t) = length t
309+ #-}
310+
298-- | /O(1)/ Returns the last character of a 'Text', which must be
311-- | /O(1)/ Returns the last character of a 'Text', which must be
299-- non-empty. Subject to array fusion.
312-- non-empty. Subject to array fusion.
300last :: Text -> Char
313last :: Text -> Char
tests/Properties.hs+6
@@ -136,7 +136,10 @@ prop_TL_init = init `eqEP` (unpackT . TL.init)
136prop_S_null = null `eqP` S.null
136prop_S_null = null `eqP` S.null
137prop_T_null = null `eqP` T.null
137prop_T_null = null `eqP` T.null
138prop_TL_null = null `eqP` TL.null
138prop_TL_null = null `eqP` TL.null
139+prop_S_length = length `eqP` S.length
140+prop_S_length64 = length `eqP` (fromIntegral . S.length64)
139prop_T_length = length `eqP` T.length
141prop_T_length = length `eqP` T.length
142+prop_TL_length = length `eqP` (fromIntegral . TL.length)
140prop_T_map f = map f `eqP` (unpackT . T.map f)
143prop_T_map f = map f `eqP` (unpackT . T.map f)
141prop_T_intercalate c = L.intercalate c `eq` (unpackT . T.intercalate (T.pack c) . map T.pack)
144prop_T_intercalate c = L.intercalate c `eq` (unpackT . T.intercalate (T.pack c) . map T.pack)
142prop_T_intersperse c = L.intersperse c `eqP` (unpackT . T.intersperse c)
145prop_T_intersperse c = L.intersperse c `eqP` (unpackT . T.intersperse c)
@@ -317,7 +320,10 @@ tests = [
317 ("prop_S_null", mytest prop_S_null),
320 ("prop_S_null", mytest prop_S_null),
318 ("prop_T_null", mytest prop_T_null),
321 ("prop_T_null", mytest prop_T_null),
319 ("prop_TL_null", mytest prop_TL_null),
322 ("prop_TL_null", mytest prop_TL_null),
323+ ("prop_S_length", mytest prop_S_length),
324+ ("prop_S_length64", mytest prop_S_length64),
320 ("prop_T_length", mytest prop_T_length),
325 ("prop_T_length", mytest prop_T_length),
326+ ("prop_TL_length", mytest prop_TL_length),
321
327
322 ("prop_T_map", mytest prop_T_map),
328 ("prop_T_map", mytest prop_T_map),
323 ("prop_T_intercalate", mytest prop_T_intercalate),
329 ("prop_T_intercalate", mytest prop_T_intercalate),