--  C->Haskell Compiler: information about the C implementation
--
--  Author : Manuel M T Chakravarty
--  Created: 5 February 01
--
--  Version $Revision: 1.2 $ from $Date: 2005/01/16 21:31:21 $
--
--  Copyright (c) 2001 Manuel M T Chakravarty
--
--  This file is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This file 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 General Public License for more details.
--
--- DESCRIPTION ---------------------------------------------------------------
--
--  This module provide some information about the specific implementation of
--  C that we are dealing with.
--
--- DOCU ----------------------------------------------------------------------
--
--  language: Haskell 98
--
--  Bit fields
--  ~~~~~~~~~~
--  Bit fields in C can be signed and unsigned.  According to K&R A8.3, they
--  can only be formed from `int', `signed int', and `unsigned int', where for
--  `int' it is implementation dependent whether the field is signed or
--  unsigned.  Moreover, the following parameters are implementation
--  dependent:
--
--  * the direction of packing bits into storage units,
--  * the size of storage units, and
--  * whether when a field that doesn't fit a partially filled storage unit
--    is split across units or the partially filled unit is padded.
--
--  Generally, unnamed fields (those without an identifier) with a width of 0
--  are guaranteed to forces the above padding.  Note that in `CPrimType' we
--  only represent 0 width fields *if* they imply padding.  In other words,
--  whenever they are unnamed, they are represented by a `CPrimType', and if
--  they are named, they are represented by a `CPrimType' only if that
--  targeted C compiler chooses to let them introduce padding.  If a field
--  does not have any effect, it is dropped during the conversion of a C type
--  into a `CPrimType'-based representation.
--
--  In the code, we assume that the alignment of a bitfield (as determined by
--  `bitfieldAlignment') is independent of the size of the bitfield.
--
--- TODO ----------------------------------------------------------------------
--

module CInfo (
  CPrimType(..), size, alignment,
  bitfieldDirection, bitfieldPadding, bitfieldIntSigned, bitfieldAlignment
) where

import Foreign.C

-- we can't rely on the compiler used to compile c2hs already having the new
-- FFI, so this is system dependent
--
import C2HSConfig (Ptr, FunPtr,
                   bitfieldDirection, bitfieldPadding, bitfieldIntSigned,
                   bitfieldAlignment)
import qualified
       C2HSConfig as Storable
                  (Storable(sizeOf, alignment))


-- calibration of C's primitive types
-- ----------------------------------

-- C's primitive types (EXPORTED)
--
--  * `CFunPtrPT' doesn't occur in Haskell representations of C types, but we
--   need to know their size, which may be different from `CPtrPT'
--
data CPrimType = CPtrPT         -- void *
               | CFunPtrPT      -- void *()
               | CCharPT        -- char
               | CUCharPT       -- unsigned char
               | CSCharPT       -- signed char
               | CIntPT         -- int
               | CShortPT       -- short int
               | CLongPT        -- long int
               | CLLongPT       -- long long int
               | CUIntPT        -- unsigned int
               | CUShortPT      -- unsigned short int
               | CULongPT       -- unsigned long int
               | CULLongPT      -- unsigned long long int
               | CFloatPT       -- float
               | CDoublePT      -- double
               | CLDoublePT     -- long double
               | CSFieldPT  Int -- signed bit field
               | CUFieldPT  Int -- unsigned bit field
               deriving (Eq)

-- size of primitive type of C (EXPORTED)
--
--  * negative size implies that it is a bit, not an octet size
--
size                :: CPrimType -> Int
size CPtrPT          = Storable.sizeOf (undefined :: Ptr ())
size CFunPtrPT       = Storable.sizeOf (undefined :: FunPtr ())
size CCharPT         = 1
size CUCharPT        = 1
size CSCharPT        = 1
size CIntPT          = Storable.sizeOf (undefined :: CInt)
size CShortPT        = Storable.sizeOf (undefined :: CShort)
size CLongPT         = Storable.sizeOf (undefined :: CLong)
size CLLongPT        = Storable.sizeOf (undefined :: CLLong)
size CUIntPT         = Storable.sizeOf (undefined :: CUInt)
size CUShortPT       = Storable.sizeOf (undefined :: CUShort)
size CULongPT        = Storable.sizeOf (undefined :: CULong)
size CULLongPT       = Storable.sizeOf (undefined :: CLLong)
size CFloatPT        = Storable.sizeOf (undefined :: CFloat)
size CDoublePT       = Storable.sizeOf (undefined :: CDouble)
--size CLDoublePT      = Storable.sizeOf (undefined :: CLDouble)
size (CSFieldPT bs)  = -bs
size (CUFieldPT bs)  = -bs

-- alignment of C's primitive types (EXPORTED)
--
--  * more precisely, the padding put before the type's member starts when the
--   preceding component is a char
--
alignment                :: CPrimType -> Int
alignment CPtrPT          = Storable.alignment (undefined :: Ptr ())
alignment CFunPtrPT       = Storable.alignment (undefined :: FunPtr ())
alignment CCharPT         = 1
alignment CUCharPT        = 1
alignment CSCharPT        = 1
alignment CIntPT          = Storable.alignment (undefined :: CInt)
alignment CShortPT        = Storable.alignment (undefined :: CShort)
alignment CLongPT         = Storable.alignment (undefined :: CLong)
alignment CLLongPT        = Storable.alignment (undefined :: CLLong)
alignment CUIntPT         = Storable.alignment (undefined :: CUInt)
alignment CUShortPT       = Storable.alignment (undefined :: CUShort)
alignment CULongPT        = Storable.alignment (undefined :: CULong)
alignment CULLongPT       = Storable.alignment (undefined :: CULLong)
alignment CFloatPT        = Storable.alignment (undefined :: CFloat)
alignment CDoublePT       = Storable.alignment (undefined :: CDouble)
--alignment CLDoublePT      = Storable.alignment (undefined :: CLDouble)
alignment (CSFieldPT bs)  = fieldAlignment bs
alignment (CUFieldPT bs)  = fieldAlignment bs

-- alignment constraint for a C bitfield
--
--  * gets the bitfield size (in bits) as an argument
--
--  * alignments constraints smaller or equal to zero are reserved for bitfield
--   alignments
--
--  * bitfields of size 0 always trigger padding; thus, they get the maximal
--   size
--
--  * if bitfields whose size exceeds the space that is still available in a
--   partially filled storage unit trigger padding, the size of a storage unit
--   is provided as the alignment constraint; otherwise, it is 0 (meaning it
--   definitely starts at the current position)
--
--  * here, alignment constraint /= 0 are somewhat subtle; they mean that is
--   the given number of bits doesn't fit in what's left in the current
--   storage unit, alignment to the start of the next storage unit has to be
--   triggered
--
fieldAlignment                      :: Int -> Int
fieldAlignment 0                     = - (size CIntPT - 1)
fieldAlignment bs | bitfieldPadding  = - bs
                  | otherwise        = 0