use std;
use std::ops::{Deref, DerefMut};
use std::ffi::CString;
use std::io::Read;
use std::fs::File;
use std::path::PathBuf;
use std::collections::HashSet;
use std::convert::Into;
use error::{Result as OclResult, Error as OclError};
use core::{self, Program as ProgramCore, Context as ContextCore,
ProgramInfo, ProgramInfoResult, ProgramBuildInfo, ProgramBuildInfoResult};
use standard::{Context, Device, DeviceSpecifier};
#[derive(Clone, Debug)]
pub enum BuildOpt {
CmplrDefine { ident: String, val: String },
CmplrInclDir { path: String },
CmplrOther(String),
IncludeDefine { ident: String, val: String },
IncludeRaw(String),
IncludeRawEof(String),
}
impl BuildOpt {
pub fn cmplr_def<S: Into<String>>(ident: S, val: i32) -> BuildOpt {
BuildOpt::CmplrDefine {
ident: ident.into(),
val: val.to_string(),
}
}
pub fn include_def<S: Into<String>>(ident: S, val: String) -> BuildOpt {
BuildOpt::IncludeDefine {
ident: ident.into(),
val: val,
}
}
}
#[derive(Clone, Debug)]
pub struct ProgramBuilder {
options: Vec<BuildOpt>,
src_files: Vec<PathBuf>,
device_spec: Option<DeviceSpecifier>,
}
impl ProgramBuilder {
pub fn new() -> ProgramBuilder {
ProgramBuilder {
options: Vec::with_capacity(64),
src_files: Vec::with_capacity(16),
device_spec: None,
}
}
pub fn build(&self, context: &Context) -> OclResult<Program> {
let device_list = match self.device_spec {
Some(ref ds) => try!(ds.to_device_list(context.platform())),
None => vec![],
};
if device_list.len() == 0 {
return OclError::err("ocl::ProgramBuilder::build: No devices found.");
}
Program::new(
try!(self.get_src_strings().map_err(|e| e.to_string())),
try!(self.get_compiler_options().map_err(|e| e.to_string())),
context,
&device_list[..])
}
pub fn cmplr_def<S: Into<String>>(mut self, name: S, val: i32) -> ProgramBuilder {
self.options.push(BuildOpt::cmplr_def(name, val));
self
}
pub fn cmplr_opt<S: Into<String>>(mut self, co: S) -> ProgramBuilder {
self.options.push(BuildOpt::CmplrOther(co.into()));
self
}
pub fn bo(mut self, bo: BuildOpt) -> ProgramBuilder {
self.options.push(bo);
self
}
pub fn src_file<P: Into<PathBuf>>(mut self, file_path: P) -> ProgramBuilder {
let file_path = file_path.into();
assert!(file_path.is_file(), "ProgramBuilder::src_file(): Source file error: \
'{}' does not exist.", file_path.display());
self.src_files.push(file_path);
self
}
pub fn src<S: Into<String>>(mut self, src: S) -> ProgramBuilder {
self.options.push(BuildOpt::IncludeRawEof(src.into()));
self
}
pub fn devices<D: Into<DeviceSpecifier>>(mut self, device_spec: D)
-> ProgramBuilder
{
assert!(self.device_spec.is_none(), "ocl::ProgramBuilder::devices(): Devices already specified");
self.device_spec = Some(device_spec.into());
self
}
pub fn get_device_spec(&self) -> &Option<DeviceSpecifier> {
&self.device_spec
}
pub fn get_compiler_options(&self) -> OclResult<CString> {
let mut opts: Vec<String> = Vec::with_capacity(64);
opts.push(" ".to_owned());
for option in self.options.iter() {
match option {
&BuildOpt::CmplrDefine { ref ident, ref val } => {
opts.push(format!("-D{}={}", ident, val))
},
&BuildOpt::CmplrInclDir { ref path } => {
opts.push(format!("-I{}", path))
},
&BuildOpt::CmplrOther(ref s) => {
opts.push(s.clone())
},
_ => (),
}
}
CString::new(opts.join(" ").into_bytes()).map_err(|err| OclError::from(err))
}
pub fn get_src_strings(&self) -> OclResult<Vec<CString>> {
let mut src_strings: Vec<CString> = Vec::with_capacity(64);
let mut src_file_history: HashSet<PathBuf> = HashSet::with_capacity(64);
src_strings.extend_from_slice(&try!(self.get_includes()));
for srcpath in self.src_files.iter() {
let mut src_bytes: Vec<u8> = Vec::with_capacity(100000);
if src_file_history.contains(srcpath) { continue; }
src_file_history.insert(srcpath.clone());
let mut src_file_handle = try!(File::open(srcpath));
try!(src_file_handle.read_to_end(&mut src_bytes));
src_bytes.shrink_to_fit();
src_strings.push(try!(CString::new(src_bytes)));
}
src_strings.extend_from_slice(&try!(self.get_includes_eof()));
Ok(src_strings)
}
fn get_includes(&self) -> OclResult<Vec<CString>> {
let mut strings = Vec::with_capacity(64);
strings.push(try!(CString::new("\n".as_bytes())));
for option in self.options.iter() {
match option {
&BuildOpt::IncludeDefine { ref ident, ref val } => {
strings.push(try!(CString::new(format!("#define {} {}\n", ident, val)
.into_bytes())));
},
&BuildOpt::IncludeRaw(ref text) => {
strings.push(try!(CString::new(text.clone().into_bytes())));
},
_ => (),
};
}
Ok(strings)
}
fn get_includes_eof(&self) -> OclResult<Vec<CString>> {
let mut strings = Vec::with_capacity(64);
strings.push(try!(CString::new("\n".as_bytes())));
for option in self.options.iter() {
match option {
&BuildOpt::IncludeRawEof(ref text) => {
strings.push(try!(CString::new(text.clone().into_bytes())));
},
_ => (),
};
}
Ok(strings)
}
}
#[derive(Clone, Debug)]
pub struct Program {
obj_core: ProgramCore,
devices: Vec<Device>,
}
impl Program {
pub fn builder() -> ProgramBuilder {
ProgramBuilder::new()
}
pub fn new(src_strings: Vec<CString>, cmplr_opts: CString, context_obj_core: &ContextCore,
device_ids: &[Device]) -> OclResult<Program>
{
let obj_core = try!(core::create_build_program(context_obj_core, &src_strings, &cmplr_opts,
device_ids).map_err(|e| e.to_string()));
Ok(Program {
obj_core: obj_core,
devices: Vec::from(device_ids),
})
}
pub fn core_as_ref(&self) -> &ProgramCore {
&self.obj_core
}
pub fn devices(&self) -> &[Device] {
&self.devices
}
pub fn info(&self, info_kind: ProgramInfo) -> ProgramInfoResult {
core::get_program_info(&self.obj_core, info_kind)
}
pub fn build_info(&self, device: Device, info_kind: ProgramBuildInfo) -> ProgramBuildInfoResult {
core::get_program_build_info(&self.obj_core, &device, info_kind)
}
fn fmt_info(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Program")
.field("ReferenceCount", &self.info(ProgramInfo::ReferenceCount))
.field("Context", &self.info(ProgramInfo::Context))
.field("NumDevices", &self.info(ProgramInfo::NumDevices))
.field("Devices", &self.info(ProgramInfo::Devices))
.field("Source", &self.info(ProgramInfo::Source))
.field("BinarySizes", &self.info(ProgramInfo::BinarySizes))
.field("Binaries", &self.info(ProgramInfo::Binaries))
.field("NumKernels", &self.info(ProgramInfo::NumKernels))
.field("KernelNames", &self.info(ProgramInfo::KernelNames))
.finish()
}
}
impl std::fmt::Display for Program {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt_info(f)
}
}
impl Deref for Program {
type Target = ProgramCore;
fn deref(&self) -> &ProgramCore {
&self.obj_core
}
}
impl DerefMut for Program {
fn deref_mut(&mut self) -> &mut ProgramCore {
&mut self.obj_core
}
}