use std::ffi::CString;
use std::ptr::null_mut;
use std::rc::Rc;
use crate::ffi;
use crate::parameter::{ParamGet, ParamSet};
use crate::util;
use crate::{Error, Result};
use util::AsPtr;
#[derive(Debug, Eq, PartialEq)]
pub(crate) struct UserAllocEnv {
ptr: *mut ffi::GRBenv,
}
impl AsPtr for UserAllocEnv {
type Ptr = ffi::GRBenv;
unsafe fn as_mut_ptr(&self) -> *mut ffi::GRBenv {
self.ptr
}
}
impl Drop for UserAllocEnv {
fn drop(&mut self) {
debug_assert!(!self.ptr.is_null());
unsafe { ffi::GRBfreeenv(self.ptr) };
self.ptr = null_mut();
}
}
pub struct Env {
user_allocated: Rc<UserAllocEnv>,
gurobi_allocated: Option<*mut ffi::GRBenv>,
}
impl AsPtr for Env {
type Ptr = ffi::GRBenv;
unsafe fn as_mut_ptr(&self) -> *mut Self::Ptr {
self.gurobi_allocated
.unwrap_or_else(|| self.user_allocated.as_mut_ptr())
}
}
pub struct EmptyEnv {
env: Env,
}
impl EmptyEnv {
pub fn get<P: ParamGet<V>, V>(&self, param: P) -> Result<V> {
self.env.get(param)
}
pub fn set<P: ParamSet<V>, V>(&mut self, param: P, value: V) -> Result<&mut Self> {
self.env.set(param, value)?;
Ok(self)
}
pub fn start(self) -> Result<Env> {
self.env
.check_apicall(unsafe { ffi::GRBstartenv(self.env.as_mut_ptr()) })?;
Ok(self.env)
}
}
impl Env {
thread_local!(pub(crate) static DEFAULT_ENV : Env = Env::new("gurobi.log").unwrap());
pub(crate) fn is_shared(&self) -> bool {
Rc::strong_count(&self.user_allocated) > 1 || Rc::weak_count(&self.user_allocated) > 0
}
unsafe fn new_user_allocated(ptr: *mut ffi::GRBenv) -> Env {
debug_assert!(!ptr.is_null());
Env {
user_allocated: Rc::new(UserAllocEnv { ptr }),
gurobi_allocated: None,
}
}
pub(crate) unsafe fn new_gurobi_allocated(original: &Env, ptr: *mut ffi::GRBenv) -> Env {
debug_assert!(!ptr.is_null());
Env {
user_allocated: Rc::clone(&original.user_allocated),
gurobi_allocated: Some(ptr),
}
}
pub fn empty() -> Result<EmptyEnv> {
let mut env = null_mut();
let err_code = unsafe { ffi::shims::empty_env(&mut env) };
if err_code != 0 {
return Err(Error::FromAPI(get_error_msg(env), err_code));
}
let env = unsafe { Env::new_user_allocated(env) };
Ok(EmptyEnv { env })
}
pub fn new(logfilename: &str) -> Result<Env> {
let mut env = null_mut();
let logfilename = CString::new(logfilename)?;
let error = unsafe { ffi::shims::load_env(&mut env, logfilename.as_ptr()) };
if error != 0 {
return Err(Error::FromAPI(get_error_msg(env), error));
}
Ok(unsafe { Env::new_user_allocated(env) })
}
pub fn get<P: ParamGet<V>, V>(&self, param: P) -> Result<V> {
param.get(self)
}
pub fn set<P: ParamSet<V>, V>(&mut self, param: P, value: V) -> Result<()> {
param.set(self, value)
}
pub fn read_params(&mut self, filename: &str) -> Result<()> {
let filename = CString::new(filename)?;
self.check_apicall(unsafe { ffi::GRBreadparams(self.as_mut_ptr(), filename.as_ptr()) })
}
pub fn write_params(&self, filename: &str) -> Result<()> {
let filename = CString::new(filename)?;
self.check_apicall(unsafe { ffi::GRBwriteparams(self.as_mut_ptr(), filename.as_ptr()) })
}
pub fn message(&self, message: &str) {
let msg = CString::new(message).unwrap();
unsafe { ffi::GRBmsg(self.as_mut_ptr(), msg.as_ptr()) };
}
pub(crate) fn error_from_api(&self, error: ffi::c_int) -> Error {
Error::FromAPI(get_error_msg(unsafe { self.as_mut_ptr() }), error)
}
pub(crate) fn check_apicall(&self, error: ffi::c_int) -> Result<()> {
if error != 0 {
return Err(self.error_from_api(error));
}
Ok(())
}
}
fn get_error_msg(env: *mut ffi::GRBenv) -> String {
unsafe { util::copy_c_str(ffi::GRBgeterrormsg(env)) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{param, Model};
#[test]
fn param_get_set() {
use super::*;
let mut env = Env::new("").unwrap();
env.set(param::IISMethod, 1).unwrap();
assert_eq!(env.get(param::IISMethod).unwrap(), 1);
env.set(param::IISMethod, 0).unwrap();
assert_eq!(env.get(param::IISMethod).unwrap(), 0);
assert!(env.set(param::IISMethod, 9999).is_err());
}
#[test]
fn default_env_created_once() -> Result<()> {
let m1 = Model::new("m1")?;
let m2 = Model::new("m2")?;
assert_eq!(&m1.get_env().user_allocated, &m2.get_env().user_allocated);
Ok(())
}
}