mirror of
https://github.com/tursodatabase/limbo.git
synced 2025-08-04 01:58:16 +00:00
core: Optimize read_record() function
The SerialType::try_from() was pretty high up in CPU profiles so I asked my dear friend Claude to optimize it away by using the serial type integer value directly instead of constructing a fancy enumeration.
This commit is contained in:
parent
d1fa995ac5
commit
6d44ad22fd
1 changed files with 298 additions and 250 deletions
|
@ -910,41 +910,106 @@ fn read_payload(unread: &[u8], payload_size: usize, pager: Rc<Pager>) -> (Vec<u8
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SerialType {
|
||||
Null,
|
||||
Int8, // 8-bit twos-complement integer: https://www.sqlite.org/fileformat.html
|
||||
BEInt16,
|
||||
BEInt24,
|
||||
BEInt32,
|
||||
BEInt48,
|
||||
BEInt64,
|
||||
BEFloat64,
|
||||
ConstInt0,
|
||||
ConstInt1,
|
||||
Blob(usize),
|
||||
String(usize),
|
||||
pub type SerialType = u64;
|
||||
|
||||
pub const SERIAL_TYPE_NULL: SerialType = 0;
|
||||
pub const SERIAL_TYPE_INT8: SerialType = 1;
|
||||
pub const SERIAL_TYPE_BEINT16: SerialType = 2;
|
||||
pub const SERIAL_TYPE_BEINT24: SerialType = 3;
|
||||
pub const SERIAL_TYPE_BEINT32: SerialType = 4;
|
||||
pub const SERIAL_TYPE_BEINT48: SerialType = 5;
|
||||
pub const SERIAL_TYPE_BEINT64: SerialType = 6;
|
||||
pub const SERIAL_TYPE_BEFLOAT64: SerialType = 7;
|
||||
pub const SERIAL_TYPE_CONSTINT0: SerialType = 8;
|
||||
pub const SERIAL_TYPE_CONSTINT1: SerialType = 9;
|
||||
|
||||
pub trait SerialTypeExt {
|
||||
fn is_null(self) -> bool;
|
||||
fn is_int8(self) -> bool;
|
||||
fn is_beint16(self) -> bool;
|
||||
fn is_beint24(self) -> bool;
|
||||
fn is_beint32(self) -> bool;
|
||||
fn is_beint48(self) -> bool;
|
||||
fn is_beint64(self) -> bool;
|
||||
fn is_befloat64(self) -> bool;
|
||||
fn is_constint0(self) -> bool;
|
||||
fn is_constint1(self) -> bool;
|
||||
fn is_blob(self) -> bool;
|
||||
fn is_string(self) -> bool;
|
||||
fn blob_size(self) -> usize;
|
||||
fn string_size(self) -> usize;
|
||||
fn is_valid(self) -> bool;
|
||||
}
|
||||
|
||||
impl TryFrom<u64> for SerialType {
|
||||
type Error = LimboError;
|
||||
impl SerialTypeExt for u64 {
|
||||
fn is_null(self) -> bool {
|
||||
self == SERIAL_TYPE_NULL
|
||||
}
|
||||
|
||||
fn try_from(value: u64) -> Result<Self> {
|
||||
match value {
|
||||
0 => Ok(Self::Null),
|
||||
1 => Ok(Self::Int8),
|
||||
2 => Ok(Self::BEInt16),
|
||||
3 => Ok(Self::BEInt24),
|
||||
4 => Ok(Self::BEInt32),
|
||||
5 => Ok(Self::BEInt48),
|
||||
6 => Ok(Self::BEInt64),
|
||||
7 => Ok(Self::BEFloat64),
|
||||
8 => Ok(Self::ConstInt0),
|
||||
9 => Ok(Self::ConstInt1),
|
||||
n if value >= 12 && value % 2 == 0 => Ok(Self::Blob(((n - 12) / 2) as usize)),
|
||||
n if value >= 13 && value % 2 == 1 => Ok(Self::String(((n - 13) / 2) as usize)),
|
||||
_ => crate::bail_corrupt_error!("Invalid serial type: {}", value),
|
||||
}
|
||||
fn is_int8(self) -> bool {
|
||||
self == SERIAL_TYPE_INT8
|
||||
}
|
||||
|
||||
fn is_beint16(self) -> bool {
|
||||
self == SERIAL_TYPE_BEINT16
|
||||
}
|
||||
|
||||
fn is_beint24(self) -> bool {
|
||||
self == SERIAL_TYPE_BEINT24
|
||||
}
|
||||
|
||||
fn is_beint32(self) -> bool {
|
||||
self == SERIAL_TYPE_BEINT32
|
||||
}
|
||||
|
||||
fn is_beint48(self) -> bool {
|
||||
self == SERIAL_TYPE_BEINT48
|
||||
}
|
||||
|
||||
fn is_beint64(self) -> bool {
|
||||
self == SERIAL_TYPE_BEINT64
|
||||
}
|
||||
|
||||
fn is_befloat64(self) -> bool {
|
||||
self == SERIAL_TYPE_BEFLOAT64
|
||||
}
|
||||
|
||||
fn is_constint0(self) -> bool {
|
||||
self == SERIAL_TYPE_CONSTINT0
|
||||
}
|
||||
|
||||
fn is_constint1(self) -> bool {
|
||||
self == SERIAL_TYPE_CONSTINT1
|
||||
}
|
||||
|
||||
fn is_blob(self) -> bool {
|
||||
self >= 12 && self % 2 == 0
|
||||
}
|
||||
|
||||
fn is_string(self) -> bool {
|
||||
self >= 13 && self % 2 == 1
|
||||
}
|
||||
|
||||
fn blob_size(self) -> usize {
|
||||
debug_assert!(self.is_blob());
|
||||
((self - 12) / 2) as usize
|
||||
}
|
||||
|
||||
fn string_size(self) -> usize {
|
||||
debug_assert!(self.is_string());
|
||||
((self - 13) / 2) as usize
|
||||
}
|
||||
|
||||
fn is_valid(self) -> bool {
|
||||
self <= 9 || self.is_blob() || self.is_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_serial_type(value: u64) -> Result<SerialType> {
|
||||
if value.is_valid() {
|
||||
Ok(value)
|
||||
} else {
|
||||
crate::bail_corrupt_error!("Invalid serial type: {}", value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -954,131 +1019,152 @@ pub fn read_record(payload: &[u8]) -> Result<Record> {
|
|||
assert!((header_size as usize) >= nr);
|
||||
let mut header_size = (header_size as usize) - nr;
|
||||
pos += nr;
|
||||
|
||||
let mut serial_types = Vec::with_capacity(header_size);
|
||||
while header_size > 0 {
|
||||
let (serial_type, nr) = read_varint(&payload[pos..])?;
|
||||
let serial_type = SerialType::try_from(serial_type)?;
|
||||
let serial_type = validate_serial_type(serial_type)?;
|
||||
serial_types.push(serial_type);
|
||||
pos += nr;
|
||||
assert!(header_size >= nr);
|
||||
header_size -= nr;
|
||||
}
|
||||
|
||||
let mut values = Vec::with_capacity(serial_types.len());
|
||||
for serial_type in &serial_types {
|
||||
for &serial_type in &serial_types {
|
||||
let (value, n) = read_value(&payload[pos..], serial_type)?;
|
||||
pos += n;
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
Ok(Record::new(values))
|
||||
}
|
||||
|
||||
pub fn read_value(buf: &[u8], serial_type: &SerialType) -> Result<(OwnedValue, usize)> {
|
||||
match *serial_type {
|
||||
SerialType::Null => Ok((OwnedValue::Null, 0)),
|
||||
SerialType::Int8 => {
|
||||
if buf.is_empty() {
|
||||
crate::bail_corrupt_error!("Invalid UInt8 value");
|
||||
}
|
||||
let val = buf[0] as i8;
|
||||
Ok((OwnedValue::Integer(val as i64), 1))
|
||||
}
|
||||
SerialType::BEInt16 => {
|
||||
if buf.len() < 2 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt16 value");
|
||||
}
|
||||
Ok((
|
||||
OwnedValue::Integer(i16::from_be_bytes([buf[0], buf[1]]) as i64),
|
||||
2,
|
||||
))
|
||||
}
|
||||
SerialType::BEInt24 => {
|
||||
if buf.len() < 3 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt24 value");
|
||||
}
|
||||
let sign_extension = if buf[0] <= 127 { 0 } else { 255 };
|
||||
Ok((
|
||||
OwnedValue::Integer(
|
||||
i32::from_be_bytes([sign_extension, buf[0], buf[1], buf[2]]) as i64
|
||||
),
|
||||
3,
|
||||
))
|
||||
}
|
||||
SerialType::BEInt32 => {
|
||||
if buf.len() < 4 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt32 value");
|
||||
}
|
||||
Ok((
|
||||
OwnedValue::Integer(i32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) as i64),
|
||||
4,
|
||||
))
|
||||
}
|
||||
SerialType::BEInt48 => {
|
||||
if buf.len() < 6 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt48 value");
|
||||
}
|
||||
let sign_extension = if buf[0] <= 127 { 0 } else { 255 };
|
||||
Ok((
|
||||
OwnedValue::Integer(i64::from_be_bytes([
|
||||
sign_extension,
|
||||
sign_extension,
|
||||
buf[0],
|
||||
buf[1],
|
||||
buf[2],
|
||||
buf[3],
|
||||
buf[4],
|
||||
buf[5],
|
||||
])),
|
||||
6,
|
||||
))
|
||||
}
|
||||
SerialType::BEInt64 => {
|
||||
if buf.len() < 8 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt64 value");
|
||||
}
|
||||
Ok((
|
||||
OwnedValue::Integer(i64::from_be_bytes([
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
|
||||
])),
|
||||
8,
|
||||
))
|
||||
}
|
||||
SerialType::BEFloat64 => {
|
||||
if buf.len() < 8 {
|
||||
crate::bail_corrupt_error!("Invalid BEFloat64 value");
|
||||
}
|
||||
Ok((
|
||||
OwnedValue::Float(f64::from_be_bytes([
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
|
||||
])),
|
||||
8,
|
||||
))
|
||||
}
|
||||
SerialType::ConstInt0 => Ok((OwnedValue::Integer(0), 0)),
|
||||
SerialType::ConstInt1 => Ok((OwnedValue::Integer(1), 0)),
|
||||
SerialType::Blob(n) => {
|
||||
if buf.len() < n {
|
||||
crate::bail_corrupt_error!("Invalid Blob value");
|
||||
}
|
||||
Ok((OwnedValue::Blob(buf[0..n].to_vec().into()), n))
|
||||
}
|
||||
SerialType::String(n) => {
|
||||
if buf.len() < n {
|
||||
crate::bail_corrupt_error!(
|
||||
"Invalid String value, length {} < expected length {}",
|
||||
buf.len(),
|
||||
n
|
||||
);
|
||||
}
|
||||
let bytes = buf[0..n].to_vec();
|
||||
Ok((
|
||||
OwnedValue::Text(Text {
|
||||
value: Rc::new(bytes),
|
||||
subtype: TextSubtype::Text,
|
||||
}),
|
||||
n,
|
||||
))
|
||||
}
|
||||
pub fn read_value(buf: &[u8], serial_type: SerialType) -> Result<(OwnedValue, usize)> {
|
||||
if serial_type.is_null() {
|
||||
return Ok((OwnedValue::Null, 0));
|
||||
}
|
||||
|
||||
if serial_type.is_int8() {
|
||||
if buf.is_empty() {
|
||||
crate::bail_corrupt_error!("Invalid UInt8 value");
|
||||
}
|
||||
let val = buf[0] as i8;
|
||||
return Ok((OwnedValue::Integer(val as i64), 1));
|
||||
}
|
||||
|
||||
if serial_type.is_beint16() {
|
||||
if buf.len() < 2 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt16 value");
|
||||
}
|
||||
return Ok((
|
||||
OwnedValue::Integer(i16::from_be_bytes([buf[0], buf[1]]) as i64),
|
||||
2,
|
||||
));
|
||||
}
|
||||
|
||||
if serial_type.is_beint24() {
|
||||
if buf.len() < 3 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt24 value");
|
||||
}
|
||||
let sign_extension = if buf[0] <= 127 { 0 } else { 255 };
|
||||
return Ok((
|
||||
OwnedValue::Integer(i32::from_be_bytes([sign_extension, buf[0], buf[1], buf[2]]) as i64),
|
||||
3,
|
||||
));
|
||||
}
|
||||
|
||||
if serial_type.is_beint32() {
|
||||
if buf.len() < 4 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt32 value");
|
||||
}
|
||||
return Ok((
|
||||
OwnedValue::Integer(i32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) as i64),
|
||||
4,
|
||||
));
|
||||
}
|
||||
|
||||
if serial_type.is_beint48() {
|
||||
if buf.len() < 6 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt48 value");
|
||||
}
|
||||
let sign_extension = if buf[0] <= 127 { 0 } else { 255 };
|
||||
return Ok((
|
||||
OwnedValue::Integer(i64::from_be_bytes([
|
||||
sign_extension,
|
||||
sign_extension,
|
||||
buf[0],
|
||||
buf[1],
|
||||
buf[2],
|
||||
buf[3],
|
||||
buf[4],
|
||||
buf[5],
|
||||
])),
|
||||
6,
|
||||
));
|
||||
}
|
||||
|
||||
if serial_type.is_beint64() {
|
||||
if buf.len() < 8 {
|
||||
crate::bail_corrupt_error!("Invalid BEInt64 value");
|
||||
}
|
||||
return Ok((
|
||||
OwnedValue::Integer(i64::from_be_bytes([
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
|
||||
])),
|
||||
8,
|
||||
));
|
||||
}
|
||||
|
||||
if serial_type.is_befloat64() {
|
||||
if buf.len() < 8 {
|
||||
crate::bail_corrupt_error!("Invalid BEFloat64 value");
|
||||
}
|
||||
return Ok((
|
||||
OwnedValue::Float(f64::from_be_bytes([
|
||||
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
|
||||
])),
|
||||
8,
|
||||
));
|
||||
}
|
||||
|
||||
if serial_type.is_constint0() {
|
||||
return Ok((OwnedValue::Integer(0), 0));
|
||||
}
|
||||
|
||||
if serial_type.is_constint1() {
|
||||
return Ok((OwnedValue::Integer(1), 0));
|
||||
}
|
||||
|
||||
if serial_type.is_blob() {
|
||||
let n = serial_type.blob_size();
|
||||
if buf.len() < n {
|
||||
crate::bail_corrupt_error!("Invalid Blob value");
|
||||
}
|
||||
return Ok((OwnedValue::Blob(Rc::new(buf[0..n].to_vec())), n));
|
||||
}
|
||||
|
||||
if serial_type.is_string() {
|
||||
let n = serial_type.string_size();
|
||||
if buf.len() < n {
|
||||
crate::bail_corrupt_error!(
|
||||
"Invalid String value, length {} < expected length {}",
|
||||
buf.len(),
|
||||
n
|
||||
);
|
||||
}
|
||||
let bytes = buf[0..n].to_vec();
|
||||
return Ok((
|
||||
OwnedValue::Text(Text {
|
||||
value: Rc::new(bytes),
|
||||
subtype: TextSubtype::Text,
|
||||
}),
|
||||
n,
|
||||
));
|
||||
}
|
||||
|
||||
// This should never happen if validate_serial_type is used correctly
|
||||
crate::bail_corrupt_error!("Invalid serial type: {}", serial_type)
|
||||
}
|
||||
|
||||
pub fn read_varint(buf: &[u8]) -> Result<(u64, usize)> {
|
||||
|
@ -1391,128 +1477,90 @@ mod tests {
|
|||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(0, SerialType::Null)]
|
||||
#[case(1, SerialType::Int8)]
|
||||
#[case(2, SerialType::BEInt16)]
|
||||
#[case(3, SerialType::BEInt24)]
|
||||
#[case(4, SerialType::BEInt32)]
|
||||
#[case(5, SerialType::BEInt48)]
|
||||
#[case(6, SerialType::BEInt64)]
|
||||
#[case(7, SerialType::BEFloat64)]
|
||||
#[case(8, SerialType::ConstInt0)]
|
||||
#[case(9, SerialType::ConstInt1)]
|
||||
#[case(12, SerialType::Blob(0))]
|
||||
#[case(13, SerialType::String(0))]
|
||||
#[case(14, SerialType::Blob(1))]
|
||||
#[case(15, SerialType::String(1))]
|
||||
fn test_read_serial_type(#[case] input: u64, #[case] expected: SerialType) {
|
||||
let result = SerialType::try_from(input).unwrap();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_invalid_serial_type() {
|
||||
let result = SerialType::try_from(10);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(&[], SerialType::Null, OwnedValue::Null)]
|
||||
#[case(&[255], SerialType::Int8, OwnedValue::Integer(-1))]
|
||||
#[case(&[0x12, 0x34], SerialType::BEInt16, OwnedValue::Integer(0x1234))]
|
||||
#[case(&[0xFE], SerialType::Int8, OwnedValue::Integer(-2))]
|
||||
#[case(&[0x12, 0x34, 0x56], SerialType::BEInt24, OwnedValue::Integer(0x123456))]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78], SerialType::BEInt32, OwnedValue::Integer(0x12345678))]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC], SerialType::BEInt48, OwnedValue::Integer(0x123456789ABC))]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF], SerialType::BEInt64, OwnedValue::Integer(0x123456789ABCDEFF))]
|
||||
#[case(&[64, 9, 33, 251, 84, 68, 45, 24], SerialType::BEFloat64, OwnedValue::Float(std::f64::consts::PI))]
|
||||
#[case(&[], SerialType::ConstInt0, OwnedValue::Integer(0))]
|
||||
#[case(&[], SerialType::ConstInt1, OwnedValue::Integer(1))]
|
||||
#[case(&[1, 2, 3], SerialType::Blob(3), OwnedValue::Blob(vec![1, 2, 3].into()))]
|
||||
#[case(&[65, 66, 67], SerialType::String(3), OwnedValue::build_text("ABC"))]
|
||||
#[case(&[0x80], SerialType::Int8, OwnedValue::Integer(-128))]
|
||||
#[case(&[0x80, 0], SerialType::BEInt16, OwnedValue::Integer(-32768))]
|
||||
#[case(&[0x80, 0, 0], SerialType::BEInt24, OwnedValue::Integer(-8388608))]
|
||||
#[case(&[0x80, 0, 0, 0], SerialType::BEInt32, OwnedValue::Integer(-2147483648))]
|
||||
#[case(&[0x80, 0, 0, 0, 0, 0], SerialType::BEInt48, OwnedValue::Integer(-140737488355328))]
|
||||
#[case(&[0x80, 0, 0, 0, 0, 0, 0, 0], SerialType::BEInt64, OwnedValue::Integer(-9223372036854775808))]
|
||||
#[case(&[0x7f], SerialType::Int8, OwnedValue::Integer(127))]
|
||||
#[case(&[0x7f, 0xff], SerialType::BEInt16, OwnedValue::Integer(32767))]
|
||||
#[case(&[0x7f, 0xff, 0xff], SerialType::BEInt24, OwnedValue::Integer(8388607))]
|
||||
#[case(&[0x7f, 0xff, 0xff, 0xff], SerialType::BEInt32, OwnedValue::Integer(2147483647))]
|
||||
#[case(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff], SerialType::BEInt48, OwnedValue::Integer(140737488355327))]
|
||||
#[case(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], SerialType::BEInt64, OwnedValue::Integer(9223372036854775807))]
|
||||
#[case(&[], SERIAL_TYPE_NULL, OwnedValue::Null)]
|
||||
#[case(&[255], SERIAL_TYPE_INT8, OwnedValue::Integer(-1))]
|
||||
#[case(&[0x12, 0x34], SERIAL_TYPE_BEINT16, OwnedValue::Integer(0x1234))]
|
||||
#[case(&[0xFE], SERIAL_TYPE_INT8, OwnedValue::Integer(-2))]
|
||||
#[case(&[0x12, 0x34, 0x56], SERIAL_TYPE_BEINT24, OwnedValue::Integer(0x123456))]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78], SERIAL_TYPE_BEINT32, OwnedValue::Integer(0x12345678))]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC], SERIAL_TYPE_BEINT48, OwnedValue::Integer(0x123456789ABC))]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF], SERIAL_TYPE_BEINT64, OwnedValue::Integer(0x123456789ABCDEFF))]
|
||||
#[case(&[0x40, 0x09, 0x21, 0xFB, 0x54, 0x44, 0x2D, 0x18], SERIAL_TYPE_BEFLOAT64, OwnedValue::Float(std::f64::consts::PI))]
|
||||
#[case(&[1, 2], SERIAL_TYPE_CONSTINT0, OwnedValue::Integer(0))]
|
||||
#[case(&[65, 66], SERIAL_TYPE_CONSTINT1, OwnedValue::Integer(1))]
|
||||
#[case(&[1, 2, 3], 18, OwnedValue::Blob(vec![1, 2, 3].into()))]
|
||||
#[case(&[65, 66, 67], 19, OwnedValue::build_text("ABC"))]
|
||||
#[case(&[0x80], SERIAL_TYPE_INT8, OwnedValue::Integer(-128))]
|
||||
#[case(&[0x80, 0], SERIAL_TYPE_BEINT16, OwnedValue::Integer(-32768))]
|
||||
#[case(&[0x80, 0, 0], SERIAL_TYPE_BEINT24, OwnedValue::Integer(-8388608))]
|
||||
#[case(&[0x80, 0, 0, 0], SERIAL_TYPE_BEINT32, OwnedValue::Integer(-2147483648))]
|
||||
#[case(&[0x80, 0, 0, 0, 0, 0], SERIAL_TYPE_BEINT48, OwnedValue::Integer(-140737488355328))]
|
||||
#[case(&[0x80, 0, 0, 0, 0, 0, 0, 0], SERIAL_TYPE_BEINT64, OwnedValue::Integer(-9223372036854775808))]
|
||||
#[case(&[0x7f], SERIAL_TYPE_INT8, OwnedValue::Integer(127))]
|
||||
#[case(&[0x7f, 0xff], SERIAL_TYPE_BEINT16, OwnedValue::Integer(32767))]
|
||||
#[case(&[0x7f, 0xff, 0xff], SERIAL_TYPE_BEINT24, OwnedValue::Integer(8388607))]
|
||||
#[case(&[0x7f, 0xff, 0xff, 0xff], SERIAL_TYPE_BEINT32, OwnedValue::Integer(2147483647))]
|
||||
#[case(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff], SERIAL_TYPE_BEINT48, OwnedValue::Integer(140737488355327))]
|
||||
#[case(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], SERIAL_TYPE_BEINT64, OwnedValue::Integer(9223372036854775807))]
|
||||
fn test_read_value(
|
||||
#[case] buf: &[u8],
|
||||
#[case] serial_type: SerialType,
|
||||
#[case] expected: OwnedValue,
|
||||
) {
|
||||
let result = read_value(buf, &serial_type).unwrap();
|
||||
assert_eq!(result, (expected, buf.len()));
|
||||
let result = read_value(buf, serial_type).unwrap();
|
||||
assert_eq!(result.0, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serial_type_helpers() {
|
||||
assert!(SERIAL_TYPE_NULL.is_null());
|
||||
assert!(SERIAL_TYPE_INT8.is_int8());
|
||||
assert!(SERIAL_TYPE_BEINT16.is_beint16());
|
||||
assert!(SERIAL_TYPE_BEINT24.is_beint24());
|
||||
assert!(SERIAL_TYPE_BEINT32.is_beint32());
|
||||
assert!(SERIAL_TYPE_BEINT48.is_beint48());
|
||||
assert!(SERIAL_TYPE_BEINT64.is_beint64());
|
||||
assert!(SERIAL_TYPE_BEFLOAT64.is_befloat64());
|
||||
assert!(SERIAL_TYPE_CONSTINT0.is_constint0());
|
||||
assert!(SERIAL_TYPE_CONSTINT1.is_constint1());
|
||||
|
||||
assert!(12u64.is_blob());
|
||||
assert!(14u64.is_blob());
|
||||
assert!(13u64.is_string());
|
||||
assert!(15u64.is_string());
|
||||
|
||||
assert_eq!(12u64.blob_size(), 0);
|
||||
assert_eq!(14u64.blob_size(), 1);
|
||||
assert_eq!(16u64.blob_size(), 2);
|
||||
|
||||
assert_eq!(13u64.string_size(), 0);
|
||||
assert_eq!(15u64.string_size(), 1);
|
||||
assert_eq!(17u64.string_size(), 2);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(&[], SerialType::Int8)]
|
||||
#[case(&[0x12], SerialType::BEInt16)]
|
||||
#[case(&[0x12, 0x34], SerialType::BEInt24)]
|
||||
#[case(&[0x12, 0x34, 0x56], SerialType::BEInt32)]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78], SerialType::BEInt48)]
|
||||
#[case(&[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE], SerialType::BEInt64)]
|
||||
#[case(&[64, 9, 33, 251, 84, 68, 45], SerialType::BEFloat64)]
|
||||
#[case(&[1, 2], SerialType::Blob(3))]
|
||||
#[case(&[65, 66], SerialType::String(3))]
|
||||
// TODO: UTF-8 validation is disabled #[case(&[192], SerialType::String(1))] // invalid UTF-8 sequence
|
||||
fn test_read_invalid_value(#[case] buf: &[u8], #[case] serial_type: SerialType) {
|
||||
let result = read_value(buf, &serial_type);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(&[0x01], (1, 1))]
|
||||
#[case(&[0x81, 0x01], (129, 2))]
|
||||
#[case(&[0x81, 0x81, 0x01], (16513, 3))]
|
||||
#[case(&[0x81, 0x81, 0x81, 0x01], (2113665, 4))]
|
||||
#[case(&[0x81, 0x81, 0x81, 0x81, 0x01], (270549121, 5))]
|
||||
#[case(&[0x81, 0x81, 0x81, 0x81, 0x81, 0x01], (34630287489, 6))]
|
||||
#[case(&[0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x01], (4432676798593, 7))]
|
||||
#[case(&[0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x01], (567382630219905, 8))]
|
||||
#[case(&[0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x01], (145249953336295681, 9))]
|
||||
fn read_varint_test(#[case] input: &[u8], #[case] expected: (u64, usize)) {
|
||||
let result = read_varint(input).unwrap();
|
||||
#[case(0, SERIAL_TYPE_NULL)]
|
||||
#[case(1, SERIAL_TYPE_INT8)]
|
||||
#[case(2, SERIAL_TYPE_BEINT16)]
|
||||
#[case(3, SERIAL_TYPE_BEINT24)]
|
||||
#[case(4, SERIAL_TYPE_BEINT32)]
|
||||
#[case(5, SERIAL_TYPE_BEINT48)]
|
||||
#[case(6, SERIAL_TYPE_BEINT64)]
|
||||
#[case(7, SERIAL_TYPE_BEFLOAT64)]
|
||||
#[case(8, SERIAL_TYPE_CONSTINT0)]
|
||||
#[case(9, SERIAL_TYPE_CONSTINT1)]
|
||||
#[case(12, 12)] // Blob(0)
|
||||
#[case(13, 13)] // String(0)
|
||||
#[case(14, 14)] // Blob(1)
|
||||
#[case(15, 15)] // String(1)
|
||||
fn test_validate_serial_type(#[case] input: u64, #[case] expected: SerialType) {
|
||||
let result = validate_serial_type(input).unwrap();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_invalid_varint() {
|
||||
let buf = [0b11111110];
|
||||
let result = read_varint(&buf);
|
||||
fn test_invalid_serial_type() {
|
||||
let result = validate_serial_type(10);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ** 0x00 becomes 0x00000000
|
||||
// ** 0x7f becomes 0x0000007f
|
||||
// ** 0x81 0x00 becomes 0x00000080
|
||||
// ** 0x82 0x00 becomes 0x00000100
|
||||
// ** 0x80 0x7f becomes 0x0000007f
|
||||
// ** 0x81 0x91 0xd1 0xac 0x78 becomes 0x12345678
|
||||
// ** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
|
||||
#[rstest]
|
||||
#[case((0, 1), &[0x00])]
|
||||
#[case((1, 1), &[0x01])]
|
||||
#[case((129, 2), &[0x81, 0x01] )]
|
||||
#[case((16513, 3), &[0x81, 0x81, 0x01] )]
|
||||
#[case((2113665, 4), &[0x81, 0x81, 0x81, 0x01] )]
|
||||
#[case((270549121, 5), &[0x81, 0x81, 0x81, 0x81, 0x01] )]
|
||||
#[case((34630287489, 6), &[0x81, 0x81, 0x81, 0x81, 0x81, 0x01] )]
|
||||
#[case((4432676798593, 7), &[0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x01] )]
|
||||
#[case((567382630219905, 8), &[0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x01] )]
|
||||
#[case((145249953336295681, 9), &[0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x01] )]
|
||||
fn test_write_varint(#[case] value: (u64, usize), #[case] output: &[u8]) {
|
||||
let mut buf: [u8; 10] = [0; 10];
|
||||
let n = write_varint(&mut buf, value.0);
|
||||
assert_eq!(n, value.1);
|
||||
for i in 0..output.len() {
|
||||
assert_eq!(buf[i], output[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue