Merge "aconfig: remove bucket_index from PackageTableNode/FlagTableNode struct" into main am: 4767d785c1

Original change: https://android-review.googlesource.com/c/platform/build/+/2932354

Change-Id: I59df9c45001095530555a1fa8e39fea45189c6bb
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Dennis Shen
2024-01-30 15:51:34 +00:00
committed by Automerger Merge Worker
4 changed files with 152 additions and 133 deletions

View File

@@ -32,40 +32,58 @@ fn new_header(container: &str, num_flags: u32) -> FlagTableHeader {
} }
} }
fn new_node( // a struct that contains FlagTableNode and a bunch of other information to help
package_id: u32, // flag table creation
flag_name: &str, #[derive(PartialEq, Debug, Clone)]
flag_type: u16, struct FlagTableNodeWrapper {
flag_id: u16, pub node: FlagTableNode,
num_buckets: u32, pub bucket_index: u32,
) -> FlagTableNode {
let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);
FlagTableNode {
package_id,
flag_name: flag_name.to_string(),
flag_type,
flag_id,
next_offset: None,
bucket_index,
}
} }
fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result<Vec<FlagTableNode>> { impl FlagTableNodeWrapper {
let flag_ids = assign_flag_ids(package.package_name, package.boolean_flags.iter().copied())?; fn new(
package package_id: u32,
.boolean_flags flag_name: &str,
.iter() flag_type: u16,
.map(|&pf| { flag_id: u16,
let fid = flag_ids num_buckets: u32,
.get(pf.name()) ) -> Self {
.ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?; let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);
// all flags are boolean value at the moment, thus using the last bit. When more let node = FlagTableNode {
// flag value types are supported, flag value type information should come from the package_id,
// parsed flag, and we will set the flag_type bit mask properly. flag_name: flag_name.to_string(),
let flag_type = 1; flag_type,
Ok(new_node(package.package_id, pf.name(), flag_type, *fid, num_buckets)) flag_id,
}) next_offset: None,
.collect::<Result<Vec<_>>>() };
Self { node, bucket_index }
}
fn create_nodes(package: &FlagPackage, num_buckets: u32) -> Result<Vec<Self>> {
let flag_ids =
assign_flag_ids(package.package_name, package.boolean_flags.iter().copied())?;
package
.boolean_flags
.iter()
.map(|&pf| {
let fid = flag_ids
.get(pf.name())
.ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
// all flags are boolean value at the moment, thus using the last bit.
// When more flag value types are supported, flag value type information
// should come from the parsed flag, and we will set the flag_type bit
// mask properly.
let flag_type = 1;
Ok(Self::new(
package.package_id,
pf.name(),
flag_type,
*fid,
num_buckets,
))
})
.collect::<Result<Vec<_>>>()
}
} }
pub fn create_flag_table(container: &str, packages: &[FlagPackage]) -> Result<FlagTable> { pub fn create_flag_table(container: &str, packages: &[FlagPackage]) -> Result<FlagTable> {
@@ -73,44 +91,48 @@ pub fn create_flag_table(container: &str, packages: &[FlagPackage]) -> Result<Fl
let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum(); let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
let num_buckets = get_table_size(num_flags)?; let num_buckets = get_table_size(num_flags)?;
let mut table = FlagTable { let mut header = new_header(container, num_flags);
header: new_header(container, num_flags), let mut buckets = vec![None; num_buckets as usize];
buckets: vec![None; num_buckets as usize], let mut node_wrappers = packages
nodes: packages .iter()
.iter() .map(|pkg| FlagTableNodeWrapper::create_nodes(pkg, num_buckets))
.map(|pkg| create_nodes(pkg, num_buckets)) .collect::<Result<Vec<_>>>()?
.collect::<Result<Vec<_>>>()? .concat();
.concat(),
};
// initialize all header fields // initialize all header fields
table.header.bucket_offset = table.header.as_bytes().len() as u32; header.bucket_offset = header.as_bytes().len() as u32;
table.header.node_offset = table.header.bucket_offset + num_buckets * 4; header.node_offset = header.bucket_offset + num_buckets * 4;
table.header.file_size = table.header.node_offset header.file_size = header.node_offset
+ table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32; + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
// sort nodes by bucket index for efficiency // sort nodes by bucket index for efficiency
table.nodes.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index)); node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
// fill all node offset // fill all node offset
let mut offset = table.header.node_offset; let mut offset = header.node_offset;
for i in 0..table.nodes.len() { for i in 0..node_wrappers.len() {
let node_bucket_idx = table.nodes[i].bucket_index; let node_bucket_idx = node_wrappers[i].bucket_index;
let next_node_bucket_idx = let next_node_bucket_idx =
if i + 1 < table.nodes.len() { Some(table.nodes[i + 1].bucket_index) } else { None }; if i + 1 < node_wrappers.len() { Some(node_wrappers[i + 1].bucket_index) } else { None };
if table.buckets[node_bucket_idx as usize].is_none() { if buckets[node_bucket_idx as usize].is_none() {
table.buckets[node_bucket_idx as usize] = Some(offset); buckets[node_bucket_idx as usize] = Some(offset);
} }
offset += table.nodes[i].as_bytes().len() as u32; offset += node_wrappers[i].node.as_bytes().len() as u32;
if let Some(index) = next_node_bucket_idx { if let Some(index) = next_node_bucket_idx {
if index == node_bucket_idx { if index == node_bucket_idx {
table.nodes[i].next_offset = Some(offset); node_wrappers[i].node.next_offset = Some(offset);
} }
} }
} }
let table = FlagTable {
header,
buckets,
nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(),
};
Ok(table) Ok(table)
} }
@@ -126,7 +148,6 @@ mod tests {
flag_type: u16, flag_type: u16,
flag_id: u16, flag_id: u16,
next_offset: Option<u32>, next_offset: Option<u32>,
bucket_index: u32,
) -> FlagTableNode { ) -> FlagTableNode {
FlagTableNode { FlagTableNode {
package_id, package_id,
@@ -134,7 +155,6 @@ mod tests {
flag_type, flag_type,
flag_id, flag_id,
next_offset, next_offset,
bucket_index,
} }
} }
@@ -186,13 +206,13 @@ mod tests {
let nodes: &Vec<FlagTableNode> = &flag_table.as_ref().unwrap().nodes; let nodes: &Vec<FlagTableNode> = &flag_table.as_ref().unwrap().nodes;
assert_eq!(nodes.len(), 8); assert_eq!(nodes.len(), 8);
assert_eq!(nodes[0], new_expected_node(0, "enabled_ro", 1, 1, None, 0)); assert_eq!(nodes[0], new_expected_node(0, "enabled_ro", 1, 1, None));
assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(150), 1)); assert_eq!(nodes[1], new_expected_node(0, "enabled_rw", 1, 2, Some(150)));
assert_eq!(nodes[2], new_expected_node(1, "disabled_ro", 1, 0, None, 1)); assert_eq!(nodes[2], new_expected_node(1, "disabled_ro", 1, 0, None));
assert_eq!(nodes[3], new_expected_node(2, "enabled_ro", 1, 1, None, 5)); assert_eq!(nodes[3], new_expected_node(2, "enabled_ro", 1, 1, None));
assert_eq!(nodes[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235), 7)); assert_eq!(nodes[4], new_expected_node(1, "enabled_fixed_ro", 1, 1, Some(235)));
assert_eq!(nodes[5], new_expected_node(1, "enabled_ro", 1, 2, None, 7)); assert_eq!(nodes[5], new_expected_node(1, "enabled_ro", 1, 2, None));
assert_eq!(nodes[6], new_expected_node(2, "enabled_fixed_ro", 1, 0, None, 9)); assert_eq!(nodes[6], new_expected_node(2, "enabled_fixed_ro", 1, 0, None));
assert_eq!(nodes[7], new_expected_node(0, "disabled_rw", 1, 0, None, 15)); assert_eq!(nodes[7], new_expected_node(0, "disabled_rw", 1, 0, None));
} }
} }

View File

@@ -17,7 +17,7 @@
use anyhow::Result; use anyhow::Result;
use aconfig_storage_file::{ use aconfig_storage_file::{
get_bucket_index, get_table_size, PackageTable, PackageTableHeader, PackageTableNode, get_table_size, PackageTable, PackageTableHeader, PackageTableNode,
FILE_VERSION, FILE_VERSION,
}; };
@@ -34,14 +34,24 @@ fn new_header(container: &str, num_packages: u32) -> PackageTableHeader {
} }
} }
fn new_node(package: &FlagPackage, num_buckets: u32) -> PackageTableNode { // a struct that contains PackageTableNode and a bunch of other information to help
let bucket_index = get_bucket_index(&package.package_name.to_string(), num_buckets); // package table creation
PackageTableNode { #[derive(PartialEq, Debug)]
package_name: String::from(package.package_name), struct PackageTableNodeWrapper {
package_id: package.package_id, pub node: PackageTableNode,
boolean_offset: package.boolean_offset, pub bucket_index: u32,
next_offset: None, }
bucket_index,
impl PackageTableNodeWrapper {
fn new(package: &FlagPackage, num_buckets: u32) -> Self {
let node = PackageTableNode {
package_name: String::from(package.package_name),
package_id: package.package_id,
boolean_offset: package.boolean_offset,
next_offset: None,
};
let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets);
Self { node, bucket_index }
} }
} }
@@ -49,40 +59,46 @@ pub fn create_package_table(container: &str, packages: &[FlagPackage]) -> Result
// create table // create table
let num_packages = packages.len() as u32; let num_packages = packages.len() as u32;
let num_buckets = get_table_size(num_packages)?; let num_buckets = get_table_size(num_packages)?;
let mut table = PackageTable { let mut header = new_header(container, num_packages);
header: new_header(container, num_packages), let mut buckets = vec![None; num_buckets as usize];
buckets: vec![None; num_buckets as usize], let mut node_wrappers: Vec<_> = packages
nodes: packages.iter().map(|pkg| new_node(pkg, num_buckets)).collect(), .iter()
}; .map(|pkg| PackageTableNodeWrapper::new(pkg, num_buckets))
.collect();
// initialize all header fields // initialize all header fields
table.header.bucket_offset = table.header.as_bytes().len() as u32; header.bucket_offset = header.as_bytes().len() as u32;
table.header.node_offset = table.header.bucket_offset + num_buckets * 4; header.node_offset = header.bucket_offset + num_buckets * 4;
table.header.file_size = table.header.node_offset header.file_size = header.node_offset
+ table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32; + node_wrappers.iter().map(|x| x.node.as_bytes().len()).sum::<usize>() as u32;
// sort nodes by bucket index for efficiency // sort node_wrappers by bucket index for efficiency
table.nodes.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index)); node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
// fill all node offset // fill all node offset
let mut offset = table.header.node_offset; let mut offset = header.node_offset;
for i in 0..table.nodes.len() { for i in 0..node_wrappers.len() {
let node_bucket_idx = table.nodes[i].bucket_index; let node_bucket_idx = node_wrappers[i].bucket_index;
let next_node_bucket_idx = let next_node_bucket_idx =
if i + 1 < table.nodes.len() { Some(table.nodes[i + 1].bucket_index) } else { None }; if i + 1 < node_wrappers.len() { Some(node_wrappers[i + 1].bucket_index) } else { None };
if table.buckets[node_bucket_idx as usize].is_none() { if buckets[node_bucket_idx as usize].is_none() {
table.buckets[node_bucket_idx as usize] = Some(offset); buckets[node_bucket_idx as usize] = Some(offset);
} }
offset += table.nodes[i].as_bytes().len() as u32; offset += node_wrappers[i].node.as_bytes().len() as u32;
if let Some(index) = next_node_bucket_idx { if let Some(index) = next_node_bucket_idx {
if index == node_bucket_idx { if index == node_bucket_idx {
table.nodes[i].next_offset = Some(offset); node_wrappers[i].node.next_offset = Some(offset);
} }
} }
} }
let table = PackageTable {
header,
buckets,
nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(),
};
Ok(table) Ok(table)
} }
@@ -125,7 +141,6 @@ mod tests {
package_id: 1, package_id: 1,
boolean_offset: 3, boolean_offset: 3,
next_offset: None, next_offset: None,
bucket_index: 0,
}; };
assert_eq!(nodes[0], first_node_expected); assert_eq!(nodes[0], first_node_expected);
let second_node_expected = PackageTableNode { let second_node_expected = PackageTableNode {
@@ -133,7 +148,6 @@ mod tests {
package_id: 0, package_id: 0,
boolean_offset: 0, boolean_offset: 0,
next_offset: Some(158), next_offset: Some(158),
bucket_index: 3,
}; };
assert_eq!(nodes[1], second_node_expected); assert_eq!(nodes[1], second_node_expected);
let third_node_expected = PackageTableNode { let third_node_expected = PackageTableNode {
@@ -141,7 +155,6 @@ mod tests {
package_id: 2, package_id: 2,
boolean_offset: 6, boolean_offset: 6,
next_offset: None, next_offset: None,
bucket_index: 3,
}; };
assert_eq!(nodes[2], third_node_expected); assert_eq!(nodes[2], third_node_expected);
} }

