blob: 40362340e09ed8dc770a400297ae2f649a1663f0 [file] [log] [blame]
Dennis Shenadc7b732023-12-11 18:59:13 +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
17use crate::storage::{self, FlagPackage};
18use anyhow::Result;
19
20#[derive(PartialEq, Debug)]
21pub struct PackageTableHeader {
22 pub version: u32,
23 pub container: String,
24 pub file_size: u32,
25 pub num_packages: u32,
26 pub bucket_offset: u32,
27 pub node_offset: u32,
28}
29
30impl PackageTableHeader {
31 fn new(container: &str, num_packages: u32) -> Self {
32 Self {
33 version: storage::FILE_VERSION,
34 container: String::from(container),
35 file_size: 0,
36 num_packages,
37 bucket_offset: 0,
38 node_offset: 0,
39 }
40 }
41
42 fn as_bytes(&self) -> Vec<u8> {
43 let mut result = Vec::new();
44 result.extend_from_slice(&self.version.to_le_bytes());
45 let container_bytes = self.container.as_bytes();
46 result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
47 result.extend_from_slice(container_bytes);
48 result.extend_from_slice(&self.file_size.to_le_bytes());
49 result.extend_from_slice(&self.num_packages.to_le_bytes());
50 result.extend_from_slice(&self.bucket_offset.to_le_bytes());
51 result.extend_from_slice(&self.node_offset.to_le_bytes());
52 result
53 }
54}
55
56#[derive(PartialEq, Debug)]
57pub struct PackageTableNode {
58 pub package_name: String,
59 pub package_id: u32,
Dennis Shend0886502024-01-09 16:49:53 +000060 // offset of the first boolean flag in this flag package with respect to the start of
61 // boolean flag value array in the flag value file
Dennis Shenadc7b732023-12-11 18:59:13 +000062 pub boolean_offset: u32,
63 pub next_offset: Option<u32>,
64 pub bucket_index: u32,
65}
66
67impl PackageTableNode {
68 fn new(package: &FlagPackage, num_buckets: u32) -> Self {
69 let bucket_index =
70 storage::get_bucket_index(&package.package_name.to_string(), num_buckets);
71 Self {
72 package_name: String::from(package.package_name),
73 package_id: package.package_id,
74 boolean_offset: package.boolean_offset,
75 next_offset: None,
76 bucket_index,
77 }
78 }
79
80 fn as_bytes(&self) -> Vec<u8> {
81 let mut result = Vec::new();
82 let name_bytes = self.package_name.as_bytes();
83 result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
84 result.extend_from_slice(name_bytes);
85 result.extend_from_slice(&self.package_id.to_le_bytes());
86 result.extend_from_slice(&self.boolean_offset.to_le_bytes());
87 result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
88 result
89 }
90}
91
Dennis Shene6330982023-12-20 19:27:19 +000092#[derive(PartialEq, Debug)]
Dennis Shenadc7b732023-12-11 18:59:13 +000093pub struct PackageTable {
94 pub header: PackageTableHeader,
95 pub buckets: Vec<Option<u32>>,
96 pub nodes: Vec<PackageTableNode>,
97}
98
99impl PackageTable {
100 pub fn new(container: &str, packages: &[FlagPackage]) -> Result<Self> {
101 // create table
102 let num_packages = packages.len() as u32;
103 let num_buckets = storage::get_table_size(num_packages)?;
104 let mut table = Self {
105 header: PackageTableHeader::new(container, num_packages),
106 buckets: vec![None; num_buckets as usize],
107 nodes: packages.iter().map(|pkg| PackageTableNode::new(pkg, num_buckets)).collect(),
108 };
109
Dennis Shene6330982023-12-20 19:27:19 +0000110 // initialize all header fields
111 table.header.bucket_offset = table.header.as_bytes().len() as u32;
112 table.header.node_offset = table.header.bucket_offset + num_buckets * 4;
113 table.header.file_size = table.header.node_offset
114 + table.nodes.iter().map(|x| x.as_bytes().len()).sum::<usize>() as u32;
115
Dennis Shenadc7b732023-12-11 18:59:13 +0000116 // sort nodes by bucket index for efficiency
117 table.nodes.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
118
119 // fill all node offset
Dennis Shene6330982023-12-20 19:27:19 +0000120 let mut offset = table.header.node_offset;
Dennis Shenadc7b732023-12-11 18:59:13 +0000121 for i in 0..table.nodes.len() {
122 let node_bucket_idx = table.nodes[i].bucket_index;
123 let next_node_bucket_idx = if i + 1 < table.nodes.len() {
124 Some(table.nodes[i + 1].bucket_index)
125 } else {
126 None
127 };
128
129 if table.buckets[node_bucket_idx as usize].is_none() {
130 table.buckets[node_bucket_idx as usize] = Some(offset);
131 }
132 offset += table.nodes[i].as_bytes().len() as u32;
133
134 if let Some(index) = next_node_bucket_idx {
135 if index == node_bucket_idx {
136 table.nodes[i].next_offset = Some(offset);
137 }
138 }
139 }
140
Dennis Shenadc7b732023-12-11 18:59:13 +0000141 Ok(table)
142 }
143
144 pub fn as_bytes(&self) -> Vec<u8> {
145 [
146 self.header.as_bytes(),
147 self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
148 self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
149 ]
150 .concat()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::storage::{
158 group_flags_by_package, tests::parse_all_test_flags, tests::read_str_from_bytes,
159 tests::read_u32_from_bytes,
160 };
161
162 impl PackageTableHeader {
163 // test only method to deserialize back into the header struct
164 fn from_bytes(bytes: &[u8]) -> Result<Self> {
165 let mut head = 0;
166 Ok(Self {
167 version: read_u32_from_bytes(bytes, &mut head)?,
168 container: read_str_from_bytes(bytes, &mut head)?,
169 file_size: read_u32_from_bytes(bytes, &mut head)?,
170 num_packages: read_u32_from_bytes(bytes, &mut head)?,
171 bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
172 node_offset: read_u32_from_bytes(bytes, &mut head)?,
173 })
174 }
175 }
176
177 impl PackageTableNode {
178 // test only method to deserialize back into the node struct
179 fn from_bytes(bytes: &[u8], num_buckets: u32) -> Result<Self> {
180 let mut head = 0;
181 let mut node = Self {
182 package_name: read_str_from_bytes(bytes, &mut head)?,
183 package_id: read_u32_from_bytes(bytes, &mut head)?,
184 boolean_offset: read_u32_from_bytes(bytes, &mut head)?,
185 next_offset: match read_u32_from_bytes(bytes, &mut head)? {
186 0 => None,
187 val => Some(val),
188 },
189 bucket_index: 0,
190 };
191 node.bucket_index = storage::get_bucket_index(&node.package_name, num_buckets);
192 Ok(node)
193 }
194 }
195
Dennis Shene6330982023-12-20 19:27:19 +0000196 impl PackageTable {
197 // test only method to deserialize back into the table struct
198 fn from_bytes(bytes: &[u8]) -> Result<Self> {
199 let header = PackageTableHeader::from_bytes(bytes)?;
200 let num_packages = header.num_packages;
201 let num_buckets = storage::get_table_size(num_packages)?;
202 let mut head = header.as_bytes().len();
203 let buckets = (0..num_buckets)
204 .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
205 0 => None,
206 val => Some(val),
207 })
208 .collect();
209 let nodes = (0..num_packages)
210 .map(|_| {
211 let node = PackageTableNode::from_bytes(&bytes[head..], num_buckets).unwrap();
212 head += node.as_bytes().len();
213 node
214 })
215 .collect();
216
217 let table = Self { header, buckets, nodes };
218 Ok(table)
219 }
220 }
221
Dennis Shenadc7b732023-12-11 18:59:13 +0000222 pub fn create_test_package_table() -> Result<PackageTable> {
223 let caches = parse_all_test_flags();
224 let packages = group_flags_by_package(caches.iter());
225 PackageTable::new("system", &packages)
226 }
227
228 #[test]
229 // this test point locks down the table creation and each field
230 fn test_table_contents() {
231 let package_table = create_test_package_table();
232 assert!(package_table.is_ok());
233
234 let header: &PackageTableHeader = &package_table.as_ref().unwrap().header;
235 let expected_header = PackageTableHeader {
236 version: storage::FILE_VERSION,
237 container: String::from("system"),
Dennis Shen8bab8592023-12-20 17:45:00 +0000238 file_size: 208,
239 num_packages: 3,
Dennis Shenadc7b732023-12-11 18:59:13 +0000240 bucket_offset: 30,
241 node_offset: 58,
242 };
243 assert_eq!(header, &expected_header);
244
245 let buckets: &Vec<Option<u32>> = &package_table.as_ref().unwrap().buckets;
Dennis Shene6330982023-12-20 19:27:19 +0000246 let expected: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
Dennis Shenadc7b732023-12-11 18:59:13 +0000247 assert_eq!(buckets, &expected);
248
249 let nodes: &Vec<PackageTableNode> = &package_table.as_ref().unwrap().nodes;
Dennis Shen8bab8592023-12-20 17:45:00 +0000250 assert_eq!(nodes.len(), 3);
Dennis Shenadc7b732023-12-11 18:59:13 +0000251 let first_node_expected = PackageTableNode {
252 package_name: String::from("com.android.aconfig.storage.test_2"),
253 package_id: 1,
Dennis Shend0886502024-01-09 16:49:53 +0000254 boolean_offset: 3,
Dennis Shenadc7b732023-12-11 18:59:13 +0000255 next_offset: None,
256 bucket_index: 0,
257 };
258 assert_eq!(nodes[0], first_node_expected);
259 let second_node_expected = PackageTableNode {
260 package_name: String::from("com.android.aconfig.storage.test_1"),
261 package_id: 0,
262 boolean_offset: 0,
Dennis Shene6330982023-12-20 19:27:19 +0000263 next_offset: Some(158),
Dennis Shenadc7b732023-12-11 18:59:13 +0000264 bucket_index: 3,
265 };
266 assert_eq!(nodes[1], second_node_expected);
Dennis Shen8bab8592023-12-20 17:45:00 +0000267 let third_node_expected = PackageTableNode {
268 package_name: String::from("com.android.aconfig.storage.test_4"),
269 package_id: 2,
Dennis Shend0886502024-01-09 16:49:53 +0000270 boolean_offset: 6,
Dennis Shen8bab8592023-12-20 17:45:00 +0000271 next_offset: None,
272 bucket_index: 3,
273 };
274 assert_eq!(nodes[2], third_node_expected);
Dennis Shenadc7b732023-12-11 18:59:13 +0000275 }
276
277 #[test]
278 // this test point locks down the table serialization
279 fn test_serialization() {
Dennis Shend4ea2602024-01-09 18:56:17 +0000280 let package_table = create_test_package_table().unwrap();
Dennis Shenadc7b732023-12-11 18:59:13 +0000281
Dennis Shene6330982023-12-20 19:27:19 +0000282 let header: &PackageTableHeader = &package_table.header;
Dennis Shenadc7b732023-12-11 18:59:13 +0000283 let reinterpreted_header = PackageTableHeader::from_bytes(&header.as_bytes());
284 assert!(reinterpreted_header.is_ok());
285 assert_eq!(header, &reinterpreted_header.unwrap());
286
Dennis Shene6330982023-12-20 19:27:19 +0000287 let nodes: &Vec<PackageTableNode> = &package_table.nodes;
Dennis Shenadc7b732023-12-11 18:59:13 +0000288 let num_buckets = storage::get_table_size(header.num_packages).unwrap();
289 for node in nodes.iter() {
290 let reinterpreted_node = PackageTableNode::from_bytes(&node.as_bytes(), num_buckets);
291 assert!(reinterpreted_node.is_ok());
292 assert_eq!(node, &reinterpreted_node.unwrap());
293 }
Dennis Shene6330982023-12-20 19:27:19 +0000294
295 let reinterpreted_table = PackageTable::from_bytes(&package_table.as_bytes());
296 assert!(reinterpreted_table.is_ok());
297 assert_eq!(&package_table, &reinterpreted_table.unwrap());
Dennis Shenadc7b732023-12-11 18:59:13 +0000298 }
299}