Merge "aconfig: add flag table offset query function" into main am: 9a76f645a2
Original change: https://android-review.googlesource.com/c/platform/build/+/2932350 Change-Id: Iaf3a110db36e224e2a03a2781a0061ee74ae32e9 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
7455bfa7a5
2 changed files with 111 additions and 13 deletions
|
@ -17,7 +17,7 @@
|
||||||
use crate::commands::assign_flag_ids;
|
use crate::commands::assign_flag_ids;
|
||||||
use crate::storage::FlagPackage;
|
use crate::storage::FlagPackage;
|
||||||
use aconfig_storage_file::{
|
use aconfig_storage_file::{
|
||||||
get_bucket_index, get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION,
|
get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
|
@ -39,8 +39,7 @@ fn new_node(
|
||||||
flag_id: u16,
|
flag_id: u16,
|
||||||
num_buckets: u32,
|
num_buckets: u32,
|
||||||
) -> FlagTableNode {
|
) -> FlagTableNode {
|
||||||
let full_flag_name = package_id.to_string() + "/" + flag_name;
|
let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);
|
||||||
let bucket_index = get_bucket_index(&full_flag_name, num_buckets);
|
|
||||||
FlagTableNode {
|
FlagTableNode {
|
||||||
package_id,
|
package_id,
|
||||||
flag_name: flag_name.to_string(),
|
flag_name: flag_name.to_string(),
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
//! flag table module defines the flag table file format and methods for serialization
|
//! flag table module defines the flag table file format and methods for serialization
|
||||||
//! and deserialization
|
//! and deserialization
|
||||||
|
|
||||||
use crate::{read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
|
use crate::{read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes, get_bucket_index};
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
/// Flag table header struct
|
/// Flag table header struct
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
@ -86,9 +86,9 @@ impl FlagTableNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deserialize from bytes
|
/// Deserialize from bytes
|
||||||
pub fn from_bytes(bytes: &[u8], num_buckets: u32) -> Result<Self> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
let mut head = 0;
|
let mut head = 0;
|
||||||
let mut node = Self {
|
let node = Self {
|
||||||
package_id: read_u32_from_bytes(bytes, &mut head)?,
|
package_id: read_u32_from_bytes(bytes, &mut head)?,
|
||||||
flag_name: read_str_from_bytes(bytes, &mut head)?,
|
flag_name: read_str_from_bytes(bytes, &mut head)?,
|
||||||
flag_type: read_u16_from_bytes(bytes, &mut head)?,
|
flag_type: read_u16_from_bytes(bytes, &mut head)?,
|
||||||
|
@ -99,10 +99,14 @@ impl FlagTableNode {
|
||||||
},
|
},
|
||||||
bucket_index: 0,
|
bucket_index: 0,
|
||||||
};
|
};
|
||||||
let full_flag_name = node.package_id.to_string() + "/" + &node.flag_name;
|
|
||||||
node.bucket_index = crate::get_bucket_index(&full_flag_name, num_buckets);
|
|
||||||
Ok(node)
|
Ok(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calculate node bucket index
|
||||||
|
pub fn find_bucket_index(package_id: u32, flag_name: &str, num_buckets: u32) -> u32 {
|
||||||
|
let full_flag_name = package_id.to_string() + "/" + flag_name;
|
||||||
|
get_bucket_index(&full_flag_name, num_buckets)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
@ -138,8 +142,10 @@ impl FlagTable {
|
||||||
.collect();
|
.collect();
|
||||||
let nodes = (0..num_flags)
|
let nodes = (0..num_flags)
|
||||||
.map(|_| {
|
.map(|_| {
|
||||||
let node = FlagTableNode::from_bytes(&bytes[head..], num_buckets).unwrap();
|
let mut node = FlagTableNode::from_bytes(&bytes[head..]).unwrap();
|
||||||
head += node.as_bytes().len();
|
head += node.as_bytes().len();
|
||||||
|
node.bucket_index = FlagTableNode::find_bucket_index(
|
||||||
|
node.package_id, &node.flag_name, num_buckets);
|
||||||
node
|
node
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -149,6 +155,42 @@ impl FlagTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Query flag within package offset
|
||||||
|
pub fn find_flag_offset(buf: &[u8], package_id: u32, flag: &str) -> Result<Option<u16>> {
|
||||||
|
let interpreted_header = FlagTableHeader::from_bytes(buf)?;
|
||||||
|
if interpreted_header.version > crate::FILE_VERSION {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Cannot read storage file with a higher version of {} with lib version {}",
|
||||||
|
interpreted_header.version,
|
||||||
|
crate::FILE_VERSION
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_buckets = (interpreted_header.node_offset - interpreted_header.bucket_offset) / 4;
|
||||||
|
let bucket_index = FlagTableNode::find_bucket_index(package_id, flag, num_buckets);
|
||||||
|
|
||||||
|
let mut pos = (interpreted_header.bucket_offset + 4 * bucket_index) as usize;
|
||||||
|
let mut flag_node_offset = read_u32_from_bytes(buf, &mut pos)? as usize;
|
||||||
|
if flag_node_offset < interpreted_header.node_offset as usize
|
||||||
|
|| flag_node_offset >= interpreted_header.file_size as usize
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;
|
||||||
|
if interpreted_node.package_id == package_id &&
|
||||||
|
interpreted_node.flag_name == flag {
|
||||||
|
return Ok(Some(interpreted_node.flag_id));
|
||||||
|
}
|
||||||
|
match interpreted_node.next_offset {
|
||||||
|
Some(offset) => flag_node_offset = offset as usize,
|
||||||
|
None => return Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -228,13 +270,70 @@ mod tests {
|
||||||
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();
|
let num_buckets = crate::get_table_size(header.num_flags).unwrap();
|
||||||
for node in nodes.iter() {
|
for node in nodes.iter() {
|
||||||
let reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes(), num_buckets);
|
let mut reinterpreted_node = FlagTableNode::from_bytes(&node.as_bytes()).unwrap();
|
||||||
assert!(reinterpreted_node.is_ok());
|
reinterpreted_node.bucket_index = FlagTableNode::find_bucket_index(
|
||||||
assert_eq!(node, &reinterpreted_node.unwrap());
|
reinterpreted_node.package_id,
|
||||||
|
&reinterpreted_node.flag_name,
|
||||||
|
num_buckets
|
||||||
|
);
|
||||||
|
assert_eq!(node, &reinterpreted_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
let reinterpreted_table = FlagTable::from_bytes(&flag_table.as_bytes());
|
let reinterpreted_table = FlagTable::from_bytes(&flag_table.as_bytes());
|
||||||
assert!(reinterpreted_table.is_ok());
|
assert!(reinterpreted_table.is_ok());
|
||||||
assert_eq!(&flag_table, &reinterpreted_table.unwrap());
|
assert_eq!(&flag_table, &reinterpreted_table.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// this test point locks down table query
|
||||||
|
fn test_flag_query() {
|
||||||
|
let flag_table = create_test_flag_table().unwrap().as_bytes();
|
||||||
|
let baseline = vec![
|
||||||
|
(0, "enabled_ro", 1u16),
|
||||||
|
(0, "enabled_rw", 2u16),
|
||||||
|
(1, "disabled_ro", 0u16),
|
||||||
|
(2, "enabled_ro", 1u16),
|
||||||
|
(1, "enabled_fixed_ro", 1u16),
|
||||||
|
(1, "enabled_ro", 2u16),
|
||||||
|
(2, "enabled_fixed_ro", 0u16),
|
||||||
|
(0, "disabled_rw", 0u16),
|
||||||
|
];
|
||||||
|
for (package_id, flag_name, expected_offset) in baseline.into_iter() {
|
||||||
|
let flag_offset =
|
||||||
|
find_flag_offset(&flag_table[..], package_id, flag_name)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(flag_offset, expected_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// this test point locks down table query of a non exist flag
|
||||||
|
fn test_not_existed_flag_query() {
|
||||||
|
let flag_table = create_test_flag_table().unwrap().as_bytes();
|
||||||
|
let flag_offset =
|
||||||
|
find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
|
||||||
|
assert_eq!(flag_offset, None);
|
||||||
|
let flag_offset =
|
||||||
|
find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
|
||||||
|
assert_eq!(flag_offset, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// this test point locks down query error when file has a higher version
|
||||||
|
fn test_higher_version_storage_file() {
|
||||||
|
let mut table = create_test_flag_table().unwrap();
|
||||||
|
table.header.version = crate::FILE_VERSION + 1;
|
||||||
|
let flag_table = table.as_bytes();
|
||||||
|
let error = find_flag_offset(&flag_table[..], 0, "enabled_ro")
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
format!("{:?}", error),
|
||||||
|
format!(
|
||||||
|
"Cannot read storage file with a higher version of {} with lib version {}",
|
||||||
|
crate::FILE_VERSION + 1,
|
||||||
|
crate::FILE_VERSION
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue