Skip to content

Commit caa1112

Browse files
committed
Add peekCString, withCString for converting NUL terminated C strings
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>
1 parent 1127b30 commit caa1112

File tree

1 file changed

+31
-5
lines changed

1 file changed

+31
-5
lines changed

Data/Text/Foreign.hs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ module Data.Text.Foreign
2020
, useAsPtr
2121
, asForeignPtr
2222
-- ** Encoding as UTF-8
23+
, peekCString
2324
, peekCStringLen
25+
, withCString
2426
, withCStringLen
2527
-- * Unsafe conversion code
2628
, lengthWord16
@@ -39,12 +41,13 @@ import Control.Monad.ST.Unsafe (unsafeIOToST)
3941
#else
4042
import Control.Monad.ST (unsafeIOToST)
4143
#endif
42-
import Data.ByteString.Unsafe (unsafePackCStringLen, unsafeUseAsCStringLen)
44+
import Data.ByteString (useAsCString)
45+
import Data.ByteString.Unsafe (unsafePackCString, unsafePackCStringLen, unsafeUseAsCStringLen)
4346
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
4447
import Data.Text.Internal (Text(..), empty)
4548
import Data.Text.Unsafe (lengthWord16)
4649
import Data.Word (Word16)
47-
import Foreign.C.String (CStringLen)
50+
import Foreign.C.String (CString, CStringLen)
4851
import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrArray, withForeignPtr)
4952
import Foreign.Marshal.Alloc (allocaBytes)
5053
import Foreign.Ptr (Ptr, castPtr, plusPtr)
@@ -153,6 +156,16 @@ asForeignPtr t@(Text _arr _off len) = do
153156
withForeignPtr fp $ unsafeCopyToPtr t
154157
return (fp, I16 len)
155158

159+
-- | /O(n)/ Decode a NUL terminated C string, which is assumed to have
160+
-- been encoded as UTF-8. If decoding fails, a 'UnicodeException' is
161+
-- thrown.
162+
--
163+
-- @since 1.2.4.0
164+
peekCString :: CString -> IO Text
165+
peekCString cs = do
166+
bs <- unsafePackCString cs
167+
return $! decodeUtf8 bs
168+
156169
-- | /O(n)/ Decode a C string with explicit length, which is assumed
157170
-- to have been encoded as UTF-8. If decoding fails, a
158171
-- 'UnicodeException' is thrown.
@@ -163,9 +176,22 @@ peekCStringLen cs = do
163176
bs <- unsafePackCStringLen cs
164177
return $! decodeUtf8 bs
165178

166-
-- | Marshal a 'Text' into a C string encoded as UTF-8 in temporary
167-
-- storage, with explicit length information. The encoded string may
168-
-- contain NUL bytes, and is not followed by a trailing NUL byte.
179+
-- | /O(n)/ Marshal a 'Text' into a NUL terminated C string encoded as
180+
-- UTF-8 in temporary storage. The 'Text' must not contain any NUL
181+
-- characters.
182+
--
183+
-- The temporary storage is freed when the subcomputation terminates
184+
-- (either normally or via an exception), so the pointer to the
185+
-- temporary storage must /not/ be used after this function returns.
186+
--
187+
-- @since 1.2.4.0
188+
withCString :: Text -> (CString -> IO a) -> IO a
189+
withCString t act = useAsCString (encodeUtf8 t) act
190+
191+
-- | /O(n)/ Marshal a 'Text' into a C string encoded as UTF-8 in
192+
-- temporary storage, with explicit length information. The encoded
193+
-- string may contain NUL bytes, and is not followed by a trailing NUL
194+
-- byte.
169195
--
170196
-- The temporary storage is freed when the subcomputation terminates
171197
-- (either normally or via an exception), so the pointer to the

0 commit comments

Comments
 (0)