View File

@@ -68,7 +68,6 @@ pub struct FlagTableNode {
pub flag_type: u16, pub flag_type: u16,
pub flag_id: u16, pub flag_id: u16,
pub next_offset: Option<u32>, pub next_offset: Option<u32>,
pub bucket_index: u32,
} }
impl FlagTableNode { impl FlagTableNode {
@@ -97,7 +96,6 @@ impl FlagTableNode {
0 => None, 0 => None,
val => Some(val), val => Some(val),
}, },
bucket_index: 0,
}; };
Ok(node) Ok(node)
} }
@@ -142,13 +140,11 @@ impl FlagTable {
.collect(); .collect();
let nodes = (0..num_flags) let nodes = (0..num_flags)
.map(|_| { .map(|_| {
let mut node = FlagTableNode::from_bytes(&bytes[head..]).unwrap(); let node = FlagTableNode::from_bytes(&bytes[head..])?;
head += node.as_bytes().len(); head += node.as_bytes().len();
node.bucket_index = FlagTableNode::find_bucket_index( Ok(node)
node.package_id, &node.flag_name, num_buckets);
node
}) })
.collect(); .collect::<Result<Vec<_>>>()?;
let table = Self { header, buckets, nodes }; let table = Self { header, buckets, nodes };
Ok(table) Ok(table)
@@ -203,7 +199,6 @@ mod tests {
flag_type: u16, flag_type: u16,
flag_id: u16, flag_id: u16,
next_offset: Option<u32>, next_offset: Option<u32>,
bucket_index: u32,
) -> Self { ) -> Self {
Self { Self {
package_id, package_id,
@@ -211,7 +206,6 @@ mod tests {
flag_type, flag_type,
flag_id, flag_id,
next_offset, next_offset,
bucket_index,
} }
} }
} }
@@ -245,14 +239,14 @@ mod tests {
None, None,
]; ];
let nodes = vec![ let nodes = vec![
FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None, 0), FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150), 1), FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None, 1), FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None, 5), FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235), 7), FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None, 7), FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None, 9), FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None, 15), FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
]; ];
Ok(FlagTable { header, buckets, nodes }) Ok(FlagTable { header, buckets, nodes })
} }
@@ -268,14 +262,8 @@ mod tests {
assert_eq!(header, &reinterpreted_header.unwrap()); assert_eq!(header, &reinterpreted_header.unwrap());
let nodes: &Vec<FlagTableNode> = &flag_table.nodes; let nodes: &Vec<FlagTableNode> = &flag_table.nodes;
let num_buckets = crate::get_table_size(header.num_flags).unwrap();
for node in nodes.iter() { for node in nodes.iter() {
let mut reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes()).unwrap(); let reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes()).unwrap();
reinterpreted_node.bucket_index = FlagTableNode::find_bucket_index(
reinterpreted_node.package_id,
&reinterpreted_node.flag_name,
num_buckets
);
assert_eq!(node, &reinterpreted_node); assert_eq!(node, &reinterpreted_node);
} }

