use crate::io::{BufRead, Read, Write};
use core::{mem, ptr, slice};
use crate::message;
use crate::serialize;
use crate::{Error, ErrorKind, Result};
struct PackedRead<R>
where
R: BufRead,
{
inner: R,
}
impl<R> PackedRead<R>
where
R: BufRead,
{
fn get_read_buffer(&mut self) -> Result<(*const u8, *const u8)> {
let buf = self.inner.fill_buf()?;
Ok((buf.as_ptr(), buf.as_ptr().wrapping_add(buf.len())))
}
}
#[inline]
fn ptr_sub<T>(p1: *const T, p2: *const T) -> usize {
(p1 as usize - p2 as usize) / mem::size_of::<T>()
}
macro_rules! refresh_buffer(
($this:expr, $size:ident, $in_ptr:ident, $in_end:ident, $out:ident,
$outBuf:ident, $buffer_begin:ident) => (
{
$this.inner.consume($size);
let (b, e) = $this.get_read_buffer()?;
$in_ptr = b;
$in_end = e;
$size = ptr_sub($in_end, $in_ptr);
$buffer_begin = b;
if $size == 0 {
return Err(Error::from_kind(ErrorKind::PrematureEndOfPackedInput));
}
}
);
);
impl<R> Read for PackedRead<R>
where
R: BufRead,
{
fn read(&mut self, out_buf: &mut [u8]) -> Result<usize> {
let len = out_buf.len();
if len == 0 {
return Ok(0);
}
assert!(len % 8 == 0, "PackedRead reads must be word-aligned.");
unsafe {
let out_buf_start = out_buf.as_mut_ptr();
let mut out = out_buf_start;
let out_end: *mut u8 = out.wrapping_add(len);
let (mut in_ptr, mut in_end) = self.get_read_buffer()?;
let mut buffer_begin = in_ptr;
let mut size = ptr_sub(in_end, in_ptr);
if size == 0 {
return Ok(0);
}
loop {
let tag: u8;
assert_eq!(
ptr_sub(out, out_buf_start) % 8,
0,
"Output pointer should always be aligned here."
);
if ptr_sub(in_end, in_ptr) < 10 {
if ptr_sub(in_end, in_ptr) == 0 {
refresh_buffer!(self, size, in_ptr, in_end, out, out_buf, buffer_begin);
continue;
}
tag = *in_ptr;
in_ptr = in_ptr.offset(1);
for i in 0..8 {
if (tag & (1u8 << i)) != 0 {
if ptr_sub(in_end, in_ptr) == 0 {
refresh_buffer!(
self,
size,
in_ptr,
in_end,
out,
out_buf,
buffer_begin
);
}
*out = *in_ptr;
out = out.offset(1);
in_ptr = in_ptr.offset(1);
} else {
*out = 0;
out = out.offset(1);
}
}
if ptr_sub(in_end, in_ptr) == 0 && (tag == 0 || tag == 0xff) {
refresh_buffer!(self, size, in_ptr, in_end, out, out_buf, buffer_begin);
}
} else {
tag = *in_ptr;
in_ptr = in_ptr.offset(1);
for n in 0..8 {
let is_nonzero = (tag & (1u8 << n)) != 0;
*out = (*in_ptr) & ((-i8::from(is_nonzero)) as u8);
out = out.offset(1);
in_ptr = in_ptr.offset(isize::from(is_nonzero));
}
}
if tag == 0 {
assert!(
ptr_sub(in_end, in_ptr) > 0,
"Should always have non-empty buffer here."
);
let run_length: usize = (*in_ptr) as usize * 8;
in_ptr = in_ptr.offset(1);
if run_length > ptr_sub(out_end, out) {
return Err(Error::from_kind(
ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary,
));
}
ptr::write_bytes(out, 0, run_length);
out = out.add(run_length);
} else if tag == 0xff {
assert!(
ptr_sub(in_end, in_ptr) > 0,
"Should always have non-empty buffer here"
);
let mut run_length: usize = (*in_ptr) as usize * 8;
in_ptr = in_ptr.offset(1);
if run_length > ptr_sub(out_end, out) {
return Err(Error::from_kind(
ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary,
));
}
let in_remaining = ptr_sub(in_end, in_ptr);
if in_remaining >= run_length {
ptr::copy_nonoverlapping(in_ptr, out, run_length);
out = out.add(run_length);
in_ptr = in_ptr.add(run_length);
} else {
ptr::copy_nonoverlapping(in_ptr, out, in_remaining);
out = out.add(in_remaining);
run_length -= in_remaining;
self.inner.consume(size);
{
let buf = slice::from_raw_parts_mut::<u8>(out, run_length);
self.inner.read_exact(buf)?;
}
out = out.add(run_length);
if out == out_end {
return Ok(len);
} else {
let (b, e) = self.get_read_buffer()?;
in_ptr = b;
in_end = e;
size = ptr_sub(e, b);
buffer_begin = in_ptr;
continue;
}
}
}
if out == out_end {
self.inner.consume(ptr_sub(in_ptr, buffer_begin));
return Ok(len);
}
}
}
}
}
#[cfg(feature = "alloc")]
pub fn read_message<R>(
read: R,
options: message::ReaderOptions,
) -> Result<crate::message::Reader<serialize::OwnedSegments>>
where
R: BufRead,
{
let packed_read = PackedRead { inner: read };
serialize::read_message(packed_read, options)
}
#[cfg(feature = "alloc")]
pub fn try_read_message<R>(
read: R,
options: message::ReaderOptions,
) -> Result<Option<crate::message::Reader<serialize::OwnedSegments>>>
where
R: BufRead,
{
let packed_read = PackedRead { inner: read };
serialize::try_read_message(packed_read, options)
}
pub fn read_message_no_alloc<R>(
read: R,
buffer: &mut [u8],
options: message::ReaderOptions,
) -> Result<crate::message::Reader<serialize::NoAllocBufferSegments<&[u8]>>>
where
R: BufRead,
{
let packed_read = PackedRead { inner: read };
serialize::read_message_no_alloc(packed_read, buffer, options)
}
pub fn try_read_message_no_alloc<R>(
read: R,
buffer: &mut [u8],
options: message::ReaderOptions,
) -> Result<Option<crate::message::Reader<serialize::NoAllocBufferSegments<&[u8]>>>>
where
R: BufRead,
{
let packed_read = PackedRead { inner: read };
serialize::try_read_message_no_alloc(packed_read, buffer, options)
}
struct PackedWrite<W>
where
W: Write,
{
inner: W,
}
impl<W> Write for PackedWrite<W>
where
W: Write,
{
fn write_all(&mut self, in_buf: &[u8]) -> Result<()> {
unsafe {
let mut buf_idx: usize = 0;
let mut buf: [u8; 64] = [0; 64];
let mut in_ptr: *const u8 = in_buf.as_ptr();
let in_end: *const u8 = in_buf.as_ptr().wrapping_add(in_buf.len());
while in_ptr < in_end {
if buf_idx + 10 > buf.len() {
self.inner.write_all(&buf[..buf_idx])?;
buf_idx = 0;
}
let tag_pos = buf_idx;
buf_idx += 1;
let bit0 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit0 as usize;
in_ptr = in_ptr.offset(1);
let bit1 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit1 as usize;
in_ptr = in_ptr.offset(1);
let bit2 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit2 as usize;
in_ptr = in_ptr.offset(1);
let bit3 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit3 as usize;
in_ptr = in_ptr.offset(1);
let bit4 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit4 as usize;
in_ptr = in_ptr.offset(1);
let bit5 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit5 as usize;
in_ptr = in_ptr.offset(1);
let bit6 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit6 as usize;
in_ptr = in_ptr.offset(1);
let bit7 = u8::from(*in_ptr != 0);
*buf.get_unchecked_mut(buf_idx) = *in_ptr;
buf_idx += bit7 as usize;
in_ptr = in_ptr.offset(1);
let tag: u8 = bit0
| (bit1 << 1)
| (bit2 << 2)
| (bit3 << 3)
| (bit4 << 4)
| (bit5 << 5)
| (bit6 << 6)
| (bit7 << 7);
*buf.get_unchecked_mut(tag_pos) = tag;
if tag == 0 {
let mut in_word: *const [u8; 8] = in_ptr as *const [u8; 8];
let mut limit: *const [u8; 8] = in_end as *const [u8; 8];
if ptr_sub(limit, in_word) > 255 {
limit = in_word.offset(255);
}
while in_word < limit && *in_word == [0; 8] {
in_word = in_word.offset(1);
}
*buf.get_unchecked_mut(buf_idx) = ptr_sub(in_word, in_ptr as *const [u8; 8])
.try_into()
.unwrap();
buf_idx += 1;
in_ptr = in_word as *const u8;
} else if tag == 0xff {
let run_start = in_ptr;
let mut limit = in_end;
if ptr_sub(limit, in_ptr) > 255 * 8 {
limit = in_ptr.offset(255 * 8);
}
while in_ptr < limit {
let mut c = 0;
for _ in 0..8 {
c += u8::from(*in_ptr == 0);
in_ptr = in_ptr.offset(1);
}
if c >= 2 {
in_ptr = in_ptr.offset(-8);
break;
}
}
let count: usize = ptr_sub(in_ptr, run_start);
*buf.get_unchecked_mut(buf_idx) = (count / 8).try_into().unwrap();
buf_idx += 1;
self.inner.write_all(&buf[..buf_idx])?;
buf_idx = 0;
self.inner
.write_all(slice::from_raw_parts::<u8>(run_start, count))?;
}
}
self.inner.write_all(&buf[..buf_idx])?;
Ok(())
}
}
}
pub fn write_message<W, A>(write: W, message: &crate::message::Builder<A>) -> Result<()>
where
W: Write,
A: crate::message::Allocator,
{
let packed_write = PackedWrite { inner: write };
serialize::write_message(packed_write, message)
}
#[cfg(feature = "alloc")]
#[cfg(test)]
mod tests {
use crate::io::{Read, Write};
use quickcheck::{quickcheck, TestResult};
use super::read_message;
use crate::message::ReaderOptions;
use crate::serialize::test::write_message_segments;
use crate::serialize_packed::{PackedRead, PackedWrite};
use crate::ErrorKind;
#[test]
pub(crate) fn premature_eof() {
let input_bytes: &[u8] = &[];
let mut packed_read = PackedRead { inner: input_bytes };
let mut output_bytes: alloc::vec::Vec<u8> = vec![0; 8];
assert!(packed_read.read_exact(&mut output_bytes[..]).is_err());
}
pub(crate) fn check_unpacks_to(packed: &[u8], unpacked: &[u8]) {
let mut packed_read = PackedRead { inner: packed };
let mut bytes: alloc::vec::Vec<u8> = vec![0; unpacked.len()];
packed_read.read_exact(&mut bytes[..]).unwrap();
assert!(packed_read.inner.is_empty()); assert_eq!(bytes, unpacked);
}
pub(crate) fn check_packing(unpacked: &[u8], packed: &[u8]) {
let mut bytes: alloc::vec::Vec<u8> = vec![0; packed.len()];
{
let mut packed_write = PackedWrite {
inner: &mut bytes[..],
};
packed_write.write_all(unpacked).unwrap();
}
assert_eq!(bytes, packed);
check_unpacks_to(packed, unpacked);
}
#[test]
pub(crate) fn simple_packing() {
check_packing(&[], &[]);
check_packing(&[0; 8], &[0, 0]);
check_packing(&[0, 0, 12, 0, 0, 34, 0, 0], &[0x24, 12, 34]);
check_packing(
&[1, 3, 2, 4, 5, 7, 6, 8],
&[0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0],
);
check_packing(
&[0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8],
&[0, 0, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0],
);
check_packing(
&[0, 0, 12, 0, 0, 34, 0, 0, 1, 3, 2, 4, 5, 7, 6, 8],
&[0x24, 12, 34, 0xff, 1, 3, 2, 4, 5, 7, 6, 8, 0],
);
check_packing(
&[1, 3, 2, 4, 5, 7, 6, 8, 8, 6, 7, 4, 5, 2, 3, 1],
&[0xff, 1, 3, 2, 4, 5, 7, 6, 8, 1, 8, 6, 7, 4, 5, 2, 3, 1],
);
check_packing(
&[
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4,
5, 6, 7, 8, 0, 2, 4, 0, 9, 0, 5, 1,
],
&[
0xff, 1, 2, 3, 4, 5, 6, 7, 8, 3, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1,
2, 3, 4, 5, 6, 7, 8, 0xd6, 2, 4, 9, 5, 1,
],
);
check_packing(
&[
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 6, 2, 4, 3, 9, 0, 5, 1, 1, 2, 3, 4,
5, 6, 7, 8, 0, 2, 4, 0, 9, 0, 5, 1,
],
&[
0xff, 1, 2, 3, 4, 5, 6, 7, 8, 3, 1, 2, 3, 4, 5, 6, 7, 8, 6, 2, 4, 3, 9, 0, 5, 1, 1,
2, 3, 4, 5, 6, 7, 8, 0xd6, 2, 4, 9, 5, 1,
],
);
check_packing(
&[
8, 0, 100, 6, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 3, 1,
],
&[0xed, 8, 100, 6, 1, 1, 2, 0, 2, 0xd4, 1, 2, 3, 1],
);
check_packing(&[0; 8], &[0, 0]);
check_packing(&[0; 16], &[0, 1]);
check_packing(
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
],
&[0, 2],
);
check_packing(&[0; 258 * 8], &[0, 255, 0, 1]);
}
quickcheck! {
#[cfg_attr(miri, ignore)] fn test_round_trip(segments: alloc::vec::Vec<alloc::vec::Vec<crate::Word>>) -> TestResult {
use crate::message::ReaderSegments;
if segments.is_empty() { return TestResult::discard(); }
let mut buf: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
write_message_segments(&mut PackedWrite { inner: &mut buf }, &segments);
let message = read_message(&mut &buf[..], ReaderOptions::new()).unwrap();
let result_segments = message.into_segments();
TestResult::from_bool(segments.iter().enumerate().all(|(i, segment)| {
crate::Word::words_to_bytes(&segment[..]) == result_segments.get_segment(i as u32).unwrap()
}))
}
#[cfg_attr(miri, ignore)] fn test_unpack(packed: alloc::vec::Vec<u8>) -> TestResult {
let len = packed.len();
let mut packed_read = PackedRead { inner: &packed[..] };
let mut out_buffer: alloc::vec::Vec<u8> = vec![0; len * 8];
let _ = packed_read.read_exact(&mut out_buffer);
TestResult::from_bool(true)
}
}
#[test]
fn did_not_end_cleanly_on_a_segment_boundary() {
let packed = &[0xff, 1, 2, 3, 4, 5, 6, 7, 8, 37, 1, 2];
let mut packed_read = PackedRead { inner: &packed[..] };
let mut bytes: alloc::vec::Vec<u8> = vec![0; 200];
match packed_read.read_exact(&mut bytes[..]) {
Ok(_) => panic!("should have been an error"),
Err(e) => {
assert_eq!(
e.kind,
ErrorKind::PackedInputDidNotEndCleanlyOnASegmentBoundary,
);
}
}
}
#[test]
fn premature_end_of_packed_input() {
fn helper(packed: &[u8]) {
let mut packed_read = PackedRead { inner: packed };
let mut bytes: alloc::vec::Vec<u8> = vec![0; 200];
match packed_read.read_exact(&mut bytes[..]) {
Ok(_) => panic!("should have been an error"),
Err(e) => {
assert_eq!(e.kind, ErrorKind::PrematureEndOfPackedInput);
}
}
}
helper(&[0xf0, 1, 2]);
helper(&[0]);
helper(&[0xff, 1, 2, 3, 4, 5, 6, 7, 8]);
helper(&[1, 1]);
}
#[test]
fn packed_segment_table() {
let packed_buf = &[0x11, 4, 1, 0, 1, 0, 0];
check_unpacks_to(
packed_buf,
&[
4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
],
);
read_message(&mut &packed_buf[..], Default::default()).unwrap();
}
}