Skip to content

Commit a9f54d4

Browse files
committed
Support Text literals.
1 parent c30b3db commit a9f54d4

File tree

3 files changed

+28
-6
lines changed

3 files changed

+28
-6
lines changed

inline-r/src/Data/Vector/SEXP.hs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,10 @@ module Data.Vector.SEXP
252252
-- ** SEXP specific helpers.
253253
, toString
254254
, toByteString
255+
, unsafeWithByteString
255256
) where
256257

258+
import Control.Exception (evaluate)
257259
import Control.Monad.R.Class
258260
import Control.Monad.R.Internal
259261
import Control.Memory.Region
@@ -265,7 +267,6 @@ import Foreign.R ( SEXP(..) )
265267
import qualified Foreign.R as R
266268
import Foreign.R.Type ( SEXPTYPE(Char) )
267269

268-
import Control.Monad.Primitive ( PrimMonad )
269270
import Control.Monad.ST (ST, runST)
270271
import Data.Int
271272
import Data.Proxy (Proxy(..))
@@ -274,6 +275,7 @@ import qualified Data.Vector.Generic as G
274275
import Data.Vector.Generic.New (run)
275276
import Data.ByteString ( ByteString )
276277
import qualified Data.ByteString as B
278+
import qualified Data.ByteString.Unsafe as B
277279

278280
import Control.Applicative hiding (empty)
279281
#if MIN_VERSION_vector(0,11,0)
@@ -288,7 +290,8 @@ import qualified Data.Vector.Fusion.Stream as Stream
288290
import qualified Data.Vector.Fusion.Stream.Monadic as MStream
289291
#endif
290292

291-
import Control.Monad.Primitive ( unsafeInlineIO, unsafePrimToPrim )
293+
import Control.Monad.Primitive ( PrimMonad, unsafeInlineIO, unsafePrimToPrim )
294+
import qualified Control.DeepSeq as DeepSeq
292295
import Data.Word ( Word8 )
293296
import Foreign ( Storable, Ptr, castPtr, peekElemOff )
294297
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
@@ -455,6 +458,17 @@ toByteString v = unsafeInlineIO $
455458
B.packCStringLen ( castPtr $ unsafeToPtr v
456459
, fromIntegral $ vectorLength v)
457460

461+
-- | This function is unsafe and ByteString should not be used
462+
-- outside of the function. Any change to bytestring will be
463+
-- reflected in the source vector, thus breaking referencial
464+
-- transparancy.
465+
unsafeWithByteString :: DeepSeq.NFData a => Vector s 'Char Word8 -> (ByteString -> IO a) -> a
466+
unsafeWithByteString v f = unsafeInlineIO $ do
467+
x <- B.unsafePackCStringLen (castPtr $ unsafeToPtr v
468+
,fromIntegral $ vectorLength v)
469+
w <- DeepSeq.force <$> f x
470+
evaluate w
471+
458472
------------------------------------------------------------------------
459473
-- Vector API
460474
--

inline-r/src/Foreign/R.hsc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module Foreign.R
5757
, mkChar
5858
, CEType(..)
5959
, mkCharCE
60+
, mkCharLenCE
6061
, mkWeakRef
6162
-- * Node attributes
6263
, typeOf
@@ -328,6 +329,10 @@ mkCharCE :: CEType -> CString -> IO (SEXP V 'R.Char)
328329
mkCharCE (cIntFromEnum -> ce) value = sexp <$>
329330
[C.exp| SEXP { Rf_mkCharCE($(char * value), $(int ce)) } |]
330331

332+
mkCharLenCE :: CEType -> CString -> Int -> IO (SEXP V 'R.Char)
333+
mkCharLenCE (cIntFromEnum -> ce) value (fromIntegral -> len) = sexp <$>
334+
[C.exp| SEXP { Rf_mkCharLenCE($(char * value), $(int len), $(int ce)) } |]
335+
331336
-- | Intern a string @name@ into the symbol table.
332337
--
333338
-- If @name@ is not found, it is added to the symbol table. The symbol

inline-r/src/Language/R/Literal.hs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ import Data.Singletons ( Sing, SingI, fromSing, sing )
5656
import Control.DeepSeq ( NFData )
5757
import Control.Monad ( void, zipWithM_ )
5858
import Data.Int (Int32)
59+
import qualified Data.ByteString.Unsafe as B
5960
import Data.Complex (Complex)
6061
import Data.Text (Text)
61-
import qualified Data.Text as T
62+
import qualified Data.Text.Encoding as T
6263
import Foreign ( FunPtr, castPtr )
6364
import Foreign.C.String ( withCString )
6465
import Foreign.Storable ( Storable, pokeElemOff )
@@ -176,10 +177,12 @@ instance Literal [String] 'R.String where
176177
instance Literal Text 'R.String where
177178
mkSEXPIO s =
178179
mkSEXPVectorIO sing
179-
[GHC.withCString utf8 (T.unpack s) (R.mkCharCE R.CE_UTF8)]
180+
[ B.unsafeUseAsCStringLen (T.encodeUtf8 s) $
181+
uncurry (R.mkCharLenCE R.CE_UTF8) ]
180182
fromSEXP (hexp -> String v) =
181-
case map (\(hexp -> Char xs) -> SVector.toString xs) (SVector.toList v) of
182-
[x] -> T.pack x
183+
case SVector.toList v of
184+
[hexp -> Char x] -> SVector.unsafeWithByteString x $ \p -> do
185+
pure $ T.decodeUtf8 p
183186
_ -> failure "fromSEXP" "Not a singleton vector"
184187
fromSEXP _ =
185188
failure "fromSEXP" "String expected where some other expression appeared."

0 commit comments

Comments
 (0)