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