on master help sign in
Eliminate dependency on the array package

--HG-- extra : convert_revision : 1e1c2e7ca040f4eb6749385ad6d5e630c3a63dbc

commita986b8cb34a2dbeae4f55b62c27383ab56040d2c authorBryan O'Sullivan <bos@serpentine.com> dateMon 26 Jan 2009, 09:13 (18 years ago) parentcf427a5
7 files changed+286−61
Data/Text.hs+12−13 Data/Text/Array.hs+237 Data/Text/Fusion.hs+19−21 Data/Text/Internal.hs+5−6 tests/Bench.hs+7−17 tests/FusionBench.hs+3−1 text.cabal+3−3
Data/Text.hs+12−13
@@ -112,8 +112,7 @@ import Prelude (Char,Bool,Int,Maybe,String,
112112 IO, FilePath)
113113import Data.Char (isSpace)
114114import Control.Monad.ST(ST)
115−import Data.Array.Base(unsafeNewArray_,unsafeWrite,unsafeAt)
116−import Data.Array.ST(STUArray, runSTUArray)
115+import qualified Data.Text.Array as A
117116import qualified Data.ByteString as B
118117import Data.ByteString(ByteString)
119118import qualified Data.List as L
@@ -218,18 +217,18 @@ snoc t c = unstream (S.snoc (stream t) c)
218217-- | /O(n)/ Appends one 'Text' to the other by copying both of them
219218-- into a new 'Text'. Subject to array fusion.
220219append :: Text -> Text -> Text
221−append (Text arr1 off1 len1) (Text arr2 off2 len2) = Text (runSTUArray x) 0 len
220+append (Text arr1 off1 len1) (Text arr2 off2 len2) = Text (A.run x) 0 len
222221 where
223222 len = len1+len2
224223 x = do
225− arr <- unsafeNewArray_ (0,len-1) :: ST s (STUArray s Int Word16)
224+ arr <- A.unsafeNew len :: ST s (A.MArray s Word16)
226225 copy arr1 off1 (len1+off1) arr 0
227226 copy arr2 off2 (len2+off2) arr len1
228227 return arr
229228 where
230229 copy arr i max arr' j
231230 | i >= max = return ()
232− | otherwise = do unsafeWrite arr' j (arr `unsafeAt` i)
231+ | otherwise = do A.unsafeWrite arr' j (arr `A.unsafeIndex` i)
233232 copy arr (i+1) max arr' (j+1)
234233{-# INLINE append #-}
235234
@@ -254,8 +253,8 @@ last (Text arr off len)
254253 | n < 0xDC00 || n > 0xDFFF = unsafeChr n
255254 | otherwise = U16.chr2 n0 n
256255 where
257− n = unsafeAt arr (off+len-1)
258− n0 = unsafeAt arr (off+len-2)
256+ n = A.unsafeIndex arr (off+len-1)
257+ n0 = A.unsafeIndex arr (off+len-2)
259258{-# INLINE [1] last #-}
260259
261260{-# RULES
@@ -274,7 +273,7 @@ tail (Text arr off len)
274273 | n >= 0xD800 && n <= 0xDBFF = Text arr (off+2) (len-2)
275274 | otherwise = Text arr (off+1) (len-1)
276275 where
277− n = unsafeAt arr off
276+ n = A.unsafeIndex arr off
278277{-# INLINE [1] tail #-}
279278
280279
@@ -286,7 +285,7 @@ init (Text arr off len) | len <= 0 = errorEmptyList "init"
286285 | n >= 0xDC00 && n <= 0xDFFF = Text arr off (len-2)
287286 | otherwise = Text arr off (len-1)
288287 where
289− n = unsafeAt arr (off+len-1)
288+ n = A.unsafeIndex arr (off+len-1)
290289{-# INLINE [1] init #-}
291290
292291{-# RULES
@@ -305,7 +304,7 @@ null t = S.null (stream t)
305304-- | /O(n)/ Returns the number of characters in a 'Text'.
306305-- Subject to array fusion.
307306length :: Text -> Int
308−length length t = S.length (stream t)
307+length t = S.length (stream t)
309308{-# INLINE length #-}
310309
311310-- -----------------------------------------------------------------------------
@@ -458,7 +457,7 @@ take n t@(Text arr off len)
458457 | c < 0xD800 || c > 0xDBFF = loop (i+1) (count+1)
459458 | otherwise = loop (i+2) (count+1)
460459 where
461− c = arr `unsafeAt` i
460+ c = arr `A.unsafeIndex` i
462461{-# INLINE [1] take #-}
463462
464463{-# RULES
@@ -484,7 +483,7 @@ drop n t@(Text arr off len)
484483 | c < 0xD800 || c > 0xDBFF = loop (i+1) (count+1) (l-1)
485484 | otherwise = loop (i+2) (count+1) (l-2)
486485 where
487− c = arr `unsafeAt` i
486+ c = arr `A.unsafeIndex` i
488487{-# INLINE [1] drop #-}
489488
490489{-# RULES
@@ -580,7 +579,7 @@ words (Text arr off len) = loop0 off off
580579 then []
581580 else [(Text arr start (n-start))]
582581 where
583− c = arr `unsafeAt` n
582+ c = arr `A.unsafeIndex` n
584583{-# INLINE words #-}
585584
586585errorEmptyList :: String -> a
Data/Text/Array.hsnew file+237
@@ -0,0 +1,237 @@
1+{-# LANGUAGE CPP, ExistentialQuantification, MagicHash, Rank2Types,
2+ ScopedTypeVariables, UnboxedTuples #-}
3+-- |
4+-- Module : Data.Text.Array
5+-- Copyright : (c) Bryan O'Sullivan 2009
6+--
7+-- License : BSD-style
8+-- Maintainer : bos@serpentine.com,
9+-- Stability : experimental
10+-- Portability : portable
11+--
12+-- Packed, unboxed, heap-resident arrays. Suitable for performance
13+-- critical use, both in terms of large data quantities and high
14+-- speed.
15+--
16+-- This module is intended to be imported @qualified@, to avoid name
17+-- clashes with "Prelude" functions, e.g.
18+--
19+-- > import qualified Data.Text.Array as A
20+--
21+-- The names in this module resemble those in the 'Data.Array' family
22+-- of modules, but are shorter due to the assumption of qualifid
23+-- naming.
24+module Data.Text.Array
25+ (
26+ -- * Types
27+ IArray(..)
28+ , Elt(..)
29+ , Array
30+ , MArray
31+
32+ -- * Functions
33+ , empty
34+ , new
35+ , unsafeNew
36+ , unsafeFreeze
37+ , run
38+ ) where
39+
40+#if defined(__GLASGOW_HASKELL__)
41+#include "MachDeps.h"
42+
43+import GHC.Base (ByteArray#, MutableByteArray#, indexWord16Array#,
44+ newByteArray#, readWord16Array#, unsafeCoerce#,
45+ writeWord16Array#, (+#), (*#))
46+import GHC.Prim (Int#)
47+import GHC.ST (ST(..), runST)
48+import GHC.Types (Int(..))
49+import GHC.Word (Word16(..))
50+
51+#elif defined(__HUGS__)
52+
53+import Hugs.ByteArray (ByteArray, MutableByteArray, readByteArray,
54+ newMutableByteArray, readMutableByteArray,
55+ unsafeFreezeMutableByteArray, writeMutableByteArray)
56+import Foreign.Storable (Storable, sizeOf)
57+import Hugs.ST (ST(..), runST)
58+
59+#else
60+# error not implemented for this compiler
61+#endif
62+
63+import Data.Typeable
64+import Data.Word (Word16)
65+import Prelude hiding (length, read)
66+
67+#include "Typeable.h"
68+
69+-- | Immutable array type.
70+data Array e = Array
71+ {-# UNPACK #-} !Int -- length (in units of e, not bytes)
72+#if defined(__GLASGOW_HASKELL__)
73+ ByteArray#
74+#elif defined(__HUGS__)
75+ !ByteArray
76+#endif
77+
78+INSTANCE_TYPEABLE1(Array,arrayTc,"Array")
79+
80+-- | Mutable array type, for use in the ST monad.
81+data MArray s e = MArray
82+ {-# UNPACK #-} !Int -- length (in units of e, not bytes)
83+#if defined(__GLASGOW_HASKELL__)
84+ (MutableByteArray# s)
85+#elif defined(__HUGS__)
86+ !(MutableByteArray s)
87+#endif
88+
89+INSTANCE_TYPEABLE2(MArray,mArrayTc,"MArray")
90+
91+-- | Operations supported by all arrays.
92+class IArray a where
93+ -- | Return the length of an array.
94+ length :: a -> Int
95+
96+instance IArray (Array e) where
97+ length (Array len _ba) = len
98+ {-# INLINE length #-}
99+
100+instance IArray (MArray s e) where
101+ length (MArray len _ba) = len
102+ {-# INLINE length #-}
103+
104+check :: IArray a => String -> a -> Int -> (a -> Int -> b) -> b
105+check func ary i f
106+ | i >= 0 && i < length ary = f ary i
107+ | otherwise = error ("Data.Array.Flat." ++ func ++ ": index out of bounds")
108+{-# INLINE check #-}
109+
110+-- | Operations supported by all elements that can be stored in
111+-- arrays.
112+class Elt e where
113+ -- | Indicate how many bytes would be used for an array of the
114+ -- given size.
115+ bytesInArray :: Int -> e -> Int
116+ -- | Unchecked read of an immutable array. May return garbage or
117+ -- crash on an out-of-bounds access.
118+ unsafeIndex :: Array e -> Int -> e
119+ -- | Unchecked read of a mutable array. May return garbage or
120+ -- crash on an out-of-bounds access.
121+ unsafeRead :: MArray s e -> Int -> ST s e
122+ -- | Unchecked write of a mutable array. May return garbage or
123+ -- crash on an out-of-bounds access.
124+ unsafeWrite :: MArray s e -> Int -> e -> ST s ()
125+
126+ -- | Read an immutable array. An invalid index results in a
127+ -- runtime error.
128+ index :: Array e -> Int -> e
129+ index ary i = check "index" ary i unsafeIndex
130+ {-# INLINE index #-}
131+
132+ -- | Read a mutable array. An invalid index results in a runtime
133+ -- error.
134+ read :: Array e -> Int -> ST s e
135+ read ary i = check "read" ary i read
136+ {-# INLINE read #-}
137+
138+ -- | Write a mutable array. An invalid index results in a runtime
139+ -- error.
140+ write :: Array e -> Int -> ST s e
141+ write ary i = check "write" ary i write
142+ {-# INLINE write #-}
143+
144+-- | Freeze a mutable array. Do not mutate the 'MArray' afterwards!
145+unsafeFreeze :: MArray s e -> ST s (Array e)
146+
147+#if defined(__GLASGOW_HASKELL__)
148+
149+wORD16_SCALE :: Int# -> Int#
150+wORD16_SCALE n# = scale# *# n# where I# scale# = SIZEOF_WORD16
151+
152+-- | Create an uninitialized mutable array.
153+unsafeNew :: forall s e. Elt e => Int -> ST s (MArray s e)
154+unsafeNew n = ST $ \s1# ->
155+ case bytesInArray n (undefined :: e) of
156+ (I# len#) -> case newByteArray# len# s1# of
157+ (# s2#, marr# #) -> (# s2#, MArray n marr# #)
158+{-# INLINE unsafeNew #-}
159+
160+unsafeFreeze (MArray len mba#) = ST $ \s# ->
161+ (# s#, Array len (unsafeCoerce# mba#) #)
162+{-# INLINE unsafeFreeze #-}
163+
164+-- | Create a mutable array, with its elements initialized with the
165+-- given value.
166+new :: forall s e. Elt e => Int -> e -> ST s (MArray s e)
167+
168+#elif defined(__HUGS__)
169+
170+unsafeIndexArray :: Storable e => Array e -> Int -> e
171+unsafeIndexArray (Array off _len arr) i = readByteArray arr (off + i)
172+
173+unsafeReadMArray :: Storable e => MArray s e -> Int -> ST s e
174+unsafeReadMArray (MArray _len marr) = readMutableByteArray marr
175+
176+unsafeWriteMArray :: Storable e => MArray s e -> Int -> e -> ST s ()
177+unsafeWriteMArray (MArray _len marr) = writeMutableByteArray marr
178+
179+-- | Create an uninitialized mutable array.
180+unsafeNew :: (Storable e) => Int -> ST s (MArray s e)
181+unsafeNew n = new undefined
182+ where new :: (Storable e) => e -> ST s (MArray s e)
183+ new unused = do
184+ marr <- newMutableByteArray (n * sizeOf unused)
185+ return (MArray n marr)
186+
187+unsafeFreeze (MArray len mba) = do
188+ ba <- unsafeFreezeMutableByteArray mba
189+ return (Array 0 len ba)
190+
191+-- | Create a mutable array, with its elements initialized with the
192+-- given value.
193+new :: (Storable e) => Int -> e -> ST s (MArray s e)
194+#endif
195+
196+new len initVal = do
197+ marr <- unsafeNew len
198+ sequence_ [unsafeWrite marr i initVal | i <- [0..len-1]]
199+ return marr
200+
201+instance Elt Word16 where
202+#if defined(__GLASGOW_HASKELL__)
203+
204+ bytesInArray (I# i#) _ = I# (wORD16_SCALE i#)
205+ {-# INLINE bytesInArray #-}
206+
207+ unsafeIndex (Array _len ba#) (I# i#) =
208+ case indexWord16Array# ba# i# of r# -> (W16# r#)
209+ {-# INLINE unsafeIndex #-}
210+
211+ unsafeRead (MArray _len mba#) (I# i#) = ST $ \s# ->
212+ case readWord16Array# mba# i# s# of
213+ (# s2#, r# #) -> (# s2#, W16# r# #)
214+ {-# INLINE unsafeRead #-}
215+
216+ unsafeWrite (MArray _len marr#) (I# i#) (W16# e#) = ST $ \s1# ->
217+ case writeWord16Array# marr# i# e# s1# of
218+ s2# -> (# s2#, () #)
219+ {-# INLINE unsafeWrite #-}
220+
221+#elif defined(__HUGS__)
222+
223+ bytesInArray n w = sizeOf w * n
224+ unsafeIndex = unsafeIndexArray
225+ unsafeRead = unsafeReadMArray
226+ unsafeWrite = unsafeWriteMArray
227+
228+#endif
229+
230+-- | An empty immutable array.
231+empty :: Elt e => Array e
232+empty = runST (unsafeNew 0 >>= unsafeFreeze)
233+
234+-- | Run an action in the ST monad and return an immutable array of
235+-- its result.
236+run :: Elt e => (forall s. ST s (MArray s e)) -> Array e
237+run k = runST (k >>= unsafeFreeze)
Data/Text/Fusion.hs+19−21
@@ -57,7 +57,6 @@ import Data.Char (ord)
5757import Control.Exception(assert)
5858import Control.Monad(liftM2)
5959import Control.Monad.ST(runST,ST)
60−import Data.Array.Base
6160import Data.Bits (shiftL, shiftR, (.&.))
6261import qualified Data.ByteString as B
6362import qualified Data.ByteString.Unsafe as B
@@ -70,6 +69,7 @@ import GHC.Exts (Int(..), (+#))
7069import System.IO.Unsafe(unsafePerformIO)
7170import Data.Text.Internal(Text(..),empty)
7271import Data.Text.UnsafeChar(unsafeChr,unsafeChr8,unsafeChr32)
72+import qualified Data.Text.Array as A
7373import qualified Data.Text.Utf8 as U8
7474import qualified Data.Text.Utf16 as U16
7575import qualified Data.Text.Utf32 as U32
@@ -103,32 +103,32 @@ stream (Text arr off len) = Stream next off len
103103 | n >= 0xD800 && n <= 0xDBFF = Yield (U16.chr2 n n2) (i + 2)
104104 | otherwise = Yield (unsafeChr n) (i + 1)
105105 where
106− n = unsafeAt arr i
107− n2 = unsafeAt arr (i + 1)
106+ n = A.unsafeIndex arr i
107+ n2 = A.unsafeIndex arr (i + 1)
108108{-# INLINE [0] stream #-}
109109
110110-- | /O(n)/ Convert a Stream Char into a Text.
111111unstream :: Stream Char -> Text
112112unstream (Stream next0 s0 len) = Text (fst a) 0 (snd a)
113113 where
114− a :: ((UArray Int Word16),Int)
115− a = runST ((unsafeNewArray_ (0,len+1) :: ST s (STUArray s Int Word16))
116− >>= (\arr -> loop arr 0 (len+1) s0))
114+ a :: ((A.Array Word16),Int)
115+ a = runST ((A.unsafeNew len :: ST s (A.MArray s Word16))
116+ >>= (\arr -> loop arr 0 len s0))
117117 loop arr !i !top !s
118− | i + 1 > top = do arr' <- unsafeNewArray_ (0,top*2)
118+ | i + 1 > top = do arr' <- A.unsafeNew (top*2)
119119 case next0 s of
120− Done -> liftM2 (,) (unsafeFreezeSTUArray arr) (return i)
120+ Done -> liftM2 (,) (A.unsafeFreeze arr) (return i)
121121 _ -> copy arr arr' >> loop arr' i (top*2) s
122122 | otherwise = case next0 s of
123− Done -> liftM2 (,) (unsafeFreezeSTUArray arr) (return i)
123+ Done -> liftM2 (,) (A.unsafeFreeze arr) (return i)
124124 Skip s' -> loop arr i top s'
125125 Yield x s'
126126 | n < 0x10000 -> do
127− unsafeWrite arr i (fromIntegral n :: Word16)
127+ A.unsafeWrite arr i (fromIntegral n :: Word16)
128128 loop arr (i+1) top s'
129129 | otherwise -> do
130− unsafeWrite arr i l
131− unsafeWrite arr (i + 1) r
130+ A.unsafeWrite arr i l
131+ A.unsafeWrite arr (i + 1) r
132132 loop arr (i+2) top s'
133133 where
134134 n :: Int
@@ -142,16 +142,14 @@ unstream (Stream next0 s0 len) = Text (fst a) 0 (snd a)
142142{-# INLINE [0] unstream #-}
143143
144144
145−copy :: STUArray s Int Word16 -> STUArray s Int Word16 -> ST s ()
146−copy src dest = (do
147− (_,top) <- getBounds src
148− copy_loop 0 top)
145+copy :: A.MArray s Word16 -> A.MArray s Word16 -> ST s ()
146+copy src dest = copy_loop 0
149147 where
150− copy_loop i top
151− | i > top = return ()
152− | otherwise = do v <- unsafeRead src i
153− unsafeWrite dest i v
154− copy_loop (i+1) top
148+ len = A.length src
149+ copy_loop i
150+ | i > len = return ()
151+ | otherwise = do A.unsafeRead src i >>= A.unsafeWrite dest i
152+ copy_loop (i+1)
155153
156154-- | /O(n)/ Determines if two streams are equal.
157155eq :: Ord a => Stream a -> Stream a -> Bool
Data/Text/Internal.hs+5−6
@@ -23,18 +23,17 @@ module Data.Text.Internal
2323 , empty
2424 ) where
2525
26−import Data.Array.ST (newArray_,runSTUArray)
27−import Data.Array.Unboxed (UArray)
26+import qualified Data.Text.Array as A
2827import Data.Typeable (Typeable)
2928import Data.Word (Word16)
3029
3130-- | A space efficient, packed, unboxed Unicode text type.
32−data Text = Text {-# UNPACK #-} !(UArray Int Word16) -- payload
33− {-# UNPACK #-} !Int -- offset
34− {-# UNPACK #-} !Int -- length
31+data Text = Text {-# UNPACK #-} !(A.Array Word16) -- payload
32+ {-# UNPACK #-} !Int -- offset
33+ {-# UNPACK #-} !Int -- length
3534 deriving (Typeable)
3635
3736-- | /O(1)/ The empty 'Text'.
3837empty :: Text
39−empty = Text (runSTUArray (newArray_ (0,0))) 0 0
38+empty = Text A.empty 0 0
4039{-# INLINE [1] empty #-}
tests/Bench.hs+7−17
@@ -17,36 +17,25 @@ import qualified Data.Text.Fusion as S
1717import Data.Text.Fusion (Encoding(..))
1818
1919import qualified Data.List as L
20−import qualified Data.ByteString as B
20+import qualified Data.ByteString.Char8 as B
21+import qualified Data.ByteString as B8
2122import Data.ByteString (ByteString)
2223import Data.Word
2324import qualified System.IO.UTF8 as UTF8
2425
25−main = do ascii_bs <- B.readFile "ascii.txt"
26+main = do ascii_bs <- B.readFile "text/test/ascii.txt"
2627 let ascii_txt = T.decode ASCII ascii_bs
2728 let ascii_str = T.unpack ascii_txt
2829 force (ascii_txt,ascii_str,ascii_bs)
2930 printf " # Text\t\tString\tByteString\n"
3031 run 1 (ascii_txt,ascii_str,ascii_bs) ascii_tests
31− performGC
32− bmp_txt <- T.readFile Utf8 "bmp.txt"
33− let bmp_str = T.unpack bmp_txt
34− force (bmp_txt,bmp_str)
35− printf " # Text\t\tString\t\n"
36− run 1 (bmp_txt, bmp_str, B.empty) bmp_tests
37− performGC
38− smp_sip_txt <- T.readFile Utf8 "smp_sip.txt"
39− let smp_sip_str = T.unpack smp_sip_txt
40− force (smp_sip_txt, smp_sip_str)
41− printf " # Text\t\tString\t\n"
42− run 1 (smp_sip_txt, smp_sip_str,B.empty) smp_sip_tests
4332
4433
4534ascii_tests = [
4635 ("cons",
4736 [F (app1 (T.cons '\88')),
4837 F (app2 ((:) '\88') ),
49− F (app3 (B.cons 88) )]),
38+ F (app3 (B8.cons 88) )]),
5039 ("head",
5140 [F (app1 T.head),
5241 F (app2 L.head),
@@ -78,7 +67,7 @@ ascii_tests = [
7867 ("filter",
7968 [F $ app1 $ T.filter (/= '\101'),
8069 Flist $ app2 $ L.filter (/= '\101'),
81− F $ app3 $ B.filter (/= 101)]),
70+ F $ app3 $ B8.filter (/= 101)]),
8271 ("foldl'",
8372 [F (app1 $ T.foldl' (\a w -> a+1::Int) 0),
8473 F (app2 $ L.foldl' (\a w -> a+1::Int) 0),
@@ -95,7 +84,8 @@ ascii_tests = [
9584 F (app3 $ B.take 30000000)]),
9685 ("words",
9786 [F (app1 $ T.words),
98− Flist (app2 $ L.words)])
87+ Flist (app2 $ L.words),
88+ F (app3 $ B.words)])
9989 ]
10090
10191bmp_tests = [
tests/FusionBench.hs+3−1
@@ -1,4 +1,4 @@
1−import Prelude hiding (zip,zip3,fst,snd)
1+import Prelude hiding (zip,zip3)
22
33import BenchUtils
44import Data.Char
@@ -19,6 +19,8 @@ main = do ascii_str <- readFile "ascii.txt"
1919 printf " # Text\t\tString\tByteString\n"
2020 run 1 (ascii_txt,ascii_str,ascii_bs) ascii_tests
2121
22+trd (a,b,c) = c
23+
2224ascii_tests = [
2325 ("map/map",
2426 [F $ T.map pred . T.map succ . fst,
text.cabal+3−3
@@ -17,6 +17,7 @@ cabal-version: >= 1.2
1717library
1818 exposed-modules:
1919 Data.Text
20+ Data.Text.Array
2021 Data.Text.UnsafeChar
2122 Data.Text.Internal
2223 Data.Text.Fusion
@@ -25,12 +26,11 @@ library
2526 Data.Text.Utf16
2627
2728 build-depends:
28− base >= 3 && < 4,
29− array >= 0.1 && < 0.3,
29+ base < 5,
3030 bytestring >= 0.9 && < 1.0
3131 if impl(ghc >= 6.10)
3232 build-depends:
33− ghc-prim
33+ ghc-prim, base >= 4
3434
3535 -- gather extensive profiling data for now
3636 ghc-prof-options: -auto-all