use crate::tree::Key;
use btrfs_disk::raw;
use bytes::BufMut;
use std::mem;
use uuid::Uuid;
fn put_uuid(buf: &mut impl BufMut, uuid: &Uuid) {
buf.put_slice(uuid.as_bytes());
}
#[must_use]
pub fn root_item(
generation: u64,
bytenr: u64,
root_dirid: u64,
nodesize: u32,
) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_root_item>();
let inode_size = mem::size_of::<raw::btrfs_inode_item>();
let mut buf = Vec::with_capacity(size);
buf.put_u64_le(generation); buf.put_bytes(0, 32); buf.put_u32_le(1); buf.put_bytes(0, 8); buf.put_u32_le(0o40755); buf.put_bytes(0, inode_size - 56);
buf.put_u64_le(generation); buf.put_u64_le(root_dirid); buf.put_u64_le(bytenr); buf.put_u64_le(0); buf.put_u64_le(u64::from(nodesize)); buf.put_u64_le(0); buf.put_u64_le(0); buf.put_u32_le(1);
let level_off = mem::offset_of!(raw::btrfs_root_item, level);
let pad_to_level = level_off - buf.len();
buf.put_bytes(0, pad_to_level);
buf.put_u8(0);
buf.put_u64_le(generation);
buf.resize(size, 0);
buf
}
#[must_use]
pub fn chunk_tree_root_item(
generation: u64,
bytenr: u64,
nodesize: u32,
) -> Vec<u8> {
root_item(
generation,
bytenr,
u64::from(raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID),
nodesize,
)
}
#[must_use]
pub fn extent_item(
refs: u64,
generation: u64,
skinny: bool,
owner_root: u64,
) -> Vec<u8> {
let tree_block_info_size = if skinny {
0
} else {
mem::size_of::<raw::btrfs_tree_block_info>()
};
let mut buf = Vec::new();
buf.put_u64_le(refs);
buf.put_u64_le(generation);
buf.put_u64_le(u64::from(raw::BTRFS_EXTENT_FLAG_TREE_BLOCK));
buf.put_bytes(0, tree_block_info_size);
#[allow(clippy::cast_possible_truncation)] buf.put_u8(raw::BTRFS_TREE_BLOCK_REF_KEY as u8);
buf.put_u64_le(owner_root);
buf
}
#[must_use]
pub fn block_group_item(used: u64, chunk_objectid: u64, flags: u64) -> Vec<u8> {
let mut buf =
Vec::with_capacity(mem::size_of::<raw::btrfs_block_group_item>());
buf.put_u64_le(used);
buf.put_u64_le(chunk_objectid);
buf.put_u64_le(flags);
buf
}
#[must_use]
pub fn dev_item(
devid: u64,
total_bytes: u64,
bytes_used: u64,
sector_size: u32,
dev_uuid: &Uuid,
fsid: &Uuid,
) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_dev_item>();
let mut buf = Vec::with_capacity(size);
buf.put_u64_le(devid);
buf.put_u64_le(total_bytes);
buf.put_u64_le(bytes_used);
buf.put_u32_le(sector_size); buf.put_u32_le(sector_size); buf.put_u32_le(sector_size); buf.put_bytes(0, 30); put_uuid(&mut buf, dev_uuid);
put_uuid(&mut buf, fsid);
buf
}
use crate::layout::StripeInfo;
#[must_use]
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::too_many_arguments)]
pub fn chunk_item(
length: u64,
owner: u64,
chunk_type: u64,
io_align: u32,
io_width: u32,
sector_size: u32,
sub_stripes: u16,
stripes: &[StripeInfo],
) -> Vec<u8> {
let mut buf = Vec::new();
buf.put_u64_le(length);
buf.put_u64_le(owner);
buf.put_u64_le(crate::layout::STRIPE_LEN);
buf.put_u64_le(chunk_type);
buf.put_u32_le(io_align);
buf.put_u32_le(io_width);
buf.put_u32_le(sector_size);
buf.put_u16_le(stripes.len() as u16);
buf.put_u16_le(sub_stripes);
for stripe in stripes {
buf.put_u64_le(stripe.devid);
buf.put_u64_le(stripe.offset);
put_uuid(&mut buf, &stripe.dev_uuid);
}
buf
}
#[must_use]
pub fn chunk_item_bootstrap(
length: u64,
owner: u64,
chunk_type: u64,
sector_size: u32,
stripe: &StripeInfo,
) -> Vec<u8> {
chunk_item(
length,
owner,
chunk_type,
sector_size,
sector_size,
sector_size,
1, &[StripeInfo {
devid: stripe.devid,
offset: stripe.offset,
dev_uuid: stripe.dev_uuid,
}],
)
}
#[must_use]
pub fn dev_extent(
chunk_tree: u64,
chunk_objectid: u64,
chunk_offset: u64,
length: u64,
chunk_tree_uuid: &Uuid,
) -> Vec<u8> {
let mut buf = Vec::with_capacity(mem::size_of::<raw::btrfs_dev_extent>());
buf.put_u64_le(chunk_tree);
buf.put_u64_le(chunk_objectid);
buf.put_u64_le(chunk_offset);
buf.put_u64_le(length);
put_uuid(&mut buf, chunk_tree_uuid);
buf
}
#[must_use]
pub fn dev_stats_zeroed() -> Vec<u8> {
vec![0u8; 5 * 8]
}
#[must_use]
pub fn qgroup_status(
version: u64,
generation: u64,
flags: u64,
enable_gen: Option<u64>,
) -> Vec<u8> {
let cap = if enable_gen.is_some() {
mem::size_of::<raw::btrfs_qgroup_status_item>()
} else {
4 * 8
};
let mut buf = Vec::with_capacity(cap);
buf.put_u64_le(version);
buf.put_u64_le(generation);
buf.put_u64_le(flags);
buf.put_u64_le(0); if let Some(eg) = enable_gen {
buf.put_u64_le(eg);
}
buf
}
#[must_use]
pub fn qgroup_info_zeroed() -> Vec<u8> {
vec![0u8; mem::size_of::<raw::btrfs_qgroup_info_item>()]
}
#[must_use]
pub fn qgroup_info(
generation: u64,
referenced: u64,
exclusive: u64,
) -> Vec<u8> {
let mut buf =
Vec::with_capacity(mem::size_of::<raw::btrfs_qgroup_info_item>());
buf.put_u64_le(generation);
buf.put_u64_le(referenced); buf.put_u64_le(referenced); buf.put_u64_le(exclusive); buf.put_u64_le(exclusive); buf
}
#[must_use]
pub fn qgroup_limit_zeroed() -> Vec<u8> {
vec![0u8; mem::size_of::<raw::btrfs_qgroup_limit_item>()]
}
#[must_use]
pub fn free_space_info(extent_count: u32, flags: u32) -> Vec<u8> {
let mut buf = Vec::with_capacity(8);
buf.put_u32_le(extent_count);
buf.put_u32_le(flags);
buf
}
#[must_use]
pub fn inode_item_dir(generation: u64, nbytes: u64, now_sec: u64) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_inode_item>();
let mut buf = Vec::with_capacity(size);
buf.put_u64_le(generation); buf.put_u64_le(0); buf.put_u64_le(0); buf.put_u64_le(nbytes); buf.put_u64_le(0); buf.put_u32_le(1); buf.put_u32_le(0); buf.put_u32_le(0); buf.put_u32_le(0o40755); buf.put_u64_le(0); buf.put_u64_le(0); buf.put_u64_le(0); buf.put_bytes(0, 32);
for _ in 0..4 {
buf.put_u64_le(now_sec);
buf.put_u32_le(0); }
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn inode_ref(index: u64, name: &[u8]) -> Vec<u8> {
let mut buf = Vec::with_capacity(8 + 2 + name.len());
buf.put_u64_le(index);
buf.put_u16_le(name.len() as u16);
buf.put_slice(name);
buf
}
pub struct InodeItemArgs {
pub generation: u64,
pub transid: u64,
pub size: u64,
pub nbytes: u64,
pub nlink: u32,
pub uid: u32,
pub gid: u32,
pub mode: u32,
pub rdev: u64,
pub flags: u64,
pub atime: (u64, u32),
pub ctime: (u64, u32),
pub mtime: (u64, u32),
pub otime: (u64, u32),
}
#[must_use]
pub fn inode_item(args: &InodeItemArgs) -> Vec<u8> {
let item_size = mem::size_of::<raw::btrfs_inode_item>();
let mut buf = Vec::with_capacity(item_size);
buf.put_u64_le(args.generation);
buf.put_u64_le(args.transid);
buf.put_u64_le(args.size);
buf.put_u64_le(args.nbytes);
buf.put_u64_le(0); buf.put_u32_le(args.nlink);
buf.put_u32_le(args.uid);
buf.put_u32_le(args.gid);
buf.put_u32_le(args.mode);
buf.put_u64_le(args.rdev);
buf.put_u64_le(args.flags);
buf.put_u64_le(0); buf.put_bytes(0, 32);
for &(sec, nsec) in &[args.atime, args.ctime, args.mtime, args.otime] {
buf.put_u64_le(sec);
buf.put_u32_le(nsec);
}
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn dir_item(
location: &Key,
transid: u64,
name: &[u8],
file_type: u8,
) -> Vec<u8> {
let mut buf = Vec::with_capacity(30 + name.len());
buf.put_slice(&disk_key(location));
buf.put_u64_le(transid);
buf.put_u16_le(0); buf.put_u16_le(name.len() as u16);
buf.put_u8(file_type);
buf.put_slice(name);
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn xattr_item(name: &[u8], value: &[u8]) -> Vec<u8> {
let zeroed_location = Key::new(0, 0, 0);
let mut buf = Vec::with_capacity(30 + name.len() + value.len());
buf.put_slice(&disk_key(&zeroed_location));
buf.put_u64_le(0); buf.put_u16_le(value.len() as u16); buf.put_u16_le(name.len() as u16);
buf.put_u8(raw::BTRFS_FT_XATTR as u8);
buf.put_slice(name);
buf.put_slice(value);
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn file_extent_inline(
generation: u64,
ram_bytes: u64,
compression: u8,
data: &[u8],
) -> Vec<u8> {
let mut buf = Vec::with_capacity(21 + data.len());
buf.put_u64_le(generation);
buf.put_u64_le(ram_bytes);
buf.put_u8(compression);
buf.put_u8(0); buf.put_u16_le(0); buf.put_u8(raw::BTRFS_FILE_EXTENT_INLINE as u8);
buf.put_slice(data);
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn file_extent_reg(
generation: u64,
disk_bytenr: u64,
disk_num_bytes: u64,
offset: u64,
num_bytes: u64,
ram_bytes: u64,
compression: u8,
) -> Vec<u8> {
let mut buf = Vec::with_capacity(53);
buf.put_u64_le(generation);
buf.put_u64_le(ram_bytes);
buf.put_u8(compression);
buf.put_u8(0); buf.put_u16_le(0); buf.put_u8(raw::BTRFS_FILE_EXTENT_REG as u8);
buf.put_u64_le(disk_bytenr);
buf.put_u64_le(disk_num_bytes);
buf.put_u64_le(offset);
buf.put_u64_le(num_bytes);
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn data_extent_item(
refs: u64,
generation: u64,
root: u64,
objectid: u64,
offset: u64,
count: u32,
) -> Vec<u8> {
let mut buf = Vec::with_capacity(53);
buf.put_u64_le(refs);
buf.put_u64_le(generation);
buf.put_u64_le(u64::from(raw::BTRFS_EXTENT_FLAG_DATA));
buf.put_u8(raw::BTRFS_EXTENT_DATA_REF_KEY as u8);
buf.put_u64_le(root);
buf.put_u64_le(objectid);
buf.put_u64_le(offset);
buf.put_u32_le(count);
buf
}
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub fn root_ref(dirid: u64, sequence: u64, name: &[u8]) -> Vec<u8> {
let mut buf = Vec::with_capacity(18 + name.len());
buf.put_u64_le(dirid);
buf.put_u64_le(sequence);
buf.put_u16_le(name.len() as u16);
buf.put_slice(name);
buf
}
#[must_use]
pub fn disk_key(key: &Key) -> Vec<u8> {
let mut buf = vec![0u8; 17];
key.write_to(&mut buf, 0);
buf
}
#[cfg(test)]
mod tests {
use super::*;
use btrfs_disk::items;
#[test]
fn roundtrip_block_group_item() {
let data = block_group_item(
4096,
u64::from(raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID),
u64::from(raw::BTRFS_BLOCK_GROUP_SYSTEM),
);
let parsed = items::BlockGroupItem::parse(&data).unwrap();
assert_eq!(parsed.used, 4096);
assert_eq!(
parsed.chunk_objectid,
u64::from(raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID)
);
assert_eq!(parsed.flags, items::BlockGroupFlags::SYSTEM);
}
#[test]
fn roundtrip_dev_item() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let fsid =
Uuid::parse_str("cafebabe-cafe-babe-cafe-babecafebabe").unwrap();
let data = dev_item(1, 1_000_000_000, 4_000_000, 4096, &uuid, &fsid);
let parsed = items::DeviceItem::parse(&data).unwrap();
assert_eq!(parsed.devid, 1);
assert_eq!(parsed.total_bytes, 1_000_000_000);
assert_eq!(parsed.bytes_used, 4_000_000);
assert_eq!(parsed.io_align, 4096);
assert_eq!(parsed.io_width, 4096);
assert_eq!(parsed.sector_size, 4096);
assert_eq!(parsed.uuid, uuid);
assert_eq!(parsed.fsid, fsid);
}
#[test]
fn roundtrip_chunk_item() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let stripe = StripeInfo {
devid: 1,
offset: 0x100000,
dev_uuid: uuid,
};
let data = chunk_item_bootstrap(
4 * 1024 * 1024,
u64::from(raw::BTRFS_EXTENT_TREE_OBJECTID),
u64::from(raw::BTRFS_BLOCK_GROUP_SYSTEM),
4096,
&stripe,
);
let parsed = items::ChunkItem::parse(&data).unwrap();
assert_eq!(parsed.length, 4 * 1024 * 1024);
assert_eq!(parsed.owner, u64::from(raw::BTRFS_EXTENT_TREE_OBJECTID));
assert_eq!(parsed.chunk_type, items::BlockGroupFlags::SYSTEM);
assert_eq!(parsed.num_stripes, 1);
assert_eq!(parsed.stripes.len(), 1);
assert_eq!(parsed.stripes[0].devid, 1);
assert_eq!(parsed.stripes[0].offset, 0x100000);
assert_eq!(parsed.stripes[0].dev_uuid, uuid);
}
#[test]
fn roundtrip_dev_extent() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let data = dev_extent(
u64::from(raw::BTRFS_CHUNK_TREE_OBJECTID),
u64::from(raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID),
0x100000,
4 * 1024 * 1024,
&uuid,
);
let parsed = items::DeviceExtent::parse(&data).unwrap();
assert_eq!(
parsed.chunk_tree,
u64::from(raw::BTRFS_CHUNK_TREE_OBJECTID)
);
assert_eq!(
parsed.chunk_objectid,
u64::from(raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID)
);
assert_eq!(parsed.chunk_offset, 0x100000);
assert_eq!(parsed.length, 4 * 1024 * 1024);
assert_eq!(parsed.chunk_tree_uuid, uuid);
}
#[test]
fn roundtrip_free_space_info() {
let data = free_space_info(3, 0);
let parsed = items::FreeSpaceInfo::parse(&data).unwrap();
assert_eq!(parsed.extent_count, 3);
assert_eq!(parsed.flags, items::FreeSpaceInfoFlags::empty());
}
#[test]
fn roundtrip_root_item() {
let data = root_item(
1,
0x100000,
u64::from(raw::BTRFS_FIRST_FREE_OBJECTID),
16384,
);
let parsed = items::RootItem::parse(&data).unwrap();
assert_eq!(parsed.generation, 1);
assert_eq!(parsed.bytenr, 0x100000);
assert_eq!(
parsed.root_dirid,
u64::from(raw::BTRFS_FIRST_FREE_OBJECTID)
);
assert_eq!(parsed.refs, 1);
assert_eq!(parsed.generation_v2, 1);
}
#[test]
fn extent_item_skinny_size() {
let data = extent_item(1, 1, true, 5);
assert_eq!(data.len(), mem::size_of::<raw::btrfs_extent_item>() + 9);
}
#[test]
fn extent_item_non_skinny_size() {
let data = extent_item(1, 1, false, 5);
assert_eq!(
data.len(),
mem::size_of::<raw::btrfs_extent_item>()
+ mem::size_of::<raw::btrfs_tree_block_info>()
+ 9
);
}
#[test]
fn dev_stats_zeroed_size() {
let data = dev_stats_zeroed();
assert_eq!(data.len(), 40);
assert!(data.iter().all(|&b| b == 0));
}
#[test]
#[allow(clippy::cast_possible_truncation)]
fn roundtrip_chunk_item_dup() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let stripes = [
StripeInfo {
devid: 1,
offset: 5 * 1024 * 1024,
dev_uuid: uuid,
},
StripeInfo {
devid: 1,
offset: 5 * 1024 * 1024 + 32 * 1024 * 1024,
dev_uuid: uuid,
},
];
let data = chunk_item(
32 * 1024 * 1024,
u64::from(raw::BTRFS_EXTENT_TREE_OBJECTID),
u64::from(raw::BTRFS_BLOCK_GROUP_METADATA)
| u64::from(raw::BTRFS_BLOCK_GROUP_DUP),
crate::layout::STRIPE_LEN as u32,
crate::layout::STRIPE_LEN as u32,
4096,
1,
&stripes,
);
let parsed = items::ChunkItem::parse(&data).unwrap();
assert_eq!(parsed.length, 32 * 1024 * 1024);
assert_eq!(parsed.num_stripes, 2);
assert_eq!(parsed.stripes.len(), 2);
assert_eq!(parsed.stripes[0].devid, 1);
assert_eq!(parsed.stripes[0].offset, 5 * 1024 * 1024);
assert_eq!(parsed.stripes[1].devid, 1);
assert_eq!(
parsed.stripes[1].offset,
5 * 1024 * 1024 + 32 * 1024 * 1024
);
assert_eq!(parsed.io_align, crate::layout::STRIPE_LEN as u32);
assert_eq!(parsed.io_width, crate::layout::STRIPE_LEN as u32);
}
#[test]
#[allow(clippy::cast_possible_truncation)]
fn roundtrip_chunk_item_non_bootstrap_single() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let stripes = [StripeInfo {
devid: 1,
offset: 69 * 1024 * 1024,
dev_uuid: uuid,
}];
let data = chunk_item(
64 * 1024 * 1024,
u64::from(raw::BTRFS_EXTENT_TREE_OBJECTID),
u64::from(raw::BTRFS_BLOCK_GROUP_DATA),
crate::layout::STRIPE_LEN as u32,
crate::layout::STRIPE_LEN as u32,
4096,
1,
&stripes,
);
let parsed = items::ChunkItem::parse(&data).unwrap();
assert_eq!(parsed.length, 64 * 1024 * 1024);
assert_eq!(parsed.num_stripes, 1);
assert_eq!(parsed.io_align, crate::layout::STRIPE_LEN as u32);
assert_eq!(parsed.io_width, crate::layout::STRIPE_LEN as u32);
}
#[test]
fn roundtrip_inode_item() {
let data = inode_item(&InodeItemArgs {
generation: 1,
transid: 1,
size: 4096,
nbytes: 4096,
nlink: 2,
uid: 1000,
gid: 1000,
mode: 0o100644,
rdev: 0,
flags: 0,
atime: (1000, 500),
ctime: (1001, 600),
mtime: (1002, 700),
otime: (1003, 800),
});
assert_eq!(data.len(), mem::size_of::<raw::btrfs_inode_item>());
}
#[test]
#[allow(clippy::cast_possible_truncation)]
fn roundtrip_dir_item() {
let location = Key::new(257, raw::BTRFS_INODE_ITEM_KEY as u8, 0);
let data =
dir_item(&location, 1, b"hello.txt", raw::BTRFS_FT_REG_FILE as u8);
let parsed = items::DirItem::parse_all(&data);
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].name, b"hello.txt");
assert_eq!(parsed[0].transid, 1);
assert_eq!(parsed[0].file_type, items::FileType::RegFile);
assert_eq!(parsed[0].location.objectid, 257);
}
#[test]
fn roundtrip_xattr_item() {
let data = xattr_item(b"user.test", b"value123");
let parsed = items::DirItem::parse_all(&data);
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].name, b"user.test");
assert_eq!(parsed[0].data, b"value123");
assert_eq!(parsed[0].file_type, items::FileType::Xattr);
}
#[test]
fn roundtrip_file_extent_inline() {
let data = file_extent_inline(1, 5, 0, b"hello");
let parsed = items::FileExtentItem::parse(&data).unwrap();
assert_eq!(parsed.generation, 1);
assert_eq!(parsed.ram_bytes, 5);
assert_eq!(parsed.compression, items::CompressionType::None);
assert_eq!(parsed.extent_type, items::FileExtentType::Inline);
match parsed.body {
items::FileExtentBody::Inline { inline_size } => {
assert_eq!(inline_size, 5);
}
_ => panic!("expected inline body"),
}
}
#[test]
fn roundtrip_file_extent_reg() {
let data = file_extent_reg(1, 0x500000, 4096, 0, 4096, 4096, 0);
assert_eq!(data.len(), 53);
let parsed = items::FileExtentItem::parse(&data).unwrap();
assert_eq!(parsed.generation, 1);
assert_eq!(parsed.ram_bytes, 4096);
assert_eq!(parsed.extent_type, items::FileExtentType::Regular);
match parsed.body {
items::FileExtentBody::Regular {
disk_bytenr,
disk_num_bytes,
offset,
num_bytes,
} => {
assert_eq!(disk_bytenr, 0x500000);
assert_eq!(disk_num_bytes, 4096);
assert_eq!(offset, 0);
assert_eq!(num_bytes, 4096);
}
_ => panic!("expected regular body"),
}
}
#[test]
fn data_extent_item_size() {
let data = data_extent_item(1, 1, 5, 257, 0, 1);
assert_eq!(data.len(), 53);
}
#[test]
fn roundtrip_root_ref() {
let data = root_ref(256, 3, b"mysubvol");
let parsed = items::RootRef::parse(&data).unwrap();
assert_eq!(parsed.dirid, 256);
assert_eq!(parsed.sequence, 3);
assert_eq!(parsed.name, b"mysubvol");
}
#[test]
fn roundtrip_qgroup_status_quota() {
let data = qgroup_status(1, 42, 0x5, None);
assert_eq!(data.len(), 32); let parsed = items::QgroupStatus::parse(&data).unwrap();
assert_eq!(parsed.version, 1);
assert_eq!(parsed.generation, 42);
assert_eq!(parsed.flags, 0x5);
assert_eq!(parsed.scan, 0);
assert!(parsed.enable_gen.is_none());
}
#[test]
fn roundtrip_qgroup_status_squota() {
let data = qgroup_status(1, 42, 0x9, Some(42));
assert_eq!(data.len(), 40); let parsed = items::QgroupStatus::parse(&data).unwrap();
assert_eq!(parsed.version, 1);
assert_eq!(parsed.generation, 42);
assert_eq!(parsed.flags, 0x9);
assert_eq!(parsed.enable_gen, Some(42));
}
#[test]
fn qgroup_info_zeroed_size() {
let data = qgroup_info_zeroed();
assert_eq!(data.len(), 40); let parsed = items::QgroupInfo::parse(&data).unwrap();
assert_eq!(parsed.generation, 0);
assert_eq!(parsed.referenced, 0);
assert_eq!(parsed.exclusive, 0);
}
#[test]
fn qgroup_limit_zeroed_size() {
let data = qgroup_limit_zeroed();
assert_eq!(data.len(), 40); let parsed = items::QgroupLimit::parse(&data).unwrap();
assert_eq!(parsed.flags, 0);
assert_eq!(parsed.max_referenced, 0);
assert_eq!(parsed.max_exclusive, 0);
}
}