GHC's Addr module is meant to be used in conjunction with the FFI (at
least this is what the docs told me :-), but its plethora of similar
functions is not very nice and some often needed functionality is
missing. Attached is my proposed new version of Addr, being very
similar to the things in the "Staying alive" thread. Some changes:

   * marshal.../unmarshal... are normal functions now and not methods
     of Marshalable anymore.

   * marshalList now returns the length of the list, too.

   * Some convenience functions for in/inout/out parameters are added.

   * After playing around with this module, I think that it is a
     Good Thing (tm) that ...OffAddr use element offsets and not byte
     offsets. It makes instance declarations of the following
     form much easier:

        instance Marshalable a => Marshalable (Foo a) where ...

     Apart from that, it is much more consistent with the old
     definitions.

Alas, almost nobody mailed his/her wishes for a marshaling library, so
this proposal is obviously biased towards HOpenGL's (and I think
Manuel's) needs.

Comments/suggestions?

Cheers,
   Sven
-- 
Sven Panne                                        Tel.: +49/89/2178-2235
LMU, Institut fuer Informatik                     FAX : +49/89/2178-2211
LFE Programmier- und Modellierungssprachen              Oettingenstr. 67
mailto:[EMAIL PROTECTED]            D-80538 Muenchen
http://www.informatik.uni-muenchen.de/~Sven.Panne
{- Hey Emacs, this is -*- haskell -*- !
   @configure_input@
This file was part of HOpenGL - a binding of OpenGL and GLUT for Haskell.
Copyright (C) 1999  Sven Panne <[EMAIL PROTECTED]>

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library (COPYING.LIB); if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

module Addr (
   Addr,
   nullAddr,          -- Addr
   plusAddr,          -- Addr -> Int -> Addr
   Marshalable(
      sizeOf,         -- a -> Int
      alignment,      -- a -> Int
      indexOffAddr,   -- Addr -> Int ->    a
      readOffAddr,    -- Addr -> Int -> IO a
      writeOffAddr),  -- Addr -> Int -> a -> IO ()
   marshal,           --  Marshalable a        =>       a  -> IO Addr
   marshalList,       --  Marshalable a        =>      [a] -> IO (Int, Addr),
   marshalListZero,   --  Marshalable a        => a -> [a] -> IO Addr
   unmarshal,         --  Marshalable a        =>        Addr -> IO  a
   unmarshalList,     --  Marshalable a        => Int -> Addr -> IO [a]
   unmarshalListZero, -- (Marshalable a, Eq a) => a   -> Addr -> IO [a]
   inParamWith,       -- (a -> IO Addr) ->                   (Addr -> IO b) -> a -> IO 
b
   inParam,           -- Marshalable a =>                    (Addr -> IO b) -> a -> IO 
b
   inOutParamWith,    -- (a -> IO Addr) -> (Addr -> IO a) -> (Addr -> IO b) -> a -> IO 
a
   inOutParam,        -- Marshalable a =>                    (Addr -> IO b) -> a -> IO 
a
   outParamWith,      -- (a -> Int)     -> (Addr -> IO a) -> (Addr -> IO b)      -> IO 
a
   outParam,          -- Marshalable a =>                    (Addr -> IO b)      -> IO 
a
   malloc,            -- Int  -> IO Addr
   free               -- Addr -> IO ()
) where

import Monad(when, zipWithM_)
import Addr
import Int
import Word

----------------------------------------------------------------------
-- Haskell equivalent of raw pointers

{- We get these from Addr
data Addr = ...
instance Eq   Addr where ...
instance Ord  Addr where ...
instance Show Addr where ...

nullAddr :: Addr
plusAddr :: Addr -> Int -> Addr
-}

-- replacement for intToAddr/addrToInt
instance Enum Addr where
   toEnum   = intToAddr
   fromEnum = addrToInt

----------------------------------------------------------------------
-- primitive marshaling

class Marshalable a where
   sizeOf       :: a -> Int
   alignment    :: a -> Int
   -- replacement for index-/read-/write???OffAddr
   indexOffAddr :: Addr -> Int ->    a
   readOffAddr  :: Addr -> Int -> IO a
   writeOffAddr :: Addr -> Int -> a -> IO ()

-- system-dependent, but rather obvious instances
instance Marshalable Char where
   sizeOf       = const @SIZEOF_CHAR@
   alignment    = const @ALIGNOF_CHAR@
   indexOffAddr = indexCharOffAddr
   readOffAddr  = readCharOffAddr
   writeOffAddr = writeCharOffAddr

instance Marshalable Int where
   sizeOf       = const @SIZEOF_INT@
   alignment    = const @ALIGNOF_INT@
   indexOffAddr = indexIntOffAddr
   readOffAddr  = readIntOffAddr
   writeOffAddr = writeIntOffAddr

instance Marshalable Addr where
   sizeOf       = const @SIZEOF_VOID_P@
   alignment    = const @ALIGNOF_VOID_P@
   indexOffAddr = indexAddrOffAddr
   readOffAddr  = readAddrOffAddr
   writeOffAddr = writeAddrOffAddr

instance Marshalable Float where
   sizeOf       = const @SIZEOF_FLOAT@
   alignment    = const @ALIGNOF_FLOAT@
   indexOffAddr = indexFloatOffAddr
   readOffAddr  = readFloatOffAddr
   writeOffAddr = writeFloatOffAddr

instance Marshalable Double where
   sizeOf       = const @SIZEOF_DOUBLE@
   alignment    = const @ALIGNOF_DOUBLE@
   indexOffAddr = indexDoubleOffAddr
   readOffAddr  = readDoubleOffAddr
   writeOffAddr = writeDoubleOffAddr

instance Marshalable Word8 where
   sizeOf       = const 1
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexWord8OffAddr
   readOffAddr  = readWord8OffAddr
   writeOffAddr = writeWord8OffAddr

instance Marshalable Word16 where
   sizeOf       = const 2
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexWord16OffAddr
   readOffAddr  = readWord16OffAddr
   writeOffAddr = writeWord16OffAddr

instance Marshalable Word32 where
   sizeOf       = const 4
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexWord32OffAddr
   readOffAddr  = readWord32OffAddr
   writeOffAddr = writeWord32OffAddr

instance Marshalable Word64 where
   sizeOf       = const 8
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexWord64OffAddr
   readOffAddr  = readWord64OffAddr
   writeOffAddr = writeWord64OffAddr

instance Marshalable Int8 where
   sizeOf       = const 1
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexInt8OffAddr
   readOffAddr  = readInt8OffAddr
   writeOffAddr = writeInt8OffAddr

instance Marshalable Int16 where
   sizeOf       = const 2
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexInt16OffAddr
   readOffAddr  = readInt16OffAddr
   writeOffAddr = writeInt16OffAddr

instance Marshalable Int32 where
   sizeOf       = const 4
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexInt32OffAddr
   readOffAddr  = readInt32OffAddr
   writeOffAddr = writeInt32OffAddr

instance Marshalable Int64 where
   sizeOf       = const 8
   alignment    = sizeOf   -- not sure about this
   indexOffAddr = indexInt64OffAddr
   readOffAddr  = readInt64OffAddr
   writeOffAddr = writeInt64OffAddr

----------------------------------------------------------------------
-- convenience functions for (un-)marshaling

-- Performance paranoia, one could use:
-- marshal x = marshalList [x]
marshal :: Marshalable a => a -> IO Addr
marshal x = do
   buf <- malloc (sizeOf x)
   writeOffAddr buf 0 x
   return buf

marshalList :: Marshalable a => [a] -> IO (Int, Addr)
marshalList xs = do
   let numElements = length xs
   buf <- malloc (numElements * sizeOf (head xs))
   zipWithM_ (writeOffAddr buf) [ 0 .. ] xs
   return (numElements, buf)

-- Performance paranoia, one could use:
-- marshalListZero zeroElem xs = marshalList (xs ++ [zeroElem])
marshalListZero ::  Marshalable a => a -> [a] -> IO Addr
marshalListZero zeroElem xs = do
   let numElements = length xs
   buf <- malloc ((numElements+1) * sizeOf (head xs))
   zipWithM_ (writeOffAddr buf) [ 0 .. ] xs
   writeOffAddr buf numElements zeroElem
   return buf

-- Performance paranoia, one could use:
-- unmarshal buf = liftM head $ unmarshalList 1 buf
unmarshal ::  Marshalable a => Addr -> IO a
unmarshal buf = readOffAddr buf 0

unmarshalList :: Marshalable a => Int -> Addr -> IO [a]
unmarshalList numElements buf =
   mapM (readOffAddr buf) [ 0 .. numElements-1 ]

unmarshalListZero :: (Marshalable a, Eq a) => a -> Addr -> IO [a]
unmarshalListZero zeroElem buf = loop 0 []
   where loop idx accu = do x <- readOffAddr buf idx
                            if x == zeroElem
                               then return $ reverse accu
                               else loop (idx+1) (x:accu)

----------------------------------------------------------------------

inParamWith :: (a -> IO Addr) -> (Addr -> IO b) -> a -> IO b
inParamWith marshal_ act x = do
   buf <- marshal_ x
   val <- act buf
   free buf
   return val

inParam :: Marshalable a => (Addr -> IO b) -> a -> IO b
inParam = inParamWith marshal

inOutParamWith :: (a -> IO Addr) -> (Addr -> IO a) -> (Addr -> IO b) -> a -> IO a
inOutParamWith marshal_ unmarshal_ act x = do
   buf <- marshal_ x
   act buf
   val <- unmarshal_ buf
   free buf
   return val

inOutParam :: Marshalable a => (Addr -> IO b) -> a -> IO a
inOutParam = inOutParamWith marshal unmarshal

outParamWith :: (a -> Int) -> (Addr -> IO a) -> (Addr -> IO b) -> IO a
outParamWith sizeOf_ unmarshal_ act = do
   buf <- malloc (sizeOf_ undefined)
   act buf
   m <- unmarshal_ buf
   free buf
   return m

outParam :: Marshalable a => (Addr -> IO b) -> IO a
outParam = outParamWith sizeOf unmarshal

----------------------------------------------------------------------
-- (de-)allocation of raw bytes

malloc :: Int -> IO Addr
malloc numBytes = do
   buf <- mallocAux numBytes
   when (buf == nullAddr)
        (ioError (userError ("malloc(" ++ show numBytes ++ ") failed")))
   return buf

-- Hmmm, Int is a little bit strange here, C uses size_t
foreign import ccall "malloc" unsafe mallocAux :: Int  -> IO Addr
foreign import ccall "free"   unsafe free      :: Addr -> IO ()

Reply via email to