Skip to content

Commit

Permalink
Add peekCString, withCString for converting NUL terminated C strings
Browse files Browse the repository at this point in the history
Since NUL-terminated CString is much more common in foreign APIs than
CStringLen, one should not have to manually build these functions out
of `peekCStringLen`, `withCStringLen` (and perhaps get it wrong, like
I did in jgm/cmark-hs#13).

While here, document that `peekCStringLen`, `withCStringLen` are O(n)
as well.

Fixes #32.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
  • Loading branch information
andersk committed Mar 13, 2019
1 parent 1127b30 commit 0bcbdd0
Showing 1 changed file with 30 additions and 5 deletions.
35 changes: 30 additions & 5 deletions Data/Text/Foreign.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ module Data.Text.Foreign
, useAsPtr
, asForeignPtr
-- ** Encoding as UTF-8
, peekCString
, peekCStringLen
, withCString
, withCStringLen
-- * Unsafe conversion code
, lengthWord16
Expand All @@ -39,12 +41,12 @@ import Control.Monad.ST.Unsafe (unsafeIOToST)
#else
import Control.Monad.ST (unsafeIOToST)
#endif
import Data.ByteString.Unsafe (unsafePackCStringLen, unsafeUseAsCStringLen)
import Data.ByteString.Unsafe (unsafePackCString, unsafePackCStringLen, unsafeUseAsCString, unsafeUseAsCStringLen)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Data.Text.Internal (Text(..), empty)
import Data.Text.Unsafe (lengthWord16)
import Data.Word (Word16)
import Foreign.C.String (CStringLen)
import Foreign.C.String (CString, CStringLen)
import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrArray, withForeignPtr)
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Ptr (Ptr, castPtr, plusPtr)
Expand Down Expand Up @@ -153,6 +155,16 @@ asForeignPtr t@(Text _arr _off len) = do
withForeignPtr fp $ unsafeCopyToPtr t
return (fp, I16 len)

-- | /O(n)/ Decode a NUL terminated C string, which is assumed to have
-- been encoded as UTF-8. If decoding fails, a 'UnicodeException' is
-- thrown.
--
-- @since 1.2.4.0
peekCString :: CString -> IO Text
peekCString cs = do
bs <- unsafePackCString cs
return $! decodeUtf8 bs

-- | /O(n)/ Decode a C string with explicit length, which is assumed
-- to have been encoded as UTF-8. If decoding fails, a
-- 'UnicodeException' is thrown.
Expand All @@ -163,9 +175,22 @@ peekCStringLen cs = do
bs <- unsafePackCStringLen cs
return $! decodeUtf8 bs

-- | Marshal a 'Text' into a C string encoded as UTF-8 in temporary
-- storage, with explicit length information. The encoded string may
-- contain NUL bytes, and is not followed by a trailing NUL byte.
-- | /O(n)/ Marshal a 'Text' into a NUL terminated C string encoded as
-- UTF-8 in temporary storage. The 'Text' must not contain any NUL
-- characters.
--
-- The temporary storage is freed when the subcomputation terminates
-- (either normally or via an exception), so the pointer to the
-- temporary storage must /not/ be used after this function returns.
--
-- @since 1.2.4.0
withCString :: Text -> (CString -> IO a) -> IO a
withCString t act = unsafeUseAsCString (encodeUtf8 t) act

-- | /O(n)/ Marshal a 'Text' into a C string encoded as UTF-8 in
-- temporary storage, with explicit length information. The encoded
-- string may contain NUL bytes, and is not followed by a trailing NUL
-- byte.
--
-- The temporary storage is freed when the subcomputation terminates
-- (either normally or via an exception), so the pointer to the
Expand Down

0 comments on commit 0bcbdd0

Please sign in to comment.