View File

@@ -69,7 +69,6 @@ pub struct PackageTableNode {
// boolean flag value array in the flag value file // boolean flag value array in the flag value file
pub boolean_offset: u32, pub boolean_offset: u32,
pub next_offset: Option<u32>, pub next_offset: Option<u32>,
pub bucket_index: u32,
} }
impl PackageTableNode { impl PackageTableNode {
@@ -96,10 +95,16 @@ impl PackageTableNode {
0 => None, 0 => None,
val => Some(val), val => Some(val),
}, },
bucket_index: 0,
}; };
Ok(node) Ok(node)
} }
/// Get the bucket index for a package table node, defined it here so the
/// construction side (aconfig binary) and consumption side (flag read lib)
/// use the same method of hashing
pub fn find_bucket_index(package: &str, num_buckets: u32) -> u32 {
get_bucket_index(&package, num_buckets)
}
} }
/// Package table struct /// Package table struct
@@ -135,12 +140,11 @@ impl PackageTable {
.collect(); .collect();
let nodes = (0..num_packages) let nodes = (0..num_packages)
.map(|_| { .map(|_| {
let mut node = PackageTableNode::from_bytes(&bytes[head..]).unwrap(); let node = PackageTableNode::from_bytes(&bytes[head..])?;
head += node.as_bytes().len(); head += node.as_bytes().len();
node.bucket_index = get_bucket_index(&node.package_name, num_buckets); Ok(node)
node
}) })
.collect(); .collect::<Result<Vec<_>>>()?;
let table = Self { header, buckets, nodes }; let table = Self { header, buckets, nodes };
Ok(table) Ok(table)
@@ -166,7 +170,7 @@ pub fn find_package_offset(buf: &[u8], package: &str) -> Result<Option<PackageOf
} }
let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4; let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
let bucket_index = get_bucket_index(&package, num_buckets); let bucket_index = PackageTableNode::find_bucket_index(&package, num_buckets);
let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize; let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize; let mut package_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
@@ -210,21 +214,18 @@ mod tests {
package_id: 1, package_id: 1,
boolean_offset: 3, boolean_offset: 3,
next_offset: None, next_offset: None,
bucket_index: 0,
}; };
let second_node = PackageTableNode { let second_node = PackageTableNode {
package_name: String::from("com.android.aconfig.storage.test_1"), package_name: String::from("com.android.aconfig.storage.test_1"),
package_id: 0, package_id: 0,
boolean_offset: 0, boolean_offset: 0,
next_offset: Some(158), next_offset: Some(158),
bucket_index: 3,
}; };
let third_node = PackageTableNode { let third_node = PackageTableNode {
package_name: String::from("com.android.aconfig.storage.test_4"), package_name: String::from("com.android.aconfig.storage.test_4"),
package_id: 2, package_id: 2,
boolean_offset: 6, boolean_offset: 6,
next_offset: None, next_offset: None,
bucket_index: 3,
}; };
let nodes = vec![first_node, second_node, third_node]; let nodes = vec![first_node, second_node, third_node];
Ok(PackageTable { header, buckets, nodes }) Ok(PackageTable { header, buckets, nodes })
@@ -241,11 +242,8 @@ mod tests {
assert_eq!(header, &reinterpreted_header.unwrap()); assert_eq!(header, &reinterpreted_header.unwrap());
let nodes: &Vec<PackageTableNode> = &package_table.nodes; let nodes: &Vec<PackageTableNode> = &package_table.nodes;
let num_buckets = crate::get_table_size(header.num_packages).unwrap();
for node in nodes.iter() { for node in nodes.iter() {
let mut reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes()).unwrap(); let reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes()).unwrap();
reinterpreted_node.bucket_index =
get_bucket_index(&reinterpreted_node.package_name, num_buckets);
assert_eq!(node, &reinterpreted_node); assert_eq!(node, &reinterpreted_node);
} }