Merge "aconfig: add api to create flag info file based on package map and flag map file" into main
This commit is contained in:
@@ -130,6 +130,11 @@ impl FlagInfoNode {
|
|||||||
let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };
|
let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };
|
||||||
Ok(node)
|
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
|
/// Flag info list struct
|
||||||
@@ -221,7 +226,7 @@ mod tests {
|
|||||||
let bytes = &flag_info_list.into_bytes();
|
let bytes = &flag_info_list.into_bytes();
|
||||||
let mut head = 0;
|
let mut head = 0;
|
||||||
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
|
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
|
||||||
assert_eq!(version, 1234)
|
assert_eq!(version, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@@ -44,14 +44,16 @@ use std::cmp::Ordering;
|
|||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::hash::{Hash, Hasher};
|
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_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
|
||||||
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
|
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
|
||||||
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
|
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
|
||||||
pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
|
pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
|
||||||
|
|
||||||
use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit, InvalidStoredFlagType};
|
use crate::AconfigStorageError::{
|
||||||
|
BytesParseFail, FileCreationFail, HashTableSizeLimit, InvalidStoredFlagType,
|
||||||
|
};
|
||||||
|
|
||||||
/// Storage file version
|
/// Storage file version
|
||||||
pub const FILE_VERSION: u32 = 1;
|
pub const FILE_VERSION: u32 = 1;
|
||||||
@@ -164,7 +166,6 @@ pub enum AconfigStorageError {
|
|||||||
InvalidStoredFlagType(#[source] anyhow::Error),
|
InvalidStoredFlagType(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get the right hash table size given number of entries in the table. Use a
|
/// Get the right hash table size given number of entries in the table. Use a
|
||||||
/// load factor of 0.5 for performance.
|
/// load factor of 0.5 for performance.
|
||||||
pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
|
pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
|
||||||
@@ -263,25 +264,82 @@ pub fn list_flags(
|
|||||||
let (package_name, package_offset) = package_info[node.package_id as usize];
|
let (package_name, package_offset) = package_info[node.package_id as usize];
|
||||||
let flag_offset = package_offset + node.flag_id as u32;
|
let flag_offset = package_offset + node.flag_id as u32;
|
||||||
let flag_value = flag_value_list.booleans[flag_offset as usize];
|
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| {
|
flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
|
||||||
match v1.0.cmp(&v2.0) {
|
Ordering::Equal => v1.1.cmp(&v2.1),
|
||||||
Ordering::Equal => v1.1.cmp(&v2.1),
|
other => other,
|
||||||
other => other,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Ok(flags)
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::{
|
use crate::test_utils::{
|
||||||
create_test_flag_table, create_test_flag_value_list, create_test_package_table,
|
create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
|
||||||
write_bytes_to_temp_file,
|
create_test_package_table, write_bytes_to_temp_file,
|
||||||
};
|
};
|
||||||
|
use tempfile::NamedTempFile;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// this test point locks down the flag list api
|
// this test point locks down the flag list api
|
||||||
@@ -309,13 +367,13 @@ mod tests {
|
|||||||
String::from("com.android.aconfig.storage.test_1"),
|
String::from("com.android.aconfig.storage.test_1"),
|
||||||
String::from("enabled_ro"),
|
String::from("enabled_ro"),
|
||||||
StoredFlagType::ReadOnlyBoolean,
|
StoredFlagType::ReadOnlyBoolean,
|
||||||
true
|
true,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
String::from("com.android.aconfig.storage.test_1"),
|
String::from("com.android.aconfig.storage.test_1"),
|
||||||
String::from("enabled_rw"),
|
String::from("enabled_rw"),
|
||||||
StoredFlagType::ReadWriteBoolean,
|
StoredFlagType::ReadWriteBoolean,
|
||||||
true
|
true,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
String::from("com.android.aconfig.storage.test_2"),
|
String::from("com.android.aconfig.storage.test_2"),
|
||||||
@@ -333,7 +391,7 @@ mod tests {
|
|||||||
String::from("com.android.aconfig.storage.test_2"),
|
String::from("com.android.aconfig.storage.test_2"),
|
||||||
String::from("enabled_ro"),
|
String::from("enabled_ro"),
|
||||||
StoredFlagType::ReadOnlyBoolean,
|
StoredFlagType::ReadOnlyBoolean,
|
||||||
true
|
true,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
String::from("com.android.aconfig.storage.test_4"),
|
String::from("com.android.aconfig.storage.test_4"),
|
||||||
@@ -345,9 +403,36 @@ mod tests {
|
|||||||
String::from("com.android.aconfig.storage.test_4"),
|
String::from("com.android.aconfig.storage.test_4"),
|
||||||
String::from("enabled_ro"),
|
String::from("enabled_ro"),
|
||||||
StoredFlagType::ReadOnlyBoolean,
|
StoredFlagType::ReadOnlyBoolean,
|
||||||
true
|
true,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
assert_eq!(flags, expected);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -133,14 +133,15 @@ pub fn create_test_flag_value_list() -> FlagValueList {
|
|||||||
|
|
||||||
pub fn create_test_flag_info_list() -> FlagInfoList {
|
pub fn create_test_flag_info_list() -> FlagInfoList {
|
||||||
let header = FlagInfoHeader {
|
let header = FlagInfoHeader {
|
||||||
version: 1234,
|
version: 1,
|
||||||
container: String::from("mockup"),
|
container: String::from("mockup"),
|
||||||
file_type: StorageFileType::FlagInfo as u8,
|
file_type: StorageFileType::FlagInfo as u8,
|
||||||
file_size: 35,
|
file_size: 35,
|
||||||
num_flags: 8,
|
num_flags: 8,
|
||||||
boolean_flag_offset: 27,
|
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 }
|
FlagInfoList { header, nodes }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user