#![allow(non_camel_case_types)]
pub mod ports {
use libc::c_char;
use haiku_sys::*;
use std::ffi::{CStr, CString};
use std::mem;
use std::time::Duration;
use kernel::teams::Team;
use support::{ErrorKind, HaikuError, Result};
pub struct Port {
port: port_id,
owned: bool
}
pub struct PortInfo {
pub team: Team,
pub name: String,
pub capacity: i32,
pub queue_count: i32,
pub total_count: i32
}
impl Port {
pub fn create(name: &str, capacity: i32) -> Result<Port> {
if name.len() > B_OS_NAME_LENGTH {
return Err(HaikuError::new(ErrorKind::InvalidInput, "The name is too long"));
}
let c_name = CString::new(name).unwrap();
let port = unsafe { create_port(capacity, c_name.as_ptr()) };
if port < 0 {
Err(HaikuError::from_raw_os_error(port))
} else {
Ok(Port {
port: port,
owned: true
})
}
}
pub fn find(name: &str) -> Option<Port> {
if name.len() > B_OS_NAME_LENGTH {
return None;
}
let c_name = CString::new(name).unwrap();
let port = unsafe { find_port(c_name.as_ptr()) };
if port < 0 {
None
} else {
Some(Port {
port: port,
owned: false
})
}
}
pub fn from_id(id: port_id) -> Option<Port> {
if id < 0 {
return None;
}
let mut info: port_info = unsafe { mem::zeroed() };
let status = unsafe {
get_port_info(id, &mut info)
};
if status == 0 {
Some(Port {
port: id,
owned: false
})
} else {
None
}
}
pub fn write(&self, type_code: i32, data: &[u8]) -> Result<()>{
let status = unsafe {
write_port(self.port, type_code, data.as_ptr(), data.len() as usize)
};
if status == 0 {
Ok(())
} else {
Err(HaikuError::from_raw_os_error(status))
}
}
pub fn try_write(&self, type_code: i32, data: &[u8], timeout: Duration) -> Result<()>{
let timeout_ms = timeout.as_secs() as i64 * 1_000_000 + timeout.subsec_micros() as i64;
let status = unsafe {
write_port_etc(self.port, type_code, data.as_ptr(), data.len() as usize,
B_TIMEOUT, timeout_ms)
};
if status == 0 {
Ok(())
} else {
Err(HaikuError::from_raw_os_error(status))
}
}
pub fn read(&self) -> Result<(i32, Vec<u8>)> {
if !self.owned {
panic!("You are trying to read from a port that you do not own. This is not allowed");
}
let size = unsafe { port_buffer_size(self.port) };
if size < 0 {
return Err(HaikuError::from_raw_os_error(size as i32));
}
let mut dst = Vec::with_capacity(size as usize);
let pdst = dst.as_mut_ptr();
let mut type_code: i32 = 0;
let dst_len = unsafe {
read_port(self.port, &mut type_code, pdst, size as usize)
};
if dst_len > 0 && dst_len != size {
panic!("read_port does not return data with the predicted size");
}
if dst_len < 0 {
Err(HaikuError::from_raw_os_error(dst_len as i32))
} else {
unsafe { dst.set_len(dst_len as usize); };
Ok((type_code, dst))
}
}
pub fn try_read(&self, timeout: Duration) -> Result<(i32, Vec<u8>)> {
if !self.owned {
panic!("You are trying to read from a port that you do not own. This is not allowed");
}
let timeout_ms = timeout.as_secs() as i64 * 1_000_000 + timeout.subsec_micros() as i64;
let size = unsafe {
port_buffer_size_etc(self.port, B_TIMEOUT, timeout_ms)
};
if size < 0 {
return Err(HaikuError::from_raw_os_error(size as i32));
}
let mut dst = Vec::with_capacity(size as usize);
let pdst = dst.as_mut_ptr();
let mut type_code: i32 = 0;
let dst_len = unsafe {
read_port_etc(self.port, &mut type_code, pdst, size as usize,
B_TIMEOUT, timeout_ms)
};
if dst_len > 0 && dst_len != size {
panic!("read_port does not return data with the predicted size");
}
if dst_len < 0 {
Err(HaikuError::from_raw_os_error(dst_len as i32))
} else {
unsafe { dst.set_len(dst_len as usize); };
Ok((type_code, dst))
}
}
pub fn close(&self) -> Result<()> {
if !self.owned {
panic!("You are trying to close a port that you do not own. This is not allowed");
}
let status = unsafe { close_port(self.port) };
if status == 0 {
Ok(())
} else {
Err(HaikuError::from_raw_os_error(status))
}
}
pub fn get_count(&self) -> Result<usize> {
let status = unsafe {
port_count(self.port)
};
if status < 0 {
Err(HaikuError::from_raw_os_error(status as i32))
} else {
Ok(status as usize)
}
}
pub fn get_info(&self) -> Result<PortInfo> {
let mut info: port_info = unsafe { mem::zeroed() };
let status = unsafe {
get_port_info(self.port, &mut info)
};
if status != 0 {
Err(HaikuError::from_raw_os_error(status))
} else {
let c_name = unsafe {
CStr::from_ptr((&info.name) as *const c_char)
};
Ok(PortInfo{
team: Team::from(info.team).unwrap(),
name: String::from(c_name.to_str().unwrap()),
capacity: info.capacity,
queue_count: info.queue_count,
total_count: info.total_count
})
}
}
pub fn get_port_id(&self) -> port_id{
self.port
}
}
impl Clone for Port {
fn clone(&self) -> Self {
Port { port: self.port, owned: false }
}
}
impl Drop for Port {
fn drop(&mut self) {
if self.owned {
unsafe { delete_port(self.port); };
}
}
}
}
pub mod teams {
use haiku_sys::*;
pub struct Team {
id: team_id
}
impl Team {
pub fn from(id: team_id) -> Option<Team> {
if id < 0 {
None
} else {
Some(Team{ id })
}
}
pub fn get_team_id(&self) -> team_id {
self.id
}
}
}
use std::time::Duration;
pub const INFINITE_TIMEOUT: Duration = Duration::from_micros(i64::max_value() as u64);
pub(crate) mod helpers {
use std::ffi::CStr;
use std::str;
use haiku_sys::*;
use libc::{c_char, dev_t, ino_t, size_t};
use support::{Result, HaikuError};
pub(crate) fn get_path_for_entry_ref(device: dev_t, dir: ino_t, leaf: *const c_char) -> Result<String> {
extern {
pub fn _kern_entry_ref_to_path(device: dev_t, inode: ino_t, leaf: *const c_char, buf: *mut c_char, bufferSize: size_t) -> status_t;
}
let mut buf = [0 as c_char; B_PATH_NAME_LENGTH];
let p = buf.as_mut_ptr();
let path = unsafe {
let result = _kern_entry_ref_to_path(device, dir, leaf, p, buf.len());
if result != 0 {
return Err(HaikuError::from_raw_os_error(result));
}
let p = p as *const _;
str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
};
Ok(path)
}
}
pub fn debugger(message: &str) {
use libc::c_char;
use std::ffi::CString;
extern {
fn debugger(message: *const c_char);
}
let msg = CString::new(message).unwrap();
unsafe { debugger(msg.as_ptr()) };
}
#[test]
fn test_basic_port() {
use kernel::ports::Port;
let port = Port::create("test_basic_port", 16).unwrap();
let port_data = b"testdata for port\n";
let port_code: i32 = 47483658;
port.write(port_code, port_data).unwrap();
assert_eq!(port.get_count().unwrap(), 1);
let (read_code, read_data) = port.read().unwrap();
assert_eq!(port_code, read_code);
assert_eq!(port_data.len(), read_data.len());
port.close().unwrap();
assert!(port.write(port_code, port_data).is_err());
}
#[test]
fn test_port_with_timeout() {
use kernel::ports::Port;
use std::time::Duration;
let port = Port::create("timeout_port", 1).unwrap();
assert!(port.try_read(Duration::new(5,0)).is_err());
}
#[test]
fn test_find_port() {
use kernel::ports::Port;
assert!(Port::find("x-vnd.haiku-debug_server").is_some());
assert!(Port::find("random port").is_none());
}