aboutsummaryrefslogtreecommitdiff
path: root/tools/aconfig/aconfig_storage_file/src/flag_info.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aconfig/aconfig_storage_file/src/flag_info.rs')
-rw-r--r--tools/aconfig/aconfig_storage_file/src/flag_info.rs243
1 files changed, 243 insertions, 0 deletions
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
new file mode 100644
index 0000000000..beac38d156
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
@@ -0,0 +1,243 @@
+/*
+ * 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 into_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 {
+ HasServerOverride = 1 << 0,
+ IsReadWrite = 1 << 1,
+ HasLocalOverride = 1 << 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,
+ "readwrite: {}, server override: {}, local override: {}",
+ self.attributes & (FlagInfoBit::IsReadWrite as u8) != 0,
+ self.attributes & (FlagInfoBit::HasServerOverride as u8) != 0,
+ self.attributes & (FlagInfoBit::HasLocalOverride as u8) != 0,
+ )?;
+ Ok(())
+ }
+}
+
+impl FlagInfoNode {
+ /// Serialize to bytes
+ pub fn into_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)
+ }
+
+ /// 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
+#[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 into_bytes(&self) -> Vec<u8> {
+ [
+ self.header.into_bytes(),
+ self.nodes.iter().map(|v| v.into_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.into_bytes().len();
+ let nodes = (0..num_flags)
+ .map(|_| {
+ let node = FlagInfoNode::from_bytes(&bytes[head..])?;
+ head += node.into_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.into_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.into_bytes()).unwrap();
+ assert_eq!(node, &reinterpreted_node);
+ }
+
+ let flag_info_bytes = flag_info_list.into_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.into_bytes();
+ let mut head = 0;
+ let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+ assert_eq!(version, 1);
+ }
+
+ #[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.into_bytes()).unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!("BytesParseFail(binary file is not a flag info file)")
+ );
+ }
+}