use zerocopy::{FromBytes, Immutable, KnownLayout};
use crate::Result;
use crate::protocol::backend::query::{DataRow, FieldDescription};
pub use zerocopy::big_endian::{
I16 as I16BE, I32 as I32BE, I64 as I64BE, U16 as U16BE, U32 as U32BE, U64 as U64BE,
};
pub trait FixedWireSize {
const WIRE_SIZE: usize;
}
impl FixedWireSize for i8 {
const WIRE_SIZE: usize = 1;
}
impl FixedWireSize for u8 {
const WIRE_SIZE: usize = 1;
}
impl FixedWireSize for zerocopy::big_endian::I16 {
const WIRE_SIZE: usize = 2;
}
impl FixedWireSize for zerocopy::big_endian::U16 {
const WIRE_SIZE: usize = 2;
}
impl FixedWireSize for zerocopy::big_endian::I32 {
const WIRE_SIZE: usize = 4;
}
impl FixedWireSize for zerocopy::big_endian::U32 {
const WIRE_SIZE: usize = 4;
}
impl FixedWireSize for zerocopy::big_endian::I64 {
const WIRE_SIZE: usize = 8;
}
impl FixedWireSize for zerocopy::big_endian::U64 {
const WIRE_SIZE: usize = 8;
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
pub struct LengthPrefixed<T> {
len: zerocopy::big_endian::I32,
value: T,
}
impl<T: FixedWireSize> FixedWireSize for LengthPrefixed<T> {
const WIRE_SIZE: usize = 4 + T::WIRE_SIZE;
}
impl<T: Copy> LengthPrefixed<T> {
#[inline]
pub fn len(&self) -> i32 {
self.len.get()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len.get() == 0
}
#[inline]
pub fn is_null(&self) -> bool {
self.len.get() == -1
}
#[inline]
pub fn get(&self) -> T {
self.value
}
}
pub trait RefFromRow<'a>: Sized {
fn ref_from_row_binary(cols: &[FieldDescription], row: DataRow<'a>) -> Result<&'a Self>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn length_prefixed_i32() {
let data: &[u8] = &[0, 0, 0, 4, 0, 0, 0, 42];
let prefixed: &LengthPrefixed<I32BE> = zerocopy::FromBytes::ref_from_bytes(data).unwrap();
assert_eq!(prefixed.len(), 4);
assert!(!prefixed.is_null());
assert_eq!(prefixed.get().get(), 42);
}
#[test]
fn length_prefixed_i64() {
let value: i64 = 12345678901234;
let mut data = [0u8; 12];
data[0..4].copy_from_slice(&8_i32.to_be_bytes());
data[4..12].copy_from_slice(&value.to_be_bytes());
let prefixed: &LengthPrefixed<I64BE> = zerocopy::FromBytes::ref_from_bytes(&data).unwrap();
assert_eq!(prefixed.len(), 8);
assert_eq!(prefixed.get().get(), value);
}
#[test]
fn wire_size() {
assert_eq!(<LengthPrefixed<I32BE> as FixedWireSize>::WIRE_SIZE, 8);
assert_eq!(<LengthPrefixed<I64BE> as FixedWireSize>::WIRE_SIZE, 12);
assert_eq!(<LengthPrefixed<I16BE> as FixedWireSize>::WIRE_SIZE, 6);
}
#[test]
fn contiguous_struct() {
let mut data = [0u8; 20]; data[0..4].copy_from_slice(&4_i32.to_be_bytes());
data[4..8].copy_from_slice(&42_i32.to_be_bytes());
data[8..12].copy_from_slice(&8_i32.to_be_bytes());
data[12..20].copy_from_slice(&12345_i64.to_be_bytes());
#[derive(FromBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
struct TestRow {
col1: LengthPrefixed<I32BE>,
col2: LengthPrefixed<I64BE>,
}
let row: &TestRow = zerocopy::FromBytes::ref_from_bytes(&data).unwrap();
assert_eq!(row.col1.get().get(), 42);
assert_eq!(row.col2.get().get(), 12345);
}
}