#![allow(unused)]
use ::core::{
ffi::{c_int, c_void},
ptr::{NonNull, null_mut},
};
use ::libc::{
CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_SPACE, EAGAIN, EBADMSG, EINTR, EINVAL, ENOBUFS,
ERANGE, SCM_RIGHTS, SOL_SOCKET, abort, c_uchar, calloc, close, cmsghdr, free, iovec, memcpy,
memset, msghdr, sendmsg, writev,
};
use super::imsg::{ibuf, msgbuf};
use super::queue::{
tailq_first, tailq_foreach, tailq_init, tailq_insert_tail, tailq_next, tailq_remove,
};
use super::{freezero, recallocarray::recallocarray};
use crate::errno;
const IOV_MAX: usize = 1024;
pub unsafe fn ibuf_open(len: usize) -> *mut ibuf {
unsafe {
if len == 0 {
errno!() = EINVAL;
return null_mut();
}
let buf: *mut ibuf = calloc(1, size_of::<ibuf>()) as *mut ibuf;
if buf.is_null() {
return null_mut();
}
(*buf).buf = calloc(len, 1) as *mut c_uchar;
if (*buf).buf.is_null() {
free(buf as *mut c_void);
return null_mut();
}
(*buf).max = len;
(*buf).size = len;
(*buf).fd = -1;
buf
}
}
pub unsafe fn ibuf_dynamic(len: usize, max: usize) -> *mut ibuf {
unsafe {
if len == 0 || max < len {
errno!() = EINVAL;
return null_mut();
}
let buf: *mut ibuf = calloc(1, size_of::<ibuf>()) as *mut ibuf;
if buf.is_null() {
return null_mut();
}
if len > 0 {
(*buf).buf = calloc(len, 1) as *mut c_uchar;
if (*buf).buf.is_null() {
free(buf as *mut c_void);
return null_mut();
}
}
(*buf).max = len;
(*buf).size = len;
(*buf).fd = -1;
buf
}
}
pub unsafe fn ibuf_realloc(buf: *mut ibuf, len: usize) -> i32 {
unsafe {
if len > usize::MAX - (*buf).wpos || (*buf).wpos + len > (*buf).max {
errno!() = ERANGE;
return -1;
}
let b = recallocarray((*buf).buf as *mut c_void, (*buf).size, (*buf).wpos + len, 1);
if b.is_null() {
return -1;
}
(*buf).buf = b as *mut u8;
(*buf).size = (*buf).wpos + len;
0
}
}
pub unsafe fn ibuf_reserve(buf: *mut ibuf, len: usize) -> *mut c_void {
unsafe {
if len > usize::MAX - (*buf).wpos || (*buf).max == 0 {
errno!() = ERANGE;
return null_mut();
}
if (*buf).wpos + len > (*buf).size && ibuf_realloc(buf, len) == -1 {
return null_mut();
}
let b = (*buf).buf.add((*buf).wpos);
(*buf).wpos += len;
b as *mut c_void
}
}
pub unsafe fn ibuf_add(buf: *mut ibuf, data: *const c_void, len: usize) -> i32 {
unsafe {
let b = ibuf_reserve(buf, len);
if b.is_null() {
return -1;
}
memcpy(b, data, len);
0
}
}
pub unsafe fn ibuf_add_ibuf(buf: *mut ibuf, from: *const ibuf) -> c_int {
unsafe { ibuf_add(buf, ibuf_data(from), ibuf_size(from)) }
}
pub unsafe fn ibuf_add_buf(buf: *mut ibuf, from: *const ibuf) -> c_int {
unsafe { ibuf_add_ibuf(buf, from) }
}
pub unsafe fn ibuf_add_n8(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u8::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value;
ibuf_add(buf, &raw const v as _, size_of::<u8>())
}
}
pub unsafe fn ibuf_add_n16(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = (value as u16).to_be();
ibuf_add(buf, &raw const v as _, size_of::<u16>())
}
}
pub unsafe fn ibuf_add_n32(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = (value as u32).to_be();
ibuf_add(buf, &raw const v as _, size_of::<u32>())
}
}
pub unsafe fn ibuf_add_n64(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
let v = value.to_be();
ibuf_add(buf, &raw const v as _, size_of::<u64>())
}
}
pub unsafe fn ibuf_add_h16(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u16;
ibuf_add(buf, &raw const v as _, size_of::<u16>())
}
}
pub unsafe fn ibuf_add_h32(buf: *mut ibuf, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u32;
ibuf_add(buf, &raw const v as _, size_of::<u32>())
}
}
pub unsafe fn ibuf_add_h64(buf: *mut ibuf, value: u64) -> c_int {
unsafe { ibuf_add(buf, &raw const value as *const c_void, size_of::<u64>()) }
}
pub unsafe fn ibuf_add_zero(buf: *mut ibuf, len: usize) -> c_int {
unsafe {
let b: *mut c_void = ibuf_reserve(buf, len);
if b.is_null() {
return -1;
}
memset(b, 0, len);
0
}
}
pub unsafe fn ibuf_seek(buf: *mut ibuf, pos: usize, len: usize) -> *mut c_void {
unsafe {
if ibuf_size(buf) < pos || usize::MAX - pos < len || ibuf_size(buf) < pos + len {
errno!() = ERANGE;
return null_mut();
}
(*buf).buf.add((*buf).rpos + pos) as _
}
}
pub unsafe fn ibuf_set(buf: *mut ibuf, pos: usize, data: *const c_void, len: usize) -> c_int {
unsafe {
let b = ibuf_seek(buf, pos, len);
if b.is_null() {
return -1;
}
memcpy(b, data, len);
0
}
}
pub unsafe fn ibuf_set_n8(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u8::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u8;
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u8>())
}
}
pub unsafe fn ibuf_set_n16(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = u16::to_be(value as u16);
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u16>())
}
}
pub unsafe fn ibuf_set_n32(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = u32::to_be(value as u32);
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u32>())
}
}
pub unsafe fn ibuf_set_n64(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
let v = u64::to_be(value);
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u64>())
}
}
pub unsafe fn ibuf_set_h16(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u16::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u16;
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u16>())
}
}
pub unsafe fn ibuf_set_h32(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
if value > u32::MAX as u64 {
errno!() = EINVAL;
return -1;
}
let v = value as u32;
ibuf_set(buf, pos, &raw const v as *const c_void, size_of::<u32>())
}
}
pub unsafe fn ibuf_set_h64(buf: *mut ibuf, pos: usize, value: u64) -> c_int {
unsafe {
ibuf_set(
buf,
pos,
&raw const value as *const c_void,
size_of::<u64>(),
)
}
}
pub unsafe fn ibuf_data(buf: *const ibuf) -> *mut c_void {
unsafe { (*buf).buf.add((*buf).rpos) as *mut c_void }
}
pub unsafe fn ibuf_size(buf: *const ibuf) -> usize {
unsafe { (*buf).wpos - (*buf).rpos }
}
pub unsafe fn ibuf_left(buf: *const ibuf) -> usize {
unsafe {
if (*buf).max == 0 {
return 0;
}
(*buf).max - (*buf).wpos
}
}
pub unsafe fn ibuf_truncate(buf: *mut ibuf, len: usize) -> c_int {
unsafe {
if ibuf_size(buf) >= len {
(*buf).wpos = (*buf).rpos + len;
return 0;
}
if (*buf).max == 0 {
errno!() = ERANGE;
return -1;
}
ibuf_add_zero(buf, len - ibuf_size(buf))
}
}
pub unsafe fn ibuf_rewind(buf: *mut ibuf) {
unsafe {
(*buf).rpos = 0;
}
}
pub unsafe fn ibuf_close(msgbuf: *mut msgbuf, buf: *mut ibuf) {
unsafe {
ibuf_enqueue(msgbuf, buf);
}
}
pub unsafe fn ibuf_from_buffer(buf: *mut ibuf, data: *mut c_void, len: usize) {
unsafe {
memset(buf as _, 0, size_of::<ibuf>());
(*buf).buf = data as _;
(*buf).wpos = len;
(*buf).size = len;
(*buf).fd = -1;
}
}
pub unsafe fn ibuf_from_ibuf(buf: *mut ibuf, from: *const ibuf) {
unsafe {
ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from));
}
}
pub unsafe fn ibuf_get(buf: *mut ibuf, data: *mut c_void, len: usize) -> c_int {
unsafe {
if ibuf_size(buf) < len {
errno!() = EBADMSG;
return -1;
}
memcpy(data, ibuf_data(buf), len);
(*buf).rpos += len;
0
}
}
pub unsafe fn ibuf_get_ibuf(buf: *mut ibuf, len: usize, new: *mut ibuf) -> c_int {
unsafe {
if ibuf_size(buf) < len {
errno!() = EBADMSG;
return -1;
}
ibuf_from_buffer(new, ibuf_data(buf), len);
(*buf).rpos += len;
0
}
}
pub unsafe fn ibuf_get_n8(buf: *mut ibuf, value: *mut u8) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u8>()) }
}
pub unsafe fn ibuf_get_n16(buf: *mut ibuf, value: *mut u16) -> c_int {
unsafe {
let rv = ibuf_get(buf, value as _, size_of::<u16>());
*value = u16::from_be(*value);
rv
}
}
pub unsafe fn ibuf_get_n32(buf: *mut ibuf, value: *mut u32) -> c_int {
unsafe {
let rv = ibuf_get(buf, value as _, size_of::<u32>());
*value = u32::from_be(*value);
rv
}
}
pub unsafe fn ibuf_get_n64(buf: *mut ibuf, value: *mut u64) -> c_int {
unsafe {
let rv = ibuf_get(buf, value as _, size_of::<u64>());
*value = u64::from_be(*value);
rv
}
}
pub unsafe fn ibuf_get_h16(buf: *mut ibuf, value: *mut u16) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u16>()) }
}
pub unsafe fn ibuf_get_h32(buf: *mut ibuf, value: *mut u32) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u32>()) }
}
pub unsafe fn ibuf_get_h64(buf: *mut ibuf, value: *mut u64) -> c_int {
unsafe { ibuf_get(buf, value as _, size_of::<u64>()) }
}
pub unsafe fn ibuf_skip(buf: *mut ibuf, len: usize) -> c_int {
unsafe {
if ibuf_size(buf) < len {
errno!() = EBADMSG;
return -1;
}
(*buf).rpos += len;
0
}
}
pub unsafe fn ibuf_free(buf: *mut ibuf) {
unsafe {
if buf.is_null() {
return;
}
if (*buf).max == 0 {
abort();
}
if (*buf).fd != -1 {
close((*buf).fd);
}
freezero((*buf).buf.cast(), (*buf).size);
free(buf as *mut c_void);
}
}
pub unsafe fn ibuf_fd_avail(buf: *mut ibuf) -> c_int {
unsafe { ((*buf).fd != -1) as c_int }
}
pub unsafe fn ibuf_fd_get(buf: *mut ibuf) -> c_int {
unsafe {
let fd = (*buf).fd;
(*buf).fd = -1;
fd
}
}
pub unsafe fn ibuf_fd_set(buf: *mut ibuf, fd: c_int) {
unsafe {
if (*buf).max == 0 {
abort();
}
if (*buf).fd != -1 {
close((*buf).fd);
}
(*buf).fd = fd;
}
}
pub unsafe fn ibuf_write(msgbuf: *mut msgbuf) -> c_int {
unsafe {
let mut i: u32 = 0;
let mut iov: [iovec; IOV_MAX] = [const {
iovec {
iov_base: null_mut(),
iov_len: 0,
}
}; IOV_MAX];
for buf in tailq_foreach(&raw mut (*msgbuf).bufs).map(NonNull::as_ptr) {
if i as usize >= IOV_MAX {
break;
}
iov[i as usize].iov_base = ibuf_data(buf);
iov[i as usize].iov_len = ibuf_size(buf);
i += 1;
}
if i == 0 {
return 0;
}
let mut n: isize;
'again: loop {
n = writev((*msgbuf).fd, iov.as_ptr(), i as i32);
if n == -1 {
if errno!() == EINTR {
continue 'again;
}
if errno!() == ENOBUFS {
errno!() = EAGAIN;
}
return -1;
}
break 'again; }
if n == 0 {
errno!() = 0;
return 0;
}
msgbuf_drain(msgbuf, n as usize);
1
}
}
pub unsafe fn msgbuf_init(msgbuf: *mut msgbuf) {
unsafe {
(*msgbuf).queued = 0;
(*msgbuf).fd = -1;
tailq_init(&raw mut (*msgbuf).bufs);
}
}
unsafe fn msgbuf_drain(msgbuf: *mut msgbuf, mut n: usize) {
unsafe {
let mut buf = tailq_first(&raw mut (*msgbuf).bufs);
while !buf.is_null() && n > 0 {
let next = tailq_next(buf);
if n >= ibuf_size(buf) {
n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf);
} else {
(*buf).rpos += n;
n = 0;
}
buf = next;
}
}
}
pub unsafe fn msgbuf_clear(msgbuf: *mut msgbuf) {
unsafe {
let mut buf;
while {
buf = tailq_first(&raw mut (*msgbuf).bufs);
!buf.is_null()
} {
ibuf_dequeue(msgbuf, buf);
}
}
}
pub unsafe fn msgbuf_write(msgbuf: *mut msgbuf) -> c_int {
unsafe {
let mut iov: [iovec; IOV_MAX] = std::mem::zeroed();
let mut buf0: *mut ibuf = null_mut();
let mut i: u32 = 0;
let mut msg: msghdr = std::mem::zeroed();
let mut cmsgbuf: cmsgbuf = std::mem::zeroed();
union cmsgbuf {
_hdr: cmsghdr,
buf: [u8; unsafe { CMSG_SPACE(size_of::<c_int>() as _) as usize }],
}
for buf in tailq_foreach(&raw mut (*msgbuf).bufs).map(NonNull::as_ptr) {
if i as usize >= IOV_MAX {
break;
}
if i > 0 && (*buf).fd != -1 {
break;
}
iov[i as usize].iov_base = ibuf_data(buf);
iov[i as usize].iov_len = ibuf_size(buf);
i += 1;
if (*buf).fd != -1 {
buf0 = buf;
}
}
msg.msg_iov = iov.as_mut_ptr();
msg.msg_iovlen = i.try_into().unwrap();
if !buf0.is_null() {
msg.msg_control = &raw mut cmsgbuf.buf as _;
msg.msg_controllen = size_of_val(&cmsgbuf.buf) as _;
let cmsg = CMSG_FIRSTHDR(&raw const msg);
(*cmsg).cmsg_len = CMSG_LEN(size_of::<c_int>() as u32) as _;
(*cmsg).cmsg_level = SOL_SOCKET;
(*cmsg).cmsg_type = SCM_RIGHTS;
*CMSG_DATA(cmsg).cast() = (*buf0).fd;
}
let mut n: isize;
'again: loop {
n = sendmsg((*msgbuf).fd, &raw const msg, 0);
if n == -1 {
if errno!() == EINTR {
continue 'again;
}
if errno!() == ENOBUFS {
errno!() = EAGAIN;
}
return -1;
}
break 'again;
}
if n == 0 {
errno!() = 0;
return 0;
}
if !buf0.is_null() {
close((*buf0).fd);
(*buf0).fd = -1;
}
msgbuf_drain(msgbuf, n as usize);
1
}
}
pub unsafe fn msgbuf_queuelen(msgbuf: *mut msgbuf) -> u32 {
unsafe { (*msgbuf).queued }
}
unsafe fn ibuf_enqueue(msgbuf: *mut msgbuf, buf: *mut ibuf) {
unsafe {
if (*buf).max == 0 {
abort();
}
tailq_insert_tail::<_, _>(&raw mut (*msgbuf).bufs, buf);
(*msgbuf).queued += 1;
}
}
unsafe fn ibuf_dequeue(msgbuf: *mut msgbuf, buf: *mut ibuf) {
unsafe {
tailq_remove(&raw mut (*msgbuf).bufs, buf);
(*msgbuf).queued -= 1;
ibuf_free(buf);
}
}
#[cfg(test)]
mod tests {
use super::*;
unsafe fn readable(buf: *const ibuf) -> Vec<u8> {
unsafe {
let p = ibuf_data(buf) as *const u8;
let n = ibuf_size(buf);
std::slice::from_raw_parts(p, n).to_vec()
}
}
#[test]
fn test_ibuf_open_basic_and_zero() {
unsafe {
let buf = ibuf_open(16);
assert!(!buf.is_null());
assert_eq!(ibuf_size(buf), 0);
assert_eq!((*buf).max, 16);
assert_eq!((*buf).size, 16);
assert_eq!((*buf).wpos, 0);
assert_eq!((*buf).rpos, 0);
assert_eq!((*buf).fd, -1);
assert_eq!(ibuf_left(buf), 16);
ibuf_free(buf);
errno!() = 0;
let z = ibuf_open(0);
assert!(z.is_null());
assert_eq!(errno!(), EINVAL);
}
}
#[test]
fn test_ibuf_dynamic_errors_and_alloc() {
unsafe {
errno!() = 0;
let a = ibuf_dynamic(0, 16);
assert!(a.is_null());
assert_eq!(errno!(), EINVAL);
errno!() = 0;
let b = ibuf_dynamic(8, 4);
assert!(b.is_null());
assert_eq!(errno!(), EINVAL);
let buf = ibuf_dynamic(4, 16);
assert!(!buf.is_null());
assert_eq!(ibuf_size(buf), 0);
assert_eq!((*buf).size, 4);
assert_eq!((*buf).fd, -1);
let src = [0xAAu8, 0xBB, 0xCC, 0xDD];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_size(buf), 4);
assert_eq!(readable(buf), src);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_bytes_and_data() {
unsafe {
let buf = ibuf_open(16);
let first = [1u8, 2, 3, 4];
let second = [5u8, 6];
assert_eq!(ibuf_add(buf, first.as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_size(buf), 4);
assert_eq!(ibuf_add(buf, second.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_size(buf), 6);
assert_eq!(readable(buf), [1, 2, 3, 4, 5, 6]);
assert_eq!((*buf).wpos, 6);
assert_eq!(ibuf_add(buf, first.as_ptr() as *const c_void, 0), 0);
assert_eq!(ibuf_size(buf), 6);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_reserve_and_backfill() {
unsafe {
let buf = ibuf_open(8);
let hdr = ibuf_reserve(buf, 4);
assert!(!hdr.is_null());
assert_eq!(ibuf_size(buf), 4);
assert_eq!((*buf).wpos, 4);
let body = [0x41u8, 0x42];
assert_eq!(ibuf_add(buf, body.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_size(buf), 6);
std::ptr::copy_nonoverlapping([0xDEu8, 0xAD, 0xBE, 0xEF].as_ptr(), hdr as *mut u8, 4);
assert_eq!(readable(buf), [0xDE, 0xAD, 0xBE, 0xEF, 0x41, 0x42]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_reserve_over_max() {
unsafe {
let buf = ibuf_open(4);
errno!() = 0;
let p = ibuf_reserve(buf, 8);
assert!(p.is_null());
assert_eq!(errno!(), ERANGE);
assert_eq!((*buf).wpos, 0);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_get_n_and_h_round_trip() {
unsafe {
let buf = ibuf_open(64);
assert_eq!(ibuf_add_n8(buf, 0x12), 0);
assert_eq!(ibuf_add_n16(buf, 0x1234), 0);
assert_eq!(ibuf_add_n32(buf, 0x1234_5678), 0);
assert_eq!(ibuf_add_n64(buf, 0x1234_5678_9abc_def0), 0);
assert_eq!(ibuf_add_h16(buf, 0xBEEF), 0);
assert_eq!(ibuf_add_h32(buf, 0xDEAD_BEEF), 0);
assert_eq!(ibuf_add_h64(buf, 0x0011_2233_4455_6677), 0);
assert_eq!(ibuf_size(buf), 1 + 2 + 4 + 8 + 2 + 4 + 8);
let mut v8: u8 = 0;
let mut v16: u16 = 0;
let mut v32: u32 = 0;
let mut v64: u64 = 0;
assert_eq!(ibuf_get_n8(buf, &mut v8), 0);
assert_eq!(v8, 0x12);
assert_eq!(ibuf_get_n16(buf, &mut v16), 0);
assert_eq!(v16, 0x1234);
assert_eq!(ibuf_get_n32(buf, &mut v32), 0);
assert_eq!(v32, 0x1234_5678);
assert_eq!(ibuf_get_n64(buf, &mut v64), 0);
assert_eq!(v64, 0x1234_5678_9abc_def0);
assert_eq!(ibuf_get_h16(buf, &mut v16), 0);
assert_eq!(v16, 0xBEEF);
assert_eq!(ibuf_get_h32(buf, &mut v32), 0);
assert_eq!(v32, 0xDEAD_BEEF);
assert_eq!(ibuf_get_h64(buf, &mut v64), 0);
assert_eq!(v64, 0x0011_2233_4455_6677);
assert_eq!(ibuf_size(buf), 0);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_n16_is_big_endian() {
unsafe {
let buf = ibuf_open(2);
assert_eq!(ibuf_add_n16(buf, 0x0102), 0);
assert_eq!(readable(buf), [0x01, 0x02]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_n_overflow() {
unsafe {
let buf = ibuf_open(16);
errno!() = 0;
assert_eq!(ibuf_add_n8(buf, 0x100), -1);
assert_eq!(errno!(), EINVAL);
errno!() = 0;
assert_eq!(ibuf_add_n16(buf, 0x1_0000), -1);
assert_eq!(errno!(), EINVAL);
errno!() = 0;
assert_eq!(ibuf_add_n32(buf, 0x1_0000_0000), -1);
assert_eq!(errno!(), EINVAL);
assert_eq!(ibuf_size(buf), 0);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_add_zero() {
unsafe {
let buf = ibuf_open(8);
let pre = [0xFFu8, 0xFF];
assert_eq!(ibuf_add(buf, pre.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_add_zero(buf, 3), 0);
assert_eq!(ibuf_size(buf), 5);
assert_eq!(readable(buf), [0xFF, 0xFF, 0x00, 0x00, 0x00]);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_seek_and_set() {
unsafe {
let buf = ibuf_open(16);
let src = [0u8, 1, 2, 3, 4, 5, 6, 7];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 8), 0);
let p = ibuf_seek(buf, 2, 4);
assert!(!p.is_null());
let seen = std::slice::from_raw_parts(p as *const u8, 4);
assert_eq!(seen, [2, 3, 4, 5]);
let patch = [0xAAu8, 0xBB];
assert_eq!(ibuf_set(buf, 4, patch.as_ptr() as *const c_void, 2), 0);
assert_eq!(ibuf_size(buf), 8);
assert_eq!(readable(buf), [0, 1, 2, 3, 0xAA, 0xBB, 6, 7]);
errno!() = 0;
assert!(ibuf_seek(buf, 6, 4).is_null());
assert_eq!(errno!(), ERANGE);
errno!() = 0;
assert!(ibuf_seek(buf, 9, 0).is_null());
assert_eq!(errno!(), ERANGE);
errno!() = 0;
assert_eq!(ibuf_set(buf, 7, patch.as_ptr() as *const c_void, 2), -1);
assert_eq!(errno!(), ERANGE);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_set_n32_backfill() {
unsafe {
let buf = ibuf_open(8);
assert_eq!(ibuf_add_zero(buf, 4), 0);
let body = [0xDEu8, 0xAD, 0xBE, 0xEF];
assert_eq!(ibuf_add(buf, body.as_ptr() as *const c_void, 4), 0);
assert_eq!(ibuf_set_n32(buf, 0, 4), 0);
assert_eq!(readable(buf), [0x00, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF]);
let mut len: u32 = 0;
assert_eq!(ibuf_get_n32(buf, &mut len), 0);
assert_eq!(len, 4);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_set_n8_overflow() {
unsafe {
let buf = ibuf_open(4);
assert_eq!(ibuf_add_zero(buf, 1), 0);
errno!() = 0;
assert_eq!(ibuf_set_n8(buf, 0, 0x100), -1);
assert_eq!(errno!(), EINVAL);
ibuf_free(buf);
}
}
#[test]
fn test_ibuf_data_moves_with_rpos() {
unsafe {
let buf = ibuf_open(8);
let src = [10u8, 20, 30, 40];
assert_eq!(ibuf_add(buf, src.as_ptr() as *const c_void, 4), 0);
let base = ibuf_data(buf) as usize;
let mut two = [0u8; 2];
assert_eq!(ibuf_get(buf, two.as_mut_ptr() as *mut c_void, 2), 0);
assert_eq!(two, [10, 20]);
assert_eq!(ibuf_size(buf), 2);
assert_eq!(ibuf_data(buf) as usize, base + 2);
assert_eq!(readable(buf), [30, 40]);
errno!() = 0;
let mut big = [0u8; 8];
assert_eq!(ibuf_get(buf, big.as_mut_ptr() as *mut c_void, 8), -1);
assert_eq!(errno!(), EBADMSG);
assert_eq!(ibuf_size(buf), 2);
ibuf_free(buf);
}
}
}