use std::marker::PhantomData;
use std::ptr::null;
use std::{fmt, slice, str};
use libc::{c_char, size_t};
#[repr(C)]
pub struct rustls_slice_bytes<'a> {
pub data: *const u8,
pub len: size_t,
phantom: PhantomData<&'a [u8]>,
}
impl<'a> Default for rustls_slice_bytes<'a> {
fn default() -> rustls_slice_bytes<'a> {
Self {
data: &[0u8; 0] as *const u8,
len: 0,
phantom: PhantomData,
}
}
}
impl<'a> From<&'a [u8]> for rustls_slice_bytes<'a> {
fn from(s: &[u8]) -> Self {
rustls_slice_bytes {
data: s.as_ptr(),
len: s.len(),
phantom: PhantomData,
}
}
}
#[test]
fn test_rustls_slice_bytes() {
let bytes = b"abcd";
let rsb: rustls_slice_bytes = bytes.as_ref().into();
unsafe {
assert_eq!(*rsb.data, b'a');
assert_eq!(*rsb.data.offset(3), b'd');
assert_eq!(rsb.len, 4);
}
}
pub struct rustls_slice_slice_bytes<'a> {
pub(crate) inner: &'a [&'a [u8]],
}
#[no_mangle]
pub extern "C" fn rustls_slice_slice_bytes_len(input: *const rustls_slice_slice_bytes) -> size_t {
match unsafe { input.as_ref() } {
Some(c) => c.inner.len(),
None => 0,
}
}
#[no_mangle]
pub extern "C" fn rustls_slice_slice_bytes_get(
input: *const rustls_slice_slice_bytes,
n: size_t,
) -> rustls_slice_bytes {
let input = {
match unsafe { input.as_ref() } {
Some(c) => c,
None => {
return rustls_slice_bytes {
data: null(),
len: 0,
phantom: PhantomData,
};
}
}
};
match input.inner.get(n) {
Some(rsb) => (*rsb).into(),
None => rustls_slice_bytes {
data: null(),
len: 0,
phantom: PhantomData,
},
}
}
#[test]
fn test_rustls_slice_slice_bytes() {
let many_bytes: Vec<&[u8]> = vec![b"abcd", b"", b"xyz"];
let rssb = rustls_slice_slice_bytes { inner: &many_bytes };
assert_eq!(rustls_slice_slice_bytes_len(&rssb), 3);
assert_eq!(rustls_slice_slice_bytes_get(&rssb, 0).len, 4);
assert_eq!(rustls_slice_slice_bytes_get(&rssb, 1).len, 0);
assert_ne!(rustls_slice_slice_bytes_get(&rssb, 1).data, null());
assert_eq!(rustls_slice_slice_bytes_get(&rssb, 2).len, 3);
assert_eq!(rustls_slice_slice_bytes_get(&rssb, 3).len, 0);
assert_eq!(rustls_slice_slice_bytes_get(&rssb, 3).data, null());
unsafe {
assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 0).data, b'a');
assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 0).data.offset(3), b'd');
assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 2).data, b'x');
assert_eq!(*rustls_slice_slice_bytes_get(&rssb, 2).data.offset(2), b'z');
}
}
#[repr(C)]
pub struct rustls_str<'a> {
pub data: *const c_char,
pub len: size_t,
phantom: PhantomData<&'a str>,
}
#[derive(Debug)]
pub struct NulByte {}
impl<'a> TryFrom<&'a str> for rustls_str<'a> {
type Error = NulByte;
fn try_from(s: &str) -> Result<Self, Self::Error> {
if s.contains('\0') {
return Err(NulByte {});
}
Ok(rustls_str {
data: s.as_ptr() as *const c_char,
len: s.len(),
phantom: PhantomData,
})
}
}
impl Default for rustls_str<'_> {
fn default() -> rustls_str<'static> {
Self::from_str_unchecked("")
}
}
impl rustls_str<'_> {
pub fn from_str_unchecked(s: &'static str) -> rustls_str<'static> {
rustls_str {
data: s.as_ptr() as *const _,
len: s.len(),
phantom: PhantomData,
}
}
pub unsafe fn into_static(self) -> rustls_str<'static> {
std::mem::transmute(self)
}
pub unsafe fn to_str(&self) -> &str {
str::from_utf8_unchecked(slice::from_raw_parts(self.data as *const u8, self.len))
}
}
impl fmt::Debug for rustls_str<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let raw = unsafe {
slice::from_raw_parts(self.data as *const u8, self.len)
};
let s = str::from_utf8(raw).unwrap_or("%!(ERROR)");
f.debug_struct("rustls_str")
.field("data", &s)
.field("len", &self.len)
.finish()
}
}
#[test]
fn test_rustls_str() {
let s = "abcd";
let rs: rustls_str = s.try_into().unwrap();
assert_eq!(rs.len, 4);
unsafe {
assert_eq!(*rs.data, 'a' as c_char);
assert_eq!(*rs.data.offset(3), 'd' as c_char);
}
let rs = unsafe { rs.to_str() };
assert_eq!(rs, s);
}
#[cfg(test)]
mod tests {
use crate::rslice::*;
#[test]
fn test_rustls_str_debug() {
let s = "abcd";
let rs: rustls_str = s.try_into().unwrap();
assert_eq!(format!("{rs:?}"), r#"rustls_str { data: "abcd", len: 4 }"#);
}
}
#[test]
fn test_rustls_str_rejects_nul() {
assert!(matches!(rustls_str::try_from("\0"), Err(NulByte {})));
assert!(matches!(rustls_str::try_from("abc\0"), Err(NulByte {})));
assert!(matches!(rustls_str::try_from("ab\0cd"), Err(NulByte {})));
}
pub struct rustls_slice_str<'a> {
pub(crate) inner: &'a [&'a str],
}
#[no_mangle]
pub extern "C" fn rustls_slice_str_len(input: *const rustls_slice_str) -> size_t {
unsafe {
match input.as_ref() {
Some(c) => c.inner.len(),
None => 0,
}
}
}
#[no_mangle]
pub extern "C" fn rustls_slice_str_get(input: *const rustls_slice_str, n: size_t) -> rustls_str {
let input: &rustls_slice_str = unsafe {
match input.as_ref() {
Some(c) => c,
None => {
return rustls_str {
data: null(),
len: 0,
phantom: PhantomData,
};
}
}
};
input
.inner
.get(n)
.and_then(|&s| s.try_into().ok())
.unwrap_or(rustls_str {
data: null(),
len: 0,
phantom: PhantomData,
})
}
#[test]
fn test_rustls_slice_str() {
let many_strings = vec!["abcd", "", "xyz"];
let rss = rustls_slice_str {
inner: &many_strings,
};
assert_eq!(rustls_slice_str_len(&rss), 3);
assert_eq!(rustls_slice_str_get(&rss, 0).len, 4);
assert_eq!(rustls_slice_str_get(&rss, 1).len, 0);
assert_ne!(rustls_slice_str_get(&rss, 1).data, null());
assert_eq!(rustls_slice_str_get(&rss, 2).len, 3);
assert_eq!(rustls_slice_str_get(&rss, 3).len, 0);
assert_eq!(rustls_slice_str_get(&rss, 3).data, null());
unsafe {
assert_eq!(*rustls_slice_str_get(&rss, 0).data, 'a' as c_char);
assert_eq!(*rustls_slice_str_get(&rss, 0).data.offset(3), 'd' as c_char);
assert_eq!(*rustls_slice_str_get(&rss, 2).data, 'x' as c_char);
assert_eq!(*rustls_slice_str_get(&rss, 2).data.offset(2), 'z' as c_char);
}
}
#[repr(C)]
pub struct rustls_slice_u16<'a> {
pub data: *const u16,
pub len: size_t,
phantom: PhantomData<&'a [u16]>,
}
impl<'a> From<&'a [u16]> for rustls_slice_u16<'a> {
fn from(s: &[u16]) -> Self {
rustls_slice_u16 {
data: s.as_ptr(),
len: s.len(),
phantom: PhantomData,
}
}
}
#[test]
fn test_rustls_slice_u16() {
let u16s = vec![101, 314, 2718];
let rsu: rustls_slice_u16 = (&*u16s).into();
assert_eq!(rsu.len, 3);
unsafe {
assert_eq!(*rsu.data, 101);
assert_eq!(*rsu.data.offset(1), 314);
assert_eq!(*rsu.data.offset(2), 2718);
}
}