{-# OPTIONS_GHC -XCPP #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  System.Win32.Com.HDirect.HDirect
-- Copyright   :  (c) Sigbjorn Finne, sof@dcs.gla.ac.uk 1999
-- License     :  BSD-style (see the file libraries/base/LICENSE)
-- 
-- Maintainer  :  sof@forkIO.com
-- Stability   :  provisional
-- Portability :  portable
--
-- Stubs for marshalling and unmarshalling primitive types, as used
-- by HaskellDirect-generated code.
-- 
-----------------------------------------------------------------------------
module System.Win32.Com.HDirect.HDirect 
    (module System.Win32.Com.HDirect.HDirect
    , Int8, Int16, Int32, Int64
    , Word8, Word16, Word32, Word64

    , Char, Double, Float, Bool
    
    , Ptr, StablePtr
    , deRefStablePtr) where

import Data.Char
import Data.Int  ( Int8, Int16, Int32, Int64 )
import Data.Word ( Word8, Word16, Word32, Word64 )
import Control.Monad
import System.Win32.Com.HDirect.Pointer
import System.IO.Unsafe ( unsafePerformIO )


import Foreign.StablePtr
import Foreign.Storable
import Foreign.ForeignPtr
import Foreign.ForeignPtr.Unsafe
import Foreign.Ptr
import Foreign.C.Types ( CChar )
import Foreign.C.String
import Foreign.Marshal.Alloc (mallocBytes)
import qualified Foreign.Marshal.Alloc as Alloc (free)

import Data.Bits
#if __GLASGOW_HASKELL__ >= 505
import GHC.Base ( Int(..), Int#, getTag )
#else
import GlaExts ( Int(..), Int#, dataToTag# )
getTag :: a -> Int#
getTag x = dataToTag# x
{- WAS: x `seq` dataToTag# x
        this won't work 
      (seq's type is a->b->b, where b isn't 'open',
           but has to be of kind *)
-}
#endif

infixl 5 .+.

#if __GLASGOW_HASKELL__ >= 601
foreignPtrToPtr = unsafeForeignPtrToPtr
#endif

{-
At the moment the IDL compiler will emit calls to types with identity marshallers
(i.e., by-value marshallers for primitive, FFI-recognised types), so we need
to provide stubs for these here.
-}

--Int* marshalling functions:

marshallInt :: Int -> IO Int
marshallInt x = return x

unmarshallInt :: Int -> IO Int
unmarshallInt x = return x

writeInt :: Ptr Int -> Int -> IO ()
writeInt ptr v = poke ptr v

readInt :: Ptr Int -> IO Int
readInt ptr = peek ptr

--ToDo: generate host-specific versions of 
sizeofInt :: Word32
sizeofInt = fromIntegral (sizeOf (0 :: Int))

--Int8
marshallInt8 :: Int8 -> IO Int8
marshallInt8 v = return v

unmarshallInt8 :: Int8 -> IO Int8
unmarshallInt8 v = return v

writeInt8 :: Ptr Int8 -> Int8 -> IO ()
writeInt8 ptr v = poke ptr v

readInt8 :: Ptr Int8 -> IO Int8
readInt8 ptr = peek ptr

sizeofInt8 :: Word32
sizeofInt8 = fromIntegral (sizeOf (1 :: Int8))

--Int16
marshallInt16 :: Int16 -> IO Int16
marshallInt16 x = return x
unmarshallInt16 :: Int16 -> IO Int16
unmarshallInt16 x = return x

writeInt16 :: Ptr Int16 -> Int16 -> IO ()
writeInt16 ptr v = poke ptr v

readInt16 :: Ptr Int16 -> IO Int16
readInt16 ptr = peek ptr

sizeofInt16 :: Word32
sizeofInt16 = fromIntegral (sizeOf (0 :: Int16))

-- Int32
marshallInt32 :: Int32 -> IO Int32
marshallInt32 x = return x

unmarshallInt32 :: Int32 -> IO Int32
unmarshallInt32 x = return x

writeInt32 :: Ptr Int32 -> Int32 -> IO ()
writeInt32 ptr v = poke ptr v

readInt32 :: Ptr Int32 -> IO Int32
readInt32 ptr = peek ptr

sizeofInt32 :: Word32
sizeofInt32 = fromIntegral (sizeOf (0::Int32))

marshallInt64 :: Int64 -> IO Int64
marshallInt64 x = return x

unmarshallInt64 :: Int64 -> IO Int64
unmarshallInt64 x = return x

writeInt64 :: Ptr Int64 -> Int64 -> IO ()
readInt64 :: Ptr Int64 -> IO Int64
writeInt64 ptr v = poke ptr v
readInt64 ptr = peek ptr

sizeofInt64 :: Word32
sizeofInt64 = fromIntegral (sizeOf (0 :: Int64))

type Hyper   = Int64
marshallHyper :: Hyper -> IO Int64
unmarshallHyper :: Int64 -> IO Hyper
writeHyper :: Ptr Hyper -> Hyper -> IO ()
readHyper :: Ptr Hyper -> IO Hyper
sizeofHyper :: Word32

marshallHyper   = marshallInt64
unmarshallHyper = unmarshallInt64
writeHyper      = writeInt64
readHyper       = readInt64
sizeofHyper     = fromIntegral (sizeOf (0 :: Int64))

writeInteger :: Ptr Integer -> Integer -> IO ()
writeInteger ptr x = writeInt64 (castPtr ptr) (fromIntegral x)

readInteger :: Ptr Integer -> IO Integer
readInteger ptr = do
  x <- readInt64 (castPtr ptr)
  return (fromIntegral x)

marshallInteger :: Integer -> IO (Int32, Int32)
marshallInteger i =  return (fromInteger lo, fromInteger hi)
 where
   (hi,lo) = i `divMod` (toInteger (maxBound :: Int) + 1)

unmarshallInteger :: (Int32,Int32) -> IO Integer
unmarshallInteger (hi,lo) =  return ((toInteger lo) + (toInteger hi) * (toInteger (maxBound :: Int) + 1))

marshallUInteger :: Integer -> IO (Int32, Int32)
marshallUInteger = marshallInteger

unmarshallUInteger :: (Int32,Int32) -> IO Integer
unmarshallUInteger = unmarshallInteger

readUInteger :: Ptr Integer -> IO Integer
readUInteger = readInteger

writeUInteger :: Ptr Integer -> Integer -> IO ()
writeUInteger = writeInteger

--Characters and bytes:
--
-- NOTE: we assume that Char is CChar (==an 8-bit byte.)

marshallChar :: Char -> IO Char
marshallChar x = return x

unmarshallChar :: Char -> IO Char
unmarshallChar x = return x

writeChar :: Ptr Char -> Char -> IO ()
writeChar ptr v = poke ((castPtr ptr) :: Ptr CChar) (castCharToCChar v)

readChar :: Ptr Char -> IO Char
readChar ptr = peek ((castPtr ptr) :: Ptr CChar) >>= return.castCCharToChar

sizeofChar :: Word32
sizeofChar     = fromIntegral (sizeOf (undefined :: CChar))

-- wide chars.
type Wchar_t = Word16

marshallWchar_t :: Wchar_t -> IO Wchar_t
marshallWchar_t   = marshallWord16
unmarshallWchar_t :: Wchar_t -> IO Wchar_t
unmarshallWchar_t = unmarshallWord16
writeWchar_t :: Ptr Wchar_t -> Wchar_t -> IO ()
writeWchar_t      = writeWord16
readWchar_t :: Ptr Wchar_t -> IO Wchar_t
readWchar_t       = readWord16
sizeofWchar_t :: Word32
sizeofWchar_t     = fromIntegral (sizeOf (0::Word16))


type Octet   = Byte
type Byte    = Word8

marshallByte :: Byte -> IO Byte
marshallByte   = marshallWord8
unmarshallByte :: Byte -> IO Byte
unmarshallByte = unmarshallWord8
writeByte :: Ptr Byte -> Byte -> IO ()
writeByte      = writeWord8
readByte :: Ptr Byte -> IO Byte
readByte       = readWord8
sizeofByte :: Word32
sizeofByte     = fromIntegral (sizeOf (0::Word8))

--Words:

-- Word8:
marshallWord8 :: Word8 -> IO Word8
marshallWord8 x = return x

unmarshallWord8 :: Word8 -> IO Word8
unmarshallWord8 x = return x

writeWord8 :: Ptr Word8 -> Word8 -> IO ()
writeWord8 ptr v = poke ptr v

readWord8 :: Ptr Word8 -> IO Word8
readWord8 ptr = peek ptr

sizeofWord8 :: Word32
sizeofWord8 = fromIntegral (sizeOf (undefined :: Word8))

-- Word16:
marshallWord16 :: Word16 -> IO Word16
marshallWord16 x = return x

unmarshallWord16 :: Word16 -> IO Word16
unmarshallWord16 x = return x

writeWord16 :: Ptr Word16 -> Word16 -> IO ()
writeWord16 ptr v = poke ptr v

readWord16 :: Ptr Word16 -> IO Word16
readWord16 ptr = peek ptr

sizeofWord16 :: Word32
sizeofWord16 = fromIntegral (sizeOf (undefined :: Word16))

-- Word32:
marshallWord32 :: Word32 -> IO Word32
marshallWord32 x = return x

unmarshallWord32 :: Word32 -> IO Word32
unmarshallWord32 x = return x

writeWord32 :: Ptr Word32 -> Word32 -> IO ()
writeWord32 ptr v = poke ptr v

readWord32 :: Ptr Word32 -> IO Word32
readWord32 ptr = peek ptr

sizeofWord32 :: Word32
sizeofWord32 = fromIntegral (sizeOf (undefined :: Word32))

marshallWord64 :: Word64 -> IO Word64
marshallWord64 x = return x

unmarshallWord64 :: Word64 -> IO Word64
unmarshallWord64 x = return x

writeWord64 :: Ptr Word64 -> Word64 -> IO ()
readWord64 :: Ptr Word64 -> IO Word64
writeWord64 p v = poke p v
readWord64 p = peek p

sizeofWord64 :: Word32
sizeofWord64 = fromIntegral (sizeOf (undefined :: Word64))


--Addr marshallers:
{-
begin{code}
marshallAddr :: Ptr Addr -> IO Addr
marshallAddr p = return p

unmarshallAddr :: Ptr Addr -> IO Addr
unmarshallAddr p = return p

writeAddr :: Ptr Addr -> Addr -> IO ()
{- BEGIN_DEBUG
writeAddr ptr a | ptr == nullAddr = ioError (userError "writeAddr: NULL pointer")
   END_DEBUG -}
writeAddr ptr a = writeAddrOffAddr ptr 0 a

readAddr :: Ptr Addr -> IO Addr
readAddr a = readAddrOffAddr a 0

sizeofAddr :: Word32
sizeofAddr     = fromIntegral (sizeOf (undefined :: Foreign.Ptr.Ptr ()))

freeAddr :: Addr -> IO ()
freeAddr = free
end{code}
-}

--Double marshallers:

marshallDouble :: Double -> IO Double
marshallDouble x = return x

unmarshallDouble :: Double -> IO Double
unmarshallDouble x = return x

writeDouble :: Ptr Double -> Double -> IO ()
writeDouble ptr x = poke ptr x

readDouble :: Ptr Double -> IO Double
readDouble ptr = peek ptr

sizeofDouble :: Word32
sizeofDouble   = fromIntegral (sizeOf (undefined :: Double))

writeFloat :: Ptr Float -> Float -> IO ()
writeFloat ptr x = poke ptr x

readFloat :: Ptr Float -> IO Float
readFloat ptr = peek ptr

sizeofFloat :: Word32
sizeofFloat   = fromIntegral (sizeOf (undefined :: Float))


--Booleans - represented externally by a long (Int32):

marshallBool :: Bool -> IO Int32
marshallBool v = marshallInt32 (if v then (-1) else 0)

unmarshallBool :: Int32 -> IO Bool
unmarshallBool v = return (v /= 0)

writeBool :: Ptr Bool -> Bool -> IO ()
writeBool ptr v = writeInt32 (castPtr ptr) (if v then (-1) else 0)

readBool :: Ptr Bool -> IO Bool
readBool ptr = do
  v <- readInt32 (castPtr ptr)
  return ( v /= 0 )

sizeofBool :: Word32
sizeofBool = fromIntegral (sizeOf (0 :: Int32))

addNCastPtr :: Ptr a -> Word32 -> Ptr b
addNCastPtr v off = v `plusPtr` (fromIntegral off)

derefPtr :: Ptr (Ptr a) -> IO (Ptr a)
derefPtr p = peek p

indexPtr :: Ptr (Ptr a) -> Int -> IO (Ptr a)
indexPtr p off = peekElemOff p off

--The unit of allocation is an 8-bit byte.

allocOutPtr :: IO (Ptr a)
allocOutPtr = alloc 4 -- 4 bytes in a pointer. ToDo: 64-bit platform'ify.

allocBytes :: Int -> IO (Ptr a)
allocBytes len = alloc (fromIntegral len)

allocWords :: Int -> IO (Ptr a)
allocWords len = alloc (4 * fromIntegral len)

alloc_malloc :: Word32 -> IO (Ptr a)
alloc_malloc s = mallocBytes (fromIntegral s)

free_malloc = Alloc.free

free = freeMemory

-- Note: we want/have to use the COM task allocator here so as to
-- remain consistent (=> finalization happens by calling one allocator,
-- it being the COM one.)
alloc :: Word32 -> IO (Ptr a)
alloc s = allocMemory s

doThenFree ::(Ptr a -> IO ()) -> (Ptr b -> IO c) -> Ptr d -> IO c
doThenFree f act ptr = do
   v <- act (castPtr ptr)
   f (castPtr ptr)
   return v

trivialFree :: a -> IO ()
trivialFree _ = return ()

fillIn :: Int -> (Ptr a -> IO ()) -> IO (Ptr a)
fillIn sz refreemarshall = do
  ptr <- allocBytes sz
  refreemarshall ptr
  return ptr

--[ptr]Ptr marshalling

marshallPtr :: Ptr a -> IO (Ptr a)
marshallPtr v = return v

unmarshallPtr :: Ptr a -> IO (Ptr a)
unmarshallPtr v = return v

writePtr :: Ptr (Ptr a) -> Ptr a -> IO ()
writePtr a v = poke a v

readPtr :: Ptr a -> IO (Ptr b)
readPtr a = peek (castPtr a)

writefptr :: Ptr b -> ForeignPtr a -> IO ()
writefptr p v = poke (castPtr p) (foreignPtrToPtr v)

--[unique]Ptr marshalling

marshallunique :: (IO (Ptr a))
               -> (Ptr a -> a -> IO ())
           -> Maybe a
           -> IO (Ptr a)
marshallunique allocRef marshallInto mb = 
  case mb of
    Nothing -> return nullPtr
    Just x  -> marshallref allocRef marshallInto x

marshallMaybe :: (a -> IO b) -> b -> Maybe a -> IO b
marshallMaybe _mshall def  Nothing  = return def
marshallMaybe mshall  _def (Just x) = mshall x

writeMaybe :: (Ptr a -> a -> IO ())
           -> Ptr (Maybe a)
       -> Maybe a
       -> IO ()
writeMaybe _  ptr Nothing  = writePtr (castPtr ptr) nullPtr
writeMaybe wr ptr (Just x) = wr (castPtr ptr) x

readMaybe :: (Ptr a -> IO b) -> Ptr a -> IO (Maybe b)
readMaybe rd ptr
 | ptr == nullPtr = return Nothing
 | otherwise      = rd ptr >>= return . Just

writeunique :: IO (Ptr a)
        -> (Ptr a -> a -> IO ())
        -> Ptr (Maybe a)
        -> Maybe a
        -> IO ()
writeunique allocRef marshallInto p mb =
  case mb of
    Nothing  -> writePtr (castPtr p) nullPtr
    Just x   -> writeref allocRef marshallInto (castPtr p) x

-- used to handle unique function pointers, since their marshallers
-- have a non-standard type (cf. 2nd arg.)
writeunique_fun :: IO (Ptr a)
            -> (Ptr (Ptr a) -> a -> IO ())
             -> Ptr (Maybe a)
            -> Maybe a
            -> IO ()
writeunique_fun allocRef marshallInto p mb =
  case mb of
    Nothing  -> writePtr (castPtr p) nullPtr
    Just x   -> writeref allocRef (\ pv v -> marshallInto (castPtr pv) v) (castPtr p) x

unmarshallunique :: (Ptr a -> IO a) -> Ptr a -> IO (Maybe a)
unmarshallunique refUnmarshall ptr
 | ptr == nullPtr = return Nothing
 | otherwise      = do
    x <- unmarshallref refUnmarshall ptr
    return (Just x)


readunique :: (Ptr a -> IO a) -> Ptr b -> IO (Maybe a)
readunique refUnmarshall ptr
 | ptr == nullPtr = return Nothing
 | otherwise      = do
   v <- readPtr (castPtr ptr)
   if nullPtr == v then
      return Nothing
    else do
      x <- refUnmarshall (castPtr v)
      return (Just x)

freeunique :: (Ptr a -> IO ()) -> Ptr (Ptr a) -> IO ()
freeunique freeptee ptr
 | ptr == nullPtr = return ()
 | otherwise      = do
    ptr' <- derefPtr ptr
    freeptee ptr'
    free ptr


--Marshalling [unique]void* pointers

marshallunique_ptr :: Maybe (Ptr a) -> IO (Ptr a)
marshallunique_ptr mb = 
  case mb of
    Nothing -> marshallPtr nullPtr
    Just x  -> marshallPtr x


--[ref]Ptr marshalling

marshallref :: (IO (Ptr a)) -> (Ptr a -> a -> IO ()) -> a -> IO (Ptr a)
marshallref allocRef marshallInto v = do
   px <- allocRef
   marshallInto px v
   return px

writeref :: (IO (Ptr a)) -> (Ptr a -> a -> IO ()) -> Ptr (Ptr a) -> a -> IO ()
writeref allocRef marshallInto ptr v = do
  px <- marshallref allocRef marshallInto v
  writePtr ptr px

unmarshallref :: (Ptr a -> IO b) -> Ptr a -> IO b
unmarshallref refUnmarshall ptr = refUnmarshall ptr

readref :: (Ptr a -> IO a) -> Ptr (Ptr a) -> IO a
readref refUnmarshall ptr = do
  px <- readPtr ptr
  unmarshallref refUnmarshall (castPtr px)

freeref :: (Ptr b -> IO ()) -> Ptr a -> IO ()
freeref free_inner pptr = do
   ptr <- readPtr (castPtr pptr)
   free_inner ptr
   free pptr

--All by-reference marshalling and unmarshalling functions
--of enums can be expressed using these two stubs:

writeenum16 :: (b -> IO Int16) -> Ptr Int16 -> b -> IO ()
writeenum16 marshall pv v =
  marshall v >>= \ x ->
  writeInt16 pv x

readenum16 :: (Int16 -> IO a) -> Ptr (Int16) -> IO a
readenum16 unmarshall pv = do
  v <- readInt16 pv
  unmarshall v

marshallEnum16 :: Enum a => a -> IO Int16
marshallEnum16 v = return (fromIntegral (fromEnum v))

unmarshallEnum16 :: Enum a => Int16 -> IO a
unmarshallEnum16 x = return (toEnum (fromIntegral x))

marshallEnum32 :: Enum a => a -> IO Int32
marshallEnum32 v = return (fromIntegral (fromEnum v))

unmarshallEnum32 :: Enum a => Int32 -> IO a
unmarshallEnum32 x = return (toEnum (fromIntegral x))

writeEnum32 :: Enum a => Ptr b -> a -> IO ()
writeEnum32 p v = writeInt32 (castPtr p) (fromIntegral (fromEnum v))

readEnum32 :: Enum a => Ptr b -> IO a
readEnum32 p = do
  x <- readInt32 (castPtr p)
  return (toEnum (fromIntegral x))

writeEnum16 :: Enum a => Ptr b -> a -> IO ()
writeEnum16 p v = writeInt16 (castPtr p) (fromIntegral (fromEnum v))

readEnum16 :: Enum a => Ptr b -> IO a
readEnum16 p = do
  x <- readInt16 (castPtr p)
  return (toEnum (fromIntegral x))

marshalllist :: Word32
         -> (Ptr a -> a -> IO ())
         -> [a]
         -> IO (Ptr b)
marshalllist szof writeelt ls = do
 arr <- alloc (len*szof)
 foldM writeElt (castPtr arr) ls
 return (castPtr arr)
  where
   len = fromIntegral (length ls)

   writeElt addr v = do
     writeelt addr v
     return (addNCastPtr addr szof)

unmarshalllist :: Word32 -> Word32 -> Word32 -> (Ptr any -> IO a) -> Ptr b -> IO [a]
unmarshalllist szof offset len unpack ptr = do
 let ptr0 = addNCastPtr ptr (offset*szof)
 loop ptr0 len
  where
   loop _ 0 = return []
   loop p n = do
    v  <- unpack p
    let p' = addNCastPtr p szof
    vs <- loop p' (n-1)
    return (v:vs)

unmarshallSingle :: (Ptr a -> IO a) -> Ptr a -> IO [a]
unmarshallSingle ref ptr 
 | ptr == nullPtr = return []
 | otherwise      = do
      x <- ref ptr
      return [x]

writelist :: Bool -> Word32 -> (Ptr a -> a -> IO ()) -> Ptr [a] -> [a] -> IO ()
writelist do_alloc szof writeelt pptr ls = do
 the_ptr <- 
    (if do_alloc then do
        ptr <- alloc (szof * fromIntegral len)
        writePtr (castPtr pptr) ptr
        return (castPtr ptr)
      else
        return (castPtr pptr))
 foldM writeElt the_ptr ls
 return ()
  where
   len = length ls

   writeElt addr v = do
      writeelt addr v
      return (addNCastPtr addr szof)

readlist :: Word32 -> Word32 -> (Ptr a -> IO a) -> Ptr [a] -> IO [a]
readlist szof len unpack ptr = do
 let ptr0 = castPtr ptr
 loop ptr0 len
  where
   loop _ 0 = return []
   loop p n = do
    v  <- unpack p
    let p' = addNCastPtr p szof
    vs <- loop p' (n-1)
    return (v:vs)

freelist :: Word32 -> Word32 -> (Ptr a -> IO ()) -> Ptr [a] -> IO ()
freelist szof len free_elt ptr = do
    go (castPtr ptr) len
    free ptr
  where
    go _pptr 0 = return ()
    go pptr  x = do
       p <- readPtr pptr
       free_elt p
       let pptr' = addNCastPtr pptr szof
       go pptr' (x-1)

--Unpacking null terminated character strings:

marshallString :: String -> IO (Ptr String)
marshallString str = do
 pstr  <- alloc (len*sizeofChar)
 pstr' <- foldM writeElt (castPtr pstr) str
 writeChar (castPtr pstr') '\0'
 return pstr
  where
   len = fromIntegral (length str + 1)

   writeElt addr v = do
     writeChar addr v
     return (addNCastPtr addr sizeofChar)

marshallBString :: Int -> String -> IO (Ptr String)
marshallBString len str = do
 pstr  <- alloc (len'*sizeofChar)
 pstr' <- foldM writeElt (castPtr pstr) (take len str)
 writeChar (castPtr pstr') '\0'
 return pstr
  where
   len' = fromIntegral (len + 1)

   writeElt addr v = do
     writeChar addr v
     return (addNCastPtr addr sizeofChar)

unmarshallString :: Ptr String -> IO String
unmarshallString ptr
 | ptr == nullPtr  = return ""
 | otherwise       = do
   let ptr0 = addNCastPtr ptr 0
   loop ptr0
  where
   loop p = do
    v  <- readChar p
    if v == '\0'
     then return []
     else do
       let p' = addNCastPtr p sizeofChar
       vs <- loop p'
       return (v:vs)

-- at most len chars. or zero terminated.
unmarshallBString :: Int -> Ptr String -> IO String
unmarshallBString len ptr
 | ptr == nullPtr  = return ""
 | otherwise       = do
   let ptr0 = addNCastPtr ptr 0
   loop ptr0 0
  where
   loop _ n | n > len = return ""
   loop p n = do
    v  <- readChar p
    if v == '\0'
     then return []
     else do
       let p' = addNCastPtr p sizeofChar
       vs <- loop p' (n+1)
       return (v:vs)

readString :: Ptr (Ptr String) -> IO String
readString pstr = do
  ptr <- readPtr pstr
  unmarshallString ptr

readBString :: Int -> Ptr (Ptr String) -> IO String
readBString len pstr = do
  ptr <- readPtr pstr
  unmarshallBString len ptr

writeString :: Bool -> Ptr String -> String -> IO ()
writeString do_alloc ppstr str = do
  pstr <-
    if not do_alloc then
       return (castPtr ppstr)
     else do
       arr <- alloc (fromIntegral string_len)
       writePtr (castPtr ppstr) arr
       return arr
  pstr' <- foldM writeElt (castPtr pstr) str
  writeChar (castPtr pstr') '\0'
 where
   string_len = length str + 1 {- terminator -}

   writeElt addr v = do
     writeChar addr v
     return (addNCastPtr addr sizeofChar)

writeBString :: Bool -> Int -> Ptr a -> String -> IO ()
writeBString do_alloc len ptr str = do
  pstr  <-
    if do_alloc then
        alloc (fromIntegral len * sizeofChar)
    else
        return (castPtr ptr)
  pstr' <- foldM writeElt pstr (take len str)
  writeChar pstr' '\0'
 where
   writeElt addr v = do
     writeChar addr v
     return (addNCastPtr addr sizeofChar)

freeString :: Ptr String -> IO ()
freeString = free

--Sequence marshallers - i.e., R/W a sequence of values to/from a list.

marshallSequence :: (Ptr a -> a -> IO ())
         -> (Ptr a -> IO ())
         -> Word32
         -> Maybe Word32
         -> [a]
         -> IO (Ptr a)
marshallSequence wElt wTermin szElt mbLen ls = do
   pseq  <- alloc (len*szElt) -- assume that the sequence is packed without gaps.
   pseq' <- foldM writeElt pseq the_ls
   wTermin pseq'
   return pseq'
  where
    (len, the_ls) = 
      case mbLen of
        Nothing -> (fromIntegral (length ls + 1), ls)
        Just x  -> (x + 1, take (fromIntegral x) ls)

    writeElt addr v = do
      wElt addr v
      return (addNCastPtr addr szElt)

unmarshallSequence :: ( Eq a )
           => (Ptr (Ptr a) -> IO a)
           -> (Ptr (Ptr a) -> IO Bool)
           -> Word32
           -> Maybe Word32
           -> Ptr (Ptr a)
           -> IO [a]
unmarshallSequence rElt termPred szElt mbLen ptr
 | ptr == nullPtr  = return []
 | otherwise       = do
   let ptr0 = addNCastPtr ptr 0
   loop 0 ptr0
  where
   lenPred = 
     case mbLen of
       Nothing -> const False
       Just x  -> \ y -> y >= x

   loop len p = do
    flg <- termPred p
    if flg || (lenPred len)
     then return []
     else do
       v  <- rElt p
       let p' = addNCastPtr p szElt
       vs <- loop (len+1) p'
       return (v:vs)

readSequence :: ( Eq a )
         => (Ptr (Ptr a) -> IO a)
         -> (Ptr (Ptr a) -> IO Bool)
         -> Word32
         -> Maybe Word32
         -> Ptr (Ptr a)
         -> IO [a]
readSequence rElt termPred szElt mbLen ptr = do
  ptr' <- readPtr ptr
  unmarshallSequence rElt termPred szElt mbLen (castPtr ptr')

writeSequence :: ( Eq a )
          => Bool
          -> (Ptr a -> a -> IO ())
          -> (Ptr a -> IO ())
          -> Word32
          -> Maybe Word32
          -> Ptr a 
          -> [a] 
          -> IO ()
writeSequence do_alloc wElt wTermin szElt mbLen ppseq ls = do
  pseq <-
    if not do_alloc then
       return (castPtr ppseq)
     else do
       arr <- alloc (szElt * seq_len)
       writePtr (castPtr ppseq) arr
       return arr
  pseq' <- foldM writeElt pseq the_ls
  wTermin pseq'
 where
   (seq_len, the_ls) = 
      case mbLen of
        Nothing -> (fromIntegral (length ls + 1), ls)
        Just x  -> (x + 1, take (fromIntegral x) ls)

   writeElt addr v = do
     wElt addr v
     return (addNCastPtr addr szElt)

freeSequence :: Ptr a -> IO ()
freeSequence = free

-- at most len elements
marshallblist :: Word32 -> Word32 -> (Ptr a -> a -> IO ()) -> [a] -> IO (Ptr [a])
marshallblist szof l writeelt ls = do
 arr <- alloc (l'*szof)
 foldM writeElt (castPtr arr) ls
 return arr
  where
   l' = atLeast l (fromIntegral (0::Int)) ls

   atLeast len  n _ | len == n = len
   atLeast _len n [] = n
   atLeast len  n (_:xs) = atLeast len (n+1) xs

   writeElt addr v = do
    writeelt addr v
    return (addNCastPtr addr szof)

writeblist :: Word32 -> Word32 -> (Ptr a -> a -> IO ()) -> Ptr [a] -> [a] -> IO ()
writeblist szof len writeelt ptr ls = do
 foldM writeElt (castPtr ptr) (take (fromIntegral len) ls)
 return ()
  where

   writeElt addr v = do
    writeelt addr v
    return (addNCastPtr addr szof)

readblist :: Word32 -> Word32 -> (Ptr a -> IO a) -> Ptr a -> IO [a]
readblist szof len unpack ptr = do
 let ptr0 = castPtr ptr
 loop ptr0 len
  where
   loop _p 0 = return []
   loop p n  = do
    v  <- unpack p
    let p' = addNCastPtr p szof
    vs <- loop p' (n-1)
    return (v:vs)


--Misc coercion functions/shortcuts:
--(ToDo: try to avoid generating them in the first place!)

word16ToInt32 :: Word16 -> Int32
word16ToInt32 w = fromIntegral w -- intToInt32 (word16ToInt w)

-- This coercion is relying on no exceptions being thrown if
-- the Word32 > (maxBound::Int32).
word32ToInt32 :: Word32 -> Int32
word32ToInt32 w = fromIntegral w

-- This coercion is reling on no exceptions being thrown if w < 0.
int32ToWord32 :: Int32 -> Word32
int32ToWord32 w = fromIntegral w

int16ToWord32 :: Int16 -> Word32
int16ToWord32 w = fromIntegral w -- intToWord32 (int16ToInt w)

intToChar :: Int -> Char
intToChar = chr

charToInt32 :: Char -> Int32
charToInt32 c = fromIntegral (ord c)

word32ToChar :: Word32 -> Char
word32ToChar w = chr (fromIntegral w)

charToWord32 :: Char -> Word32
charToWord32 c = fromIntegral (ord c)


toInt32 :: Integral a => a -> Int32
toInt32 i = fromIntegral i -- intToInt32 (toInt i)

toInt16 :: Integral a => a -> Int16
toInt16 i = fromIntegral i -- intToInt16 (toInt i)

--ForeignPtr marshallers:

marshallFO :: ForeignPtr a -> IO (ForeignPtr a)
marshallFO x = return x

unmarshallFO :: ForeignPtr a -> IO (ForeignPtr a)
unmarshallFO x = return x

writeFO :: Ptr (ForeignPtr a) -> ForeignPtr a -> IO ()
writeFO ptr fo = poke (castPtr ptr) (foreignPtrToPtr fo)

-- a C pointer, really.
sizeofForeignPtr :: Word32
sizeofForeignPtr = sizeofPtr

nullFinaliser :: FunPtr (Ptr a -> IO ())
nullFinaliser = castFunPtr (castPtrToFunPtr finalNoFree)

nullFO :: ForeignPtr a
nullFO = unsafePerformIO $ makeFO nullPtr nullFinaliser

readStablePtr :: Ptr (StablePtr a) -> IO (StablePtr a)
readStablePtr ptr = peek ptr

--Default by-value 'marshallers' for structs and unions.

marshallStruct :: String -> a -> IO b
marshallStruct nm _ = ioError (userError (nm ++ ": Marshalling structs by value is not supported yet."))

unmarshallStruct :: String -> a -> IO c
unmarshallStruct nm _ = ioError (userError (nm ++ ": Unmarshalling structs by value is not supported yet."))

marshallUnion :: String -> a -> IO b
marshallUnion nm _ = ioError (userError (nm ++ ": Marshalling unions by value is not supported yet."))

unmarshallUnion :: String -> a -> b -> IO c
unmarshallUnion nm _ _ = ioError (userError (nm ++ ": Unmarshalling unions by value is not supported yet."))

--Ptr marshallers:

marshallPointer :: Ptr a -> IO (Ptr a)
marshallPointer p = return p

unmarshallPointer :: Ptr a -> IO (Ptr a)
unmarshallPointer ptr = return ptr --makeNoFreePtr ptr

writePointer :: Ptr (Ptr a) -> Ptr a -> IO ()
writePointer = poke

readPointer :: Ptr (Ptr a) -> IO (Ptr a)
readPointer ptr = peek ptr

sizeofPtr :: Word32
sizeofPtr  = fromIntegral (sizeOf (undefined :: Foreign.Ptr.Ptr ()))

primInvokeIt :: (Ptr b -> Ptr a -> IO c) -> Int -> IO (Ptr a) -> IO c
primInvokeIt meth offset mk_obj_ptr = do
  obj_ptr <- mk_obj_ptr 
  lpVtbl  <- derefPtr (castPtr obj_ptr)
  methPtr <- indexPtr lpVtbl offset
  meth methPtr obj_ptr

primInvokeItFO :: (Ptr b -> Ptr a -> IO c) -> Int -> IO (ForeignPtr a) -> IO c
primInvokeItFO meth offset mk_obj_ptr = do
  obj_ptr <- mk_obj_ptr
  lpVtbl  <- peek (foreignPtrToPtr (castForeignPtr obj_ptr))
  methPtr <- indexPtr lpVtbl offset
  withForeignPtr obj_ptr (\ optr -> meth methPtr optr)

stackStringLen :: Int -> String -> (Ptr String -> IO a) -> IO a
stackStringLen len str f
      = let slen = length str + 1 `max` len
        in stackFrame (fromIntegral slen) $ \pstr -> do 
          writeString False pstr str
          f pstr

enumToFlag :: Enum a => a -> Int
enumToFlag tg = fromIntegral ((1::Word32) `shiftL` enumToInt tg)

enumToInt :: Enum a => a -> Int
enumToInt tg = I# (getTag tg)

flagToIntTag :: Int -> Int
flagToIntTag f | f < 0     = error "flagToIntTag: negative tag"
               | otherwise = go 0 f
   -- could've used Prelude.logBase, but don't need the precision it offers.
 where
    go n 0 = n
    go n 1 = n + 1
    go n x = go (n + 1) (x `div` 2)

unboxInt :: Int -> Int#
unboxInt (I# x#) = x#

toIntFlag :: Int -> Int -> Int
toIntFlag b v = fromIntegral ((1::Word32) `shiftL` (v + flagToIntTag b))

pow2Series :: Int -> Int32 -> [Int32]
pow2Series len start = take len (iterate double start)
 where
   double x
     | x == 0    = 1
     | otherwise = 2*x

orList :: [Int] -> Int
orList ls = fromIntegral (foldl (\ acc x -> (fromIntegral x) .|. acc) (0::Int32) ls)

orFlags :: (Num a,Flags a) => [a] -> a
orFlags ls = foldl (.+.) 0 ls

class Flags a where
  (.+.) :: a -> a -> a