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:
Pekka Enberg 2025-02-26 13:22:42 +02:00
parent d1fa995ac5
commit 6d44ad22fd

View file

@ -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]);
}
}
}