From ca780e71ef329c8dd63d6dd00a451ae0d6aacd83 Mon Sep 17 00:00:00 2001 From: Jason Travis Smith Date: Fri, 6 Jan 2017 19:10:10 -0500 Subject: [PATCH] Initial library commit. --- .gitignore | 17 ++++ Cargo.toml | 13 +++ README.md | 5 ++ src/c_enum.rs | 110 +++++++++++++++++++++++ src/c_flags.rs | 232 ++++++++++++++++++++++++++++++++++++++++++++++++ src/c_string.rs | 61 +++++++++++++ src/lib.rs | 20 +++++ src/raw.rs | 45 ++++++++++ 8 files changed, 503 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/c_enum.rs create mode 100644 src/c_flags.rs create mode 100644 src/c_string.rs create mode 100644 src/lib.rs create mode 100644 src/raw.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3853ea9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Ignore swap files from text editors. +*.swp + +# Ignore compiled files. +*.o +*.so +*.rlib +*.dll +*.exe + +# Ignore files/directories generated by Cargo. +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, +# leave it for libraries. +# More information here: http://doc.crates.io/guide.html#cargotoml-vs-cargolock +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c2a54d0 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "binding" +version = "0.1.0" +authors = ["Jason Travis Smith "] +description = "Defines macros and functions used when binding to C libraries." +license = "" +repository = "https://gitlab.com/CyberMages/binding.git" +documentation = "" +keywords = ["binding", "c types", "c enums", "c flags", "c strings"] + + +[dependencies.scribe] +git = "https://gitlab.com/CyberMages/scribe.git" diff --git a/README.md b/README.md new file mode 100644 index 0000000..459fe96 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Binding # +There are many common data types and functions used when interfacing +with C libraries. The Binding library attempts to define the more commonly +needed types and functions to help speed up the process of wrapping +these libraries. diff --git a/src/c_enum.rs b/src/c_enum.rs new file mode 100644 index 0000000..43d3cb7 --- /dev/null +++ b/src/c_enum.rs @@ -0,0 +1,110 @@ +/// Create a C exportable enum. These are useful for working with C FFIs. +/// +/// As C has no idea about tuples or structs enums +/// using this macro must only contain Unit variants. +#[macro_export] +macro_rules! c_enum +{ + { + $(#[$attribute: meta])* enum $name: ident : $fieldType: ty + { + $( + $(#[$variantAttribute: meta])* + variant $variant: ident = $value: expr + ),* + } + } => + { + $(#[$attribute])* + #[repr(C)] + pub enum $name + { + $($(#[$variantAttribute])* $variant = $value,)* + } + + impl $name + { + /// Generate a variant of the enum from a given value. + /// + /// This is not very performant. Becareful when using this + /// to not use this in a high performance loop. + pub fn from_value(val: $fieldType) -> Option<$name> + { + // This would be better as a match statement, but + // unfortunately, it needs to be a giant set of if + // statements since we only have the expression type + // to work with, not a pattern type. + /* + match val + { + $($value => {Some($name::$variant)})* + + _ => {None} + } + */ + $(if val == $value {return Some($name::$variant);})* + + // No variant was found. + None + } + + /// 'true', if the given value matches a variant + /// of the enumeration; Otherwise, 'false'. + pub fn is_valid_value(val: $fieldType) -> bool + { + match $name::from_value(val) + { + Some(_) => + { + true + } + + None => + { + false + } + } + } + + /// Turn an enum variant into a value. + pub fn to_value(&self) -> $fieldType + { + match *self + { + $($name::$variant => {$value})* + } + } + + /// Get a str representation of this variant. + pub fn to_str(&self) -> &'static str + { + match *self + { + $($name::$variant => {stringify!($variant)})* + } + } + + /// Get a String representation of this variant. + pub fn to_string(&self) -> String + { + String::from(self.to_str()) + } + } + + impl ::std::fmt::Debug for $name + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + write!(f, "{}", self.to_str()) + } + } + + impl ::std::fmt::Display for $name + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + write!(f, "{}", self.to_str()) + } + } + } +} diff --git a/src/c_flags.rs b/src/c_flags.rs new file mode 100644 index 0000000..38447b6 --- /dev/null +++ b/src/c_flags.rs @@ -0,0 +1,232 @@ +/// Creates a Flag type that is also very useful for working with C FFIs. +#[macro_export] +macro_rules! c_flags +{ + { + $(#[$attribute: meta])* flags $name: ident : $fieldType: ty + { + $( + $(#[$flagAttribute: meta])* + const $flag: ident = $value: expr + ),+ + } + } => + { + // Create the structure that will represent the bitflag. + // Derive the basic needed functionality and + // add any required extra attributes. + $(#[$attribute])* + #[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] + pub struct $name + { + bits: $fieldType + } + + + $($(#[$flagAttribute])* pub const $flag: $name = $name {bits: $value};)+ + + + impl $name + { + /// Create a new default version of the + /// bitflag structure. + pub fn new() -> $name + { + $name::empty() + } + + /// Create a new bitflag structure from + /// the given bits. + pub fn from_bits(bits: $fieldType) -> Option<$name> + { + if bits & !$name::all().get_bits() != 0 + { + None + } + else + { + Some($name {bits: bits}) + } + } + + /// Returns an empty set of flags. + #[inline] + pub fn empty() -> $name + { + $name {bits: 0} + } + + /// Returns the set containing all flags. + pub fn all() -> $name + { + $name {bits: $($flag.bits)|+} + } + + /// Returns `true` if no flags are currently stored; + /// Otherwise, `false` is returned. + pub fn is_empty(&self) -> bool + { + *self == $name::empty() + } + + /// Returns `true` if all the flags are currently set; + /// Otherwise, `false` is returned. + pub fn is_all(&self) -> bool + { + *self == $name::all() + } + + /// Returns the raw value of the flags currently stored. + pub fn get_bits(&self) -> $fieldType + { + self.bits + } + + /// Turn the flag into a String representation of its self. + #[warn(unused_assignments)] + pub fn to_string(&self) -> String + { + let mut first: bool; + let mut string: String; + + // Push the left bracket onto the string. + string = String::new(); + string.push_str("{"); + + // Handle checking each flag to see if it + // is part of this flag. + first = true; + $( + if self.intersects($flag) == true + { + // If this is not the first flag, + // then add an OR symbol. + if first == false + { + string.push_str(" | "); + } + + // Push the flags name onto the string. + string.push_str(stringify!($flag)); + first = false; + } + )+ + + // No flags were set. + if first == true + { + // Put a space in the string. + string.push_str(" "); + } + + // Push the right bracket onto the string + // and return it. + string.push_str("}"); + string + } + + /// Returns `true` if there are flags common to + /// both `self` and `other`; Otherwise, `false` is returned. + pub fn intersects(&self, other: $name) -> bool + { + !(*self & other).is_empty() + } + + /// Returns `true` all of the flags in `other` are + /// contained within `self`; Otherwise, `false` is returned. + pub fn contains(&self, other: $name) -> bool + { + (*self & other) == other + } + + /// Inserts the specified flags in-place. + pub fn insert(&mut self, other: $name) + { + self.bits |= other.bits; + } + + /// Removes the specified flags in-place. + pub fn remove(&mut self, other: $name) + { + self.bits &= !other.bits; + } + + /// Toggles the specified flags in-place. + pub fn toggle(&mut self, other: $name) + { + self.bits ^= other.bits; + } + } + + impl ::std::ops::BitOr for $name + { + type Output = $name; + + /// Returns the union of the two sets of flags. + fn bitor(self, other: $name) -> $name + { + $name {bits: self.bits | other.bits} + } + } + + impl ::std::ops::BitXor for $name + { + type Output = $name; + + /// Returns the left flags, but with all the right flags toggled. + fn bitxor(self, other: $name) -> $name + { + $name {bits: self.bits ^ other.bits} + } + } + + impl ::std::ops::BitAnd for $name + { + type Output = $name; + + /// Returns the intersection between the two sets of flags. + fn bitand(self, other: $name) -> $name + { + $name {bits: self.bits & other.bits} + } + } + + impl ::std::ops::Sub for $name + { + type Output = $name; + + /// Returns the set difference of the two sets of flags. + fn sub(self, other: $name) -> $name + { + $name {bits: self.bits & !other.bits} + } + } + + impl ::std::ops::Not for $name + { + type Output = $name; + + /// Returns the complement of this set of flags. + fn not(self) -> $name + { + $name {bits: !self.bits} & $name::all() + } + } + + impl ::std::fmt::Debug for $name + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + write!(f, "{}", self.to_string()) + } + } + + impl ::std::fmt::Display for $name + { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result + { + write!(f, "{}", self.to_string()) + } + } + } +} diff --git a/src/c_string.rs b/src/c_string.rs new file mode 100644 index 0000000..95fe0a9 --- /dev/null +++ b/src/c_string.rs @@ -0,0 +1,61 @@ +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + + + +/// Defines an empty string. +pub const EMPTY_STRING: &'static str = ""; + + + +/// Creates a new String from the data in the pointer. +/// +/// Any conversion errors will result in an Empty +/// String being returned. Option is not used here for +/// simplicities sake. +pub fn from_c_string(c_string: *const c_char) -> String +{ + unsafe + { + CStr::from_ptr(c_string).to_string_lossy().into_owned() + } +} + +/// Creates a CString from a given str. +/// +/// This may be "" if there is an UTF8 conversion error. +/// If it can not create an empty CString, then it will panic. +pub fn to_c_string

