Loading tools/aconfig/aconfig_storage_file/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ rust_defaults { rustlibs: [ "libanyhow", "libthiserror", "libtempfile", ], } Loading tools/aconfig/aconfig_storage_file/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ once_cell = "1.19.0" tempfile = "3.9.0" cxx = "1.0" thiserror = "1.0.56" clap = { version = "4.1.8", features = ["derive"] } [build-dependencies] protobuf-codegen = "3.2.0" Loading tools/aconfig/aconfig_storage_file/src/flag_table.rs +49 −57 Original line number Diff line number Diff line Loading @@ -20,9 +20,10 @@ use crate::AconfigStorageError::{self, BytesParseFail}; use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes}; use anyhow::anyhow; use std::fmt; /// Flag table header struct #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagTableHeader { pub version: u32, pub container: String, Loading @@ -32,6 +33,23 @@ pub struct FlagTableHeader { pub node_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagTableHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Size: {}", self.version, self.container, self.file_size )?; writeln!( f, "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}", self.num_flags, self.bucket_offset, self.node_offset )?; Ok(()) } } impl FlagTableHeader { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading Loading @@ -62,7 +80,7 @@ impl FlagTableHeader { } /// Flag table node struct #[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Clone)] pub struct FlagTableNode { pub package_id: u32, pub flag_name: String, Loading @@ -71,6 +89,18 @@ pub struct FlagTableNode { pub next_offset: Option<u32>, } /// Implement debug print trait for node impl fmt::Debug for FlagTableNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Package Id: {}, Flag: {}, Type: {}, Offset: {}, Next: {:?}", self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset )?; Ok(()) } } impl FlagTableNode { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading Loading @@ -108,13 +138,28 @@ impl FlagTableNode { } } #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagTable { pub header: FlagTableHeader, pub buckets: Vec<Option<u32>>, pub nodes: Vec<FlagTableNode>, } /// Implement debug print trait for flag table impl fmt::Debug for FlagTable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Buckets:")?; writeln!(f, "{:?}", self.buckets)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } /// Flag table struct impl FlagTable { /// Serialize to bytes Loading Loading @@ -156,60 +201,7 @@ impl FlagTable { #[cfg(test)] mod tests { use super::*; impl FlagTableNode { // create test baseline, syntactic sugar fn new_expected( package_id: u32, flag_name: &str, flag_type: u16, flag_id: u16, next_offset: Option<u32>, ) -> Self { Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset } } } pub fn create_test_flag_table() -> FlagTable { let header = FlagTableHeader { version: 1234, container: String::from("system"), file_size: 320, num_flags: 8, bucket_offset: 30, node_offset: 98, }; let buckets: Vec<Option<u32>> = vec![ Some(98), Some(124), None, None, None, Some(177), None, Some(203), None, Some(261), None, None, None, None, None, Some(293), None, ]; let nodes = vec![ FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None), FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)), FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None), FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None), FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)), FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None), FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None), FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None), ]; FlagTable { header, buckets, nodes } } use crate::test_utils::create_test_flag_table; #[test] // this test point locks down the table serialization Loading tools/aconfig/aconfig_storage_file/src/flag_value.rs +32 −14 Original line number Diff line number Diff line Loading @@ -19,9 +19,10 @@ use crate::AconfigStorageError; use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use std::fmt; /// Flag value header struct #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagValueHeader { pub version: u32, pub container: String, Loading @@ -30,6 +31,23 @@ pub struct FlagValueHeader { pub boolean_value_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagValueHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Size: {}", self.version, self.container, self.file_size )?; writeln!( f, "Num of Flags: {}, Value Offset:{}", self.num_flags, self.boolean_value_offset )?; Ok(()) } } impl FlagValueHeader { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading Loading @@ -58,12 +76,23 @@ impl FlagValueHeader { } /// Flag value list struct #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagValueList { pub header: FlagValueHeader, pub booleans: Vec<bool>, } /// Implement debug print trait for flag value impl fmt::Debug for FlagValueList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Values:")?; writeln!(f, "{:?}", self.booleans)?; Ok(()) } } impl FlagValueList { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading @@ -89,18 +118,7 @@ impl FlagValueList { #[cfg(test)] mod tests { use super::*; pub fn create_test_flag_value_list() -> FlagValueList { let header = FlagValueHeader { version: 1234, container: String::from("system"), file_size: 34, num_flags: 8, boolean_value_offset: 26, }; let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true]; FlagValueList { header, booleans } } use crate::test_utils::create_test_flag_value_list; #[test] // this test point locks down the value list serialization Loading tools/aconfig/aconfig_storage_file/src/lib.rs +89 −0 Original line number Diff line number Diff line Loading @@ -36,9 +36,14 @@ pub mod flag_table; pub mod flag_value; pub mod package_table; #[cfg(test)] mod test_utils; use anyhow::anyhow; use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::Read; pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; pub use crate::flag_value::{FlagValueHeader, FlagValueList}; Loading Loading @@ -166,4 +171,88 @@ pub enum AconfigStorageError { #[error("invalid storage file byte offset")] InvalidStorageFileOffset(#[source] anyhow::Error), #[error("failed to create file")] FileCreationFail(#[source] anyhow::Error), } /// Read in storage file as bytes pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> { let mut file = File::open(file_path).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)) })?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!( "Failed to read 4 bytes from file {}: {}", file_path, errmsg )) })?; Ok(buffer) } /// List flag values from storage files pub fn list_flags( package_map: &str, flag_map: &str, flag_val: &str, ) -> Result<Vec<(String, bool)>, AconfigStorageError> { let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?; let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?; let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?; let mut package_info = vec![("", 0); package_table.header.num_packages as usize]; for node in package_table.nodes.iter() { package_info[node.package_id as usize] = (&node.package_name, node.boolean_offset); } let mut flags = Vec::new(); for node in flag_table.nodes.iter() { let (package_name, package_offset) = package_info[node.package_id as usize]; let full_flag_name = String::from(package_name) + "/" + &node.flag_name; let flag_offset = package_offset + node.flag_id as u32; let flag_value = flag_value_list.booleans[flag_offset as usize]; flags.push((full_flag_name, flag_value)); } flags.sort_by(|v1, v2| v1.0.cmp(&v2.0)); Ok(flags) } #[cfg(test)] mod tests { use super::*; use crate::test_utils::{ create_test_flag_table, create_test_flag_value_list, create_test_package_table, write_bytes_to_temp_file, }; #[test] // this test point locks down the flag list api fn test_list_flag() { let package_table = write_bytes_to_temp_file(&create_test_package_table().as_bytes()).unwrap(); let flag_table = write_bytes_to_temp_file(&create_test_flag_table().as_bytes()).unwrap(); let flag_value_list = write_bytes_to_temp_file(&create_test_flag_value_list().as_bytes()).unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); let flag_value_list_path = flag_value_list.path().display().to_string(); let flags = list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap(); let expected = [ (String::from("com.android.aconfig.storage.test_1/disabled_rw"), false), (String::from("com.android.aconfig.storage.test_1/enabled_ro"), true), (String::from("com.android.aconfig.storage.test_1/enabled_rw"), false), (String::from("com.android.aconfig.storage.test_2/disabled_ro"), false), (String::from("com.android.aconfig.storage.test_2/enabled_fixed_ro"), true), (String::from("com.android.aconfig.storage.test_2/enabled_ro"), true), (String::from("com.android.aconfig.storage.test_4/enabled_fixed_ro"), false), (String::from("com.android.aconfig.storage.test_4/enabled_ro"), true), ]; assert_eq!(flags, expected); } } Loading
tools/aconfig/aconfig_storage_file/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ rust_defaults { rustlibs: [ "libanyhow", "libthiserror", "libtempfile", ], } Loading
tools/aconfig/aconfig_storage_file/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ once_cell = "1.19.0" tempfile = "3.9.0" cxx = "1.0" thiserror = "1.0.56" clap = { version = "4.1.8", features = ["derive"] } [build-dependencies] protobuf-codegen = "3.2.0" Loading
tools/aconfig/aconfig_storage_file/src/flag_table.rs +49 −57 Original line number Diff line number Diff line Loading @@ -20,9 +20,10 @@ use crate::AconfigStorageError::{self, BytesParseFail}; use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes}; use anyhow::anyhow; use std::fmt; /// Flag table header struct #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagTableHeader { pub version: u32, pub container: String, Loading @@ -32,6 +33,23 @@ pub struct FlagTableHeader { pub node_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagTableHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Size: {}", self.version, self.container, self.file_size )?; writeln!( f, "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}", self.num_flags, self.bucket_offset, self.node_offset )?; Ok(()) } } impl FlagTableHeader { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading Loading @@ -62,7 +80,7 @@ impl FlagTableHeader { } /// Flag table node struct #[derive(PartialEq, Debug, Clone)] #[derive(PartialEq, Clone)] pub struct FlagTableNode { pub package_id: u32, pub flag_name: String, Loading @@ -71,6 +89,18 @@ pub struct FlagTableNode { pub next_offset: Option<u32>, } /// Implement debug print trait for node impl fmt::Debug for FlagTableNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Package Id: {}, Flag: {}, Type: {}, Offset: {}, Next: {:?}", self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset )?; Ok(()) } } impl FlagTableNode { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading Loading @@ -108,13 +138,28 @@ impl FlagTableNode { } } #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagTable { pub header: FlagTableHeader, pub buckets: Vec<Option<u32>>, pub nodes: Vec<FlagTableNode>, } /// Implement debug print trait for flag table impl fmt::Debug for FlagTable { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Buckets:")?; writeln!(f, "{:?}", self.buckets)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } /// Flag table struct impl FlagTable { /// Serialize to bytes Loading Loading @@ -156,60 +201,7 @@ impl FlagTable { #[cfg(test)] mod tests { use super::*; impl FlagTableNode { // create test baseline, syntactic sugar fn new_expected( package_id: u32, flag_name: &str, flag_type: u16, flag_id: u16, next_offset: Option<u32>, ) -> Self { Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset } } } pub fn create_test_flag_table() -> FlagTable { let header = FlagTableHeader { version: 1234, container: String::from("system"), file_size: 320, num_flags: 8, bucket_offset: 30, node_offset: 98, }; let buckets: Vec<Option<u32>> = vec![ Some(98), Some(124), None, None, None, Some(177), None, Some(203), None, Some(261), None, None, None, None, None, Some(293), None, ]; let nodes = vec![ FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None), FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)), FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None), FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None), FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)), FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None), FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None), FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None), ]; FlagTable { header, buckets, nodes } } use crate::test_utils::create_test_flag_table; #[test] // this test point locks down the table serialization Loading
tools/aconfig/aconfig_storage_file/src/flag_value.rs +32 −14 Original line number Diff line number Diff line Loading @@ -19,9 +19,10 @@ use crate::AconfigStorageError; use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use std::fmt; /// Flag value header struct #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagValueHeader { pub version: u32, pub container: String, Loading @@ -30,6 +31,23 @@ pub struct FlagValueHeader { pub boolean_value_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagValueHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Size: {}", self.version, self.container, self.file_size )?; writeln!( f, "Num of Flags: {}, Value Offset:{}", self.num_flags, self.boolean_value_offset )?; Ok(()) } } impl FlagValueHeader { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading Loading @@ -58,12 +76,23 @@ impl FlagValueHeader { } /// Flag value list struct #[derive(PartialEq, Debug)] #[derive(PartialEq)] pub struct FlagValueList { pub header: FlagValueHeader, pub booleans: Vec<bool>, } /// Implement debug print trait for flag value impl fmt::Debug for FlagValueList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Values:")?; writeln!(f, "{:?}", self.booleans)?; Ok(()) } } impl FlagValueList { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { Loading @@ -89,18 +118,7 @@ impl FlagValueList { #[cfg(test)] mod tests { use super::*; pub fn create_test_flag_value_list() -> FlagValueList { let header = FlagValueHeader { version: 1234, container: String::from("system"), file_size: 34, num_flags: 8, boolean_value_offset: 26, }; let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true]; FlagValueList { header, booleans } } use crate::test_utils::create_test_flag_value_list; #[test] // this test point locks down the value list serialization Loading
tools/aconfig/aconfig_storage_file/src/lib.rs +89 −0 Original line number Diff line number Diff line Loading @@ -36,9 +36,14 @@ pub mod flag_table; pub mod flag_value; pub mod package_table; #[cfg(test)] mod test_utils; use anyhow::anyhow; use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::Read; pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; pub use crate::flag_value::{FlagValueHeader, FlagValueList}; Loading Loading @@ -166,4 +171,88 @@ pub enum AconfigStorageError { #[error("invalid storage file byte offset")] InvalidStorageFileOffset(#[source] anyhow::Error), #[error("failed to create file")] FileCreationFail(#[source] anyhow::Error), } /// Read in storage file as bytes pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> { let mut file = File::open(file_path).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg)) })?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer).map_err(|errmsg| { AconfigStorageError::FileReadFail(anyhow!( "Failed to read 4 bytes from file {}: {}", file_path, errmsg )) })?; Ok(buffer) } /// List flag values from storage files pub fn list_flags( package_map: &str, flag_map: &str, flag_val: &str, ) -> Result<Vec<(String, bool)>, AconfigStorageError> { let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?; let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?; let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?; let mut package_info = vec![("", 0); package_table.header.num_packages as usize]; for node in package_table.nodes.iter() { package_info[node.package_id as usize] = (&node.package_name, node.boolean_offset); } let mut flags = Vec::new(); for node in flag_table.nodes.iter() { let (package_name, package_offset) = package_info[node.package_id as usize]; let full_flag_name = String::from(package_name) + "/" + &node.flag_name; let flag_offset = package_offset + node.flag_id as u32; let flag_value = flag_value_list.booleans[flag_offset as usize]; flags.push((full_flag_name, flag_value)); } flags.sort_by(|v1, v2| v1.0.cmp(&v2.0)); Ok(flags) } #[cfg(test)] mod tests { use super::*; use crate::test_utils::{ create_test_flag_table, create_test_flag_value_list, create_test_package_table, write_bytes_to_temp_file, }; #[test] // this test point locks down the flag list api fn test_list_flag() { let package_table = write_bytes_to_temp_file(&create_test_package_table().as_bytes()).unwrap(); let flag_table = write_bytes_to_temp_file(&create_test_flag_table().as_bytes()).unwrap(); let flag_value_list = write_bytes_to_temp_file(&create_test_flag_value_list().as_bytes()).unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); let flag_value_list_path = flag_value_list.path().display().to_string(); let flags = list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap(); let expected = [ (String::from("com.android.aconfig.storage.test_1/disabled_rw"), false), (String::from("com.android.aconfig.storage.test_1/enabled_ro"), true), (String::from("com.android.aconfig.storage.test_1/enabled_rw"), false), (String::from("com.android.aconfig.storage.test_2/disabled_ro"), false), (String::from("com.android.aconfig.storage.test_2/enabled_fixed_ro"), true), (String::from("com.android.aconfig.storage.test_2/enabled_ro"), true), (String::from("com.android.aconfig.storage.test_4/enabled_fixed_ro"), false), (String::from("com.android.aconfig.storage.test_4/enabled_ro"), true), ]; assert_eq!(flags, expected); } }