@@ -1,4 +1,5 @@
1{-# LANGUAGE BangPatterns, CPP, GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE BangPatterns, CPP, GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE JavaScriptFFI, ForeignFunctionInterface, UnliftedFFITypes, MagicHash #-}
3-- Module : Data.Text.Foreign
4-- Module : Data.Text.Foreign
4-- Copyright : (c) 2009, 2010 Bryan O'Sullivan
5-- Copyright : (c) 2009, 2010 Bryan O'Sullivan
@@ -50,6 +51,13 @@ import Foreign.Marshal.Alloc (allocaBytes)
50import Foreign.Ptr (Ptr, castPtr, plusPtr)
51import Foreign.Ptr (Ptr, castPtr, plusPtr)
51import Foreign.Storable (peek, poke)
52import Foreign.Storable (peek, poke)
52import qualified Data.Text.Array as A
53import qualified Data.Text.Array as A
55+import Data.JSString (JSString)
56+import GHC.Exts (ByteArray#, Int#)
57+import GHC.Prim (int2Word#)
58+import GHC.Int (Int(..))
59+import GHC.Word (Word16(..))
54-- $interop
62-- $interop
@@ -74,11 +82,15 @@ fromPtr :: Ptr Word16 -- ^ source array
74 -> I16 -- ^ length of source array (in 'Word16' units)
82 -> I16 -- ^ length of source array (in 'Word16' units)
75 -> IO Text
83 -> IO Text
76fromPtr _ (I16 0) = return empty
84fromPtr _ (I16 0) = return empty
77−fromPtr ptr (I16 len) =
85+fromPtr ptr (I16 (len@(I# len#))) =
78#if defined(ASSERTS)
86#if defined(ASSERTS)
79 assert (len > 0) $
87 assert (len > 0) $
89+#if defined(__GHCJS__)
90+ return $! Text (js_toString (A.aBA arr) 0# len#)
81 return $! Text arr 0 len
92 return $! Text arr 0 len
83 arr = A.run (A.new len >>= copy)
95 arr = A.run (A.new len >>= copy)
84 copy marr = loop ptr 0
96 copy marr = loop ptr 0
@@ -102,14 +114,30 @@ fromPtr ptr (I16 len) =
102-- end of the prefix will be advanced by one additional 'Word16' unit
114-- end of the prefix will be advanced by one additional 'Word16' unit
103-- to maintain its validity.
115-- to maintain its validity.
104takeWord16 :: I16 -> Text -> Text
116takeWord16 :: I16 -> Text -> Text
118+takeWord16 (I16 n@(I# n#)) t@(Text jss)
120+ | n >= len || m >= len = t
121+ | otherwise = Text (js_substr 0# n# jss)
105takeWord16 (I16 n) t@(Text arr off len)
123takeWord16 (I16 n) t@(Text arr off len)
106 | n <= 0 = empty
124 | n <= 0 = empty
107 | n >= len || m >= len = t
125 | n >= len || m >= len = t
108 | otherwise = Text arr off m
126 | otherwise = Text arr off m
132+ m@(I# m#) | w < 0xD800 || w > 0xDBFF = n
135+ w# = js_charCodeAt (n-1) jss
110 m | w < 0xD800 || w > 0xDBFF = n
137 m | w < 0xD800 || w > 0xDBFF = n
111 | otherwise = n+1
138 | otherwise = n+1
112 w = A.unsafeIndex arr (off+n-1)
139 w = A.unsafeIndex arr (off+n-1)
114-- | /O(1)/ Return the suffix of the 'Text', with @n@ 'Word16' units
142-- | /O(1)/ Return the suffix of the 'Text', with @n@ 'Word16' units
115-- dropped from its beginning.
143-- dropped from its beginning.
@@ -118,18 +146,45 @@ takeWord16 (I16 n) t@(Text arr off len)
118-- beginning of the suffix will be advanced by one additional 'Word16'
146-- beginning of the suffix will be advanced by one additional 'Word16'
119-- unit to maintain its validity.
147-- unit to maintain its validity.
120dropWord16 :: I16 -> Text -> Text
148dropWord16 :: I16 -> Text -> Text
150+dropWord16 (I16 n@(I# n#)) t@(Text jss)
152+ | n >= len || m >= len = empty
153+ | otherwise = Text (js_substr1 n# jss)
121dropWord16 (I16 n) t@(Text arr off len)
155dropWord16 (I16 n) t@(Text arr off len)
122 | n <= 0 = t
156 | n <= 0 = t
123 | n >= len || m >= len = empty
157 | n >= len || m >= len = empty
124 | otherwise = Text arr (off+m) (len-m)
158 | otherwise = Text arr (off+m) (len-m)
164+ m@(I# m#) | w < 0xD800 || w > 0xDBFF = n
167+ w# = js_charCodeAt (n-1) jss
126 m | w < 0xD800 || w > 0xDBFF = n
169 m | w < 0xD800 || w > 0xDBFF = n
127 | otherwise = n+1
170 | otherwise = n+1
128 w = A.unsafeIndex arr (off+n-1)
171 w = A.unsafeIndex arr (off+n-1)
130-- | /O(n)/ Copy a 'Text' to an array. The array is assumed to be big
174-- | /O(n)/ Copy a 'Text' to an array. The array is assumed to be big
131-- enough to hold the contents of the entire 'Text'.
175-- enough to hold the contents of the entire 'Text'.
132unsafeCopyToPtr :: Text -> Ptr Word16 -> IO ()
176unsafeCopyToPtr :: Text -> Ptr Word16 -> IO ()
178+unsafeCopyToPtr (Text jst) ptr = loop ptr off
181+ loop !p !i | i == end = return ()
183+ poke p (W16# (int2Word# (js_charCodeAt i jst)))
184+ loop (p `plusPtr` 2) (i + 1)
133unsafeCopyToPtr (Text arr off len) ptr = loop ptr off
188unsafeCopyToPtr (Text arr off len) ptr = loop ptr off
135 end = off + len
190 end = off + len
@@ -137,21 +192,38 @@ unsafeCopyToPtr (Text arr off len) ptr = loop ptr off
137 | otherwise = do
192 | otherwise = do
138 poke p (A.unsafeIndex arr i)
193 poke p (A.unsafeIndex arr i)
139 loop (p `plusPtr` 2) (i + 1)
194 loop (p `plusPtr` 2) (i + 1)
141-- | /O(n)/ Perform an action on a temporary, mutable copy of a
197-- | /O(n)/ Perform an action on a temporary, mutable copy of a
142-- 'Text'. The copy is freed as soon as the action returns.
198-- 'Text'. The copy is freed as soon as the action returns.
143useAsPtr :: Text -> (Ptr Word16 -> I16 -> IO a) -> IO a
199useAsPtr :: Text -> (Ptr Word16 -> I16 -> IO a) -> IO a
201+useAsPtr t@(Text jst) action =
202+ allocaBytes (len * 2) $ \buf -> do
203+ unsafeCopyToPtr t buf
204+ action (castPtr buf) (fromIntegral len)
205+ where len = js_length jst
144useAsPtr t@(Text _arr _off len) action =
207useAsPtr t@(Text _arr _off len) action =
145 allocaBytes (len * 2) $ \buf -> do
208 allocaBytes (len * 2) $ \buf -> do
146 unsafeCopyToPtr t buf
209 unsafeCopyToPtr t buf
147 action (castPtr buf) (fromIntegral len)
210 action (castPtr buf) (fromIntegral len)
149-- | /O(n)/ Make a mutable copy of a 'Text'.
213-- | /O(n)/ Make a mutable copy of a 'Text'.
150asForeignPtr :: Text -> IO (ForeignPtr Word16, I16)
214asForeignPtr :: Text -> IO (ForeignPtr Word16, I16)
216+asForeignPtr t@(Text jst) = do
217+ fp <- mallocForeignPtrArray len
218+ withForeignPtr fp $ unsafeCopyToPtr t
219+ return (fp, I16 len)
220+ where len = js_length jst
151asForeignPtr t@(Text _arr _off len) = do
222asForeignPtr t@(Text _arr _off len) = do
152 fp <- mallocForeignPtrArray len
223 fp <- mallocForeignPtrArray len
153 withForeignPtr fp $ unsafeCopyToPtr t
224 withForeignPtr fp $ unsafeCopyToPtr t
154 return (fp, I16 len)
225 return (fp, I16 len)
156-- | /O(n)/ Decode a C string with explicit length, which is assumed
228-- | /O(n)/ Decode a C string with explicit length, which is assumed
157-- to have been encoded as UTF-8. If decoding fails, a
229-- to have been encoded as UTF-8. If decoding fails, a
@@ -174,3 +246,17 @@ peekCStringLen cs = do
174-- @since 1.0.0.0
246-- @since 1.0.0.0
175withCStringLen :: Text -> (CStringLen -> IO a) -> IO a
247withCStringLen :: Text -> (CStringLen -> IO a) -> IO a
176withCStringLen t act = unsafeUseAsCStringLen (encodeUtf8 t) act
248withCStringLen t act = unsafeUseAsCStringLen (encodeUtf8 t) act
251+foreign import javascript unsafe
252+ "h$textToString($1,$2,$3)"
253+ js_toString :: ByteArray# -> Int# -> Int# -> JSString
254+foreign import javascript unsafe
255+ "$3.substr($1,$2)" js_substr :: Int# -> Int# -> JSString -> JSString
256+foreign import javascript unsafe
257+ "$2.substr($1)" js_substr1 :: Int# -> JSString -> JSString
258+foreign import javascript unsafe
259+ "$1.length" js_length :: JSString -> Int
260+foreign import javascript unsafe
261+ "$2.charCodeAt($1)" js_charCodeAt :: Int -> JSString -> Int#