on be95661 help sign in
text/Data/Text/Array.hs
238 lines 7.7 kB history raw
1{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface, MagicHash, Rank2Types,
2 RecordWildCards, UnboxedTuples, UnliftedFFITypes #-}
3{-# OPTIONS_GHC -fno-warn-unused-matches #-}
4-- |
5-- Module : Data.Text.Array
6-- Copyright : (c) 2009, 2010, 2011 Bryan O'Sullivan
7--
8-- License : BSD-style
9-- Maintainer : bos@serpentine.com, rtomharper@googlemail.com,
10-- duncan@haskell.org
11-- Stability : experimental
12-- Portability : portable
13--
14-- Packed, unboxed, heap-resident arrays. Suitable for performance
15-- critical use, both in terms of large data quantities and high
16-- speed.
17--
18-- This module is intended to be imported @qualified@, to avoid name
19-- clashes with "Prelude" functions, e.g.
20--
21-- > import qualified Data.Text.Array as A
22--
23-- The names in this module resemble those in the 'Data.Array' family
24-- of modules, but are shorter due to the assumption of qualifid
25-- naming.
26module Data.Text.Array
27 (
28 -- * Types
29 Array(aBA)
30 , MArray(maBA)
32 -- * Functions
33 , copyM
34 , copyI
35 , empty
36 , equal
37#if defined(ASSERTS)
38 , length
39#endif
40 , run
41 , run2
42 , toList
43 , unsafeFreeze
44 , unsafeIndex
45 , new
46 , unsafeWrite
47 ) where
49#if defined(ASSERTS)
50-- This fugly hack is brought by GHC's apparent reluctance to deal
51-- with MagicHash and UnboxedTuples when inferring types. Eek!
52# define CHECK_BOUNDS(_func_,_len_,_k_) \
53if (_k_) < 0 || (_k_) >= (_len_) then error ("Data.Text.Array." ++ (_func_) ++ ": bounds error, offset " ++ show (_k_) ++ ", length " ++ show (_len_)) else
54#else
55# define CHECK_BOUNDS(_func_,_len_,_k_)
56#endif
58#include "MachDeps.h"
60#if defined(ASSERTS)
61import Control.Exception (assert)
62#endif
63import Control.Monad.ST (unsafeIOToST)
64import Data.Bits ((.&.), xor)
65import Data.Text.Unsafe.Base (inlinePerformIO)
66import Data.Text.UnsafeShift (shiftL, shiftR)
67#if __GLASGOW_HASKELL__ >= 703
68import Foreign.C.Types (CInt(CInt), CSize(CSize))
69#else
70import Foreign.C.Types (CInt, CSize)
71#endif
72import GHC.Base (ByteArray#, MutableByteArray#, Int(..),
73 indexWord16Array#, newByteArray#,
74 unsafeCoerce#, writeWord16Array#)
75import GHC.ST (ST(..), runST)
76import GHC.Word (Word16(..))
77import Prelude hiding (length, read)
79-- | Immutable array type.
80data Array = Array {
81 aBA :: ByteArray#
82#if defined(ASSERTS)
83 , aLen :: {-# UNPACK #-} !Int -- length (in units of Word16, not bytes)
84#endif
85 }
87-- | Mutable array type, for use in the ST monad.
88data MArray s = MArray {
89 maBA :: MutableByteArray# s
90#if defined(ASSERTS)
91 , maLen :: {-# UNPACK #-} !Int -- length (in units of Word16, not bytes)
92#endif
93 }
95#if defined(ASSERTS)
96-- | Operations supported by all arrays.
97class IArray a where
98 -- | Return the length of an array.
99 length :: a -> Int
101instance IArray Array where
102 length = aLen
103 {-# INLINE length #-}
105instance IArray (MArray s) where
106 length = maLen
107 {-# INLINE length #-}
108#endif
110-- | Create an uninitialized mutable array.
111new :: forall s. Int -> ST s (MArray s)
112new n
113 | n < 0 || n .&. highBit /= 0 = error $ "Data.Text.Array.new: size overflow"
114 | otherwise = ST $ \s1# ->
115 case newByteArray# len# s1# of
116 (# s2#, marr# #) -> (# s2#, MArray marr#
117#if defined(ASSERTS)
118 n
119#endif
120 #)
121 where !(I# len#) = bytesInArray n
122 highBit = maxBound `xor` (maxBound `shiftR` 1)
123{-# INLINE new #-}
125-- | Freeze a mutable array. Do not mutate the 'MArray' afterwards!
126unsafeFreeze :: MArray s -> ST s Array
127unsafeFreeze MArray{..} = ST $ \s# ->
128 (# s#, Array (unsafeCoerce# maBA)
129#if defined(ASSERTS)
130 maLen
131#endif
132 #)
133{-# INLINE unsafeFreeze #-}
135-- | Indicate how many bytes would be used for an array of the given
136-- size.
137bytesInArray :: Int -> Int
138bytesInArray n = n `shiftL` 1
139{-# INLINE bytesInArray #-}
141-- | Unchecked read of an immutable array. May return garbage or
142-- crash on an out-of-bounds access.
143unsafeIndex :: Array -> Int -> Word16
144unsafeIndex Array{..} i@(I# i#) =
145 CHECK_BOUNDS("unsafeIndex",aLen,i)
146 case indexWord16Array# aBA i# of r# -> (W16# r#)
147{-# INLINE unsafeIndex #-}
149-- | Unchecked write of a mutable array. May return garbage or crash
150-- on an out-of-bounds access.
151unsafeWrite :: MArray s -> Int -> Word16 -> ST s ()
152unsafeWrite MArray{..} i@(I# i#) (W16# e#) = ST $ \s1# ->
153 CHECK_BOUNDS("unsafeWrite",maLen,i)
154 case writeWord16Array# maBA i# e# s1# of
155 s2# -> (# s2#, () #)
156{-# INLINE unsafeWrite #-}
158-- | Convert an immutable array to a list.
159toList :: Array -> Int -> Int -> [Word16]
160toList ary off len = loop 0
161 where loop i | i < len = unsafeIndex ary (off+i) : loop (i+1)
162 | otherwise = []
164-- | An empty immutable array.
165empty :: Array
166empty = runST (new 0 >>= unsafeFreeze)
168-- | Run an action in the ST monad and return an immutable array of
169-- its result.
170run :: (forall s. ST s (MArray s)) -> Array
171run k = runST (k >>= unsafeFreeze)
173-- | Run an action in the ST monad and return an immutable array of
174-- its result paired with whatever else the action returns.
175run2 :: (forall s. ST s (MArray s, a)) -> (Array, a)
176run2 k = runST (do
177 (marr,b) <- k
178 arr <- unsafeFreeze marr
179 return (arr,b))
181-- | Copy some elements of a mutable array.
182copyM :: MArray s -- ^ Destination
183 -> Int -- ^ Destination offset
184 -> MArray s -- ^ Source
185 -> Int -- ^ Source offset
186 -> Int -- ^ Count
187 -> ST s ()
188copyM dest didx src sidx count
189 | count <= 0 = return ()
190 | otherwise =
191#if defined(ASSERTS)
192 assert (sidx + count <= length src) .
193 assert (didx + count <= length dest) .
194#endif
195 unsafeIOToST $ memcpyM (maBA dest) (fromIntegral didx)
196 (maBA src) (fromIntegral sidx)
197 (fromIntegral count)
198{-# INLINE copyM #-}
200-- | Copy some elements of an immutable array.
201copyI :: MArray s -- ^ Destination
202 -> Int -- ^ Destination offset
203 -> Array -- ^ Source
204 -> Int -- ^ Source offset
205 -> Int -- ^ First offset in destination /not/ to
206 -- copy (i.e. /not/ length)
207 -> ST s ()
208copyI dest i0 src j0 top
209 | i0 >= top = return ()
210 | otherwise = unsafeIOToST $
211 memcpyI (maBA dest) (fromIntegral i0)
212 (aBA src) (fromIntegral j0)
213 (fromIntegral (top-i0))
214{-# INLINE copyI #-}
216-- | Compare portions of two arrays for equality. No bounds checking
217-- is performed.
218equal :: Array -- ^ First
219 -> Int -- ^ Offset into first
220 -> Array -- ^ Second
221 -> Int -- ^ Offset into second
222 -> Int -- ^ Count
223 -> Bool
224equal arrA offA arrB offB count = inlinePerformIO $ do
225 i <- memcmp (aBA arrA) (fromIntegral offA)
226 (aBA arrB) (fromIntegral offB) (fromIntegral count)
227 return $! i == 0
228{-# INLINE equal #-}
230foreign import ccall unsafe "_hs_text_memcpy" memcpyI
231 :: MutableByteArray# s -> CSize -> ByteArray# -> CSize -> CSize -> IO ()
233foreign import ccall unsafe "_hs_text_memcmp" memcmp
234 :: ByteArray# -> CSize -> ByteArray# -> CSize -> CSize -> IO CInt
236foreign import ccall unsafe "_hs_text_memcpy" memcpyM
237 :: MutableByteArray# s -> CSize -> MutableByteArray# s -> CSize -> CSize
238 -> IO ()