% % (c) The GRASP/AQUA Project, Glasgow University, 1993-1998 % \section{Working with C strings} A collection of lower-level functions to help converting between C strings and Haskell Strings (packed or otherwise). A more user-friendly Haskell interface to packed string representation is the PackedString interface. \begin{code} module CString ( unpackCString -- :: Addr -> [Char] , unpackNBytes -- :: Addr -> Int -> [Char] , unpackNBytesST -- :: Addr -> Int -> ST s [Char] , unpackNBytesAccST -- :: Addr -> Int -> [Char] -> ST s [Char] , unpackCString# -- :: Addr# -> [Char] ** , unpackNBytes# -- :: Addr# -> Int# -> [Char] ** , unpackNBytesST# -- :: Addr# -> Int# -> ST s [Char] -- terrrible names... , unpackCStringIO -- :: Addr -> IO String , unpackCStringLenIO -- :: Addr -> Int -> IO String , unpackNBytesIO -- :: Addr -> Int -> IO [Char] , unpackNBytesAccIO -- :: Addr -> Int -> [Char] -> IO [Char] , unpackNBytesBAIO -- :: ByteArray Int -> Int -> IO [Char] , unpackNBytesAccBAIO -- :: ByteArray Int -> Int -> [Char] -> IO [Char] , packString -- :: [Char] -> ByteArray Int , packStringST -- :: [Char] -> ST s (ByteArray Int) , packStringIO -- :: [Char] -> IO (ByteArray Int) , packNBytesST -- :: Int -> [Char] -> ByteArray Int , packCString# -- :: [Char] -> ByteArray# , unpackCStringBA -- :: ByteArray Int -> [Char] , unpackNBytesBA -- :: ByteArray Int -> Int -> [Char] , unpackCStringBA# -- :: ByteArray# -> Int# -> [Char] , unpackNBytesBA# -- :: ByteArray# -> Int# -> [Char] -- unmarshaling (char*) vectors. , unvectorize -- :: Addr -> Int -> IO [String] , vectorize -- :: [[Char]] -> IO (ByteArray Int) , allocChars -- :: Int -> IO (MutableByteArray RealWorld Int) , allocWords -- :: Int -> IO (MutableByteArray RealWorld Int) , freeze -- :: MutableByteArray RealWorld Int -> IO (ByteArray Int) , strcpy -- :: Addr -> IO String ) where import PrelPack import GlaExts import Addr import PrelIOBase ( IO(..) ) import MutableArray \end{code} \begin{code} packStringIO :: [Char] -> IO (ByteArray Int) packStringIO str = stToIO (packStringST str) \end{code} \begin{code} unpackCStringIO :: Addr -> IO String unpackCStringIO addr | addr == nullAddr = return "" | otherwise = unpack 0# where unpack nh = do ch <- readCharOffAddr addr (I# nh) if ch == '\0' then return [] else do ls <- unpack (nh +# 1#) return (ch : ls) -- unpack 'len' chars unpackCStringLenIO :: Addr -> Int -> IO String unpackCStringLenIO addr l@(I# len#) | len# <# 0# = ioError (userError ("CString.unpackCStringLenIO: negative length (" ++ show l ++ ")")) | len# ==# 0# = return "" | otherwise = unpack [] (len# -# 1#) where unpack acc 0# = do ch <- readCharOffAddr addr (I# 0#) return (ch:acc) unpack acc nh = do ch <- readCharOffAddr addr (I# nh) unpack (ch:acc) (nh -# 1#) unpackNBytesIO :: Addr -> Int -> IO [Char] unpackNBytesIO a l = stToIO (unpackNBytesST a l) unpackNBytesAccIO :: Addr -> Int -> [Char] -> IO [Char] unpackNBytesAccIO a l acc = stToIO (unpackNBytesAccST a l acc) unpackNBytesBAIO :: ByteArray Int -> Int -> IO [Char] unpackNBytesBAIO ba l = unpackNBytesAccBAIO ba l [] -- note: no bounds checking! unpackNBytesAccBAIO :: ByteArray Int -> Int -> [Char] -> IO [Char] unpackNBytesAccBAIO _ 0 rest = return rest unpackNBytesAccBAIO (ByteArray _ ba) (I# len#) rest = unpack rest (len# -# 1#) where unpack acc i# | i# <# 0# = return acc | otherwise = case indexCharArray# ba i# of ch -> unpack (C# ch : acc) (i# -# 1#) \end{code} Turn a NULL-terminated vector of null-terminated strings into a string list (ToDo: create a module of common marshaling functions) \begin{code} unvectorize :: Addr -> Int -> IO [String] unvectorize ptr n | str == nullAddr = return [] | otherwise = do x <- unpackCStringIO str xs <- unvectorize ptr (n+1) return (x : xs) where str = indexAddrOffAddr ptr n \end{code} Turn a string list into a NULL-terminated vector of null-terminated strings No indices...I hate indices. Death to Ix. \begin{code} vectorize :: [String] -> IO (ByteArray Int) vectorize vs = do arr <- allocWords (len + 1) fill arr 0 vs freeze arr where len :: Int len = length vs fill :: MutableByteArray RealWorld Int -> Int -> [String] -> IO () fill arr n [] = _casm_ ``((PP_)%0)[%1] = NULL;'' arr n fill arr n (x:xs) = do barr <- packStringIO x _casm_ ``((PP_)%0)[%1] = (P_)%2;'' arr n barr fill arr (n+1) xs \end{code} Allocating chunks of memory in the Haskell heap, leaving out the bounds - use with care. \begin{code} -- Allocate a mutable array of characters with no indices. allocChars :: Int -> IO (MutableByteArray RealWorld Int) allocChars size = stToIO (newCharArray (0,size)) allocWords :: Int -> IO (MutableByteArray RealWorld Int) allocWords size = stToIO (newIntArray (0,size)) -- Freeze these index-free mutable arrays freeze :: MutableByteArray RealWorld Int -> IO (ByteArray Int) freeze mb = stToIO (unsafeFreezeByteArray mb) -- Copy a null-terminated string from outside the heap to -- Haskellized nonsense inside the heap strcpy :: Addr -> IO String strcpy str = unpackCStringIO str \end{code}