Loading tools/aconfig/aconfig/src/storage/flag_table.rs +1 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ use crate::commands::assign_flag_ids; use crate::storage::FlagPackage; use aconfig_storage_file::{ get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION, StorageFileType get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, FILE_VERSION, }; use anyhow::{anyhow, Result}; Loading tools/aconfig/aconfig/src/storage/flag_value.rs +1 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ use crate::commands::assign_flag_ids; use crate::storage::FlagPackage; use aconfig_protos::ProtoFlagState; use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION, StorageFileType}; use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType, FILE_VERSION}; use anyhow::{anyhow, Result}; fn new_header(container: &str, num_flags: u32) -> FlagValueHeader { Loading tools/aconfig/aconfig/src/storage/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ pub mod flag_table; pub mod flag_value; pub mod package_table; use anyhow::Result; use anyhow::{anyhow, Result}; use std::collections::{HashMap, HashSet}; use crate::storage::{ Loading Loading @@ -107,6 +107,7 @@ where let flag_value = create_flag_value(container, &packages)?; Ok(flag_value.as_bytes()) } _ => Err(anyhow!("aconfig does not support the creation of this storage file type")), } } Loading tools/aconfig/aconfig/src/storage/package_table.rs +2 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,8 @@ use anyhow::Result; use aconfig_storage_file::{ get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION, StorageFileType get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType, FILE_VERSION, }; use crate::storage::FlagPackage; Loading tools/aconfig/aconfig_storage_file/src/flag_info.rs 0 → 100644 +238 −0 Original line number Diff line number Diff line /* * 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. */ //! flag info module defines the flag info file format and methods for serialization //! and deserialization use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; use std::fmt; /// Flag info header struct #[derive(PartialEq)] pub struct FlagInfoHeader { pub version: u32, pub container: String, pub file_type: u8, pub file_size: u32, pub num_flags: u32, pub boolean_flag_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagInfoHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Type: {:?}, File Size: {}", self.version, self.container, StorageFileType::try_from(self.file_type), self.file_size )?; writeln!( f, "Num of Flags: {}, Boolean Flag Offset:{}", self.num_flags, self.boolean_flag_offset )?; Ok(()) } } impl FlagInfoHeader { /// Serialize to bytes pub 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_type.to_le_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_flag_offset.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> { let mut head = 0; let list = Self { version: read_u32_from_bytes(bytes, &mut head)?, container: read_str_from_bytes(bytes, &mut head)?, file_type: read_u8_from_bytes(bytes, &mut head)?, file_size: read_u32_from_bytes(bytes, &mut head)?, num_flags: read_u32_from_bytes(bytes, &mut head)?, boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?, }; if list.file_type != StorageFileType::FlagInfo as u8 { return Err(AconfigStorageError::BytesParseFail(anyhow!( "binary file is not a flag info file" ))); } Ok(list) } } /// bit field for flag info #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlagInfoBit { IsSticky = 0, IsReadWrite = 1, HasOverride = 2, } /// Flag info node struct #[derive(PartialEq, Clone)] pub struct FlagInfoNode { pub attributes: u8, } /// Implement debug print trait for node impl fmt::Debug for FlagInfoNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "sticky: {}, readwrite: {}, override: {}", self.attributes & (FlagInfoBit::IsSticky as u8), self.attributes & (FlagInfoBit::IsReadWrite as u8), self.attributes & (FlagInfoBit::HasOverride as u8), )?; Ok(()) } } impl FlagInfoNode { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { let mut result = Vec::new(); result.extend_from_slice(&self.attributes.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> { let mut head = 0; let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? }; Ok(node) } } /// Flag info list struct #[derive(PartialEq)] pub struct FlagInfoList { pub header: FlagInfoHeader, pub nodes: Vec<FlagInfoNode>, } /// Implement debug print trait for flag info list impl fmt::Debug for FlagInfoList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } impl FlagInfoList { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { [ self.header.as_bytes(), self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(), ] .concat() } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> { let header = FlagInfoHeader::from_bytes(bytes)?; let num_flags = header.num_flags; let mut head = header.as_bytes().len(); let nodes = (0..num_flags) .map(|_| { let node = FlagInfoNode::from_bytes(&bytes[head..])?; head += node.as_bytes().len(); Ok(node) }) .collect::<Result<Vec<_>, AconfigStorageError>>() .map_err(|errmsg| { AconfigStorageError::BytesParseFail(anyhow!( "fail to parse flag info list: {}", errmsg )) })?; let list = Self { header, nodes }; Ok(list) } } #[cfg(test)] mod tests { use super::*; use crate::test_utils::create_test_flag_info_list; #[test] // this test point locks down the value list serialization fn test_serialization() { let flag_info_list = create_test_flag_info_list(); let header: &FlagInfoHeader = &flag_info_list.header; let reinterpreted_header = FlagInfoHeader::from_bytes(&header.as_bytes()); assert!(reinterpreted_header.is_ok()); assert_eq!(header, &reinterpreted_header.unwrap()); let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes; for node in nodes.iter() { let reinterpreted_node = FlagInfoNode::from_bytes(&node.as_bytes()).unwrap(); assert_eq!(node, &reinterpreted_node); } let flag_info_bytes = flag_info_list.as_bytes(); let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes); assert!(reinterpreted_info_list.is_ok()); assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap()); assert_eq!(flag_info_bytes.len() as u32, header.file_size); } #[test] // this test point locks down that version number should be at the top of serialized // bytes fn test_version_number() { let flag_info_list = create_test_flag_info_list(); let bytes = &flag_info_list.as_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version, 1234) } #[test] // this test point locks down file type check fn test_file_type_check() { let mut flag_info_list = create_test_flag_info_list(); flag_info_list.header.file_type = 123u8; let error = FlagInfoList::from_bytes(&flag_info_list.as_bytes()).unwrap_err(); assert_eq!( format!("{:?}", error), format!("BytesParseFail(binary file is not a flag info file)") ); } } Loading
tools/aconfig/aconfig/src/storage/flag_table.rs +1 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ use crate::commands::assign_flag_ids; use crate::storage::FlagPackage; use aconfig_storage_file::{ get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION, StorageFileType get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, FILE_VERSION, }; use anyhow::{anyhow, Result}; Loading
tools/aconfig/aconfig/src/storage/flag_value.rs +1 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ use crate::commands::assign_flag_ids; use crate::storage::FlagPackage; use aconfig_protos::ProtoFlagState; use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION, StorageFileType}; use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType, FILE_VERSION}; use anyhow::{anyhow, Result}; fn new_header(container: &str, num_flags: u32) -> FlagValueHeader { Loading
tools/aconfig/aconfig/src/storage/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ pub mod flag_table; pub mod flag_value; pub mod package_table; use anyhow::Result; use anyhow::{anyhow, Result}; use std::collections::{HashMap, HashSet}; use crate::storage::{ Loading Loading @@ -107,6 +107,7 @@ where let flag_value = create_flag_value(container, &packages)?; Ok(flag_value.as_bytes()) } _ => Err(anyhow!("aconfig does not support the creation of this storage file type")), } } Loading
tools/aconfig/aconfig/src/storage/package_table.rs +2 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,8 @@ use anyhow::Result; use aconfig_storage_file::{ get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION, StorageFileType get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType, FILE_VERSION, }; use crate::storage::FlagPackage; Loading
tools/aconfig/aconfig_storage_file/src/flag_info.rs 0 → 100644 +238 −0 Original line number Diff line number Diff line /* * 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. */ //! flag info module defines the flag info file format and methods for serialization //! and deserialization use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes}; use crate::{AconfigStorageError, StorageFileType}; use anyhow::anyhow; use std::fmt; /// Flag info header struct #[derive(PartialEq)] pub struct FlagInfoHeader { pub version: u32, pub container: String, pub file_type: u8, pub file_size: u32, pub num_flags: u32, pub boolean_flag_offset: u32, } /// Implement debug print trait for header impl fmt::Debug for FlagInfoHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "Version: {}, Container: {}, File Type: {:?}, File Size: {}", self.version, self.container, StorageFileType::try_from(self.file_type), self.file_size )?; writeln!( f, "Num of Flags: {}, Boolean Flag Offset:{}", self.num_flags, self.boolean_flag_offset )?; Ok(()) } } impl FlagInfoHeader { /// Serialize to bytes pub 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_type.to_le_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_flag_offset.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> { let mut head = 0; let list = Self { version: read_u32_from_bytes(bytes, &mut head)?, container: read_str_from_bytes(bytes, &mut head)?, file_type: read_u8_from_bytes(bytes, &mut head)?, file_size: read_u32_from_bytes(bytes, &mut head)?, num_flags: read_u32_from_bytes(bytes, &mut head)?, boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?, }; if list.file_type != StorageFileType::FlagInfo as u8 { return Err(AconfigStorageError::BytesParseFail(anyhow!( "binary file is not a flag info file" ))); } Ok(list) } } /// bit field for flag info #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlagInfoBit { IsSticky = 0, IsReadWrite = 1, HasOverride = 2, } /// Flag info node struct #[derive(PartialEq, Clone)] pub struct FlagInfoNode { pub attributes: u8, } /// Implement debug print trait for node impl fmt::Debug for FlagInfoNode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, "sticky: {}, readwrite: {}, override: {}", self.attributes & (FlagInfoBit::IsSticky as u8), self.attributes & (FlagInfoBit::IsReadWrite as u8), self.attributes & (FlagInfoBit::HasOverride as u8), )?; Ok(()) } } impl FlagInfoNode { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { let mut result = Vec::new(); result.extend_from_slice(&self.attributes.to_le_bytes()); result } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> { let mut head = 0; let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? }; Ok(node) } } /// Flag info list struct #[derive(PartialEq)] pub struct FlagInfoList { pub header: FlagInfoHeader, pub nodes: Vec<FlagInfoNode>, } /// Implement debug print trait for flag info list impl fmt::Debug for FlagInfoList { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "Header:")?; write!(f, "{:?}", self.header)?; writeln!(f, "Nodes:")?; for node in self.nodes.iter() { write!(f, "{:?}", node)?; } Ok(()) } } impl FlagInfoList { /// Serialize to bytes pub fn as_bytes(&self) -> Vec<u8> { [ self.header.as_bytes(), self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(), ] .concat() } /// Deserialize from bytes pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> { let header = FlagInfoHeader::from_bytes(bytes)?; let num_flags = header.num_flags; let mut head = header.as_bytes().len(); let nodes = (0..num_flags) .map(|_| { let node = FlagInfoNode::from_bytes(&bytes[head..])?; head += node.as_bytes().len(); Ok(node) }) .collect::<Result<Vec<_>, AconfigStorageError>>() .map_err(|errmsg| { AconfigStorageError::BytesParseFail(anyhow!( "fail to parse flag info list: {}", errmsg )) })?; let list = Self { header, nodes }; Ok(list) } } #[cfg(test)] mod tests { use super::*; use crate::test_utils::create_test_flag_info_list; #[test] // this test point locks down the value list serialization fn test_serialization() { let flag_info_list = create_test_flag_info_list(); let header: &FlagInfoHeader = &flag_info_list.header; let reinterpreted_header = FlagInfoHeader::from_bytes(&header.as_bytes()); assert!(reinterpreted_header.is_ok()); assert_eq!(header, &reinterpreted_header.unwrap()); let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes; for node in nodes.iter() { let reinterpreted_node = FlagInfoNode::from_bytes(&node.as_bytes()).unwrap(); assert_eq!(node, &reinterpreted_node); } let flag_info_bytes = flag_info_list.as_bytes(); let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes); assert!(reinterpreted_info_list.is_ok()); assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap()); assert_eq!(flag_info_bytes.len() as u32, header.file_size); } #[test] // this test point locks down that version number should be at the top of serialized // bytes fn test_version_number() { let flag_info_list = create_test_flag_info_list(); let bytes = &flag_info_list.as_bytes(); let mut head = 0; let version = read_u32_from_bytes(bytes, &mut head).unwrap(); assert_eq!(version, 1234) } #[test] // this test point locks down file type check fn test_file_type_check() { let mut flag_info_list = create_test_flag_info_list(); flag_info_list.header.file_type = 123u8; let error = FlagInfoList::from_bytes(&flag_info_list.as_bytes()).unwrap_err(); assert_eq!( format!("{:?}", error), format!("BytesParseFail(binary file is not a flag info file)") ); } }