blob: 36ea3094d31cffff1eca74f608f4402cd8caf571 [file] [log] [blame]
Dennis Shen0d1c5622023-12-01 21:04:29 +00001/*
2 * Copyright (C) 2023 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
Dennis Shenb65b3502024-01-04 15:57:42 +000017pub mod flag_table;
Dennis Shend4ea2602024-01-09 18:56:17 +000018pub mod flag_value;
Dennis Shenadc7b732023-12-11 18:59:13 +000019pub mod package_table;
20
21use anyhow::{anyhow, Result};
22use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
23use std::hash::{Hash, Hasher};
24use std::path::PathBuf;
Dennis Shen0d1c5622023-12-01 21:04:29 +000025
26use crate::commands::OutputFile;
27use crate::protos::{ProtoParsedFlag, ProtoParsedFlags};
Dennis Shend4ea2602024-01-09 18:56:17 +000028use crate::storage::{
29 flag_table::FlagTable, flag_value::FlagValueList, package_table::PackageTable,
30};
Dennis Shenadc7b732023-12-11 18:59:13 +000031
32pub const FILE_VERSION: u32 = 1;
33
34pub const HASH_PRIMES: [u32; 29] = [
Dennis Shenb65b3502024-01-04 15:57:42 +000035 7, 17, 29, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241,
Dennis Shenadc7b732023-12-11 18:59:13 +000036 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611,
37 402653189, 805306457, 1610612741,
38];
39
40/// Get the right hash table size given number of entries in the table. Use a
41/// load factor of 0.5 for performance.
42pub fn get_table_size(entries: u32) -> Result<u32> {
43 HASH_PRIMES
44 .iter()
45 .find(|&&num| num >= 2 * entries)
46 .copied()
47 .ok_or(anyhow!("Number of packages is too large"))
48}
49
50/// Get the corresponding bucket index given the key and number of buckets
51pub fn get_bucket_index<T: Hash>(val: &T, num_buckets: u32) -> u32 {
52 let mut s = DefaultHasher::new();
53 val.hash(&mut s);
54 (s.finish() % num_buckets as u64) as u32
55}
Dennis Shen0d1c5622023-12-01 21:04:29 +000056
57pub struct FlagPackage<'a> {
58 pub package_name: &'a str,
59 pub package_id: u32,
60 pub flag_names: HashSet<&'a str>,
61 pub boolean_flags: Vec<&'a ProtoParsedFlag>,
Dennis Shend0886502024-01-09 16:49:53 +000062 // offset of the first boolean flag in this flag package with respect to the start of
63 // boolean flag value array in the flag value file
Dennis Shen0d1c5622023-12-01 21:04:29 +000064 pub boolean_offset: u32,
65}
66
67impl<'a> FlagPackage<'a> {
68 fn new(package_name: &'a str, package_id: u32) -> Self {
69 FlagPackage {
70 package_name,
71 package_id,
72 flag_names: HashSet::new(),
73 boolean_flags: vec![],
74 boolean_offset: 0,
75 }
76 }
77
78 fn insert(&mut self, pf: &'a ProtoParsedFlag) {
79 if self.flag_names.insert(pf.name()) {
80 self.boolean_flags.push(pf);
81 }
82 }
83}
84
85pub fn group_flags_by_package<'a, I>(parsed_flags_vec_iter: I) -> Vec<FlagPackage<'a>>
86where
87 I: Iterator<Item = &'a ProtoParsedFlags>,
88{
89 // group flags by package
90 let mut packages: Vec<FlagPackage<'a>> = Vec::new();
Dennis Shenadc7b732023-12-11 18:59:13 +000091 let mut package_index: HashMap<&str, usize> = HashMap::new();
Dennis Shen0d1c5622023-12-01 21:04:29 +000092 for parsed_flags in parsed_flags_vec_iter {
93 for parsed_flag in parsed_flags.parsed_flag.iter() {
94 let index = *(package_index.entry(parsed_flag.package()).or_insert(packages.len()));
95 if index == packages.len() {
96 packages.push(FlagPackage::new(parsed_flag.package(), index as u32));
97 }
98 packages[index].insert(parsed_flag);
99 }
100 }
101
102 // calculate package flag value start offset, in flag value file, each boolean
Dennis Shend0886502024-01-09 16:49:53 +0000103 // is stored as a single byte
Dennis Shen0d1c5622023-12-01 21:04:29 +0000104 let mut boolean_offset = 0;
105 for p in packages.iter_mut() {
106 p.boolean_offset = boolean_offset;
Dennis Shend0886502024-01-09 16:49:53 +0000107 boolean_offset += p.boolean_flags.len() as u32;
Dennis Shen0d1c5622023-12-01 21:04:29 +0000108 }
109
110 packages
111}
112
113pub fn generate_storage_files<'a, I>(
Dennis Shenadc7b732023-12-11 18:59:13 +0000114 container: &str,
Dennis Shen0d1c5622023-12-01 21:04:29 +0000115 parsed_flags_vec_iter: I,
116) -> Result<Vec<OutputFile>>
117where
118 I: Iterator<Item = &'a ProtoParsedFlags>,
119{
Dennis Shenadc7b732023-12-11 18:59:13 +0000120 let packages = group_flags_by_package(parsed_flags_vec_iter);
121
122 // create and serialize package map
123 let package_table = PackageTable::new(container, &packages)?;
124 let package_table_file_path = PathBuf::from("package.map");
125 let package_table_file =
126 OutputFile { contents: package_table.as_bytes(), path: package_table_file_path };
127
Dennis Shenb65b3502024-01-04 15:57:42 +0000128 // create and serialize flag map
129 let flag_table = FlagTable::new(container, &packages)?;
130 let flag_table_file_path = PathBuf::from("flag.map");
131 let flag_table_file =
132 OutputFile { contents: flag_table.as_bytes(), path: flag_table_file_path };
133
Dennis Shend4ea2602024-01-09 18:56:17 +0000134 // create and serialize flag value
135 let flag_value = FlagValueList::new(container, &packages)?;
136 let flag_value_file_path = PathBuf::from("flag.val");
137 let flag_value_file =
138 OutputFile { contents: flag_value.as_bytes(), path: flag_value_file_path };
139
140 Ok(vec![package_table_file, flag_table_file, flag_value_file])
Dennis Shen0d1c5622023-12-01 21:04:29 +0000141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::Input;
147
Dennis Shend4ea2602024-01-09 18:56:17 +0000148 /// Read and parse bytes as u8
149 pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8> {
150 let val = u8::from_le_bytes(buf[*head..*head + 1].try_into()?);
151 *head += 1;
152 Ok(val)
153 }
154
Dennis Shend0886502024-01-09 16:49:53 +0000155 /// Read and parse bytes as u16
156 pub fn read_u16_from_bytes(buf: &[u8], head: &mut usize) -> Result<u16> {
157 let val = u16::from_le_bytes(buf[*head..*head + 2].try_into()?);
158 *head += 2;
159 Ok(val)
160 }
161
Dennis Shenadc7b732023-12-11 18:59:13 +0000162 /// Read and parse bytes as u32
163 pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32> {
164 let val = u32::from_le_bytes(buf[*head..*head + 4].try_into()?);
165 *head += 4;
166 Ok(val)
167 }
168
169 /// Read and parse bytes as string
170 pub fn read_str_from_bytes(buf: &[u8], head: &mut usize) -> Result<String> {
171 let num_bytes = read_u32_from_bytes(buf, head)? as usize;
172 let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())?;
173 *head += num_bytes;
174 Ok(val)
175 }
176
Dennis Shen0d1c5622023-12-01 21:04:29 +0000177 pub fn parse_all_test_flags() -> Vec<ProtoParsedFlags> {
178 let aconfig_files = [
179 (
180 "com.android.aconfig.storage.test_1",
Dennis Shenb65b3502024-01-04 15:57:42 +0000181 "storage_test_1.aconfig",
182 include_bytes!("../../tests/storage_test_1.aconfig").as_slice(),
Dennis Shen0d1c5622023-12-01 21:04:29 +0000183 ),
184 (
185 "com.android.aconfig.storage.test_2",
186 "storage_test_2.aconfig",
187 include_bytes!("../../tests/storage_test_2.aconfig").as_slice(),
188 ),
Dennis Shen8bab8592023-12-20 17:45:00 +0000189 (
190 "com.android.aconfig.storage.test_4",
191 "storage_test_4.aconfig",
192 include_bytes!("../../tests/storage_test_4.aconfig").as_slice(),
193 ),
Dennis Shen0d1c5622023-12-01 21:04:29 +0000194 ];
195
196 aconfig_files
197 .into_iter()
198 .map(|(pkg, file, content)| {
199 let bytes = crate::commands::parse_flags(
200 pkg,
201 Some("system"),
202 vec![Input {
203 source: format!("tests/{}", file).to_string(),
204 reader: Box::new(content),
205 }],
206 vec![],
207 crate::commands::DEFAULT_FLAG_PERMISSION,
208 )
209 .unwrap();
210 crate::protos::parsed_flags::try_from_binary_proto(&bytes).unwrap()
211 })
212 .collect()
213 }
214
215 #[test]
216 fn test_flag_package() {
217 let caches = parse_all_test_flags();
218 let packages = group_flags_by_package(caches.iter());
219
220 for pkg in packages.iter() {
221 let pkg_name = pkg.package_name;
222 assert_eq!(pkg.flag_names.len(), pkg.boolean_flags.len());
223 for pf in pkg.boolean_flags.iter() {
224 assert!(pkg.flag_names.contains(pf.name()));
225 assert_eq!(pf.package(), pkg_name);
226 }
227 }
228
Dennis Shen8bab8592023-12-20 17:45:00 +0000229 assert_eq!(packages.len(), 3);
Dennis Shen0d1c5622023-12-01 21:04:29 +0000230
231 assert_eq!(packages[0].package_name, "com.android.aconfig.storage.test_1");
232 assert_eq!(packages[0].package_id, 0);
Dennis Shenb65b3502024-01-04 15:57:42 +0000233 assert_eq!(packages[0].flag_names.len(), 3);
Dennis Shen0d1c5622023-12-01 21:04:29 +0000234 assert!(packages[0].flag_names.contains("enabled_rw"));
235 assert!(packages[0].flag_names.contains("disabled_rw"));
236 assert!(packages[0].flag_names.contains("enabled_ro"));
Dennis Shen0d1c5622023-12-01 21:04:29 +0000237 assert_eq!(packages[0].boolean_offset, 0);
238
239 assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
240 assert_eq!(packages[1].package_id, 1);
241 assert_eq!(packages[1].flag_names.len(), 3);
242 assert!(packages[1].flag_names.contains("enabled_ro"));
243 assert!(packages[1].flag_names.contains("disabled_ro"));
244 assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
Dennis Shend0886502024-01-09 16:49:53 +0000245 assert_eq!(packages[1].boolean_offset, 3);
Dennis Shen8bab8592023-12-20 17:45:00 +0000246
247 assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
248 assert_eq!(packages[2].package_id, 2);
249 assert_eq!(packages[2].flag_names.len(), 2);
250 assert!(packages[2].flag_names.contains("enabled_ro"));
251 assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
Dennis Shend0886502024-01-09 16:49:53 +0000252 assert_eq!(packages[2].boolean_offset, 6);
Dennis Shen0d1c5622023-12-01 21:04:29 +0000253 }
254}