(string: P) -> CString + where P: AsRef +{ + // Try to create a CString from the given str. + match CString::new(string.as_ref()) + { + Ok(c_string) => + { + // Return the new CString. + c_string + } + + Err(error) => + { + warn!("Unable to create a CString from the given str: {}", error); + + // Try to atleast make an empty CString. + match CString::new(EMPTY_STRING) + { + Ok(c_string) => + { + // Return the new CString. + c_string + } + + Err(final_error) => + { + // This is a show stopping error. + error!("Unable to create an empty CString: {}", final_error); + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..19795a1 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +//! The Binding library is a place where common macros and functions +//! that are used when interfacing with C libraries can be stored +//! for use amoungst all of CyberMages LLC's projects. +#[macro_use] +extern crate scribe; + + + +// Basic C type macro modules. +mod c_enum; +mod c_flags; +mod c_string; + +// Raw platform data retrieval. +mod raw; + + + +pub use self::c_string::{EMPTY_STRING, from_c_string, to_c_string}; +pub use self::raw::{AsRaw, AsRawMut, AsRawPtr, AsRawMutPtr, FromRaw, IntoRaw}; diff --git a/src/raw.rs b/src/raw.rs new file mode 100644 index 0000000..93929ec --- /dev/null +++ b/src/raw.rs @@ -0,0 +1,45 @@ +/// Some times there are types that need to return a platform specific +/// data type that can be used by the platform specific functions. This +/// type creates a path to easily get that data. +pub trait AsRaw where T: Sized +{ + fn as_raw(&self) -> &T; +} + +/// Some times there are types that need to return a platform specific +/// data type that can be used by the platform specific functions. This +/// type creates a path to easily get that data in a mutable manner. +pub trait AsRawMut where T: Sized +{ + fn as_raw_mut(&mut self) -> &mut T; +} + +/// Some times there are types that need to return a platform specific +/// data type that can be used by the platform specific functions. This +/// type creates a path to easily get that data as a raw const pointer. +pub trait AsRawPtr where T: Sized +{ + fn as_raw_ptr(&self) -> *const T; +} + +/// Some times there are types that need to return a platform specific +/// data type that can be used by the platform specific functions. This +/// type creates a path to easily get that data as a raw mutable pointer. +pub trait AsRawMutPtr where T: Sized +{ + fn as_raw_mut_ptr(&mut self) -> *mut T; +} + +/// This makes it possible to create an object from a given piece of platform +/// specific data. +pub trait FromRaw +{ + fn from_raw(raw: T) -> Self; +} + +/// This makes it possible to convert an object into platform +/// specific data. +pub trait IntoRaw +{ + fn into_raw(self) -> T; +}