on master help sign in
Reduce the amount of code generated for string literals

Previously, every Text literal generated a big wad of inefficient code: {-# LANGUAGE OverloadedStrings #-} foo :: Text foo = "foo" This would first convert to a String, then to a Text. To make matters worse, the code for conversion from String to Text was inlined at every site where a string literal occurred (expected, but undesired, behaviour). In this change, we introduce a direct conversion from Addr# to each of the Text types, and we ensure that uses of these never result in excessive code generation.

commit2b2cb084c4689c06f1a7851ff8eb1e412eb02c1b authorBryan O'Sullivan <bos@serpentine.com> dateSat 7 Apr 2012, 21:33 (15 years ago) parent0d6d979
3 files changed+69−6
Data/Text.hs+17−2 Data/Text/Fusion/Common.hs+35−2 Data/Text/Lazy.hs+17−2
Data/Text.hs+17−2
@@ -1,9 +1,9 @@
1−{-# LANGUAGE BangPatterns, CPP, Rank2Types, UnboxedTuples #-}
1+{-# LANGUAGE BangPatterns, CPP, MagicHash, Rank2Types, UnboxedTuples #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33
44-- |
55-- Module : Data.Text
6−-- Copyright : (c) 2009, 2010, 2011 Bryan O'Sullivan,
6+-- Copyright : (c) 2009, 2010, 2011, 2012 Bryan O'Sullivan,
77-- (c) 2009 Duncan Coutts,
88-- (c) 2008, 2009 Tom Harper
99--
@@ -222,6 +222,8 @@ import Data.ByteString (ByteString)
222222import qualified Data.Text.Lazy as L
223223import Data.Int (Int64)
224224#endif
225+import qualified GHC.CString as GHC
226+import GHC.Prim (Addr#)
225227
226228-- $strict
227229--
@@ -372,6 +374,19 @@ unpack :: Text -> String
372374unpack = S.unstreamList . stream
373375{-# INLINE [1] unpack #-}
374376
377+-- | /O(n)/ Convert a literal string into a Text.
378+unpackCString# :: Addr# -> Text
379+unpackCString# addr# = unstream (S.streamCString# addr#)
380+{-# NOINLINE unpackCString# #-}
381+
382+{-# RULES "TEXT literal" forall a.
383+ unstream (S.streamList (L.map safe (GHC.unpackCString# a)))
384+ = unpackCString# a #-}
385+
386+{-# RULES "TEXT literal UTF8" forall a.
387+ unstream (S.streamList (L.map safe (GHC.unpackCStringUtf8# a)))
388+ = unpackCString# a #-}
389+
375390-- | /O(1)/ Convert a character into a Text. Subject to fusion.
376391-- Performs replacement on invalid scalar values.
377392singleton :: Char -> Text
Data/Text/Fusion/Common.hs+35−2
@@ -1,7 +1,7 @@
1−{-# LANGUAGE BangPatterns, Rank2Types #-}
1+{-# LANGUAGE BangPatterns, MagicHash, Rank2Types #-}
22-- |
33-- Module : Data.Text.Fusion.Common
4−-- Copyright : (c) Bryan O'Sullivan 2009
4+-- Copyright : (c) Bryan O'Sullivan 2009, 2012
55--
66-- License : BSD-style
77-- Maintainer : bos@serpentine.com, rtomharper@googlemail.com,
@@ -17,6 +17,7 @@ module Data.Text.Fusion.Common
1717 singleton
1818 , streamList
1919 , unstreamList
20+ , streamCString#
2021
2122 -- * Basic interface
2223 , cons
@@ -104,10 +105,13 @@ import Prelude (Bool(..), Char, Eq(..), Int, Integral, Maybe(..),
104105 (&&), fromIntegral, otherwise)
105106import qualified Data.List as L
106107import qualified Prelude as P
108+import Data.Bits (shiftL)
107109import Data.Int (Int64)
108110import Data.Text.Fusion.Internal
109111import Data.Text.Fusion.CaseMapping (foldMapping, lowerMapping, upperMapping)
110112import Data.Text.Fusion.Size
113+import GHC.Prim (Addr#, chr#, indexCharOffAddr#, ord#)
114+import GHC.Types (Char(..), Int(..))
111115
112116singleton :: Char -> Stream Char
113117singleton c = Stream next False 1
@@ -131,6 +135,35 @@ unstreamList (Stream next s0 _len) = unfold s0
131135
132136{-# RULES "STREAM streamList/unstreamList fusion" forall s. streamList (unstreamList s) = s #-}
133137
138+-- | Stream the UTF-8-like packed encoding used by GHC to represent
139+-- constant strings in generated code.
140+--
141+-- This encoding uses the byte sequence "\xc0\x80" to represent NUL,
142+-- and the string is NUL-terminated.
143+streamCString# :: Addr# -> Stream Char
144+streamCString# addr = Stream step 0 unknownSize
145+ where
146+ step !i
147+ | b == 0 = Done
148+ | b <= 0x7f = Yield (C# b#) (i+1)
149+ | b <= 0xdf = let !c = chr $ ((b-0xc0) `shiftL` 6) + next 1
150+ in Yield c (i+2)
151+ | b <= 0xef = let !c = chr $ ((b-0xe0) `shiftL` 12) +
152+ (next 1 `shiftL` 6) +
153+ next 2
154+ in Yield c (i+3)
155+ | otherwise = let !c = chr $ ((b-0xf0) `shiftL` 18) +
156+ (next 1 `shiftL` 12) +
157+ (next 2 `shiftL` 6) +
158+ next 3
159+ in Yield c (i+4)
160+ where b = I# (ord# b#)
161+ next n = I# (ord# (at# (i+n))) - 0x80
162+ b# = at# i
163+ at# (I# i#) = indexCharOffAddr# addr i#
164+ chr (I# i#) = C# (chr# i#)
165+{-# INLINE [0] streamCString# #-}
166+
134167-- ----------------------------------------------------------------------------
135168-- * Basic stream functions
136169
Data/Text/Lazy.hs+17−2
@@ -1,8 +1,8 @@
11{-# OPTIONS_GHC -fno-warn-orphans #-}
2−{-# LANGUAGE BangPatterns, CPP #-}
2+{-# LANGUAGE BangPatterns, MagicHash, CPP #-}
33-- |
44-- Module : Data.Text.Lazy
5−-- Copyright : (c) 2009, 2010 Bryan O'Sullivan
5+-- Copyright : (c) 2009, 2010, 2012 Bryan O'Sullivan
66--
77-- License : BSD-style
88-- Maintainer : bos@serpentine.com, rtomharper@googlemail.com,
@@ -217,6 +217,8 @@ import Data.Text.Lazy.Internal (Text(..), chunk, empty, foldlChunks, foldrChunks
217217import Data.Text.Internal (firstf, safe, textP)
218218import qualified Data.Text.Util as U
219219import Data.Text.Lazy.Search (indices)
220+import qualified GHC.CString as GHC
221+import GHC.Prim (Addr#)
220222
221223-- $fusion
222224--
@@ -351,6 +353,19 @@ unpack :: Text -> String
351353unpack t = S.unstreamList (stream t)
352354{-# INLINE [1] unpack #-}
353355
356+-- | /O(n)/ Convert a literal string into a Text.
357+unpackCString# :: Addr# -> Text
358+unpackCString# addr# = unstream (S.streamCString# addr#)
359+{-# NOINLINE unpackCString# #-}
360+
361+{-# RULES "TEXT literal" forall a.
362+ unstream (S.streamList (L.map safe (GHC.unpackCString# a)))
363+ = unpackCString# a #-}
364+
365+{-# RULES "TEXT literal UTF8" forall a.
366+ unstream (S.streamList (L.map safe (GHC.unpackCStringUtf8# a)))
367+ = unpackCString# a #-}
368+
354369-- | /O(1)/ Convert a character into a Text. Subject to fusion.
355370-- Performs replacement on invalid scalar values.
356371singleton :: Char -> Text