Merge "aconfig: create flag value file" into main
This commit is contained in:
@@ -295,8 +295,6 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(header, &expected_header);
|
assert_eq!(header, &expected_header);
|
||||||
|
|
||||||
println!("{:?}", &flag_table.as_ref().unwrap().nodes);
|
|
||||||
|
|
||||||
let buckets: &Vec<Option<u32>> = &flag_table.as_ref().unwrap().buckets;
|
let buckets: &Vec<Option<u32>> = &flag_table.as_ref().unwrap().buckets;
|
||||||
let expected_bucket: Vec<Option<u32>> = vec![
|
let expected_bucket: Vec<Option<u32>> = vec![
|
||||||
Some(98),
|
Some(98),
|
||||||
@@ -338,9 +336,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
// this test point locks down the table serialization
|
// this test point locks down the table serialization
|
||||||
fn test_serialization() {
|
fn test_serialization() {
|
||||||
let flag_table = create_test_flag_table();
|
let flag_table = create_test_flag_table().unwrap();
|
||||||
assert!(flag_table.is_ok());
|
|
||||||
let flag_table = flag_table.unwrap();
|
|
||||||
|
|
||||||
let header: &FlagTableHeader = &flag_table.header;
|
let header: &FlagTableHeader = &flag_table.header;
|
||||||
let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
|
let reinterpreted_header = FlagTableHeader::from_bytes(&header.as_bytes());
|
||||||
|
181
tools/aconfig/src/storage/flag_value.rs
Normal file
181
tools/aconfig/src/storage/flag_value.rs
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::commands::assign_flag_ids;
|
||||||
|
use crate::protos::ProtoFlagState;
|
||||||
|
use crate::storage::{self, FlagPackage};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub struct FlagValueHeader {
|
||||||
|
pub version: u32,
|
||||||
|
pub container: String,
|
||||||
|
pub file_size: u32,
|
||||||
|
pub num_flags: u32,
|
||||||
|
pub boolean_value_offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlagValueHeader {
|
||||||
|
fn new(container: &str, num_flags: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
version: storage::FILE_VERSION,
|
||||||
|
container: String::from(container),
|
||||||
|
file_size: 0,
|
||||||
|
num_flags,
|
||||||
|
boolean_value_offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
result.extend_from_slice(&self.version.to_le_bytes());
|
||||||
|
let container_bytes = self.container.as_bytes();
|
||||||
|
result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
|
||||||
|
result.extend_from_slice(container_bytes);
|
||||||
|
result.extend_from_slice(&self.file_size.to_le_bytes());
|
||||||
|
result.extend_from_slice(&self.num_flags.to_le_bytes());
|
||||||
|
result.extend_from_slice(&self.boolean_value_offset.to_le_bytes());
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub struct FlagValueList {
|
||||||
|
pub header: FlagValueHeader,
|
||||||
|
pub booleans: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlagValueList {
|
||||||
|
pub fn new(container: &str, packages: &[FlagPackage]) -> Result<Self> {
|
||||||
|
// create list
|
||||||
|
let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
|
||||||
|
|
||||||
|
let mut list = Self {
|
||||||
|
header: FlagValueHeader::new(container, num_flags),
|
||||||
|
booleans: vec![false; num_flags as usize],
|
||||||
|
};
|
||||||
|
|
||||||
|
for pkg in packages.iter() {
|
||||||
|
let start_offset = pkg.boolean_offset as usize;
|
||||||
|
let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
|
||||||
|
for pf in pkg.boolean_flags.iter() {
|
||||||
|
let fid = flag_ids
|
||||||
|
.get(pf.name())
|
||||||
|
.ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
|
||||||
|
|
||||||
|
list.booleans[start_offset + (*fid as usize)] =
|
||||||
|
pf.state() == ProtoFlagState::ENABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize all header fields
|
||||||
|
list.header.boolean_value_offset = list.header.as_bytes().len() as u32;
|
||||||
|
list.header.file_size = list.header.boolean_value_offset + num_flags;
|
||||||
|
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
[
|
||||||
|
self.header.as_bytes(),
|
||||||
|
self.booleans
|
||||||
|
.iter()
|
||||||
|
.map(|&v| u8::from(v).to_le_bytes())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.concat(),
|
||||||
|
]
|
||||||
|
.concat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::storage::{
|
||||||
|
group_flags_by_package, tests::parse_all_test_flags, tests::read_str_from_bytes,
|
||||||
|
tests::read_u32_from_bytes, tests::read_u8_from_bytes,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl FlagValueHeader {
|
||||||
|
// test only method to deserialize back into the header struct
|
||||||
|
fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
let mut head = 0;
|
||||||
|
Ok(Self {
|
||||||
|
version: read_u32_from_bytes(bytes, &mut head)?,
|
||||||
|
container: read_str_from_bytes(bytes, &mut head)?,
|
||||||
|
file_size: read_u32_from_bytes(bytes, &mut head)?,
|
||||||
|
num_flags: read_u32_from_bytes(bytes, &mut head)?,
|
||||||
|
boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlagValueList {
|
||||||
|
// test only method to deserialize back into the flag value struct
|
||||||
|
fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||||
|
let header = FlagValueHeader::from_bytes(bytes)?;
|
||||||
|
let num_flags = header.num_flags;
|
||||||
|
let mut head = header.as_bytes().len();
|
||||||
|
let booleans = (0..num_flags)
|
||||||
|
.map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1)
|
||||||
|
.collect();
|
||||||
|
let list = Self { header, booleans };
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_test_flag_value_list() -> Result<FlagValueList> {
|
||||||
|
let caches = parse_all_test_flags();
|
||||||
|
let packages = group_flags_by_package(caches.iter());
|
||||||
|
FlagValueList::new("system", &packages)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// this test point locks down the flag value creation and each field
|
||||||
|
fn test_list_contents() {
|
||||||
|
let flag_value_list = create_test_flag_value_list();
|
||||||
|
assert!(flag_value_list.is_ok());
|
||||||
|
|
||||||
|
let header: &FlagValueHeader = &flag_value_list.as_ref().unwrap().header;
|
||||||
|
let expected_header = FlagValueHeader {
|
||||||
|
version: storage::FILE_VERSION,
|
||||||
|
container: String::from("system"),
|
||||||
|
file_size: 34,
|
||||||
|
num_flags: 8,
|
||||||
|
boolean_value_offset: 26,
|
||||||
|
};
|
||||||
|
assert_eq!(header, &expected_header);
|
||||||
|
|
||||||
|
let booleans: &Vec<bool> = &flag_value_list.as_ref().unwrap().booleans;
|
||||||
|
let expected_booleans: Vec<bool> = vec![false; header.num_flags as usize];
|
||||||
|
assert_eq!(booleans, &expected_booleans);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// this test point locks down the value list serialization
|
||||||
|
fn test_serialization() {
|
||||||
|
let flag_value_list = create_test_flag_value_list().unwrap();
|
||||||
|
|
||||||
|
let header: &FlagValueHeader = &flag_value_list.header;
|
||||||
|
let reinterpreted_header = FlagValueHeader::from_bytes(&header.as_bytes());
|
||||||
|
assert!(reinterpreted_header.is_ok());
|
||||||
|
assert_eq!(header, &reinterpreted_header.unwrap());
|
||||||
|
|
||||||
|
let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_list.as_bytes());
|
||||||
|
assert!(reinterpreted_value_list.is_ok());
|
||||||
|
assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
|
||||||
|
}
|
||||||
|
}
|
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod flag_table;
|
pub mod flag_table;
|
||||||
|
pub mod flag_value;
|
||||||
pub mod package_table;
|
pub mod package_table;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
@@ -24,7 +25,9 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
use crate::commands::OutputFile;
|
use crate::commands::OutputFile;
|
||||||
use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
|
use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
|
||||||
use crate::storage::{flag_table::FlagTable, package_table::PackageTable};
|
use crate::storage::{
|
||||||
|
flag_table::FlagTable, flag_value::FlagValueList, package_table::PackageTable,
|
||||||
|
};
|
||||||
|
|
||||||
pub const FILE_VERSION: u32 = 1;
|
pub const FILE_VERSION: u32 = 1;
|
||||||
|
|
||||||
@@ -128,7 +131,13 @@ where
|
|||||||
let flag_table_file =
|
let flag_table_file =
|
||||||
OutputFile { contents: flag_table.as_bytes(), path: flag_table_file_path };
|
OutputFile { contents: flag_table.as_bytes(), path: flag_table_file_path };
|
||||||
|
|
||||||
Ok(vec![package_table_file, flag_table_file])
|
// create and serialize flag value
|
||||||
|
let flag_value = FlagValueList::new(container, &packages)?;
|
||||||
|
let flag_value_file_path = PathBuf::from("flag.val");
|
||||||
|
let flag_value_file =
|
||||||
|
OutputFile { contents: flag_value.as_bytes(), path: flag_value_file_path };
|
||||||
|
|
||||||
|
Ok(vec![package_table_file, flag_table_file, flag_value_file])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -136,6 +145,13 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::Input;
|
use crate::Input;
|
||||||
|
|
||||||
|
/// Read and parse bytes as u8
|
||||||
|
pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8> {
|
||||||
|
let val = u8::from_le_bytes(buf[*head..*head + 1].try_into()?);
|
||||||
|
*head += 1;
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
/// Read and parse bytes as u16
|
/// Read and parse bytes as u16
|
||||||
pub fn read_u16_from_bytes(buf: &[u8], head: &mut usize) -> Result<u16> {
|
pub fn read_u16_from_bytes(buf: &[u8], head: &mut usize) -> Result<u16> {
|
||||||
let val = u16::from_le_bytes(buf[*head..*head + 2].try_into()?);
|
let val = u16::from_le_bytes(buf[*head..*head + 2].try_into()?);
|
||||||
|
@@ -277,9 +277,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
// this test point locks down the table serialization
|
// this test point locks down the table serialization
|
||||||
fn test_serialization() {
|
fn test_serialization() {
|
||||||
let package_table = create_test_package_table();
|
let package_table = create_test_package_table().unwrap();
|
||||||
assert!(package_table.is_ok());
|
|
||||||
let package_table = package_table.unwrap();
|
|
||||||
|
|
||||||
let header: &PackageTableHeader = &package_table.header;
|
let header: &PackageTableHeader = &package_table.header;
|
||||||
let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
|
let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
|
||||||
|
Reference in New Issue
Block a user