aconfig: add whole table serialization test
1, Add whole table serialization test to also lock down how the buckets
are serialized. Before this change, we are only locking down the header
and nodes serialization.
2, Switch over to the absolute offset in the file instead of relative
offset. Before we are using relative offset. For example, if a bucket is
Some(10), it means that it is pointing to 10 bytes over the start of the
node region of the table. Now it will be the absolute byte offset with
respect to the start of the table.
Bug: b/312243587
Test: atest aconfig.test
Change-Id: If7abc8c6b6687c0bc0c40bbfc6afbe0e46ece770
diff --git a/tools/aconfig/src/storage/package_table.rs b/tools/aconfig/src/storage/package_table.rs
index a6ce013..940c5b2 100644
--- a/tools/aconfig/src/storage/package_table.rs
+++ b/tools/aconfig/src/storage/package_table.rs
@@ -87,6 +87,7 @@
}
}
+#[derive(PartialEq, Debug)]
pub struct PackageTable {
pub header: PackageTableHeader,
pub buckets: Vec<Option<u32>>,
@@ -104,11 +105,17 @@
nodes: packages.iter().map(|pkg| PackageTableNode::new(pkg, num_buckets)).collect(),
};
+ // initialize all header fields
+ table.header.bucket_offset = table.header.as_bytes().len() as u32;
+ table.header.node_offset = table.header.bucket_offset + num_buckets * 4;
+ table.header.file_size = table.header.node_offset
+ + table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32;
+
// sort nodes by bucket index for efficiency
table.nodes.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
// fill all node offset
- let mut offset = 0;
+ let mut offset = table.header.node_offset;
for i in 0..table.nodes.len() {
let node_bucket_idx = table.nodes[i].bucket_index;
let next_node_bucket_idx = if i + 1 < table.nodes.len() {
@@ -129,12 +136,6 @@
}
}
- // fill table region offset
- table.header.bucket_offset = table.header.as_bytes().len() as u32;
- table.header.node_offset = table.header.bucket_offset + num_buckets * 4;
- table.header.file_size = table.header.node_offset
- + table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32;
-
Ok(table)
}
@@ -190,6 +191,32 @@
}
}
+ impl PackageTable {
+ // test only method to deserialize back into the table struct
+ fn from_bytes(bytes: &[u8]) -> Result<Self> {
+ let header = PackageTableHeader::from_bytes(bytes)?;
+ let num_packages = header.num_packages;
+ let num_buckets = storage::get_table_size(num_packages)?;
+ let mut head = header.as_bytes().len();
+ let buckets = (0..num_buckets)
+ .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
+ 0 => None,
+ val => Some(val),
+ })
+ .collect();
+ let nodes = (0..num_packages)
+ .map(|_| {
+ let node = PackageTableNode::from_bytes(&bytes[head..], num_buckets).unwrap();
+ head += node.as_bytes().len();
+ node
+ })
+ .collect();
+
+ let table = Self { header, buckets, nodes };
+ Ok(table)
+ }
+ }
+
pub fn create_test_package_table() -> Result<PackageTable> {
let caches = parse_all_test_flags();
let packages = group_flags_by_package(caches.iter());
@@ -214,7 +241,7 @@
assert_eq!(header, &expected_header);
let buckets: &Vec<Option<u32>> = &package_table.as_ref().unwrap().buckets;
- let expected: Vec<Option<u32>> = vec![Some(0), None, None, Some(50), None, None, None];
+ let expected: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
assert_eq!(buckets, &expected);
let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
@@ -231,7 +258,7 @@
package_name: String::from("com.android.aconfig.storage.test_1"),
package_id: 0,
boolean_offset: 0,
- next_offset: Some(100),
+ next_offset: Some(158),
bucket_index: 3,
};
assert_eq!(nodes[1], second_node_expected);
@@ -250,18 +277,23 @@
fn test_serialization() {
let package_table = create_test_package_table();
assert!(package_table.is_ok());
+ let package_table = package_table.unwrap();
- let header: &PackageTableHeader = &package_table.as_ref().unwrap().header;
+ let header: &PackageTableHeader = &package_table.header;
let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
assert!(reinterpreted_header.is_ok());
assert_eq!(header, &reinterpreted_header.unwrap());
- let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
+ let nodes: &Vec<PackageTableNode> = &package_table.nodes;
let num_buckets = storage::get_table_size(header.num_packages).unwrap();
for node in nodes.iter() {
let reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes(), num_buckets);
assert!(reinterpreted_node.is_ok());
assert_eq!(node, &reinterpreted_node.unwrap());
}
+
+ let reinterpreted_table = PackageTable::from_bytes(&package_table.as_bytes());
+ assert!(reinterpreted_table.is_ok());
+ assert_eq!(&package_table, &reinterpreted_table.unwrap());
}
}