1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! flag value module defines the flag value file format and methods for serialization
18 //! and deserialization
19 
20 use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
21 use crate::{AconfigStorageError, StorageFileType};
22 use anyhow::anyhow;
23 use std::fmt;
24 
25 /// Flag value header struct
26 #[derive(PartialEq)]
27 pub struct FlagValueHeader {
28     pub version: u32,
29     pub container: String,
30     pub file_type: u8,
31     pub file_size: u32,
32     pub num_flags: u32,
33     pub boolean_value_offset: u32,
34 }
35 
36 /// Implement debug print trait for header
37 impl fmt::Debug for FlagValueHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result38     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39         writeln!(
40             f,
41             "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
42             self.version,
43             self.container,
44             StorageFileType::try_from(self.file_type),
45             self.file_size
46         )?;
47         writeln!(
48             f,
49             "Num of Flags: {}, Value Offset:{}",
50             self.num_flags, self.boolean_value_offset
51         )?;
52         Ok(())
53     }
54 }
55 
56 impl FlagValueHeader {
57     /// Serialize to bytes
into_bytes(&self) -> Vec<u8>58     pub fn into_bytes(&self) -> Vec<u8> {
59         let mut result = Vec::new();
60         result.extend_from_slice(&self.version.to_le_bytes());
61         let container_bytes = self.container.as_bytes();
62         result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
63         result.extend_from_slice(container_bytes);
64         result.extend_from_slice(&self.file_type.to_le_bytes());
65         result.extend_from_slice(&self.file_size.to_le_bytes());
66         result.extend_from_slice(&self.num_flags.to_le_bytes());
67         result.extend_from_slice(&self.boolean_value_offset.to_le_bytes());
68         result
69     }
70 
71     /// Deserialize from bytes
from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError>72     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
73         let mut head = 0;
74         let list = Self {
75             version: read_u32_from_bytes(bytes, &mut head)?,
76             container: read_str_from_bytes(bytes, &mut head)?,
77             file_type: read_u8_from_bytes(bytes, &mut head)?,
78             file_size: read_u32_from_bytes(bytes, &mut head)?,
79             num_flags: read_u32_from_bytes(bytes, &mut head)?,
80             boolean_value_offset: read_u32_from_bytes(bytes, &mut head)?,
81         };
82         if list.file_type != StorageFileType::FlagVal as u8 {
83             return Err(AconfigStorageError::BytesParseFail(anyhow!(
84                 "binary file is not a flag value file"
85             )));
86         }
87         Ok(list)
88     }
89 }
90 
91 /// Flag value list struct
92 #[derive(PartialEq)]
93 pub struct FlagValueList {
94     pub header: FlagValueHeader,
95     pub booleans: Vec<bool>,
96 }
97 
98 /// Implement debug print trait for flag value
99 impl fmt::Debug for FlagValueList {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result100     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101         writeln!(f, "Header:")?;
102         write!(f, "{:?}", self.header)?;
103         writeln!(f, "Values:")?;
104         writeln!(f, "{:?}", self.booleans)?;
105         Ok(())
106     }
107 }
108 
109 impl FlagValueList {
110     /// Serialize to bytes
into_bytes(&self) -> Vec<u8>111     pub fn into_bytes(&self) -> Vec<u8> {
112         [
113             self.header.into_bytes(),
114             self.booleans.iter().map(|&v| u8::from(v).to_le_bytes()).collect::<Vec<_>>().concat(),
115         ]
116         .concat()
117     }
118 
119     /// Deserialize from bytes
from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError>120     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
121         let header = FlagValueHeader::from_bytes(bytes)?;
122         let num_flags = header.num_flags;
123         let mut head = header.into_bytes().len();
124         let booleans =
125             (0..num_flags).map(|_| read_u8_from_bytes(bytes, &mut head).unwrap() == 1).collect();
126         let list = Self { header, booleans };
127         Ok(list)
128     }
129 }
130 
131 #[cfg(test)]
132 mod tests {
133     use super::*;
134     use crate::test_utils::create_test_flag_value_list;
135 
136     #[test]
137     // this test point locks down the value list serialization
test_serialization()138     fn test_serialization() {
139         let flag_value_list = create_test_flag_value_list();
140 
141         let header: &FlagValueHeader = &flag_value_list.header;
142         let reinterpreted_header = FlagValueHeader::from_bytes(&header.into_bytes());
143         assert!(reinterpreted_header.is_ok());
144         assert_eq!(header, &reinterpreted_header.unwrap());
145 
146         let flag_value_bytes = flag_value_list.into_bytes();
147         let reinterpreted_value_list = FlagValueList::from_bytes(&flag_value_bytes);
148         assert!(reinterpreted_value_list.is_ok());
149         assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
150         assert_eq!(flag_value_bytes.len() as u32, header.file_size);
151     }
152 
153     #[test]
154     // this test point locks down that version number should be at the top of serialized
155     // bytes
test_version_number()156     fn test_version_number() {
157         let flag_value_list = create_test_flag_value_list();
158         let bytes = &flag_value_list.into_bytes();
159         let mut head = 0;
160         let version = read_u32_from_bytes(bytes, &mut head).unwrap();
161         assert_eq!(version, 1);
162     }
163 
164     #[test]
165     // this test point locks down file type check
test_file_type_check()166     fn test_file_type_check() {
167         let mut flag_value_list = create_test_flag_value_list();
168         flag_value_list.header.file_type = 123u8;
169         let error = FlagValueList::from_bytes(&flag_value_list.into_bytes()).unwrap_err();
170         assert_eq!(
171             format!("{:?}", error),
172             format!("BytesParseFail(binary file is not a flag value file)")
173         );
174     }
175 }
176