+withCStringLen = withCAStringLen
+
+-- | Determines whether a character can be accurately encoded in a 'CString'.
+-- Unrepresentable characters are converted to @\'?\'@.
+--
+-- Currently only Latin-1 characters are representable.
+charIsRepresentable :: Char -> IO Bool
+charIsRepresentable c = return (ord c < 256)
+
+-- single byte characters
+-- ----------------------
+--
+-- ** NOTE: These routines don't handle conversions! **
+
+-- | Convert a C byte, representing a Latin-1 character, to the corresponding
+-- Haskell character.
+castCCharToChar :: CChar -> Char
+castCCharToChar ch = unsafeChr (fromIntegral (fromIntegral ch :: Word8))
+
+-- | Convert a Haskell character to a C character.
+-- This function is only safe on the first 256 characters.
+castCharToCChar :: Char -> CChar
+castCharToCChar ch = fromIntegral (ord ch)
+
+-- | Convert a C @unsigned char@, representing a Latin-1 character, to
+-- the corresponding Haskell character.
+castCUCharToChar :: CUChar -> Char
+castCUCharToChar ch = unsafeChr (fromIntegral (fromIntegral ch :: Word8))
+
+-- | Convert a Haskell character to a C @unsigned char@.
+-- This function is only safe on the first 256 characters.
+castCharToCUChar :: Char -> CUChar
+castCharToCUChar ch = fromIntegral (ord ch)
+
+-- | Convert a C @signed char@, representing a Latin-1 character, to the
+-- corresponding Haskell character.
+castCSCharToChar :: CSChar -> Char
+castCSCharToChar ch = unsafeChr (fromIntegral (fromIntegral ch :: Word8))
+
+-- | Convert a Haskell character to a C @signed char@.
+-- This function is only safe on the first 256 characters.
+castCharToCSChar :: Char -> CSChar
+castCharToCSChar ch = fromIntegral (ord ch)
+
+-- | Marshal a NUL terminated C string into a Haskell string.
+--
+peekCAString :: CString -> IO String
+#ifndef __GLASGOW_HASKELL__
+peekCAString cp = do
+ cs <- peekArray0 nUL cp
+ return (cCharsToChars cs)
+#else
+peekCAString cp = do
+ l <- lengthArray0 nUL cp
+ if l <= 0 then return "" else loop "" (l-1)
+ where
+ loop s i = do
+ xval <- peekElemOff cp i
+ let val = castCCharToChar xval
+ val `seq` if i <= 0 then return (val:s) else loop (val:s) (i-1)
+#endif
+
+-- | Marshal a C string with explicit length into a Haskell string.
+--
+peekCAStringLen :: CStringLen -> IO String
+#ifndef __GLASGOW_HASKELL__
+peekCAStringLen (cp, len) = do
+ cs <- peekArray len cp
+ return (cCharsToChars cs)
+#else
+peekCAStringLen (cp, len)
+ | len <= 0 = return "" -- being (too?) nice.
+ | otherwise = loop [] (len-1)
+ where
+ loop acc i = do
+ xval <- peekElemOff cp i
+ let val = castCCharToChar xval
+ -- blow away the coercion ASAP.
+ if (val `seq` (i == 0))
+ then return (val:acc)
+ else loop (val:acc) (i-1)
+#endif
+
+-- | Marshal a Haskell string into a NUL terminated C string.
+--
+-- * the Haskell string may /not/ contain any NUL characters
+--
+-- * new storage is allocated for the C string and must be
+-- explicitly freed using 'Foreign.Marshal.Alloc.free' or
+-- 'Foreign.Marshal.Alloc.finalizerFree'.
+--
+newCAString :: String -> IO CString
+#ifndef __GLASGOW_HASKELL__
+newCAString = newArray0 nUL . charsToCChars
+#else
+newCAString str = do
+ ptr <- mallocArray0 (length str)
+ let
+ go [] n = pokeElemOff ptr n nUL
+ go (c:cs) n = do pokeElemOff ptr n (castCharToCChar c); go cs (n+1)
+ go str 0
+ return ptr
+#endif
+
+-- | Marshal a Haskell string into a C string (ie, character array) with
+-- explicit length information.
+--
+-- * new storage is allocated for the C string and must be
+-- explicitly freed using 'Foreign.Marshal.Alloc.free' or
+-- 'Foreign.Marshal.Alloc.finalizerFree'.
+--
+newCAStringLen :: String -> IO CStringLen
+#ifndef __GLASGOW_HASKELL__
+newCAStringLen str = newArrayLen (charsToCChars str)
+#else
+newCAStringLen str = do
+ ptr <- mallocArray0 len
+ let
+ go [] n = n `seq` return () -- make it strict in n
+ go (c:cs) n = do pokeElemOff ptr n (castCharToCChar c); go cs (n+1)
+ go str 0
+ return (ptr, len)
+ where
+ len = length str
+#endif