#![crate_name="ctypes"]
#![crate_type="lib"]
#![feature(globs, phase)]
#![no_std]
#![allow(non_camel_case_types)]


//! `ctypes`, the integer typedef madness
//!
//! The following are defined:
//!
//! register size: `ireg`/`ureg` and pointer size: `imem`/`umem`
//! These are not the same and have no defined relation:
//! - 16-bit can have `ireg:16 imem:32` (i.e. far pointers)
//! - x32 has `ireg:64 imem:32`, 
//! - 128-bit systems will probably use `ireg:128 imem:64`.
//!
//! The C types 
//! `ichar`/`uchar`, 
//! `ishort`/`ushort`, 
//! `iint`/`uint_`, (`uint` once the Rust primitive changes name. See the various RFCs.)
//! `ilong`/`ulong`, 
//! `ilonglong`/`ulonglong`
//! `float` and `double`.
//!
//! `imax`/`umax` can hold any of the above.
//!
//! `void` is the opaque pointer target.


extern crate core;
#[cfg(test)] #[phase(plugin, link)] extern crate std;

#[cfg(target_word_size = "32")]
pub use lp32::*;

// Some systems did things differently, but those have been dead for at least a decade now.
#[cfg(all(unix, target_word_size = "64"))]
pub use lp64::*;

// Windows is special, as always: `long` is 32 even on 64-bit
#[cfg(all(target_word_size = "64", target_os = "windows"))]
pub use llp64::*;

// The Linux x32 ABI
#[cfg(all(target_os = "linux", x32abi))]
pub use x32::*;



/// Type used to construct void pointers for use with C.
///
/// This type is only useful as a pointer target. Do not use it as a
/// return type for FFI functions which have the `void` return type in
/// C. Use the unit type `()` or omit the return type instead.
///
/// For LLVM to recognize the void pointer type and by extension
/// functions like malloc(), we need to have it represented as i8* in
/// LLVM bitcode. The enum used here ensures this and prevents misuse
/// of the "raw" type by only having private variants.. We need two
/// variants, because the compiler complains about the repr attribute
/// otherwise.
#[repr(u8)]
#[allow(missing_copy_implementations)]
pub enum void {
    __variant1,
    __variant2,
}


// These are correct everywhere we compile right now.
// 16-bit and weird systems will need their own cfg magic.
mod common {
    pub type ichar      = i8;      pub type uchar       = u8;
    pub type iint       = i32;     pub type uint_       = u32;
    pub type ishort     = i16;     pub type ushort      = u16;
    pub type ilonglong  = i64;     pub type ulonglong   = u64;
    pub type float      = f32;     pub type double      = f64;
    pub type imax       = i64;     pub type umax        = u64;
}

pub mod lp32 {
    pub use common::*;
    pub type ilong = i32;   pub type ulong = u32;
    pub type imem  = i32;   pub type umem  = u32;
    pub type ireg  = i32;   pub type ureg  = u32;
}

pub mod lp64 {
    pub use common::*;
    pub type ilong = i64;   pub type ulong = u64;
    pub type imem  = i64;   pub type umem  = u64;
    pub type ireg  = i64;   pub type ureg  = u64;
}

pub mod llp64 {
    pub use common::*;
    pub type ilong = i32;   pub type ulong = u32;
    pub type imem  = i64;   pub type umem  = u64;
    pub type ireg  = i64;   pub type ureg  = u64;
}

pub mod x32 {
    pub use common::*;
    pub type ilong = i64;   pub type ulong = u64;
    pub type imem  = i32;   pub type umem  = u32;
    pub type ireg  = i64;   pub type ureg  = u64;
}

#[cfg(test)] mod tests {
    #[test]
    fn int_is_not_always_int() {
        use core::mem::size_of;
        use super::*;
        if cfg!(target_word_size = "64") {
            assert!(size_of::<iint>() != size_of::<int>());
        }
    }
}