Loading tools/aconfig/aconfig_storage_file/src/flag_info.rs +6 −1 Original line number Diff line number Diff line Loading @@ -130,6 +130,11 @@ impl FlagInfoNode { let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? }; Ok(node) } /// Create flag info node pub fn create(is_flag_rw: bool) -> Self { Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } } } } /// Flag info list struct Loading Loading @@ -221,7 +226,7 @@ mod tests { let bytes = &flag_info_list.into_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version, 1234) assert_eq!(version, 1); } #[test] Loading tools/aconfig/aconfig_storage_file/src/lib.rs +100 −15 Original line number Diff line number Diff line Loading @@ -44,14 +44,16 @@ use std::cmp::Ordering; use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::Read; use std::io::{Read, Write}; pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode}; pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; pub use crate::flag_value::{FlagValueHeader, FlagValueList}; pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode}; use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit, InvalidStoredFlagType}; use crate::AconfigStorageError::{ BytesParseFail, FileCreationFail, HashTableSizeLimit, InvalidStoredFlagType, }; /// Storage file version pub const FILE_VERSION: u32 = 1; Loading Loading @@ -164,7 +166,6 @@ pub enum AconfigStorageError { InvalidStoredFlagType(#[source] anyhow::Error), } /// Get the right hash table size given number of entries in the table. Use a /// load factor of 0.5 for performance. pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> { Loading Loading @@ -263,25 +264,82 @@ pub fn list_flags( let (package_name, package_offset) = package_info[node.package_id as usize]; let flag_offset = package_offset + node.flag_id as u32; let flag_value = flag_value_list.booleans[flag_offset as usize]; flags.push((String::from(package_name), node.flag_name.clone(), node.flag_type, flag_value)); flags.push(( String::from(package_name), node.flag_name.clone(), node.flag_type, flag_value, )); } flags.sort_by(|v1, v2| { match v1.0.cmp(&v2.0) { flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) { Ordering::Equal => v1.1.cmp(&v2.1), other => other, } }); Ok(flags) } /// Create flag info file pub fn create_flag_info( package_map: &str, flag_map: &str, flag_info_out: &str, ) -> Result<(), 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)?)?; if package_table.header.container != flag_table.header.container { return Err(FileCreationFail(anyhow!( "container for package map {} and flag map {} does not match", package_table.header.container, flag_table.header.container, ))); } let mut package_offsets = vec![0; package_table.header.num_packages as usize]; for node in package_table.nodes.iter() { package_offsets[node.package_id as usize] = node.boolean_offset; } let mut is_flag_rw = vec![false; flag_table.header.num_flags as usize]; for node in flag_table.nodes.iter() { let flag_offset = package_offsets[node.package_id as usize] + node.flag_id as u32; is_flag_rw[flag_offset as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean; } let mut list = FlagInfoList { header: FlagInfoHeader { version: FILE_VERSION, container: flag_table.header.container, file_type: StorageFileType::FlagInfo as u8, file_size: 0, num_flags: flag_table.header.num_flags, boolean_flag_offset: 0, }, nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(), }; list.header.boolean_flag_offset = list.header.into_bytes().len() as u32; list.header.file_size = list.into_bytes().len() as u32; let mut file = File::create(flag_info_out).map_err(|errmsg| { FileCreationFail(anyhow!("fail to create file {}: {}", flag_info_out, errmsg)) })?; file.write_all(&list.into_bytes()).map_err(|errmsg| { FileCreationFail(anyhow!("fail to write to file {}: {}", flag_info_out, errmsg)) })?; Ok(()) } #[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, create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list, create_test_package_table, write_bytes_to_temp_file, }; use tempfile::NamedTempFile; #[test] // this test point locks down the flag list api Loading Loading @@ -309,13 +367,13 @@ mod tests { String::from("com.android.aconfig.storage.test_1"), String::from("enabled_ro"), StoredFlagType::ReadOnlyBoolean, true true, ), ( String::from("com.android.aconfig.storage.test_1"), String::from("enabled_rw"), StoredFlagType::ReadWriteBoolean, true true, ), ( String::from("com.android.aconfig.storage.test_2"), Loading @@ -333,7 +391,7 @@ mod tests { String::from("com.android.aconfig.storage.test_2"), String::from("enabled_ro"), StoredFlagType::ReadOnlyBoolean, true true, ), ( String::from("com.android.aconfig.storage.test_4"), Loading @@ -345,9 +403,36 @@ mod tests { String::from("com.android.aconfig.storage.test_4"), String::from("enabled_ro"), StoredFlagType::ReadOnlyBoolean, true true, ), ]; assert_eq!(flags, expected); } fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> { let file = NamedTempFile::new().map_err(|_| { AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file")) })?; Ok(file) } #[test] // this test point locks down the flag info creation fn test_create_flag_info() { let package_table = write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap(); let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap(); let flag_info = create_empty_temp_file().unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); let flag_info_path = flag_info.path().display().to_string(); assert!(create_flag_info(&package_table_path, &flag_table_path, &flag_info_path).is_ok()); let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(&flag_info_path).unwrap()).unwrap(); let expected_flag_info = create_test_flag_info_list(); assert_eq!(flag_info, expected_flag_info); } } tools/aconfig/aconfig_storage_file/src/test_utils.rs +3 −2 Original line number Diff line number Diff line Loading @@ -133,14 +133,15 @@ pub fn create_test_flag_value_list() -> FlagValueList { pub fn create_test_flag_info_list() -> FlagInfoList { let header = FlagInfoHeader { version: 1234, version: 1, container: String::from("mockup"), file_type: StorageFileType::FlagInfo as u8, file_size: 35, num_flags: 8, boolean_flag_offset: 27, }; let nodes: Vec<FlagInfoNode> = vec![FlagInfoNode { attributes: 0 }; 8]; let is_flag_rw = [true, false, true, false, false, false, false, false]; let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(); FlagInfoList { header, nodes } } Loading Loading
tools/aconfig/aconfig_storage_file/src/flag_info.rs +6 −1 Original line number Diff line number Diff line Loading @@ -130,6 +130,11 @@ impl FlagInfoNode { let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? }; Ok(node) } /// Create flag info node pub fn create(is_flag_rw: bool) -> Self { Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } } } } /// Flag info list struct Loading Loading @@ -221,7 +226,7 @@ mod tests { let bytes = &flag_info_list.into_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version, 1234) assert_eq!(version, 1); } #[test] Loading
tools/aconfig/aconfig_storage_file/src/lib.rs +100 −15 Original line number Diff line number Diff line Loading @@ -44,14 +44,16 @@ use std::cmp::Ordering; use std::collections::hash_map::DefaultHasher; use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::Read; use std::io::{Read, Write}; pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode}; pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; pub use crate::flag_value::{FlagValueHeader, FlagValueList}; pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode}; use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit, InvalidStoredFlagType}; use crate::AconfigStorageError::{ BytesParseFail, FileCreationFail, HashTableSizeLimit, InvalidStoredFlagType, }; /// Storage file version pub const FILE_VERSION: u32 = 1; Loading Loading @@ -164,7 +166,6 @@ pub enum AconfigStorageError { InvalidStoredFlagType(#[source] anyhow::Error), } /// Get the right hash table size given number of entries in the table. Use a /// load factor of 0.5 for performance. pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> { Loading Loading @@ -263,25 +264,82 @@ pub fn list_flags( let (package_name, package_offset) = package_info[node.package_id as usize]; let flag_offset = package_offset + node.flag_id as u32; let flag_value = flag_value_list.booleans[flag_offset as usize]; flags.push((String::from(package_name), node.flag_name.clone(), node.flag_type, flag_value)); flags.push(( String::from(package_name), node.flag_name.clone(), node.flag_type, flag_value, )); } flags.sort_by(|v1, v2| { match v1.0.cmp(&v2.0) { flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) { Ordering::Equal => v1.1.cmp(&v2.1), other => other, } }); Ok(flags) } /// Create flag info file pub fn create_flag_info( package_map: &str, flag_map: &str, flag_info_out: &str, ) -> Result<(), 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)?)?; if package_table.header.container != flag_table.header.container { return Err(FileCreationFail(anyhow!( "container for package map {} and flag map {} does not match", package_table.header.container, flag_table.header.container, ))); } let mut package_offsets = vec![0; package_table.header.num_packages as usize]; for node in package_table.nodes.iter() { package_offsets[node.package_id as usize] = node.boolean_offset; } let mut is_flag_rw = vec![false; flag_table.header.num_flags as usize]; for node in flag_table.nodes.iter() { let flag_offset = package_offsets[node.package_id as usize] + node.flag_id as u32; is_flag_rw[flag_offset as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean; } let mut list = FlagInfoList { header: FlagInfoHeader { version: FILE_VERSION, container: flag_table.header.container, file_type: StorageFileType::FlagInfo as u8, file_size: 0, num_flags: flag_table.header.num_flags, boolean_flag_offset: 0, }, nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(), }; list.header.boolean_flag_offset = list.header.into_bytes().len() as u32; list.header.file_size = list.into_bytes().len() as u32; let mut file = File::create(flag_info_out).map_err(|errmsg| { FileCreationFail(anyhow!("fail to create file {}: {}", flag_info_out, errmsg)) })?; file.write_all(&list.into_bytes()).map_err(|errmsg| { FileCreationFail(anyhow!("fail to write to file {}: {}", flag_info_out, errmsg)) })?; Ok(()) } #[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, create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list, create_test_package_table, write_bytes_to_temp_file, }; use tempfile::NamedTempFile; #[test] // this test point locks down the flag list api Loading Loading @@ -309,13 +367,13 @@ mod tests { String::from("com.android.aconfig.storage.test_1"), String::from("enabled_ro"), StoredFlagType::ReadOnlyBoolean, true true, ), ( String::from("com.android.aconfig.storage.test_1"), String::from("enabled_rw"), StoredFlagType::ReadWriteBoolean, true true, ), ( String::from("com.android.aconfig.storage.test_2"), Loading @@ -333,7 +391,7 @@ mod tests { String::from("com.android.aconfig.storage.test_2"), String::from("enabled_ro"), StoredFlagType::ReadOnlyBoolean, true true, ), ( String::from("com.android.aconfig.storage.test_4"), Loading @@ -345,9 +403,36 @@ mod tests { String::from("com.android.aconfig.storage.test_4"), String::from("enabled_ro"), StoredFlagType::ReadOnlyBoolean, true true, ), ]; assert_eq!(flags, expected); } fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> { let file = NamedTempFile::new().map_err(|_| { AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file")) })?; Ok(file) } #[test] // this test point locks down the flag info creation fn test_create_flag_info() { let package_table = write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap(); let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap(); let flag_info = create_empty_temp_file().unwrap(); let package_table_path = package_table.path().display().to_string(); let flag_table_path = flag_table.path().display().to_string(); let flag_info_path = flag_info.path().display().to_string(); assert!(create_flag_info(&package_table_path, &flag_table_path, &flag_info_path).is_ok()); let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(&flag_info_path).unwrap()).unwrap(); let expected_flag_info = create_test_flag_info_list(); assert_eq!(flag_info, expected_flag_info); } }
tools/aconfig/aconfig_storage_file/src/test_utils.rs +3 −2 Original line number Diff line number Diff line Loading @@ -133,14 +133,15 @@ pub fn create_test_flag_value_list() -> FlagValueList { pub fn create_test_flag_info_list() -> FlagInfoList { let header = FlagInfoHeader { version: 1234, version: 1, container: String::from("mockup"), file_type: StorageFileType::FlagInfo as u8, file_size: 35, num_flags: 8, boolean_flag_offset: 27, }; let nodes: Vec<FlagInfoNode> = vec![FlagInfoNode { attributes: 0 }; 8]; let is_flag_rw = [true, false, true, false, false, false, false, false]; let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(); FlagInfoList { header, nodes } } Loading