#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
use std::ffi::{CStr, CString};
use std::{io, ptr::NonNull};
use libc::c_void;
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
use libc::{c_char, size_t};
use crate::ffi::{log::platform_log, r#async::WakerHandle};
const LOG_TAG: &str = "ffi";
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
extern "C" {
fn platform_create_socket() -> *mut SocketCHandle;
fn platform_socket_bind(
c_handle: *mut SocketCHandle,
af: i32,
local_ip: *const c_char,
local_port: u16,
) -> i32;
fn platform_socket_connect(
c_handle: *mut SocketCHandle,
af: i32,
remote_ip: *const c_char,
remote_port: u16,
) -> i32;
fn platform_socket_finish_connect(
c_handle: *mut SocketCHandle,
waker_handle: *mut c_void,
) -> i32; fn platform_read_socket(
c_handle: *mut SocketCHandle,
waker_handle: *mut c_void,
buffer: *mut u8,
buffer_len: size_t,
bytes_read: *mut size_t,
) -> i32; fn platform_write_socket(
c_handle: *mut SocketCHandle,
waker_handle: *mut c_void,
buffer: *const u8,
buffer_len: size_t,
bytes_written: *mut size_t,
) -> i32; fn platform_shutdown_socket(c_handle: *mut SocketCHandle, waker_handle: *mut c_void) -> i32; fn platform_close_socket(c_handle: *mut SocketCHandle);
fn platform_free_socket(c_handle: *mut SocketCHandle);
}
#[repr(C)]
pub struct SocketCHandle {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
pub struct SocketCHandleWrapper(NonNull<SocketCHandle>);
impl Drop for SocketCHandleWrapper {
fn drop(&mut self) {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
let c_handle = self.0.as_ptr();
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
unsafe {
platform_free_socket(c_handle);
}
}
}
unsafe impl Send for SocketCHandleWrapper {}
pub fn create_socket() -> io::Result<SocketCHandleWrapper> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
unsafe {
if let Some(c_handle) = platform_create_socket().as_mut() {
return Ok(SocketCHandleWrapper(NonNull::new(c_handle).unwrap()));
}
}
}
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn socket_bind(
c_socket: &SocketCHandleWrapper,
af: i32,
local_ip: &str,
local_port: u16,
) -> io::Result<()> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
let local_ip = CString::new(local_ip).unwrap();
let local_ip = local_ip.as_ptr();
unsafe {
let r = platform_socket_bind(c_socket, af, local_ip, local_port);
platform_log(LOG_TAG, format!("platform_socket_bind returns {}", r));
if r == 0 {
return Ok(());
} else if r == libc::EAGAIN || r == libc::EWOULDBLOCK {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
} else {
return Err(io::Error::from(io::ErrorKind::Other));
}
}
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn socket_connect(
c_socket: &SocketCHandleWrapper,
af: i32,
remote_ip: &str,
remote_port: u16,
) -> io::Result<()> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
let remote_ip = CString::new(remote_ip).unwrap();
let remote_ip = remote_ip.as_ptr();
unsafe {
let r = platform_socket_connect(c_socket, af, remote_ip, remote_port);
platform_log(LOG_TAG, format!("platform_socket_connect returns {}", r));
if r == 0 {
return Ok(());
} else if r == libc::EAGAIN || r == libc::EWOULDBLOCK {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
} else {
return Err(io::Error::from(io::ErrorKind::Other));
}
}
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn socket_finish_connect(
c_socket: &SocketCHandleWrapper,
waker: WakerHandle,
) -> io::Result<()> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
let waker = Box::new(waker);
let waker = Box::into_raw(waker);
let waker = waker as *mut c_void;
unsafe {
let r = platform_socket_finish_connect(c_socket, waker);
platform_log(
LOG_TAG,
format!("platform_socket_finish_connect returns {}", r),
);
if r == 0 {
return Ok(());
} else if r == libc::EALREADY {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
} else {
return Err(io::Error::from(io::ErrorKind::Other));
}
}
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn read_socket(
c_socket: &SocketCHandleWrapper,
buffer: &mut [u8],
waker: WakerHandle,
) -> io::Result<usize> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
let buffer_len = buffer.len();
let buffer = buffer.as_mut_ptr();
let waker = Box::new(waker);
let waker = Box::into_raw(waker);
let waker = waker as *mut c_void;
unsafe {
let mut bytes_read = 0;
let r = platform_read_socket(c_socket, waker, buffer, buffer_len, &mut bytes_read);
platform_log(
LOG_TAG,
format!(
"platform_read_socket returns {} with {} bytes read",
r, bytes_read
),
);
if r == 0 {
return Ok(bytes_read);
} else if r == libc::EAGAIN || r == libc::EWOULDBLOCK {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
} else {
return Err(io::Error::from(io::ErrorKind::Other));
}
}
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn write_socket(
c_socket: &SocketCHandleWrapper,
buffer: &[u8],
waker: WakerHandle,
) -> io::Result<usize> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
let buffer_len = buffer.len();
let buffer = buffer.as_ptr();
let waker = Box::new(waker);
let waker = Box::into_raw(waker);
let waker = waker as *mut c_void;
unsafe {
let mut bytes_written = 0;
let r = platform_write_socket(c_socket, waker, buffer, buffer_len, &mut bytes_written);
platform_log(
LOG_TAG,
format!(
"platform_write_socket returns {} with {} bytes written",
r, bytes_written
),
);
if r == 0 {
return Ok(bytes_written);
} else if r == libc::EAGAIN || r == libc::EWOULDBLOCK {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
} else {
return Err(io::Error::from(io::ErrorKind::Other));
}
}
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn shutdown_socket(c_socket: &SocketCHandleWrapper, waker: WakerHandle) -> io::Result<()> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
let waker = Box::new(waker);
let waker = Box::into_raw(waker);
let waker = waker as *mut c_void;
unsafe {
let r = platform_shutdown_socket(c_socket, waker);
platform_log(LOG_TAG, format!("platform_shutdown_socket returns {}", r));
if r == 0 {
return Ok(());
} else if r == libc::EALREADY {
return Err(io::Error::from(io::ErrorKind::WouldBlock));
} else {
return Err(io::Error::from(io::ErrorKind::Other));
}
}
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
Err(io::Error::from(io::ErrorKind::Unsupported))
}
pub fn close_socket(c_socket: &SocketCHandleWrapper) {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
{
let c_socket = c_socket.0.as_ptr();
unsafe {
platform_close_socket(c_socket);
}
}
}
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
extern "C" {
fn platform_get_socket_info(socket_c_handle: *mut SocketCHandle) -> *mut SocketInfoCHandle;
fn platform_get_socket_af(c_handle: *mut SocketInfoCHandle) -> i32;
fn platform_get_socket_l_addr(c_handle: *mut SocketInfoCHandle) -> *const c_char;
fn platform_get_socket_l_port(c_handle: *mut SocketInfoCHandle) -> u16;
fn platform_free_socket_info(c_handle: *mut SocketInfoCHandle);
}
#[repr(C)]
pub struct SocketInfoCHandle {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
pub struct SocketInfoCHandleWrapper(NonNull<SocketInfoCHandle>);
impl Drop for SocketInfoCHandleWrapper {
fn drop(&mut self) {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
let c_handle = self.0.as_ptr();
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
unsafe {
platform_free_socket_info(c_handle);
}
}
}
unsafe impl Send for SocketInfoCHandleWrapper {}
pub fn get_socket_info(android_socket: &SocketCHandleWrapper) -> Option<SocketInfoCHandleWrapper> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
let android_socket = android_socket.0.as_ptr();
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
unsafe {
if let Some(c_handle) = platform_get_socket_info(android_socket).as_mut() {
return Some(SocketInfoCHandleWrapper(NonNull::new(c_handle).unwrap()));
}
}
None
}
pub fn get_socket_af(android_socket_info: &SocketInfoCHandleWrapper) -> i32 {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
let android_socket_info = android_socket_info.0.as_ptr();
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
unsafe {
return platform_get_socket_af(android_socket_info);
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
0
}
pub fn get_socket_local_address(android_socket_info: &SocketInfoCHandleWrapper) -> Option<String> {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
let android_socket_info = android_socket_info.0.as_ptr();
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
unsafe {
if let Some(ptr) = platform_get_socket_l_addr(android_socket_info).as_ref() {
let str = CStr::from_ptr(ptr).to_string_lossy().into_owned();
return Some(str);
}
}
None
}
pub fn get_socket_local_port(android_socket_info: &SocketInfoCHandleWrapper) -> u16 {
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
let android_socket_info = android_socket_info.0.as_ptr();
#[cfg(all(feature = "ohos", all(target_os = "linux", target_env = "ohos")))]
unsafe {
return platform_get_socket_l_port(android_socket_info);
}
#[cfg(not(all(feature = "ohos", all(target_os = "linux", target_env = "ohos"))))]
0
}