Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9881ae26 authored by Dennis Shen's avatar Dennis Shen Committed by Automerger Merge Worker
Browse files

Merge "aconfig_storage_file: add flag_info definition" into main am: abf5ea2f am: 1fa28542

parents 7a1a423f 1fa28542
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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};

+1 −1
Original line number Diff line number Diff line
@@ -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 {
+2 −1
Original line number Diff line number Diff line
@@ -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::{
@@ -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")),
    }
}

+2 −1
Original line number Diff line number Diff line
@@ -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;
+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